Monday, April 30, 2018

A Fox, Some Hounds and a Camel - A Fun Walk Through Ladew Gardens

To celebrate my birthday, Shira and I took a day-trip to Ladew Topiary Gardens outside of Baltimore, MD. It was a chance to explore a new place, snap interesting pictures and generally get some outside time. Maybe selecting the coldest day in Spring wasn't ideal, but hey, you've got to play the cards you're dealt.

Because of the crazy weather, some of the Spring plants had yet to blossom. The azaleas and rhododendron were close to popping, but weren't there yet. Still, the tulips and Virginia blubells were out in force. And I suppose the chilly weather kept the crowds away, too.

Pretty flowers, however, aren't the sole stars of the garden. The real champions are the topiaries and sculptures. The first scene you see when walking through the garden is that of a fox being chased by hounds. Hunting and sporting practices aside, the scene is quite vivid and really well done. Other highlights included a camel (just for you, Mom!), a victory sign and what Shira and I guessed was a unicorn. The topiaries look so natural, and there are so many of them, that you forget how much of an accomplishment they are. The gardens make it look easy.

The gusty, chilly wind wasn't ideal while we were strolling through the gardens. But, it did make the kinetic sculptures on site come to life. I grabbed a few pictures of some of the pieces, hoping to unravel their mysteries from the comfort of my own home. The sculptures make for a quirky addition to the garden; one that I wholly approve of.

There was something else about the gardens that took me some time to appreciate. I realized that many of the gardens we visit are botanical, and often strive to catalog and showcase as many plants as possible. Ladew Gardens has a different mission: it's there to showcase one man's attempt at creating a beautiful and pleasurable space. Sure, there's structure and plenty to learn from the gardens, but there's also a playfulness and personal feeling to the garden that make it especially unique. Ladew isn't a competitor to say the US Botanic Garden, it's an alternative and worthy peer.

On our way back to DC we stopped at Dougie's BBQ for a fantastic Kosher meal. The burger I had was beyond delicious!

Friday, April 27, 2018

Elise Four - Strong, Human Powered, Encryption

If you want to understand a subject, teach it to someone. If you want to truly and completely understand a subject, teach it to a computer. That is, program it.

This idea of coding to reach understanding is a lesson I've learned countless times. Most recently, I experienced this adage while tackling the Elise Four exercise from Programming Praxis. The task was to implement the EliseFour (aka, LC4) Encryption Algorithm. This approach to encryption is especially interesting because of its competing goals. First, it attempts to provide for strong encryption. Secondly, it's designed to be run offline by human power alone. (Not unlike this solution)

See the contradiction there? All the tools one thinks of when considering strong encryption, such as terrifically hard calculations and massively large numbers, are off the table. LC4 therefor has to choose clever conventions over raw processing power. The author solves this challenge by constructing a grid of Scrabble-like tiles and re-arranging them using a simple set of rules. While I'm interesting in implementing an offline version of the algorithm, for now, I've coded the algorithm in Scheme.

Here's the algorithm at work:

;; Example 1
Key:  xv7ydq #opaj_ 39rzut 8b45wc sgehmi knf26l 
Nonce:  solwbf 
Text:  im_about_to_put_the_hammer_down 
Encrypted:  i2zqpilr2yqgptltrzx2_9fzlmbo3y8_9pyssx8nf2 
Decrypted:  im_about_to_put_the_hammer_down#rubberduck 

;; Example 2
Key:  xv7ydq #opaj_ 39rzut 8b45wc sgehmi knf26l 
Nonce:  argvpx 
Text:  hurrrraaaaaaaaaaaaaaaaaaaaaay 
Encrypted:  3bcxnt57hus6accn97v4iie__hjbml8wr 
Decrypted:  hurrrraaaaaaaaaaaaaaaaaaaaaay#ben 

The source code for my solution can be found in the usual spot.

Example 1 comes directly from the paper describing the algorithm and was my lifeline for implementing and verifying the algorithm. You can see that to communicate with the LC4 algorithm, both users need to know the key value ahead of time. The key will always be an arrangement of the 36 different characters in the alphabet.

