Mis fotos

Gracias a mi aplicación para subir fotografías, y a mi aplicación para editar etiquetas, terminé ya de organizar, etiquetar, respaldar y subir todas mis fotos, incluyendo las que tomé el fin de semana pasado cuando Juan se casó.

Galería

Galería

En el procesó afiné y mejoré varias cosas de mis aplicaciones, y me parece que ya son suficientemente robustas. La metodología que tengo para manejar mis fotos está ya bastante estandarizada, y se integra de manera perfecta con GNOME 3, así que espero no volver a quedarme tan atrasado en el mantenimiento de mi galería. Además, como ahora tengo disco duro de sobra, decidí liberar a mi laptop de un montón de cosas (videos e imágenes de CDs y DVDs, principalmente) que no tenían razón de estar ahí, con lo que puedo ya tener una copia de mi colección de fotos en mi computadora portátil.

No lo he platicado en el blog, pero estoy estrenando laptop (desde hace varios meses), y tiene un disco duro de estado sólido; esto es espectacular porque todo corre estúpidamente rápido… la desventaja es que es diminuto para el tamaño de discos duros al que estoy acostumbrado (tiene 128 GB). Es la primera laptop donde borré completamente el Windows que venía instalado; necesitaba el espacio. Actualmente mi colección de fotos ronda en los 21 Gigabytes; dado que son del orden de 9,000 fotografías, espero no llegar a los 60 Gigabytes pronto: me quedan menos de 40 Gigabytes libres en la laptop. Cuando llegue a ese tamaño, espero ya haber cambiado de computadora portátil.

Tener las fotos en la laptop (y no sólo aventadas en un directorio, sino además ya bien integradas en Shotwell) me permitirá actualizar mi galería incluso si salgo de México en un viaje largo; podré organizar las fotos en mi laptop, e incluso subirlas a la galería en línea desde donde quiera que esté. Gran parte del problema durante mis viajes largos de los últimos años fue que la base de datos de mis fotos en F-Spot estaba en México en mi máquina de escritorio, y no sabía cómo sincronizar dos instalaciones distintas del programa en máquinas diferentes. Con Shotwell ya sé cómo hacerlo: es sólo es cosa de mantener mi directorio $HOME/Pictures sincronizado entre las dos máquinas, y copiar la última versión del archivo $HOME/.local/share/shotwell/data/photo.db sobre la versión viejita en la máquina que se esté sincronizando.

Además de la copia en mi máquina de escritorio y en mi laptop, tengo una copia de mis fotos en mi media center (que tendré que escribir un programa que me permita exportar la base de datos de Shotwell y meterla en la base de datos de XBMC, porque si no sólo se puede ver fotos por directorio), otra en mi servidorcito Atom, y una más en una máquina debajo de siete capas de adamantium y kriptonita que la tengo corriendo en la Zona Fantasma. Y una copia más (pero con las fotos escaladas a 1024×768) en Xochitl en mi galería en línea, que los invito una vez más a que la exploren.

Imprimir entrada Imprimir entrada

Django sin cadenas

Como comentaba en mi anterior entrada, por fin me pagaron en la UNAM. Generalmente ahora pagan unos dos meses depués de que se comienza a trabajar (no como cuando yo di clases la primera vez, que se tardaron más de seis meses), pero en esta ocasión hubo un problema con mi grupo en particular por el nuevo plan de estudios de mi carrera.

Como sea, me pagaron el miércoles por fin y decidí que nada más eso merecía que fuera al cine, después de más de un mes de no ir. Así que fui a ver la última de Tarantino, Django Unchained. Se aplican las de siempre.

Django Unchained

Django Unchained

Como Inglourious Basterds, Django es un cuento de hadas. Sólo en lugar de utilizar una ambientación Nazi, la nueva película de Tarantino combina elementos del spaghetti western, ambientándolo en el Sur Profundo de los Estados Unidos; en las palabras del director, es un southern. La identificación con un cuento de hadas se hace incluso explícita cuando el personaje del Dr. Schultz le cuenta a Django el cuento de hadas nórdico de la saga Völsunga. Y si alguien me sale con que esas mamadas no son cuentos de hadas, le lanzo un zapato.

Todo este choro mareador es para explicar que la historia de Django es una bola de mamadas. Cualquier intento de enmarcarlo en la verdad histórica del esclavismo gringo del siglo XIX es bastante idiota, y probablemente inútil, así que no entiendo por qué tanta gente se ha desgarrado las vestiduras tratando de atacar la película, o bien por ser la fantasía encarnada de la “culpa de hombre blanco”, o bien por ser una trivialización de la tragedia que fue el esclavismo gringo.

La película es un cuento de hadas, y en ese sentido es bien chingona. Yo no he visto The Soloist, ni tampoco Ray, que he oído son las dos mejores películas de Jamie Foxx; pero en Django da una actuación espectacular el pinche negro. Describirlo como “permanentemente encabronado” se queda corto. Christoph Waltz hace de nuevo el papel de un alemán increíblemente culto y educado, además de encantador, pero en esta película de hecho su personaje es un hombre básicamente bueno. Que se gana la vida matando gente por dinero, pero básicamente bueno.

Y Leonardo DiCaprio sí me sorprendió; es un excelente villano. Debería hacerla de villano de ahora en adelante; de verdad se roba la segunda parte de la película. Incluso Samuel L. Jackson aparece (por fin) interpretando un papel distinto al que viene interpretando desde hace veinte años, y me parece que hacerla de viejito le queda bastante bien. Claro que su papel es muy lucidor, encarnando a toda la gente mierda, miembros de un grupo oprimido, que se alía con sus opresores para joder a sus semenjantes.

La “controversia” que está generando la película en el gabacho es resultado del crimen que los gringos cometieron, de crear una nación donde “All men are born free and equal”, pero que convenientemente “All men” no incluye a los negros… excepto para pagar impuestos, en cuyo caso decidieron que cinco negros equivalían a tres blancos. Es una nación viciada de origen, incluyendo a sus adorados “founding fathers”, los cuáles todos tenían esclavos (excepto Franklin), por un crimen que es mucho peor y mucho más duradero que los cometidos por Hitler y sus seguidores. No se pueden perdonar a ellos mismos (y está bien, es imperdonable), y entonces cualquier cosa relacionada a ese negro (je) pasado suyo los vuelve medio locos.

Para el resto del mundo que sí es civilizado eso no nos causa ningún conflicto, y me parece que podemos apreciar y disfrutar esta película por lo que es: un extraordinariamente bien hecho cuento de hadas, con vaqueritos que se despedazan a balazos. Así que vayan y véanla, en el cine de ser posible.

Imprimir entrada Imprimir entrada

Ocho años

Otra vez se me pasó el aniversario de mi blog, el 26 de enero. Al menos esta vez fue por menos de dos semanas.

El año pasado estuvo marcado para mí por dos cosas, cada una de las cuales se llevó más o menos seis meses: escribir mi tesis de doctorado, y quedarme sin novia, sin casa, sin dinero y sin trabajo. Lo segundo impidió (junto con otras cosas) que lo primero culminara en lo natural, que sería el doctorarme.

Este año tengo que doctorarme, y de hecho tengo que hacerlo en los próximos meses. Ayer por fin me pagaron en la UNAM (después de más de seis meses; así suele ser ahí), y entonces ya estoy mucho más estable de lo que estuve los últimos seis meses. La chamba también se calmará un poco en las próximas dos semanas (después de ponerse intensa durante casi todo el mes pasado), y básicamente eso significa que ya puedo dedicarle tiempo a por fin sacar el grado de doctor. Vamos a ver cuánto tarda eso.

¿De lo demás? No lo sé con certeza, pero comienzo a entender el viejo adagio de que cuando se cierra una puerta, se abre una ventana.

Y la vista es hermosa.

Como siempre, gracias a todos los lectores que me han seguido durante todos estos años. Prometo tratar de escribir más seguido; el último año fue un desastre en ese sentido.

Imprimir entrada Imprimir entrada

La boda de Juan y Érika

Uno de mis mejores amigos, Juan, se casó el sábado. No había escrito al respecto por razones.

En 2005 Omar tuvo a bien casarse con Paola; tuvieron que pasar casi ocho años para que el segundo de nuestro pequeño grupo de cuatro (Omar, Enrique, Juan y yo) se volviera a casar. A este paso, Enrique se va a casar en 2021, y yo en 2029; no se ven buenas las probabilidades de que pueda cargar a mi esposa para entrar a nuestro hogar por primera vez como casados.

Yo estuve presente el día en que Érika y Juan se conocieron, en gran medida porque fui parte de la conspiración que varios amigos hicimos para que esto ocurriera, a pesar de la férrea resistencia de Juan. Resistencia no a conocer a Érika, sino a salir de su casa; para que no tuviera de otra tuvimos que hacer una reunión en su casa y llevarle ahí a Érika para que por fin la conociera. Y luego todavía tuvo el descaro de reclamarnos que por qué no se la presentamos antes.

La historia de Juan y Érika es larga y tortuosa como suelen ser todas las grandes historias de amor, y no la voy a relatar aquí en primer lugar porque es una historia que les tocará a ellos contarles a sus hijos, y en segundo lugar porque qué hueva. Lo que voy a relatar es el día de la boda, donde me tocó cumplir el papel de chofer, dado que Juan me pidió que lo llevara a él de su departamento al salón de la boda, y al final de la misma que lo llevara a él y a su nueva esposa al mismo departamento, convertido ahora en el hogar donde comenzarán a vivir su vida como pareja casada.

Pero antes un paréntesis: una muestra de que Enrique, Omar y yo de verdad queremos a Juan, es que accedimos los tres a vestir traje para el día de la boda, cuando en general nunca lo hacemos. Así que yo comencé el día bañándome y poniéndome mi traje, que tuve que comprar porque el que me regaló mi hermano después de titularme de la maestría ya no me queda. Me aprieta de la cintura, lo cual no es de extrañar; lo que es de extrañar es que me aprieta aún más de los hombros. O cada vez estoy más ancho de espaldas, o la grasa se me acumula en los hombros.

Como sea, Juan me pidió que llegara a las 3:00, y no queriendo hacer un Enrique traté de llegar antes. Lo conseguí, pero por muy poco: llegué al diez para las tres. Juan se disfrazó de pingüino, se puso saliva en las patillas, y nos fuimos de ahí a la casa de Érika para recoger varias cosas que ella iba a necesitar en la noche. Ya todo empacado, nos fuimos al salón, que resultó estar dentro del deportivo al que mi mamá lleva casi quince años yendo a hacer ejercicio.

A las cinco de la tarde llegó Érika, y al mirar a Juan disfrazado de pingüino, de inmediato se puso a llorar. Yo le hice ver que todavía estaba a tiempo de arrepentirse, que no tenía porqué echarse a llorar, pero eso sólo la hizo llorar todavía más. Así que le dije que mejor lo reservara para cuando Juan le pegara, pero eso, al parecer, también la hizo llorar aún más, por lo que mejor me callé. Ya que las lágrimas se detuvieron un poco (se la pasó llorando toda la boda), vino la sesión de fotos.

Juan y Érika

Juan y Érika

Yo no tomé las fotos oficiales; yo sólo iba de advenedizo.

Una hora de fotos después, llegaron los papás de Juan y su hermana menor, Mina. Otro paréntesis: yo la última (y única vez) que había visto a Mina había sido diez años atrás, cuando ella era una niña preparatoriana de 17 años que había venido la Ciudad de México a visitar a su hermano mayor. Nada en el mundo me podría haber preparado para la impresión que me causó ver al despampanante mujerón en el que la niña preparatoriana de 17 años se había convertido en tan sólo una década.

Por supuesto, ella no me reconoció al verme.

Después llegaron Fede, el hermano menor de Juan, junto con su esposa, y poco a poco fueron llegando más familiares del novio y de la novia, mientras los fotógrafos oficiales hacían su trabajo, y yo me colaba para robar fotos con mi camarita.

Los novios y sus familias

Los novios y sus familias

