<?xml version="1.0" encoding="UTF-8" standalone="no"?><rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" version="2.0">
	<channel>
		<title>gimco's tech disorder</title>
		<description>undefined ;)</description>
		<link>http://blog.gimco.es</link>
		<atom:link href="http://blog.gimco.es/feed.xml" rel="self" type="application/rss+xml"/>
		
			<language>en-us</language><item>
				<title>U-Boot</title>
				<description>&lt;p&gt;Hace ya casi ¡10 años! que me compré un NAS para la casa. En su momento quería que tuviera dos bahías de discos duros poder configurar RAID y poder utilizar Linux, por lo que me decidí por un &lt;a href="https://techcrunch.com/2009/11/04/review-iomega-storcenter-ix2-200-nas/"&gt;Iomega StorCenter ix2-200&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src="/assets/iomega-ix2.jpg" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;Despues de estar algunas semanas con el firmware original, decidí tomar el control completo del servidor e &lt;a href="https://web.archive.org/web/20190307130532/http://iomega.nas-central.org/wiki/Category:Ix2-200-install-debian"&gt;instalar&lt;/a&gt; &lt;a href="https://lists.debian.org/debian-arm/2016/02/msg00075.html"&gt;Debian 6&lt;/a&gt;. Ha estado desempeñando sus múltiples labores a la perfección, pero debido a las obvias limitaciones y riesgos de seguridad por utilizar una versión tan antigua decidí actualizar el sistema. Comencé el proceso de &lt;a href="https://www.prado.lt/how-to-upgrade-debian-7-wheezy-to-10-buster-safely"&gt;actualizar&lt;/a&gt; entre las distintas versiones de debian, realizando varias veces los pasos de modificar el nombre de la release (&lt;code class="language-plaintext highlighter-rouge"&gt;squeeze → wheezy → jessie → stretch → buster&lt;/code&gt;) e ir actualizando:&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;# Modificar /etc/apt/sources.list 
apt-get update
apt-get upgrade
apt-get dist-upgrade
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;El problema es que en algún punto el sistema se rompió y tras uno de los reinicios ya no era capaz de conectar por SSH al servidor, y por lo tanto perdí el acceso al dispositivo (y parece que no soy &lt;a href="https://lars.karlslund.dk/2011/11/recovering-iomega-ix2-200-nas-after-failed-upgrade/"&gt;el único&lt;/a&gt; al que le pasan estas cosas).&lt;/p&gt;

&lt;h2 id="conexión-ttl-uart"&gt;Conexión TTL UART&lt;/h2&gt;

&lt;p&gt;En estos casos, la mayoría de los dispositivos y microcontroladores proporcionan un mecanismo para acceder a una especie de consola o terminal mediante una conexión a un puerto serie. Necesitaremos hacernos con un dispositivo &lt;code class="language-plaintext highlighter-rouge"&gt;USB to TTL&lt;/code&gt; como el siguiente:&lt;/p&gt;

&lt;p&gt;&lt;img src="/assets/ttl-uart.jpg" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;Una vez desmontado el dispositivo buscaremos en la placa las indicaciones para conectar los cables a los pines de transmisión, recepción, voltaje y tierra. Hay que informarse si trabaja a 3 o 5 voltios (y conectar en nuestro USBTTL el pin correspondiente), y recordar invertir el orden de los cables de transmisión y recepción.&lt;/p&gt;

