30 de diciembre de 2017

Rainbows in my home

In the rainy afternoons, at about 6 or 7 p.m. when it is close to sundown is common to see rainbows from the window of my home. Actually it is not so common, because it has to coincide the right hour, that the rain is neither too light nor too heavy so the sunlight can pass under the clouds, and I have to be at home to see it. The important things are that there are not too many buildings ahead and the window to look to the east, because the sun in the afternoon is to the west and the rainbow appears by the light that is reflected in the droplets (it is more complicated, but that gives the correct direction). (With a window to the west you should try at 6 or 7 in the morning, but it's too sleepy at that time.)



I made a scheme. Obviously it is not to scale. I think I put the colors in the correct order (the order in which they appear in the reflection, not the order in which they appear in the rainbow) (I have a probability of almost 50% of picking the correct order :)) In the photo you can see the primary and secondary rainbow, but in the diagram I drew shows only the primary one. (When I made the drawing I noticed that there has to be another upward reflection that should illuminate the clouds, I suppose that the effect in the clouds is very dim and it can’t be seen.)


In the kitchen we have a wooden ornament with a rainbow (it was for hanging things, but the hanger broke). It was left by the previous owner of the apartment and is stuck very strongly to the wall so we keep it there. Is there any relationship?


PS: If you like rainbows, check out this article about a triple rainbow near Seattle.

Arcoiris en mi casa

Las tardes lluviosas, a eso de las 6 o 7 de la tarde cuando está cerca de anochecer es común ver arcoíris desde la ventana de mi casa. En realidad no es tan común, porque tiene que coincidir un buen horario, que la lluvia no sea ni muy suave ni muy fuerte así el sol puede pasar por debajo de las nubes, y tengo que estar en casa para verlo. Lo importante es no tener muchos edificios adelante y que la ventana mire al este, porque el sol a la tardecita está al oeste y el arcoíris aparece por la luz que se refleja en las gotitas (es más complicado, pero eso da la dirección correcta). (Con una ventana al oeste habría que probar a las 6 o 7 de la mañana, pero hace sueño a esa hora.)



Hice un esquema. Obviamente no está a escala. Creo haber puesto los colores en el orden correcto (el orden en que aparecen en el reflejo, no el orden en el que se ven en el arcoíris) (tengo una probabilidad de casi el 50% de acertar :)) En la foto se ve el arcoíris primario y secundario, pero en el esquema dibujé sólo el primario. (Al hacer el dibujo noté que tiene que haber otro reflejo hacia arriba que debería iluminar las nubes, supongo que el efecto en las nubes es muy tenue y no se ve.)



En la cocina tenemos un adorno de madera con un arcoíris (servía para colgar cosas, pero el colgador se rompió). Era de la anterior dueña de la casa y está muy pegado a la pared así que quedó ahí. ¿Habrá alguna relación? 



PD: Si te gustan los arcoíris, mirá este artículo sobre un arcoíris triple cerca de Seattle (en Inglés).

6 de noviembre de 2017

Testing the random numbers in Racket with Random Sanity

Introduction

A few days ago I was looking at the Random Sanity project. It is a web service where you send a string of numbers supposedly at random, to see how bad is the random number generator. It doesn’t make a very exhaustive check, so it only detects cases where the generator is very bad, or cases in which the string of random numbers has already been sent.

The string consists of 64 bytes encoded in hexadecimal. For example

and the result is "
true" or "false".

The idea is to use it to test the random number generation functions in Racket and see if they pass this test.

Auxiliary constructions

random-bytes

Generates a string of bytes of length n. It is similar to crypto-random-bytes, but it uses pseudorandom numbers instead of cryptographically secure numbers. (Strange: There is no for/bytes.)

(define (random-bytes n)
  (list->bytes
   (for/list ([i (in-range n)])
     (random 256))))

with-random-seed

