Bringing back the J to Bifid Cipher

Donnerstag, 22. Oktober 2009

As a programming language enthusiast I often examine new (to me) languages, paradigms and concepts. Recently I played with J - sth. like APL with keyboard friendly syntax. If you are familiar with point-free functional programming, J isn't that complicated. More tedious is the used vocabulary (verbs, nouns, frets, agenda, gerunds...) for trivial things and the fact, that you can't use search engines - neither for "J" (-> "site:jsoftware.com"), nor for a function symbol (combination).

Mind bending are the many one-liners and examples in the documentation. And believe me, there isn't much more satisfaction as in unraveling the first lines of J code. ;)

As practical example on "hello world" level I used the Bifid Cipher:

pre=:(]->&9)@-&65@(a.&i.@toupper)
enc=:|:@((%&2@#,2:)$[)@(<.@%&5,5&|)
dec=:((2:,%&2@#)$[)@(<.@%&5,@,.5&|)
pst=:{&a.@(]-<&75)@(5&*@[/+66&+@]/)

encode=:pst\@enc\@pre
decode=:pst\@dec\@pre


Keep in mind, this is of course not the shortest or fastest or nicest implementation. It's just my first J program.

A short explanation for the curious

Read from right to left, @ is function composition and & binds a parameter.

pre - prepare our string

1. take a list of characters (the string), convert every character to uppercase (toupper) and look up (&i.) the characters in the ascii table (a.)
   a.&i.@toupper 'hello'

72 69 76 76 79

2. subtract 65 (ascii value of 'A') from every list item (-&65) (a 0..4 indexed cipher table to save some inc- (>:) and decrements (<:))
   -&65@(a.&i.@toupper) 'hello'

7 4 11 11 14

3. and decrement every item greater than 9 (]->&9) for the missing 'J'(!)
   (]->&9)@-&65@(a.&i.@toupper) 'hello'

7 4 10 10 13

The last part needs some explanation. (]->&9) is a so called "monadic fork". We have three functions f (] "identity"), h (- "minus") and g (>&9 "greater than 9") and the constellation as monadic fork (f h g) x executes as h(f(x),g(x)). The comparison gives a list of 0s and 1s, "NB." is line comment:
   NB. the identity (f):
   ] 7 4 11 11 14
7 4 11 11 14
   NB. greater than 9 (g):
   (>&9) 7 4 11 11 14
0 0 1 1 1
   NB. together with minus h(f(x),g(x)):
   (]->&9) 7 4 11 11 14
7 4 10 10 13


enc - encode

1. Now you are familiar with forks, you can interpret (<.@%&5,5&|) also, didn't you? On the left side we divide every element by 5 (%&5) and round (<. "floor") to the next smallest integer. On the other side we compute the remainder of the division with 5 (| "mod") and append (,) that list to our left result.
   (<.@%&5,5&|) 7 4 10 10 13
1 0 2 2 2 2 4 0 0 3

2. with ((%&2@#,2:)$[) we reshape ($) our list to nx2, n is the length (#) of the list divided by 2 (%&2), (2:) is the constant function, giving back 2
   ((%&2@#,2:)$]) 1 0 2 2 2 2 4 0 0 3
1 0
2 2
2 2
4 0
0 3

3. and transpose (|:) it for our final conversion in pst
   |:((%&2@#,2:)$]) 1 0 2 2 2 2 4 0 0 3
1 2 2 4 0
0 2 2 0 3


dec - decode

Decode is similar to encode.
1. div (<.@%&5) and mod (%&|) our list but merge (,@,.) the result
   NB. append from enc
   (<.@%&5,5&|) 5 12 12 20 3
1 2 2 4 0 0 2 2 0 3
   NB. merge from dec
   (<.@%&5,@,.5&|) 5 12 12 20 3
1 0 2 2 2 2 4 0 0 3

2. and again reshape the result to 2xn with n as half list length
   ((2:,%&2@#)$[) 1 0 2 2 2 2 4 0 0 3
1 0 2 2 2
2 4 0 0 3


pst - result to string

1. we have to rebuild our string, so we take the first row ([/) and multiply with 5 (5&*) and add (+) the result to our second (]/) row, to which we added 66 (66&+)
   NB. our table
   t=:((2:,%&2@#)$[)@(<.@%&5,@,.5&|) 5 12 12 20 3
   t
1 0 2 2 2
2 4 0 0 3
   NB. first row
   [/ t
1 0 2 2 2
   NB. second row
   ]/ t
2 4 0 0 3
   NB. multiply and add twice
   (5&*@[/ + 66&+@]/) t
73 70 76 76 79

2. we have to decrement the values below 75 ('K') (]-<&75)
   (]-<&75)(5&*@[/ + 66&+@]/) t
72 69 76 76 79

3. and lookup our values in the ascii table ({&a.)
   NB. decoding result
   ({&a.) 72 69 76 76 79
HELLO
   NB. encoding result
   ({&a.) 70 78 78 86 68
FNNVD


Now we can compose and use our encode and decode functions:
   encode=:pst\@enc\@pre
   decode=:pst\@dec\@pre
   encode 'PROGRAMMINGPRAXISWITHJ'
OMQNHHQWUIGWRFGSKFOMTP
   encode 'PROGRAMMINGPRAXIS'
OMQNHHQWUIGBIMWCS
   decode 'OMQNHHQWUIGWRFGSKFOMTP'
PROGRAMMINGPRAXISWITHK
   decode 'OMQNHHQWUIGBIMWCS'
PROGRAMMINGPRAXIS


Another example

Not enough? I see you are as fascinated as I am. The next example ist from j602/system/main/convert.ijs
av=: 3 : '({&a.)`(a.&i.) @. (2&=@(3!:0)) y'

3 : '... y' defines a verb, imagine it as a function with one argument y. (()`()@.()) is:
m@.n is a verb defined by the gerund m with an agenda specified by n ; that is, the verb represented by the train selected from m by the indices n . If n is boxed, the train is parenthesized accordingly. The case m@.v uses the result of the verb v to perform the selection.

Pure beauty! Here (2&=@(3!:0)) acts as a case or if expression. It returns a value which becomes the index of the left "list of functions" (take it with salt). 3!:0 returns the "type" (more salt!) of a parameter. If it is a character it returns 2, the following comparison (2&=) returns 1 and (a.&i) evaluates to the index of this character in the ascii table. Otherwise we assume it is a number and return the corresponding character.
   (2&=@(3!:0)) 'A'
1
   (2&=@(3!:0)) 97
0
   ({&a.) 99
a
   (a.&i.) 'a'
97
   ({&a.)`(a.&i.) @. (2&=@(3!:0)) 'abc'
97 98 99
   ({&a.)`(a.&i.) @. (2&=@(3!:0)) 97 98 99
abc


Trackbacks


Trackback-URL für diesen Eintrag
    Keine Trackbacks

Kommentare


    Noch keine Kommentare

Kommentar schreiben


Um maschinelle und automatische Übertragung von Spamkommentaren zu verhindern, bitte die Zeichenfolge im dargestellten Bild in der Eingabemaske eintragen. Nur wenn die Zeichenfolge richtig eingegeben wurde, kann der Kommentar angenommen werden. Bitte beachten Sie, dass Ihr Browser Cookies unterstützen muss, um dieses Verfahren anzuwenden.
CAPTCHA

  Kommentare werden erst nach redaktioneller Prüfung freigeschaltet!