A las siete tenía que llegar el juez de lo civil (Juan y Érika tuvieron el buen gusto de casarse únicamente por lo civil, igual que Omar y Paola), y era importante realizar la ceremonia rápido porque el tipo tenía otra boda a las ocho, así que nos pasaron a todos a un salón, y nada más llegó el juez comenzó la ceremonia. Yo seguí robando fotos en todo momento.

La ceremonia

La ceremonia

El juez no era buen orador, pero le echaba ganas. Le imbuía drama a cada una de sus frases, acompañándolas de gesticulaciones con las manos, mientras explicaba la importancia de matrimoniarse para poder… algo, no sé. La verdad yo estaba bastante emocionado de por sí, dado que Érika continuaba llorando todo el tiempo; el performance del juez sólo lo acentuaba.

Después vino la firma de documentos, que como todo el mundo sabe es la parte más romántica de cualquier boda.

Érika firmando

Érika firmando

Juan firmando

Juan firmando

Y entonces los novios leyeron sus votos. Es necesario que explique lo siguiente antes de continuar; Juan es uno de mis mejores amigos: para mí él (junto con Enrique y Omar) es un hermano. Lo conozco desde que teníamos 19 años, y sé lo difícil que le es expresar lo que siente, mucho más aún con palabras. Así que oír decirle a Érika sus votos, mientras se le quebraba la voz, sí estuvo a punto de hacerme llorar a mí también. Claro que la hermana mayor de Érika también jugó sucio; contrató un cuarteto de cuerdas que se pusieran a tocar la aria Aire de Bach mientras leían sus votos. Así es trampa.

Después de que todos lloramos, el juez hizo que se pusieran los anillos (que Juan se las ingenió para hacerlo mal… dos veces), y por fin los declaró matrimoniados.

Recién casados

Recién casados

Después siguió lo que sigue en todas las bodas; se comió, se partió un pastel, la novia aventó un ramo, y el novio manoseó a la novia y después aventó la liga. Y hubo baile todo el tiempo; que por cierto (otro paréntesis), después de quedarme sin novia, sin casa, sin dinero y sin trabajo pensé que al menos una ventaja sería que no tendría que bailar. Hasta que llegó a sacarme a bailar la mamá de Juan, que por supuesto no había forma de que le dijera que no, así que pues ya qué.

Ah, y tomé una foto de Juan bailando, que creí nunca lo vería:

Juan y Érika bailando

Juan y Érika bailando

Yo me la pasé increíblemente bien en la boda de Juan y Érika. Pude platicar con Omar, que él sí se negó a bailar (habrá que esperar si conseguimos arrastrarlo cuando Enrique o yo nos casemos), y era tan intensa, sincera y contagiosa la felicidad de las dos familias de los novios, que era medio imposible no sentirse uno mismo abrumado y capturado por tanta y tan bien intencionada alegría. Cuando Fede, el hermano de Juan, tomó el micrófono ligeramente embriagado para dirigirle unas palabras a los novios, y a él también se le quebró la voz por la emoción y los tres galones de alcohol que había ingerido, yo sí ya pensé que era conspiración para hacerlo llorar a uno.

Yo no pude beber casi, porque era el chofer de los novios, y me tuve que esperar a que salieran los últimos para agarrar a los recién casados y llevarlos a su hogar. Eso, y ayudar a cargar las cosas de Érika que seguían en mi carro.

Yo conocí a Érika el mismo día que Juan lo hizo, en septiembre de 2006, hace casi siete años. Podría decir mucho acerca de sus cualidades, de su sentido del humor, y de su belleza. Pero creo que todo eso es superfluo, y queda desmedidamente encogido, comparado con lo más halagador que puedo decir de ella: que cambió a mi amigo, y que lo hizo para bien. El muchacho solitario, callado, antisocial e increíblemente tímido que siempre proyectaba un aura de que quería romperle su madre a alguien, Érika lo convirtió en alguien alegre, fuerte, seguro de sí mismo, y mucho más responsable con su vida académica y profesional de lo que probablemente nadie de los que lo conocíamos hubiera podido imaginar. Obviamente Érika no fue la única causa; pero que nadie me diga que no fue la principal.

Nada más por eso yo le estoy infinitamente agradecido, y sería suficiente para que tuviera mi lealtad y cariño incondicional de aquí a que cuelgue los tenis. Pero además en estos años he tenido la oportunidad (aunque ciertamente menos de lo que me hubiera gustado) de conocerla y convivir con ella, y me ha dado una muestra de la increíble persona que es, y pude entender cómo conquistó de forma inexorable a mi amigo, que también es una de las personas más extraordinarias que yo conozco. Y es por ello que quiero decir algo que es completamente innecesario además de altamente ridículo. Pero es que si no lo hiciera así, no sería yo.

Bienvenida a la familia, Érika.

Geeks + Érika

Geeks + Érika
Imprimir entrada Imprimir entrada

Quick Photo Editor

Limpié y subí también mi aplicacioncita para editar etiquetas en fotos; la pueden encontrar en Github.

Quick Photo Editor

Quick Photo Editor

Llevo años escribiendo pequeñas aplicaciones que me quedo nada más para mí. No creo necesariamente que le vayan a servir a absolutamente nadie más, pero el hacerlas públicas me obliga a tener el código en buen estado, legible, y a escribir el mínimo de documentación e infraestructura necesarias para que no sea nada más un archivo en Vala, Python, C o Perl aventado en algún directorio de mi $HOME, que años después no tengo ni idea de qué hacía o por qué lo había escrito.