The seed of the pseudorandom numbers in Racket is initialized with the milliseconds near the beginning of the program. It is sometimes convenient to locally modify the seed of pseudorandom numbers without affecting the pseudorandom numbers of the rest of the program. This macro allows us to evaluate the body, fixing the seed, and after that continue with the normal pseudorandom sequence.

(define-syntax-rule (with-random-seed seed body ...)
  (parameterize ([current-pseudo-random-generator (make-pseudo-random-generator)])
    (random-seed seed)
    body ...))

Let’s try some examples

(displayln (random 10))                   ; ==> maybe 3
(with-random-seed 1234567890
  (displayln (random 10))                 ; ==>        1
  (displayln (random 10)))                ; ==>        5
(displayln (random 10))                   ; ==> maybe 2
(with-random-seed 1234567890
  (displayln (random 10))                 ; ==>        1
  (displayln (random 10)))                ; ==>        5
(displayln (random 10))                   ; ==> maybe 5

The tester

The main function is the tester, which uses a bytes string. The tester formats it and sends it via hppt to Random Sanity. Then it reads the test result and transforms it into #t or #f, which are more idiomatic. The function has very few error checks, for simplicity and for laziness.

#;(require net/http-client
           file/sha1)
 (define (test-random-bytes b)
  (let-values ([(status headers content)
                (http-sendrecv "rest.randomsanity.org"
                               (string-append "/v1/q/"
                                              (bytes->hex-string b)))])
    (let ([text (port->string content)])
      (cond
        [(string=? text "true") #t]
        [(string=? text "false") #f]
        [else (raise-result-error 'test-random-bytes
                                  "true or false"
                                  text)]))))

Some tests

All zeros # "000 ... 000"

It is not a good example of random numbers and obviously the result is #f.

(test-random-bytes (make-bytes 64 0))  ; ==> #f

Pseudo-random numbers with a fixed seed

The first time someone uses one seed the result is #t (it was me, believe me, I saw a #t) but when someone runs the program again with the same seed the sequence of pseudorandom numbers is repeated, so in all subsequent retries the result is #f. If you test another seed, you should be able to see a #t once and then #f. (Using the same seed 1234567890 in other languages will probably get a string of different bytes, so you can probably see another "true" once. More details.)

(with-random-seed 1234567890
  (test-random-bytes (random-bytes 64)))   ; ==> #f (except the first time)

Fixed seed pseudorandom numbers

Racket initializes the seed of random numbers with the milliseconds at some point near the start of the execution of the program. So every time you will get a different sequence, probably never seen before. Unless this article is an absolute success and millions of people try to run the program, it is very unlikely to be so unlucky to get a repeated seed. So you almost certainly will get #t.

(test-random-bytes (random-bytes 64))   ; ==> #t (almost sure)

Cryptographically secure numbers

For some applications it is a risk that someone else guesses the sequence. In the previous example someone could estimate when the program was run and test all the possible values of the milliseconds in a nearby range until finding the match. For these cases you can use crypto-random-bytes that generates cryptographically safe numbers. If there is no problem in the operating system or in Racket it should be (almost) impossible to find a match, and get a result that is not #t. (This is precisely the goal of Random Sanity, to detect that a serious error was accidentally introduced into the random number generator.)

#;(require racket/random)
(test-random-bytes (crypto-random-bytes 64))  ;  ==> #t

The complete program

It is necessary to use some modules, so for convenience I’ll repeat the complete program. (Some parts of the program that don´t use internet connections can be tested in PasteRack.)

#lang racket

(require net/http-client
         file/sha1
         racket/random)

(define (random-bytes n)
  (list->bytes
   (for/list ([i (in-range n)])
    (random 256))))

(define-syntax-rule (with-random-seed seed body ...)
  (parameterize ([current-pseudo-random-generator (make-pseudo-random-generator)])
    (random-seed seed)
    body ...))

(define (test-random-bytes b)
  (let-values ([(status headers content)
                (http-sendrecv "rest.randomsanity.org"
                               (string-append "/v1/q/"
                                              (bytes->hex-string b)))])
    (let ([text (port->string content)])
      (cond
        [(string=? text "true") #t]
        [(string=? text "false") #f]
        [else (raise-result-error 'test-random-bytes
                                  "true or false"
                                  text)]))))

(test-random-bytes (make-bytes 64 0))        ; ==> #f
(with-random-seed 1234567890
  (test-random-bytes (random-bytes 64)))     ; ==> #f
(test-random-bytes (random-bytes 64))        ; ==> #t
(test-random-bytes (crypto-random-bytes 64)) ; ==> #t

Probando los números aleatorios de Racket con Random Sanity

Introducción

Hace unos días estuve mirando el proyecto Random Sanity. Es un servicio web al que uno manda una cadena de números supuestamente al azar, para que vea que tan malo es el generador de números al azar. No hace un chequeo muy exhaustivo, así que sólo detecta casos en que el generador es muy malo, o casos en que la cadena de números al azar ya fue enviada.

La cadena consiste en 64 bytes codificados en hexadecimal. Por ejemplo


y el resultado es "true" o "false".

La idea es usarlo para probar las funciones de generación de números al azar en Racket y ver si pasan este test.

Construcciones auxiliares

random-bytes

Genera una cadena de bytes de largo n. Es parecida a crypto-random-bytes, pero usa números pseudoaleatorios en vez de números criptográficamente seguros. (Extraño: No existe for/bytes.)

(define (random-bytes n)
  (list->bytes
   (for/list ([i (in-range n)])
     (random 256))))

with-random-seed

La semilla de los números pseudoaleatorios en Racket se inicializa con los milisegundos cerca del comienzo del programa. A veces es cómodo modificar localmente la semilla de los números pseudoaleatorios sin afectar a los números pseudoaleatorios del resto del programa. Esta macro permite evaluar el body fijando la semilla y que después todo vuelva a la normalidad.

(define-syntax-rule (with-random-seed seed body ...)
  (parameterize ([current-pseudo-random-generator (make-pseudo-random-generator)])
    (random-seed seed)
    body ...))

Probemos algunos ejemplos

(displayln (random 10))                   ; ==> quizás 3
(with-random-seed 1234567890
  (displayln (random 10))                 ; ==>        1
  (displayln (random 10)))                ; ==>        5
(displayln (random 10))                   ; ==> quizás 2
(with-random-seed 1234567890
  (displayln (random 10))                 ; ==>        1
  (displayln (random 10)))                ; ==>        5
(displayln (random 10))                   ; ==> quizás 5

Testeador

La función principal es el testeador, que usa una cadena de bytes. La formatea y envía por hppt a Random Sanity. Luego lee el resultado del test y lo transforma en #t o #f, que son más idiomáticos. La función tiene muy pocos chequeos de errores, por simplicidad y por pachorra.

#;(require net/http-client
           file/sha1)
 (define (test-random-bytes b)
  (let-values ([(status headers content)
                (http-sendrecv "rest.randomsanity.org"
                               (string-append "/v1/q/"
                                              (bytes->hex-string b)))])
    (let ([text (port->string content)])
      (cond
        [(string=? text "true") #t]
        [(string=? text "false") #f]
        [else (raise-result-error 'test-random-bytes
                                  "true or false"
                                  text)]))))

Algunos test

Todos ceros #"000...000"

No es un buen ejemplo de números aleatorios y obviamente el resultado es #f.

(test-random-bytes (make-bytes 64 0))  ; ==> #f

Números pseudoaleatorios con una semilla fija

La primera vez que alguien usa la semilla da #t (fui yo, créanme que vi un #t) pero al ejecutarlo nuevamente con la misma semilla se repite la secuencia de números pseudoalatorios y entonces todas las veces subsiguientes da #f. Probando otra semilla deberían poder ver una vez #t y después #f. (Probando la misma semilla 1234567890 en otros lenguajes probablemente se obtenga una cadena de bytes diferentes, así que probablemente puedan ver una vez el primer "true". Más detalles)

(with-random-seed 1234567890
  (test-random-bytes (random-bytes 64)))   ; ==> #f (salvo la primera vez)

Números pseudoaleatorios sin semilla fija

Racket inicializa la semilla de números aleatorios con los milisegundos en algún momento cerca del inicio de la ejecución del programa. Así que cada vez se obtiene una secuencia diferente, probablemente nunca vista antes. A menos que este artículo sea un éxito desmesurado y millones de personas se pongan a probar el programa es muy poco probable que al ejecutarlo uno tenga tanta mala suerte que se repita la semilla. Así que casi seguramente uno obtiene #t.

(test-random-bytes (random-bytes 64))   ; ==> #t (casi seguro)

Números criptográficamente seguros

Para algunas aplicaciones es un riesgo que alguien adivine la secuencia. En el ejemplo anterior alguien podría estimar cuándo se ejecutó el programa y probar con todos los posibles valores de los milisegundos en un rango cercano hasta encontrar la coincidencia. Para esos casos se puede usar crypto-random-bytes que genera números criptográficamente seguros. Si no hay ningún problema en el sistema operativo o en Racket debería ser (casi) imposible encontrar una coincidencia y que el resultado no sea #t. (Este es justamente el objetivo de Random Sanity, detectar que involuntariamente se introdujo un error grave en el generador de números aleatorios.)

#;(require racket/random)
(test-random-bytes (crypto-random-bytes 64))  ;  ==> #t

El programa completo

Es necesario usar algunos módulos, así que por comodidad repitamos el programa completo. (Algunas partes del programa que no usan conexiones de internet se pueden probar en PasteRack.)


#lang racket

(require net/http-client
         file/sha1
         racket/random)

(define (random-bytes n)
  (list->bytes
   (for/list ([i (in-range n)])
    (random 256))))

(define-syntax-rule (with-random-seed seed body ...)
  (parameterize ([current-pseudo-random-generator (make-pseudo-random-generator)])
    (random-seed seed)
    body ...))

(define (test-random-bytes b)
  (let-values ([(status headers content)
                (http-sendrecv "rest.randomsanity.org"
                               (string-append "/v1/q/"
                                              (bytes->hex-string b)))])
    (let ([text (port->string content)])
      (cond
        [(string=? text "true") #t]
        [(string=? text "false") #f]
        [else (raise-result-error 'test-random-bytes
                                  "true or false" text)]))))

(test-random-bytes (make-bytes 64 0))        ; ==> #f
(with-random-seed 1234567890
  (test-random-bytes (random-bytes 64)))     ; ==> #f
(test-random-bytes (random-bytes 64))        ; ==> #t
(test-random-bytes (crypto-random-bytes 64)) ; ==> #t

22 de febrero de 2017

Trying Chez Scheme

A new virtual machine for Racket

The plan is to replace the Racket virtual machine with Chez Scheme, including the interpreter and the JIT and everything else, within a year. There are still many unknowns and details to be solved. In particular, I'm interested in how Chez optimizations are compared to the optimizations Racket currently has. Moreover, there are a million small incompatibilities between Chez and Racket that must be fixed in the middle layer and they will directly or indirectly affect the optimizations.

The problem is that in order to understand what is going on, I need to break^H^H^H^H^H make modifications and maybe try to make improvements to the current optimizations in Chez, and see if the changes that I try are correct or they produce errors. So the first step is to compile Chez and take a look at the tests.


Compiling Chez on Windows

I can compile it, but I still have errors in the tests. I guess I have something misconfigured on my machine. I'm still fighting against iconv.dll. And also there is probably some other problem because there are too many tests that fail. Let's forget Windows for now.


Compiling Chez on Linux

Compiling on Linux was easier than I expected. Almost no surprises. Foolproof instructions for installing on a virtual machine:


  • Download and install VirtualBox
  • Download the Ubuntu disk (desktop)
  • Install Ubuntu on the virtual machine (not on the real machine). The only hard question is the size of the hard drive, I put 12GB and that is enough. I recommend turning off the option to lock the screen after 5 minutes of inactivity because several of the following steps take a long time.

The following steps are explained in the BUILDING file. But in case of doubt the recipe is:

sudo apt-get install git
sudo apt-get install libncurses5-dev libncursesw5-dev
sudo apt-get install libx11-dev
git clone https://github.com/cisco/ChezScheme.git
cd ChezScheme
./configure
sudo make install
make test
cat a6le/mats/summary

The make test step performs a lot of tests and it takes about an hour, so you have to be patient. The errors will be shown at a6le/mats/summary and if there are no errors the file should be something like

-------- o = 0 --------
-------- o = 3 --------
[...]
-------- o = 3 ehc=t eval=interpret --------

Where each line represents a possible configuration of the tests. In my case it was a success (almost) at the first attempt.


Automatic tests in Travis

For the tests in Travis I wanted that in case of an error that the program finish with an error code other than 0, for Travis to notice it and signal the fault.

In addition, I wanted to be able to run each of the test configurations separately, so they can run in parallel in Travis. This also helps to identify where the error is.

One of the problems is that some of the variables of the makefile are reused.

o : In some makefiles it is obviously the extension of the files with object code, that is o=.o or o=.obj according to the compiler. In others makefiles it is obviously the level of optimization, typically o=0 or o=3.

cp0 : In some makefiles it is the number of times the step cp0 that performs the optimizations of the Chez code is applied, and in others it takes the values t or f to indicate whether the step cp0 is applied or not. (Maybe it’s possible to change the t/f by 0/1 (or 0/3) and use the same meaning everywhere?)

I solved it by creating two new variables one_o and one_cp0 that at some point are copied to the original variables, taking some care to handle the case where they are not defined.

To the main makefile I added an option to run all the tests with a given configuration instead of all the tests with all the configurations. For example:

make testone one_o=3 one_cp0=t

The changes in the makefiles are not very elegant, moreover, they are probably a monstrosity, but they run in my machine (be careful with the tabs):

makefiles/Makefile.in
testone:
(cd $(workarea) && $(MAKE) testone PREFIX=$(workarea)/)

makefiles/Makefile-workarea.in
ifdef one_o
  new_o = o=$(one_o)
else
  new_o = 
endif
ifdef one_cp0
  new_cp0 = cp0=$(one_cp0)
else
  new_cp0 = 
endif

testone: build

(cd mats ; $(MAKE) allxonewerror $(new_o) $(new_cp0))
@echo "test run complete.  check $(PREFIX)mats/summary for errors."

/mats/Mf-base
allxonewerror: prettyclean
$(MAKE) allxhelp
$(MAKE) maybe-raise-error

maybe-raise-error:

if [ "`wc -l < report-${conf}`" != "0"  ] ; then\
 $(MAKE) raise-error ;\
fi

raise-error:

$(error We found an error)

The .travis.yml file is more or less standard. Instead of make install I use make build to avoid using sudo, so Travis runs the tests almost immediately instead of waiting for amachine with sudo enabled. My version still generates a small complaint because build uses internally sudo or something like that, but it does not seem to be important for the final result.

.travis.yml
sudo: false
language: c

matrix:

  include:
  - os: linux
    compiler: gcc
    env: CHEZ_TEST_ARGS="one_o=0"
  - os: linux
    compiler: gcc
    env: CHEZ_TEST_ARGS="one_o=3"
#[…]
  - os: linux
    compiler: gcc
    env: CHEZ_TEST_ARGS="one_o=3 eval=interpret ehc=t rmg=2"

script:

- ./configure
- make build
- make testone $CHEZ_TEST_ARGS

The code is available in github (tests ), along with an additional change that adds an obviously wrong test just to see that when there is an error it is detected (test ). 


Some ideas

My plan is to analyze the Chez code now, but there are some improvements that I think I will ignore for now.

I would like instead of running all tests for each option with make testone, being able to run each test separately, so the output in Travis is shorter and easier to understand and it is easier to find the error.

And I would like to try to fix the compilation in Windows and if possible add the tests in AppVeyor.

Probando Chez Scheme

Una nueva máquina virtual para Racket

El plan es que dentro de un año reemplazar la máquina virtual de Racket por la de Chez Scheme, incluyendo el intérprete y el JIT y todo lo demás. Todavía hay muchas incógnitas y detalles por resolver. A mí, en particular me interesa ver cómo son las optimizaciones que tiene Chez para poder compararas con las que tiene actualmente Racket. Además hay un millón de pequeñas incompatibilidades entre Chez y Racket que van a tener que ser arregladas en la capa intermedia y que directa o indirectamente van a afectar a las optimizaciones.

El problema es que para entender lo que está pasando necesito romper^H^H^H^H^H hacer modificaciones y quizás intentar hacer mejoras en las optimizaciones actuales de Chez y ver si los cambios que se me ocurren andan bien o producen errores. Así que el primer paso es compilar Chez y ver los testeos.

Compilando Chez en Windows

Lo pude compilar, pero todavía tengo errores en los test. Supongo que tengo algo mal configurado en mi máquina. Todavía sigo peleando contra iconv.dll. Y además probablemente haya algún otro problema más porque hay demasiados test que fallan. Olvidémonos de Windows por ahora.


Compilando Chez en Linux


La compilación en Linux fue más fácil de lo que esperaba. Casi sin sorpresas. Instrucciones a prueba de bobos para instalarlo en una máquina virtual:

  • Bajar e instalar VirtualBox
  • Bajar el disco de Ubuntu (desktop)
  • Instalar Ubuntu en la máquina virtual (no en a real). La única pregunta difícil es el tamaño del disco rígido, yo le puse 12GB y con eso alcanza bien. Recomiendo desactivar la opción de bloquear la pantalla a los 5 minutos de inactividad, porque varios de los siguientes pasos tardan mucho tiempo.

Los siguientes pasos están explicados en el archivo BUILDING. Pero en caso de dudas la receta es:

sudo apt-get install git
sudo apt-get install libncurses5-dev libncursesw5-dev
sudo apt-get install libx11-dev
git clone https://github.com/cisco/ChezScheme.git
cd ChezScheme
./ configure
sudo make install
make test
cat a6le/mats/summary

El paso make test realiza muchos testeos y tarda como una hora, así que hay que tener paciencia. Los errores van a parar a a6le/mats/summary y si no hay errores el archivo debería ser algo como

-------- o=0 --------
-------- o=3 --------
[...]
-------- o=3 ehc=t eval=interpret --------

En dónde cada línea representa una configuración posible de los testeos. En mi caso fue un éxito (casi) al primer intento.


Testeos automáticos en Travis


Para los testeos en Travis quería que si había un error el programa terminara con un código de error distinto de 0 para que Travis se diera cuenta y marcara el fallo.

Además, quería poder correr cada una de las configuraciones de los testeos por separado. Así se pueden correr en paralelo en Travis. Esto también ayuda a identificar dónde está el error.

Uno de los problemas es que algunas de las variables de los makefile se reutilizan. 

o : En algunos makefiles es obviamente la extensión de los archivos con código objeto, o sea o=.o ó o=.obj según el compilador. En otros makefiles es obviamente el nivel de optimización, típicamente o=0 ó o=3.

cp0 : En algunos makefiles es la cantidad de veces que se aplica el paso cp0 que realiza las optimizaciones del código de Chez y en otros toma valores t ó f para indicar si el paso cp0 se aplica o no. (¿Quizás se pueda cambiar el t/f por 0/10/3) y usar el mismo significado en todos lados?)

Lo solucioné creando dos variables nuevas one_o y one_cp0 que en algún momento se copian a las variables originales, teniendo un poco de cuidado para manejar el caso en que no están definidas.

Al archivo makefile principal le agregué una opción para correr todos los testeos con una dada configuración en vez de todos los testeos con todas las configuraciones. Por ejemplo: 

make testone one_o=3 one_cp0=t

Los cambios en los makefiles no son muy elegantes, es más, probablemente sean una monstruosidad, pero andan (ojo con los tabs):

makefiles/Makefile.in
testone:
(cd $(workarea) && $(MAKE) testone PREFIX=$(workarea)/)

makefiles/Makefile-workarea.in

ifdef one_o
  new_o = o=$(one_o)
else
  new_o = 
endif
ifdef one_cp0
  new_cp0 = cp0=$(one_cp0)
else
  new_cp0 = 
endif

testone: build

(cd mats ; $(MAKE) allxonewerror $(new_o) $(new_cp0))
@echo "test run complete.  check $(PREFIX)mats/summary for errors."

/mats/Mf-base

allxonewerror: prettyclean
$(MAKE) allxhelp
$(MAKE) maybe-raise-error

maybe-raise-error:

if [ "`wc -l < report-${conf}`" != "0"  ] ; then\
 $(MAKE) raise-error ;\
fi

raise-error:

$(error We found an error)

El archivo .travis.yml es más o menos estándar. En vez de make install uso make build para evitar usar sudo, así Travis ejecuta los test casi inmediatamente en vez de tener que esperar a que haya una máquina disponible con sudo habilitado. Mi versión todavía genera una pequeña queja porque build usa internamente sudo algo así, pero no parece ser importante para el resultado final.

.travis.yml 
sudo: false
language: c

matrix:

  include:
  - os: linux
    compiler: gcc
    env: CHEZ_TEST_ARGS="one_o=0"
  - os: linux
    compiler: gcc
    env: CHEZ_TEST_ARGS="one_o=3"
#[…]
  - os: linux
    compiler: gcc
    env: CHEZ_TEST_ARGS="one_o=3 eval=interpret ehc=t rmg=2"

script:

- ./configure
- make build
- make testone $CHEZ_TEST_ARGS

El código está disponible en github (tests ), junto con un cambio adicional que agrega un testeo obviamente erróneo sólo para ver que cuando hay un error se detecta (test ).


Algunas ideas:


Mi plan es ponerme a analizar el código de Chez directamente, pero quedan en el tintero algunas mejoras que creo que voy a ignorar por ahora. 

Me gustaría que en vez de ejecutar todos los test para cada opción con make testone, poder ejecutar cada test por separado, así la salida en Travis es más corta y fácil de entender y es más fácil encontrar el error.

Por otro lado, me gustaría tratar de arreglar la compilación en Windows y si es posible agregar los test en AppVeyor.

18 de enero de 2017

Homemade Crunchy Nougat Recipe

We were getting ready for dinner and I noticed there were no nougats. Very  wrong. Moreover, almost all businesses had closed. I just got to buy some walnuts and almonds.

Ingredients:
  • Some walnuts (let's say ¿20?)
  • Some almonds (let's say ¿20?)
  • Some sugar (let’s say, a quarter of the apparent size of the mountain of walnuts and almonds)
Steps:

  • Butter a metal baking dish.
  • Put the sugar in a metal jug (I do not put water) and heat it until the caramel has a slight brown tone.
  • Add the walnuts and almonds and stir a little to warm the mix.
  • Throw the mixture into the baking dish and distribute so that there is a flat layer instead of a mountain. More or less with the high of a walnut or hazelnut.
  • ¡Caution! ¡Wait for it to cool down! The caramel is hot, very hot. It's hot even after it gets half-hard. It's hot even after it seems cold. It's hot even after waiting a while. Better to wait for it than to get burn.
  • Unstick the crunchy nougat, try to break it and eat it. (If it is too high, it is difficult to break.)

Ideas:
  • I found it very sugary, next time I'll try with less sugar.
  • I should have waited a little longer for the caramel to take color, so it had a more caramely taste, but I was afraid to burn it.