Encryption and decryption also depend on a nonce value, which is used to add additional noise into the system. The nonce value is to be shared with the recipient, and no harm comes from it being exposed to an attacker.

Example 2 emphasizes that even text with a telling shape comes out as random gibberish when encrypted; a sign that his is indeed a secure algorithm.

LC4 is an elegant solution to an unusual encryption challenge. On one level, I knew this by browsing the source paper on the topic. But now that I've struggled through an implementation, I know this on a completely different level. And when I say struggle, I mean it. Encryption algorithms are tricky to implement because the goal is to take text and turn it into garbage, but very specific garbage. So when I had variable c where y belonged, or neglected to reset r and y the algorithm still produced jumbled text, but it was the wrong jumbled text.

At least four times I was absolutely sure I had coded the encryption and decryption algorithm exactly as described, yet my code was failing. I did what any smart programmer does in these situations: I stepped away and re-attacked the problem after some rest. With some time away from the code, the bugs were obvious and easy to address.

When I finished coding the algorithm I had far more than just working code. I had an intimate appreciation for key generation, nonce usage, dictionary selection, authentication operation and a half dozen other subtle details that work together to make LC4 function. Oh, and the dopamine rush from seeing my text get encrypted and then decrypted was nice too.

Thursday, April 26, 2018

10 out of 10 - A Perfect DC Weather Day

The Capital Weather Gang promised a 10 out of 10 day, and the weather totally cooperated.

What a perfect day to stroll through the neighborhood, snapping pics of all the blooming flowers and trees.

Even the weeds were photogenic today!

Tuesday, April 24, 2018

Lightweight, R7RS Compliant and in palm of my hand - Compiling chibi-scheme on Android

I'm eyeing this Programming Praxis exercise as one to tackle next but there's a catch before I can start programming. TinyScheme, my current Scheme Interpreter of choice, doesn't have support for generating random numbers, and the exercise requires this.

I could hack something together using TinyScheme (and probably the Programming Praxis Standard Prelude). Or, I could do as John Cowen suggested, and try out a different implementation. He suggested Chibi-Scheme. Chibi Scheme is lightweight, but unlike TinyScheme, it's also quite modern. This means, among other things, it should give me easy access to the SRFI-27: Sources of Random Bits library. Longer term, it would be ideal to have access to R7RS features and other modern niceties.

I busted out my cell phone and hardware keyboard. I opened up Termux and went to work. Here's my attempt at getting chibi-scheme running on Android:

Let's be clever and build this on the SD card.

[localhost]{~}(1)$ cd storage/external-1/src

[localhost]{src}(1)$ git clone
Cloning into 'chibi-scheme'...
remote: Counting objects: 18101, done.
remote: Compressing objects:  ....
Receiving objects: 100% 
Resolving deltas:   0% 

[localhost]{src}(1)$ cd chibi-scheme

[localhost]{chibi-scheme}(1)$ ./configure
bash: ./configure: /bin/sh: bad interpreter: Permission denied

D'oh. OK, don't panic.

[localhost]{chibi-scheme}(1)$ sh configure
Autoconf is an evil piece bloatware encouraging cargo-cult programming.
Make, on the other hand, is a beautiful little prolog for the filesystem.
Just run 'make'.

Oooh, sassy software. I like it.

[localhost]{chibi-scheme}(1)$ make
The program 'make' is not installed. Install it by executing:
 pkg install make

No problem, I'll just do what Termux tells me to do...

[localhost]{chibi-scheme}(1)$ pkg install make
Building dependency tree...
Reading state information...
All packages are up to date.
The following packages were automatically installed and are no longer required:
  binutils libandroid-support-dev libffi libllvm ndk-stl ndk-sysroot
Use 'apt autoremove' to remove them.
The following NEW packages will be installed:
0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded.
Need to get 76.5 kB of archives.
After this operation, 246 kB of additional disk space will be used.
Get:1 stable/main aarch64 make aarch64 4.2.1 [76.5 kB]
Preparing to unpack .../make_4.2.1_aarch64.deb ...