&lt;p&gt;&lt;img src="/assets/iomega-ix2-ttl.jpg" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;También deberemos saber las opciones de transmisión que debamos configurar. Una vez enchufado nuestro USBTTL, consultamos los mensajes del kernel con &lt;code class="language-plaintext highlighter-rouge"&gt;dmesg&lt;/code&gt; para saber que nuevo dispositivo en &lt;code class="language-plaintext highlighter-rouge"&gt;/dev&lt;/code&gt; se ha creado. Una vez conectados todos los pines nos podremos conectar a nuestro dispositivo, usando alguna aplicación como &lt;a href="https://www.decisivetactics.com/products/serial/"&gt;Serial&lt;/a&gt;, o símplemente con el siguiente comando:&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;screen /dev/tty.SLAB_USBtoUART 11520
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Al encender el NAS comenzaremos a ver mensajes del proceso de inicio interno!&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;         __  __                      _ _
        |  \/  | __ _ _ ____   _____| | |
        | |\/| |/ _` | '__\ \ / / _ \ | |
        | |  | | (_| | |   \ V /  __/ | |
        |_|  |_|\__,_|_|    \_/ \___|_|_|
 _   _     ____              _
| | | |   | __ )  ___   ___ | |_
| | | |___|  _ \ / _ \ / _ \| __|
| |_| |___| |_) | (_) | (_) | |_
 \___/    |____/ \___/ \___/ \__|
 ** MARVELL BOARD: RD-88F6281A LE

U-Boot 1.1.4 (Sep  8 2009 - 09:31:54) Marvell version: 3.4.14

Mapower version: 2.0 (32MB) (2009/09/08)

U-Boot code: 00600000 -&amp;gt; 0067FFF0  BSS: -&amp;gt; 006CEE60

Soc: 88F6281 A0 (DDR2)
CPU running @ 1000Mhz L2 running @ 333Mhz
SysClock = 333Mhz , TClock = 200Mhz

DRAM CAS Latency = 5 tRP = 5 tRAS = 18 tRCD=6
DRAM CS[0] base 0x00000000   size 256MB
DRAM Total size 256MB  16bit width
Flash:  0 kB
Addresses 8M - 0M are saved for the U-Boot usage.
Mem malloc Initialization (8M - 7M): Done
NAND:32 MB

CPU : Marvell Feroceon (Rev 1)

Streaming disabled
Write allocate disabled

Module 0 is RGMII
Module 1 is TDM

USB 0: host mode
PEX 0: interface detected no Link.
Net:   egiga0, egiga1 [PRIME]
Fan lookup table initialized.

Current remote temperature: 47
Current fan speed: 0

Hit any key to stop autoboot:  0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id="das-u-boot"&gt;Das U-Boot&lt;/h2&gt;

&lt;p&gt;Los mensajes que nos aparecen inicialmente son del bootloader utilizado por la placa de nuestro NAS. El bootloader es un mini programa ofrece algunas utilidades que nos permiten configurar y realizar algunas acciones para poder iniciar un sistema mas complejo, en nuestro caso, u-boot se encarga de cargar en la memoria el kernel de linux y proceder a su arranque.&lt;/p&gt;

&lt;p&gt;Si pulsamos intro durante el arranque, podremos interactuar con la líneas de comandos de u-boot. Se ejecutamos &lt;code class="language-plaintext highlighter-rouge"&gt;help&lt;/code&gt; podremos ver todos los comandos que tenemos disponibles:&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;Marvell&amp;gt;&amp;gt; help
?       - alias for 'help'
base    - print or set address offset
boot    - boot default, i.e., run 'bootcmd'
bootd   - boot default, i.e., run 'bootcmd'
bootext2    dev:boot_part1,boot_part2 addr boot_image linux_dev_name 
bootm   - boot application image from memory
bootp   - boot image via network using BootP/TFTP protocol
bubt    - Burn an image on the Boot Nand Flash.
chpart  - change active partition
cmp     - memory compare
cmpm    - Compare Memory
cp      - memory copy
cpumap - Display CPU memory mapping settings.
crc32   - checksum calculation
date    - get/set/reset date &amp;amp; time
dclk    - Display the MV device CLKs.
dhcp    - invoke DHCP client to obtain IP/boot params
diskboot- boot from IDE device
echo    - echo args to console
eeprom  - EEPROM sub-system
erase   - erase FLASH memory
ext2load- load binary file from a Ext2 filesystem
ext2ls  - list files in a directory (default /)
fatinfo - print information about filesystem
fatload - load binary file from a dos filesystem
fatls   - list files in a directory (default /)
fi      - Find value in the memory.
flinfo  - print FLASH memory information
fsinfo  - print information about filesystems
fsload  - load binary file from a filesystem image
g       - start application at cached address 'addr'(default addr 0x40000)
go      - start application at address 'addr'
help    - print online help
icrc32  - checksum calculation
ide     - IDE sub-system
iloop   - infinite loop on address range
imd     - i2c memory display
imm[.b, .s, .w, .l]     - i2c memory modify (auto-incrementing)
imw     - memory write (fill)
inm     - memory modify (constant address)
iprobe  - probe to discover valid I2C chip addresses
ir      - reading and changing MV internal register values.
loop    - infinite loop on address range
ls      - list files in a directory (default /)
map     - Diasplay address decode windows
md      - memory display
me      - PCI master enable
mm      - memory modify (auto-incrementing)
mp      - map PCI BAR
mtdparts- define flash/nand partitions
mtest   - simple RAM test
mw      - memory write (fill)
nand                   - NAND sub-system
nboot   - boot from NAND device
nbubt   - Burn a boot loader image on the Boot Nand Flash.
nm      - memory modify (constant address)
pci     - list and access PCI Configuration Space
phyRead - Read PCI-E Phy register
pciePhyWrite    - Write PCI-E Phy register
phyRead - Read Phy register
phyWrite        - Write Phy register
ping    - send ICMP ECHO_REQUEST to network host
printenv- print environment variables
protect - enable or disable FLASH write protection
rarpboot- boot image via network using RARP/TFTP protocol
rcvr    - Satrt recovery process (Distress Beacon with TFTP server)
reset   - Perform RESET of the CPU
resetenv        - Return all environment variable to default.
run     - run commands in an environment variable
saveenv - save environment variables to persistent storage
se      - PCI Slave enable
setenv  - set environment variables
sflash  - read, write or erase the external SPI Flash.
sg      - scanning the PHYs status
sp      - Scan PCI bus.
tftpboot- boot image via network using TFTP protocol
usb     - USB sub-system
usbboot - boot from USB device
version - print monitor version
Marvell&amp;gt;&amp;gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Con el mando &lt;code class="language-plaintext highlighter-rouge"&gt;printenv&lt;/code&gt; podremos ver cuáles son las opciones configuradas en u-boot para realizar el inicio, y podremos ajustar la variables de entorno que precisemos (como por ejemplo agregar nuevos parámetros de inicio al kernel) y podremos guardar los cambios.&lt;/p&gt;

&lt;p&gt;A continuación podemos ver la configuración actual de este dispositivo:&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;Marvell&amp;gt;&amp;gt; printenv
baudrate=115200
loads_echo=0
rootpath=/mnt/ARM_FS/
console=console=ttyS0,115200 mtdparts=nand_mtd:0xc0000@0(uboot)ro,0x1f00000@0x100000(root)
CASset=min
MALLOC_len=1
bootargs_root=root=/dev/nfs rw
bootargs_end=:::DB88FXX81:eth0:none
image_name=uImage
standalone=fsload 0x2000000 $(image_name);setenv bootargs $(console) root=/dev/mtdblock0 rw ip=$(ipaddr):$(serverip)$(bootargs_end) $(mvPhoneConfig); bootm 0x2000000;
ethmtu=1500
eth1mtu=1500
mvPhoneConfig=mv_phone_config=dev0:fxs,dev1:fxs
mvNetConfig=mv_net_config=(00:11:88:0f:62:81,0:1:2:3),mtu=1500
usb0Mode=host
yuk_ethaddr=00:00:00:EE:51:81
nandEcc=1bit
netretry=no
rcvrip=169.254.100.100
loadaddr=0x02000000
autoload=no
FanHysteresis=2
FanTempStart=58
ethprime=egiga1
eth1addr=00:D0:B8:16:51:14
uboot_start=0x0
uboot_size=0xc0000
env_start=0xa0000
env_size=0x20000
kernel_start=0x100000
kernel_size=0x300000
initrd_start=0x540000
flash_load=run make_boot_args load1 load2 boot
load1=nand read.e 0x2000000 $(kernel_start) $(kernel_size)
load2=nand read.e 0x4500000 $(initrd_start) $(initrd_size)
boot=bootm 0x2000000 0x4500000
make_boot_args=setenv bootargs console=ttyS0,115200 mtdparts=nand_mtd:;setenv bootargs $(bootargs)$(uboot_size)@$(uboot_start)(uboot),;setenv bootargs $(bootargs)$(env_size)@$(env_start)(env),;setenv bootargs $(bootargs)$(kernel_size)@$(kernel_start)(zImage),;setenv bootargs $(bootargs)$(initrd_size)@$(initrd_start)(initrd),;setenv bootargs $(bootargs)128m@0x0(flash);
iomega=123
ethaddr=AA:00:00:00:00:01
arcNumber=1682
filesize=69603a
fileaddr=5400000
netmask=255.255.255.0
ipaddr=192.168.1.11
serverip=192.168.1.10
bootcmd=run load1 load2 boot
bootargs=console=ttyS0,115200 mtdparts=orion_nand:0x300000@0x100000(uImage),0x1000000@0x540000(uInitrd) root=/dev/mapper/secure-root rootdelay=10
initrd_size=0x1000000
bootargs_console=console=ttyS0,115200 mtdparts=orion_nand:0x300000@0x100000(uImage),0x1000000@0x540000(uInitrd) root=/dev/mapper/secure-root rootdelay=10
stdin=serial
stdout=serial
stderr=serial
mainlineLinux=yes
enaMonExt=no
enaCpuStream=no
enaWrAllo=no
pexMode=RC
disL2Cache=no
setL2CacheWT=yes
disL2Prefetch=yes
enaICPref=yes
enaDCPref=yes
sata_dma_mode=yes
netbsd_en=no
vxworks_en=no
bootdelay=3
disaMvPnp=no
hddPowerCtrl=no
enaAutoRecovery=yes
ethact=egiga1

Environment size: 2264/16380 bytes
Marvell&amp;gt;&amp;gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id="enviar-el-nuevo-kernel-por-tftp"&gt;Enviar el nuevo kernel por tftp&lt;/h2&gt;

&lt;p&gt;De los mensajes de inicio, vemos que el dispositivo dispone de 256 megas de RAM y 32 megas de NAND. Al cargar el bootloader se hace una espera de unos segundos para comenzar a ejecutar los pasos que vemos en la variable &lt;code class="language-plaintext highlighter-rouge"&gt;bootcmd=run load1 load2 boot&lt;/code&gt;. El dispositivo leerá de la NAND y copiará el kernel y el initramfs a la memoria RAM para finalmente iniciar.&lt;/p&gt;

&lt;p&gt;Si tenemos problemas porque hemos corrompido la NAND, tendremos que hacer llegar el nuevo kernel (e initramfs) que queremos grabar. Para esto podemos hacer uso de &lt;a href="https://rick.cogley.info/post/run-a-tftp-server-on-mac-osx/"&gt;tftp&lt;/a&gt;. Los dos ficheros del kernel y del initramfs necesitan estar en un formato compatible con uboot. Si lo que queremos hacer es una instalación desde cero, en el caso de debian, podemos buscar &lt;a href="http://ftp.debian.org/debian/dists/jessie/main/installer-armel/current/images/kirkwood/netboot/marvell/openrd/"&gt;la versión del instalador&lt;/a&gt; compatible con nuestro dispositivo.&lt;/p&gt;

&lt;p&gt;En caso contrario, deberemos convertir los ficheros para poder ser procesados por uboot:&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;mkimage -A arm -O linux -T kernel -C none -a 0x00008000 -e 0x00008000 -n "2.6.32.5-kirkwood" -d vmlinuz uImage
mkimage -A arm -O linux -T ramdisk -C none -a 0x01100000 -e 0x01100000 -n "2.6.32.5-kirkwood" -d initrd.img uInitrd
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Colocamos la imagen del kernel y el initramfs en el directorio de nuestro servidor tftp y procedemos a recibir en el dispositivo los datos y cargarlos en las direcciones de memoria correspondientes:&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;dhcp
set serverip=192.168.x.y
tftpboot 0x2000000 uImage
tftpboot 0x5400000 uInitrd
bootm 0x2000000 0x5400000
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Una vez hayamos probado que inicia bien con el kernel y el initramfs, lo podremos hacer permanente. Para ello, despues de reiniciar la máquina y volver a cargar en memoria los ficheros mediante tftp, procederemos a borrar la NAND y copiar de memoria RAM a NAND:&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;nand erase 0x100000 0x300000
nand erase 0x540000 0x300000
nand write.e 0x2000000 0x100000 0x300000
nand write.e 0x5400000 0x540000 0x300000
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;De esta forma, la próxima vez que iniciemos se procederá al inicio normal.&lt;/p&gt;

&lt;h2 id="buscando-el-kernel"&gt;Buscando el kernel&lt;/h2&gt;

&lt;p&gt;Lo siguiente que hice fue buscar el kernel disponible en debian para mi arquitectura para extraer el kernel y el initramfs. Para ello instalé u-boot-tools para poder realizar la conversión con &lt;code class="language-plaintext highlighter-rouge"&gt;mkimage&lt;/code&gt;, descargué directamente el paquete y extraje el contenido:&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;apt install u-boot-tools
wget "http://ftp.us.debian.org/debian/pool/main/l/linux/linux-image-5.6.0-1-marvell_5.6.7-1_armel.deb"
ar x linux-image-5.6.0-1-marvell_5.6.7-1_armel.deb
tar xvf data.tar.xz
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Una vez convertido el kernel y el initramfs, lo envié por tftp a la memoria del dispositivo. Pero al intentar iniciar el kernel me encontré con la desagradable sorpresa de que no iniciaba, ya que las opciones de compilación del kernel de debian no era compatible con mi dispositivo:&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;Error: unrecognized/unsupported machine ID (r1 = 0x00000692).
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id="compilar-un-kernel-arm"&gt;Compilar un kernel ARM&lt;/h2&gt;

&lt;p&gt;La única opción en este punto es comprobar si &lt;a href="https://www.kernel.org/doc/html/latest/arm/marvel.html#kirkwood-family"&gt;la arquitectura&lt;/a&gt; seguía estando disponbile de forma oficial y compilar un kernel con las opciones necesarias. Para ello hay que informarse bien del hardware del que disponemos, al menos CPU y tarjetas de red, para poder seleccionar en la configuración las opciones correctas. Para que sirva de referencia para el futuro, esta es parte de la información devuelta por el comando hwinfo sobre la cpu y la tarjeta de red del dispositivo:&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;----- /proc/cpuinfo -----
  Processor	: Feroceon 88FR131 rev 1 (v5l)
  BogoMIPS	: 990.41
  Features	: swp half thumb fastmult edsp
  CPU implementer	: 0x56
  CPU architecture: 5TE
  CPU variant	: 0x2
  CPU part	: 0x131
  CPU revision	: 1

  Hardware	: Marvell RD-88F6281 Reference Board
  Revision	: 0000
  Serial		: 0000000000000000
----- /proc/cpuinfo end -----

05: None 00.0: 0200 Ethernet controller
  [Created at pci.912]
  Unique ID: XveX.iOgNgvnpcfD
  SysFS ID: /devices/platform/mv643xx_eth_port.0
  SysFS BusID: mv643xx_eth_port.0
  Hardware Class: network
  Model: "Marvell MV64360/64361/64362 System Controller"
  Vendor: pci 0x11ab "Marvell Technology Group Ltd."
  Device: pci 0x6460 "MV64360/64361/64362 System Controller"
  Device File: eth0
  HW Address: aa:00:00:00:00:01
  Link detected: no
  Module Alias: "platform:mv643xx_eth_port"
  Driver Info #0:
    Driver Status: mv643xx_eth is active
    Driver Activation Cmd: "modprobe mv643xx_eth"
  Config Status: cfg=new, avail=yes, need=no, active=unknown
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;El proyecto debian tiene alguna información útil para ayudarnos a compilar un kernel para otra arquitectura o lo que es lo mismo, hacer &lt;a href="https://wiki.debian.org/HowToCrossBuildAnOfficialDebianKernelPackage"&gt;crossbuild&lt;/a&gt;. Aunque no fue suficiente la simple compilación, ya que al parecer a partir de cierta versión del kernel, se comenzó a usar &lt;a href="https://en.wikipedia.org/wiki/Device_tree"&gt;Device Tree&lt;/a&gt;, que son una especie de metadatos binarios que informan al kernel de distintas características hardware de la máquina.&lt;/p&gt;

&lt;p&gt;Afortunádamente, encontré &lt;a href="https://www.id3p.de/english-kirkwood-orion-mainline-linux-4-12-for-tk71/?lang=en"&gt;un post&lt;/a&gt; de un desarrollador que se encontraba en la misma tesitura que yo. Rebuscando dentro de los ficheros Makefile del kernel, conseguí seguir la pista para averiguar las opciones de compilación necesarias:&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;dpkg --add-architecture armel
apt-get update
apt-get install crossbuild-essential-armel
apt install sbuild
apt source linux-image-5.6.0-1-marvell
cd linux-5.6.7/
export ARCH=armel
export (dpkg-architecture -a$ARCH)
make menuconfig
make mvebu_v5_defconfig
make zImage
make dtbs
cat arch/arm/boot/zImage arch/arm/boot/dts/kirkwood-iomega_ix2_200.dtb &amp;gt; zImage-kirkwood-iomega
./scripts/mkuboot.sh -A arm -O linux -C none -T kernel -a 0x00008000 -e 0x00008000 -n 'Linux-5.6.7' -d ./zImage-kirkwood-iomega uImage-kirkwood-iomega
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Finalmente conseguí iniciar un kernel mas moderno en este antiguo NAS (aunque se me olvidó meterle mano al tema de los &lt;a href="https://patchwork.kernel.org/patch/1349351/"&gt;leds&lt;/a&gt;), y de paso aprendí algunas cosillas por el camino. &#127881;&#127881;&lt;/p&gt;

</description>
				<pubDate>Tue, 02 Jun 2020 00:00:00 +0000</pubDate>
				<link>http://blog.gimco.es/2020/06/02/uboot.html</link>
				<guid isPermaLink="true">http://blog.gimco.es/2020/06/02/uboot.html</guid>
			<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Bruno Orcha García</dc:creator></item>
		
			<item>
				<title>Multientidad con Hibernate 3</title>
				<description>&lt;p&gt;En una antigua aplicación se presentó la necesidad de agregar un nuevo conjunto de usuarios de forma que pudieran administrar sus datos y procedimientos de forma independiente al conjunto actual de usuarios. Estaríamos ante el clásico problema de la multientidad (multi-tenancy).&lt;/p&gt;

&lt;p&gt;El término multientidad hace referencia a una arquitectura en la que una única instancia de la aplicación es capaz de dar servicio a varios clientes (entidades), muy común en sistemas SaaS (&lt;a href="https://en.wikipedia.org/wiki/Software_as_a_service"&gt;Software as a Service&lt;/a&gt;). El problema que se intenta resolver es el aislamiento de la información entre las distintas entidades.&lt;/p&gt;

&lt;p&gt;En un sistema con soporte multientidad podemos crear distintos conjunto de datos independientes que serían nuestra entidades. Cada entidad tendrá su propio conjunto de información, usuarios, roles y administradores independientes de las otras entidades.&lt;/p&gt;

&lt;h2 id="tipo-de-multientidad"&gt;Tipo de multientidad&lt;/h2&gt;

&lt;p&gt;La aplicación en cuestión no fue creada con esta capacidad desde el principio, por lo que podríamos abordar este nuevo requisito de distintas formas:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;em&gt;Una instancia por entidad.&lt;/em&gt; Realizaríamos un nuevo despliegue para dar servicio a la nueva entidad. ¡Esto no dotaría a la aplicación de capacidad multientidad!, pero resolvería el problema de forma puntual. Un nuevo despliegue no requeriría la modificación de la aplicación actual, ya que sólo tendríamos que crear un nuevo entorno (con una url diferente) y una nueva base de datos. Esta no es una opción escalable, puesto que conforme aumenten el número de entidades desperdiciamos recursos y aumentamos la complejidad del mantenimiento.&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Usar una base de datos distinta por entidad.&lt;/em&gt; En este caso modificaríamos la aplicación para que sea capaz de decidir qué conexión de base de datos se debe utilizar para cada petición solicitada.&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Usar un esquema de base de datos por entidad.&lt;/em&gt; Este caso es similar al anterior pero sólo utilizamos una única base de datos (y un pool de conexiones común), y lo que hacemos es establecer el usuario o el esquema según cada entidad (utilizando &lt;code class="language-plaintext highlighter-rouge"&gt;CONNECT&lt;/code&gt; o &lt;code class="language-plaintext highlighter-rouge"&gt;ALTER SESSION SET CURRENT_SCHEMA&lt;/code&gt;).&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Multientidad lógica.&lt;/em&gt; En este caso se utiliza una única base de datos y esquema, y agregamos un campo discriminador en cada una de las tablas que necesitemos independizar por cada entidad. Es decir, agregamos una nueva columna entidad a ciertas tablas que nos indica a que entidad corresponde cada fila de datos.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src="/assets/multientidad-tipos.png" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;Las opciones mas populares son las dos últimas, y la utilización de una u otra lo marcará nuestras necesidades concretas. Por ejemplo, si la creación de nuevas entidades no forma parte de la lógica principal, y se realiza de forma esporádica (mediante intervención del equipo de sistemas o DBAs) optaremos por la opción de un esquema por entidad. Esta opción además tiene la ventaja de simplificar las copias de seguridad y restauración de los datos de la entidad.&lt;/p&gt;

&lt;p&gt;Si por el contrario estamos creando una plataforma SaaS y queremos permitir que los usuarios puedan crear de forma autónoma nuevas entidades como parte del proceso de registro, probablemente usemos la última opción.&lt;/p&gt;

&lt;h2 id="impacto"&gt;Impacto&lt;/h2&gt;

&lt;p&gt;En el caso concreto de esta antigua aplicación web se decidió utilizar la opción del campo discriminador. La librería de persistencia utilizada era Hibernate, la cuál tiene distintos tipos de &lt;a href="https://docs.jboss.org/hibernate/orm/4.2/devguide/en-US/html/ch16.html"&gt;soporte multientidad&lt;/a&gt; a partir de la &lt;a href="https://hibernate.atlassian.net/browse/HHH-6054"&gt;versión 4.2&lt;/a&gt;. Desgraciadamente la versión utilizada era la 3.3 y el intento de actualización de las librerías desencadenaba la subida de versiones de otros módulos que tendrían un gran impacto en el código existente (dependency hell). Por lo que se decidió implementar la multientidad con la versión de Hibernate utilizada.&lt;/p&gt;

&lt;p&gt;El primer paso sería agregar el campo discriminador &lt;code class="language-plaintext highlighter-rouge"&gt;entidad&lt;/code&gt; en cada una de la tablas que deban tener subconjuntos de datos independientes. Posteriormente tendríamos que modificar todas y cada una de las consultas HQL de la aplicación para agregar una nueva condición de la entidad, y cada uno de los métodos agregar el nuevo parámetro con el valor de la entidad que queremos consultar o modificar los datos. Por toda la aplicación estaríamos arrastrando el parámetro de la entidad activa para filtrar los datos adecuadamente. Haciendo esto estaríamos &lt;em&gt;contaminando&lt;/em&gt; nuestro modelo para implementar un aspecto que debería ser transversal.&lt;/p&gt;

&lt;h2 id="implementación"&gt;Implementación&lt;/h2&gt;

&lt;p&gt;Lo primero que haremos es modificar el esquema de base de datos y agregar las columnas que harán de discriminador en las tablas. Estas nuevas columnas pueden ser claves ajenas hacia una tabla donde almacenemos información adicional de la Entidad. Si alguna de las tablas tuviera claves únicas (por ejemplo alguna columna que fuera un código), tendríamos que modificar la condición del índice de unicidad para agregar la columna entidad. No es el caso de las claves primarias (y por tanto únicas) que suelen rellenarse a partir de un objeto secuencia.&lt;/p&gt;

&lt;p&gt;&lt;img src="/assets/multientidad.png" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;Las columnas entidad solo serían necesarias en las tablas de primer nivel. Las tablas de primer nivel son aquellas que no dependen de otras. Por ejemplo, en una aplicación que gestionase facturas en la que todos los usuarios (de la misma entidad) pudiesen consultarlas indistintamente, la tabla &lt;code class="language-plaintext highlighter-rouge"&gt;Facturas&lt;/code&gt; serían un caso de tabla de primer nivel. Las tablas que almacensaen el detalle de estas facturas (las líneas de facturas por ejemplo) no necesitarían un campo discriminador, ya que siempre se obtendrían a partir de su tabla padre la cuál ya tendría asociada la entidad a la que pertenece. Es decir, la pertenencia del dato a la entidad se resuelve &lt;em&gt;de forma transitiva&lt;/em&gt; a través de las tablas a las que apuntan las claves ajenas.&lt;/p&gt;

&lt;p&gt;Para implementar el soporte multientidad de forma transparente aprovecharemos la funcionalidad de &lt;a href="https://docs.jboss.org/hibernate/orm/3.3/reference/en/html/filters.html"&gt;definición de filtros de Hibernate&lt;/a&gt; que nos permite activar o desactivar ciertas condiciones en la sesión de Hibernate para filtrar las consultas de datos. Después de realizar las modificaciones en el esquema de base de datos, crearemos un interfaz con los métodos getter y setter para este nuevo atributo discriminador. Cada clase de dominio de primer nivel que queramos independizar por entidad deberá implementar este interfaz.&lt;/p&gt;

&lt;div class="language-java highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="c1"&gt;//src/main/java/com/foo/bar/domain/FiltrarPorEntidad.java&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;FiltrarPorEntidad&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="no"&gt;FILTRO&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"FILTRAR_POR_ENTIDAD"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Integer&lt;/span&gt; &lt;span class="nf"&gt;getEntidad&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setEntidad&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Integer&lt;/span&gt; &lt;span class="n"&gt;entidad&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Lo siguiente será definir la condición y los parámetros del filtro que utilizaremos para filtrar los datos, tan simple como agregar la condición &lt;code class="language-plaintext highlighter-rouge"&gt;entidad = :entidad&lt;/code&gt;. El filtro lo definimos a nivel de paquete para tenerlo disponible de forma global para todas las clases de dominio.&lt;/p&gt;

&lt;div class="language-java highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="c1"&gt;//src/main/java/com/foo/bar/domain/package-info.java&lt;/span&gt;
&lt;span class="nd"&gt;@FilterDef&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;defaultCondition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"entidad = :entidad"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
           &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FiltrarPorEntidad&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;FILTRO&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
           &lt;span class="n"&gt;parameters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;@ParamDef&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"entidad"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"int"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.foo.bar.domain&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.hibernate.annotations.FilterDef&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.hibernate.annotations.ParamDef&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Ahora tendremos que modificar cada una de las clases de dominio, indicando la disponibilidad del filtro por entidad, agregando el interfaz y el nuevo atributo:&lt;/p&gt;

&lt;div class="language-diff highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt; @Entity
 @Table(name="Nodo")
&lt;span class="gi"&gt;+@Filter(name=FiltrarPorEntidad.FILTRO)
&lt;/span&gt;&lt;span class="gd"&gt;-public class Nodo extends BaseEntidad
&lt;/span&gt;&lt;span class="gi"&gt;+public class Nodo extends BaseEntidad implements FiltrarPorEntidad 
+    private Integer entidad;
+
+    @Column (name = "ENTIDAD", nullable = false)
+    public Integer getEntidad() {
+        return entidad;
+    }
+
+    public void setEntidad(Integer entidad) {
+        this.entidad = entidad;
+    }
&lt;/span&gt;     ...
 }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id="filtrando-datos-en-las-consultas"&gt;Filtrando datos en las consultas&lt;/h2&gt;

&lt;p&gt;Ya tendríamos marcadas todas las entidades que necesitamos filtrar. Ahora tendríamos que activar el filtro en cada consulta que realicemos. Es habitual que nuestras clases de servicio o DAOs hereden de una clase base que agregue cierta lógica común. Aprovecharemos esta clase para activar el filtro cada vez que obtengamos el objeto sesión de Hibernate con la que ejecutamos las sentencias HQL:&lt;/p&gt;

&lt;div class="language-java highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BaseDO&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;

    &lt;span class="kd"&gt;protected&lt;/span&gt; &lt;span class="nc"&gt;Session&lt;/span&gt; &lt;span class="nf"&gt;getSession&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Session&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sessionFactory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCurrentSession&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;session&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;enableFilter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;FiltrarPorEntidad&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;FILTRO&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setParameter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"entidad"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;getEntidadActiva&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;protected&lt;/span&gt; &lt;span class="nc"&gt;Query&lt;/span&gt; &lt;span class="nf"&gt;getNamedQueryPorEntidad&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;queryName&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Session&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sessionFactory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCurrentSession&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="nc"&gt;Query&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getNamedQuery&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;queryName&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setParameter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"entidad"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;getEntidadActiva&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;Integer&lt;/span&gt; &lt;span class="nf"&gt;getEntidadActiva&lt;/span&gt; &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// obtener la entidad activa actualmente&lt;/span&gt;
        &lt;span class="c1"&gt;// usando alguna clase utilidad o usando ThreadLocal &lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Una parte interesate es cómo saber qué entidad es la activa en cada momento. Esto dependerá de la arquitectura de cada proyecto. Lo mas habitual es crear algún tipo de Filter o Interceptor que se ejecute al principio de cada petición y que averigüe la entidad, ya sea viendo el dominio, la url, algún parámetro de sesión o extrayéndolo del usuario autenticado. Una vez averiguado estableceremos el valor de la entidad activa en alguna clase utilidad u objeto ThreadLocal que podamos consultar desde &lt;code class="language-plaintext highlighter-rouge"&gt;BaseDAO.getEntidadActiva()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Hasta aquí habríamos conseguido que de forma transparente cada vez que en una consulta aparezca alguna clase de dominio se agregue el filtro por entidad de forma automática.&lt;/p&gt;

&lt;h2 id="entidad-en-nuevos-objetos"&gt;Entidad en nuevos objetos&lt;/h2&gt;

&lt;p&gt;Ya tendríamos las consulta de datos filtrada pero nos quedaría la última parte, que sería establecer la entidad en los objetos de nueva creación. No queremos que los desarrolladores deban acordarse de llamar a los métodos &lt;code class="language-plaintext highlighter-rouge"&gt;setEntidad&lt;/code&gt; cada vez que vayan a persistir un nuevo objeto. Utilizaremos un listener de Hibernate para las operaciones de guardar o actualizar datos que registraremos en el framework de Hibernate mediante la creación de una clase de integración. Comenzamos por crear un fichero &lt;code class="language-plaintext highlighter-rouge"&gt;META-INF/services/org.hibernate.integrator.spi.Integrator&lt;/code&gt; que contendrá el paquete y clase que implementará esta integración:&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;//src/main/resources/META-INF/services/org.hibernate.integrator.spi.Integrator
com.foo.bar.domain.MultiEntidadIntegrator
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;La clase integración registrará nuestro listener que establecerá el campo entidad en los nuevos objetos que queramos persistir. Ademas, en el caso de las actualizaciones, comprobaremos que estamos modificando solamente objetos de la entidad activa, evitando por tanto que por error modifiquemos objetos que no pertenecen a la entidad del usuario que realiza la operación.&lt;/p&gt;

&lt;div class="language-java highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MultiEntidadIntegrator&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;Integrator&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;integrate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Configuration&lt;/span&gt; &lt;span class="n"&gt;configuration&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                          &lt;span class="nc"&gt;SessionFactoryImplementor&lt;/span&gt; &lt;span class="n"&gt;sessionFactory&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                          &lt;span class="nc"&gt;SessionFactoryServiceRegistry&lt;/span&gt; &lt;span class="n"&gt;serviceRegistry&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

        &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;EventListenerRegistry&lt;/span&gt; &lt;span class="n"&gt;eventListenerRegistry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;serviceRegistry&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getService&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;EventListenerRegistry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;MultiEntidadListener&lt;/span&gt; &lt;span class="n"&gt;el&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MultiEntidadListener&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;eventListenerRegistry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;prependListeners&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;EventType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;SAVE_UPDATE&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;el&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;integrate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;MetadataImplementor&lt;/span&gt; &lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                          &lt;span class="nc"&gt;SessionFactoryImplementor&lt;/span&gt; &lt;span class="n"&gt;sessionFactory&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                          &lt;span class="nc"&gt;SessionFactoryServiceRegistry&lt;/span&gt; &lt;span class="n"&gt;serviceRegistry&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;disintegrate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;SessionFactoryImplementor&lt;/span&gt; &lt;span class="n"&gt;sessionFactory&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                             &lt;span class="nc"&gt;SessionFactoryServiceRegistry&lt;/span&gt; &lt;span class="n"&gt;serviceRegistry&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MultiEntidadListener&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;SaveOrUpdateEventListener&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;serialVersionUID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1L&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onSaveOrUpdate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;SaveOrUpdateEvent&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;HibernateException&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Object&lt;/span&gt; &lt;span class="n"&gt;entity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getObject&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entity&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nc"&gt;FiltrarPorEntidad&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;FiltrarPorEntidad&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;FiltrarPorEntidad&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="nc"&gt;Integer&lt;/span&gt; &lt;span class="n"&gt;entidadActiva&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BaseDAO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getEntidadActiva&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getEntidad&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setEntidad&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entidadActiva&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getEntidad&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;entidadActiva&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;mensaje&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                    &lt;span class="s"&gt;"Intento de cambiar un objeto %s de la entidad %d a la %d"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getClass&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;
                    &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getEntidad&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;
                    &lt;span class="n"&gt;entidadActiva&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;RuntimeException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mensaje&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id="conclusión"&gt;Conclusión&lt;/h2&gt;

&lt;p&gt;Con esta técnica hemos conseguido agregar el soporte multientidad a una aplicación existente para dar servicio a nuevos clientes minimizando el impacto necesario para llevarlo a cabo. También deberemos agregar una gestión mínima de entidades accesible por un perfil superadministrador que nos permita la creación de nuevas entidades.&lt;/p&gt;

&lt;p&gt;Existen otros enfoques que también podrían haber sido interesantes de abordar como una solución PL/SQL completamente, mediante el uso de vistas de las tablas que filtren los datos según algún parámetro de sesión de base de datos que pudiéramos establecer (&lt;code class="language-plaintext highlighter-rouge"&gt;SYS_CONTEXT&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Y por último algunos enlaces de interés:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="http://docs.oracle.com/cd/B19306_01/server.102/b14200/functions165.htm"&gt;http://docs.oracle.com/cd/B19306_01/server.102/b14200/functions165.htm&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://stackoverflow.com/questions/25855260/global-hibernate-filter-on-all-database-queries"&gt;https://stackoverflow.com/questions/25855260/global-hibernate-filter-on-all-database-queries&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://www.mkyong.com/hibernate/hibernate-data-filter-example-xml-and-annotation/"&gt;https://www.mkyong.com/hibernate/hibernate-data-filter-example-xml-and-annotation/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://picodotdev.github.io/blog-bitix/2015/02/ejemplo-de-listener-de-eventos-de-hibernate/"&gt;https://picodotdev.github.io/blog-bitix/2015/02/ejemplo-de-listener-de-eventos-de-hibernate/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://stackoverflow.com/questions/5150660/adding-listener-to-hibernate-session"&gt;https://stackoverflow.com/questions/5150660/adding-listener-to-hibernate-session&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
				<pubDate>Wed, 27 Feb 2019 00:00:00 +0000</pubDate>
				<link>http://blog.gimco.es/2019/02/27/multientidad-hibernate3.html</link>
				<guid isPermaLink="true">http://blog.gimco.es/2019/02/27/multientidad-hibernate3.html</guid>
			<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Bruno Orcha García</dc:creator></item>
		
			<item>
				<title>ANTLR</title>
				<description>&lt;p&gt;La asignatura de Compiladores en la Universidad fue la más intensa y extenuante con diferencia. Era una asignatura que condensaba una materia teórica amplia y densa en muy corto espacio de tiempo, donde romábamos apuntes a una velocidad endiablada, éramos aprendices de taquígrafos, fotocopiadores de pizarras. No en vano llamábamos a nuestra profesora Turbotere. Nuestra única salvación era acudir al legendario libro del dragón rojo para poder encajar todas las piezas y entender lo que estábamos haciendo. Fue mi primer contacto con las gramáticas, analizadores léxico-sintácticos y la utilización de las herramientas lex/flex/yacc/bison.&lt;/p&gt;

&lt;p&gt;&lt;img src="/assets/antlr-libro.png" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;La segunda incursión fue mas satisfactoria con la utilización de &lt;a href="https://www.antlr.org/"&gt;ANTLR&lt;/a&gt; y la creación de un compilador para un lenguaje de programación pseudocódigo inventado. Ver que puedes programar usando un lenguaje que tú mismo has diseñado y que después puedes compilarlo y ejecutarlo como cualquier otro lenguaje es inspirador.&lt;/p&gt;

&lt;p&gt;Aunque este tipo de herramientas son muy poderosas pertenecen una rama de la informática poco habitual para la gran mayoría de desarrolladores de aplicaciones web.&lt;/p&gt;

&lt;h2 id="refactorización"&gt;Refactorización&lt;/h2&gt;

&lt;p&gt;Durante la migración de una aplicación legacy con mas de dos décadas a sus espaldas y programada en el lenguaje de programación Natural de Software AG (un lenguaje similar a COBOL) tuve la oportunidad de volver a utilizar algunas de estas herramientas. Si bien, uno no de los primeros consejos cuando intentamos integrarnos o sustituir estos sistemas legacy es “don’t feed the monster” (implementar nuevo código en el sistema antiguo debería ser la última de nuestras opciones), mediante la creación de un parseador en ANTLR para el lenguaje NATURAL, pudimos analizar miles y miles de ficheros de código fuente y obtener información muy valiosa y precisa.&lt;/p&gt;

&lt;p&gt;Por ejemplo, pudimos analizar las dependencias del sistema, obteniendo así un grafo de todas las llamadas e importaciones desde el módulo principal a los distintos módulos y librerías. Gracias a esto pudimos conseguir un listado exacto de los objetos realmente utilizados por la aplicación, pudiendo descartar centenares de subprogramas obsoletos que ya no se utilizaban (no eran invocados por nadie).&lt;/p&gt;

&lt;p&gt;El entorno de desarrollo disponible para interactuar con este sistema es bastante arcaico, siendo la única herramienta disponible un simple buscar y reemplazar (y nada de expresiones regulares por supuesto).&lt;/p&gt;

&lt;p&gt;&lt;img src="/assets/natural-scan-replace.png" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;Ya que con el parseador somos capaces de “entender” el código fuente, pudimos realizar refactorizaciones de código precisas de forma automática de distintas partes de la aplicación, pudiendo agregar por ejemplo llamadas de auditoria y notificación de eventos en las operaciones de modificación de base de datos por ejemplo.&lt;/p&gt;

&lt;p&gt;Analizando la definición de los ficheros en la base de datos ADABAS que se utilizaba, creamos herramientas que generaban el código SQL DDL, triggers y PL/SQL necesario para exportar, replicar o sincronizar de forma bidireccional los datos en una base de datos Oracle con ADABAS.&lt;/p&gt;

&lt;h2 id="ejemplo"&gt;Ejemplo&lt;/h2&gt;

&lt;p&gt;Históricamente Java ha sido un lenguaje poco conciso (la inclusión de var, y lambdas en las últimas versiones han ido mejorando esto): creación de getters y setters, definición de logs en cada clase, métodos toString y equals, llamadas a los métodos close/dispose, etc. 
&lt;a href="https://projectlombok.org/"&gt;Lombok&lt;/a&gt; es una librería capaz de enriquecer el código java y evitar tanto código denso y repetitivo. Una de las anotaciones mas útiles de Lombok es &lt;a href="https://projectlombok.org/features/Data"&gt;@Data&lt;/a&gt; que usado en nuestros beans nos evita tener que crear los getters, setters y métodos toString y hashCode.&lt;/p&gt;

&lt;p&gt;&lt;img src="/assets/lombok-data.jpg" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;Veamos un ejemplo de como crear una pequeña utilidad de refactorización que elimine todos los métodos de una clase y agregue la anotación &lt;code class="language-plaintext highlighter-rouge"&gt;@Data&lt;/code&gt; usando ANTLR. Para este ejemplo creamos un simple proyecto java, nos descargamos la librería de ANTLR y una de las &lt;a href="https://github.com/antlr/grammars-v4/tree/master/java"&gt;gramáticas de java&lt;/a&gt; de ejemplo. Procesamos con ANTLR las gramáticas para generar los ficheros necesarios para interactuar con nuestro parseador:&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;$ cd src
$ ls
JavaLexer.g4
JavaParser.g4

$ java -jar ../antlr-4.7.1-complete.jar JavaLexer.g4

$ java -jar ../antlr-4.7.1-complete.jar JavaParser.g4

$ ls
JavaLexer.g4
JavaLexer.interp
JavaLexer.java
JavaLexer.tokens
JavaParser.g4
JavaParser.interp
JavaParser.java
JavaParser.tokens
JavaParserBaseListener.java
JavaParserListener.java
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Una vez generados (y compilados) estos ficheros podríamos ver los tokens que son interpretados por el analizador o ver gráficamente el árbol sintáctico de algún fichero de entrada:&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;$ java -cp ../antlr-4.7.1-complete.jar:../bin org.antlr.v4.gui.TestRig \
        Java compilationUnit -tokens Persona.java

[@0,0:5='import',&amp;lt;'import'&amp;gt;,1:0]
[@1,6:6=' ',&amp;lt;WS&amp;gt;,channel=1,1:6]
[@2,7:10='java',&amp;lt;IDENTIFIER&amp;gt;,1:7]
[@3,11:11='.',&amp;lt;'.'&amp;gt;,1:11]
[@4,12:15='util',&amp;lt;IDENTIFIER&amp;gt;,1:12]
[@5,16:16='.',&amp;lt;'.'&amp;gt;,1:16]
[@6,17:20='Date',&amp;lt;IDENTIFIER&amp;gt;,1:17]
[@7,21:21=';',&amp;lt;';'&amp;gt;,1:21]
[@8,22:23='\n\n',&amp;lt;WS&amp;gt;,channel=1,1:22]
[@9,24:29='public',&amp;lt;'public'&amp;gt;,3:0]
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id="creando-nuestras-herramientas"&gt;Creando nuestras herramientas&lt;/h2&gt;

&lt;p&gt;Crearemos una clase listener capaz de recibir los eventos producidos por el parseador tanto a la hora de comenzar la ejecución de una regla como al terminar una regla. Utilizaremos un objeto de tipo &lt;a href="https://www.antlr.org/api/Java/org/antlr/v4/runtime/TokenStreamRewriter.html"&gt;TokenStreamRewriter&lt;/a&gt; que es el que nos permite realizar operaciones de inserción, modificación o borrado de tokens con los cambios que queremos en el fichero de salida.&lt;/p&gt;

&lt;div class="language-java highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LombokDataDomainRefactor&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;JavaParserBaseListener&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;TokenStreamRewriter&lt;/span&gt; &lt;span class="n"&gt;rewriter&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;LombokDataDomainRefactor&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;CommonTokenStream&lt;/span&gt; &lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;rewriter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TokenStreamRewriter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;IOException&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;En primer lugar crearemos un analizador léxico para nuestro fichero java de entrada, que puede ser cualquier clase Java Bean. Con este analizador léxico obtendremos un stream de tokens, los mismos que vimos con el ejemplo anterior al utilizar la utilidad &lt;code class="language-plaintext highlighter-rouge"&gt;TestRig&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Seguidamente crearemos un analizar sintáctico para este stream de tokens, y obtendremos el árbol sintáctico utilizando la regla principal de nuestra gramática, en este caso &lt;code class="language-plaintext highlighter-rouge"&gt;compilationUnit&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Por último recorreremos este árbol parseado usando nuestro listener, que recibirá los eventos sobre la entrada y salida de las distintas reglas de la gramática:&lt;/p&gt;

&lt;div class="language-java highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;IOException&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;unit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"src/Persona.java"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nc"&gt;JavaLexer&lt;/span&gt; &lt;span class="n"&gt;lexer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;JavaLexer&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;CharStreams&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromFileName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;unit&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="nc"&gt;CommonTokenStream&lt;/span&gt; &lt;span class="n"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CommonTokenStream&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lexer&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="nc"&gt;JavaParser&lt;/span&gt; &lt;span class="n"&gt;parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;JavaParser&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="nc"&gt;ParseTree&lt;/span&gt; &lt;span class="n"&gt;tree&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;compilationUnit&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

    &lt;span class="nc"&gt;ParseTreeWalker&lt;/span&gt; &lt;span class="n"&gt;walker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ParseTreeWalker&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="nc"&gt;LombokDataDomainRefactor&lt;/span&gt; &lt;span class="n"&gt;listener&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;LombokDataDomainRefactor&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;walker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;walk&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;listener&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tree&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;rewrite&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;listener&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;rewriter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getText&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="nc"&gt;FileOutputStream&lt;/span&gt; &lt;span class="n"&gt;fos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FileOutputStream&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;unit&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;fos&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;write&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rewrite&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getBytes&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="n"&gt;fos&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;close&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Es el momento de realizar las operaciones. Cuando entremos en la regla de declaración de un método (&lt;code class="language-plaintext highlighter-rouge"&gt;methodDeclaration&lt;/code&gt;), utilizaremos nuestro reescritor de tokens, y eliminaremos todos los tokens que lo componen:&lt;/p&gt;

&lt;div class="language-java highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Override&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;enterMethodDeclaration&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;JavaParser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;MethodDeclarationContext&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;JavaParser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ClassBodyDeclarationContext&lt;/span&gt; &lt;span class="n"&gt;parent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;JavaParser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ClassBodyDeclarationContext&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;rewriter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;delete&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stop&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Por último, agregamos la línea del import y la anotación &lt;code class="language-plaintext highlighter-rouge"&gt;@Data&lt;/code&gt; justo antes del comienzo de declaración de la clase (regla &lt;code class="language-plaintext highlighter-rouge"&gt;typeDeclaration&lt;/code&gt;)&lt;/p&gt;

&lt;div class="language-java highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Override&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;enterCompilationUnit&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;JavaParser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;CompilationUnitContext&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;JavaParser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;TypeDeclarationContext&lt;/span&gt; &lt;span class="n"&gt;typeDeclaration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;typeDeclaration&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;rewriter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;insertBefore&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;typeDeclaration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"import lombok.Data;\n\n@Data\n"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id="conclusiones"&gt;Conclusiones&lt;/h2&gt;

&lt;p&gt;ANTLR es una herramienta muy potente aunque de muy bajo nivel. Conocer este tipo de herramientas puede hacer que determinados problemas que parecen imposibles e inabarcables se conviertan en algo relativamente sencillo. Crear la gramática para el lenguage Natural requirió superar varias dificultades como la utilización de Island Grammars, Lexical Modes, &lt;a href="https://stackoverflow.com/questions/41421644/antlr4-how-to-build-a-grammar-allowed-keywords-as-identifier"&gt;keywords as identifiers&lt;/a&gt; o la inyección de tokens virtuales (END ALL).&lt;/p&gt;

&lt;p&gt;Por último, otra de las opciones (aunque inacabadas) fue la posibilidad de utilizar &lt;a href="https://www.eclipse.org/Xtext"&gt;Xtext&lt;/a&gt;. Xtext es un framework que nos permite desarrollar nuestro propios lenguajes de programación y DSL que funcionen bajo la JVM. Pero en lugar de limitarse a generar el parseador, es capaz de generar los plugins necesarios para los IDE Eclipse y IntelliJ. Usando Xtext seríamos capaz de crear nuestro propio editor inteligente con colores para la sintaxis, control de errores, navegación entre ficheros/métodos, auto completar, refactorizar, etc. además de que nuestro nuevo lenguaje podría convivir dentro de cualquier proyecto Java, tal y como ocurre con Groovy, Scala o Clojure. Desde luego una opción muy interesante para dotar de nueva vida a lenguajes y sistemas legacy con décadas a sus espaldas.&lt;/p&gt;

</description>
				<pubDate>Sat, 26 Jan 2019 00:00:00 +0000</pubDate>
				<link>http://blog.gimco.es/2019/01/26/antlr.html</link>
				<guid isPermaLink="true">http://blog.gimco.es/2019/01/26/antlr.html</guid>
			<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Bruno Orcha García</dc:creator></item>
		
			<item>
				<title>Marco de fotos interactivo con Raspberry Pi</title>
				<description>&lt;p&gt;La &lt;a href="https://www.raspberrypi.org"&gt;Raspberry Pi&lt;/a&gt; es la plataforma ideal para dar rienda suelta a nuestra imaginación y poder crear proyectos de todo tipo. Usarlo como &lt;a href="https://www.raspberrypi.org/blog/build-remote-control-robot-magpi-51/"&gt;centro de control&lt;/a&gt; para todo tipo de artilugios robóticos, usarlo como NAS o nube personal con &lt;a href="https://syncthing.net"&gt;Syncthing&lt;/a&gt; o &lt;a href="https://owncloud.org"&gt;ownCloud&lt;/a&gt;, &lt;a href="https://www.makeuseof.com/tag/make-wireless-printer-raspberry-pi/"&gt;compartir una impresora en red&lt;/a&gt;, crear un altavoz inalámbrico con &lt;a href="http://www.pimusicbox.com"&gt;Pi MusicBox&lt;/a&gt;, convertir cualquier televisor en un mini ordenador con &lt;a href="https://www.raspbian.org"&gt;rasbpian&lt;/a&gt;, usarlo como centro multimedia con &lt;a href="https://kodi.tv"&gt;Kodi&lt;/a&gt;, o permitirte jugar a juegos retro con &lt;a href="https://retropie.org.uk"&gt;RetroPie&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;En mi caso por ahora se ha reducido al último caso, aprovechando un televisor que teníamos en el jardín y las Raspberry Pi como centro de juegos retro con RetroPie para las tardes de verano (Super Bomberman y Micro Machines &#128525; ).&lt;/p&gt;

&lt;p&gt;&lt;img src="/assets/superbomberman.gif" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;Aunque cuando celebrábamos algún cumpleaños, no sacábamos la Raspberry Pi  para evitar vicios y destrozos con tanto niño suelto, por lo que dejábamos la tele siempre apagada. Pensando que uso le podríamos dar se nos ocurrió que podríamos mostrar una selección de fotos del cumpleañero.&lt;/p&gt;

&lt;h2 id="marco-de-fotos"&gt;Marco de fotos&lt;/h2&gt;

&lt;p&gt;Teniendo en cuenta que la Raspberry Pi ya estaba configurada con RetroPie, y que RetroPie utiliza framebuffer en lugar de X-Windows, busqué alguna utilidad sencilla que permitiera mostrar imágenes como un carrusel de fotos. Íbamos a convertir el televisor en un gran marco de fotos. El comando en cuestión es &lt;a href="http://manpages.ubuntu.com/manpages/xenial/man1/fbi.1.html"&gt;&lt;code class="language-plaintext highlighter-rouge"&gt;fbi&lt;/code&gt;&lt;/a&gt; “Linux framebuffer imageviewer”.&lt;/p&gt;

&lt;p&gt;RetroPie usa la aplicación &lt;a href="http://www.emulationstation.org"&gt;EmulationStation&lt;/a&gt; como interfaz gráfico para navegar por las distintas consolas y juegos. En una de los menús tenemos acceso a múltiples utilidades que nos permiten configurar distintos aspectos del sistemas. Todas estas opciones no son mas que scripts de configuración que existen en el directorio &lt;code class="language-plaintext highlighter-rouge"&gt;/home/pi/RetroPie/retropiemenu&lt;/code&gt;. Agregaremos en este menú una nueva opción para iniciar nuestro carrusel de fotos.&lt;/p&gt;

&lt;p&gt;Crearemos un script con permisos de ejecución en este directorio con nuestro comando, por ejemplo:&lt;/p&gt;

&lt;div class="language-bash highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

fbi &lt;span class="nt"&gt;-noverbose&lt;/span&gt; &lt;span class="nt"&gt;-blend&lt;/span&gt; 1000 &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; 2 imagen1.jpg imagen2.jpg imagen3.jpg ...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;El script anterior, mostrará de forma indefinida las imágenes indicadas, sin información adicional (&lt;code class="language-plaintext highlighter-rouge"&gt;-noverbose&lt;/code&gt;), con un fundido entre imágenes de 1 segundos de duración (&lt;code class="language-plaintext highlighter-rouge"&gt;-blend&lt;/code&gt;), autoajustando el tamaño de las imágenes (&lt;code class="language-plaintext highlighter-rouge"&gt;-a&lt;/code&gt;) y dejando 2 segundos entre cada imagen (&lt;code class="language-plaintext highlighter-rouge"&gt;-t&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;a href=""&gt;&lt;/a&gt; gif animado seleccionando la opción&lt;/p&gt;

&lt;h2 id="compartir-fotos"&gt;Compartir fotos&lt;/h2&gt;

&lt;p&gt;Tener un conjunto de fotos seleccionadas que se muestran está bien, pero más interesante es permitir a los invitados interactuar con lo que están viendo en la televisión y poder compartir con todos sus propias fotos, sobre todo las que puedes hacer en ese mismo momento durante la celebración!&lt;/p&gt;

&lt;p&gt;Para ello crearemos una aplicación web en la propia Raspberry Pi, que mostrará a nuestros usuarios una simple página web que les permita subir una foto de sus móviles. Las fotos las iremos guardando en una carpeta dentro de las Raspberry Pi y con un script similar al anterior se irán mostrando en la pantalla. Por lo que nuestra solución constará de dos partes un script en bash que muestra las fotos y una aplicación web realizada en python en este caso (aprovechando que ya viene instalado).&lt;/p&gt;

&lt;p&gt;Puesto que el comando &lt;code class="language-plaintext highlighter-rouge"&gt;fbi&lt;/code&gt; anterior necesita una lista fija de fotos, lo que haremos es crear un bucle y recalcular la lista de las últimas fotos e invocar al comando &lt;code class="language-plaintext highlighter-rouge"&gt;fbi&lt;/code&gt; con la opción &lt;code class="language-plaintext highlighter-rouge"&gt;-1&lt;/code&gt; para que una vez mostrada las fotos termine su ejecución. Crearemos ademas una condición para permitirnos salir de este bucle que muestra fotos por si queremos volver al menú de retropie.&lt;/p&gt;

&lt;div class="language-bash highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nv"&gt;$STOP_FILE&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    &lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-1t&lt;/span&gt; images/&lt;span class="k"&gt;*&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 25 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$IMAGE_LIST&lt;/span&gt;
    fbi &lt;span class="nt"&gt;-1&lt;/span&gt; &lt;span class="nt"&gt;-noverbose&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; 8 logo.png &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null 2&amp;gt;&amp;amp;1
    fbi &lt;span class="nt"&gt;-1&lt;/span&gt; &lt;span class="nt"&gt;-noverbose&lt;/span&gt; &lt;span class="nt"&gt;-blend&lt;/span&gt; 1000 &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; 2 &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; &lt;span class="nv"&gt;$IMAGE_LIST&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null 2&amp;gt;&amp;amp;1
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Lo primer que hacemos dentro del bucle es obtener las 25 fotos mas recientes y las ponemos en orden aleatorio (sort -R). Mostramos durante 8 segundos la imagen &lt;code class="language-plaintext highlighter-rouge"&gt;logo.png&lt;/code&gt; (que habremos preparado con información del evento, la url para subir fotos, etc.). Por último mostramos la lista de fotos.&lt;/p&gt;

&lt;h2 id="servidor-web-en-python"&gt;Servidor web en python&lt;/h2&gt;

&lt;p&gt;Python ya incluye de serie un servidor web básico que podemos utilizar para compartir la carpeta actual en la dirección &lt;code class="language-plaintext highlighter-rouge"&gt;http://localhost:8080&lt;/code&gt; con el comando:&lt;/p&gt;

&lt;div class="language-bash highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; SimpleHTTPServer 8080
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Para evitar hacer uso de librerías de terceros como Flask, Django o Pyramid, podemos simplemente extender la funcionalidad de este módulo para procesar la subida de los ficheros por el método &lt;code class="language-plaintext highlighter-rouge"&gt;POST&lt;/code&gt;.&lt;/p&gt;

&lt;div class="language-python highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PhotoHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SimpleHTTPRequestHandler&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;do_POST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
		    &lt;span class="c1"&gt;# Guardar la imagen subida por el usuario
&lt;/span&gt;		    &lt;span class="p"&gt;...&lt;/span&gt;
		    
&lt;span class="n"&gt;httpd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SocketServer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TCPServer&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8080&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;PhotoHandler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;httpd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;handle_request&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Por último creamos una sencilla página web que será lo que verán nuestros invitados cuando quieran compartir una foto.&lt;/p&gt;

&lt;p&gt;&lt;img src="/assets/photoframepi-upload.gif" alt="" /&gt;&lt;/p&gt;

&lt;h2 id="publicarlo"&gt;Publicarlo&lt;/h2&gt;

&lt;p&gt;A la hora de publicarlo o hacerlo accesible tenemos varias opciones según la disponibilidad de dominios y el nivel de privacidad que queramos.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Podemos obligar a nuestros invitados a que se conecten a la red wifi del lugar y utilizar la ip local para acceder, al estilo http://192.168.0.X:8080.&lt;/li&gt;
  &lt;li&gt;Si disponemos o registramos un dominio, podemos configurar la opción de iframe (o configurar un cname) y poner como destino la ip interna anterior. De esta manera, el acceso será mediante un dominio reconocible “cumple-de-pepito.es” pero sólo funcionará si estamos conectados a la red wifi del evento.&lt;/li&gt;
  &lt;li&gt;Podemos mapear un puerto del router para exponer el servicio a internet y configurar nuestro dominio a la ip pública del router.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="usar-un-servicio-externo-de-imágenes"&gt;Usar un servicio externo de imágenes&lt;/h2&gt;

&lt;p&gt;En casos de muchas peticiones, me ha ocurrido que las Raspberry Pi se ha reiniciado, probablemente debido a que el cargador utilizado no suministraba la suficiente potencia, mostrándose a veces el cuadrado multicolor en la esquina superior derecha. Además, si no queremos ofrecer ni obligar a los invitados que se conecten al wifi existe otra opción. Podemos utilizar un servicio de almacenamiento de imágenes externo como por ejemplo &lt;a href="https://cloudinary.com"&gt;cloudinary&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Cambiaremos el servidor web anterior por un proceso en python que use la api de cloudinary para descargar las nuevas fotos subidas cada 10 segundos por ejemplo:&lt;/p&gt;

&lt;div class="language-python highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="n"&gt;CLOUDINARY_API&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'https://ID:KEY@api.cloudinary.com/v1_1/gimco/resources/image?max_results=500&amp;amp;direction=asc&amp;amp;start_at=%s'&lt;/span&gt;
&lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'2017-01-01'&lt;/span&gt;

&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CLOUDINARY_API&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt;
        &lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt; &lt;span class="s"&gt;'Buscando nuevos ficheros &amp;gt; '&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt; 
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;urlopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'resources'&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="n"&gt;image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'images/'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'public_id'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;'.jpg'&lt;/span&gt;
            &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'url'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'created_at'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;isfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="s"&gt;'Downlading '&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;image&lt;/span&gt;
                &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;urlretrieve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="s"&gt;'Error!'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exc_info&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="s"&gt;'Fin de comprobacion, esperando 10 segundos'&lt;/span&gt;
    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Además, deberemos subir el contenido de la carpeta web con nuestra página html al espacio estático disponible con nuestro dominio para que sea accesible a los invitados. También deberemos cambiar la dirección a la que subir la imagen por la url del servicio de cloudinary:&lt;/p&gt;

&lt;div class="language-diff highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="p"&gt;@@ -12,13 +12,14 @@&lt;/span&gt;
         &amp;lt;p&amp;gt;Subiendo imagen ...&amp;lt;/p&amp;gt;
         &amp;lt;img src="img/spinner.gif"/&amp;gt;
       &amp;lt;/div&amp;gt;
&lt;span class="gd"&gt;-      &amp;lt;form id="image-form" ENCTYPE="multipart/form-data" method="post"&amp;gt;
&lt;/span&gt;&lt;span class="gi"&gt;+      &amp;lt;form id="image-form" ENCTYPE="multipart/form-data" method="post" action="https://api.cloudinary.com/v1_1/gimco/image/upload"&amp;gt;
&lt;/span&gt;         &amp;lt;p&amp;gt;Pulsa sobre el icono de abajo y selecciona una foto para subir.&amp;lt;/p&amp;gt;
         &amp;lt;p&amp;gt; &#128513; &#127881; &#127873; &#127874; &#127856; &#127882; &#128539; &amp;lt;/p&amp;gt;
         &amp;lt;p&amp;gt;¡En un minuto la podremos ver en la televisión!&amp;lt;/p&amp;gt;
         &amp;lt;div class="image-upload"&amp;gt;
           &amp;lt;label for="file"&amp;gt;&amp;lt;img src="img/camara.png"/&amp;gt;&amp;lt;/label&amp;gt;
           &amp;lt;input id="file" name="file" type="file" accept="image/*" onchange="uploadImage()" /&amp;gt;
&lt;span class="gi"&gt;+          &amp;lt;input type="hidden" name="upload_preset" value="brunito" /&amp;gt;
&lt;/span&gt;         &amp;lt;/div&amp;gt;
       &amp;lt;/form&amp;gt;
     &amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Podremos configurar ademas en cloudinary mas opciones a la hora de subir las imágenes (upload_preset), pudiendo redimensionar, rotar y convertir las imágenes a JPEG.&lt;/p&gt;

&lt;p&gt;Puedes consultar los &lt;a href="https://github.com/gimco/photoframepi"&gt;fuentes del proyecto&lt;/a&gt; en github.&lt;/p&gt;

</description>
				<pubDate>Fri, 29 Dec 2017 00:00:00 +0000</pubDate>
				<link>http://blog.gimco.es/2017/12/29/photoframepi.html</link>
				<guid isPermaLink="true">http://blog.gimco.es/2017/12/29/photoframepi.html</guid>
			<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Bruno Orcha García</dc:creator></item>
		
			<item>
				<title>Tuenti Challenge 7</title>
				<description>&lt;p&gt;Cuando queremos aprender un nuevo lenguaje o paradigma, sólo nos queda practicar para asimilarlo. Es por esto que me gusta realizar problemas y retos no sólo para perfeccionar mi dominio de Clojure, si no también para descubrir y aprender nuevos enfoques a la hora de afrontar los problemas.&lt;/p&gt;

&lt;p&gt;La semana pasada terminó la séptima edición del &lt;a href="https://contest.tuenti.net/Info/about"&gt;Tuenti Challenge&lt;/a&gt;. Una colección de problemas de programación, análisis, optimización y hacking para los amantes de los retos. Podéis &lt;a href="https://contest.tuenti.net/Challenges"&gt;consultar los 15 problemas&lt;/a&gt; de los que constó esta edición en la página del concurso.&lt;/p&gt;

&lt;p&gt;Aquí os dejo el análisis de los problemas y estrategia que implementé de los problemas que pude hacer y el &lt;a href="https://github.com/gimco/programming-challenges/tree/master/tuenti-contest-2017"&gt;código de las soluciones&lt;/a&gt;.&lt;/p&gt;

&lt;ul id="markdown-toc"&gt;
  &lt;li&gt;&lt;a href="#1-pizza-love" id="markdown-toc-1-pizza-love"&gt;1. Pizza love&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="#2-bowling" id="markdown-toc-2-bowling"&gt;2. Bowling&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="#3-board-games" id="markdown-toc-3-board-games"&gt;3.  Board games&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="#4-help-pythagoras-junior" id="markdown-toc-4-help-pythagoras-junior"&gt;4. Help Pythagoras Junior&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="#5-ghost-in-the-http" id="markdown-toc-5-ghost-in-the-http"&gt;5. Ghost in the HTTP&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="#6-the-tower" id="markdown-toc-6-the-tower"&gt;6. The Tower&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="#7-word-soup-challenge" id="markdown-toc-7-word-soup-challenge"&gt;7. Word Soup Challenge&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="#8-uni-code-to-rule-them-all" id="markdown-toc-8-uni-code-to-rule-them-all"&gt;8. Uni code to rule them all&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="#9-the-supreme-scalextric-architect" id="markdown-toc-9-the-supreme-scalextric-architect"&gt;9. The Supreme Scalextric Architect&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="#10-passwords" id="markdown-toc-10-passwords"&gt;10. Passwords&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="#11-colors" id="markdown-toc-11-colors"&gt;11. Colors&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="#12-thats-a-lot-of-moneyz" id="markdown-toc-12-thats-a-lot-of-moneyz"&gt;12. That’s a lot of moneyz&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="#13-rlyeh" id="markdown-toc-13-rlyeh"&gt;13. R’lyeh&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;h2 id="1-pizza-love"&gt;1. Pizza love&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://cdn.rawgit.com/gimco/programming-challenges/master/tuenti-contest-2017/01/01.html"&gt;Problema&lt;/a&gt; y &lt;a href="https://github.com/gimco/programming-challenges/blob/master/tuenti-contest-2017/01/01.clj"&gt;Solución&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;El primer ejercicio suele ser sencillo, destinado principalmente a familiarizarse con el procedimiento de probar, enviar la fase de test y de submit, y tratar con los ejemplos difíciles que suelen desbordar los tipos de datos mas simples.&lt;/p&gt;

&lt;p&gt;En este caso, nos dan una lista de número que representan el número de porciones que come cada persona. Sabiendo que las pizzas traen 8 porciones nuestro objetivo es averiguar cuantas pizzas necesitaremos comprar. Tan solo debemos sumar las porciones de todas las personas, dividir por 8 y redondear al siguiente entero &lt;a href="https://docs.oracle.com/javase/7/docs/api/java/lang/Math.html#ceil(double)"&gt;Math.ceil&lt;/a&gt;.&lt;/p&gt;

&lt;div class="language-clojure highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Math/ceil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;slices&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id="2-bowling"&gt;2. Bowling&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://cdn.rawgit.com/gimco/programming-challenges/master/tuenti-contest-2017/02/02.html"&gt;Problema&lt;/a&gt; y &lt;a href="https://github.com/gimco/programming-challenges/blob/master/tuenti-contest-2017/02/02.clj"&gt;Solución&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;En esta ocasión tenemos que implementar el sistema de puntuación de los bolos. Las partidas constan de 10 rondas y en cada ronda tendremos que intentar tirar los 10 bolos y dos lanzamientos para conseguirlo. Si lo conseguimos a la primera habremos conseguido un “pleno” o “strike”. Si conseguimos tirar todos a la segunda será un “semipleno” o “spare”. Los puntos en cada ronda es igual al número de bolos tirados. Además si hicimos pleno o semipleno, sumaremos a la puntuación de ese ronda los bolos de las siguientes tiradas: los puntos de la siguiente tirada si hicimos semipleno y los puntos de las 2 siguientes tiradas si hicimos pleno.&lt;/p&gt;

&lt;p&gt;Lo que tenemos es una secuencia con los bolos tirados y deberemos mostrar la puntuación obtenida en cada ronda. Hay que tener en cuenta que cada ronda puede constar de una tirada (si hacemos pleno) o de dos, por lo que deberemos ir llevando la cuenta para saber en que ronda nos encontramos. Además deberemos saber cuando se ha hecho pleno y semipleno para sumar puntos extras.&lt;/p&gt;

&lt;p&gt;En el peor de los casos, si hacemos 3 plenos seguidos, estaríamos modificando el marcador de 3 rondas: la actual y las dos anteriores para contabilizar los puntos extras.&lt;/p&gt;

&lt;p&gt;Podríamos ir leyendo tirada a tirada, y actualizando varios indicadores que nos digan si hicimos pleno o semipleno en las dos anteriores rondas. Pero ya que tenemos la secuencia completa de tiradas, es mas sencillo sumar directamente las tiradas futuras.&lt;/p&gt;

&lt;div class="language-clojure highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;defn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bowling-score&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;rolls&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;loop&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;scores&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;'&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;r1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;r2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;r3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rolls&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rolls&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;cond&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;count&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;scores&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;scores&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;reverse&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;r1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;recur&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;conj&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;scores&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;r1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;r2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;r3&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;next&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rolls&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;r1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;r2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;recur&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;conj&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;scores&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;r1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;r2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;r3&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;nnext&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rolls&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="no"&gt;:else&lt;/span&gt;&lt;span class="w"&gt;                 &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;recur&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;conj&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;scores&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;r1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;r2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;nnext&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rolls&lt;/span&gt;&lt;span class="p"&gt;)))))&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id="3-board-games"&gt;3.  Board games&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://cdn.rawgit.com/gimco/programming-challenges/master/tuenti-contest-2017/03/03.html"&gt;Problema&lt;/a&gt; y &lt;a href="https://github.com/gimco/programming-challenges/blob/master/tuenti-contest-2017/03/03.clj"&gt;Solución&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ahora nos metemos en el papel de un diseñador de juegos de mesa. Nuestro objetivo es decidir cuántas cartas de puntos y de que cuantías necesitamos, para que combinándolas podamos conseguir todos las posibles puntuaciones que se pudieran dar en el juego. Por ejemplo, si nos dicen que un determinado juego de mesa se puede conseguir hasta 20 puntos, deberemos poder combinar nuestras cartas de puntos para poder obtener los valores del 1 al 20.&lt;/p&gt;

&lt;p&gt;A priori pareciera que tuviéramos que generar combinaciones de cartas, y probar distintas opciones hasta obtener el mínimo número de cartas necesaria. Este es un problema mucho mas sencillo de lo que aparenta la descripción. Como las cartas no se pueden repetir, vemos que para cada determinada cuantía de puntos habrá cartas que estarán y otras que no estarán. Después de algunas pruebas veremos que el sistema que estamos buscando no es mas que la representación binaria.&lt;/p&gt;

&lt;p&gt;Así que el problema se reduce a contestar, cuantos bits se necesitan para representar cada determinado número, que no es mas que el redondeo hacia arriba del logaritmo en base dos del número máximo.&lt;/p&gt;

&lt;p&gt;Para un juego donde máximo puedes obtener 22 puntos necesitaríamos: &lt;code class="language-plaintext highlighter-rouge"&gt;⌈ log₂ 22 ⌉ = 5&lt;/code&gt;. Por lo que necesitaríamos 5 bits, o lo que es lo mismo, 5 cartas con las potencias de dos: 1 2 4 8 16. En Java tenemos el logaritmo neperiano y en base 10, pero aplicando propiedades podemos obtener el valor dividiendo por el logaritmo de la base que queremos, en nuestro caso:&lt;/p&gt;

&lt;div class="language-clojure highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Math/ceil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Math/log&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Math/log&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Otra opción sin usar logaritmos es ir multiplicando por dos tantas repetidamente hasta que lleguemos o nos pasemos del número objetivo. El número de veces que hemos multiplicado por dos será el número de cartas que necesitemos.&lt;/p&gt;

&lt;h2 id="4-help-pythagoras-junior"&gt;4. Help Pythagoras Junior&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://cdn.rawgit.com/gimco/programming-challenges/master/tuenti-contest-2017/04/04.html"&gt;Problema&lt;/a&gt; y &lt;a href="https://github.com/gimco/programming-challenges/blob/master/tuenti-contest-2017/04/04.clj"&gt;Solución&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Tenemos que ayudar al nieto de Pitágoras a buscar el triangulo con menor perímetros entre todos los triángulos que se puedan formar dada una lista de posibles lados. Los triángulos pueden ser de cualquier tipo (no tienen porqué ser triángulos rectángulos como en el teorema de su abuelo), por lo que para formar un triángulo, la suma de los dos lados mas pequeños debe ser mayor que el lado mas grande. Podríamos probar por fuerza bruta todas las posibles combinaciones de los tres lados, comprobar que formen un triángulo, calcular su perímetro y buscar el mínimo de todos estos valores.&lt;/p&gt;

&lt;p&gt;Aunque hallaríamos la solución, el problema sería computacionalmente inviable ya que el número de operaciones crecería exponencialmente. Debemos optimizar el algoritmo para que se pueda resolver en un tiempo razonable. Lo primero que haremos es ordenar todos los lados de menor a mayor y comenzaremos a probar. El algoritmo consiste en tres bucles anidados que calculan las combinaciones.&lt;/p&gt;

&lt;p&gt;En el momento que encontramos un triángulo &lt;strong&gt;(a, b, c)&lt;/strong&gt; sabemos que este triángulo será el menor de todos los posibles que tengan los lados &lt;strong&gt;a&lt;/strong&gt; y &lt;strong&gt;b&lt;/strong&gt;, ya que el resto de los lados serán mayor que nuestro &lt;strong&gt;c&lt;sub&gt;min&lt;/sub&gt;&lt;/strong&gt;. Esta es una de las podas u optimizaciones que realizaremos.&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;a + b + c  = x
a + b + c' = x'

si c' &amp;gt; c  =&amp;gt;  x' &amp;gt; x
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Junto con el valor del perímetro mínimo deberemos guardar también el valor de &lt;strong&gt;c&lt;sub&gt;min&lt;/sub&gt;&lt;/strong&gt;. En el momento que &lt;strong&gt;b&lt;/strong&gt; sobrepase al &lt;strong&gt;c&lt;sub&gt;min&lt;/sub&gt;&lt;/strong&gt; del triangulo mínimo, todos los perímetros también serán mayores, por lo que debemos probar con el siguiente a.&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;a  + b  + c  = x
a' + b' + c' = x'

a' &amp;gt; a, b' &amp;gt; c   =&amp;gt;  x' &amp;gt; x
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id="5-ghost-in-the-http"&gt;5. Ghost in the HTTP&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://cdn.rawgit.com/gimco/programming-challenges/master/tuenti-contest-2017/05/05.html"&gt;Problema&lt;/a&gt; y &lt;a href="https://github.com/gimco/programming-challenges/tree/master/tuenti-contest-2017/05"&gt;Solución&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Esta es una prueba de investigación. Sólo se nos presenta un enlace que al cargarlo, nos muestra un una página html simple con un mensaje y un nuevo enlace:&lt;/p&gt;

&lt;div class="language-html highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/ghost"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Just a whisper. I hear it in my ghost...&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;También podemos ver que que en el favicon aparece la palabra PUSH que será una pista para resolverlo. Si accedemos al enlace lo único que obtenemos es el texto:&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;iVBORw0KGgoAA
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Que parece corresponder con datos con codificación base64. Si decodificamos estos datos vemos que parece la cabecera de una imagen PNG, pero el fichero es demasiado pequeño y no está completo.&lt;/p&gt;

&lt;div class="language-bash highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"iVBORw0KGgoAA"&lt;/span&gt; | &lt;span class="nb"&gt;base64&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; | xxd
&lt;span class="nb"&gt;base64&lt;/span&gt;: invalid input
00000000: 8950 4e47 0d0a 1a0a 00                   .PNG.....
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Usando como pista el nombre del problema, echaremos un vistazo a las cabeceras HTTP de la petición:&lt;/p&gt;

&lt;div class="language-bash highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nt"&gt;-k&lt;/span&gt; https://52.49.91.111:8443/ghost 
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; GET /ghost HTTP/1.1
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Host: 52.49.91.111:8443
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; User-Agent: curl/7.52.1
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Accept: &lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 
&amp;lt; HTTP/2 200 
&amp;lt; accept-ranges: bytes
&amp;lt; content-type: text/plain&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;charset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;utf-8
&amp;lt; last-modified: Mon, 24 Apr 2017 11:13:13 GMT
&amp;lt; content-length: 3445
&amp;lt; &lt;span class="nb"&gt;date&lt;/span&gt;: Fri, 05 May 2017 06:35:35 GMT
&amp;lt; 
curl: &lt;span class="o"&gt;(&lt;/span&gt;18&lt;span class="o"&gt;)&lt;/span&gt; transfer closed with 3432 bytes remaining to &lt;span class="nb"&gt;read
&lt;/span&gt;iVBORw0KGgoAA
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Aquí podemos ver por un lado, como el comando curl nos informa de que se ha cerrado la conexión. Vemos que la cabecera &lt;code class="language-plaintext highlighter-rouge"&gt;Content-Length&lt;/code&gt; nos informa que el recurso ocupa &lt;strong&gt;3445&lt;/strong&gt; bytes pero el servidor sólo nos ha enviado 13 bytes. Revisando las cabeceras vemos que el servidor acepta rangos &lt;code class="language-plaintext highlighter-rouge"&gt;Accept-Ranges: bytes&lt;/code&gt;. Si probamos a curl a pedir un rango:&lt;/p&gt;

&lt;div class="language-bash highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nt"&gt;-k&lt;/span&gt; https://52.49.91.111:8443/ghost &lt;span class="nt"&gt;-r&lt;/span&gt; 13-
AANSUhEUgAAAN
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Nos devuelve los siguientes 13 bytes del fichero!. Así que lo que tenemos que hacer es seguir haciendo peticiones en bloques de 13 bytes hasta obtener el fichero competo:&lt;/p&gt;

&lt;div class="language-bash highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="k"&gt;for &lt;/span&gt;r &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;seq &lt;/span&gt;0 13 3445&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  curl &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nt"&gt;-k&lt;/span&gt; https://52.49.91.111:8443/ghost &lt;span class="nt"&gt;-r&lt;/span&gt; 13- &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; ghost.b64 &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Una vez descargado y decodificado con base64 vemos que se trata de &lt;a href="https://raw.githubusercontent.com/gimco/programming-challenges/master/tuenti-contest-2017/05/ghost.png"&gt;una imagen con el número &lt;code class="language-plaintext highlighter-rouge"&gt;4017-8120&lt;/code&gt;&lt;/a&gt; que parece ser un rango … Si probamos a pedir este rango concreto al servidor obtenemos la siguiente respuesta:&lt;/p&gt;

&lt;div class="language-bash highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nt"&gt;-k&lt;/span&gt; https://52.49.91.111:8443/ghost &lt;span class="nt"&gt;-r&lt;/span&gt; 4017-8120
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; GET /ghost HTTP/1.1
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Host: 52.49.91.111:8443
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Range: &lt;span class="nv"&gt;bytes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4017-8120
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; User-Agent: curl/7.52.1
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Accept: &lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 
&amp;lt; HTTP/2 200 
&amp;lt; content-type: text/plain&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;charset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;utf-8
&amp;lt; content-length: 48
&amp;lt; &lt;span class="nb"&gt;date&lt;/span&gt;: Fri, 05 May 2017 06:45:12 GMT
&amp;lt; 
You found me. Pushing my token, did you get it?
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Otra vez referencia a la palabra “push”. Podemos percatarnos que el servidor nos informa que soporta HTTP/2. Una de las nuevas funcionalidades de HTTP/2 es &lt;a href="https://en.wikipedia.org/wiki/HTTP/2_Server_Push"&gt;Server Push&lt;/a&gt;, que es la posibilidad de que el servidor envíe recursos antes que el cliente los solicite. Esto se hace para acelerar la carga de páginas, ya que con una única petición a un html, el servidor podría devolvernos el html solicitado junto las imágenes y el css que probablemente se vayan a pedir posteriormente.&lt;/p&gt;

&lt;p&gt;El comando curl no soporta push, así que probamos con otro cliente que soporte HTTP/2 Server Push, y esta vez si vemos que el servidor nos había enviado (por push) la clave que estábamos buscando:&lt;/p&gt;

&lt;div class="language-bash highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;nghttp https://52.49.91.111:8443/ghost &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Range: bytes=4017-8120"&lt;/span&gt;
YourEffortToRemainWhatYouAreIsWhatLimitsYou
You found me. Pushing my token, did you get it?
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id="6-the-tower"&gt;6. The Tower&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://cdn.rawgit.com/gimco/programming-challenges/master/tuenti-contest-2017/06/06.html"&gt;Problema&lt;/a&gt; y &lt;a href="https://github.com/gimco/programming-challenges/blob/master/tuenti-contest-2017/06/06.clj"&gt;Solución&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Este problema parece estar inspirado la serie &lt;a href="http://towerofgod.wikia.com/wiki/Tower_of_God_(series)"&gt;Tower of God&lt;/a&gt;. Nuestro objetivo es subir a lo alto de una torre de &lt;strong&gt;N&lt;/strong&gt; plantas. Si estamos en la planta &lt;strong&gt;x&lt;/strong&gt;, subir la escalera a la siguiente planta nos llevará &lt;strong&gt;x&lt;/strong&gt; años. Bajar a la planta anterior no tiene coste. También nos informan de una serie de atajos entre plantas (que pueden subir o bajar). Nos piden averiguar el mínimo número de años que tardaríamos en llegar a lo alto de la torre.&lt;/p&gt;

&lt;p&gt;Rápidamente se intuye que es un problema de grafos, donde cada planta puede ser un nodo, y los atajos y escaleras serán las aristas que tendrán distintos pesos en años. Así pues, tan solo deberemos aplicar el algoritmo de Dijkstra que nos averigüe el camino mas corto. Pero como siempre no suele ser tan sencillo. Generar un nodo por cada planta puede servir para los ejemplos simples, pero cuando nos encontramos con torres que tienen 10000 plantas, los tiempos se disparan.&lt;/p&gt;

&lt;p&gt;Si analizamos el problema vemos que podemos simplificar el grafo. Un grafo con 100 nodos que se corresponda al camino de la planta 1 a la planta 100, sería equivalente a un grafo con 2 nodos, cuya arista cueste lo mismo que el sumatorio de todas las anteriores.&lt;/p&gt;

&lt;p&gt;&lt;img src="/assets/tower-of-god.png" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;Por lo para la solución final formaremos un grafo que estará formado únicamente por los nodos que pertenezcan a atajos (además de la primera y última planta). A estos nodos agregamos las arista que corresponden a los atajos. Las aristas de coste cero cuando vamos hacia atrás, y calculamos las aristas que corresponderían al camino normal de subir las escaleras.&lt;/p&gt;

&lt;h2 id="7-word-soup-challenge"&gt;7. Word Soup Challenge&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://cdn.rawgit.com/gimco/programming-challenges/master/tuenti-contest-2017/07/07.html"&gt;Problema&lt;/a&gt; y &lt;a href="https://github.com/gimco/programming-challenges/tree/master/tuenti-contest-2017/07"&gt;Solución&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Si accedemos al enlace nos aparece el juego de sopa de letras. En este nivel tenemos 5 minutos para resolverlo. Si lo hacemos manualmente se nos mostrará el siguiente nivel que es una monstruosidad que ha de resolverse en 20 segundos.&lt;/p&gt;

&lt;p&gt;&lt;img src="/assets/word-soup.png" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;Existen &lt;a href="https://raw.githubusercontent.com/bunkat/wordfind/master/src/wordfind.js"&gt;solucionadores de sopa de letras en javascript&lt;/a&gt;, por lo que parece que es tan simple como simular los clicks del ratón y listo. Pero como siempre, resulta que no es tan sencillo. Si vemos el código fuente vemos que el código JavaScript se carga dinámicamente a través de una conexión websocket. Además, los distintos intentos de hacer click desde javascript son infructuosos.&lt;/p&gt;

&lt;p&gt;Usando las developer tools de chrome podemos poner un breakpoint global sobre los eventos de ratón. Al hacer click sobre una letra, podremos ver el código javascript que se ejecuta. Ahí podemos ver que se sobre escribe la variable $ y que se comprueba que el evento del ratón tenga activada la opción &lt;a href="https://developer.mozilla.org/es/docs/Web/API/Event/isTrusted"&gt;isTrusted&lt;/a&gt; que indica que es un evento de ratón real, motivo por el cual no podemos simularla con javascript.&lt;/p&gt;

&lt;p&gt;Después de analizar un poco el código vemos que podemos comunicarnos directamente con el websocket utilizando estas dos funciones:&lt;/p&gt;

&lt;div class="language-javascript highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="nx"&gt;m&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;o&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;-saltbae&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;o&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;o&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;charCodeAt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;|=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;mark&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;btoa&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Por lo que sólo nos queda cargar jQuery, Wordfind, buscar la lista de palabras, el array bidimensional con las letras y solucionarlo:&lt;/p&gt;

&lt;div class="language-javascript highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="nx"&gt;solve&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;words&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;div[id^=word-]&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toArray&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerText&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;board&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tr&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toArray&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;jQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;td&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toArray&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerText&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="nx"&gt;wordfind&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;solve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;board&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;words&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;found&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mark&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id="8-uni-code-to-rule-them-all"&gt;8. Uni code to rule them all&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://cdn.rawgit.com/gimco/programming-challenges/master/tuenti-contest-2017/08/08.html"&gt;Problema&lt;/a&gt; y &lt;a href="https://github.com/gimco/programming-challenges/blob/master/tuenti-contest-2017/08/08.clj"&gt;Solución&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Aquí ponen a prueba nuestra conocimiento sobre &lt;a href="https://es.wikipedia.org/wiki/Unicode"&gt;Unicode&lt;/a&gt;. El problema es tan sencillo como detectar en qué filas aparece un número, ignorando posibles espacios en blanco delante y detrás de este. Una vez detectado el número hay que mostrarlo en hexadecimal.&lt;/p&gt;

&lt;p&gt;Puede que los ejemplos parezcan confusos al principio, hasta que caemos en la cuenta que los dígitos se pueden representar de distinta forma en otros idiomas!. Por ejemplo, esto es un teclado árabe donde se puede apreciar los grafos correspondientes a las letras y números occidentales:&lt;/p&gt;

&lt;p&gt;&lt;img src="/assets/arab-keyboard.jpg" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;Ya en el propio enunciado te indican que los números pueden estar representados en distintos idiomas. Unicode también agrupa los símbolos por categorias, concretamente tenemos que ver las dos que ya nos dicen en el enunciado, la &lt;a href="http://www.fileformat.info/info/unicode/category/Nd/list.htm"&gt;Nd&lt;/a&gt; y la &lt;a href="http://www.fileformat.info/info/unicode/category/Zs/list.htm"&gt;Zs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Afortunadamente Java tiene soporte para Unicode, y lo que es aún mejor, las propias expresiones regulares soportan categorías de Unicode. Así que una expresión regular típica que busca dígitos ignorando los espacios que sería:&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;\s*\d+\s*
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;extendiendo esta expresión regular a las categorías Unicode sería:&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;\p{Zs}*\p{Nd}+\p{Zs}*
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Tan solo debemos leer el fichero adecuadamente. Analizando los ficheros de prueba averiguamos que están en &lt;code class="language-plaintext highlighter-rouge"&gt;UTF-16 Little-endian&lt;/code&gt;, y que al principio del fichero tenemos dos bytes que forman el &lt;a href="https://es.wikipedia.org/wiki/Marca_de_orden_de_bytes"&gt;BOM (Byte Order Mark)&lt;/a&gt;, bytes que nos informan que codificación se está utilizando. Como nosotros ya estamos forzando a que sea UTF-16LE podemos ignorar estos bytes.&lt;/p&gt;

&lt;p&gt;Por último y como viene siendo habitual, hay que utilizar &lt;a href="https://docs.oracle.com/javase/7/docs/api/java/math/BigInteger.html"&gt;BigInteger&lt;/a&gt; para poder tratar con los números gigantes que vienen en las pruebas, además de que esta clase soporta la conversión de cadena Unicode a número, y la transformación de número a hexadecimal.&lt;/p&gt;

&lt;h2 id="9-the-supreme-scalextric-architect"&gt;9. The Supreme Scalextric Architect&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://cdn.rawgit.com/gimco/programming-challenges/master/tuenti-contest-2017/09/09.html"&gt;Problema&lt;/a&gt; y &lt;a href="https://github.com/gimco/programming-challenges/blob/master/tuenti-contest-2017/09/09.clj"&gt;Solución&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Este ha sido uno de mis favoritos. Dado un conjunto de piezas (secciones simples, dobles y curvas), nuestra misión es averiguar las piezas necesarias para construir el circuito mas grande posible. A priori el problema parece muy complejo ya que el número de combinaciones posibles parece bastante grande.&lt;/p&gt;

&lt;p&gt;Pero pensamos en primer lugar en el circuito cerrado mas pequeño posible, nos daremos cuenta que es el compuesto por 4 curvas formando un círculo. De esto sabemos que si en el conjunto de piezas, no hay al menos 4 curvas, es imposible hacer un circuito cerrado.&lt;/p&gt;

&lt;p&gt;Una vez tenemos el circuito mas pequeño podemos pensar en como agrandarlo. Vemos claramente que no se pueden agregar piezas individuales, si no que cada operaciones de crecimiento del circuito consiste en agregar un conjunto de piezas.&lt;/p&gt;

&lt;p&gt;Por ejemplo, una transformación sería agregar dos piezas simples. Nos da igual si hacemos crecer el circuito a lo alto o a lo largo, porque al fin y al cabo estamos hablando del mismo circuito. Intentar agregar 10 piezas simples, sería como aplicar 5 veces esta transformación simple.&lt;/p&gt;

&lt;p&gt;Si representamos cada operaciones como un vector de tres valores [S C D], con el número de piezas simples (S), curvas (C) y dobles (D), la transformación anterior se puede representar como [2 0 0]. Si analizamos las posibles mínimas transformaciones que podemos hacer nos encontraremos con las siguientes 12 transformaciones:&lt;/p&gt;

&lt;p&gt;&lt;img src="/assets/scalextric.png" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;Cualquier otro circuito o transformación estaría compuesto por varias de estas 12 transformaciones mínimas. Ahora el espacio de búsqueda se reduce en gran medida y podemos abarcarla en tiempo. Empezaríamos con un vector que se corresponde con el número total de piezas disponibles, y comenzaríamos a aplicar transformaciones básicas que consistiría en restar el vector correspondiente. Además, dado un conjunto de piezas muy grande, podríamos simplificarlo eliminando de forma repetitiva un conjunto fijo de transformaciones como [8 8 8] por ejemplo.&lt;/p&gt;

&lt;h2 id="10-passwords"&gt;10. Passwords&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://cdn.rawgit.com/gimco/programming-challenges/master/tuenti-contest-2017/10/10.html"&gt;Problema&lt;/a&gt; y &lt;a href="https://github.com/gimco/programming-challenges/tree/master/tuenti-contest-2017/10/10.clj"&gt;Solución&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hemos borrado la base de datos de contraseñas de los usuarios y debemos intentar restaurar los contraseñas de todos ellos. Afortunadamente las contraseñas son autogeneradas por un algoritmo que cambia cada día y tenemos el log de cuando cada usuario regeneró la contraseña. Como disponemos del repositorio git con el código del algoritmo, sólo tenemos que saber la fecha, buscar la versión del algoritmo y ejecutarlo. Para buscar la versión del código podemos utilizar el siguiente comando:&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;git rev-list -n 1 --before="FECHA"  --all
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Que quiere decir, busca la primera revisión (&lt;code class="language-plaintext highlighter-rouge"&gt;-n 1&lt;/code&gt;) anterior a la fecha dada, en todas las ramas (&lt;code class="language-plaintext highlighter-rouge"&gt;--all&lt;/code&gt;). Una vez tengamos el hash de la revisión solo debemos hacer checkout. Por ejemplo, veamos el tercer ejemplo:&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;xoajoj 2
2013-05-19 1
2016-08-20 1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Esto quiere decir que el usuario ‘xoajoj’ regeneró la contraseña en dos días distintos. Y en cada uno de estos día sólo se regeneró una sola vez. Para restaurar la contraseña haríamos lo siguiente:&lt;/p&gt;

&lt;div class="language-bash highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git checkout &lt;span class="si"&gt;$(&lt;/span&gt;git rev-list &lt;span class="nt"&gt;-n&lt;/span&gt; 1 &lt;span class="nt"&gt;--before&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"2013-05-19 23:59:59"&lt;/span&gt; &lt;span class="nt"&gt;--all&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
Previous HEAD position was c4f77fc... Updating script
HEAD is now at 6ffca36... Updating script

&lt;span class="nv"&gt;$ &lt;/span&gt;./script.php xoajoj
&lt;span class="nv"&gt;$t0&lt;/span&gt;&lt;span class="se"&gt;\&amp;amp;&lt;/span&gt;wArw^ 08f654824c4c1144fc0fad332112d619

&lt;span class="nv"&gt;$ &lt;/span&gt;git checkout &lt;span class="o"&gt;(&lt;/span&gt;git rev-list &lt;span class="nt"&gt;-n&lt;/span&gt; 1 &lt;span class="nt"&gt;--before&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"2016-08-20 23:59:59"&lt;/span&gt; &lt;span class="nt"&gt;--all&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
Previous HEAD position was 6ffca36... Updating script
HEAD is now at c4f77fc... Updating script

&lt;span class="nv"&gt;$ &lt;/span&gt;./script.php xoajoj 08f654824c4c1144fc0fad332112d619
WSUmXS~&lt;span class="k"&gt;*&lt;/span&gt;oR 60c9c0f66b325c193d293ced8c85cf71
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Con un poco de scripting podemos automatizar este proceso, pero pronto nos daremos cuenta de uno de los problemas. Y es que &lt;code class="language-plaintext highlighter-rouge"&gt;git rev-list&lt;/code&gt; realiza una búsqueda de los commits que están conectados por así decirlo. De modo que los commits que fueron eliminados o abandonados no aparecen. Pero a menos que hagamos un &lt;code class="language-plaintext highlighter-rouge"&gt;git prune&lt;/code&gt;, esos commits siguen estando en la base de datos, aunque no seamos capaces de llegar a ellos.&lt;/p&gt;

&lt;p&gt;Después de varias pruebas, para solventar esto, me decidí a listar todos los objetos dentro de la base de datos .git/object. Para cada uno de ellos comprueba con &lt;code class="language-plaintext highlighter-rouge"&gt;git cat-file -t&lt;/code&gt; si son de tipo commit, y si lo son extraigo la fecha y el algoritmo con &lt;code class="language-plaintext highlighter-rouge"&gt;git show&lt;/code&gt;:&lt;/p&gt;

&lt;div class="language-bash highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;find .git/objects/ &lt;span class="se"&gt;\&lt;/span&gt;
		| egrep &lt;span class="s1"&gt;'[0-9a-f]{38}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
		| perl &lt;span class="nt"&gt;-pe&lt;/span&gt; &lt;span class="s1"&gt;'s:^.*([0-9a-f][0-9a-f])/([0-9a-f]{38}):\1\2:'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
		| &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;read hash&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
	if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git cat-file &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="nv"&gt;$hash&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"commit"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
		&lt;/span&gt;&lt;span class="nv"&gt;DATE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git show &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;--format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;%ci &lt;span class="nv"&gt;$hash&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
		&lt;span class="nv"&gt;DATE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DATE&lt;/span&gt;:0:10&lt;span class="k"&gt;}&lt;/span&gt;

		&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$DATE&lt;/span&gt;
		git show &lt;span class="nv"&gt;$hash&lt;/span&gt;:script.php &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$SCRIPTDIR&lt;/span&gt;/&lt;span class="nv"&gt;$DATE&lt;/span&gt;.php
	&lt;span class="k"&gt;fi
done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Al final de esto tendremos un directorio con todos los script con su correspondiente fecha listo para usarse. Pero como siempre nos encontraremos con otro problema, y es que la ejecución es muy lenta. Analizando el algoritmo de los script php vemos que hay un bucle con &lt;strong&gt;10 millones de iteraciones&lt;/strong&gt; que es el responsable de la lentitud:&lt;/p&gt;

&lt;div class="language-php highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="nv"&gt;$counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$secret3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;10000000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;# This loop makes the passwords hard to reverse&lt;/span&gt;
  &lt;span class="nv"&gt;$counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$counter&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nv"&gt;$secret1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="nv"&gt;$secret2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Vemos que el valor de &lt;code class="language-plaintext highlighter-rouge"&gt;$counter&lt;/code&gt; en cada iteración se vuelve a multiplicar por &lt;code class="language-plaintext highlighter-rouge"&gt;$secret1&lt;/code&gt; y se calcula el módulo una y otra vez. Es un caso de &lt;a href="https://es.wikipedia.org/wiki/Exponenciación_modular"&gt;exponenciación modular&lt;/a&gt; muy utilizada en criptografía, y de la cual existe un cálculo rápido implementada en la mayoría de los lenguajes. Así que el bucle con 10 millones de iteraciones se puede sustituir por la siguiente veloz sentencia:&lt;/p&gt;

&lt;div class="language-php highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="nv"&gt;$counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$secret3&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nb"&gt;bcpowmod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$secret1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10000000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$secret2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="nv"&gt;$secret2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Esto mejora de forma considerable los tiempos de cálculo pero existen ejemplos que tardan mas. Por lo que la última opción es extraer los valores &lt;code class="language-plaintext highlighter-rouge"&gt;$secret1&lt;/code&gt; y &lt;code class="language-plaintext highlighter-rouge"&gt;$secret2&lt;/code&gt; que es lo que cambia cada día y re-implementar el algoritmo en otro lenguaje para obtener un mejor rendimiento y así evitar tantas llamadas a lenguajes interpretados, ademas de poder precalcular algunas operaciones.&lt;/p&gt;

&lt;h2 id="11-colors"&gt;11. Colors&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://cdn.rawgit.com/gimco/programming-challenges/master/tuenti-contest-2017/11/11.html"&gt;Problema&lt;/a&gt; y &lt;a href="https://github.com/gimco/programming-challenges/blob/master/tuenti-contest-2017/11/11.clj"&gt;Solución&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;De nuevo un problema de grafos. Debemos calcular el tiempo mínimo para llegar a cada una de las distintas galaxias que están conectadas por agujeros de gusano, que sólo podremos utilizar si poseemos la energía de color adecuada.&lt;/p&gt;

&lt;p&gt;Inicialmente parecía claro tener que aplicar &lt;a href="https://en.wikipedia.org/wiki/Bellman–Ford_algorithm"&gt;Bellman–Ford&lt;/a&gt; para obtener todos los caminos mínimos, pero en este problema las aristas pueden estar activas o no en función de nuestro estado, e incluso existir bucles (como ir y volver a otra galaxia para conseguir energía de otro color para poder usar un tipo de agujero de gusano).&lt;/p&gt;

&lt;p&gt;Así que opté por implementar el algoritmo de búsqueda manualmente, aunque no es la solución válida ya que el tiempo crece exponencialmente. Del enunciado se intuye que nuestro estado y cada galaxia se podría representar como un número binario, donde cada bit indica la existencia o no de la energía de un color, y las aristas como máscaras binarias para saber si la cumplimos o no. Aunque en mi caso no exploré esta opción y decidí saltar el ejercicio por que el tiempo se terminaba.&lt;/p&gt;

&lt;h2 id="12-thats-a-lot-of-moneyz"&gt;12. That’s a lot of moneyz&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://cdn.rawgit.com/gimco/programming-challenges/master/tuenti-contest-2017/12/12.html"&gt;Problema&lt;/a&gt; y &lt;a href="https://github.com/gimco/programming-challenges/blob/master/tuenti-contest-2017/12/Tuenti7Challenge12.java"&gt;Solución&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Este ha sido mi otro problema favorito del concurso de este año. Tenemos que ayudar a nuestro amigo a contar monedas. Para ello, al conectarnos a la dirección y el puerto indicados, se nos envían una serie de imágenes JPEG de las monedas que tenemos contar (siempre imágenes distintas). Si nos conectamos con telnet o netcat veríamos esto:&lt;/p&gt;

&lt;p&gt;&lt;img src="/assets/money-nc.png" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;Si procesamos los datos adecuadamente veríamos imágenes como esta:&lt;/p&gt;

&lt;p&gt;&lt;img src="/assets/tuenti-coins1.jpg" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;Las imágenes vienen dañadas, con algunas secciones verticales invertidas, pero son fácilmente corregibles ya que cada dos columnas de 40 pixeles se invierte la imagen. Inicialmente intenté resolverlo analizando el histograma de las imágenes, para intentar averiguar las monedas contando la cantidad de determinados colores que existen, pero al ser JPEG existía mucho ruido y zonas difuminadas.&lt;/p&gt;

&lt;p&gt;Así que toca echar mano de procesamiento de imágenes, cosa que siempre me había llamado la atención pero que nunca había trasteado. Para solucionarlo utilicé el que parece la librería mas popular: &lt;a href="http://opencv.org"&gt;OpenCV&lt;/a&gt; y su binding para Java.&lt;/p&gt;

&lt;p&gt;Una vez restaurada la imagen, el primer paso sería detectar los distintos círculos que existen en la imagen que corresponderían con las monedas aplicando &lt;a href="https://en.wikipedia.org/wiki/Circle_Hough_Transform"&gt;CHT (Circle_Hough_Transform)&lt;/a&gt;. Aplicado sobre la imagen, obtenemos una lista de puntos que se corresponden con los centros de las circunferencias detectadas junto con sus radios.&lt;/p&gt;

&lt;p&gt;Como cada moneda tiene un tamaño distinto, quizás podríamos saber de qué moneda se trata sabiendo el radio mas próximo. Desgraciadamente las detecciones no tienen tal nivel de exactitud, y a veces detecta círculos mas pequeños o ligeramente desplazados debido a la calidad de las imágenes.&lt;/p&gt;

&lt;p&gt;Después de distintos intentos, opté por utilizar el algoritmo Template Matching, que permite buscar zonas de imágenes similares. Así que puesto que detectaba correctamente los centros de las monedas, lo que hice fue extraer una región cuadrada de cada uno de los centros detectados. Por otro lado, generé pequeños cuadrados de los centros de cada una de las monedas posibles que los usuarios como los patrones a buscar:&lt;/p&gt;

&lt;p&gt;&lt;img src="/assets/patrones.png" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;Por último, para cada sección cuadrada realicé una búsqueda de los patrones de las monedas para ver si se encontraba y por tanto saber de qué moneda se trataban.&lt;/p&gt;

&lt;p&gt;&lt;img src="/assets/tuenti-coins2.png" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;Desgraciadamente para mi lo resolví minutos después de que se cerrara el tiempo límite y no pude entregar la solución. Para que os hagáis una idea de cómo les gusta a los de Tuenti complicar las pruebas, esta era una de las últimas imágenes que teníamos que resolver:&lt;/p&gt;

&lt;p&gt;&lt;img src="/assets/tuenti-coins3.jpg" alt="" /&gt;&lt;/p&gt;

&lt;h2 id="13-rlyeh"&gt;13. R’lyeh&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://cdn.rawgit.com/gimco/programming-challenges/master/tuenti-contest-2017/13/13.html"&gt;Problema&lt;/a&gt; y &lt;a href="https://github.com/gimco/programming-challenges/blob/master/tuenti-contest-2017/13/13.cpp"&gt;Solución&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Después de una intrigante introducción basada en &lt;a href="https://es.wikipedia.org/wiki/H._P._Lovecraft"&gt;Lovecraft&lt;/a&gt;, se nos insta a averiguar la relación entre los números esculpidos y escritos sobre una estatua. Tenemos la función que dado un número esculpido, nos devuelve el número escrito correspondiente. Pero se nos pide justo lo contrario, dado un conjunto de número escritos, averiguar el esculpido correspondiente. El problema es tan sencillo como implementar la función inversa a la que no dan. Sencillo si no fuera por el aspecto de esta función:&lt;/p&gt;

&lt;div class="language-cpp highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="kt"&gt;int64_t&lt;/span&gt; &lt;span class="nf"&gt;carvedToWritten&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int64_t&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;int64_t&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int64_t&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;int64_t&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int64_t&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;int64_t&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int64_t&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kt"&gt;int64_t&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;^=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1LL&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(((&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1LL&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;|=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1LL&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Misión imposible intentar entender que hace. Si usamos esta función para generar 1000 pares de números &lt;code class="language-plaintext highlighter-rouge"&gt;(carved - written)&lt;/code&gt; y los ordenamos por written tampoco apreciamos relación alguna:&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;written | carved                           written | carved
--------+-------                           --------+-------
     1  |      1                                24 |    143
     2  |      4                                25 |     17
     4  |      8                                30 |     19
     5  |      9                                32 |     64
     6  |      3                                33 |     66
     7  |      5                                34 |    587
     8  |     16                                37 |     73
     9  |     18                                64 |    128
     14 |    572                                65 |    129
     16 |     32                                66 |     11
     17 |     33                                67 |     14
     18 |     36                                70 |     12
     20 |      7                                71 |     38
     21 |     10                                72 |    144
     23 |      6                                75 |    573
...           
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Si nos fijamos bien en la función, vemos que hay un bucle exterior que itera 64 veces y realiza la operación &lt;code class="language-plaintext highlighter-rouge"&gt;r |= (a &amp;amp; (1LL &amp;lt;&amp;lt; i))&lt;/code&gt;. Que es lo mismo que establecer los bits del 0 al 64 del resultado final. Si agregamos un &lt;code class="language-plaintext highlighter-rouge"&gt;printf&lt;/code&gt; en este punto para ver los valores que va tomando la variable &lt;code class="language-plaintext highlighter-rouge"&gt;a&lt;/code&gt; en cada iteración observamos lo siguiente:&lt;/p&gt;

&lt;p&gt;&lt;img src="/assets/carved-to-written.png" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;Ahora si que vemos un patrón que se repite constamente. Para los bits impares se toma el valor &lt;code class="language-plaintext highlighter-rouge"&gt;(n + 1) / 2&lt;/code&gt;, y para los pares &lt;code class="language-plaintext highlighter-rouge"&gt;n * (n + 1) / 2&lt;/code&gt;. Así que podemos simplificar la función original, que además mejorará el rendimiento:&lt;/p&gt;

&lt;div class="language-cpp highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="kt"&gt;int64_t&lt;/span&gt; &lt;span class="nf"&gt;carvedToWrittenFast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int64_t&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;int64_t&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int64_t&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;int64_t&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;|=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1LL&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Una vez comprobamos que esta nueva función es equivalente a la primera, podemos analizar su funcionamiento. Podemos obtener los bits impares de &lt;code class="language-plaintext highlighter-rouge"&gt;n&lt;/code&gt; directamente del valor de &lt;code class="language-plaintext highlighter-rouge"&gt;r&lt;/code&gt;, realizando la operación inversa a &lt;code class="language-plaintext highlighter-rouge"&gt;(n + 1) / 2&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src="/assets/rlyeh.png" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;Como sabemos que el número objetivo es de 32 bits, y gracias a la operación anterior conocemos ya 16 bits del valor final, la opción mas sencilla es calcular por fuerza bruta los otros 16 bits restantes, lo que nos llevará a probar 65536 valores en el peor de los casos:&lt;/p&gt;

&lt;div class="language-cpp highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="kt"&gt;uint32_t&lt;/span&gt; &lt;span class="nf"&gt;writtenToCarved&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int64_t&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    
    &lt;span class="kt"&gt;uint32_t&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mh"&gt;0xAAAAAAAA&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mh"&gt;0xFFFF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;uint32_t&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;|=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;carvedToWrittenFast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;carvedToWrittenFast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;hr /&gt;

&lt;p&gt;Me quedan por solucionar dos problemas: &lt;em&gt;Blackjack&lt;/em&gt; y &lt;em&gt;Intervals&lt;/em&gt;, que en otro momento intentaré solucionar y actualizaré esta entrada con su correspondiente análisis.&lt;/p&gt;
</description>
				<pubDate>Mon, 08 May 2017 00:00:00 +0000</pubDate>
				<link>http://blog.gimco.es/2017/05/08/tuenti-challenge-7.html</link>
				<guid isPermaLink="true">http://blog.gimco.es/2017/05/08/tuenti-challenge-7.html</guid>
			<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Bruno Orcha García</dc:creator></item>
		
			<item>
				<title>Mainframes</title>
				<description>&lt;p&gt;Últimamente he tenido la oportunidad de conocer una parte de la historia de la informática que para mi era totalmente desconocida. Me estoy refiriendo al mundo de los &lt;a href="https://en.wikipedia.org/wiki/Mainframe_computer"&gt;mainframes&lt;/a&gt;. Estos sistemas han sido el orgullo de los centros de datos de empresas y universidades. Son ordenadores caros (muy caros), potentes y del tamaño de frigoríficos americanos. Aunque hoy en día IBM sigue comercializando estos sistemas, la fuerte competencia de las soluciones de virtualización y la popularidad de los proveedores en la nube, no los hace tan imprescindibles como antaño.&lt;/p&gt;

&lt;p&gt;&lt;img src="/assets/mainframe-390.gif" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;No cabe duda de que han sido las plataformas donde se han implementado los grandes sistemas de información de los 80 y los 90, y aunque parezca sorprendente, muchos de ellos siguen dando servicio varias décadas después. Hoy en días estos sistemas siguen en la sombra, dando soporte y gestionando el negocio clave de multitud de sistemas. De hecho, según cita &lt;a href="https://en.wikipedia.org/wiki/Software_AG"&gt;una de las principales empresas software de Europa&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;COBOL is still a world-player in IT. In fact, 75 percent of the world’s business transactions are processed by COBOL.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Podemos identificarlos con sus pantallas negras tan características (emuladores de terminal). Yo al menos los he podido identificar en sitios tan diversos como en los sistemas de gestión de garantía de Thermomix, en El Corte Inglés, Hospitales, Supermercados, Aerolíneas. El negocio de IBM con estos sistemas es increíble. Desorbitados costes de licencias, licencias que hay que pagar anualmente por “ejecución de software”. Es decir, por el mero hecho de arrancar el sistema.&lt;/p&gt;

&lt;p&gt;&lt;img src="/assets/natural-adabas-example.jpg" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;Son los conocidos backoffice, o sistemas legacy. Sistemas que llevan funcionando 20 años, desarrollados en los 80’s y 90’s y que por una u otra razón no han podido ser actualizados o migrados a nuevas tecnologías o nuevas plataformas. &lt;strong&gt;Sistemas con millones y millones de líneas de código&lt;/strong&gt;, que actualmente están funcionando, sin fallos. Y todos hemos aprendido la regla de oro: &lt;em&gt;“Si funciona no lo toques”&lt;/em&gt;. No es casualidad que el &lt;a href="http://www.dev-books.com"&gt;libro más mencionado en Stack Overflow&lt;/a&gt; sea precisamente &lt;a href="https://www.amazon.es/Working-Effectively-Legacy-Robert-Martin/dp/0131177052"&gt;“Working Effectively with Legacy Code”&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src="/assets/book-working-legacy-code.jpg" alt="" /&gt;&lt;/p&gt;

&lt;h2 id="acceso-al-sistema"&gt;Acceso al sistema&lt;/h2&gt;

&lt;p&gt;Para conectarnos al mainframe se utiliza un emulador de &lt;a href="https://es.wikipedia.org/wiki/IBM_3270"&gt;terminal 3270&lt;/a&gt;, que sería el equivalente a un telnet o ssh, aunque la interacción parece mas rica, ya que el cliente gestiona conceptos como campos de entrada, campos de solo lectura y posición del cursor. En Windows se suele utilizar los programas propietarios Entire Connection o TN3270Plus, y afortunadamente existe el proyecto &lt;a href="http://x3270.bgp.nu/index.html"&gt;x3270&lt;/a&gt; que implementa distintos clientes para todas las plataformas incluida Linux.&lt;/p&gt;

&lt;p&gt;&lt;img src="/assets/c3270.png" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;Junto con los clientes gráficos y de consola se incluye el cliente s3270 que nos permite interactuar con el mainframe en forma de scripts. Podemos utilizar este cliente para automatizar ciertas acciones (al estilo de macros) o para hacer lo que podríamos llamar &lt;a href="https://en.wikipedia.org/wiki/Web_scraping"&gt;Terminal scraping&lt;/a&gt;, para extraer información de forma sencilla obteniendo el texto en pantalla.&lt;/p&gt;

&lt;h2 id="sistema-operativos"&gt;Sistema operativos&lt;/h2&gt;

&lt;p&gt;Algunos de estos antiguos mainframes ejecutan sistemas operativos “extraños” a los ojos de los que estamos acostumbrado a los sistemas basados en UNIX. Recordemos que estos sistemas ya existían varias décadas antes de la popularización de Linux (Linus Torvals presentó Linux 1.0 en 1992). Uno de estos sistemas operativos con los que me he encontrado es &lt;a href="https://en.wikipedia.org/wiki/VSE_(operating_system)"&gt;VSE/ESA&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Como ejemplo de lo “diferente” que pueden llegar a ser estos sistemas operativos veamos por ejemplo &lt;a href="https://en.wikipedia.org/wiki/Job_Control_Language"&gt;JCL (Job Control Language)&lt;/a&gt;. Esto es el lenguaje que se utiliza para interactuar con el sistema operativo. Vendría a ser el equivalente a un shell script con bash o un .bat en windows. Lo que en el mundo linux sería copiar un fichero:&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;cp oldFile newFile
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;… en el mundo mainframe con JCL equivaldría a:&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;//IS198CPY JOB (IS198T30500),'COPY JOB',CLASS=L,MSGCLASS=X
//COPY01   EXEC PGM=IEBGENER
//SYSPRINT DD SYSOUT=*
//SYSUT1   DD DSN=OLDFILE,DISP=SHR
//SYSUT2   DD DSN=NEWFILE,
//            DISP=(NEW,CATLG,DELETE),
//            SPACE=(CYL,(40,5),RLSE),
//            DCB=(LRECL=115,BLKSIZE=1150)
//SYSIN    DD DUMMY
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Podemos hacernos una idea del aspecto que tendría un JCL que obtenga un fichero por FTP, ejecute un programa que procese los datos, genere nuevos ficheros, los transfiera por FTP a otra máquina, y envíe correos electrónicos … Para los mas curiosos todavía se pueden encontrar &lt;a href="http://tutorialspoint.com/jcl/"&gt;tutoriales de JCL&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id="desarrollo-de-aplicaciones"&gt;Desarrollo de aplicaciones&lt;/h2&gt;

&lt;p&gt;Estos mainframes eran las plataformas de desarrollo de aplicaciones empresariales de su época, y como tales proporcionaban a los programadores el entorno de programación, compilación, procesamiento y acceso a bases de datos necesarios. No existía el concepto de desarrollo en local y despliegue, si no que los desarrolladores se conectaban mediante el emulador de terminal al mainframe para programar. El entorno de desarrollo era por lo tanto basado en consola (quienes hayan conocido TurboPascal o TurboC se podrán hacer una idea). Un ejemplo de este interfaz:&lt;/p&gt;

&lt;p&gt;&lt;img src="/assets/natural314.png" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;Los lenguajes mas habituales eran C, COBOL y NATURAL. Yo en concreto me he encontrado con algunas aplicaciones desarrolladas en el lenguaje de programación &lt;a href="https://es.wikipedia.org/wiki/Natural_(lenguaje_de_programación)"&gt;NATURAL&lt;/a&gt; y la base de datos &lt;a href="https://en.wikipedia.org/wiki/ADABAS"&gt;ADABAS&lt;/a&gt;, ambos creados por la empresa Software AG.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Natural: Cobol’s ugly, brain-damaged, BABBLING IN ALL-CAPS — but regrettably still healthy and strong — cousin.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;NATURAL es un lenguaje de programación de los considerados de &lt;a href="https://en.wikipedia.org/wiki/Fourth-generation_programming_language"&gt;4ª generación&lt;/a&gt; (como Clipper), que vendrían a ser lenguajes de alto nivel y de propósito específico. En estos lenguajes el acceso y manipulación de las base de datos y la interacción con interfaces de usuario e informes forman parte del propio lenguaje, al contrario que con los lenguajes de propósito general los cuáles necesitarían de distintas librerías adicionales para implementar estas características. Esto hace que la creación de pantallas, validaciones de datos, generación de listados e informes sea rápida y sencilla.&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;DEFINE DATA
LOCAL USING LUSUARIO
LOCAL
  1 #APELLIDO (A20)
END-DEFINE
INPUT #APELLIDO
FIND LUSUARIO WITH APELLIDO = #APELLIDO
  DISPLAY NIF NOMBRE APELLIDO1
END-FIND
END
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Como curiosidad decir que la base de datos ADABAS es NoSQL … en el sentido de que no se utiliza SQL para consultarla. En su lugar hay que construir programas NATURAL para obtener la información que queramos. Soporta búsquedas “fonéticas” (à la Oracle Text/Lucene), grupos periódicos y campos multivaluados, similares a las estructuras jerárquicas y arrays que se utilizan con JSON y MongoDB, claro que todo esto en los años 80 &#128518;.&lt;/p&gt;

&lt;p&gt;&lt;img src="/assets/dilbert-legacy-code.gif" alt="" /&gt;&lt;/p&gt;

</description>
				<pubDate>Wed, 22 Feb 2017 00:00:00 +0000</pubDate>
				<link>http://blog.gimco.es/2017/02/22/mainframes.html</link>
				<guid isPermaLink="true">http://blog.gimco.es/2017/02/22/mainframes.html</guid>
			<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Bruno Orcha García</dc:creator></item>
		
			<item>
				<title>Clojure shebang</title>
				<description>&lt;p&gt;Uno de las grandes ventajas de los lenguajes de scripting como bash, python, perl, awk, ruby, php, nodejs, etc, es poder editarlos fácilmente desde consola, sin necesidad de tener que recompilar los fuentes para obtener un binario ejecutable. Esto hace que estos lenguajes sean muy populares para labores de mantenimientos y automatización, o crear pequeñas utilidades.&lt;/p&gt;

&lt;p&gt;Los sistemas &lt;a href="https://en.wikipedia.org/wiki/Unix-like"&gt;*nix&lt;/a&gt; implementan un mecanismo denominado &lt;a href="https://en.wikipedia.org/wiki/Shebang_(Unix)"&gt;shebang&lt;/a&gt; mediante el cual podemos informar al sistema operativo que intérprete se debe utilizar para ejecutar un determinado fichero. Tan solo debemos escribir al principio del fichero los símbolos &lt;code class="language-plaintext highlighter-rouge"&gt;#!&lt;/code&gt; seguido de la ruta del programa que interpretará el fichero. Probablemente te sonará si has creado alguna vez ficheros de script de bash y has tenido que comenzar el fichero con &lt;code class="language-plaintext highlighter-rouge"&gt;#!/bin/bash&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Así pues, si creamos un fichero con código Python llamado &lt;code class="language-plaintext highlighter-rouge"&gt;hola-mundo.py&lt;/code&gt; con el siguiente contenido:&lt;/p&gt;

&lt;div class="language-python highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="s"&gt;"hello world"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;le damos permiso de ejecución e intentamos ejecutarlos obtendremos el siguiente error:&lt;/p&gt;

&lt;div class="language-bash highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x hello-world.py
&lt;span class="nv"&gt;$ &lt;/span&gt;./hello-world.py
Failed to execute process &lt;span class="s1"&gt;'./hello-world.py'&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; Reason:
&lt;span class="nb"&gt;exec&lt;/span&gt;: Exec format error
The file &lt;span class="s1"&gt;'./hello-world.py'&lt;/span&gt; is marked as an executable but could not be run by the operating system.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Nuestro sistema operativo &lt;em&gt;no sabe&lt;/em&gt; como ejecutar el programa aunque le hayamos puesto la extension &lt;code class="language-plaintext highlighter-rouge"&gt;.py&lt;/code&gt;. En estos casos tendremos que utilizar  la expresión &lt;strong&gt;shebang&lt;/strong&gt; para indicar cómo ejecutar el fichero. Sólo debemos agregar en la primera línea los caracteres &lt;code class="language-plaintext highlighter-rouge"&gt;#!&lt;/code&gt; seguido del intérprete:&lt;/p&gt;

&lt;div class="language-python highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="c1"&gt;#!/usr/bin/python
&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="s"&gt;"hello world"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;De este modo el sistema operativo ya sabe cómo ejecutar el fichero. De hecho, ejecutar &lt;code class="language-plaintext highlighter-rouge"&gt;./hello-world.py&lt;/code&gt; es exactamente lo mismo que ejecutar &lt;code class="language-plaintext highlighter-rouge"&gt;/usr/bin/python hello-world.py&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Es posible que los distintos intérpretes de comandos pueden estar instalados en distintas rutas según el sistema operativo. Para evitar introducir la ruta absoluta del intérprete se suele utilizar el comando &lt;code class="language-plaintext highlighter-rouge"&gt;/usr/bin/env&lt;/code&gt; que inicializa las variables de entorno del usuario y podemos indicar solamente el nombre del intérprete:&lt;/p&gt;

&lt;div class="language-python highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="c1"&gt;#!/usr/bin/env python
&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="s"&gt;"hello world"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id="clojure"&gt;Clojure&lt;/h2&gt;

&lt;p&gt;Todos los lenguajes de scripting instalan un binario que es el propio intérprete, lo que nos permite crear los ficheros con la correspondiente línea shebang. En el caso de Clojure no es así. Al contrario que con otros lenguajes, no disponemos de un instalador “oficial” con el que se instale un comando “clojure”. Lo mas parecido a este instalador oficial son las herramientas que gestionan la compilación de proyectos clojure como son &lt;a href="http://leiningen.org/"&gt;lein&lt;/a&gt; o &lt;a href="http://boot-clj.com/"&gt;boot&lt;/a&gt;, siendo el primero el estándar de facto.&lt;/p&gt;

&lt;p&gt;Estas herramientas son las equivalentes a maven o ant en el mundo Java, y como ellas, también disponen de su correspondiente fichero de definición (&lt;code class="language-plaintext highlighter-rouge"&gt;pom.xml&lt;/code&gt; / &lt;code class="language-plaintext highlighter-rouge"&gt;build.xml&lt;/code&gt;) y de su estructura de proyecto por defecto. De hecho, si instalamos &lt;code class="language-plaintext highlighter-rouge"&gt;lein&lt;/code&gt; (tan solo debemos descargar el script de lein y ubicarlo en un directorio dentro del PATH) y creamos un proyecto básico obtenemos lo siguiente:&lt;/p&gt;

&lt;div class="language-bash highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;lein new app hello-world
Generating a project called hello-world based on the &lt;span class="s1"&gt;'app'&lt;/span&gt; template.
&lt;span class="nv"&gt;$ &lt;/span&gt;find hello-world
hello-world
hello-world/.gitignore
hello-world/.hgignore
hello-world/CHANGELOG.md
hello-world/doc
hello-world/doc/intro.md
hello-world/LICENSE
hello-world/project.clj
hello-world/README.md
hello-world/resources
hello-world/src
hello-world/src/hello_world
hello-world/src/hello_world/core.clj
hello-world/test
hello-world/test/hello_world
hello-world/test/hello_world/core_test.clj
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Si entramos en el directorio hello-world podremos ejecutar el proyecto clojure usando &lt;code class="language-plaintext highlighter-rouge"&gt;lein run&lt;/code&gt; pero que crear esta estructura de directorios y todos estos ficheros para ejecutar pequeños programa clojure que sólo consisten en un único fichero no es demasiado cómodo.&lt;/p&gt;

&lt;h2 id="creando-el-comando-clj"&gt;Creando el comando clj&lt;/h2&gt;

&lt;p&gt;Como solución podemos crear un fichero que desempeñe la labor de intérprete. Para ello creamos un fichero de nombre &lt;code class="language-plaintext highlighter-rouge"&gt;clj&lt;/code&gt; y lo agregamos a algún directorio del PATH. El fichero contendrá el siguiente código:&lt;/p&gt;

&lt;div class="language-bash highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;

&lt;span class="nv"&gt;CLJ_JAR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; ~/.lein/self-installs | head &lt;span class="nt"&gt;-1&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;exec &lt;/span&gt;java &lt;span class="nt"&gt;-Xmx1g&lt;/span&gt; &lt;span class="nt"&gt;-server&lt;/span&gt; &lt;span class="nt"&gt;-cp&lt;/span&gt; &lt;span class="nv"&gt;$CLJ_JAR&lt;/span&gt; clojure.main &lt;span class="nv"&gt;$@&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Básicamente lo que hacemos es buscar la versión mas actual de lein que contiene las librerías de clojure e iniciar una máquina virtual de Java pasando todos los parámetro recibidos. Si no tenemos lein podemos especificar en la variable &lt;code class="language-plaintext highlighter-rouge"&gt;CLJ_JAR&lt;/code&gt; la ruta al fichero &lt;code class="language-plaintext highlighter-rouge"&gt;clojure-1.X.jar&lt;/code&gt; que queramos usar. Después de crear este fichero podremos crear scripts con clojure que sean ejecutable utilizando el siguiente shebang:&lt;/p&gt;

&lt;div class="language-clojure highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="n"&gt;!/usr/bin/env&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;clj&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;println&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"hola mundo"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id="importando-dependencias"&gt;Importando dependencias&lt;/h2&gt;

&lt;p&gt;El sistema anterior tiene un inconveniente: no podemos usar librerías adicionales en nuestro fichero de clojure. Para solucionarlo podemos utilizar un plugin &lt;a href="https://github.com/kumarshantanu/lein-exec"&gt;lein-exec&lt;/a&gt;. Para ello debemos agregar el plugin a nuestro fichero &lt;code class="language-plaintext highlighter-rouge"&gt;profiles.clj&lt;/code&gt;:&lt;/p&gt;

&lt;div class="language-clojure highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="c1"&gt;; Agregar el plugin de lein-exe en ~/.lein/profiles.clj:&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:user&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:plugins&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="n"&gt;lein-exec&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"0.3.5"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Y agregar el fichero &lt;a href="https://raw.github.com/kumarshantanu/lein-exec/master/lein-exec"&gt;lein-exec&lt;/a&gt; a nuestro path. A partir de entonces, en los script que usemos con el shebang lein-exec tendremos una instancia de clojure con el plugin cargado con el que podremos indicar que librerías adicionales necesita el fichero para funcionar. Por ejemplo:&lt;/p&gt;

&lt;div class="language-clojure highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="n"&gt;!/usr/bin/env&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lein-exec&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;'&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;leiningen.exec&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:only&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;deps&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;deps&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;'&lt;/span&gt;&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="n"&gt;org.clojure/data.codec&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"0.1.0"&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;'&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;clojure.data.codec.base64&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b64&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"SGVsbG8gd29ybGQh"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;.getBytes&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;b64/decode&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;String.&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nb"&gt;println&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

</description>
				<pubDate>Thu, 17 Nov 2016 00:00:00 +0000</pubDate>
				<link>http://blog.gimco.es/2016/11/17/clojure-shebang.html</link>
				<guid isPermaLink="true">http://blog.gimco.es/2016/11/17/clojure-shebang.html</guid>
			<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Bruno Orcha García</dc:creator></item>
		
			<item>
				<title>Retropie: Crea tu retroconsola</title>
				<description>&lt;p&gt;Hace unos meses Nintendo anunciaba una nueva consola destinada a tocar la fibra sensible de todos los ochenteros que hemos tenido la suerte de haber conocido la maravillosa generación de consolas Atari/MasterSystem/Nes/MegaDrive/SuperNintendo. Se trata de la &lt;a href="https://www.nintendo.com/nes-classic"&gt;Nintendo Classic Mini&lt;/a&gt; una réplica en miniatura de la &lt;a href="https://es.wikipedia.org/wiki/Nintendo_Entertainment_System"&gt;NES&lt;/a&gt; con una selección de 30 juegos, simples, espartanos, pero super adictivos.&lt;/p&gt;

&lt;p&gt;&lt;img src="/assets/nes-classic-edition-in-hand.png" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;No tengo la menor duda de que será un superventas estas navidades. Aunque podríamos señalar dos inconvenientes: Solo permiten dos jugadores y los juegos son exclusivamente los 30 elegidos.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;¿Habrá que esperar a una Super Nintendo Classic Mini para poder volver disfrutar del &lt;a href="https://en.wikipedia.org/wiki/Yoshi%27s_Island"&gt;Yoshi’s Island&lt;/a&gt; o del Street Fighter II?&lt;/li&gt;
  &lt;li&gt;¿No podré utilizar mi &lt;a href="http://www.nes30.com"&gt;mando bluetooth retro de NES&lt;/a&gt;?&lt;/li&gt;
  &lt;li&gt;¿Tampoco tardes de cuatro jugadores con Super Bomberman, Micro Machines o Mario Kart?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Para resolver estos inconvenientes siempre podemos crear nuestra propio sistema de emulación, &lt;a href="https://www.youtube.com/watch?v=IGlKLUujURk&amp;amp;feature=youtu.be&amp;amp;t=57"&gt;algo pequeñito&lt;/a&gt; del estilo de la Nintendo Classic Mini, algo sencillo, para enchufar y jugar. Y nada como una RaspberryPi para ajustarse a estos requisitos.&lt;/p&gt;

&lt;h2 id="raspberry"&gt;Raspberry&lt;/h2&gt;

&lt;p&gt;De los modelos de Raspberry mas actuales que podemos elegir, tenemos la &lt;a href="https://www.raspberrypi.org/blog/raspberry-pi-zero/"&gt;Raspberry Pi Zero&lt;/a&gt; que es la mas pequeña y barata, pero que no dispone de WI-FI, ni Bluetooth, ni salida RCA/Euroconector, ni puertos USB “de los normales”. Aunque si eres manitas siempre puedes &lt;a href="https://www.modmypi.com/blog/how-to-add-an-rca-tv-connector-to-a-raspberry-pi-zero"&gt;añadir una salida RCA&lt;/a&gt;, &lt;a href="https://youtu.be/Kbv5mqv32tc"&gt;añadir un módulo wifi&lt;/a&gt;, y buscar algún HUB USB.&lt;/p&gt;

&lt;p&gt;Y la opción mas recomendable y sencilla es usar la &lt;a href="https://www.raspberrypi.org/blog/raspberry-pi-3-on-sale/"&gt;Raspberry Pi 3 Model B&lt;/a&gt;, que es mas cara, mas potente, tiene WI-FI, puerto ethernet, bluetooth, salida RCA y 4 puertos USB listos para usar. Solo necesitaremos un par de cosas mas:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href="https://www.amazon.es/dp/B01CCOXV34"&gt;Raspberry 3 Model B&lt;/a&gt; 39 €&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://www.amazon.es/dp/B01F1PSFY6/"&gt;Carcasa&lt;/a&gt; 8.50 €&lt;/li&gt;
  &lt;li&gt;&lt;a href="https://www.amazon.es/dp/B00PL271Y0"&gt;2 Mandos USB de estilo retro&lt;/a&gt; 12 €&lt;/li&gt;
  &lt;li&gt;Y buscar por casa una tarjeta microSD, un cable HDMI y un cargador de móvil.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src="/assets/raspberry-amazon.png" alt="" /&gt;&lt;/p&gt;

&lt;h2 id="instalación"&gt;Instalación&lt;/h2&gt;

&lt;p&gt;El siguiente paso sería instalar el software necesario. Tendremos que instalar un sistema operativo para Raspberry (cualquier de los muchos sabores de linux que hay), instalar y configurar cada uno de los emuladores con los que querríamos jugar (FCEUmm, Snes9x, DOSBox, ScummVM, Mupen, PPSSPP, y la lista sigue …), configurar los mandos USB que queramos utilizar. Pero, una vez que lo hayamos hecho, ¿como hacemos para elegir el emulador con el que queremos jugar cada vez? Necesitaríamos también un teclado y raton para poder iniciar cada emulador por separado, y poder cerrarlo y elegir el siguiente. Cada emulador tiene su propio interfaz, sus opciones de configuración, su proceso de mapeo de teclas y botones del mando, etc.&lt;/p&gt;

&lt;p&gt;Afortunadamente existe un proyecto llamado &lt;a href="http://www.libretro.com/"&gt;libretro&lt;/a&gt; que simplifica este problema. Este proyecto implementa una librería que unifica el acceso a distintos emuladores, proporcionado un interfaz único para usarlos. Esto se traduce en que una aplicación que utilice libretro podrá ser capaz de iniciar cualquier juego de forma transparente al emulador que finalmente lo ejecute, lo que nos permitirá configurar y ejecutar las distintas plataformas de forma sencilla.&lt;/p&gt;

&lt;p&gt;Ahora necesitaríamos una aplicación que nos facilite la gestión de las distintas roms, opciones de configuración y permite iniciar los distintos juegos de forma transparente al emulador sin necesidad de estar usando teclado y raton. Y &lt;a href="http://www.emulationstation.org/"&gt;EmulationStation&lt;/a&gt; es la aplicación perfecta para esto.&lt;/p&gt;

&lt;p&gt;EmulationStation es la aplicación que querremos iniciar por defecto. Nos permite navegar entre las distintas plataformas de forma gráfica, seleccionar los juegos, descargarse metadatos para que aparezca la carátula, nombre, año, etc. Y nos permite por supuesto iniciar los juegos, configurar opciones globales, configura los mandos de forma global. Y todo esto con una usabilidad y experiencia de usuario digna de alabar. &lt;a href="http://gph.is/1U3YLbG"&gt;Chapó!&lt;/a&gt;&lt;/p&gt;

&lt;iframe width="640" height="360" src="https://www.youtube.com/embed/AVQVmsFOclM" frameborder="0" allowfullscreen=""&gt;&lt;/iframe&gt;

&lt;h2 id="retropie"&gt;Retropie&lt;/h2&gt;

&lt;p&gt;Afortunadamente existe un proyecto que se ocupa de todo lo anterior. &lt;a href="https://retropie.org.uk/"&gt;Retropie&lt;/a&gt; es un proyecto que se encarga de prepararnos una distribución Debian para Raspberry junto con libretro/retroarch, con todos los emuladores y con EmulationStation de inicio. &lt;strong&gt;¡Así que todo se reduce a copiar la distribución de Retropie a nuestra microSD, y listo!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Los &lt;a href="https://github.com/RetroPie/RetroPie-Setup/wiki/First-Installation#installation"&gt;instrucciones de instalación&lt;/a&gt; son bien sencillas: &lt;a href="https://retropie.org.uk/download"&gt;descargar la imagen&lt;/a&gt; e instalarla en la microSD con &lt;a href="http://sourceforge.net/projects/win32diskimager"&gt;Windows&lt;/a&gt;, &lt;a href="https://unetbootin.github.io"&gt;Linux&lt;/a&gt; o &lt;a href="http://www.tweaking4all.com/hardware/raspberry-pi/macosx-apple-pi-baker"&gt;Mac&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Una vez iniciemos nuestra Raspberry Pi con la microSD de Retropie nos aparecerá EmulatioStation. Cada vez se detecte un nuevo tipo de mando USB, tendremos que configurar los botones. Si el mando no dispone de alguno de los botones, dejaremos pulsado durante unos segundos cualquier botón ya mapeado anteriormente.&lt;/p&gt;

&lt;p&gt;Entre las plataformas que muestra EulationStation veremos que existe una que es Retropie desde la que podremos iniciar distintos scripts de utilidades y configuración de Retropie. Uno de ellos nos permite configurar el WI-FI (necesitaremos enchufar un teclado momentáneamente) o consultar la IP actual.&lt;/p&gt;

&lt;p&gt;&lt;img src="/assets/retropie-pixel.png" alt="" /&gt;&lt;/p&gt;

&lt;p&gt;Lo único que nos queda copiar &lt;a href="http://www.freeroms.com/"&gt;nuestras roms&lt;/a&gt; en la correspondiente carpeta de la raspberry. Para eso tenemos &lt;a href="https://github.com/RetroPie/RetroPie-Setup/wiki/Transferring-Roms"&gt;varias opciones&lt;/a&gt;, copiarlas con scp (accediendo con el usuario pi y contraseña raspberry) o usando una memoria USB. Para ello retropie tiene configurado un mecanismos de sincronización con rsync. Solo creamos una carpeta  retropie en la raíz de la memoria USB y la enchufamos a la raspberry. Tras unos segundos se habrá sincronizado la estructura de carpetas de roms que se utiliza. Ya podremos volver a enchufar el USB en un PC y copiar las roms que queramos en las carpetas correspondientes.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;Así que solo nos queda disfrutar de divertidas tardes de juegos retro.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;P.D.: Para salir de los juegos y volver a EmulationStation hay que pulsar SELECT + START. Si aparece un &lt;a href="https://www.raspberrypi.org/forums/viewtopic.php?f=29&amp;amp;t=82373"&gt;cuadrado multicolor&lt;/a&gt; en la esquina es porque el cargador no suministra suficiente potencia y deberíamos probar con otro.&lt;/p&gt;

</description>
				<pubDate>Thu, 27 Oct 2016 00:00:00 +0000</pubDate>
				<link>http://blog.gimco.es/2016/10/27/retropie-crea-tu-consola-retro.html</link>
				<guid isPermaLink="true">http://blog.gimco.es/2016/10/27/retropie-crea-tu-consola-retro.html</guid>
			<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Bruno Orcha García</dc:creator></item>
		
			<item>
				<title>Docker en máquinas de 32bits</title>
				<description>&lt;p&gt;Si dispones de una máquina y/o una distribución de Linux de 32 bits (Como por ejemplo Ubuntu 14.04 32bits) e intentas instalar Docker te puedes encontrar con el siguiente mensaje:&lt;/p&gt;

&lt;p&gt;&lt;img src="/assets/docker-dont-run-on-32.png" alt="Error: you are not using a 64bit platform. Docker currently only supports 64bit platforms." /&gt;&lt;/p&gt;

&lt;p&gt;En la propia página web podemos encontrar los requisitos referentes a &lt;a href="https://docs.docker.com/installation/ubuntulinux/"&gt;Ubuntu Linux&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Docker requires a 64-bit installation regardless of your Ubuntu version. Additionally, your kernel must be 3.10 at minimum.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Aunque nuestro sistema no cumpla con estos requisitos todavía tenemos una alternativa. Usar la misma técnica que se utilizaba en MacOSX y Windows para poder instalar docker, &lt;a href="http://boot2docker.io/"&gt;boot2docker&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Las herramientas de docker utilizan el paradigma cliente/servidor, por lo que cuando lo instalamos tenemos por un lado la parte cliente, que sería el comando de consola &lt;code class="language-plaintext highlighter-rouge"&gt;docker&lt;/code&gt;, y por otro lado la parte servidor o servicio docker que utiliza ciertas características del kernel de Linux para crear y administrar los contenedores.&lt;/p&gt;

&lt;p&gt;En el caso de MacOSX y Windows, no se dispone de estas características en el kernel, por lo que la solución es instalar una máquina virtual con un Linux que proporcione esta parte servidor. Esta es la misión de boot2docker, proporcionar el mecanismo de crear e iniciar esta máquina virtual (basada en VirtualBox) para poder usar docker en otros sistemas operativos no soportados. El rendimiento será peor obviamente (ya que el “docker servicio” no se ejecutará de forma nativa como en un Linux de 64bits, si no que se ejecutará dentro del Linux de la máquina virtual de VirtualBox), pero será mas que suficiente para poder trastear.&lt;/p&gt;

&lt;p&gt;Puesto que no existe boot2docker para sistema Linux de 32 bits tendremos que compilarlo a mano. Si echamos un vistazo al &lt;a href="https://github.com/boot2docker/boot2docker-cli"&gt;código fuente&lt;/a&gt; del proyecto veremos que nos encontramos con un problema recursivo: necesitamos docker para poder compilar esta aplicación, que es la que necesitamos precisamente para poder tener docker! :P&lt;/p&gt;

&lt;p&gt;Si analizamos el &lt;a href="https://github.com/boot2docker/boot2docker-cli/blob/master/Makefile"&gt;Makefile&lt;/a&gt; y el &lt;a href="https://github.com/boot2docker/boot2docker-cli/blob/master/Dockerfile"&gt;Dockerfile&lt;/a&gt; del proyecto vemos que sencillamente se instala el compilador de &lt;a href="https://golang.org/"&gt;go&lt;/a&gt; (lenguaje con el que está programado este cliente) y algunas dependencias. Afortunadamente se puede instalar el compilador de &lt;code class="language-plaintext highlighter-rouge"&gt;go&lt;/code&gt; con los binarios de la página oficial y podemos compilar automáticamente boot2docker-cli como cualquier otra librería:&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;cd /tmp
wget http://...../go1.5.3.linux-386.tar.gz
tar zxvf go1.5.3.linux-386.tar.gz
mkdir gopath &amp;amp;&amp;amp; export GOPATH=/tmp/gopath
export GOROOT=/tmp/go
/tmp/go/bin/go get -v github.com/boot2docker/boot2docker-cli
sudo mv /tmp/gocode/bin/boot2docker-cli /usr/bin/boot2docker
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;En segundo lugar necesitamos el comando docker, que sería la parte cliente. Desde la &lt;a href="https://docs.docker.com/installation/binaries/"&gt;sección de instalación de la página oficial&lt;/a&gt; se ofrece un enlace para descargar &lt;a href="https://get.docker.com/builds/Linux/i386/docker-latest"&gt;el binario para 32 bits&lt;/a&gt;:&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;https://get.docker.com/builds/Linux/i386/docker-latest
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Ya tendríamos todo lo necesario en nuestra máquina de 32 bits. La primera vez necesitaremos que boot2docker se descargue y configure la máquina virtual de VirtualBox que proporcionará la parte servicio de docker:&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;boot2docker init
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Cada vez que queramos usar docker deberemos iniciar este servicio:&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;boot2docker start
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Una vez se inicie se nos mostrará las variables de entorno que informan al comando docker con qué servidor docker hablar. Los copiamos y ejecutamos en nuestra sesión y listo, ya podremos usar docker normalmente en nuestra máquina de 32 bits:&lt;/p&gt;

&lt;div class="language-plaintext highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;docker run ubuntu:14 echo "hola mundo!"
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Happy dockering!&lt;/p&gt;

&lt;p&gt;P.D.: Si abrimos un nuevo terminal hay que volver a establecer las variables de entorno. Podemos ejecutar &lt;code class="language-plaintext highlighter-rouge"&gt;boot2docker shellinit&lt;/code&gt; para volver a mostrarlas.&lt;/p&gt;

</description>
				<pubDate>Fri, 12 Feb 2016 00:00:00 +0000</pubDate>
				<link>http://blog.gimco.es/2016/02/12/docker-en-maquinas-de-32bits.html</link>
				<guid isPermaLink="true">http://blog.gimco.es/2016/02/12/docker-en-maquinas-de-32bits.html</guid>
			<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Bruno Orcha García</dc:creator></item>
		
			<item>
				<title>Sincronización de carpetas</title>
				<description>&lt;p&gt;En ocasiones nos vemos en la necesidad de tener que modificar ficheros en servidores remotos. Servidores de los que sólo disponemos de conexión &lt;code class="language-plaintext highlighter-rouge"&gt;ssh&lt;/code&gt; para acceder normalmente. Cuando se trata de realizar modificaciones puntuales sobre unos pocos archivos, como pueden ser ficheros de configuración o propiedades, es suficiente con iniciar una sesión &lt;code class="language-plaintext highlighter-rouge"&gt;ssh&lt;/code&gt;, y utilizar el editor de consola disponible en el servidor, como puede ser &lt;code class="language-plaintext highlighter-rouge"&gt;vi&lt;/code&gt;, &lt;code class="language-plaintext highlighter-rouge"&gt;nano&lt;/code&gt; o &lt;code class="language-plaintext highlighter-rouge"&gt;emacs&lt;/code&gt;. El problema se complica cuando necesitamos realizar estas modificaciones constantemente, por ejemplo, cuando queremos desarrollar contra un servidor remoto.&lt;/p&gt;

&lt;p&gt;Imaginemos que en el equipo del trabajo, tenemos una carpeta con algún proyecto web escrito en &lt;code class="language-plaintext highlighter-rouge"&gt;php&lt;/code&gt;, &lt;code class="language-plaintext highlighter-rouge"&gt;python&lt;/code&gt; o &lt;code class="language-plaintext highlighter-rouge"&gt;Ruby on Rails&lt;/code&gt;. Si en otro momento, estamos trabajando remotamente desde nuestras casas y nos vemos en la necesidad de seguir desarrollando en este proyecto tenemos varias opciones:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Conectarnos gráficamente al ordenador remoto y seguir utilizando nuestro entorno de desarrollo habitual. Esto lo conseguimos con herramientas como &lt;em&gt;VNC&lt;/em&gt;, &lt;em&gt;TeamViewer&lt;/em&gt;, redirigiendo las X, usando &lt;em&gt;&lt;a href="https://www.xpra.org"&gt;Xpra&lt;/a&gt;&lt;/em&gt;, etc. El problema con esta opción es que suele ser lento (por limitación del ancho de banda, latencia, uso de &lt;em&gt;VPN’s&lt;/em&gt;). Cuando programamos o usamos un IDE queremos que responda instantáneamente a nuestras pulsaciones y atajos de teclado, pero con estos sistema siempre se suele incluir un pequeño retardo que nos puede desesperar.&lt;/li&gt;
  &lt;li&gt;Montar el proyecto en el ordenador de casa. Si no disponemos de sistemas como &lt;em&gt;Dockers&lt;/em&gt; o &lt;em&gt;Valgrant&lt;/em&gt;, esto requerirá tener que instalar todas las dependencias que necesite el proyecto web. Esto puede ser complejo y requerirá instalar en el ordenador de casa las dependencias que se necesite (módulos python, gemas de ruby, librerías del sistema, etc). Si ademas dependemos de otros servicios (bases de datos, servicios web, comandos de consola) tendremos que instarlos localmente o comenzar a mapear puertos para poder acceder a ellos. Esto se complica aún mas si los sistemas operativos del trabajo y de casa son distintos (Linux vs Mac o incluso Debian 8 vs Ubuntu 14.10).&lt;/li&gt;
  &lt;li&gt;Conectarnos por &lt;code class="language-plaintext highlighter-rouge"&gt;ssh&lt;/code&gt;. Nuestro equipo del trabajo (o servidor remoto) ya está configurado y preparado para hacer funcionar el proyecto. Sólo debemos conectarnos, acceder a la carpeta del proyecto y realizar las modificaciones desde consola. El problema es que si no somos unos expertos en &lt;code class="language-plaintext highlighter-rouge"&gt;vi&lt;/code&gt; o &lt;code class="language-plaintext highlighter-rouge"&gt;emacs&lt;/code&gt;, si tenemos que estar modificando varios ficheros (abriendo y cerrando &lt;code class="language-plaintext highlighter-rouge"&gt;vi&lt;/code&gt;), navegar o buscar dentro del proyecto (usando comandos &lt;code class="language-plaintext highlighter-rouge"&gt;grep&lt;/code&gt; y &lt;code class="language-plaintext highlighter-rouge"&gt;find&lt;/code&gt;), volvemos a encontrarnos con la lentitud a la hora de programar con un entorno de desarrollo que no es el nuestro habitual.&lt;/li&gt;
  &lt;li&gt;Montar remotamente el directorio por cifs/samba/sftp/sshfs. En esta opción seguimos accediendo por &lt;code class="language-plaintext highlighter-rouge"&gt;ssh&lt;/code&gt; para lanzar la compilación o reiniciar el servidor web, pero la modificación y navegación de los ficheros del proyecto la podemos hacer con alguna aplicación desde nuestro ordenador de casa. Podríamos montar remotamente la carpeta del proyecto y abrirla con &lt;a href="http://www.aptana.com"&gt;Aptana&lt;/a&gt;, &lt;a href="http://www.eclipse.org"&gt;Eclipse&lt;/a&gt; o &lt;a href="http://www.sublimetext.com/3"&gt;SublimeText&lt;/a&gt; de nuestro ordenador de casa. Pero una vez mas la lentitud es el gran impedimento. Suelen ocurrir retardos al abrir los ficheros y sobre todo si intentamos buscar en el contenido de estos.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id="rsync"&gt;RSYNC&lt;/h2&gt;

&lt;p&gt;Existe otra opción adicional, que es la que mas utilizo últimamente. Por un lado tenemos la magnífica herramienta &lt;code class="language-plaintext highlighter-rouge"&gt;rsync&lt;/code&gt;. Este comando permite mantener sincronizados dos estructuras de carpetas de forma eficiente. Esta herramienta tiene multitud de opciones, ya que puede que nos interese o no, sincronizar también las fechas de los ficheros, los permisos, los propietarios, los enlaces simbólicos, borrar ficheros del destino, guardar los cambios en una tercera carpeta, etc. Así que por ejemplo, si en una misma máquina queremos tener sincronizada dos directorios escribiríamos lo siguiente:&lt;/p&gt;

&lt;div class="language-bash highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;rsync &lt;span class="nt"&gt;-navh&lt;/span&gt; /path/origen /path/destino
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Siempre debemos tener mucho cuidado con este comando, y prestar mucha atención a la carpeta destino. De hecho la opción &lt;code class="language-plaintext highlighter-rouge"&gt;-n&lt;/code&gt; del ejemplo es la opción &lt;code class="language-plaintext highlighter-rouge"&gt;--dry-run&lt;/code&gt; que realmente no hace modificaciones, solo nos informa sobre que operaciones que se van a realizar y que siempre deberíamos probar antes de lanzar el comando de forma final … para evitar dolorosas equivocaciones.&lt;/p&gt;

&lt;p&gt;También existe la posibilidad de utilizar este comando para sincronizar ficheros entre dos máquinas, para ellos debemos indicar con la opción &lt;code class="language-plaintext highlighter-rouge"&gt;-e&lt;/code&gt; qué comando se debe utilizar para llegar a la máquina remota. Lo mas habitual será utilizar &lt;code class="language-plaintext highlighter-rouge"&gt;ssh&lt;/code&gt; por lo que el comando quedaría de la siguiente forma:&lt;/p&gt;

&lt;div class="language-bash highlighter-rouge"&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;rsync &lt;span class="nt"&gt;-navh&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; ssh /path/origen usuario@servidor1:/path/destino
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Llegados a este punto, lo único que tendríamos que hacer es copiarnos inicialmente la carpeta remote a nuestro ordenador local, y comenzar a editarlo con nuestra herramienta favorita, como puede ser &lt;a href="http://www.sublimetext.com/3"&gt;SublimeText&lt;/a&gt;. Una vez hayamos realizado modificaciones podemos ejecutar el comando anterior y enviaremos las modificaciones realizadas en nuestro equipo local, hacia el servidor en el directorio destino.&lt;/p&gt;

&lt;h2 id="mejorando-el-flujo-de-trabajo"&gt;Mejorando el flujo de trabajo&lt;/h2&gt;

&lt;p&gt;Con lo detallado anteriormente, la forma de trabajar por ahora sería la siguiente:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Copiamos en nuestro ordenador local el proyecto remoto con el que queremos trabajar (con un simple scp o utilizando rsync en el sentido inverso).&lt;/li&gt;
  &lt;li&gt;Comenzamos a editar de forma local el proyecto con nuestro IDE.&lt;/li&gt;
  &lt;li&gt;Cada vez que queramos probar algo, ejecutamos el comando rsync.&lt;/li&gt;
  &lt;li&gt;Una vez terminada la ejecución de rsync, reiniciamos el servidor remoto (webrick, apache, tomcat, jboss, node o lo que sea) si es necesario.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Pero aún podemos mejorar esta forma de trabajar, eliminando el tercer paso, en el que somos nosotros los que manualmente tenemos que lanzar el comando rsync periódicamente.&lt;/p&gt;

&lt;p&gt;Para esto podemos utilizar la utilidad &lt;a href="https://github.com/aalto-ics-kepaco/fswatch-rsync"&gt;fswatch-rsync&lt;/a&gt;. Esto es un script en bash, que combina el uso de rsync con &lt;a href="https://github.com/alandipert/fswatch"&gt;fswatch&lt;/a&gt;. fswatch es una utilidad que utiliza inotify/kqueue/poll que permite monitorizar los eventos producidos en un directorio. De esta forma, este script bash está preparado para detectar los cambios realizados en un directorio (que será nuestro proyecto), e invocará rsync indicándole además qué ficheros son los que han cambiado, permitiendo a rsync funcionar de forma aún más rápida y optima, ya que no necesitará analizar los ficheros del directorio origen y destino para decidir qué cambios hay que realizar. Además permite ignorar algunos eventos, como pueden ser los producidos en los directorios .git o .svn.&lt;/p&gt;

&lt;p&gt;De esta forma, podemos trabajar de forma muy cómoda con proyectos en servidores remotos implementando un &lt;a href="http://www.danplanet.com/blog/2012/05/09/low-latency-continuous-rsync/"&gt;sistema de sincronización de baja latencia&lt;/a&gt;.&lt;/p&gt;

</description>
				<pubDate>Thu, 06 Aug 2015 00:00:00 +0000</pubDate>
				<link>http://blog.gimco.es/2015/08/06/sincronizacion-carpetas.html</link>
				<guid isPermaLink="true">http://blog.gimco.es/2015/08/06/sincronizacion-carpetas.html</guid>
			<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Bruno Orcha García</dc:creator></item>
		
	</channel>
</rss>