Esta aplicación está escrita en Vala, que me parece ahí reside en gran medida el futuro de GNOME; es muy divertido de programar, y los programas son razonablemente rápidos y con poco uso de memoria (contrario a C#). Además, el código es muy legible y compacto; no al grado de Python, pero me parece que sí más que C#. El programita, aunque su funcionalidad a lo mejor le es inútil a nadie que no sea yo, sirve también para estudiar un ejemplo pequeño, pero funcional, de cómo escribir una aplicación con autotools, usando gettext para internacionalización, cómo instalarle iconos, y otras cosas de ese estilo.

Imprimir entrada Imprimir entrada

Gallery 3 Uploader

Total que limpié mi programita para subir fotos a Gallery3; es básicamente para mi uso privado, pero consideré que a lo mejor a alguien le podría resultar útil al menos el módulo que se comunica con el API REST de Gallery3.

Como lo iba a hacer público, lo limpié, le puse una interfaz gráfica, lo hice que hablara varios idiomas (inglés y español, porque no sé otros), y le puse toda la parafernalia para que se pueda compilar haciendo la santísima trinidad de ./configure && make && make install. El resultado no sólo es más agradable a la vista; ahora puedo seleccionar fotos en Nautilus, hacerles click derecho, y abrirlas con esta aplicacionciota, lo cual las manda a mi galería en línea.

Gallery 3 Uploader

Gallery 3 Uploader

Le faltan muchas cosas: por ejemplo, tronará sin decir nada si alguna de las etiquetas que espero encontrar en las fotos falta, pero ya es útil para mí, y espero lo sea también para alguien más. El programita está en Github: https://github.com/canek-pelaez/g3uploader.

Ahora sólo tengo que limpiar la aplicación que edita las etiquetas.

Imprimir entrada Imprimir entrada

Criminales e incompetentes

Y la Cassez al final fue liberada.

La Cassez

La Cassez

Escribí acerca del caso hace unos diez meses, y antes hace casi dos años. Sostengo todo lo que dije en esas dos entradas, excepto por una cosa: yo estaba convencido de la culpabilidad de la Cassez, y ahora la verdad no sé. Yo no soy abogado (ni he interpretado a alguno en televisión), pero he platicado con gente capaz y que sabe de estas cosas (además de que de hecho son abogados), y he encontrado las dos opiniones (que es culpable y que es inocente) siendo defendidas con argumentos bastante convincentes.

La simple verdad es que no sabemos qué pasó, porque estos criminales incompetentes hicieron un lodazal de las circunstancias que rodearon la captura de la francesa, y probablemente entonces nunca sabremos cuál es la exacta verdad de lo que ocurrió, y qué papel jugó Florance Cassez en los crímenes por los cuales se le encarceló. Y el punto de esta entrada que escribo es que eso ya no importa ahorita: fuera inocente o culpable, tenía que ser liberada por cómo actuó el gobierno de Fox al encarcelarla, y el “gobierno” de Calderón al llevar a cabo el juicio sabiendo cómo habían sido los hechos de su detención.

Los criminales más peligrosos aquí no son ni la francesa ni los secuestradores a los que pudo o no haber ayudado; los criminales más peligrosos aquí fueron los incompetentes que destrozaron cualquier posibilidad de realizar un juicio propio contra la francesa al querer hacer su captura un reality show para que se transmitiera en horario AAA de telerisa. Y son los más peligrosos porque contaban con la fuerza del estado para realizar sus fechorías.

El resultado de esta debacle es malo desde cualquier punto de vista, sea o no Florence Cassez inocente. Si era inocente, estos incompetentes criminales encarcelaron, vituperaron y acosaron a una mujer inocente durante más de siete años de su vida. Si era culpable, estos incompetentes criminales tuvieron que soltar a una criminal que causó dolor y angustia a las familias de las víctimas de la banda de los Zodiacos, porque escogieron deliberadamente no hacer propiamente su trabajo.

En cualquiera de los dos casos, la criminal incompetencia de estos imbéciles resultó en un crimen peor que cualquiera que pudo o no haber cometido la Cassez: el uso de la fuerza del estado con el objetivo no de servir a sus constituyentes, sino el tratar de manipular la opinión pública.

Sin duda una o más personas merecen terminar en la cárcel por todo esto; y (con gran probabilidad, lamentablemente) la Cassez no es una de ellas.

Imprimir entrada Imprimir entrada

La vida a través de una cámara digital

En 2004, la Universidad de Waterloo me aceptó para que fuera a hacer mi maestría. Entre otras cosas por eso empecé este blog, porque quería escribir acerca de mis estudios en el extranjero. Por las mismas razones, estuve considerando desde febrero de 2005 el comprar una cámara digital, y en marzo Sergio, el hermano de Enrique, me hizo el favor de comprarme una Sony Cybershot DSC-P200 en el gabacho, que en abril por fin tuve en mis manos.

Por supuesto ya saben qué pasó: Conacyt decidió que la computación no era un “área estratégica” para México y no me becaron, así que me quedé aquí y pasaron los siguientes ocho años de mi vida. De eso no es esta entrada.

La entrada es de que una vez tuve mi cámara, de inmediato decidí que necesitaba un sitio en línea para poner mis fotos a la vista de todo mundo. Agarré e instalé Gallery, que era (y es, me parece) el programa más utilizado para mantener una galería en línea, y de inmediato me desagradó. Era lento, usaba mucho (y mal) JavaScript (o a lo mejor era sólo que entonces los navegadores traían pésimos motores de JS), y además no me gustó cómo se veía. Era la versión 1 del programa.

Instalé entonces Coppermine, que fue básicamente el primer programa alternativo a Gallery que encontré, y lo usé unos cuantos meses. En mi casa usaba F-Spot, que está escrito en C#, y que en ese momento me parecía un extraordinario programa. Claro, tenía entonces del orden de doce fotografías, entonces F-Spot hasta rápido parecía.

Mi colección de fotografías digitales estuvo durante varios años manejada, y de alguna manera controlada, por F-Spot. El programa no es para nada malo, sólo sufre el mismo problema que todos los programas escritos en C#: la memoria que consumen es ridículamente alta, y se vuelven lentísimos con no mucha información. Como sea, eso no lo descubriría sino hasta años después.

En 2005 todavía no lo descubría, porque les digo que tenía como quince fotos, pero mi uso de F-Spot causó que tuviera que dejar de usar Coppermine. F-Spot nunca fue pensado para usar álbumes; previendo que eso sería el futuro, F-Spot favorecía mejor las etiquetas, y entonces una foto puede pertenecer a más de una etiqueta. Uno puede emular la funcionalidad de álbumes con etiquetas, pero no al revés. Lo grave con Coppermine no fue que no tuviera etiquetas; era que no podía ni siquiera mover fotos entre álbumes. Además no podía subirlas fácilmente desde F-Spot (y en ese entonces la red era mucho más lenta), y todo se combinó para que decidiera dejar Coppermine.

Entonces volví a revisar Gallery, y seguía básicamente igual de malo que antes; pero por suerte ya estaba disponible el primer beta de Gallery2, así que decidí probarlo. Me gustó mucho más, pero lo que me convenció de usarlo fue que F-Spot tenía un plugin para mandar un conjunto de fotos a un álbum de Gallery2. Eso hizo mi vida mucho más sencilla.

Yo soy muy neurótico con mis colecciones, de lo que sea. De música, de películas, de videojuegos, o de fotos, me interesa que todo esté meticulosamente etiquetado y catalogado. En F-Spot podía ponerles a las fotos un comentario (donde generalmente pongo el nombre de los que aparecen en la foto, o el lugar donde estoy en el peor de los casos), y el plugin que subía las fotos a Gallery2 se encargaba de hacer que dicho comentario apareciera también en Gallery2. Era la gloria.

Así estuve durante años, muy feliz, aunque con varias incongruencias en mi galería en línea. Como F-Spot tenía varias etiquetas “principales” (Favoritos, Escondidas, Eventos, Lugares y Gente), yo traté de emular eso en Gallery2… lo cual es una enorme pendejada, porque terminé aventando casi todo al álbum de eventos. Además, por alguna razón que no comprendo, creé un álbum llamado “Pruebas” que aparecía ahí en la página principal de la galería, y que lo hizo durante varios años.

Como sea, quitando esas cosas todo medio funcionaba, y lo que más me importaba era que la información que metía con F-Spot a mis fotos, se conservaba cuando las subía a Gallery2. Hasta que un día los desarrolladores de F-Spot decidieron cambiar las cosas: lo que yo metía en F-Spot como comentarios a las fotos, se subía como el título a Gallery, pero entonces decidieron cambiarlo a que fuera el comentario. Y entonces no se veía esa información a nivel del álbum en Gallery2, se veía sólo en la página de la foto. Con eso podría haber vivido; lo que era horrible era que el título ahora era el nombre del archivo, algo del estilo dsc00768.jpg.

Pude parchar a F-Spot para que funcionara como lo hacía antes (de algo tiene que servir que sepa programar), pero ya para entonces, en 2008, se había comenzado a volver muy lento con todas mis fotos. Ya tenía del orden de 2,000 fotografías, y al programa le empezaba a pesar muchísimo. Además comenzó a estar súper inestable, y tenía que estar deshabilitando cosas para que no tronara.

A pesar de todo eso lo seguí usando, y aunque no de forma perfecta seguía funcionando lo más importante para mí, que podía pasar las fotos de F-Spot a Gallery2 preservando la información de las mismas.

Y entonces me fui a Europa durante tres meses, de enero a marzo de 2009, y se me ocurrió regresar con 2,500 fotografías más.

Cuando, después de meses, había metido todas las fotos de mi viaje a F-Spot, el programa ya era básicamente inusable para mí. Y además de todo, la subida de las mismas a Gallery2 no era raro que fallara de formas extrañas, lo cual dejaba una imagen de error en lugar de la foto (aunque sí generaba correctamente la miniatura, porque F-Spot era el que la generaba antes de subir la foto, lo que hacía todavía más difícil descubrir cuándo había fallado la transferencia).

Pero lo que hizo que me deshiciera de F-Spot fue que cuando regresé de California en 2009, donde visité a mi cuate Eddie en San Francisco, había estado guardando la información GPS de donde había estado, y decidí tratar de sincronizarla con las fotos (para que cada foto marcara dónde la había tomado). Y entonces me di cuenta de que las horas de las fotos estaba desfasadas por 6 horas, porque los idiotas de F-Spot estaban suponiendo que mi cámara estaba en horario GMT (UTC+0), y que como México (y mi escritorio) estaban configurados en America/Mexico_City (UTC-6), eso quería decir que tenía que moverle al tiempo de casi todas mis fotos.

No tienen idea de cómo me encabroné, porque nunca me preguntó o dijo nada al respecto, y yo pensaba que ya no podía rescatar el tiempo original. Así que cerré por última vez en mi vida F-Spot, y seguí trabajando en mi doctorado, viajando, y tomando fotos que aventaba al primer directorio que podía, sin ni siquiera pensar en que sería bueno algún día subirlas a mi galería.

Hasta que mi disco duro tronó, como comenté hace unos días.

Ya que recuperé mis fotos, y las respaldé en cuanta máquina pude respaldarlas, comencé a pensar en cómo restituir mi colección de fotos en mi máquina (sin usar F-Spot, claramente, que además al parecer dejaron ya de mantener: la última versión salió en 2010), y cómo después tener un sistema independiente de cualquier programa (o al menos de cualquier programa no escrito por mí) para sincronizarlo con mi galería en línea, que además migré a Gallery3 cuando me quedé sin novia, sin casa, sin dinero y sin trabajo.

Primero descubrí que las fechas modificadas por F-Spot eran las dadas por la etiqueta EXIF DateTimeOriginal, pero por suerte la fecha original estaba guardada en CreateDate, así que sólo escribí un script que comparara las dos fechas y reemplazara la primera con la segunda si acaso diferían; para eso utilicé exiftool. Luego decidí mover la información de F-Spot a las fotos directamente. Los comentarios que con tanto cuidado había metido a las mismas durante mis años de usar el programa estaban en una base de datos SQLite, así que escribí un programita en Python que sacaba esa información, y la guardaba en las etiquetas EXIF UserComment, Title, ImageDescription y Caption, porque me pareció que era mejor ser redundantemente redundante. Usé exiftool de nuevo para manipular las etiquetas de las fotos.

Ya que hice eso, decidí probar Shotwell, el nuevo programa para manejar fotos de GNOME 3. El programa está escrito en Vala, que todo el lenguaje me parece un maravilloso hack, y me gustó mucho cómo funciona. Sólo que entonces vi que en algunas fotos aparecían mis comentarios como títulos, y en otras sólo el nombre del archivo. Investigando (tuve que bajar hasta el código fuente de Shotwell), vi que lo que pasaba es que Shotwell usaba la etiqueta Iptc.Application2.Caption para el título, porque al parecer es lo más estándar. Esa etiqueta no es EXIF, es IPTC, así que tuve que usar el programa exiv2 para reacomodar los comentarios en mis fotos. Por suerte, todo esto ya era sólo escribir otro script que hiciera toda la chamba. También vi que Shotwell usa la etiqueta Xmp.dc.subject para etiquetas internas del programa, así que decidí que con eso haría mis álbumes.

Shotwell maneja álbumes a la antigüita, todos basados en fechas. Se pueden mezclar fotos entre álbumes, pero decidí mandar eso completamente al carajo: a partir de ahora, mis álbumes están definidos por un rango continuo de tiempo, y a la chingada con todo lo demás. Además de álbumes, Shotwell maneja etiquetas, pero son ortogonales los primeros a las segundas. De cualquier forma, decidí arbitrariamente que la única etiqueta que tendrían mis fotos sería el nombre del álbum al que pertenecen.

Como los álbumes de Shotwell están basados en tiempo, automaticamente divide todo en años, estos en meses, y ya dentro de los meses hay álbumes que pueden ser de un instante en el tiempo (si tienen una sola foto), o de varios días. Decidí que también así funcionaría mi galería en línea, y así es como reconstruí mi colección de fotos. Fue poco trabajo, en general, porque casi todo se pudo automatizar con scripts. Sólo tuve que reacomodar algunas fotos que no tenían un álbum bien definido, y de paso acomodé las fotos igual en la jerarquía del sistema de archivos: tengo un directorio 2009, dentro de él un directorio “02 Febrero”, y dentro de él un directorio para cada álbum, que como dije describen rangos continuos de tiempo.

Jerarquía de archivos

Jerarquía de archivos

La única bronca es cuando se me parte un evento que cae entre el último día de un mes y el primero del siguiente (los años nuevos suelen ser así), pero decidí que eso no era terriblemente grave. De esta forma, Shotwell no se mete para nada con mis fotos; jamás les escribe nada. Sólo lee información de ellas, y las despliega bonito, con lo que espero evitar las broncas que F-Spot me daba. Además, la organización de mis fotos en el disco duro es virtualmente idéntica a la organización de mis fotos en Shotwell.

Shotwell

Shotwell

Con mi colección reorganizada una vez más, decidí que necesitaba reestructurar mi galería en línea también. Inicialmente pensé en borrar las fotos que ahí estaban y meterlas todas de nuevo, pero resultó imposible: tuve que borrar todo y empezar de cero. Por suerte Gallery3 ofrece un API REST para manipular la galería en línea; está súper chido, muy fácil de programar, y me permite olvidarme de que nadie más me diga cómo deben subirse los datos a mi galería. Hice un programita en Python (versión 3; el uso de UTF-8 me impide que pueda usar Python 2) que le pasa uno una lista de archivos JPG, y les saca la información que me importa (básicamente la fecha, el título en Iptc.Application2.Caption y el álbum en Xmp.dc.subject), y procede como sigue:

  1. Saca el año de la foto, y comprueba que exista un álbum principal en la galería en línea llamado como el año. Si no existe, lo crea con la descripción “Año 2009”, por ejemplo.
  2. Saca el mes de la foto (01, …, 12), y comprueba que exista un álbum con ese nombre debajo del álbum del año correspondiente. Si no existe, lo crea con la descripción “Mes de Febrero”, por ejemplo.
  3. Saca el álbum de la foto, lo normaliza (quita acentos y símbolos, convierte espacios en guiones, etc), y comprueba que exista un álbum con ese nombre debajo del álbum del mes correspondiente. Si no existe, lo crea con la descripción idéntica al álbum, sin normalizar.
  4. Escala la foto a 1024×768, de ser necesario, preservando todas las etiquetas EXIF, IPTC y XMP.
  5. Sube la foto escalada al álbum correspondiente, usando como nombre el nombre del archivo, como título la etiqueta Iptc.Application2.Caption, y como descripción una vez más el álbum.

Todo esto es automático, y además el programa es suficientemente listo como para comprobar la existencia de cada álbum exactamente una vez; si ya comprobó que existe, guarda esa información para usarla en subsecuentes fotos. Además, si hay un error en la red lo detecta, y vuelve a subir la foto en ese caso. De los miles de fotos que subí, sólo me generó tres o cuatro duplicados, que además fue muy sencillo detectar. Mi programa incluso usa colorcitos para avisar qué está haciendo:

Mi programita en Python

Mi programita en Python

Las consecuencias de todo esto son varias: mi galería en línea tiene entonces una organización virtualmente idéntica a mis fotos en Shotwell (y por lo tanto en mi disco duro):

Mi galería en línea

Mi galería en línea

Pero además las fotos, durante todo este proceso, guardan la información siempre en ellas mismas; las tengo respaldadas (como ya he dicho) en varias máquinas en éste y otros sistemas solares, así que si algo terrible ocurriera con mi computadora y con mi galería en línea, no tengo nada de qué preocuparme: sólo copio mi respaldo, y puedo reconstruir mi colección en Shotwell casi de inmediato (sólo tengo que renombrar cada álbum, pero eso es muy rápido porque cada foto tiene una única etiqueta con el nombre de su álbum), y puedo reconstruir mi galería en línea de forma automática (aunque tendría que esperarme un rato a que acabaran de subirse las fotos).

Por supuesto, para que todo esto funcione las fotos en primer lugar deben tener la información dentro de ellas. Con la parte de mi colección que ya tenía organizada esto fue muy fácil, porque todo estaba en la base de datos SQLite de F-Spot. Con las fotos que no he organizado significa meterles el título (Iptc.Application2.Caption), y el álbum (Xmp.dc.subject). El álbum no me preocupa mucho, porque al cabo eso lo puedo hacer después de acomodarlas en directorios, y correr un script (que ya escribí), que lee el nombre del directorio (quitándole el prefijo numérico de ser necesario) y se lo pone a la foto.

El título es más desmadre, porque tengo que ponerlo foto por foto. Así que hice lo que cualquier programador que se llame así mismo uno haría: escribí un programa. Lo escribí en Vala (se me antojó después de ver el código de Shotwell), y de una vez le escribí el código necesario para que también pueda girar las fotos acostadas. Que de hecho no se giran, sólo se escribe una etiqueta EXIF que especifica que esa foto debe mostrarse girada.

Quick Photo

Quick Photo

El programa es bastante rápido; al dar Enter en el campo de texto, inmediatamente se guarda la información en la foto (que la imagen en sí no se modifica, sólo sus etiquetas), y se mueve a la siguiente. Lo único malo es que tengo que usar el ratón para girar la foto a la izquierda o derecha; tengo que programarle que lo pueda hacer desde el teclado, y entonces será casi perfecto para mis necesidades. Lo pienso liberar (junto con mi progamita en Python); a lo mejor a alguien le resulta útil.

Ahora sólo tengo que hacer lo que durante años estuve evitando: organizar las miles de fotos que no he organizado. Ya organicé (y subí) un par de meses de 2010; me falta el resto de ese año, el 2011 y el 2012. En 2012 no tomé casi fotos (estaba encerrado escribiendo la tesis, o quedándome sin novia, sin casa, sin dinero y sin trabajo), pero en 2011 tomé cientos de fotos en mi viaje por alrededor del mundo. Además, ya con esta infraestructura, supongo que debería también organizar las fotos de mi celular; varias lo valen, me parece, pero eso sí me va a tomar tiempo. Mientras tanto, tengo todo respaldado de forma redundantemente redundante, y cada vez que termino con un álbum nuevo (que generalmente se traduce a un día o dos de fotos), vuelvo a respaldar todos los cambios.

Mi punto con todo este impresionante choro, es que cuando creé mi galería en línea, el programa me pidió que le pusiera un nombre a la galería. Yo, falto como siempre de imaginación, le puse “Fotos de Canek”; así se sigue llamando hasta estos días. Pero después de ponerle el nombre, me pidió que lo describiera, y en ese momento (hace casi ocho años), sin pensarlo demasiado le puse “La vida a través de una cámara digital”.

Respaldando, reparando y reorganizando todas las fotos que he tomado desde abril de 2005, me di cuenta de que no pude haber elegido una mejor descripción para mi galería en línea: de verdad refleja mucho (aunque no todo, y muchas veces ni siquiera lo más importante) de lo que ha sido mi vida en estos años, que cubren básicamente mi maestría y doctorado hasta ahora. Los invito a que ustedes también le echen un ojo, si así lo desean, a mi renovada y mejorada galería en línea; pocas cosas me enorgullecen y alegran más que poder compartir las imágenes que capturan los varios momentos significativos que he tenido, y los viajes que he realizado.

Ahora que volví a revivir casi todos los momentos que fueron capturados con mi camarita Sony, no pude sino llegar a dos conclusiones: la primera, que soy increíblemente afortunado. Y la segunda: que me la he pasado poca madre en estos años. Incluso considerando los momentos amargos, las experiencias tristes, y las inevitables heridas del corazón, en retrospectiva para mí el balance es claro: lo bueno supera con creces, y por mucho, a lo malo. Me he divertido como enano en todo este tiempo.

Y lo bailado, nadie me lo quita.

Imprimir entrada Imprimir entrada

Rudo rudo

Estaba oyendo música a un volumen que tal vez podría considerarse elevado, y las vibraciones de los bajos fueron tales que causaron que se cayeran mis llaves de una repisa. Después de leer eso, alguien que no me conozca probablemente pensaría que estaba escuchando heay metal, hip-hop, o tal vez siendo conservadores, buen y viejo rock & roll.

Los que me conocen no se extrañarán de saber que no escuchaba heay metal, ni hip-hop, ni buen y viejo rock & roll. Estaba escuchando el sencillo Inside Identity, de Black Raison d’être. Nada más de leer el título de la canción y el nombre de la banda, alguien que no supiera de qué va la cosa podría pensar que eso no tiene nada de especial.

Así que para ahorrarles la búsqueda en Google, ésta es la portada del sencillo:

Black Raison d'être: Inside Identity

Black Raison d’être: Inside Identity

Por supuesto, también pueden verla/oírla en el tubo. Y antes de que nadie me pregunte, , ya vi la serie.

Y , me encanta.

Imprimir entrada Imprimir entrada

Los 500 GB

Hace unos meses se me murió un disco duro de 500 GB. No había nada ahí que no tuviera de forma redundante en otra máquina (y en algunos casos en varias otras máquinas), y lo que no estaba de forma redundante no era realmente importante para mí, o lo podía bajar de nuevo de Internet.

Todo, excepto varias decenas (si no centenas) de fotografías.

Mi galería en línea (la que acabo de actualizar, por cierto) la he tenido abandonada desde hace mucho tiempo. Regresé de mi primer viaje a Europa en marzo de 2009 con varias centenas de fotografías, y esto causó que me tardara mucho en catalogarlas y ordenarlas; mis subsecuentes viajes en 2010 agravaron la situación, y mi viaje monstruo de seis meses en el 2011 causaron que sencillamente dejara de pensar en que en algún momento de mi vida tenía que organizar mis fotos. Si lo hacía, terminaba tirado en el suelo en posición fetal respirando a través de una bolsa de papel.

Lo que terminó pasando fue que aventaba los nuevos archivos de mis fotos en varios directorios, y me olvidaba de ellos, sin jamás pensar “hey, tal vez sería buena idea respaldarlos”. Y entonces un día mi disco duro de 500 Gigabytes se murió, llevándose mi $HOME y otra partición que venía utilizando desde hacía años.

Esto ocurrió en las etapas finales de la escritura de mi tesis doctoral, así que no tuve tiempo de llorar por mis archivos perdidos. Mis documentos (incluyendo los de la tesis… y todos los demás) están triplemente respaldados en mi laptop, mi servidorcito Atom, y una máquina corriendo en una bóveda secreta debajo del océano Antártico, así que sólo cloné mis documentos en un $HOME vacío, y seguí escribiendo la tesis de doctorado.

No tiré el disco, porque pensé que tal vez un día existiría la posibilidad de recuperar la información en él.

Cuando acabé la tesis de doctorado, busqué en línea qué podía hacer para recuperar la información de un disco que estaba, aparentemente, morido. Debo resaltar que el disco duro en cuestión hacía los ruidos normales que hace un disco cuando se conecta y recibe poder, pero no aparecía en ningún lado: el BIOS de mi computadora no lo detectaba, y tampoco ocurría si lo conectaba por un cosito SATA→USB.

Internet no fue de gran ayuda, tal vez porque mi Google-fú no fue tampoco el mejor del mundo: básicamente pregunté “¿cómo le hago para revivir un disco duro morido?”. La respuesta en general fue que la mejor oportunidad (si no había click de la muerte), era agarrar el modelo del disco duro, y buscar en la red para comprar únicamente el PCB, el circuito (realmente una computadora chiquita) que está atornillado debajo. La idea era conseguir un reemplazo idéntico, y orar que con eso se pudiera recuperar la información.

Dado que ya entonces andaba corto de dinero, ni siquiera me pasó por la mente; por lo que leí había que pedir el famoso PCB a China, y nada más el envío iba a costar una lana, si es que acaso encontraba el modelo exacto que necesitaba, y sin ninguna garantía de que al final funcionara. Así que lo dejé pasar, y seguí con mi vida.

Que en este caso consistió en quedarme sin novia, sin casa, sin dinero y sin trabajo.

Cuando por fin me medio recuperé de eso, de las cosas que hice para distraerme fue actualizar y configurar propiamente mi media center. No recuerdo cuándo fue la última vez que platiqué de él, pero el año pasado lo cambié de Moovida a XBMC, que resultó ser de los programas más impresionantes en el mundo del software abierto que he visto en mi vida.

Organicé cuidadosamente mis series de televisión y mis películas, así como mi animé y mi música. Y cuando el polvo se asentó, resultó que el espacio libre en mis discos duros estaba peligrosamente bajo. Lo cual es obvio, porque había perdido 500 Gigabytes de espacio unos meses antes.

Cuando la situación se hizo insostenible, y al ver el ridículo precio al que han llegado los discos duros de 2 Terabytes, le hablé a Enrique y fuimos a que me comprara uno. El disco en sí mismo era necesario; pero además mi media center tenía un disco duro de 500 GB, y pensé que con suerte sería el mismo modelo del muerto, y por lo tanto que podría intercambiar los PCB de ambos, y milagrosamente recuperar mi información.

Así que el día que compré el disco de 2 Terabytes, agarré mi media center y lo abrí para cambiarle el disco. Cuando saqué el viejito, de inmediato vi que no me iba a servir para reparar el otro; son modelos distintos. Sin embargo, eso me hizo pensar que tal vez ahora no sería tan mala idea buscar el PCB del disco malo en la red, y eso hice.

Eso fue lo que debí hacer primero.

Resulta que ese modelo de disco duro en particular (de hecho de firmware) tiene un fallo muy conocido en la red: cuando un contador en el firmware llega a cierto número, el disco duro se apendeja y se queda ahí atorado en el estado “busy”, lo que hace que el problema sea mundialmente conocido como “Seagate BSY state”. Googléenlo, si quieren.

El punto es que en todos lados confirmaban que la información del disco estaba intacta, que era posible recuperarla, y de hecho que el mismo disco duro podía seguir siendo usado. El único problema es que necesitaba soldar un cable en especial para conectar el disco duro a un puerto serial, o canibalizar un cable de Nokia que ya tiene ese circuito integrado.

Hay una razón por la que no soy ingeniero: detesto estar peleándome con cablecitos. Por eso me ligué dos ingenieros el primer semestre de mi maestría para que con ellos pasara Arquitectura de Computadoras; yo me encargué de todo el software, y dejé que ellos se pelearan con el hardware.

Así que me puse a buscar el cable, un famoso Nokia CA-42, que para acabarla de amolar estoy seguro que tuve en algún momento de mi vida. En Amazon cuesta 7 dólares, pero no lo envían a México; en MercadoLibre está en 150 pesos, pero el envío cuesta otros 150. Así que me decidí ir al Centro a buscarlo. Lamentablemente sólo pude ir hasta hoy, porque estuve trabajando en un artículo hasta ayer en la noche. Y hoy es domingo, así que un montón de negocios estaban cerrados.

Por suerte lo encontré, pero vi con algo de temor que no era un cable de Nokia original, sino un clon chino, y ya había leído que a veces con cables no originales el procedimiento no servía. Como sea, lo compré (me costó 100 pesos), y regresé a mi casa. Voy a platicar muy por encima que es lo que tuve que hacer, pero pueden verlo a detalle aquí, aquí, o en el tubo, si les da hueva leer.

El disco duro seguía igual de muerto que meses atrás:

Disco duro muerto

Disco duro muerto

El PCB es lo que está debajo:

PCB

PCB

Lo quité:

PCB suelto

PCB suelto

Le puse un cartoncito encima de la cabeza, para que el PCB no se comunique con el disco duro, el mismo no responda, y entonces el firmware no entre en el estado ocupado:

Disco duro con cartón

Disco duro con cartón

Y volví a poner el PCB:

PCB con cartón

PCB con cartón

Después desmadré el cable de Nokia chafa, y lo conecté a un viejo cable para conectar CDs a tarjetas de sonido, para poder conectarlo fácilmente al disco duro:

Cable Nokia chafa

Cable Nokia chafa

Y finalmente prendí el disco duro, lo conecté por USB a la computadora con el cable, y comunicándome serialmente con él le mandé comandos para revivirlo. Pueden verlo a detalle en las páginas que ligo (y todo se puede hacer con Linux, sólo reemplacen Hyperterminal con minicom); la verdad no me importa mucho qué hicieran en particular los famosos comandos.

Lo que me importa es que funcionaron, y que recuperé mi disco duro.

Más importante aún es que toda la información en el mismo sigue ahí, y la estoy respaldando todita en este momento. Lo principal eran las fotos, pero de una vez estoy copiando todo, y a partir de ahora sí voy a mantener mis fotografías también triplemente respaldadas.

Y encima ahora tengo un disco duro extra de 500 GB. Y un cable Nokia chafa para hacer todo de nuevo, si acaso es necesario.

Imprimir entrada Imprimir entrada

El Hobbit: Un Viaje Inesperado

Hace dos semanas, después de quedarme sin novia, sin casa, sin dinero y sin trabajo, fui a ver The Hobbit: An Unexpected Journey. No había escrito al respecto por razones.

Se aplican las de siempre.

The Hobbit: An Unexpected Journey

The Hobbit: An Unexpected Journey

Mientras que en las películas del Señor de los Anillos uno sentía siempre que los actores andaban apresurados tratando de decir todas sus líneas, y de que los realizadores hacían malabares y magia negra para poder meter lo más posible de los libros en el limitado espacio que da una película de menos de tres horas (las versiones extendidas son mucho más relajadas en ese sentido), en esta primera entrega de la que será la triología del Hobbit ocurre precisamente lo contrario.

Aquí, como van a tener unas ocho horas en total, pueden meter lo que se les dé la gana, y así lo hacen. Me sorprendió de hecho que no pusieran partes del Silmarillion. Pero mejor cierro la boca; no quiero darles ideas.

Antes de continuar, debo hacer notar que yo prefiero las películas del Señor de los Anillos a las novelas, por la sencilla razón de que las novelas son aburridísimas, por muy bonito que estén escritas. Como decía Omar: “dejé el libro cuando después de 100 páginas Frodo aún no salía de su casa; a lo mejor en las siguientes 100 iba a la tienda por cigarros”. Además, me parece que los cambios que agregan a la historia y el diálogo (“Gondor has no King. Gondor needs no King.”) las llenan de mucha más asombrosa asombrosidad.

Como sea; dado que yo nunca he sido gran fan de la literatura infantil (y no, no considero a Harry Potter literatura infantil; al menos no en su mayoría), probablemente porque (como Omar me hizo notar) realmente no hay tal cosa en español, The Hobbit nunca fue de mis libros favoritos. Me gusta el final, la Battalla de los Cinco Ejércitos, y hay partes divertidas sin duda alguna (la parte donde los elfos de Mirkwood interrogan a Thorin es hilarante), pero en general es un librito que me da más bien hueva (magistralmente escrito, eso sí).

Por lo tanto no es de extrañarse que me haya gustado más la película, que las primeras trece páginas del libro en las que está basada.

Nada más ver de nuevo a un montonal de miembros del elenco del Señor de los Anillos haría que valiera la pena verla; pero además hacen mucho más emocionante y llena de acción la historia, y llenan el amplio tiempo libre que tienen poniéndole cosas de la mitología de la Tierra Media. En particular, Radagast es más allá de cagado, y es muy divertido que Gandalf “no recuerde” cómo se llaman los otros dos magos, los azules.

Mención aparte merece el juego de acertijos que Bilbo y Gollum tienen. Los realizadores merecen que les hagan una estuata por convertir una parte más bien ridícula y sinsentido del libro, en una escena muy divertida, muy emocionante, y con unas actuaciones espectaculares de Andy Serkis y Martin Freeman.

Que por cierto, me parece que la película me gustó tanto en gran medida por Arthur Dent encarnando de forma perfecta a Bilbo Baggins. Los enanos son muy cagados, pero Freeman hace suyo totalmente al personaje de Bilbo.

Está padre que el nerdgasmo que nos dieron con la triología del Señor de los Anillos se vaya a extender con otra triología (aunque qué descarada manera de ordeñar dinero), y me alegra mucho que esta primera parte haya salido tan bien. Sin duda las otras dos películas tendrán también toneladas de material que ni siquiera era mencionado en el libro pero, hey, yo no me quejo.

Así que vayan y véanla (si acaso no lo han hecho). A mí me hizo mucho bien ir a verla al cine.

Imprimir entrada Imprimir entrada

Homeless

A finales de septiembre Isabel y yo nos separamos. Dado que mi beca del doctorado había terminado en julio, y que me había ido a vivir con ella en enero, esto significó que además de quedarme sin novia, me quedé sin casa, sin empleo y sin dinero.

Si le hubiera ocurrido a alguien más, probablemente me hubiera reído de esa persona inmisericordemente. Como me ocurrió a mí, no lo encontré tan gracioso, la verdad.

Después de más de dos meses tratando de reorganizar mi vida, me encuentro una vez más en mi departamento, con unas trece millones de cajas que no he desempacado (y que, además, como no las empaqué yo, cada vez que abro una es toda una sorpresa; hagan de cuenta que navidá adelantada), y con un trabajo (temporal) que comenzaré en enero. Los trámites para doctorarme tuvieron que ser pospuestos algo de tiempo, obviamente.

Espero que eso explique también porqué no he escrito casi nada en mi blog en todo este tiempo. Estar de homeless no le da a uno muchas oportunidades para estarse expresando en prosa.

Por supuesto, tengo un plan; yo siempre tengo un plan. Es por eso que cuando las cosas no me salen como yo quiero (que es, como era de esperarse, bastante común), no entro en pánico. La verdad, ni siquiera me preocupo mucho; soy demasiado irreverente como para preocuparme por eventualidades como que de repente no tengo casa, ni trabajo, ni dinero, ni novia. No digo que no me importe; digo que no me preocupo. Mejor me ocupo de resolverlo (lo que tenga solución); preocuparse nunca le ha servido a nadie de nada, me parece.

Como sea, hice lo que tenía que hacer (o lo único que podía hacer, desde mi punto de vista) en este tiempo, y me parece que ya todo está cayendo en su lugar. Ya tengo casa, ya tengo trabajo (que comenzaré en menos de un mes), y no tengo dinero, pero pues familia y amigos nunca faltan, y cuando vivo solo soy sorprendentemente frugal. Modo ahorro de energía, como suelo decir.

Tener novia sí va a llevarme más tiempo, me parece; y la verdad creo que no estoy de humor por el momento, y presiento que así estaré al menos durante el futuro inmediato. Lo cual tiene sus ventajas, porque como dije arriba no tengo dinero, y tener novia suele ser caro.

Y del futuro, pues ya veremos. Como dije, no estoy preocupado; y además (como siempre) tengo un plan. Que probablemente no vaya a salir como yo quiero, pero tampoco me preocupo de eso, porque en tal caso tengo otro plan. Y así sucesivamente; it’s turtles, all the way down!

Imprimir entrada Imprimir entrada

Y el claro ganador es C

Ya no seguí con mis comparaciones entre C y Java porque, de verdad, he andado demasiado ocupado, con cosas académicas, de trabajo y personales. Pero hoy por fin terminé de escribir las pruebas unitarias para las estructuras de datos que estamos viendo en mi curso de ídem, y como la última que hemos visto fueron árboles rojinegros, decidí echarle un ojo a mis pruebas.

Para los que no lo sepan, los árboles rojinegros son árboles binarios autobalanceados, lo que en su caso particular quiere decir que un camino simple (sin regresarse nunca) de cualquier nodo a cualquiera de sus hojas siempre tiene el mismo número de nodos negros. Eso se traduce (por una propiedad de los árboles rojinegros que dice que un nodo rojo siempre tiene dos hijos negros) a que la diferencia más grande de longitudes entre ramas es que una tenga k nodos, y la otra 2k (una rama con puros nodos negros, otra con negro, rojo, negro, rojo, etc.) La estructura permite agregar y eliminar elementos con complejidad en tiempo acotada por la altura del árbol; que siempre esté balanceado garantiza que dicha complejidad es siempre O(log n). Los árboles rojinegros son una estructura de datos utilizada en todos lados por (como veremos ahorita) su espectacular desempeño; en particular, el kernel de Linux incluye una implementación desde mi punto de vista preciosa y humilladoramente elegante; la pueden checar en /usr/src/linux/lib/rbtree.c.

No voy a poner mi código para agregar o eliminar elementos a árboles rojinegros; no sólo porque mis alumnos aún no entregan su práctica, sino además porque es demasiado engorroso. No es ciencia de cohetes, pero sí hay suficientes casos como para que uno tenga que ir haciendo dibujitos en papel para no perderse en qué punto del algoritmo nos hayamos. Como sea, terminé de escribir el código en Java y C como siempre, y corrí unas pruebas con un millón de elementos (me pueden pedir el código, si quieren).

Ambas implementaciones le ganan por mucho a MergeSort; me imagino que algo tendrá que ver el uso de memoria (MergeSort crea el equivalente a log n listas con n elementos cada una durante la ejecución del algoritmo, mientras que los árboles rojinegros usan O(1) de memoria al agregar). Ambas son básicamente idénticas, incluyendo que usan recursión en el paso interesante: es recursión de cola, por lo que sencillamente lo pude haber reescrito iterativamente; pero como les digo el algoritmo ya es lo suficientemente engorroso como para que lo complicara aún más con un acumulador. La única diferencia discernible en el uso de cada versión, es que guardo la raíz cada vez que agrego en un elemento en C; tengo que hacerlo para no tener que andarla persiguiendo cada vez. En Java, al usar el diseño orientado a objetos, siempre tengo una variable de clase para la raíz.

Con Java, el algoritmo tarda 0.777676317 segundos (en promedio) en agregar 1,000,000 (un millón) de elementos. C sin optimizaciones tarda 0.376824469 segundos; con optimizaciones tarda 0.183850452 segundos. Por supuesto ambas versiones son genéricas; la de Java propiamente usando genéricos, la de C usando void* como tipo de dato, y pasando una apuntador a función para hacer comparaciones. Con 10,000,000 (diez millones) de elementos la diferencia es todavía más abismal; Java tarda 20.266719661 segundos, mientras que la versión en C tarda 1.881134884 segundos; pero esto ya no me extraña, dado que como ya había visto la última vez, con 10,000,000 de elementos, Java no puede evitar no utilizar el swap.

No me queda claro por qué C gana; dado que MergeSort también es recursiva, y ahí Java le ganaba a C, hubiera esperado que en el caso de los árboles rojinegros pasara lo mismo. Lo que sí es que el desempeño de la estructura de datos es espectacular, y a mí me parece de las estructuras más bonitas y poderosas que existen. Por supuesto los diccionarios (¿alguien sabe una mejor traducción para hash table?) también son muy padres; pero siempre está el hecho de que en el peor de los casos el buscar y el eliminar tardan O(n). Y como mis experimentos con QuickSort me recordaron hace unas semanas, el peor caso (o uno suficientemente malo) siempre anda asomándose detrás de las esquinas.

Imprimir entrada Imprimir entrada

Más sobre carreritas entre Java y C

Total que Omar me pidió mi código para hacer él mismo pruebas, lo que me obligó a limpiarlo un poquito. Entre las cosas que hice al limpiar mi código, fue cambiar cómo llenaba los arreglos y listas; originalmente los estaba llenando así (en Java):

import java.util.Random;
// ...
    Random r = new Random();
    // ...
    int n = r.nextInt() % 100;

Y así en C:

#include <stdlib.h>
// ...
    srand((unsigned int)time(NULL));
    // ...
    int n = rand() % 100;

El cambio que hice fue el reemplazar el número mágico 100 con N en el módulo al generador de números pseudo aleatorios, donde N es el número de elementos. Esto cambió radicalmente los resultados; para ponerlo en perspectiva, aquí están los resultados en C de una corrida (suelen ser todos muy similares) con módulo 100 (incluí el qsort de glibc a sugerencia de otro lector):

Tiempo QuickSort (normal): 46.125371409 segundos
Tiempo QuickSort (int): 6.318009789 segundos
Tiempo QuickSort (memcpy): 29.476040174 segundos
Tiempo QuickSort (long): 4.455134060 segundos
Tiempo QuickSort (glibc qsort): 0.182938334 segundos
Tiempo MergeSort (lista): 5.097989382 segundos
Tiempo MergeSort (dlista): 3.018067951 segundos

En Java los números son:

Tiempo QuickSort (int): 2.231362337 segundos
Tiempo QuickSort (genéricos): 35.452854731 segundos
Tiempo MergeSort: 2.599635738 segundos

Haciendo módulo N, los resultados son, en C:

Tiempo QuickSort (normal): 0.558278904 segundos
Tiempo QuickSort (int): 0.117254171 segundos
Tiempo QuickSort (memcpy): 0.279380050 segundos
Tiempo QuickSort (long): 0.121708671 segundos
Tiempo QuickSort (glibc qsort): 0.220501083 segundos
Tiempo MergeSort (lista): 5.311177622 segundos
Tiempo MergeSort (dlista): 3.196143267 segundos

Y en Java:

Tiempo QuickSort (int): 0.172914364 segundos
Tiempo QuickSort (genéricos): 0.578500354 segundos
Tiempo MergeSort: 2.15927644 segundos

Al inicio me súper saqué de onda, pero no tardé en encontrar la respuesta. Si N=1000000 (un millón), y cada entero en mi arreglo está entre 0 y 99 (inclusive), eso quiere decir que en promedio cada elemento del arreglo está repetido 10,000 veces, lo que hace que la probabilidad de encontrar un pivote malo (el mínimo o máximo del arreglo) sea mucho mayor que si estuvieran mejor distribuidos los valores. Es por ello que cuando cambio a hacer módulo N, todas mis versiones del algoritmo mejoran, algunas por varios órdenes de magnitud; en general el pivote es un buen pivote. Esta no es toda la historia, por supuesto; de ser así sólo tendría que elegir un pivote aleatorio y todo funcionaría más rápido, y como expliqué en la entrada anterior, en mis pruebas usar un pivote aleatorio no mejora sustancialmente el desempeño del algoritmo. Me parece que teniendo tantos elementos y tan poquitos valores (comparativamente) para inicializarlos, sencillamente ocurre muchas veces que un subarreglo tiene muchos elementos iguales, y entonces incluso un pivote aleatorio tiene mucha probabilidad de ser el mínimo o el máximo.

Sin embargo, esto no parece molestarle a qsort de glibc; es endiabladamente rápido. Incluso en mi versión módulo 100 corre en menos de medio segundo, y de hecho es la única implementación del algoritmo que lo hace. Como le comentaba al lector que me recomendó probar con qsort, la complejidad del mismo es más de un orden de magnitud mayor que mis versiones: qsort son unas 250 líneas de código no muy fácil de leer a comparación de 22 líneas en cada una de mis versiones. La complejidad radica en primer lugar en cómo encuentra el pivote: selecciona la mediana entre el primer, medio y último elementos del subarreglo (ordenándolos de paso); y en segundo lugar en que no hace recursión: utilizando una pilita se ahorra el estar gastando registros de activación, y mete todo dentro de un while.

No sé si sea eso lo que lo hace el ganador indiscutible de las distintas implementaciones; lo que sí sé es que glibc lo comenzaron a escribir a inicios de los ochentas, y que en estos treinta años le han metido todas las optimizaciones que se les han podido ocurrir. De cualquier forma, estoy ahora más contento con mis implementaciones en C: si el arreglo tiene sus valores bien distribuidos, cada una de mis versiones en C le gana a su equivalente en Java, y de hecho mi mejor versión genérica en C (que tiene exactamente la misma firma de qsort, por cierto) es sólo 0.05 segundos más lenta. Que no está nada mal, si me permiten decirlo.

De cualquier forma, los arreglos con muchos elementos repetidos son una entrada válida de QuickSort, así que Java sigue siendo bastante bueno en su versión para enteros (por cierto, mi versión genérica era desde el inicio mejor que la de Java; lo que pasa es que en Java el módulo incluye valores negativos, y entonces Java tenía el doble de valores distintos que la versión de C), y no terriblemente lento en comparación en las otras versiones. Para cosas genéricas C le puede ganar fácilmente; pero usando enteros nada más, Java le gana de calle a mi versión en C para arreglos con muchos elementos repetidos. Y de hecho implementando una versión iterativa de QuickSort, no dudo que Java le diera una buena pelea a qsort (un torito, para el que quiera animarse).

La otra conclusión importante es ver la enorme diferencia que puede significar el tipo de entrada para QuickSort; es por eso que Java utiliza MergeSort en general en sus algoritmos de ordenamientos. Es más lento en el mejor caso de ambos, sin duda; pero siempre es O(n log n). Además es estable (no cambia el orden de dos elementos iguales), lo que también está padre.

Dado que ya limpié el código gracias a Omar, lo pueden bajar de aquí. Dado que son implementaciones de una estructura y dos algoritmos que probablemente sean los más conocidos en el mundo, ni siquiera le pongo licencia a los archivos.

Imprimir entrada Imprimir entrada

Carreritas entre Java y C

Estoy enseñando Introducción a Ciencias de la Computación II (mejor conocida como ICC-2) por primera vez en mi vida, en gran medida por un ligero error administrativo. La verdad es que me estoy divirtiendo como enano (al parecer, tristemente disfruto yo más el curso que mis alumnos), y entre las cosas divertidas que decidí hacer fue el darle a mis alumnos la oportunidad de hacer las prácticas en C (en lugar de Java) por un punto extra durante el curso, o medio si hacen al menos la mitad. Desafortunadamente, ninguno de mis alumnos me ha entregado una práctica escrita en C.

Como sea, para poder dejarles las prácticas en C a mis alumnos, primero tengo que hacerlas yo, y eso es en gran medida la razón de que me esté divirtiendo tanto. Por supuesto también hago las prácticas en Java; como ICC-2 es en gran parte estructuras de datos, esto también significa ver las características novedosas de Java, como son iteradores y genéricos. Lo cual también es muy divertido; especialmente cuando puedo comparar los dos lenguajes en cosas como velocidad de ejecución.

Como es necesario siempre que uno ve arreglos y listas, les dejé a mis estudiantes que programaran QuickSort y MergeSort. Yo recuerdo que como estudiante tuve que programar esos algoritmos al menos tres veces: la primera en ICC-2, la segunda en Análisis de Algoritmos, y ahora sí que como dice la canción, la tercera por placer. También recuerdo claramente que QuickSort me parecía el mejor de ambos algoritmos; la inocencia de tener veinte años, supongo.

Total que implementé ambos algoritmos en C y en Java, y me llevé una sorpresa con los resultados. Voy a relatar lo que resultó de investigar porqué las diferencias en velocidades, que la verdad yo no termino de entender.

Aquí está QuickSort en Java:

public static void swap(T[] a, int i, int j) {
    if (i == j)
        return;
    T t = a[j];
    a[j] = a[i];
    a[i] = t;
}

public static < T extends Comparable < T > >
                 void quickSort(T[] a) {
    quickSort(a, 0, a.length-1);
}

private static < T extends Comparable < T > >;
                  void quickSort(T[] a, int ini, int fin) {
    if (fin - ini < 1)
        return;
    int i = ini + 1, j = fin;
    while (i < j)
        if (a[i].compareTo(a[ini]) > 0 &&
            a[j].compareTo(a[ini]) < = 0)
            swap(a, i++, j--);
	else if (a[i].compareTo(a[ini]) <= 0)
	    i++;
	else
	    j--;
    if (a[i].compareTo(a[ini]) > 0)
        i--;
    swap(a, ini, i);
    quickSort(a, ini, i-1);
    quickSort(a, i+1, fin);
}

Y aquí está en C:

inline static void
swap(void** a, int i, int j)
{
	if (i == j)
		return;
	void* t = a[i];
	a[i] = a[j];
	a[j] = t;
}

void
quicksort(void** a, int n, func_compara f)
{
	quicksort_aux(a, 0, n-1, f);
}

static void
quicksort_aux(void** a, int ini, int fin, func_compara f) {
	if (fin - ini < 1)
	    return;
	int i = ini + 1, j = fin;
	while (i < j)
		if (f(a[i], a[ini]) > 0 &&
                    f(a[j], a[ini]) < = 0)
			swap(a, i++, j--);
		else if (f(a[i], a[ini]) <= 0)
			i++;
		else
			j--;
	if (f(a[i], a[ini]) > 0)
		i--;
	swap(a, ini, i);
	quicksort_aux(a, ini, i-1, f);
	quicksort_aux(a, i+1, fin, f);
}

(En mi blog el código aparece bonito con destacamiento de sintaxis; no sé cómo aparecerá en RSS, pero dudo que bonito.)

Con el código así, la versión en Java necesita 33.4 segundos (en promedio en mi máquina) para ordenar un arreglo de un millón (1,000,000) de elementos aleatorios. Sin optimizaciones, la versión en C tarda 114.7 segundos; lo cual es una diferencia brutal, si me permiten decirlo. Con la mejor optimización (-O3; el resultado es idéntico a -Ofast), esta velocidad baja a 44.85 segundos; mucho mejor, pero de cualquier forma más lento que con Java.

La versión en C utiliza void** como tipo del arreglo, y recibe un apuntador a función f justamente para emular los genéricos de Java; la idea es que el QuickSort de C pueda ordenar arreglos de cualquier tipo de elemento. Nada más por completez, incluyo la definición del tipo func_compara, así como la implementación usada para estas pruebas:

typedef int  (*func_compara)      (const void*   a,
				   const void*   b);

int
compara_enteros(const void* a, const void* b) 
{
	int aa = *((int*)a);
	int bb = *((int*)b);
	return aa - bb;
}

Mi primera impresión fue que estos “genéricos” en C (altamente basados en la biblioteca GLib) le estaban dando en la madre a la velocidad de ejecución de mi implementación en C. El andar siguiendo los apuntadores, sacar el valor de las referencias en compara_enteros, y los castings probablemente eran la razón (pensaba yo) de que mi versión en C fuera (ligeramente) más lenta que la de Java. Así que hice trampa y volví a implementar QuickSort, pero esta vez nada más para enteros:

inline static void
swap_int(int* a, int i, int j)
{
	if (i == j)
		return;
	int t = a[i];
	a[i] = a[j];
	a[j] = t;
}

void
quicksort_int(int* a, int n)
{
	quicksort_int_aux(a, 0, n-1);
}

static void
quicksort_int_aux(int* a, int ini, int fin) {
	if (fin - ini < 1)
	    return;
	int i = ini + 1, j = fin;
	while (i < j)
		if (a[i] > a[ini] &&
                    a[j] < = a[ini])
			swap_int(a, i++, j--);
		else if (a[i] <= a[ini])
			i++;
		else
			j--;
	if (a[i] > a[ini])
		i--;
	swap_int(a, ini, i);
	quicksort_int_aux(a, ini, i-1);
	quicksort_int_aux(a, i+1, fin);
}

No muy sorprendentemente, esta versión le partió completamente su madre a la de Java: tarda en promedio 6.35 segundos. Hago notar que los elementos del arreglo son generados aleatoriamente; por lo que el escoger un pivote aleatorio entre ini y fin no serviría (en teoría) de nada. Ciertamente no marcó ninguna diferencia en mis pruebas.

Aunque esta versión es bastante rápida, estaba haciéndo muchísima trampa. De nada (o muy poco) me sirve un QuickSort rapidísimo, si voy a tener que reimplementarlo cada vez que cambie el tipo de mis arreglos. Así que me puse a pensar cómo mejorar una versión “genérica” en C. La respuesta es que sí se puede, pero es bastante feo desde mi punto de vista.

La idea es sencillamente utilizar aritmética de apuntadores, y al intercambiar elementos el copiarlos usando la memoria:

inline static void
swap_memcpy(void* a, int i, int j, size_t s, void* t)
{
	if (i == j)
		return;
	memcpy(t, a+(i * s), s);
	memcpy(a+(i * s), a+(j * s), s);
	memcpy(a+(j * s), t, s);
}

void
quicksort_memcpy(void* a, int n, size_t s, func_compara f)
{
	void* t = malloc(s);
	quicksort_memcpy_aux(a, 0, n-1, f, s, t);
	free(t);
}

static void
quicksort_memcpy_aux(void* a, int ini, int fin,
                    func_compara f, size_t s, void* t) {
	if (fin - ini < 1)
	    return;
	int i = ini + 1, j = fin;
	while (i < j)
		if (f(a+(i*s), a+(ini*s)) > 0 &&
                    f(a+(j*s), a+(ini*s)) < = 0)
			swap_memcpy(a, i++, j--, s, t);
		else if (f(a+(i*s), a+(ini*s)) <= 0)
			i++;
		else
			j--;
	if (f(a+(i*s), a+(ini*s)) > 0)
		i--;
	swap_memcpy(a, ini, i, s, t);
	quicksort_memcpy_aux(a, ini, i-1, f, s, t);
	quicksort_memcpy_aux(a, i+1, fin, f, s, t);
}

Esta versión es superior a la de void** ya que puedo pasarle un arreglo de tipo int* directamente, y funciona sin problema; la versión void** necesita por fuerza que le pase un arreglo de apuntadores al tipo que me interesa; en otras palabras, tengo que pasarle un int**, y además tengo que hacerle cast a void**. Además de esto (que no es poco), es más rápido que la primera versión en C, y más rápido que la versión en Java. No por mucho, pero más rápido: tarda 27.5 segundos con un millón de elementos.

Por lo demás, está bastante fea; necesito por fuerza el tamaño del tipo que me interesa ordenar (porque el arreglo lo veo como un chorizo enorme de bytes), y por lo mismo para intercambiar elementos del arreglo debo utilizar memcpy, además de que cargo por todas partes un pedazo de memoria t para guardar el valor temporal durante el intercambio; la alternativa hubiera sido usar variables globales (the horror!), o solicitar y liberar memoria en cada intercambio de variables.

Hasta aquí estaba más o menos satisfecho: ya tenía una versión “genérica” en C que era más rápida que la de Java (aunque desde mi punto de vista la solución sea bastante fea), pero entonces se me ocurrió que estaba siendo muy injusto: si hice una versión tramposa para C (la que sólo sirve para enteros), debería hacer una versión tramposa para Java también. Así que eso hice:

public static void swap(int[] a, int i, int j) {
    if (i == j)
        return;
    int t = a[j];
    a[j] = a[i];
    a[i] = t;
}

public static void quickSort(int[] a) {
    quickSort(a, 0, a.length-1);
}

private static void quickSort(int[] a, int ini, int fin) {
    if (fin - ini < 1)
        return;
    int i = ini + 1, j = fin;
    while (i < j)
        if (a[i] > a[ini] &&
            a[j] < = a[ini])
            swap(a, i++, j--);
        else if (a[i] <= a[ini])
            i++;
        else
            j--;
    if (a[i] > a[ini])
        i--;
    swap(a, ini, i);
    quickSort(a, ini, i-1);
    quickSort(a, i+1, fin);
}

Esta versión tarda 2.26 segundos en ordenar un arreglo de un millón de elementos, lo que la hace casi tres veces más rápida que la versión tramposa de C. ¿Por qué ocurre esto? Sinceramente, no tengo idea; lo único que se me ocurre es que con 1,000,000 recursiones, el compilador Just-In-Time (JIT) de Java alcanza a optimizar algo durante la ejecución del programa que la versión en C no puede. Yo no veo otra alterantiva; pero me encantaría oír teorías.

Sólo un pequeño dato para terminar con QuickSort; hice otra versión tramposa en C para el tipo long, y ésta corre en 4.35 segundos, lo que la sigue haciendo más lenta que la de Java, pero más rápida que la de enteros (int) en C. ¿A lo mejor porque mi máquina es arquitectura AMD64? Una vez más, no tengo idea; pero sí me gustaría saber qué carajo hace la JVM para ser tan rápida.

Los enigmas no terminaron ahí, porque también implementé MergeSort en Java y C. Primero les enseño mis estructuras de datos en ambos lenguajes; estas son mis listas en Java:

public class Lista< T > implements Iterable< T > {
    protected class Nodo< T > {
	public T elemento;
	public Nodo< T > siguiente;
	public Nodo< T > anterior;
    }
    protected Nodo< T > cabeza;
    protected Nodo< T > rabo;
    public void agregaFinal(T elemento) { ... }
    public void agregaInicio(T elemento) { ... }
    ...
}

Ignoren el Iterable; es sólo para poder recorrer la lista con el foreach de Java. Las listas en C siguen el modelo estructurado en lugar del orientado objetos; por lo tanto en C lidiamos con los nodos directamente (mientras en Java siempre están ocultos al usuario):

struct lista 
{
	void* elemento;
	struct lista* siguiente;
	struct lista* anterior;
};

struct lista* lista_agrega_final  (struct lista* lista,
				   void*         elemento);
struct lista* lista_agrega_inicio (struct lista* lista,
				   void*         elemento);

Por su puesto para su práctica mis alumnos tuvieron que implementar más cosas; pero nada de eso es relevante para lo que discuto aquí. Mi implementación de MergeSort en Java (para estas listas) fue la siguiente:

private static < T extends Comparable< T >> Lista< T >
    merge(Lista< T > li, Lista< T > ld) {
    Lista< T > l = new Lista< T >();
    Lista< T >.Nodo< T > nli = li.cabeza;
    Lista< T >.Nodo< T > nld = ld.cabeza;
    while (nli != null && nld != null) {
        if (nli.elemento.compareTo(nld.elemento) < 0) {
            l.agregaFinal(nli.elemento);
            nli = nli.siguiente;
        } else {
            l.agregaFinal(nld.elemento);
            nld = nld.siguiente;
        }
    }
    while (nli != null) {
        l.agregaFinal(nli.elemento);
        nli = nli.siguiente;
    }
    while (nld != null) {
        l.agregaFinal(nld.elemento);
        nld = nld.siguiente;
    }
    return l;
}

public static < T extends Comparable< T >> Lista< T >
    mergeSort(Lista< T > l) {
    int n = l.longitud();
    if (n == 1)
        return l;
    Lista< T > li = new Lista< T >();
    Lista< T > ld = new Lista< T >();
    int i = 0;
    Iterator< T > iterador = l.iterator();
    while (i++ < n/2)
        li.agregaFinal(iterador.next());
    while (i++ <= n)
        ld.agregaFinal(iterador.next());
	
    li = mergeSort(li);
    ld = mergeSort(ld);
    return merge(li, ld);
}

La versión en C es la que sigue:

static struct lista*
merge(struct lista* li, struct lista* ld, func_compara f) 
{
	struct lista* l = NULL;
	struct lista* ii = li;
	struct lista* id = ld;

	while (ii != NULL && id != NULL) {
		if (f(ii->elemento, id->elemento) < 0) {
			l = lista_agrega_inicio(l, ii->elemento);
			ii = ii->siguiente;
		} else {
			l = lista_agrega_inicio(l, id->elemento);
			id = id->siguiente;
		}
	}
	while (ii != NULL) {
		l = lista_agrega_inicio(l, ii->elemento);
		ii = ii->siguiente;
	}
	while (id != NULL) {
		l = lista_agrega_inicio(l, id->elemento);
		id = id->siguiente;
	}

	struct lista* tmp = lista_reversa(l);
	lista_libera(l);
	return tmp;
}

struct lista*
mergesort(struct lista* l, func_compara f)
{
	int n = lista_longitud(l);
	if (n == 1) {
		struct lista* uno =
                        lista_agrega_inicio(NULL, l->elemento);
		return uno;
	}
	struct lista* li = NULL;
	struct lista* ld = NULL;
	int i = 0;
	struct lista* tmp = l;
	while (i++ < n/2) {
		li = lista_agrega_inicio(li, tmp->elemento);
		tmp = tmp->siguiente;
	}
	while (i++ < = n) {
		ld = lista_agrega_inicio(ld, tmp->elemento);
		tmp = tmp->siguiente;
	}

	tmp = lista_reversa(li);
	lista_libera(li);
	li = tmp;

	tmp = lista_reversa(ld);
	lista_libera(ld);
	ld = tmp;

	tmp = ordenamientos_mergesort(li, f);
	lista_libera(li);
	li = tmp;

	tmp = ordenamientos_mergesort(ld, f);
	lista_libera(ld);
	ld = tmp;

	tmp = merge(li, ld, f);
	lista_libera(li);
	lista_libera(ld);
	return tmp;
}

Dado que una "lista" es realmente un nodo de la lista (siguiendo el modelo utilizado por GLib), no tengo guardado en nigún lado el rabo de la lista; por eso agrego elementos al inicio, y cuando termino la volteo. Hice mis pruebas de nuevo con 1,000,000 elementos, y lo primero que me sorprendió fue que fuera tan rápido en comparación con QuickSort; yo recordaba que cuando los implementé en mi carrera, la diferencia no era tanta. A lo mejor ahora programo mejor.

La versión en Java tarda (en promedio) 2.25 segundos; la versión en C 4.8, más del doble. Esta vez ya estaba preparado y no me sorprendió tanto, y de inmediato pensé que una obvia optimización es cargar el rabo de cada lista, y así poder agregar elementos al final en tiempo constante, sin tener que preocuparme de voltearla después. Para eso creé esta estructura de datos:

struct dlista {
	struct lista* cabeza;
	struct lista* rabo;
	int longitud;
};
void dlista_agrega_final(struct dlista* dl, void* elemento);
void dlista_agrega_inicio(struct dlista* dl, void* elemento);

Pude entonces simplificar mi versión de MergeSort:

static struct dlista*
merge(struct dlista* dli, struct dlista* dld, func_compara f) 
{
	struct dlista* dl = dlista_nueva();
	struct lista* ii = dli->cabeza;
	struct lista* id = dld->cabeza;

	while (ii != NULL && id != NULL) {
		if (f(ii->elemento, id->elemento) < 0) {
			dlista_agrega_final(dl, ii->elemento);
			ii = ii->siguiente;
		} else {
			dlista_agrega_final(dl, id->elemento);
			id = id->siguiente;
		}
	}
	while (ii != NULL) {
		dlista_agrega_final(dl, ii->elemento);
		ii = ii->siguiente;
	}
	while (id != NULL) {
		dlista_agrega_final(dl, id->elemento);
		id = id->siguiente;
	}

	return dl;
}

struct dlista*
mergesort(struct dlista* dl, func_compara f)
{
	int n = dl->longitud;
	if (n == 1) {
		struct dlista* uno = dlista_nueva();
		dlista_agrega_final(uno, dl->cabeza->elemento);
		return uno;
	}
	struct dlista* dli = dlista_nueva();
	struct dlista* dld = dlista_nueva();
	int i = 0;
	struct lista* tmp = dl->cabeza;
	while (i++ < n/2) {
		dlista_agrega_final(dli, tmp->elemento);
		tmp = tmp->siguiente;
	}
	while (i++ < = n) {
		dlista_agrega_final(dld, tmp->elemento);
		tmp = tmp->siguiente;
	}

	struct dlista* tmp2;
	tmp2 = mergesort(dli, f);
	dlista_libera(dli);
	dli = tmp2;

	tmp2 = mergesort(dld, f);
	dlista_libera(dld);
	dld = tmp2;

	tmp2 = merge(dli, dld, f);
	dlista_libera(dli);
	dlista_libera(dld);
	return tmp2;
}

Esta nueva versión corre en 2.72 segundos; mucho más cerca a la versión de Java, pero todavía más lenta. Lo único extra que se me ocurrió que podía hacer era eliminar el manejo de memoria; pensando que tal vez Java es más rápido (en este caso) porque puede diferir el liberar memoria hasta después de correr el algoritmo. Así que quité las llamadas a la función dlista_libera tratando de emular como sería tener recolector de basura, y por supuesto el algoritmo corrió ahora más lento: 2.92 segundos. ¿A lo mejor con 1,000,000 de elementos consigo forzar que Linux pase memoria al swap? No tengo idea; pero la verdad lo dugo: tengo 4 Gb de memoria, y no vi que el foquito de mi disco duro se prendiera.

Todos estos resultados pueden atribuirse a errores del programador (dícese, yo), pero honestamente no creo estar haciendo nada obviamente mal. Mi teoría favorita (y de hecho la única) es que el compilador JIT de la JVM está haciendo algo de magia que el simple ensamblador optimizado de C no puede; lo cual sería una muesta feaciente e innegable de las ventajas que pueden tener los lenguajes de programación que compilan para una máquina virtual altamente optimizada. Sumado a que es mucho más sencillo programar todas estas estructuras de datos si uno no tiene que preocuparse de manejar la memoria, y además con la fuerte (y desde mi punto de vista muy bonita) tipificación de datos que ofrecen los genéricos en Java, la verdad no vería por qué alguien escogería C sobre Java para programar cosas que tengan que repetir una misma tarea cientos de miles de veces.

Por supuesto es un experimento sólo en mi máquina, y en estos días 1,000,000 de elementos me suena a que ya no es realmente tanto. Con 10,000,000 de elementos, la versión en C tardó 36.32 segundos, y la versión en Java tardó 41.98 segundos; además de que tuve que aumentarle el tamaño al heap de la máquina virtual de Java a 4 Gb. Si lo aumentaba a 2 Gb, tardaba 54.99 segundos; en ambos casos el foquito de mi disco duro se prendió. En uso de memoria, sin duda C sigue siendo mucho superior (al costo de que uno tiene que andar manejándola a mano).

De cualquier forma, es impresionante lo rápido que es Java hoy en día; cuando yo lo comencé a aprender (el siglo pasado), todavía muchísima gente se quejaba de lo lento que era. Ahora yo (que no tengo poca experiencia programando) no puedo hacer que una versión en C del mismo algoritmo le gane.

En nuestras vidas profesionales lo más probable es que mis alumnos y yo no tengamos que implementar ninguno de estos algoritmos nunca; lo más seguro es que ya existirá una versión suficientemente buena y suficientemente optimizada disponible, lo que haría medio inútil que la implementáramos de nuevo. Sin embargo, me parece importante que un computólogo las implemente aunque sea una vez en su vida, y entienda cómo funcionan y cómo podrían utilizar una versión personalizada para algún obscuro problema que se encuentren.

Y ahora tengo que trabajar en mis algoritmos para árboles binarios.

Imprimir entrada Imprimir entrada

Diaspora

Hace (literalmente) años oí por primera vez de Diaspora, un simulador de vuelo espacial basado en Battlestar Galactica, una de mis series de ciencia ficción favorita. La idea del juego sonaba bien, pero la verdad no le di mucho crédito; hay siempre decenas de proyectos para juegos, y la mayor parte no pasan de ser más que buenas intenciones, o en el mejor de los casos mods de otros juegos. Un buen ejemplo de esto es Parsec, un juego del que oí por primera vez (y esto es en serio) el siglo pasado, y que en 2001 básicamente se murió. En 2003 liberaron el código fuente, y en febrero de este año parecía que volvería a levantar, pero al final ya no supe nada. Probablemente nunca llegue a nada.

Por suerte no ocurrió lo mismo con Diaspora: hace unos días anunciaron que la primera parte del juego estaba lista; pueden ver el trailer en el tubo. Hoy (después de que llegáramos madreadísimos de La Paz) lo instalé, y me sorprendió el profesionalismo con el que está hecho, incluyendo una completa actuación de voz. Lo único malo es que no tengo mi joystick (la verdad, ni siquiera sé si aún funciona), y el ratón no es el mejor modo de jugar este tipo de cosas.

Diaspora

Diaspora

Échenle un ojo; hay binarios para Linux, Windows y Mac OS X, y aunque el juego trae sólo unas cuantas misiones, también incluye un editor para crearlas, así que es diversión garantizada para toda la familia.

Imprimir entrada Imprimir entrada

La Paz

Cuando tenía 17 años, mi hermano, un amigo y yo tratamos de irnos a Real de Catorce a comer peyote. Eso no ocurrió, así que nos fuimos a La Paz, y de ahí a Constitución, donde una tía mía vive. Fue un viaje memorable por varias razones, y la primera vez que visité La Paz, aunque sólo unas horas de paso hacia Constitución, y un día de paso en el regreso a la Ciudad de México.

Hotel La Concha

Hotel La Concha

A los 24 años, mis cuates de la carrera y yo hicimos un “viaje de titulación” (aunque ninguno de nosotros se había titulado en ese momento) a La Paz. Estuvimos ahí diez días (más los casi cuatro necesarios para ir y volver: 18 horas de camión más 18 horas de ferry de Mazatlán a La Paz). También fue un viaje memorable, y uno de los viajes que con más cariño recuerdo.

Playa Balandra

Playa Balandra

A mis 35 años me encuentro de nuevo en La Paz, porque Edgar se nos casa aquí el sábado. Ayer Isabel y yo volamos a San José del Cabo, rentamos un carro, y de ahí manejé los 200 kilómetros a La Paz, en uno de los recorridos en carretera más divertidos que he tenido en mi vida (es altamente satisfactorio frenar con motor justo antes de entrar a una curva a 150 Km/h).

Gaviota

Gaviota

Después de asentarnos en nuestro hotel, fuimos a cenar riquísimo a un restaurante sobre el malecón, y después fuimos a un antro repleto de veinteañeras (atraídas por la barra libre para chavas), y de cuarentones libidinosos (atraídos por las veinteañeras).

El Hongo

El Hongo

Hoy fuimos a Playa Balandra, y después a comer a El Tecolote. En la noche habrá un coctel previo a la boda, y mañana es la ceremonia en sí. El domingo Isabel y yo manejamos de regreso a San José del Cabo, y volamos de regreso a la Ciudad de México.

Espíritu Santo

Espíritu Santo

Acaba de llover por aquí (cosa no muy común; no llovía hacía tres años), y todo está verde y exuberante de vida. Hace once años que vinimos era lo opuesto: estaba seco y no se veía nada moverse; pero ahora el cielo está lleno de aves, y por una razón extraña hay cientos de mariposas amarillas por todos lados. Estoy todo el tiempo esperando encontrarme con Mauricio Babilonia.

No creo escribir más entradas en el blog mientras esté aquí; pero estoy actualizando todo el tiempo mi perfil en Google Plus; es mucho más cómodo subir fotos al momento por ahí, y además durante el día la verdad yo no noto la diferencia entre mi cámara digital, y la incluida en mi teléfono.

Imprimir entrada Imprimir entrada

Fight them until we can’t

Al final de la segunda temporada de Battlestar Galactica, los colonos llevan un año viviendo en un planeta horrible, pero que los esconde de los Cylons por estar dentro de una nébula. O algo así. Todos al parecer son miserables bajo la presidencia de Gaius Baltar, includo Gaius Baltar.

Entonces por supuesto aparecen los Cylons, pero esta vez no desean exterminar a la raza humana; ahora sólo quieren oprimirnos. O algo así; el punto es que básicamente ocupan el asentamiento humano en Nueva Caprica. Mientras ven desfilar a los centuriones, el Jefe Galen Tyrol le pregunta a Starbuck: What do you want to do now, Captain?

Y Starbuck responde: The same thing we always do. Fight them until we can’t.

La respuesta es la única sensata; la alternativa es cruzarse de brazos y decir “nos chingaron, pues ya ni modo”. La escena me vino a la mente la noche de las elecciones, y de nuevo cuando el TEPJF dictaminó como ya sabíamos que iba a dictaminar, desechando todas las evidencias presentadas, y negándose a dar más tiempo a presentar aún más. Sería irrisorio, si no fuera tan trágico.

El fraude que ocurrió el primero de julio fue mucho más descarado que el de hace seis años; todos vieron (si querían ver) la compra de votos como política de partido, el derroche en gastos de campaña por parte del PRI, y la total parcialidad de Televisa. Fue el PRI de mi niñez, de 1982, de 1988; como hombre maduro ahora veo repetirse las prácticas que yo soñaba mis hijos jamás tendrían que ver. Era lo único bueno que parecía haber resultado de que Fox hubiera ganado (él sí legítimamente) las elecciones de hace doce años. Y el papel de Televisa (y medios afines) es todavía peor (y más preocupante); Televisa antes hacía lo que el gobierno en turno le dijera. Ahora impone diputados, senadores, y al parecer “presidentes”.

El sexenio ilegal e ilegítimo de Felipe Calderón se gestó con un fraude y comenzó con una resistencia enorme a su imposición. El resultado fue uno de los peores sexenios de los que tengamos historia; literalmente decenas de miles de muertos, con la delincuencia organizada más fuerte que nunca (y probablemente coludida con personajes a todos los niveles del gobierno), un desempleo y pobreza galopantes, y consecuentemente una violencia que comienza a afectar incluso a la Ciudad de México, que durante estos años se convirtió en uno de los pocos lugares donde las cosas no estaban tan mal.

El sexenio de este pobre y patético títere telenovelesco, si acaso empieza, lo hará en condiciones peores y con una resistencia mucho más fuerte, mucho más organizada, y que afortunadamente ha decidido colectivamente, en general, permanecer pacífica. Yo a regañadientes me sumo a la idea de que cualquier resistencia que demos debe seguir siendo pacífica, porque la perspectiva de que mis hijos nazcan en un país donde, después de un escándalo como el de los documentos proporcionados por The Guardian, que en cualquier otro país hubiera resultado en comités de senadores para investigar y en al menos una o dos cabezas políticas rodando, aquí no pasa absolutamente nada, sí me hace cuestionarme si de verdad no será ya necesario recurrir a la violencia como medio legítimo de resistencia.

Pero al fin y al cabo comprendo que en caso de que estalle la violencia, los que más van a morir y sufrir van a ser (de nuevo, como siempre) los más jodidos. Sólo por eso también me sumo a la idea colectiva de que resistamos pacíficamente; pero voy a comprender (y no necesariamente condenar) si no todo mundo lo hace.

(A todo esto, ¿dónde están las masas alegres de gente que votó por el PRI? Se supone lo hicieron casi 20 millones, ¿no? ¿Dónde están, colmando las plazas y las calles celebrando su “triunfo”?)

El sexenio que acaba ha sido de las peores cosas que le han pasado a México; además de lo que ya comenté, la educación pública (que es la única de calidad en este país), la investigación científica, la promoción y desarrollo de cultura, y los servicios de salud se han ido todos al carajo. Las condiciones de los trabajadores en el país se han deteriorado a grados inaceptables, y sus derechos se han visto atacados y mermados en todos los frentes. Los casos de todos los mineros muertos, Luz y Fuerza y Mexicana de Aviación son sólo unos cuantos.

El sexenio que viene (si empieza) va a ser peor. Van a tratar de ir por todo; la privatización de Pemex, la venta de la CFE (que al fin y al cabo tiene el sindicato más inútil de la historia), la SEP en control absoluto de Elba Esther Gordillo, y por lo tanto un empeoramiento de la educación pública a nivel básico y medio, y el uso de las secciones charras SNTE como arma política. No lo duden que también tratarán de cortar los presupuestos de la UNAM y el resto de las universidades públicas, e incluso tal vez “sugieran” que deberían empezar a cobrar cuotas. Y por supuesto, el ataque continuo a la Ciudad de México, cortándole su presupuesto aunque siga siendo de las entidades que más dinero produce en el país.

Las cosas están muy mal en el país desde hace varios años; se pueden poner mucho peor.

¿Qué vamos a hacer nosotros? Lo mismo que hemos hecho siempre: pelear hasta que ya no podamos.

De nosotros (de todos nosotros) depende que las cosas no se pongan mucho peor. No basta ir a estudiar/trabajar y cumplir con nuestras obligaciones (aunque ciertamente, es el mínimo que se puede esperar de cualquiera); tenemos todos que integrarnos a la vida política. No se vale nada más estarse quejando de los pinches políticos y de los pinches partidos, y esperar que mágicamente esos pinches políticos y esos pinches partidos generen de forma espontánea una clase política a nuestro gusto.

Tenemos todos como ciudadanos que ponerlos a raya; salir a las calles (de forma pacífica) y mostrarles que no estamos dispuestos a aceptar ciertas cosas. Fueron marchas y manifestaciones las que consiguieron el alto al fuego unilateral contra el EZLN en 1994; fueron marchas y manifestaciones las que lograron que pudiéramos elegir nuestros gobernantes en la Ciudad de México en 1997; fueron marchas, manifestaciones, y una huelga larguísima y desgastante, las que evitaron que se impusieran las cuotas en la UNAM; fueron marchas y manifestaciones las que impidieron el desafuero del Peje.

Salir a las calles funciona. Y podemos aumentar la intensidad de la resistencia, sin tener que recurrir a la violencia; como lo hicieron este fin de semana los chavos de #YoSoy132 tomando casetas en autopistas. Se puede organizar una huelga general que involucre trabajadores de varios ramos, y estudiantes de universidades. Se pueden hacer muchas cosas.

Pero tenemos que entrarle todos. Debemos seguir peleando.

Hasta que ya no podamos.

Imprimir entrada Imprimir entrada