[localhost]{chibi-scheme}(1)$ make
echo '#define sexp_so_extension "'.so'"' > include/chibi/install.h
echo '#define sexp_default_module_path "'/data/data/com.termux/files/usr/share/chibi:/data/data/com.termux/files/usr/lib/chibi'"' >> include/chibi/install.h
echo '#define sexp_platform "'android'"' >> include/chibi/install.h
echo '#define sexp_version "'0.8.0'"' >> include/chibi/install.h
echo '#define sexp_release_name "'`cat RELEASE`'"' >> include/chibi/install.h
cc -c  -Iinclude  -Wall -g -g3 -O3  -o main.o main.c
make: cc: Command not found
make: *** [Makefile:149: main.o] Error 127

Whoops. No compiler. After attempts to install 'gcc' and search for a compiler
package in general I finally tripped over 'clang.' That will apparently install
a C compiler.

[localhost]{chibi-scheme}(1)$ pkg install clang
The following NEW packages will be installed:
0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded.
Need to get 12.9 MB of archives.
After this operation, 61.2 MB of additional disk space will be used.
Get:1 stable/main aarch64 clang aarch64 6.0.0-1 [12.9 MB]
Preparing to unpack .../clang_6.0.0-1_aarch64.deb ...

[localhost]{chibi-scheme}(1)$ make
cc -c -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3  -o main.o main.c
cc -c -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3  -fPIC -o gc.o gc.c

It's working! It's really working!

cc -c -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3  -fPIC -o sexp.o sexp.c
cc -c -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3  -fPIC -o bignum.o bignum.c
cc -c -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3  -fPIC -o gc_heap.o gc_heap.c
cc -c -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3  -fPIC -o opcodes.o opcodes.c
cc -c -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3  -fPIC -o vm.o vm.c
cc -c -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3  -fPIC -o eval.o eval.c
cc -c -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3  -fPIC -o simplify.o simplify.c
cc -fPIC -shared -Wl,-soname, -o gc.o sexp.o bignum.o gc_heap.o opcodes.o vm.o eval.o simplify.o    -ldl -lm
ln -sf
ln: Operation not permitted
make: *** [Makefile:162:] Error 1

And it broke. Ugh.  After a much debugging I realized that my issue
was permission based. Apparently Android doesn't allow you to perform
certain operations on your SD card. I moved the source tree to internal 
storage and tried again.

[localhost]{chibi-scheme}(1)$ cd ..
[localhost]{src}(1)$ mv chibi-scheme ~/

[localhost]{src}(1)$ cd

[localhost]{~}(1)$ cd chibi-scheme
[localhost]{chibi-scheme}(1)$ make
ln -sf
ln -sf

It's working again. Whooo!

cc -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3   -o chibi-scheme main.o -L. -lchibi-scheme
LD_LIBRARY_PATH=".:/data/data/com.termux/files/usr/lib" DYLD_LIBRARY_PATH=".:" CHIBI_MODULE_PATH=lib ./chibi-scheme -q tools/chibi-ffi lib/chibi/filesystem.stub
cc -fPIC -shared -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3   -o lib/chibi/ lib/chibi/filesystem.c -L.  -lchibi-scheme
cc -fPIC -shared -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3   -o lib/chibi/ lib/chibi/weak.c -L.  -lchibi-scheme
cc -fPIC -shared -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3   -o lib/chibi/ lib/chibi/heap-stats.c -L.  -lchibi-scheme
cc -fPIC -shared -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3   -o lib/chibi/ lib/chibi/disasm.c -L.  -lchibi-scheme
cc -fPIC -shared -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3   -o lib/chibi/ lib/chibi/ast.c  -L. -lchibi-scheme
LD_LIBRARY_PATH=".:/data/data/com.termux/files/usr/lib" DYLD_LIBRARY_PATH=".:" CHIBI_MODULE_PATH=lib ./chibi-scheme -q tools/chibi-ffi lib/chibi/emscripten.stub
cc -fPIC -shared -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3   -o lib/chibi/ lib/chibi/emscripten.c -L.  -lchibi-scheme
LD_LIBRARY_PATH=".:/data/data/com.termux/files/usr/lib" DYLD_LIBRARY_PATH=".:" CHIBI_MODULE_PATH=lib ./chibi-scheme -q tools/chibi-ffi lib/chibi/process.stub
cc -fPIC -shared -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3   -o lib/chibi/ lib/chibi/process.c -L.  -lchibi-scheme
LD_LIBRARY_PATH=".:/data/data/com.termux/files/usr/lib" DYLD_LIBRARY_PATH=".:" CHIBI_MODULE_PATH=lib ./chibi-scheme -q tools/chibi-ffi lib/chibi/time.stub
cc -fPIC -shared -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3   -o lib/chibi/ lib/chibi/time.c -L.  -lchibi-scheme
LD_LIBRARY_PATH=".:/data/data/com.termux/files/usr/lib" DYLD_LIBRARY_PATH=".:" CHIBI_MODULE_PATH=lib ./chibi-scheme -q tools/chibi-ffi lib/chibi/system.stub
cc -fPIC -shared -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3   -o lib/chibi/ lib/chibi/system.c -L.  -lchibi-scheme
LD_LIBRARY_PATH=".:/data/data/com.termux/files/usr/lib" DYLD_LIBRARY_PATH=".:" CHIBI_MODULE_PATH=lib ./chibi-scheme -q tools/chibi-ffi lib/chibi/stty.stub
cc -fPIC -shared -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3   -o lib/chibi/ lib/chibi/stty.c -L.  -lchibi-scheme
LD_LIBRARY_PATH=".:/data/data/com.termux/files/usr/lib" DYLD_LIBRARY_PATH=".:" CHIBI_MODULE_PATH=lib ./chibi-scheme -q tools/chibi-ffi lib/chibi/net.stub
cc -fPIC -shared -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3   -o lib/chibi/ lib/chibi/net.c -L.  -lchibi-scheme
LD_LIBRARY_PATH=".:/data/data/com.termux/files/usr/lib" DYLD_LIBRARY_PATH=".:" CHIBI_MODULE_PATH=lib ./chibi-scheme -q tools/chibi-ffi lib/chibi/io/io.stub
cc -fPIC -shared -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3   -o lib/chibi/io/ lib/chibi/io/io.c -L.  -lchibi-scheme
cc -fPIC -shared -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3   -o lib/chibi/optimize/ lib/chibi/optimize/rest.c -L.  -lchibi-scheme
cc -fPIC -shared -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3   -o lib/chibi/optimize/ lib/chibi/optimize/profile.c -L.  -lchibi-scheme
LD_LIBRARY_PATH=".:/data/data/com.termux/files/usr/lib" DYLD_LIBRARY_PATH=".:" CHIBI_MODULE_PATH=lib ./chibi-scheme -q tools/chibi-ffi lib/chibi/crypto/crypto.stub
cc -fPIC -shared -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3   -o lib/chibi/crypto/ lib/chibi/crypto/crypto.c -L.  -lchibi-scheme
cc -fPIC -shared -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3   -o lib/srfi/27/ lib/srfi/27/rand.c -L.  -lchibi-scheme
cc -fPIC -shared -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3   -o lib/srfi/151/ lib/srfi/151/bit.c -L.  -lchibi-scheme
cc -fPIC -shared -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3   -o lib/srfi/39/ lib/srfi/39/param.c -L.  -lchibi-scheme
cc -fPIC -shared -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3   -o lib/srfi/69/ lib/srfi/69/hash.c -L.  -lchibi-scheme
cc -fPIC -shared -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3   -o lib/srfi/95/ lib/srfi/95/qsort.c -L.  -lchibi-scheme
cc -fPIC -shared -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3   -o lib/srfi/98/ lib/srfi/98/env.c -L.  -lchibi-scheme
LD_LIBRARY_PATH=".:/data/data/com.termux/files/usr/lib" DYLD_LIBRARY_PATH=".:" CHIBI_MODULE_PATH=lib ./chibi-scheme -q tools/chibi-ffi lib/srfi/144/math.stub
cc -fPIC -shared -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3   -o lib/srfi/144/ lib/srfi/144/math.c -L.  -lchibi-scheme
cc -fPIC -shared -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3   -o lib/scheme/ lib/scheme/time.c -L.  -lchibi-scheme
cc -fPIC -shared -DSEXP_USE_INTTYPES -Iinclude  -Wall -g -g3 -O3   -o lib/srfi/18/ lib/srfi/18/threads.c -L.  -lchibi-scheme
echo "# pkg-config" > chibi-scheme.pc
echo "prefix=/data/data/com.termux/files/usr" >> chibi-scheme.pc
echo "exec_prefix=\${prefix}" >> chibi-scheme.pc
echo "libdir=/data/data/com.termux/files/usr/lib" >> chibi-scheme.pc
echo "includedir=\${prefix}/include" >> chibi-scheme.pc
echo "version=0.8.0" >> chibi-scheme.pc
echo "" >> chibi-scheme.pc
cat >> chibi-scheme.pc
find lib/chibi/ -name \*.sld | \
 LD_LIBRARY_PATH=".:/data/data/com.termux/files/usr/lib" DYLD_LIBRARY_PATH=".:" CHIBI_MODULE_PATH=lib ./chibi-scheme tools/generate-install-meta.scm 0.8.0 > lib/.chibi.meta
find lib/srfi/ -name \*.sld | \
 LD_LIBRARY_PATH=".:/data/data/com.termux/files/usr/lib" DYLD_LIBRARY_PATH=".:" CHIBI_MODULE_PATH=lib ./chibi-scheme tools/generate-install-meta.scm 0.8.0 > lib/.srfi.meta
find lib/scheme/ -name \*.sld | \
 LD_LIBRARY_PATH=".:/data/data/com.termux/files/usr/lib" DYLD_LIBRARY_PATH=".:" CHIBI_MODULE_PATH=lib ./chibi-scheme tools/generate-install-meta.scm 0.8.0 > lib/.scheme.meta

Amazing - it built without issue. How about installing, will that work?

[localhost]{chibi-scheme}(1)$ make install
install -d /data/data/com.termux/files/usr/bin
install -m0755 chibi-scheme /data/data/com.termux/files/usr/bin/
install -m0755 tools/chibi-ffi /data/data/com.termux/files/usr/bin/
install -m0755 tools/chibi-doc /data/data/com.termux/files/usr/bin/
install -m0755 tools/snow-chibi /data/data/com.termux/files/usr/bin/
install -m0755 tools/snow-chibi.scm /data/data/com.termux/files/usr/bin/

many boring 'install' lines removed. You're welcome.

if type ldconfig >/dev/null 2>/dev/null; then ldconfig; fi
echo "Generating images"
Generating images
cd / && LD_LIBRARY_PATH="/data/data/com.termux/files/usr/lib:/data/data/com.termux/files/usr/lib" DYLD_LIBRARY_PATH="/data/data/com.termux/files/usr/lib:" /data/data/com.termux/files/usr/bin/chibi-scheme -d /data/data/com.termux/files/usr/share/chibi/chibi.img
cd / && LD_LIBRARY_PATH="/data/data/com.termux/files/usr/lib:/data/data/com.termux/files/usr/lib" DYLD_LIBRARY_PATH="/data/data/com.termux/files/usr/lib:" /data/data/com.termux/files/usr/bin/chibi-scheme -d /data/data/com.termux/files/usr/share/chibi/red.img
cd / && LD_LIBRARY_PATH="/data/data/com.termux/files/usr/lib:/data/data/com.termux/files/usr/lib" DYLD_LIBRARY_PATH="/data/data/com.termux/files/usr/lib:" /data/data/com.termux/files/usr/bin/chibi-scheme -mchibi.snow.commands -mchibi.snow.interface -mchibi.snow.package -mchibi.snow.utils -d /data/data/com.termux/files/usr/share/chibi/snow.img

Holy smokes, it's done. Let's see if it really worked.

[localhost]{chibi-scheme}(1)$ cd

[localhost]{~}(1)$ chibi-scheme
> (+ 1 2 3)

We have scheme! Let's try something a bit more advanced

> (import (srfi 27) (srfi 1))
> (random-integer 100)

The R7RS library functionality is working and 
srfi's are installed by default. We have success!

And here are some screenshots to show this really was all executed on my Galaxy S9 Plus:

Monday, April 23, 2018

A Little Scheme Setup and Development on the Galaxy S9 Plus

Today's goal was to setup a Scheme programming environment on my new Galaxy S9 Plus phone. I planned to use the same tools I'd used on my LG G6:

  • Termux - provides a Unixy environment, doesn't require root and is just plain awesome
  • tinyscheme - provides a lightweight, yet powerful, scheme environment. For bonus points, it's trivial to install within Termux. Just type: pkg install tinyscheme
  • emacs, vim, git - these tools run well under Termux and are installed using the pkg install command. I use the same emacs config on my phone as I do my desktop.
  • External Keyboard Helper - remaps keys on my hardware keyboard. Specifically, I use it to map Caps Lock to Escape. I've found that this keybinding is essential to making bash, emacs and vim work in a sane way.

With the tools installed, I was able to open up a new .scm file in emacs, type C-u M-x run-scheme tinyscheme and I was off and running with an interactive Scheme development environment.

While the tools installed without issue, I couldn't consider the environment setup until I wrote some code. So off I went to to find an interesting exercise to tackle.

Sticking with the theme of rendering text in unique ways, I decided to complete the Seven-Segment Devices exercise. A Seven-Segment Device is a primitive display that renders numbers and text using seven different lit up bars. Think old school calculators. Here are the seven different segments you have to work with:

|     |
3     4
|     |
|     |
5     6
|     |

Below you'll find my solution that turns any integer into text that's rendered using this seven-segment style. The exercise called for storing the data as a bit-array. I took an alternative approach, and mapped every digit in the number to a list of enabled segments. For example, 0 maps to (2 4 6 0 5 3). This is more verbose than packing the data into bits, but I was more interested in rendering the text than storing a compact representation of it.

Here are some screenshots of the code running, and the code itself is below. It felt good to be coding on my new phone, and it sure is remarkable how well all these tools Just Work.


(define (show . args)
  (display args)

(define (range lower upper)
  (if (< lower upper)
    (cons lower (range (+ 1 lower) upper))

(define *digit-width* 10)
(define *digit-height* 11)
(define *digit-sep* 4)

(define *digit-map*
  '((0 . (2 4 6 0 5 3))
    (1 . (4 6))
    (2 . (2 4 1 5 0))
    (3 . (2 4 1 6 0))
    (4 . (3 1 4 6))
    (5 . (2 3 1 6 0))
    (6 . (3 5 0 6 1))
    (7 . (2 4 6))
    (8 . (0 1 2 3 4 5 6))
    (9 . (2 4 1 3 6))))

(define (integer->bars val)
  (if (= val 0)
    (cdr (assoc 0 *digit-map*))
    (let loop ((val val) (bars '()))
      (cond ((<= val 0) 
             (let ((d (modulo val 10)))
                (inexact->exact (floor (/ val 10)))
                (cons (cdr (assoc d *digit-map*)) bars))))))))

(define (draw-sep)
  (for-each (lambda (i)
              (display " "))
            (range 0 *digit-sep*)))

(define (draw first mid last)
  (display first)
  (for-each (lambda (i)
              (display mid))
            (range 0 (- *digit-width* 2)))
  (display last))

(define (draw-row digits row)
  (define mid (/ (- *digit-height* 1) 2))
  (define top? (= row 0))
  (define bottom? (= row (- *digit-height* 1)))
  (define mid? (= row mid))
  (define upper? (and (not top?) (< row mid)))
  (define lower? (and (not bottom?) (> row mid)))
  (for-each (lambda (bars)
              (define (has? x) (member x bars))
              (cond ((and top? (has? 2))
                     (draw "+" "-" "+"))
                    ((and bottom? (has? 0))
                     (draw "+" "-" "+"))
                    ((and mid? (has? 1))
                     (draw "+" "-" "+"))
                    ((and upper? (has? 3) (has? 4))
                     (draw "|" " " "|"))
                    ((and upper? (has? 3))
                     (draw "|" " " " "))
                    ((and upper? (has? 4))
                     (draw " " " " "|"))
                    ((and lower? (has? 5) (has? 6))
                     (draw "|"   " "   "|"))
                    ((and lower? (has? 5))
                     (draw "|" " " " "))
                    ((and lower? (has? 6))
                     (draw " " " " "|"))
                     (draw " " " " " ")))

(define (draw-integer x)
  (define s (integer->bars x))
  (for-each (lambda (row)
              (draw-row s row))
            (range 0 *digit-height*))

Friday, April 20, 2018

A Better Bixby Button

I recently traded my LG G6 for a Galaxy S9 Plus. I didn't do heaps of research before the switch because: (a) the device gets high marks in reviews, and (b) I owned and enjoyed a Samsung Galaxy Note 5 for years.

So it was a bit of a surprise to find the Galaxy S9 Plus comes with a dedicated Bixby Button. That is, a button who's only purpose is to launch a virtual assistant I'd never heard of.

With in a few minutes of using the phone I'd turned off the button, as the last thing I needed was to be nagged by a program I knew nothing about.

But surely Samsung offered a way to re-purpose the button. Nope.

By default you have two choices: a button that launches Bixby or a button that does nothing.

Fortunately, this is a common issue and there are apps on the Play store to address it. The app I ended up going with was bxActions.

It took a few iterations to get fully setup. At first, I had the Bixby button disabled so bxActions was never invoked. Then I re-enabled Bixby and the button started working again. However, now pressing the button launched Bixby followed by my preferred action. I suppose that worked, but showing Bixby first was clunky. I then purchased the Pro version of bxActions and went through the full setup. When I was done, I was able to use the button and have Bixby disabled.

Now that I had a new hardware button I had to decided what to do with it. Obviously, I was going to hook it into Tasker. But Tasker doing what?

For now, I'm using this simple Task:

The conditionals in this Task make the button do the Right Thing depending on the state of the phone.

I use AutoNotification to test to see if Run Keeper is recording a track. If it is, then pressing the Bixby button launches Run Keeper. This gives me one button access to my running app, which is handy when I need to refer to the app while in motion.

During the day, when I'm working from home, I find that I use quite a few timers (mostly to know when to eat again!). Therefore, if Run Keeper isn't running, but I'm connected to my local WiFi access point, pressing the Bixby button launches my timer app.

And finally, if none of the conditions are met, the Bixby button captures one minute of audio. The S9 provides a quick way to launch the camera and capture photos, I've now added a quick way to do something similar with audio.

The Bixby button is a great example of why it's worth letting a new phone burn in before you render judgement. For the first few days of S9 ownership I was quite certain the Bixby button was a terrible design flaw. And now I see it as a hacker friendly addition to the device. But c'mon Samsung, you really think it's OK to have a hardware button exclusively tied to one app? There's not a programmer alive who will tell you that's a good idea.

Thursday, April 19, 2018

Flight Audio from Southwest 1380 - Listen and be in Awe

The Air Traffic Control audio from Southwest 1380's emergency landing is nothing short of remarkable. As has been widely reported, the pilot and co-pilot remained incredibly calm and collected, which this audio shows. But it also shows just how well the whole system worked.

Planes needed to be re-routed to make way for SW 1380, which meant that Air Traffic control and quite a few pilots had to be quick on their toes. It also seems to me that the whole protocol of Air Traffic control had be flipped. In a moment, it went from the tower directing a pilot, to the pilot telling the tower what she wanted. All this happened seamlessly, as though this sort of thing happens all the time.

I love how at 1 minute, 27 seconds in the pilot, who is about to be switched to a new frequency, still manages to sign off with a Good Day. Amazing.

If you want to study resilient, adaptive and fault tolerant systems, it seems like Air Traffic control is good place to start.

Wednesday, April 18, 2018

The Fun, Yet Impossible, Route at San Lorenzo Canyon

Here's a tip: if you see a route on All Trails that looks easy, yet none of the reviewers managed to complete it, assume you're going to fail too. That's kind of, but not quite, the mindset I approached San Lorenzo Canyon in New Mexico with. It's a 1.5 mile loop that none of the reviewers actually found. But still, how dicey could a short loop be?

I was pleased to see signage leading us towards San Lorenzo Canyon from the highway, and while the roads weren't great, our sedan had no problem traversing them. We drove until we reached what Google Maps considers the start of the canyon and parked the car. From there, we started hoofing it down the main canyon. A few folks in vehicles passed us and I began thinking I'd picked a location that was more car than hiker friendly. Still, the canyon scenery around us was quite impressive. There were steep walls, impossibly teetering rocks and everywhere you looked interestingly shaped formations. If all we were going to do was stroll down a road for a bit, I was satisfied with that.

As we neared the end of the main canyon the All Trails route suggested we veer to the right. And sure enough, there was an obvious trail to follow. So we followed it and found ourselves off the main road and winding our way through more impressive scenery.

We scrambled our way through a few obstacles, but most of the trail was easy going. Eventually we found ourselves at a dead end. However, there was a prominent cairn marking what appeared to be a steep trail leading out of the canyon.

So up we went, and emerged from the canyon to an amazing view. Not only did we have the view, but I had serious confidence that we'd be able to follow the All Trails route. Once on top of the canyon, we traced the ridge making our way back towards the car.

And then we hit our first snag. The trail we were following stopped at a cliff wall. We may have been relatively close to our car, but without rapelling gear, it was completely out of reach.

We backtracked to the point where Shira had seen a cairn we'd walked by. And sure enough, upon closer inspection, the cairn pointed to a side trail. We took it, and sharply descended to the canyon floor again. From the distance I could see a clearly marked trail, and before I knew it, we were on high quality trail once again making our way to the car. And because we were on the canyon floor, we didn't have to worry about hitting a cliff. Once again I celebrated our good fortune, knowing that *this time* we'd have no problem following the All Trails route.

And then the canyon walls started to close in and we found ourselves at what seemed to be a dead end. There was a 6 foot or so drop, leading to a who-knows-how-far drop beyond that.

We backtracked looking for other trail options. By this point, our 1.5 mile hike had turned into a 4 mile adventure. Because the hike was so short, we'd started late in the day, and now sun-down didn't seem so far off. I was humbled by the surroundings. There was no guarantee that any of the canyons had obvious connections between them. I suddenly had visions of hiking out by flashlight, or worse, having to hunker down and spend the night huddled under a space blanket. We'd left a note on our car saying we were out for a day hike, but would anyone see it?

Ultimately, we back-tracked our route completely and made it back to the main road without incident.

So here's the deal with San Lorenzo Canyon: it's beautiful, and the hiking is awesome. But definitely approach it with care. We tracked our route with Backcountry Navigator, which I highly recommend. We also had plenty of water, snacks and standard emergency gear, which provided another safety net. While these may be standard hiking precautions, you might be tempted to skip them on a 1.5 mile loop. Don't. This landscape, while breathtaking, doesn't mess around. But with those precautions in place, San Lorenzo is an awesome place to explore.

Here's the route we took. Hopefully you can learn from our experience.

We didn't see a whole lot of wildlife while in the canyon, but we did see a crazy looking caterpillar. When we Googled its description to learn the identity, the first site we found was this one. Apparently, we'd found a Northern Giant Flag Moth. What's even more surprising is that photo on that page, documented 12 years ago, was also taken in San Lorenzo Canyon. How crazy is that?