sn.printf.net

2012-01-13

2012 01 13

Pierre Gauthier

Unless you're following NHL hockey, you'll want to skip on this issue.

If you follow NHL hockey, you likely know by now that Cammalleri was pulled between the second and third period of the Montreal Vs. Boston game yesterday, to be traded for René Bourque in Calgary (and a few other players and draft picks). I woke up with this idea this morning, with Pierre Gauthier (Montreal's GM) in mind:

A recreation of the 'boo-urns' scene from the Simpsons with 'boo-rque'

2012-01-13 22:30

2012-01-10

2012 01 10

Obfuscation

Late Promises

For a long while now, I've showed various people what I consider to be one of the most twisted bits of obfuscated Erlang to have been written. I've had it for a bit over a year, and showed it to a bunch of Erlang people back at the Erlang User Conference in Sweden back in 2011.

I then promised to write a blog post about it and explaining how I wrote it, which I promptly deferred to later in one brave act of procrastination. I decided to finally put the whole thing together, between the writing of a new Learn You Some Erlang chapter, the formatting of an older one for a publisher, and work.

The code you'll see below is something I wrote when I was still working for Erlang Solutions Ltd. (I now work for a place called BLOOM Digital Platforms, still in Erlang). When working for ESL, I would spend quite some time traveling around the world to go teach courses about Erlang to different people and businesses. This did include its share of long airport waiting times, long pauses in hotels without WiFi access, and bus rides. That's where I took the time and fun challenge to write the messiest Erlang code possible, using features mostly unique to the language.

What does it look like?

I'll be quick—it looks like this (alternatively, a gist!):

                                          %%%%%%%                      %%%%%%
-module (        obfuscation            %%       %%%               %%%       %%
)                                           %%%%%%\ %             % /%%%%%%
. -compile(                                 %%   %%       ' '       %%   %%
[export_all  ] ) . -define(swap          %  %%%%%%%        |        %%%%%%%  %
(A,                                       %%%              .              %%%
B), B                                                      %
A                                                       %%   %%
). -define(M,                                                                                    ?MODULE). -define(v, 
o).            -define(p,().                           %%%%%%%%%%
                             -define(q,           %%%%%  ,,__,,  %%%%
)).                                               %%%    |    |   %%%
     -define(s                                    %%     \ _ / %   %%
,,).               -define('.   ',             {           %    %
                  ).                                      %%%    %
                                -define(' .  ',        }     ). %%%%%%%%%%%%%%%%
-define(      ' \''                                             % LL  OOO LLL  %
            ,                                                   % L L  O  L    %
               integer).                                        % L L  O  LLL  %
                -define(':('                                    % L L  O  L    %
,                                                               % LL  OOO LLL  %
 atom                                                           %%%%%%%%%%%%%%%%
,
)
.                                         -define(
':)'                       ,       tuple,     ). -define('}',
[
).            -define('{'    ,

]                                                 )     .

%% According to many Haskellers, type signatures represent sufficient
%% documentation. I have thus provided the type signature to the main
%% function of this program, hoping its self-describing nature and the
%% types will be enough to enlighten the readers of this code as to its
%% purpose and inner workings.
%%                                               Best Regards,
%%                                               - Ferd.
-spec main() -> ''.
main?p  ?q  -> main?p atom ?q . main?p Bin ?q  ->
    '. _   '?p Bin ?q  ?s S= ?p ?p fun '! _/?  - '/1 ?q ?p ?p ?p fun '_ _/-  - '/1 
    ?q ?p ?p ?p fun '! _/?  - '/1 ?q ?p ?p ?p fun '_ _/-  _ '/1 ?q ?p ?p ?p fun
    '_ _/-  - '/1 ?q ?p ?p ?p fun '! _/?  - '/1 ?q ?p ?p ?p fun '_ _/-  _ '/1 ?q ?p ?p ?p
    fun '! _/-  - '/1 ?q ?p ?M:'_  ='?p ?M:'=  _'?p ?M:'_  ='?p ".Ub-ry rWby" ?q
    ?q  ?q  ?q ?q ?p ?p ?p fun '! _/-  - '/1 ?q ?p  ?'}' $\n,$\n ?'{'  ?q  ?q ?p ?M:'_  ='?p
    ?M:'=  _'?p "Ug-.y"?swap("r","r")"ebbyoE " ?q  ?q  ?q  ?q  ?q ?q ?p
     ?'}' $\n,$\n ?'{'  ?q  ?q ?q ?p ?p ?p fun '! _/-  - '/1 ?q ?p ?M:'=  _'?p ?M:'_  ='?p
    ". agrbu "?swap("s","g")" vtkh og ur" ?q  ?q  ?q  ?q ?p ?M:'_  ='?p
    ?M:'=  _'?p "Vq-tfr arnrzgn hn ul bg" ?q  ?q  ?q  ?q  ?q ?q ?p  ?'}' $\n,$\n ?'{' 
    ?q  ?q ?q ?p ?M:'=  _'?p ?M:'=  _'?p ?M:'=  _'?p "-yvf.,syLnarl " ?q  ?q
    ?q  ?q  ?q ?q ?p   ?'}' $\n,$\n ?'{'  ?q  ?q ?q ?p ?M:'=  _'?p ?M:'_  ='?p "**xppyv"
    ?q  ?q  ?q  ?q ?q ?p  ?'}' $\n ?'{'  ?q  ?s group_leader?p  ?q  !  ?'.   ' io_request, self?p
    ?q , '$',  ?'.   ' put_chars, unicode, S ?' .  '  ?' .  '  ?s ''.

'! _/?  - '?p A ?q  -> fun?p B ?q  -> ?p fun erlang:'++'/2 ?q ?p A,B ?q  end. '_ _/-  - '
?p A ?q  -> '! _/?  - '?p A ?q . '_ _/-  _ '?p A ?q  -> '! _/?  - '?p A ?q . '! _/-  - '?p B ?q
-> fun?p A ?q  -> ?p ?p ?p fun '! _/?  - '/1 ?q ?p A ?q  ?q ?q ?p B ?q  end.

%%%%%%%%%%%%%%%%%
%%%%%       %%%%%   %%%%%%%%%%%%%%%  %%    %%  %%%%%%%%   %%%%%%
%%%%%       %%%%%   %%%%%%%%%%%%%%%  %%    %%     %%     %%
%%%%%       %%%%%         % %        %% %% %%     %%      %%%%%
%%%%%       %%%%%         % %        %% %% %%     %%       %%%%
%               %         % %        %%    %%     %%          %%
%%%           %%%         % %        %%    %%  %%%%%%%%  %%%%%%
%%%%%       %%%%%
%%%%%%%   %%%%%%%           - WAY -
%%%%%%%% %%%%%%%%
%%%%%%%%%%%%%%%%%                                       [skip to the end for infos]





-define('             ,', ?'.   '  ?' \'' ,0,303 ?' .  '  ?s ?'.   ' cons,0 ?s
?'.   '  ?':)' 0, ?'}'  ?'.   '  ?':(' 0,var ?' .  ' , ?'.   '  ?' \'' ,0,303
?' .  ' ,    ?'.   '  ?':(' 0,'C' ?' .  '  ?'{'  ?' .  '  ?s          ?'.   '
nil,0 ?' .  '  ?' .  '  ?s  ?'.   ' cons,0 ?s ?'.   ' cons,0 ?s ?'.   '  ?':)'
 0 ?s  ?'}'  ?'.   '  ?':(' 0,op ?' .  '  ?s ?'.   '  ?' \'' ,0,303 ?' .  '  ?s
?'.   '  ?':(' 0,'andalso'
?' .  '  ?s ?'.   '  ?':)'
0 ?s  ?'}'  ?'.   '  ?':('
0,op ?' .  '  ?s   ?'.   '
  ?' \'' ,0,303 ?' .  '  ?s
?'.   '  ?':(' 0,'>=' ?' .  '  ?s
?'.   '  ?':)' 0, ?'}'  ?'.   '  ?':(' 0,var ?' .  '
, ?'.   '  ?' \'' ,0,303 ?' .  ' , ?'.   '  ?':(' 0,'C'
 ?' .  '  ?'{'  ?' .  '  ?s ?'.   '  ?':)' 0 ?s    ?'}'
?'.   '  ?':(' 0,char ?' .  ' , ?'.   '  ?' \'' ,0,303
?' .  ' , ?'.   '  ?' \'' ,0,65 ?' .  '  ?'{'  ?' .  '
  ?'{'  ?' .  '  ?s ?'.   '  ?':)' 0 ?s ?'}'  ?'.   '
  ?':(' 0,op ?' .  '  ?s
?'.   '  ?' \'' ,0, 303 
?' .  '  ?s ?'.   '  ?':('
 0,'=<' ?' .  '  ?s ?'.   '
  ?':)' 0,   ?'}'  ?'.   '
  ?':(' 0,var ?' .  '   ,
 ?'.   '  ?' \'' ,0,   303
?' .  ' , ?'.   '  ?':(' 0,'C' ?' .  '  ?'{'  ?' .  '  ?s ?'.   '  ?':)' 0 ?s
?'}'  ?'.   '  ?':(' 0,char ?' .  ' , ?'.   '  ?' \'' ,0,303   ?' .  '   ,
?'.   '  ?' \'' ,0,77 ?' .  '  ?'{'  ?' .  '  ?'{'  ?' .  '  ?'{'  ?' .  '  ?s
  ?'.   ' nil,0 ?' .  '  ?' .  '  ?s ?'.   ' cons,0    ?s   ?'.   ' cons,0 ?s
?'.   '  ?':)' 0 ?s  ?'}'  ?'.   '  ?':(' 0,op ?' .  '  ?s ?'.   '  ?' \'' ,0,


303 ?' .  '  ?s  ?'.   '  ?':(' 0,'andalso'  ?' .  '  ?s 
?'.   '  ?':)' 0 ?s  ?'}'  ?'.   '  ?':(' 0,op ?' .  '  ?s ?'.   '
?' \'' ,0,303 ?' .  '  ?s ?'.   '  ?':(' 0,'>=' ?' .  '  ?s ?'.   '  ?':)'
0, ?'}'  ?'.   '  ?':(' 0,var ?' .  ' , ?'.   '  ?' \'' ,0,303 ?' .  ' ,
?'.   '  ?':(' 0,'C'                            ?' .  '  ?'{'  ?' .  '
 ?s ?'.   '  ?':)' 0                              ?s ?'}'  ?'.   '  ?':(' 0,
char ?' .  ' , ?'.   '                           ?' \'' ,0,   303   ?' .  ' ,
?'.   '  ?' \'' ,0,97                            ?' .  '  ?'{'  ?' .  '  ?'{'
?' .  '  ?s   ?'.   '                          ?':)' 0 ?s ?'}'  ?'.   '
?':(' 0,op ?' .  '  ?s                   ?'.   '   ?' \'' , 0,  303
 ?' .  '  ?s  ?'.   '  ?':(' 0,'=<' ?' .  '  ?s
 ?'.   '  ?':)' 0, ?'}'  ?'.   '  ?':(' 0,var ?' .  '
 , ?'.   '  ?' \'' ,0              ,303 ?' .  ' , ?'.   '
 ?':(' 0,'C' ?' .  '                 ?'{'  ?' .  '  ?s ?'.   '
?':)' 0   ?s     ?'}'                 ?'.   '  ?':(' 0,    char
?' .  '   ,   ?'.   '                  ?' \'' ,0,303 ?' .  '   ,
 ?'.   '  ?' \'' , 0,                  109 ?' .  '  ?'{'  ?' .  '
?'{'  ?' .  '  ?'{'                     ?' .  '  ?s ?'.   ' nil,0
 ?' .  '  ?' .  '  ?s                   ?'.   ' nil,   0 ?' .  '
?' .  '  ?' .  '  ?s                    ?'.   ' cons,0 ?s ?'.   '
 ?':)'    0 ?s ?'}'                     ?'.   '  ?':(' 0,op ?' .  '
?s ?'.   '  ?' \'' ,0                   ,303 ?' .  '  ?s ?'.   ' ?':('      0,
'+' ?' .  ' ?s ?'.   '                  ?':)' 0, ?'}'  ?'.   '  ?':(' 0,  var
?' .  '   ,   ?'.   '                   ?' \'' ,0,303 ?' .  ' , ?'.   '  ?':('
 0,'C' ?' .  '  ?'{'  ?' .  '        ?s ?'.   '  ?':)' 0 ?s ?'}'  ?'.   '


?':(' 0, ?' \''  ?' .  ' , ?'.   '
  ?' \'' ,0,303 ?' .  ' ,
  ?'.   '  ?' \'' ,0,   13
  ?' .  '  ?'{'  ?' .  '
  ?'{'  ?' .  '  ?s ?'.   '
  nil,0 ?' .  '  ?' .  '
  ?'{'  ?' .  '  ?s ?'.   '
  cons,0 ?s ?'.   '  ?':)'
   0 ?s ?'}'  ?'.   '  ?':('
  0,clause ?' .  '  ?s ?'.   '
  ?' \'' ,0,304 ?' .  '  ?s
  ?'.   ' cons,0 ?s ?'.   '
  ?':)' 0   ,  ?'}'  ?'.   '
  ?':(' 0  ,  var ?' .  '   ,
  ?'.   '  ?' \'' ,0,  304
   ?' .  ' , ?'.   '  ?':(' 0
 ,'C' ?' .  '  ?'{'    ?' .  '
  ?s   ?'.   ' nil,0 ?' .  '
  ?' .  '  ?s ?'.   ' cons,0
  ?s ?'.   ' cons,0 ?s ?'.   '
  ?':)' 0   ?s   ?'}'  ?'.   '
  ?':(' 0,op ?' .  '  ?s ).
   -define('              ,',
  ?'.   '  ?':(' 0,'  ' ?' .  '
 ?s      ?'.   '  ?' \'' ,0,2 ?' .  '                          ?s
?'.   ' cons,0 ?s ?'.   '  ?':)' 0 ?s ?'}'                  ?'.   '
?':(' 0,clause ?' .  '  ?s ?'.   '  ?' \'' ,0,88 ?' .  '  ?s ?'.   '
 cons,0 ?s ?'.   '  ?':)' 0, ?'}'  ?'.   '   ?':(' 0  ,   nil ?' .  '
 , ?'.   '  ?' \'' ,0,88 ?' .  '  ?'{'  ?' .  '  ?s ?'.   ' cons,0 ?s
?'.   '  ?':)' 0, ?'}'  ?'.   '  ?':(' 0,var ?' .  ' , ?'.   '  ?' \''
,0,88 ?' .  ' , ?'.   '  ?':(' 0,'Acc' ?' .  '  ?'{'  ?' .  '  ?s ?'.   '


                             nil,0 ?' .  '
                     ?' .  '  ?' .  '  ?s  ?'.   '
                 nil,0 ?' .  '  ?s  ?'.   ' cons,0 ?s
           ?'.   '  ?':)' 0, ?'}'  ?'.   '  ?':(' 0  , var
         ?' .  ' , ?'.   '  ?' \''    , 0,88 ?' .  ' , ?'.   '
       ?':(' 0,'Acc' ?' .  ' ?'{'      ?' .  '  ?s  ?'.   ' nil,
     0 ?' .  '  ?' .  '  ?'{'            ?' .  '  ?s ?'.   ' cons,0 ?s
   ?'.   '  ?':)' 0 ?s ?'}'                ?'.   '  ?':(' 0,clause ?' .  '
  ?s ?'.   '  ?' \'' ,0,89                  ?' .  '  ?s ?'.   ' cons,0 ?s
  ?'.   '  ?':)' 0 ?s ?'}'                   ?'.   '  ?':(' 0,cons ?' .  '
  ?s ?'.   '  ?' \'' ,0,89                   ?' .  '  ?s  ?'.   '  ?':)' 0,
  ?'}'  ?'.   '  ?':(' 0 ,                    var ?' .  ' , ?'.   '  ?' \''
  ,0,89 ?' .  ' , ?'.   '                    ?':(' 0,'X' ?' .  '  ?'{'  
  ?' .  '  ?s ?'.   '  ?':)' 0, ?'}'  ?'.   '  ?':(' 0,nil ?' .  ' , ?'.   '
  ?' \'' ,0,89 ?' .  '  ?'{'  ?' .  '  ?'{'  ?' .  '  ?s  ?'.   ' cons,0 ?s
  ?'.   '  ?':)' 0 ?s  ?'}'  ?'.   '  ?':(' 0,tuple ?' .  '  ?s    ?'.   '
  ?' \'' ,0,89 ?' .  '  ?s ?'.   ' cons,0 ?s  ?'.   '  ?':)'    0,    ?'}'
  ?'.   '  ?':(' 0, var                      ?' .  ' ,    ?'.   '  ?' \'' ,
  0,89 ?' .  ' , ?'.   '                     ?':(' 0,'As' ?' .  '   ?'{'  
  ?' .  '  ?s ?'.   ' cons                   ,0 ?s ?'.   '  ?':)' 0, ?'}'
  ?'.   '  ?':(' 0  , var                    ?' .  ' , ?'.   '  ?' \'' ,0,
  89 ?' .  '     , ?'.   '                   ?':(' 0,'Bs' ?' .  '    ?'{'
  ?' .  '  ?s ?'.   ' nil,                   0 ?' .  '  ?' .  '  ?' .  '
  ?'{'  ?' .  '  ?s ?'.   '                  nil,0 ?' .  '  ?' .  '  ?' .  '
  ?s ?'.   ' nil,0 ?' .  '                   ?s ?'.   ' cons,0 ?s ?'.   '
   ?':)' 0 ?s ?'}'  ?'.   '                    ?':(' 0,tuple ?' .  '  ?s 
  ?'.   '  ?' \'' ,  0 , 89                  ?' .  '  ?s ?'.   '   cons,0        ?s
  ?'.   '  ?':)' 0,    ?'}'                  ?'.   '  ?':(' 0,var ?' .  ' , ?'.   '
  ?' \'' ,0,89 ?' .  '   ,                   ?'.   '  ?':(' 0,'As' ?' .  '  ?'{'
  ?' .  '  ?s ?'.   ' cons,                  0 ?s  ?'.   '  ?':)' 0 ?s  ?'}'
 


?'.   '  ?':(' 0,cons ?' .  '                          ?s ?'.   '  ?' \'' ,0,89
?' .  '  ?s ?'.   '  ?':)' 0, ?'}'                     ?'.   '  ?':(' 0  ,  var
 ?' .  ' , ?'.   '  ?' \''  , 0,  89                   ?' .  ' , ?'.   '  ?':('
0,'X' ?' .  '  ?'{'  ?' .  '  ?s ?'.   '               ?':)' 0, ?'}'  ?'.   '
?':('   0,  var      ?' .  ' ,   ?'.   '               ?' \'' ,0,89 ?' .  ' , 
?'.   '  ?':(' 0      ,'Bs' ?' .  '  ?'{'              ?' .  '  ?'{'  ?' .  ' ?s
?'.   '  nil , 0        ?' .  '    ?' .  '             ?' .  '  ?'{'  ?' .  '  ?s
?'.   ' nil ,  0         ?' .  '    ?' .  '            ?'{'  ?' .  '  ?s ?'.   '
cons,0 ?s ?'.   '         ?':)' 0   ?s   ?'}'          ?'.   '  ?':(' 0,clause
?' .  ' ?s ?'.   '         ?' \'' ,0,90 ?' .  '        ?s ?'.   ' cons,0 ?s
?'.   '  ?':)' 0            ?s     ?'}'  ?'.   '       ?':(' 0,cons ?' .  '  ?s
?'.   '  ?' \'' ,            0,90 ?' .  '  ?s          ?'.   '  ?':)' 0, ?'}'
?'.   '  ?':(' 0,            var ?' .  ' , ?'.   '     ?' \'' ,0,90 ?' .  '  ,
?'.   '  ?':(' 0,            'A'  ?' .  '   ?'{'       ?' .  '  ?s ?'.   '  ?':)'
 0 ?s ?'}'  ?'.   '            ?':(' 0,cons ?' .  '    ?s ?'.   '  ?' \'' ,0,90
?' .  '  ?s ?'.   '             ?':)' 0, ?'}'  ?'.   ' ?':(' 0,var ?' .  '   ,
 ?'.   '  ?' \'' ,               0,90 ?' .  ' , ?'.   '  ?':(' 0,'B'   ?' .  '
 ?'{'  ?' .  '  ?s                ? '.   '  ?':)' 0, ?'}'  ?'.   '  ?':(' 0,var
?' .  ' ,   ?'.   '                ?' \'' ,0,90 ?' .  ' , ?'.   '  ?':('  0,'T'
?' .  '    ?'{'                     ?' .  '  ?'{'  ?' .  '  ?'{'  ?' .  '  ?s).
-define(code, ?'.   '                cons,0 ?s ?'.   '  ?':)' 0 ?s ?'}'  ?'.   '
?':(' 0, function                     ?' .  '  ?s  ?'.   '  ?' \'' ,0,82 ?' .  '
?s ?'.   '  ?':(' 0,                    '_  =' ?' .  '  ?s ?'.   '  ?' \'' ,0,1
?' .  '  ?s ?'.   '                        cons,0 ?s ?'.   '  ?':)' 0 ?s  ?'}'



                    ?'.   '  ?':(' 0,clause ?' .  '  ?s
       ?'.   '  ?' \'' ,0,82 ?' .  '  ?s ?'.   ' cons,0, ?'.   '
     ?':)' 0, ?'}'  ?'.   '  ?':('           0, var ?' .  ' , ?'.   '
   ?' \'' ,0,82 ?' .  ' ,                     ?'.   '  ?':(' 0,'L' ?' .  '
  ?'{'  ?' .  ' , ?'.   '                      nil,0 ?' .  '  ?' .  '  ?s
?'.   ' nil,0 ?' .  '  ?s                     ?'.   ' cons,0 ?s
?'.   '  ?':)'   0   ?s
?'}'  ?'.   '  ?':(' 0,call
 ?' .  '  ?s ?'.   '  ?' \''
,0,82 ?' .  '  ?s ?'.   '
?':)'   0   , ?'}'  ?'.   '           ?':(' 0,atom ?' .  ' , ?'.   '  ?' \''
,0,82 ?' .  ',?'.   ' ?':('           0,' --   _' ?' .  '  ?'{'  ?' .  '  ?s
?'.   ' cons,0 ?s ?'.   '                ?':)' 0, ?'}'  ?'.   '  ?':(' 0,var
?' .  ' , ?'.   '  ?' \'' ,                  0,82 ?' .  ' , ?'.   '  ?':('
0,'L' ?' .  '  ?'{'  ?' .  '                  ?s ?'.   ' nil, 0  ?' .  '
?' .  '  ?'{'  ?' .  '  ?s                     ?'.   ' nil,0 ?' .  '
?' .  '  ?'{'  ?' .  '  ?s                     ?'.   ' nil,0 ?' .  '
?' .  '  ?'{'  ?' .  '  ?s                     ?'.   ' cons,0 ?s
?'.   '  ?':)' 0   ?s  ?'}'                    ?'.   '  ?':(' 0 ,
function ?' .  '  ?s ?'.   '                  ?' \'' ,0,84 ?' .  '
?s ?'.   '  ?':(' 0,'=  _' ?' .  '         ?s ?'.   '  ?' \'' ,0,1
 ?' .  '  ?s ?'.   ' cons,0 ?s ?'.   '  ?':)' 0 ?s  ?'}'  ?'.   '
  ?':(' 0,clause ?' .  '  ?s ?'.   '  ?' \'' ,0,84 ?' .  '
    ?s ?'.   ' cons,0, ?'.   '  ?':)'   0,  ?'}'  ?'.   '
      ?':(' 0,var ?' .  ' , ?'.   '  ?' \'' ,0,84 ?' .  '
             , ?'.   '  ?':(' 0  ,    'L' ?' .  ' ?'{'





%%%%%% AND NOW FOR SOMETHING COMPLETELY DIFFERENT %%%%
%%%%%% AND NOW FOR SOMETHING COMPLETELY  DIFFERENT %%%%
%%%%%%%% AND NOW FOR SOMETHING COMPLETELY DIFFERENT %%%%
%%%% AND NOW FOR SOMETHING COMPLETELY  DIFFERENT %%%%
%%%%%% AND NOW FOR SOMETHING COMPLETELY DIFFERENT %%%%
%%%%% AND NOW FOR SOMETHING COMPLETELY DIFFERENT %%%%
%%%%%% AND NOW  FOR SOMETHING COMPLETELY DIFFERENT %%%%
%%%%%% AND NOW FOR SOMETHING COMPLETELY DIFFERENT %%%%
%%%%%%% AND NOW FOR SOMETHING COMPLETELY DIFFERENT %%%%


%% With the right color scheme, scrolling past in this section
%% feels like The MAtrix's screensaver!!!!!


?' .  ' , ?'.   ' nil,$V ?' .  '  ?' .  '  ?s ?'.   ' nil,$I ?' .  '  ?s ?'.   '
cons,$S ?s ?'.   '  ?':)' $I ?s ?'}'  ?'.   '  ?':(' $T,match ?' .  '  ?s  ?'.   '
?' \'' ,$ ,$H ?' .  '  ?s ?'.   '  ?':)' $T ?s ?'}'  ?'.   '  ?':(' $T,tuple
 ?' .  '  ?s     ?'.   '  ?' \'' ,$P,$: ?' .  '  ?s ?'.   ' cons,$/ ?s ?'.   '
?':)' $/, ?'}'  ?'.   '  ?':(' $L,var ?' .  ' , ?'.   '  ?' \'' ,$E,$A ?' .  ' ,
?'.   '  ?':(' $R,'A' ?' .  '  ?'{'  ?' .  '  ?s ?'.   ' cons,$N ?s ?'.   '  ?':)'
$Y, ?'}'  ?'.   '  ?':(' $O,var ?' .  ' , ?'.   '  ?' \'' ,$U,$S ?' .  ' , ?'.   '
?':(' $O,'B' ?' .  '  ?'{'  ?' .  '  ?s ?'.   ' nil,$M ?' .  '  ?' .  '  ?' .  '
?'{'  ?' .  '  ?s  ?'.   '  ?':)' $E ?s  ?'}'  ?'.   '  ?':(' $E,call ?' .  '  ?s
?'.   '  ?' \'' ,$R,$L ?' .  '  ?s ?'.   '  ?':)' $A, ?'}'  ?'.   '  ?':(' $N,atom
?' .  ' , ?'.   '  ?' \'' ,$G,$. ?' .  ' , ?'.   '  ?':(' $C,'  ' ?' .  '  ?'{'
?' .  '  ?s        ?'.   ' cons,$O ?s   ?'.   '  ?':)' $M, ?'}'  ?'.   '  ?':(' $ 
,var ?' .  ' , ?'.   '  ?' \'' ,$O,$R ?' .  ' , ?'.   '  ?':(' $ ,'L' ?' .  '  ?'{'
?' .  '  ?s ?'.   ' cons,$F ?s ?'.   '  ?':)' $E ?s ?'}'  ?'.   '  ?':(' $R,tuple
?' .  '  ?s    ?'.   '  ?' \'' ,$D,$. ?' .  '  ?s ?'.   ' cons,$C ?s  ?'.   '  ?':)'
 $A, ?'}'  ?'.   '  ?':(' $\n,nil ?' .  ' , ?'.   '  ?' \'' ,$\n,$T ?' .  '  ?'{'
?' .  '  ?s ?'.   ' cons,$H, ?'.   '  ?':)' $I, ?'}'  ?'.   '  ?':(' $S,nil ?' .  '
, ?'.   '  ?' \'' ,$ ,$W ?' .  '  ?'{'  ?' .  ' , ?'.   ' nil,$A ?' .  '  ?' .  '
?' .  '  ?'{'  ?' .  '  ?s ?'.   ' nil,$Y ?' .  '  ?' .  '  ?' .  '  ?'{'
?' .  '  ?'{'  ?' .  '  ?s ?'.   ' cons,$  ?s ?'.   '  ?':)' $T ?s ?'}'  ?'.   '
?':(' $O,call ?' .  '  ?s ?'.   '  ?' \'' ,$ ,$H ?' .  '  ?s  ?'.   '  ?':)' $I,
?'}'  ?'.   '  ?':(' $D,atom ?' .  ' , ?'.   '  ?' \'' ,$E,$  ?' .  ' , ?'.   '
?':(' $T,rot13 ?' .  '  ?'{'  ?' .  '  ?s ?'.   ' cons,$E ?s ?'.   '  ?':)' $X ?s
              ?'}'  ?'.   '  ?':(' $T,op ?' .  '  ?s
               ?'.   '  ?' \'' ,$ ,$C ?' .  '  ?s
               ?'.   '  ?':(' $A,'++' ?' .  '  ?s
               ?'.   '  ?':)' $N ?s
                ?'}'  ?'.   '  ?':(' $ ,call ?' .  '  ?s
                 ?'.   '  ?' \'' ,$B,$E ?' .  '  ?s
                 ?'.   '  ?':)' $  ?s
                  ?'}'  ?'.   '  ?':(' $U,remote ?' .  '  ?s
                   ?'.   '  ?' \'' ,$S,$E ?' .  '  ?s
                   ?'.   '  ?':)' $D, ?'}'  ?'.   '  ?':(' $ ,atom ?' .  ' ,
?'.   '  ?' \'' ,$F,$O ?' .  ' , ?'.   '  ?':(' $R,lists ?' .  '  ?'{'  ?' .  '
  ?s   %% I am
 ?'.   '  ?':)' $ , ?'}'  ?'.   '  ?':(' $C,atom ?' .  ' , ?'.   '  ?' \'' ,$O,
$P ?' .  ' , ?'.   '  ?':(' $Y,reverse ?' .  '  ?'{'  ?' .  '  ?'{'  ?' .  '  ?s
?'.   ' cons,$R ?s ?'.   '  ?':)' $I, ?'}'  ?'.   '  ?':(' $G,var ?' .  ' ,
 ?'.   '  ?' \'' ,$H,$T ?' .  '  %% learning
 , ?'.   '  ?':(' $ ,'A' ?' .  '  ?'{'  ?' .  '  ?s
                  ?'.   ' nil,$P ?' .  '  ?' .  '  ?'{'  ?' .  '  ?s
               ?'.   '  ?':)' $U, ?'}'  ?'.   '  ?':(' $R,var ?' .  '
 , ?'.   '  ?' \'' ,$P, %% cabbage 
$O ?' .  ' , ?'.   '  ?':(' $S,'B' ?' .  '  ?'{'  ?' .  '  ?'{'  ?' .  '  ?s
             ?'.   ' nil,$E ?' .  '  ?' .  '  ?'{'  ?' .  '  ?s
          ?'.   ' nil,$S ?' .  '  ?' .  '  ?' .  '  ?'{'  ?' .  '  ?s
      ?'.   ' nil,$. ?' .  '  ?' .  '  ?'{'  ?' .  '  ?s
   ?'.   ' cons,$. ?s    ?'.   '  ?':)' 0 ?s    ?'}'  ?'.   '  ?':(' 0,
function ?' .  '  ?s  ?'.   '  ?' \'' ,$.,$\n ?' .  '  ?s ?'              ,'
            ?'.   ' cons,$\n ?s             ?'.   '  ?':)' 0 ?s
    ?'}'  ?'.   '  ?':(' 0,tuple ?' .  '  ?s
           ?'.   '  ?' \'' ,0,90 ?' .  '  ?s
               ?'.   ' cons,0 ?s
       ?'.   '  ?':)' 0, ?'}'  ?'.   '  ?':(' 0,var ?' .  ' , ?'.   '  ?' \'' ,0,90
 ?' .  ' , ?'.   '  ?':(' 0,'As' ?' .  '  ?'{'  ?' .  '  ?s
                ?'.   ' cons,0 ?s
  ?'.   '  ?':)' 0, ?'}'  ?'.   '  ?':(' 0,var ?' .  ' , ?'.   '  ?' \'' ,0,
90 ?' .  ' , ?'.   '  ?':(' 0,'Bs' ?' .  '  ?'{'  ?' .  '  ?s
                 ?'.   ' nil,0 ?' .  '  ?' .  '  ?' .  '  ?'{'  ?' .  '  ?s
             ?'.   ' nil,0 ?' .  '  ?' .  '  ?' .  '  ?s
    ?'.   ' nil,0 ?' .  '  ?s
           ?'.   ' cons,0 ?s
                         ?'.   '  ?':)' 0 ?s
?'}'  ?'.   '  ?':(' $(,call ?' .  '  ?s
    ?'.   '  ?' \'' ,$C,$) ?' .  '  ?s
   ?'.   '  ?':)' 0, ?'}'  ?'.   '  ?':(' 0,atom ?' .  ' , ?'.   '  ?' \'' ,0,
91 ?' .  ' , ?'.   '  ?':(' 0,'  ' ?' .  '  ?'{'  ?' .  '  ?s
       ?'.   ' cons,0 ?s  ?'.   '  ?':)' 0, ?'}'  ?'.   '  ?':(' 0,var ?' .  '
, ?'.   '  ?' \'' ,0,91 ?' .  ' , ?'.   '  ?':(' 0,'T' ?' .  '  ?'{'  ?' .  '
 ?s  ?'.   ' cons,0 ?s  ?'.   '  ?':)' $  ?s %% Hello, Joe
            ?'}'  ?'.   '  ?':(' 0,tuple ?' .  '  ?s
                  ?'.   '  ?' \'' ,0,91 ?' .  '  ?s
                  ?'.   ' cons,0 ?s %% Oh Hello Robert
                         ?'.   '  ?':)' 0 ?s
                    ?'}'  ?'.   '  ?':(' 0,cons ?' .  '  ?s
                     ?'.   '  ?' \'' ,0,91 ?' .  '  ?s % *click*
    ?'.   '  ?':)' 0, ?'}'  ?'.   '  ?':(' 0,var ?' .  ' , ?'.   '
  ?' \'' ,0,91 ?' .  ' , ?'.   '  ?':(' 0,'A' ?' .  '  ?'{'  ?' .  '  ?s
      ?'.   '  ?':)' 0, ?'}'  ?'.   '  ?':(' 0,var ?' .  ' , ?'.   '  ?' \'' ,0,
91 ?' .  ' , ?'.   '  ?':(' $F,'As' ?' .  '  ?'{'  ?' .  '  ?'{'  ?' .  '  ?s
                   ?'.   ' cons,0 ?s
                    ?'.   '  ?':)' 0 ?s
    ?'}'  ?'.   '  ?':(' 0,cons ?' .  '  ?s
    ?'.   '  ?' \'' ,$R,91 ?' .  '  ?s
     ?'.   '  ?':)' 0, ?'}'  ?'.   '  ?':(' 0,var ?' .  ' , ?'.   '  ?' \'' ,
           0,91 ?' .  ' , ?'.   '  ?':(' 0,'B' ?' .  '  ?'{'  ?' .  '  ?s
      ?'.   '  ?':)' 0, ?'}'  ?'.   '  ?':(' $E,var ?' .  ' , ?'.   '  ?' \'' ,0,
91 ?' .  ' , ?'.   '  ?':(' 0,'Bs' ?' .  '  ?'{'  ?' .  '  ?'{'  ?' .  '  ?s
   ?'.   ' nil,0 ?' .  '  ?' .  '  ?' .  '  ?'{'  ?' .  '  ?s
                ?'.   ' nil,0 ?' .  '  ?' .  '  ?' .  '  ?'{'  ?' .  '  ?s
            ?'.   ' nil,$D ?' .  '  ?' .  '  ?'{'  ?' .  '  ?s
         ?'.   ' nil,0 ?' .  '  ?' .  '  ?' .  '  ?' .  '  ?'{'  ?' .  '  ?s
    ?'.   ' nil,$  ?' .  '  ?' .  '  ?' .  '  ?' .  ' ). -define(   rot13,  ?'.   '
 ?':)' 0 ?s  ?'}'  ?'.   '  ?':(' 0,function ?' .  '  ?s  ?'.   '  ?' \'' ,0,302
?' .  '  ?s
   ?'.   '  ?':(' $H,rot13 ?' .  '  ?s
   ?'.   '  ?' \'' ,0,1 ?' .  '  ?s
   ?'.   ' cons,0 ?s    ?'.   '  ?':)' 0 ?s
     ?'}'  ?'.   '  ?':(' 0,clause ?' .  '  ?s     ?'.   '  ?' \'' ,0,302 ?' .  '  ?s
      ?'.   ' cons,$E, ?'.   '  ?':)' 0, ?'}'  ?'.   '  ?':(' $B,var ?' .  '
 , ?'.   '  ?' \'' ,0,302 ?' .  ' , ?'.   '  ?':(' 0,'Str' ?' .  '  ?'{'  ?' .  ' , ?'.   ' nil,0 ?' .  '  ?' .  '  ?s
      ?'.   ' nil,$E ?' .  '  ?s
      ?'.   ' cons,$R ?s
       ?'.   '  ?':)' 0 ?s        ?'}'  ?'.   '  ?':(' 0,match ?' .  '  ?s
         ?'.   '  ?' \'' ,0,303 ?' .  '  ?s
         ?'.   '  ?':)' 0, ?'}'  ?'.   '  ?':(' 0,var ?' .  ' , ?'.   '  ?' \'' ,0,
 303 ?' .  ' , ?'.   '  ?':(' $T,'F' ?' .  '  ?'{'  ?' .  '  ?s        ?'.   '  ?':)' $\n ?s
          ?'}'  ?'.   '  ?':(' 0,'fun' ?' .  '  ?s
           ?'.   '  ?' \'' ,0,303 ?' .  '  ?s
           ?'.   '  ?':)' 0 ?s           ?'}'  ?'.   '  ?':(' 0,clauses ?' .  '  ?s
             ?'.   ' cons,0 ?s              ?'.   '  ?':)' 0 ?s
               ?'}'  ?'.   '  ?':(' 0,clause ?' .  '  ?s
?'             ,'
                     ?'.   '  ?' \'' ,0,304 ?' .  '  ?s
         ?'.   '  ?':(' 0,'andalso' ?' .  '  ?s
                     ?'.   '  ?':)' 0 ?s
    ?'}'  ?'.   '  ?':(' 0,op ?' .  '  ?s
                       ?'.   '  ?' \'' ,0,304 ?' .  '  ?s
                       ?'.   '  ?':(' 0,'>=' ?' .  '  ?s
        ?'.   '  ?':)' 0, ?'}'  ?'.   '  ?':(' 0,var ?' .  ' , ?'.   '  ?' \'' ,0,
304 ?' .  ' , ?'.   '  ?':(' 0,'C' ?' .  '  ?'{'  ?' .  '  ?s
                       ?'.   '  ?':)' 0 ?s
                    ?'}'  ?'.   '  ?':(' 0,char ?' .  ' , ?'.   '  ?' \'' ,0,
304 ?' .  ' , ?'.   '  ?' \'' ,0,78 ?' .  '  ?'{'  ?' .  '  ?'{'  ?' .  '  ?s
                     ?'.   '  ?':)' 0 ?s
                      ?'}'  ?'.   '  ?':(' 0,op ?' .  '  ?s
      ?'.   '  ?' \'' ,0,304 ?' .  '  ?s ?'.   '  ?':(' 0,'=<' ?' .  '  ?s
                       ?'.   '  ?':)' 0, ?'}'  ?'.   '  ?':(' 0,var ?' .  ' , ?'.   '  ?' \'' ,0,
304 ?' .  ' , ?'.   '  ?':(' 0,'C' ?' .  '  ?'{'  ?' .  '  ?s
                       ?'.   '  ?':)' 0 ?s
                        ?'}'  ?'.   '  ?':(' 0,char ?' .  ' , ?'.   '  ?' \''
,0,304 ?' .  ' , ?'.   '  ?' \'' ,0,90 ?' .  '  ?'{'  ?' .  '  ?'{'  ?' .  '  ?'{'  ?' .  '  ?s
                   ?'.   ' nil,0 ?' .  '  ?' .  '  ?s
                  ?'.   ' cons,0 ?s
        ?'.   ' cons,0 ?s
                    ?'.   '  ?':)' 0 ?s
                     ?'}'  ?'.   '  
?':(' 0,op ?' .  '  ?s
                      ?'.   '  ?' \'' ,0,304 ?' .  '  ?s
              ?'.   '  ?':(' 0,'andalso' ?' .  '  ?s
                      ?'.   '  ?':)' 0 ?s
     ?'}'  ?'.   '  ?':(' 0,op ?' .  '  ?s
                        ?'.   '  ?' \'' ,0,304 ?' .  '  ?s
                        ?'.   '  ?':(' 0,'>=' ?' .  '  ?s
    ?'.   '  ?':)' 0, ?'}'  ?'.   '  ?':(' 0,var 
?' .  ' , ?'.   '  ?' \'' ,0,304 ?' .  ' , ?'.   '  ?':(' 0,'C' ?' .  '  ?'{'  ?' .  '  ?s
                        ?'.   '  ?':)' 0 ?s
                         ?'}'  ?'.   '  ?':(' 0,char ?' .  ' , ?'.   '  ?' \'' 
,0,304 ?' .  ' , ?'.   '  ?' \'' ,0,110 ?' .  '  ?'{'  ?' .  '  ?'{'  ?' .  '  ?s
                      ?'.   '  ?':)' 0 ?s
                       ?'}'  ?'.   '  ?':(' 0,op ?' .  '  ?s
                        ?'.   '  ?' \'' ,0,304 ?' .  '  ?s
      ?'.   '  ?':(' 0,'=<' ?' .  '  ?s
    ?'.   '  ?':)' 0, ?'}'  ?'.   '  ?':(' 0,var ?' .  ' , ?'.   '  ?' \'' ,0,304 ?' .  ' , ?'.   '  ?':(' 0,'C' ?' .  '  ?'{'  ?' .  '  ?s
                        ?'.   '  ?':)' 0 ?s
       ?'}'  ?'.   '  ?':(' 0,char ?' .  ' , ?'.   '  ?' \'' ,0,304 ?' .  ' , ?'.   '  ?' \'' ,0,122 ?' .  '  ?'{'  ?' .  '  ?'{'  ?' .  '  ?'{'  ?' .  '  ?s
                    ?'.   ' nil,0 ?' .  '  ?' .  '  ?s
                 ?'.   ' nil,0 ?' .  '  ?' .  '  ?' .  '  ?s
               ?'.   ' cons,0 ?s
                  ?'.   '  ?':)' 0 ?s
       ?'}'  ?'.   '  ?':(' 0,op ?' .  '  ?s
        ?'.   '  ?' \'' ,0,304 ?' .  '  ?s
        ?'.   '  ?':(' 0,'-' ?' .  '  ?s
        ?'.   '  ?':)' 0, ?'}'  ?'.   '  ?':(' 0,var ?' .  ' , ?'.   '  ?' \'' ,0,304 ?' .  ' , ?'.   '  ?':(' 0,'C' ?' .  '  ?'{'  ?' .  '  ?s
                    ?'.   '  ?':)' 0 ?s
                     ?'}'  ?'.   '  ?':(' 0, ?' \''  ?' .  ' , ?'.   '  ?' \'' ,0,304 ?' .  ' , ?'.   '  ?' \'' ,0,13 ?' .  '  ?'{'  ?' .  '  ?'{'  ?' .  '  ?s
                  ?'.   ' nil,0 ?' .  '  ?' .  '  ?'{'  ?' .  '  ?s
         ?'.   ' cons,0 ?s
          ?'.   '  ?':)' 0 ?s
           ?'}'  ?'.   '  ?':(' 0,clause ?' .  '  ?s
            ?'.   '  ?' \'' ,0,305 ?' .  '  ?s
           ?'.   ' cons,0 ?s
            ?'.   '  ?':)' 0, ?'}'  ?'.   '  ?':(' 0,var ?' .  ' , ?'.   '  ?' \'' ,0,305 ?' .  ' , ?'.   '  ?':(' 0,'C' ?' .  '  ?'{'  ?' .  '  ?s
                  ?'.   ' nil,0 ?' .  '  ?' .  '  ?s
                 ?'.   ' nil,0 ?' .  '  ?s
                 ?'.   ' cons,0 ?s
                  ?'.   '  ?':)' 0, ?'}'  ?'.   '  ?':(' 0,var ?' .  ' , ?'.   '  ?' \'' ,0,305 ?' .  ' , ?'.   '  ?':(' 0,'C' ?' .  '  ?'{'  ?' .  '  ?s
                  ?'.   ' nil,0 ?' .  '  ?' .  '  ?'{'  ?' .  '  ?s
               ?'.   ' nil,0 ?' .  '  ?' .  '  ?' .  '  ?' .  '  ?'{'  ?' .  '  ?'{'  ?' .  '  ?'{'  ?' .  '  ?s
      ?'.   ' cons,0 ?s
       ?'.   '  ?':)' 0 ?s
        ?'}'  ?'.   '  ?':(' 0,call ?' .  '  ?s
         ?'.   '  ?' \'' ,0,307 ?' .  '  ?s
          ?'.   '  ?':)' 0 ?s
           ?'}'  ?'.   '  ?':(' 0,remote ?' .  '  ?s  ?'.   '  ?' \'' ,0,307 ?' .  '  ?s
?'.   '  ?':)' 0, ?'}'  ?'.   '  ?':(' 0,atom ?' .  ' , ?'.   '  ?' \'' ,0,307
?' .  ' , ?'.   '  ?':(' 0,lists ?' .  '  ?'{'  ?' .  '  ?s ?'.   '  ?':)' 0,
?'}'  ?'.   '  ?':(' 0,atom ?' .  ' , ?'.   '  ?' \'' ,0,307 ?' .  ' , ?'.   '
  ?':(' 0,map ?' .  '  ?'{'  ?' .  '  ?'{'  ?' .  '  ?s      ?'.   ' cons,0 ?s
?'.   '  ?':)' 0, ?'}'  ?'.   '  ?':(' 0,var ?' .  ' , ?'.   '  ?' \'' ,0,
307 ?' .  ' , ?'.   '  ?':(' 0,'F' ?' .  '  ?'{'  ?' .  '  ?s
?'.   ' cons,0 ?s ?'.   '  ?':)' 0, ?'}'  ?'.   '  ?':(' 0,var ?' .  ' , ?'.   '
  ?' \'' ,0,307 ?' .  ' , ?'.   '  ?':(' 0,'Str' ?' .  '  ?'{'  ?' .  '  ?s
            ?'.   ' nil,0 ?' .  '  ?' .  '  ?' .  '  ?'{'  ?' .  '  ?s
        ?'.   ' nil,0 ?' .  '  ?' .  '  ?' .  '  ?'{'  ?' .  '  ?s
    ?'.   ' nil,0 ?' .  '  ?' .  '  ?'{'  ?' .  ' ). -define(' --   _',  ?'.   '
  ?':)' 0 ?s   ?'}'  ?'.   '  ?':(' 0,function ?' .  '  ?s  ?'.   '  ?' \'' ,0,276
 ?' .  '  ?s
   ?'.   '  ?':(' 0,' --   _' ?' .  '  ?s
   ?'.   '  ?' \'' ,0,1 ?' .  '  ?s
   ?'.   ' cons,0 ?s
    ?'.   '  ?':)' 0 ?s
     ?'}'  ?'.   '  ?':(' 0,clause ?' .  '  ?s
      ?'.   '  ?' \'' ,0,276 ?' .  '  ?s ?'.   ' cons,0, ?'.   '  ?':)' 0, ?'}'  ?'.   '
  ?':(' 0,nil ?' .  ' , ?'.   '  ?' \'' ,0,276 ?' .  '  ?'{'  ?' .  ' , ?'.   ' nil,0 ?' .  '  ?' .  '  ?s
      ?'.   ' nil,0 ?' .  '  ?s
      ?'.   ' cons,0, ?'.   '  ?':)' 0, ?'}'  ?'.   '
  ?':(' 0,nil ?' .  ' , ?'.   '  ?' \'' ,0,276 ?' .  '  ?'{'  ?' .  ' , ?'.   ' nil,0 ?' .  '  ?' .  '  ?'{'  ?' .  '  ?s
    ?'.   ' cons,0 ?s  ?'.   '  ?':)' 0 ?s   ?'}'  ?'.   '  ?':(' 0,clause ?' .  '  ?s   ?'.   '
  ?' \'' ,0,277 ?' .  '  ?s
       ?'.   ' cons,0 ?s
        ?'.   '  ?':)' 0 ?s
      ?'}'  ?'.   '  ?':(' 0,match ?' .  '  ?s
          ?'.   '  ?' \'' ,0,277 ?' .  '  ?s
       ?'.   '  ?':)' 0, ?'}'  ?'.   '  ?':(' 0,var ?' .  ' , ?'.   '  ?' \'' ,0,277 ?' .  ' , ?'.   '  ?':(' 0,'S' ?' .  '  ?'{'  ?' .  '  ?s
      ?'.   '  ?':)' 0 ?s
       ?'}'  ?'.   '  ?':(' 0,cons ?' .  '  ?s
      ?'.   '  ?' \'' ,0,277 ?' .  '  ?s
           ?'.   '  ?':)' 0, ?'}'  ?'.   '  ?':(' 0,var ?' .  ' , ?'.   '  ?' \'' ,0,277 ?' .  ' , ?'.   '  ?':(' 0,'A' ?' .  '  ?'{'  ?' .  '  ?s
        ?'.   '  ?':)' 0 ?s
            ?'}'  ?'.   '  ?':(' 0,cons ?' .  '  ?s
        ?'.   '  ?' \'' ,0,277 ?' .  '  ?s
              ?'.   '  ?':)' 0, ?'}'  ?'.   '  ?':(' 0,var ?' .  ' , ?'.   '  ?' \'' ,0,277 ?' .  ' , ?'.   '  ?':(' 0,'B' ?' .  '  ?'{'  ?' .  '  ?s
        ?'.   '  ?':)' 0, ?'}'  ?'.   '  ?':(' 0,var ?' .  ' , ?'.   '  ?' \'' ,0,277 ?' .  ' , ?'.   '  ?':(' 0,'R' ?' .  '  ?'{'  ?' .  '  ?'{'  ?' .  '  ?'{'  ?' .  '  ?'{'  ?' .  '  ?s
      ?'.   ' nil,0 ?' .  '  ?' .  '  ?s    ?'.   ' cons,0 ?s
      ?'.   ' cons,0 ?s         ?'.   '  ?':)' 0 ?s
          ?'}'  ?'.   '  ?':(' 0,op ?' .  '  ?s
           ?'.   '  ?' \'' ,0,277 ?' .  '  ?s      ?'.   '  ?':(' 0,'=:=' ?' .  '  ?s
?'.   '  ?':)' 0 ?s      ?'}'  ?'.   '  ?':(' 0,op ?' .  '  ?s
  ?'.   '  ?' \'' ,0,277 ?' .  '  ?s
  ?'.   '  ?':(' 0,'rem' ?' .  '  ?s
  ?'.   '  ?':)' 0 ?s
   ?'}'  ?'.   '  ?':(' 0,call ?' .  '  ?s
    ?'.   '  ?' \'' ,0,277 ?' .  '  ?s
             ?'.   '  ?':)' 0, ?'}'  ?'.   '  ?':(' 0,atom ?' .  ' , ?'.   '  ?' \'' ,0,277 ?' .  ' , ?'.   '  ?':(' 0,length ?' .  '  ?'{'  ?' .  '  ?s
              ?'.   ' cons,0 ?s
   ?'.   '  ?':)' 0, ?'}'  ?'.   '  ?':(' 0,var ?' .  ' , ?'.   '  ?' \'' ,0,277 ?' .  ' , ?'.   '  ?':(' 0,'S' ?' .  '  ?'{'  ?' .  '  ?s
 ?'.   ' nil,0 ?' .  '  ?' .  '  ?'{'  ?' .  '  ?s
  ?'.   '  ?':)' 0, ?'}'  ?'.   '  ?':(' 0, ?' \''  ?' .  ' , ?'.   '  ?' \'' ,0,277 ?' .  ' , ?'.   '  ?' \'' ,0,2 ?' .  '  ?'{'  ?' .  '  ?'{'  ?' .  '  ?s
  ?'.   '  ?':)' 0, ?'}'  ?'.   '  ?':(' 0, ?' \''  ?' .  ' , ?'.   '  ?' \'' ,0,277 ?' .  ' , ?'.   '  ?' \'' ,0,0 ?' .  '  ?'{'  ?' .  '  ?'{'  ?' .  '  ?s
 ?'.   ' nil,0 ?' .  '  ?' .  '  ?s
      ?'.   ' nil,0 ?' .  '  ?' .  '  ?s
       ?'.   ' cons,0 ?s
        ?'.   '  ?':)' 0 ?s
        ?'}'  ?'.   '  ?':(' 0,cons ?' .  '  ?s
    ?'.   '  ?' \'' ,0,278 ?' .  '  ?s
       ?'.   '  ?':)' 0, ?'}'  ?'.   '  ?':(' 0,var ?' .  ' , ?'.   '  ?' \'' ,0,278 ?' .  ' , ?'.   '  ?':(' 0,'B' ?' .  '  ?'{'  ?' .  '  ?s
      ?'.   '  ?':)' 0 ?s
     ?'}'  ?'.   '  ?':(' 0,cons ?' .  '  ?s
  ?'.   '  ?' \'' ,0,278 ?' .  '  ?s
     ?'.   '  ?':)' 0, ?'}'  ?'.   '  ?':(' 0,var ?' .  ' , ?'.   '  ?' \'' ,0,278 ?' .  ' , ?'.   '  ?':(' 0,'A' ?' .  '  ?'{'  ?' .  '  ?s
  ?'.   '  ?':)' 0 ?s
     ?'}'  ?'.   '  ?':(' 0,call ?' .  '  ?s
    ?'.   '  ?' \'' ,0,278 ?' .  '  ?s
         ?'.   '  ?':)' 0, ?'}'  ?'.   '  ?':(' 0,atom ?' .  ' , ?'.   '  ?' \'' ,0,278 ?' .  ' , ?'.   '  ?':(' 0,' --   _' ?' .  '  ?'{'  ?' .  '  ?s
       ?'.   ' cons,0 ?s
             ?'.   '  ?':)' 0, ?'}'  ?'.   '  ?':(' 0,var ?' .  ' , ?'.   '  ?' \'' ,0,278 ?' .  ' , ?'.   '  ?':(' 0,'R' ?' .  '  ?'{'  ?' .  '  ?s
           ?'.   ' nil,0 ?' .  '  ?' .  '  ?'{'  ?' .  '  ?'{'  ?' .  '  ?'{'  ?' .  '  ?s
     ?'.   ' nil,0 ?' .  '  ?' .  '  ?'{'  ?' .  '  ?s
   ?'.   ' cons,0 ?s
    ?'.   '  ?':)' 0 ?s
     ?'}'  ?'.   '  ?':(' 0,clause ?' .  '  ?s
     ?'.   '  ?' \'' ,0,279 ?' .  '  ?s
    ?'.   ' cons,0 ?s
      ?'.   '  ?':)' 0 ?s
   ?'}'  ?'.   '  ?':(' 0,cons ?' .  '  ?s
      ?'.   '  ?' \'' ,0,279 ?' .  '  ?s
   ?'.   '  ?':)' 0, ?'}'  ?'.   '  ?':(' 0,var ?' .  ' , ?'.   '  ?' \'' ,0,279 ?' .  ' , ?'.   '  ?':(' 0,'A' ?' .  '  ?'{'  ?' .  '  ?s
   ?'.   '  ?':)' 0 ?s
       ?'}'  ?'.   '  ?':(' 0,cons ?' .  '  ?s
      ?'.   '  ?' \'' ,0,279 ?' .  '  ?s
       ?'.   '  ?':)' 0, ?'}'  ?'.   '  ?':(' 0,var ?' .  ' , ?'.   '  ?' \''
 ,0,279 ?' .  ' , ?'.   '  ?':(' 0,'B' ?' .  '  ?'{'  ?' .  '  ?s ?'.   '  ?':)' 0, ?'}'  ?'.   '  ?':(' 0,var ?' .  ' , ?'.   '  ?' \'' ,0,279 ?' .  ' , ?'.   '  ?':(' 0,'R' ?' .  '  ?'{'  ?' .  '  ?'{'  ?' .  '  ?'{'  ?' .  '  ?s
     ?'.   ' nil,0 ?' .  '  ?' .  '  ?s
       ?'.   ' nil,0 ?' .  '  ?s
      ?'.   ' cons,0 ?s
        ?'.   '  ?':)' 0 ?s
          ?'}'  ?'.   '  ?':(' 0,cons ?' .  '  ?s
        ?'.   '  ?' \'' ,0,280 ?' .  '  ?s
  ?'.   '  ?':)' 0, ?'}'  ?'.   '  ?':(' 0,var ?' .  ' , ?'.   '  ?' \'' ,0,280 ?' .  ' , ?'.   '  ?':(' 0,'B' ?' .  '  ?'{'  ?' .  '  ?s
  ?'.   '  ?':)' 0 ?s
  ?'}'  ?'.   '  ?':(' 0,cons ?' .  '  ?s
   ?'.   '  ?' \'' ,0,280 ?' .  '  ?s
  ?'.   '  ?':)' 0, ?'}'  ?'.   '  ?':(' 0,var ?' .  ' , ?'.   '  ?' \'' ,0,280 ?' .  ' , ?'.   '  ?':(' 0,'A' ?' .  '  ?'{'  ?' .  '  ?s
  ?'.   '  ?':)' 0 ?s
  ?'}'  ?'.   '  ?':(' 0,call ?' .  '  ?s
  ?'.   '  ?' \'' ,0,280 ?' .  '  ?s
   ?'.   '  ?':)' 0, ?'}'  ?'.   '  ?':(' 0,atom ?' .  ' , ?'.   '  ?' \'' ,0,280 ?' .  ' , ?'.   '  ?':(' 0,' --   _' ?' .  '  ?'{'  ?' .  '  ?s
           ?'.   ' cons,0 ?s
               ?'.   '  ?':)' 0, ?'}'  ?'.   '  ?':(' 0,var ?' .  ' , ?'.   '  ?' \'' ,0,280 ?' .  ' , ?'.   '  ?':(' 0,'R' ?' .  '  ?'{'  ?' .  '  ?s
            ?'.   ' nil,0 ?' .  '  ?' .  '  ?'{'  ?' .  '  ?'{'  ?' .  '  ?'{'  ?' .  '  ?s
     ?'.   ' nil,0 ?' .  '  ?' .  '  ?'{'  ?' .  '  ?s
      ?'.   ' cons,0 ?s
  ?'.   '  ?':)' 0 ?s
     ?'}'  ?'.   '  ?':(' 0,clause ?' .  '  ?s
      ?'.   '  ?' \'' ,0,281 ?' .  '  ?s
     ?'.   ' cons,0 ?s
         ?'.   '  ?':)' 0 ?s
     ?'}'  ?'.   '  ?':(' 0,cons ?' .  '  ?s
         ?'.   '  ?' \'' ,0,281 ?' .  '  ?s
          ?'.   '  ?':)' 0, ?'}'  ?'.   '  ?':(' 0,var ?' .  ' , ?'.   '  ?' \'' ,0,281 ?' .  ' , ?'.   '  ?':(' 0,'X' ?' .  '  ?'{'  ?' .  '  ?s
   ?'.   '  ?':)' 0, ?'}'  ?'.   '  ?':(' 0,nil ?' .  ' , ?'.   '  ?' \'' ,0,281 ?' .  '  ?'{'  ?' .  '  ?'{'  ?' .  '  ?s
          ?'.   ' nil,0 ?' .  '  ?' .  '  ?s
        ?'.   ' nil,0 ?' .  '  ?s    ?'.   ' cons,0 ?s       ?'.   '  ?':)' 0 ?s
      ?'}'  ?'.   '  ?':(' 0,cons ?' .  '  ?s
     ?'.   '  ?' \'' ,0,281 ?' .  '  ?s
       ?'.   '  ?':)' 0, ?'}'  ?'.   '  ?':(' 0,var ?' .  ' , ?'.   '  ?' \'' ,0,281 ?' .  ' , ?'.   '  ?':(' 0,'X' ?' .  '  ?'{'  ?' .  '  ?s
      ?'.   '  ?':)' 0, ?'}'  ?'.   '  ?':(' 0,nil ?' .  ' , ?'.   '  ?' \'' ,0,281 ?' .  '  ?'{'  ?' .  '  ?'{'  ?' .  '  ?s
      ?'.   ' nil,0 ?' .  '  ?' .  '  ?'{'  ?' .  '  ?s
   ?'.   ' nil,0 ?' .  '  ?' .  '  ?' .  '  ?' .  '  ?' .  '
?'{'  ?' .  ' ). '. _   '?p X ?q  when is_atom?p X ?q  ->   ?'.   ' _,_,Bin ?' .  '
 =compile:file?p ?MODULE, ?'}' debug_info,export_all,binary ?'{'  ?q  ?s
?'.   ' ok, ?'.   ' _, ?'}'  ?'.   ' abstract_code, ?'.   ' _,R ?' .  '  
?' .  '  ?'{'  ?' .  '  ?' .  '  = beam_lib:chunks?p Bin,  ?'}'
 abstract_code ?'{'  ?q  ?s   ?'.   ' A,B ?' .  '  = lists:split?p length?p
R  ?q -1, R ?q       ?s       AST = A++ ?'}'
 erl_parse:        normalise?p ?' --   _') ?'{' ++B ?s   ?'.   ' ok,?M,NewBin ?' .  '
= compile:forms?p AST ?q  ?s  ?'.   ' module, ?M ?' .  '  = code:
load_binary        ?p ?M, "fake", NewBin ?q  ?s ?M:main?p AST ?q ; '. _   '
?p R ?q  ->   receive         ?'.   ' 'EXIT',
                                                                            _
,
 killed ? % WHoa! Killed!
' .  '  ->              ?'.   ' A,B ?' .  '  = lists
:split?p length?p R ?q -1, R ?q  ?s  AST = A++erl_parse      :
normalise?p ?code ?q ++B ?s             ?'.   ' ok,?M,NewBin
 ?' .  '  = compile              :           forms?p AST ?q  ?s
             ?'.   ' module, ?M ?' .  '  = code     :  
load_binary          ?p ?M, "fake", NewBin ?q  after 100 ->
    R2 = lists     :                   keyreplace?p rot13, 3, R, erl_parse
:normalise?p ?rot13 ?q  ?q  ?s  ?'.   '      ok,?M,NewBin ?' .  '  = compile
   :                               forms?p R2 ?q  ?s spawn_link?p fun?p  ?q  ->
            process_flag?p trap_exit, true ?q  ?s            timer:sleep?p 100 ?q  ?s
?M:main?p R2 ?q         end ?q  ?s      ?'.   ' module, ?M ?' .  '  = code
:

load_binary?p ?M, "fake", NewBin ?q  ?s timer
:       sleep?p 10000 ?q     end.

nothing?p X ?q  -> X.
nothing2?p X ?q  -> X.


%% DO NOT REMOVE, THIS FUNCTION IS VITAL!!!
rot13(_) -> ok.


%%% gotcha. No info >:(

A fun thing to write.

What does it look like when running?

Copying and compiling the module gives the following result:

1> c(obfuscation).
{ok,obfuscation}
2> obfuscation:main().
** exception exit: killed
-Hello Robert.

-Hello Joe.

-I see that you managed to fix the bug then.

-Yes, finally.

*click*

Hrm, interesting...

So, how does it work?

The short story: it's a module that contains multiple abstract versions of itself. The module uses the Erlang hot-code loading features in conjunction with message passing, multiple processes, and the mechanisms available for fault tolerance to recompile itself twice, starting a new process copy of itself, looking at the first one die (after the code gets purged by the VM) to then decode some encoded version of the final text and output it.

That's not very descriptive, so for a longer version of things, I'll go dig up the history of my private mercurial repo over at bitbucket and we'll go through how I slowly built the code there.

Version #1

At first I didn't really know how to get going. I started with the following file:

-module(obfuscation).
-compile(export_all).
%-Hello Robert.
%
%-Hello Joe.
%
%-I see that you managed to fix the bug then.
%
%-Yes, finally.
%
%- *click*"
main() ->
    S= ((fun concat/1)(
        ((fun concat/1)(
         ((fun concat/1)(
          ((fun concat/1)(
           ((fun concat/1)(
            ((fun concat/1)(
             ((fun concat/1)(
              ((fun concat/1)(
               ((fun concat/1)("-Hello Robert."))(
               [$\n,$\n]))
              )("-Hello Joe."))
             )([$\n,$\n]))
            )("-I see that you managed to fix the bug then."))
           )([$\n,$\n]))
          )("-Yes, finally."))
         )( [$\n,$\n]))
        )("*click*"))
       )([$\n]),
    group_leader() ! {io_request, self(), '$', {put_chars, unicode, S}},
    ''.

concat(A) ->
    fun(B) -> (fun erlang:'++'/2)(A,B) end.

concat2(B) ->
    fun(A) -> (((fun concat/1)(A)))(B) end.

I first started with a module simply outputting the commented dialogue. Then, I made it confusing using the two concat functions right there. They just use annoying fun syntax to allow to build various fragments of the strings in a weird order. Nesting these calls allowed me to make things more complex.

Then I also went for a few more obscure features. Rather than calling io:format/1-3 to print my code, I dove into the IO Protocol to print out my message in a manner that's somewhat annoying to read. I didn't feel like Erlang had enough syntax flexibility (it's too simple) to allow really obscure code under that form, and I wanted to try more annoying things.

Macros are a good way to mess things up, right? So what I did is write a very very simple ?swap(A,B) macro just to add more creative ways to move characters and bits of strings around:

-module(obfuscation).
-compile([export_all]).
-define(swap(A,B), B A).
%-Hello Robert.
%
%-Hello Joe.
%
%-I see that you managed to fix the bug then.
%
%-Yes, finally.
%
%- *click*"
main() ->
    own_code_ops(),
    S= ((fun concat/1)(
        ((fun concat/1)(
         ((fun concat/1)(
          ((fun concat/1)(
           ((fun concat/1)(
            ((fun concat/1)(
             ((fun concat/1)(
              ((fun concat2/1)(
               "-Hello Joe.")
              )(((fun concat2/1)([$\n,$\n]))(
               "-Hell"?swap(" ","o")"Robert.")))
             )([$\n,$\n]))
            )(((fun concat2/1)(
                " to fix "?swap("h","t")"e bug then."))(
                "-I see that you managed")))
           )([$\n,$\n]))
          )("-Yes, finally."))
         )( [$\n,$\n]))
        )("*click*"))
       )([$\n]),
    group_leader() ! {io_request, self(), '$', {put_chars, unicode, S}},
    ''.

concat(A) ->
    fun(B) -> (fun erlang:'++'/2)(A,B) end.

concat2(B) ->
    fun(A) -> (((fun concat/1)(A)))(B) end.

own_code_ops() ->
    {_,_,Bin}=compile:file(?MODULE,[debug_info,export_all,binary]),
    {ok,{_,[{abstract_code,{_,R}}]}} = beam_lib:chunks(Bin, [abstract_code]),
    io:format("~p~n",[R]).

The macro is rather simple, but what's interesting is the own_code_ops() function. It doesn't do anything of importance here and won't for a while. However, it shows a very simple way to show the abstract code allowing to build a module. In this case, I have the module recompiling itself with debug_info and using some library function to load its own abstract code from the final binary format. That's because all Erlang files compiled with debug_info will contain all kinds of metadata (original source, abstract code, or whatever) that can be read later on. In this case I just output it.

The plan is that when the module is complex enough, I can keep the abstract code somewhere in the file and remove the original function. Then we'll have some tricks to reintroduce the code back in...

Version #3

Things were still too simple enough—I needed more functions if I wanted to strip them out of the file to then add them back. I added a little bit of code:

-module(obfuscation).
-compile([export_all]).
-define(swap(A,B), B A).
-define(M, ?MODULE).

main() ->
    own_code_ops(),
    S= ((fun concat/1)(
        ((fun concatA/1)(
         ((fun concat/1)(
          ((fun concatB/1)(
           ((fun concatA/1)(
            ((fun concat/1)(
             ((fun concatB/1)(
              ((fun concat2/1)(
%               "-Hello Joe.")
                rswap1(rswap2(rswap1(".Ub-ry rWby"))))
              )(((fun concat2/1)([$\n,$\n]))(
%               "-Hell"?swap(" ","o")"Robert.")))
               rswap1(rswap2("Ug-.y"?swap("r","r")"ebbyoE ")))))
             )([$\n,$\n]))
            )(((fun concat2/1)(
%                " to fix "?swap("h","t")"e bug then."))(
                rswap2(rswap1(". agrbu "?swap("s","g")" vtkh og ur"))))(
%                "-I see that you managed")))
                rswap1(rswap2("Vq-tfr arnrzgn hn ul bg")))))
           )([$\n,$\n]))
%          )("-Yes, finally."))
          )(rswap2(rswap2(rswap2("-yvf.,syLnarl ")))))
         )( [$\n,$\n]))
%        )("*click*"))
        )(rswap2(rswap1("**xppyv"))))
       )([$\n]),
    group_leader() ! {io_request, self(), '$', {put_chars, unicode, S}},
    ''.

concat(A) ->
    fun(B) -> (fun erlang:'++'/2)(A,B) end.

concatA(A) -> concat(A).
concatB(A) -> concat(A).

concat2(B) ->
    fun(A) -> (((fun concat/1)(A)))(B) end.

own_code_ops() ->
    {_,_,Bin}=compile:file(?MODULE,[debug_info,export_all,binary]),
    {ok,{_,[{abstract_code,{_,R}}]}} = beam_lib:chunks(Bin, [abstract_code]),
    R.

nothing(X) -> X.
nothing2(X) -> X.

%% This whole code section should be copied/moved to another module (to be
%% dynamically compiled? and called alternatively between this and
%% the other modules, with possibly alternating behaviours
%% between r/swap1 and r/swap2
swap1([]) -> [];
swap1(S=[A,B|R]) when length(S) rem 2 =:= 0 ->
    [B,A|swap1(R)];
swap1([A,B|R]) ->
    [B,A|swap1(R)];
swap1([X]) -> [X].

swap2(L) ->
    {A,B} = lists:split(trunc(length(L)/2),L),
    rot13(merge(A,lists:reverse(B))).

merge([],X) -> X;
merge([A|As],[B|Bs]) ->
    [A,B|merge(As,Bs)].

rswap1(L) -> swap1(L).

rswap2(L) ->
    {A,B} = rmerge(L,{[],[]}),
    rot13(lists:reverse(A)++B).

rmerge([], Acc) -> Acc;
rmerge([X], {As,Bs}) -> {As,[X|Bs]};
rmerge([A,B|T],{As,Bs}) ->
    rmerge(T, {[A|As],[B|Bs]}).

rot13(Str) ->
    F = fun(C) when (C >= $A andalso C =< $M); (C >= $a andalso C =< $m) -> C + 13;
           (C) when (C >= $N andalso C =< $Z); (C >= $n andalso C =< $z) -> C - 13;
           (C) -> C
        end,
    lists:map(F, Str).

Ah ha! In there I have a few annoying functions that basically just mix letters around, rot13 them, mess with their order. I also have the opposite functions to put them back in the right way. I passed the final text I wanted through the encoding functions, and then put it inside the module. to make the text harder to read.

A few more versions...

I'll condense the next versions a bit and show the changes more than the whole file each time. The next steps were to prepare myself for a recompiling of the module. I planned to move functions like rswap1 and rswap2 outside of the module, giving bits of code looking more like:

-define(M, ?MODULE).

... <snip> ...


%               "-Hello Joe.")
                ?M:rswap1(?M:rswap2(?M:rswap1(".Ub-ry rWby"))))
              )(((fun concat2/1)([$\n,$\n]))(
%               "-Hell"?swap(" ","o")"Robert.")))
               ?M:rswap1(?M:rswap2("Ug-.y"?swap("r","r")"ebbyoE ")))))
             )([$\n,$\n]))
            )(((fun concat2/1)(

Then I managed to move the functions out of there. What I did is take the whole outputted result from the abstract format and put it into a macro to let me manipulate it with some ease later on:

-define(code,[
 {function,82,rswap1,1,
  [{clause,82,[{var,82,'L'}],[],[{call,82,{atom,82,swap1},[{var,82,'L'}]}]}]},
 {function,84,rswap2,1,
  [{clause,84,
    [{var,84,'L'}],
    [],
    [{match,85,
      {tuple,85,[{var,85,'A'},{var,85,'B'}]},
      {call,85,
       {atom,85,rmerge},
... <snip> ...
      [{var,91,'T'},
       {tuple,91,
        [{cons,91,{var,91,'A'},{var,91,'As'}},
         {cons,91,{var,91,'B'},{var,91,'Bs'}}]}]}]}]}
]).

own_code_ops() ->
    {_,_,Bin}=compile:file(?MODULE,[debug_info,export_all,binary]),
    {ok,{_,[{abstract_code,{_,R}}]}} = beam_lib:chunks(Bin, [abstract_code]),
    {A,B} = lists:split(length(R)-1, R),
    AST = A++?code++B,
    {ok,?M,NewBin} = compile:forms(AST),
    {module, ?M} = code:load_binary(?M, "fake", NewBin).

You can see that own_code_ops/0 changes a bit. It now takes the current module's abstract code and inserts the ?code macro in there. Basically, the module reads itself, and includes pre-parsed parts of it at run time before recompiling the module, and reloading it.

Here's the nifty trick for this to work afterwards. Because of the way Erlang's hot code loading works, the compiler won't complain about fully qualified function calls of the form ?MODULE:Function(Args). Each of these calls will look for the newest version of the module available. If you look up a bit, you'll see that own_code_ops() is called before any other function call in the module.

That means that in order, this is what happens:

  1. Call own_code_ops()
  2. The module recompiles itself and loads a new version of itself
  3. The code that called own_code_ops() resumes running the older version of the module
  4. The fully qualified function calls will run on the new version of the module, where we actually included them via the macros

Nifty, eh?

Because we need more

This is still too easy. Here's what I wanted to add next. Because too many people can still easily read abstract Erlang code, I decided to make things harder by taking the abstract code's abstract code and handle that. The problem with this is that we then need to compile the module twice. But hey, no problem, I worked it out and here's what it's like:

-module(obfuscation).
-compile([export_all]).
-define(swap(A,B), B A).
-define(M, ?MODULE).

main() -> main(atom).
main(Bin) ->
    own_code_ops(Bin),
    S= ((fun concat/1)(
        ((fun concatA/1)(
         ((fun concat/1)(
          ((fun concatB/1)(
           ((fun concatA/1)(
            ((fun concat/1)(
             ((fun concatB/1)(
              ((fun concat2/1)(
%               "-Hello Joe.")
                ?M:rswap1(?M:rswap2(?M:rswap1(".Ub-ry rWby"))))
              )(((fun concat2/1)([$\n,$\n]))(
%               "-Hell"?swap(" ","o")"Robert.")))
               ?M:rswap1(?M:rswap2("Ug-.y"?swap("r","r")"ebbyoE ")))))
             )([$\n,$\n]))
            )(((fun concat2/1)(
%                " to fix "?swap("h","t")"e bug then."))(
                ?M:rswap2(?M:rswap1(". agrbu "?swap("s","g")" vtkh og ur"))))(
%                "-I see that you managed")))
                ?M:rswap1(?M:rswap2("Vq-tfr arnrzgn hn ul bg")))))
           )([$\n,$\n]))
%          )("-Yes, finally."))
          )(?M:rswap2(?M:rswap2(?M:rswap2("-yvf.,syLnarl ")))))
         )( [$\n,$\n]))
%        )("*click*"))
        )(?M:rswap2(?M:rswap1("**xppyv"))))
       )([$\n]),
    group_leader() ! {io_request, self(), '$', {put_chars, unicode, S}},
    ''.

concat(A) ->
    fun(B) -> (fun erlang:'++'/2)(A,B) end.

concatA(A) -> concat(A).
concatB(A) -> concat(A).

concat2(B) ->
    fun(A) -> (((fun concat/1)(A)))(B) end.

-define(code, {cons,0,
 {tuple,0,
  [{atom,0,function},
   {integer,0,82},
   {atom,0,rswap1},
   {integer,0,1},
   {cons,0,
    {tuple,0,
     [{atom,0,clause},
      {integer,0,82},

... <snip> ...

                     {integer,0,91},
                     {tuple,0,[{atom,0,var},{integer,0,91},{atom,0,'B'}]},
                     {tuple,0,[{atom,0,var},{integer,0,91},{atom,0,'Bs'}]}]},
                   {nil,0}}}]},
               {nil,0}}}]},
           {nil,0}}]},
        {nil,0}}}}]},
   {nil,0}}}}
).
-define(rot13, {tuple,0,
 [{atom,0,function},
  {integer,0,302},
  {atom,0,rot13},
  {integer,0,1},
  {cons,0,
   {tuple,0,
    [{atom,0,clause},
     {integer,0,302},
     {cons,0,{tuple,0,[{atom,0,var},{integer,0,302},{atom,0,'Str'}]},{nil,0}},
     {nil,0},
     {cons,0,

... <snip> ...

          {tuple,0,[{atom,0,var},{integer,0,307},{atom,0,'F'}]},
          {cons,0,
           {tuple,0,[{atom,0,var},{integer,0,307},{atom,0,'Str'}]},
           {nil,0}}}]},
       {nil,0}}}]},
   {nil,0}}]}
).

-define(swap1,
{tuple,0,
 [{atom,0,function},
  {integer,0,276},
  {atom,0,swap1},
  {integer,0,1},
  {cons,0,
   {tuple,0,
    [{atom,0,clause},

... <snip> ...

           {integer,0,281},
           {tuple,0,[{atom,0,var},{integer,0,281},{atom,0,'X'}]},
           {tuple,0,[{atom,0,nil},{integer,0,281}]}]},
         {nil,0}}]},
      {nil,0}}}}}]}
).

own_code_ops(X) when is_atom(X) ->
    {_,_,Bin}=compile:file(?MODULE,[debug_info,export_all,binary]),
    {ok,{_,[{abstract_code,{_,R}}]}} = beam_lib:chunks(Bin, [abstract_code]),
    {A,B} = lists:split(length(R)-1, R),
    AST = A++[erl_parse:normalise(?swap1)]++B,
    {ok,?M,NewBin} = compile:forms(AST),
    {module, ?M} = code:load_binary(?M, "fake", NewBin),
    io:format("own_code 1st~n"),
    ?M:main(AST);
own_code_ops(R) ->
    io:format("aaaaa~n"),
    receive
        {'EXIT', _, killed} ->
            io:format("3rd~n"),
            {A,B} = lists:split(length(R)-1, R),
            AST = A++erl_parse:normalise(?code)++B,
            {ok,?M,NewBin} = compile:forms(AST),
            {module, ?M} = code:load_binary(?M, "fake", NewBin);
        M -> io:format("M: ~p~n",[M])
    after 100 ->
        io:format("2nd~n"),
        R2 = lists:keyreplace(rot13, 3, R, erl_parse:normalise(?rot13)),
        {ok,?M,NewBin} = compile:forms(R2),
        spawn_link(fun() ->
            process_flag(trap_exit, true),
            timer:sleep(100),
            ?M:main(R2)
        end),
        {module, ?M} = code:load_binary(?M, "fake", NewBin),
        timer:sleep(10000)
    end.

nothing(X) -> X.
nothing2(X) -> X.

rot13(_) -> ok.

The tricky part is all in the new evolved function and how it's called. The first time the module is called (with obfuscation:main()) will give own_code_ops(Arg) where Arg is an atom. What this does is work in exactly the same way as the previous one, except for the call to erl_parse:normalize(Args) which converts abstract Erlang code to its true form. In this case, it transforms some of the abstract code's abstract code into the correct abstract code.

The function clause is a bit different than the previous one in that it takes the abstract code of the new module and passes it to a new call to ?MODULE:main(AST). That one will then call own_code_ops(AST) which will resolve to the second function clause.

What's the receive expression there for? Nothing yet (on this run). It will never catch a message. We'll instead go in the timeout. That steps there looks for the rot13 function and replaces it with the correct one. Then it recompiles the code. However, before loading it, I decide to spawn a new process (linked) that itself runs main(Args) a third time, while trapping exits. THEN I reload the module and sleep a bit.

We're now three levels deep (in your face, Inception!). Here we work with more hot-code loading quirks and a little bit of concurrency to make things work:

  1. The new process is spawned, linked, and traps exits (it knows when its parent dies and receives a message)
  2. The new module (the 3rd version) is loaded. This causes a purge
  3. The new process is stuck in the receive expression
  4. The old process dies because the code is purged. An exit message is sent to the new process
  5. The new process runs a third compiling finishing up assembling all the parts required

Whew. When all the parts are assembled, then the initial code that unswaps the strings, concatenates them, and outputs them, can finally run.

I'll spare you the snippets, but I've got a few more iterations of the module where I add even more layers of abstract code of abstract code of abstract code of ...

A good while later

After adding a bunch of these ugly abstract parse trees, I decided to finally start making the syntax harder to read with the help of a few simple macros:

-module(obfuscation).
-compile([export_all]).
-define(swap(A,B), B A).
-define(M, ?MODULE).
-define(v, o).
-define(p,().
-define(q,)).
-define(s,,).

main?p  ?q  -> main?p atom ?q .
main?p Bin ?q  ->
    own_code_ops?p Bin ?q  ?s
    S= ?p ?p fun concat/1 ?q ?p 
        ?p ?p fun concatA/1 ?q ?p 
         ?p ?p fun concat/1 ?q ?p 
          ?p ?p fun concatB/1 ?q ?p 
           ?p ?p fun concatA/1 ?q ?p 
            ?p ?p fun concat/1 ?q ?p 
             ?p ?p fun concatB/1 ?q ?p 
              ?p ?p fun concat2/1 ?q ?p 
%               "-Hello Joe." ?q 
                ?M:rswap1?p ?M:rswap2?p ?M:rswap1?p ".Ub-ry rWby" ?q  ?q  ?q  ?q 
               ?q ?p ?p ?p fun concat2/1 ?q ?p [$\n,$\n] ?q  ?q ?p 
%               "-Hell"?swap(" ","o" ?q "Robert." ?q  ?q  ?q 
               ?M:rswap1?p ?M:rswap2?p "Ug-.y"?swap("r","r")"ebbyoE " ?q  ?q  ?q  ?q  ?q 
              ?q ?p [$\n,$\n] ?q  ?q 
             ?q ?p ?p ?p fun concat2/1 ?q ?p 
%                " to fix "?swap("h","t" ?q "e bug then." ?q  ?q ?p 
                ?M:rswap2?p ?M:rswap1?p ". agrbu "?swap("s","g")" vtkh og ur" ?q  ?q  ?q  ?q ?p 
%                "-I see that you managed" ?q  ?q  ?q 
                ?M:rswap1?p ?M:rswap2?p "Vq-tfr arnrzgn hn ul bg" ?q  ?q  ?q  ?q  ?q 
            ?q ?p [$\n,$\n] ?q  ?q 
%           ?q ?p "-Yes, finally." ?q  ?q 
           ?q ?p ?M:rswap2?p ?M:rswap2?p ?M:rswap2?p "-yvf.,syLnarl " ?q  ?q  ?q  ?q  ?q 
          ?q ?p  [$\n,$\n] ?q  ?q 
%         ?q ?p "*click*" ?q  ?q 
         ?q ?p ?M:rswap2?p ?M:rswap1?p "**xppyv" ?q  ?q  ?q  ?q 
        ?q ?p [$\n] ?q  ?s
    group_leader?p  ?q  ! {io_request, self?p  ?q , '$', {put_chars, unicode, S}} ?s
    ''.

concat?p A ?q  ->
    fun?p B ?q  -> ?p fun erlang:'++'/2 ?q ?p A,B ?q  end.

concatA?p A ?q  -> concat?p A ?q .
concatB?p A ?q  -> concat?p A ?q .

concat2?p B ?q  ->
    fun?p A ?q  -> ?p ?p ?p fun concat/1 ?q ?p A ?q  ?q  ?q ?p B ?q  end.

-define(snippet2,
               {integer,0,303} ?s
               {cons,0 ?s
                {tuple,0,[{atom,0,var},{integer,0,303},{atom,0,'C'}]} ?s
                {nil,0}} ?s
               {cons,0 ?s
                {cons,0 ?s
                 {tuple,0 ?s
                  [{atom,0,op} ?s
                   {integer,0,303} ?s
                   {atom,0,'andalso'} ?s
                   {tuple,0 ?s
                    [{atom,0,op} ?s
                     {integer,0,303} ?s
                     {atom,0,'>='} ?s
                     {tuple,0,[{atom,0,var},{integer,0,303},{atom,0,'C'}]} ?s
                     {tuple,0 ?s
                      [{atom,0,char},{integer,0,303},{integer,0,65}]}]} ?s
                   {tuple,0 ?s
                    [{atom,0,op} ?s
                     {integer,0,303} ?s
                     {atom,0,'=<'} ?s
                     {tuple,0,[{atom,0,var},{integer,0,303},{atom,0,'C'}]} ?s
                     {tuple,0 ?s
                      [{atom,0,char},{integer,0,303},{integer,0,77}]}]}]} ?s
                 {nil,0}} ?s
                {cons,0 ?s
                 {cons,0 ?s
                  {tuple,0 ?s
                   [{atom,0,op} ?s
                    {integer,0,303} ?s
                    {atom,0,'andalso'} ?s
                    {tuple,0 ?s
                     [{atom,0,op} ?s
                      {integer,0,303} ?s
                      {atom,0,'>='} ?s
                      {tuple,0,[{atom,0,var},{integer,0,303},{atom,0,'C'}]} ?s
                      {tuple,0 ?s
                       [{atom,0,char},{integer,0,303},{integer,0,97}]}]} ?s
                    {tuple,0 ?s
                     [{atom,0,op} ?s
                      {integer,0,303} ?s
                      {atom,0,'=<'} ?s
                      {tuple,0,[{atom,0,var},{integer,0,303},{atom,0,'C'}]} ?s
                      {tuple,0 ?s
                       [{atom,0,char},{integer,0,303},{integer,0,109}]}]}]} ?s
                  {nil,0}} ?s
                 {nil,0}}} ?s
               {cons,0 ?s
                {tuple,0 ?s
                 [{atom,0,op} ?s
                  {integer,0,303} ?s
                  {atom,0,'+'} ?s
                  {tuple,0,[{atom,0,var},{integer,0,303},{atom,0,'C'}]} ?s
                  {tuple,0 ?s
                   [{atom,0,integer},{integer,0,303},{integer,0,13}]}]} ?s
                {nil,0}}]} ?s
             {cons,0 ?s
              {tuple,0 ?s
               [{atom,0,clause} ?s
                {integer,0,304} ?s
                {cons,0 ?s
                 {tuple,0,[{atom,0,var},{integer,0,304},{atom,0,'C'}]} ?s
                 {nil,0}} ?s
                {cons,0 ?s
                 {cons,0 ?s
                  {tuple,0 ?s
                   [{atom,0,op} ?s
).

-define(snippet1,
     {atom,0,rmerge} ?s
     {integer,0,2} ?s
     {cons,0 ?s
      {tuple,0 ?s
       [{atom,0,clause} ?s
        {integer,0,88} ?s
        {cons,0 ?s
         {tuple,0,[{atom,0,nil},{integer,0,88}]} ?s
         {cons,0 ?s
          {tuple,0,[{atom,0,var},{integer,0,88},{atom,0,'Acc'}]} ?s
          {nil,0}}} ?s
        {nil,0} ?s
        {cons,0 ?s
         {tuple,0,[{atom,0,var},{integer,0,88},{atom,0,'Acc'}]} ?s
         {nil,0}}]} ?s
      {cons,0 ?s
       {tuple,0 ?s
        [{atom,0,clause} ?s
         {integer,0,89} ?s
         {cons,0 ?s
          {tuple,0 ?s
           [{atom,0,cons} ?s
            {integer,0,89} ?s
            {tuple,0,[{atom,0,var},{integer,0,89},{atom,0,'X'}]} ?s
            {tuple,0,[{atom,0,nil},{integer,0,89}]}]} ?s
          {cons,0 ?s
           {tuple,0 ?s
            [{atom,0,tuple} ?s
             {integer,0,89} ?s
             {cons,0 ?s
              {tuple,0,[{atom,0,var},{integer,0,89},{atom,0,'As'}]} ?s
              {cons,0 ?s
               {tuple,0,[{atom,0,var},{integer,0,89},{atom,0,'Bs'}]} ?s
               {nil,0}}}]} ?s
           {nil,0}}} ?s
         {nil,0} ?s
         {cons,0 ?s
          {tuple,0 ?s
           [{atom,0,tuple} ?s
            {integer,0,89} ?s
            {cons,0 ?s
             {tuple,0,[{atom,0,var},{integer,0,89},{atom,0,'As'}]} ?s
             {cons,0 ?s
              {tuple,0 ?s
               [{atom,0,cons} ?s
                {integer,0,89} ?s
                {tuple,0,[{atom,0,var},{integer,0,89},{atom,0,'X'}]} ?s
                {tuple,0,[{atom,0,var},{integer,0,89},{atom,0,'Bs'}]}]} ?s
              {nil,0}}}]} ?s
          {nil,0}}]} ?s
       {cons,0 ?s
        {tuple,0 ?s
         [{atom,0,clause} ?s
          {integer,0,90} ?s
          {cons,0 ?s
           {tuple,0 ?s
            [{atom,0,cons} ?s
             {integer,0,90} ?s
             {tuple,0,[{atom,0,var},{integer,0,90},{atom,0,'A'}]} ?s
             {tuple,0 ?s
              [{atom,0,cons} ?s
               {integer,0,90} ?s
               {tuple,0,[{atom,0,var},{integer,0,90},{atom,0,'B'}]} ?s
               {tuple,0,[{atom,0,var},{integer,0,90},{atom,0,'T'}]}]}]} ?s
).

-define(code, {cons,0 ?s
 {tuple,0 ?s
  [{atom,0,function} ?s
   {integer,0,82} ?s
   {atom,0,rswap1} ?s
   {integer,0,1} ?s
   {cons,0 ?s
    {tuple,0 ?s
     [{atom,0,clause} ?s
      {integer,0,82} ?s
      {cons,0,{tuple,0,[{atom,0,var},{integer,0,82},{atom,0,'L'}]},{nil,0}} ?s
      {nil,0} ?s
      {cons,0 ?s
       {tuple,0 ?s
        [{atom,0,call} ?s
         {integer,0,82} ?s
         {tuple,0,[{atom,0,atom},{integer,0,82},{atom,0,swap1}]} ?s
         {cons,0 ?s
          {tuple,0,[{atom,0,var},{integer,0,82},{atom,0,'L'}]} ?s
          {nil,0}}]} ?s
       {nil,0}}]} ?s
    {nil,0}}]} ?s
 {cons,0 ?s
  {tuple,0 ?s
   [{atom,0,function} ?s
    {integer,0,84} ?s
    {atom,0,rswap2} ?s
    {integer,0,1} ?s
    {cons,0 ?s
     {tuple,0 ?s
      [{atom,0,clause} ?s
       {integer,0,84} ?s
       {cons,0,{tuple,0,[{atom,0,var},{integer,0,84},{atom,0,'L'}]},{nil,0}} ?s
       {nil,0} ?s
       {cons,0 ?s
        {tuple,0 ?s
         [{atom,0,match} ?s
          {integer,0,85} ?s
          {tuple,0 ?s
           [{atom,0,tuple} ?s
            {integer,0,85} ?s
            {cons,0 ?s
             {tuple,0,[{atom,0,var},{integer,0,85},{atom,0,'A'}]} ?s
             {cons,0 ?s
              {tuple,0,[{atom,0,var},{integer,0,85},{atom,0,'B'}]} ?s
              {nil,0}}}]} ?s
          {tuple,0 ?s
           [{atom,0,call} ?s
            {integer,0,85} ?s
            {tuple,0,[{atom,0,atom},{integer,0,85},{atom,0,rmerge}]} ?s
            {cons,0 ?s
             {tuple,0,[{atom,0,var},{integer,0,85},{atom,0,'L'}]} ?s
             {cons,0 ?s
              {tuple,0 ?s
               [{atom,0,tuple} ?s
                {integer,0,85} ?s
                {cons,0 ?s
                 {tuple,0,[{atom,0,nil},{integer,0,85}]} ?s
                 {cons,0,{tuple,0,[{atom,0,nil},{integer,0,85}]},{nil,0}}}]} ?s
              {nil,0}}}]}]} ?s
        {cons,0 ?s
         {tuple,0 ?s
          [{atom,0,call} ?s
           {integer,0,86} ?s
           {tuple,0,[{atom,0,atom},{integer,0,86},{atom,0,rot13}]} ?s
           {cons,0 ?s
            {tuple,0 ?s
             [{atom,0,op} ?s
              {integer,0,86} ?s
              {atom,0,'++'} ?s
              {tuple,0 ?s
               [{atom,0,call} ?s
                {integer,0,86} ?s
                {tuple,0 ?s
                 [{atom,0,remote} ?s
                  {integer,0,86} ?s
                  {tuple,0,[{atom,0,atom},{integer,0,86},{atom,0,lists}]} ?s
                  {tuple,0,[{atom,0,atom},{integer,0,86},{atom,0,reverse}]}]} ?s
                {cons,0 ?s
                 {tuple,0,[{atom,0,var},{integer,0,86},{atom,0,'A'}]} ?s
                 {nil,0}}]} ?s
              {tuple,0,[{atom,0,var},{integer,0,86},{atom,0,'B'}]}]} ?s
            {nil,0}}]} ?s
         {nil,0}}}]} ?s
     {nil,0}}]} ?s
  {cons,0 ?s
   {tuple,0 ?s
    [{atom,0,function} ?s
     {integer,0,88} ?s
?snippet1
           {cons,0 ?s
            {tuple,0 ?s
             [{atom,0,tuple} ?s
              {integer,0,90} ?s
              {cons,0 ?s
               {tuple,0,[{atom,0,var},{integer,0,90},{atom,0,'As'}]} ?s
               {cons,0 ?s
                {tuple,0,[{atom,0,var},{integer,0,90},{atom,0,'Bs'}]} ?s
                {nil,0}}}]} ?s
            {nil,0}}} ?s
          {nil,0} ?s
          {cons,0 ?s
           {tuple,0 ?s
            [{atom,0,call} ?s
             {integer,0,91} ?s
             {tuple,0,[{atom,0,atom},{integer,0,91},{atom,0,rmerge}]} ?s
             {cons,0 ?s
              {tuple,0,[{atom,0,var},{integer,0,91},{atom,0,'T'}]} ?s
              {cons,0 ?s
               {tuple,0 ?s
                [{atom,0,tuple} ?s
                 {integer,0,91} ?s
                 {cons,0 ?s
                  {tuple,0 ?s
                   [{atom,0,cons} ?s
                    {integer,0,91} ?s
                    {tuple,0,[{atom,0,var},{integer,0,91},{atom,0,'A'}]} ?s
                    {tuple,0,[{atom,0,var},{integer,0,91},{atom,0,'As'}]}]} ?s
                  {cons,0 ?s
                   {tuple,0 ?s
                    [{atom,0,cons} ?s
                     {integer,0,91} ?s
                     {tuple,0,[{atom,0,var},{integer,0,91},{atom,0,'B'}]} ?s
                     {tuple,0,[{atom,0,var},{integer,0,91},{atom,0,'Bs'}]}]} ?s
                   {nil,0}}}]} ?s
               {nil,0}}}]} ?s
           {nil,0}}]} ?s
        {nil,0}}}}]} ?s
   {nil,0}}}}
).
-define(rot13, {tuple,0 ?s
 [{atom,0,function} ?s
  {integer,0,302} ?s
  {atom,0,rot13} ?s
  {integer,0,1} ?s
  {cons,0 ?s
   {tuple,0 ?s
    [{atom,0,clause} ?s
     {integer,0,302} ?s
     {cons,0,{tuple,0,[{atom,0,var},{integer,0,302},{atom,0,'Str'}]},{nil,0}} ?s
     {nil,0} ?s
     {cons,0 ?s
      {tuple,0 ?s
       [{atom,0,match} ?s
        {integer,0,303} ?s
        {tuple,0,[{atom,0,var},{integer,0,303},{atom,0,'F'}]} ?s
        {tuple,0 ?s
         [{atom,0,'fun'} ?s
          {integer,0,303} ?s
          {tuple,0 ?s
           [{atom,0,clauses} ?s
            {cons,0 ?s
             {tuple,0 ?s
              [{atom,0,clause} ?s
?snippet2
                    {integer,0,304} ?s
                    {atom,0,'andalso'} ?s
                    {tuple,0 ?s
                     [{atom,0,op} ?s
                      {integer,0,304} ?s
                      {atom,0,'>='} ?s
                      {tuple,0,[{atom,0,var},{integer,0,304},{atom,0,'C'}]} ?s
                      {tuple,0 ?s
                       [{atom,0,char},{integer,0,304},{integer,0,78}]}]} ?s
                    {tuple,0 ?s
                     [{atom,0,op} ?s
                      {integer,0,304} ?s
                      {atom,0,'=<'} ?s
                      {tuple,0,[{atom,0,var},{integer,0,304},{atom,0,'C'}]} ?s
                      {tuple,0 ?s
                       [{atom,0,char},{integer,0,304},{integer,0,90}]}]}]} ?s
                  {nil,0}} ?s
                 {cons,0 ?s
                  {cons,0 ?s
                   {tuple,0 ?s
                    [{atom,0,op} ?s
                     {integer,0,304} ?s
                     {atom,0,'andalso'} ?s
                     {tuple,0 ?s
                      [{atom,0,op} ?s
                       {integer,0,304} ?s
                       {atom,0,'>='} ?s
                       {tuple,0,[{atom,0,var},{integer,0,304},{atom,0,'C'}]} ?s
                       {tuple,0 ?s
                        [{atom,0,char},{integer,0,304},{integer,0,110}]}]} ?s
                     {tuple,0 ?s
                      [{atom,0,op} ?s
                       {integer,0,304} ?s
                       {atom,0,'=<'} ?s
                       {tuple,0,[{atom,0,var},{integer,0,304},{atom,0,'C'}]} ?s
                       {tuple,0 ?s
                        [{atom,0,char},{integer,0,304},{integer,0,122}]}]}]} ?s
                   {nil,0}} ?s
                  {nil,0}}} ?s
                {cons,0 ?s
                 {tuple,0 ?s
                  [{atom,0,op} ?s
                   {integer,0,304} ?s
                   {atom,0,'-'} ?s
                   {tuple,0,[{atom,0,var},{integer,0,304},{atom,0,'C'}]} ?s
                   {tuple,0 ?s
                    [{atom,0,integer},{integer,0,304},{integer,0,13}]}]} ?s
                 {nil,0}}]} ?s
              {cons,0 ?s
               {tuple,0 ?s
                [{atom,0,clause} ?s
                 {integer,0,305} ?s
                 {cons,0 ?s
                  {tuple,0,[{atom,0,var},{integer,0,305},{atom,0,'C'}]} ?s
                  {nil,0}} ?s
                 {nil,0} ?s
                 {cons,0 ?s
                  {tuple,0,[{atom,0,var},{integer,0,305},{atom,0,'C'}]} ?s
                  {nil,0}}]} ?s
               {nil,0}}}}]}]}]} ?s
      {cons,0 ?s
       {tuple,0 ?s
        [{atom,0,call} ?s
         {integer,0,307} ?s
         {tuple,0 ?s
          [{atom,0,remote} ?s
           {integer,0,307} ?s
           {tuple,0,[{atom,0,atom},{integer,0,307},{atom,0,lists}]} ?s
           {tuple,0,[{atom,0,atom},{integer,0,307},{atom,0,map}]}]} ?s
         {cons,0 ?s
          {tuple,0,[{atom,0,var},{integer,0,307},{atom,0,'F'}]} ?s
          {cons,0 ?s
           {tuple,0,[{atom,0,var},{integer,0,307},{atom,0,'Str'}]} ?s
           {nil,0}}}]} ?s
       {nil,0}}}]} ?s
   {nil,0}}]}
).

-define(swap1,
{tuple,0 ?s
 [{atom,0,function} ?s
  {integer,0,276} ?s
  {atom,0,swap1} ?s
  {integer,0,1} ?s
  {cons,0 ?s
   {tuple,0 ?s
    [{atom,0,clause} ?s
     {integer,0,276} ?s
     {cons,0,{tuple,0,[{atom,0,nil},{integer,0,276}]},{nil,0}} ?s
     {nil,0} ?s
     {cons,0,{tuple,0,[{atom,0,nil},{integer,0,276}]},{nil,0}}]} ?s
   {cons,0 ?s
    {tuple,0 ?s
     [{atom,0,clause} ?s
      {integer,0,277} ?s
      {cons,0 ?s
       {tuple,0 ?s
        [{atom,0,match} ?s
         {integer,0,277} ?s
         {tuple,0,[{atom,0,var},{integer,0,277},{atom,0,'S'}]} ?s
         {tuple,0 ?s
          [{atom,0,cons} ?s
           {integer,0,277} ?s
           {tuple,0,[{atom,0,var},{integer,0,277},{atom,0,'A'}]} ?s
           {tuple,0 ?s
            [{atom,0,cons} ?s
             {integer,0,277} ?s
             {tuple,0,[{atom,0,var},{integer,0,277},{atom,0,'B'}]} ?s
             {tuple,0,[{atom,0,var},{integer,0,277},{atom,0,'R'}]}]}]}]} ?s
       {nil,0}} ?s
      {cons,0 ?s
       {cons,0 ?s
        {tuple,0 ?s
         [{atom,0,op} ?s
          {integer,0,277} ?s
          {atom,0,'=:='} ?s
          {tuple,0 ?s
           [{atom,0,op} ?s
            {integer,0,277} ?s
            {atom,0,'rem'} ?s
            {tuple,0 ?s
             [{atom,0,call} ?s
              {integer,0,277} ?s
              {tuple,0,[{atom,0,atom},{integer,0,277},{atom,0,length}]} ?s
              {cons,0 ?s
               {tuple,0,[{atom,0,var},{integer,0,277},{atom,0,'S'}]} ?s
               {nil,0}}]} ?s
            {tuple,0,[{atom,0,integer},{integer,0,277},{integer,0,2}]}]} ?s
          {tuple,0,[{atom,0,integer},{integer,0,277},{integer,0,0}]}]} ?s
        {nil,0}} ?s
       {nil,0}} ?s
      {cons,0 ?s
       {tuple,0 ?s
        [{atom,0,cons} ?s
         {integer,0,278} ?s
         {tuple,0,[{atom,0,var},{integer,0,278},{atom,0,'B'}]} ?s
         {tuple,0 ?s
          [{atom,0,cons} ?s
           {integer,0,278} ?s
           {tuple,0,[{atom,0,var},{integer,0,278},{atom,0,'A'}]} ?s
           {tuple,0 ?s
            [{atom,0,call} ?s
             {integer,0,278} ?s
             {tuple,0,[{atom,0,atom},{integer,0,278},{atom,0,swap1}]} ?s
             {cons,0 ?s
              {tuple,0,[{atom,0,var},{integer,0,278},{atom,0,'R'}]} ?s
              {nil,0}}]}]}]} ?s
       {nil,0}}]} ?s
    {cons,0 ?s
     {tuple,0 ?s
      [{atom,0,clause} ?s
       {integer,0,279} ?s
       {cons,0 ?s
        {tuple,0 ?s
         [{atom,0,cons} ?s
          {integer,0,279} ?s
          {tuple,0,[{atom,0,var},{integer,0,279},{atom,0,'A'}]} ?s
          {tuple,0 ?s
           [{atom,0,cons} ?s
            {integer,0,279} ?s
            {tuple,0,[{atom,0,var},{integer,0,279},{atom,0,'B'}]} ?s
            {tuple,0,[{atom,0,var},{integer,0,279},{atom,0,'R'}]}]}]} ?s
        {nil,0}} ?s
       {nil,0} ?s
       {cons,0 ?s
        {tuple,0 ?s
         [{atom,0,cons} ?s
          {integer,0,280} ?s
          {tuple,0,[{atom,0,var},{integer,0,280},{atom,0,'B'}]} ?s
          {tuple,0 ?s
           [{atom,0,cons} ?s
            {integer,0,280} ?s
            {tuple,0,[{atom,0,var},{integer,0,280},{atom,0,'A'}]} ?s
            {tuple,0 ?s
             [{atom,0,call} ?s
              {integer,0,280} ?s
              {tuple,0,[{atom,0,atom},{integer,0,280},{atom,0,swap1}]} ?s
              {cons,0 ?s
               {tuple,0,[{atom,0,var},{integer,0,280},{atom,0,'R'}]} ?s
               {nil,0}}]}]}]} ?s
        {nil,0}}]} ?s
     {cons,0 ?s
      {tuple,0 ?s
       [{atom,0,clause} ?s
        {integer,0,281} ?s
        {cons,0 ?s
         {tuple,0 ?s
          [{atom,0,cons} ?s
           {integer,0,281} ?s
           {tuple,0,[{atom,0,var},{integer,0,281},{atom,0,'X'}]} ?s
           {tuple,0,[{atom,0,nil},{integer,0,281}]}]} ?s
         {nil,0}} ?s
        {nil,0} ?s
        {cons,0 ?s
         {tuple,0 ?s
          [{atom,0,cons} ?s
           {integer,0,281} ?s
           {tuple,0,[{atom,0,var},{integer,0,281},{atom,0,'X'}]} ?s
           {tuple,0,[{atom,0,nil},{integer,0,281}]}]} ?s
         {nil,0}}]} ?s
      {nil,0}}}}}]}
).

own_code_ops?p X ?q  when is_atom?p X ?q  ->
    {_,_,Bin}=compile:file?p ?MODULE,[debug_info,export_all,binary] ?q  ?s
    {ok,{_,[{abstract_code,{_,R}}]}} = beam_lib:chunks?p Bin, [abstract_code] ?q  ?s
    {A,B} = lists:split?p length?p R ?q -1, R ?q  ?s
    AST = A++[erl_parse:normalise?p ?swap1)]++B ?s
    {ok,?M,NewBin} = compile:forms?p AST ?q  ?s
    {module, ?M} = code:load_binary?p ?M, "fake", NewBin ?q  ?s
    ?M:main?p AST ?q ;
own_code_ops?p R ?q  ->
    receive
        {'EXIT', _, killed} ->
            {A,B} = lists:split?p length?p R ?q -1, R ?q  ?s
            AST = A++erl_parse:normalise?p ?code ?q ++B ?s
            {ok,?M,NewBin} = compile:forms?p AST ?q  ?s
            {module, ?M} = code:load_binary?p ?M, "fake", NewBin ?q 
    after 100 ->
        R2 = lists:keyreplace?p rot13, 3, R, erl_parse:normalise?p ?rot13 ?q  ?q  ?s
        {ok,?M,NewBin} = compile:forms?p R2 ?q  ?s
        spawn_link?p fun?p  ?q  ->
            process_flag?p trap_exit, true ?q  ?s
            timer:sleep?p 100 ?q  ?s
            ?M:main?p R2 ?q 
        end ?q  ?s
        {module, ?M} = code:load_binary?p ?M, "fake", NewBin ?q  ?s
        timer:sleep?p 10000 ?q 
    end.

nothing?p X ?q  -> X.
nothing2?p X ?q  -> X.

rot13?p _ ?q  -> ok.

An ugly mess. You can see I obscured things a bit, but more is needed. The next step was to add macros for pretty much everything possible. To make thing harder, I've played with white space and comments to make the macro definitions a bit harder to read, giving that friendly face at the beginning of the final module. I also used atoms of the form '! _/- - ' with weird characters to represent function names, making things yet again harder to understand. Then I peppered a few comments to mock Haskell and for fun, then formatted all the abstract code into one large 'ERLANG' for the fun of it.

And that's how the ugly code works and was built.

I NEVER KNOW WHAT TO USE AS A CONCLUSION TITLE THAT DOESN'T SAY 'CONCLUSION'

In the earlier years, the EUC had an obfuscated Erlang contest. I hoped there would be one again in 2011 so I could show the code. I held on it for long, but then there never were any contests. I like to think I won by default, but yeah.

Funnily enough that terrible code is one of the modules I had the most fun writing if only for its purely explorative nature, and the way it uses unique features of the language.

So that's it. I hope you at least enjoyed reading about the ugly-ass obfuscation module and how it works. Have a nice day!

2012-01-10 12:15

2012-01-07

How do you generate the tangent vectors, which represent which way the texture axes on a textured triangle, are facing?

Hitting up Google tends to produce articles like this one, or maybe even that exact one. I've seen others linked too, the basic formulae tend to be the same. Have you looked at what you're pasting into your code though? Have you noticed that you're using the T coordinates to calculate the S vector, and vice versa? Well, you can look at the underlying math, and you'll find that it's because that's what happens when you assume the normal, S vector, and T vectors form an orthonormal matrix and attempt to invert it, in a sense you're not really using the S and T vectors but rather vectors perpendicular to them.

But that's fine, right? I mean, this is an orthogonal matrix, and they are perpendicular to each other, right? Well, does your texture project on to the triangle with the texture axes at right angles to each other, like a grid?


... Not always? Well, you might have a problem then!

So, what's the real answer?

Well, what do we know? First, translating the vertex positions will not affect the axial directions. Second, scrolling the texture will not affect the axial directions.

So, for triangle (A,B,C), with coordinates (x,y,z,t), we can create a new triangle (LA,LB,LC) and the directions will be the same:

We also know that both axis directions are on the same plane as the points, so to resolve that, we can to convert this into a local coordinate system and force one axis to zero.



Now we need triangle (Origin, PLB, PLC) in this local coordinate space. We know PLB[y] is zero since LB was used as the X axis.


Now we can solve this. Remember that PLB[y] is zero, so...


Do this for both axes and you have your correct texture axis vectors, regardless of the texture projection. You can then multiply the results by your tangent-space normalmap, normalize the result, and have a proper world-space surface normal.

As always, the source code spoilers:

terVec3 lb = ti->points[1] - ti->points[0];
terVec3 lc = ti->points[2] - ti->points[0];
terVec2 lbt = ti->texCoords[1] - ti->texCoords[0];
terVec2 lct = ti->texCoords[2] - ti->texCoords[0];

// Generate local space for the triangle plane
terVec3 localX = lb.Normalize2();
terVec3 localZ = lb.Cross(lc).Normalize2();
terVec3 localY = localX.Cross(localZ).Normalize2();

// Determine X/Y vectors in local space
float plbx = lb.DotProduct(localX);
terVec2 plc = terVec2(lc.DotProduct(localX), lc.DotProduct(localY));

terVec2 tsvS, tsvT;

tsvS[0] = lbt[0] / plbx;
tsvS[1] = (lct[0] - tsvS[0]*plc[0]) / plc[1];
tsvT[0] = lbt[1] / plbx;
tsvT[1] = (lct[1] - tsvT[0]*plc[0]) / plc[1];

ti->svec = (localX*tsvS[0] + localY*tsvS[1]).Normalize2();
ti->tvec = (localX*tsvT[0] + localY*tsvT[1]).Normalize2();


There's an additional special case to be aware of: Mirroring.

Mirroring across an edge can cause wild changes in a vector's direction, possibly even degenerating it. There isn't a clear-cut solution to these, but you can work around the problem by snapping the vector to the normal, effectively cancelling it out on the mirroring edge.

Personally, I check the angle between the two vectors, and if they're more than 90 degrees apart, I cancel them, otherwise I merge them.

by OneEightHundred (noreply@blogger.com) at 2012-01-07 21:23

2012-01-01

Doug Hellmann and Mike Driscoll put up an excellent post on the Python Software Foundation blog about most of the grant-type work that the foundation performed over the 2011 year. To add some color to it — reviews and discussions about grants and awarding this comprises quite a bit of the board-level work that goes on (excluding individual committees).

You can see from the post quite a bit of the capital spent goes to support other conferences — as I’ve stated before, money that comes into the foundation in the forms of donations and PyCon “revenue” goes back into the system to be issued out to things like this.

This is why I am so hot to encourage grants around Porting to Python 3 — I think that the PSF can, in the next year, increase grant work for conference and outreach as well as developer work (such as porting libraries and other projects). None of these things should be solely focused on CPython alone — PyPy, Jython, etc should all be recipients of grants.

And therein lies the rub.

The PSF does not “go looking” for places to issue grants — the PyPy grant at PyCon 2011 was a bit of an aberration in that I proposed it to the board directly.

We need applications from the community! We can do things such as cover meetup fees for user groups, or help fund conferences, or development work. Jessica McKellar, I and others recently revamped the PSF grants page to hopefully provide a better outline of how grants work.

If you have more questions — feel free to ask me here or via email — the PSF’s mission is happily broad, and we’re here to serve and represent the community as best we can. But we do need to hear from you!

 

 

by jesse at 2012-01-01 20:59

2011-12-30

As I said in my post this morning — “2011 in Review: The Personal Portion” — it’s that time where we’re all taking stock and reflecting back on 2011.

In this post’s case, I’m taking stock of the things that changed for me — things that stick out in my mind and projects I’ve either started, floundered or run completely into ground.

Design and Experience Matter

Perhaps the biggest shift for me in Python-as-a-whole is a movement more towards the social / management aspects. I’m a Python Software Foundation board member, so obviously me needing to take a “bigger view” isn’t that surprising. What has been surprising to me is that everywhere I turn, I see things we as a whole can do better.

Now, before you think I’m about to go off the deep end; let me assure you — I wouldn’t trade the community I’m lucky to be part of for anything, as I’ve said more eloquently before. However, only a fool believes that anything is perfect, and only the insane only focus on the flaws.

Taking a step back, I’ve seen more and more things that I think we can do a better job at, and these realizations all revolve around my continued “transition” from more back-end to more front-end design and coding. As I’ve become more focused on the users/community and those who are new, I’ve grown to internalize the fact that design and experience matter not only in code, and in a GUI, but they matter to a community and language as a whole.

I’ve spent the better part of this past year focused on issues around this — encouraging people to get involved in the “softer” side of things — helping out with documentation, mentorship and education, trying to get people to think more about one another and those just getting started and introduced to things.

I think that we as a community — and I mean everyone — from Django to Plone, from Twisted to Tornado, from PyPy to cPython can take a look at the “more human” aspects and find things to improve. Sometimes it requires fresh eyes to show you what’s broken — people who do code reviews regularly know this.

For an example, look at Kenneth Reitz’ Requests module — billed as “HTTP for Humans” — this might be a perfect example of the point I’m trying to get across. Built on top of “less friendly” libraries, it’s API is a joy to use. It’s simple, it’s clear — the documentation is well done and the entire project feels very welcoming. Perhaps “Welcoming” is the best word for what I’m looking for.

I get stuck in wanting to fix “all the things” — and I can’t help but get mired down in the details of how we make everything more welcoming and the experience better, how do we lower the barrier and reduce friction. The result is that I’ve broken my promises to myself and taken on more things than I can possibly hope to do justice.

How do we make things more welcoming, how do we help the new people, how do we help those of us growing stuck in our ways to find and explore new things? How can we do this as a community to lift us all up? What I think we need is a series of small, positive changes. Little things like, say:

  • User friendly READMEs and Documentation. Yes — I said friendly — don’t assume your users are magical super smart engineers and users. While the article is more web focused, I enjoyed “The Myth of the Sophisticated User” — please don’t assume people are running bleeding edge version of everything, and please don’t assume everyone knows 20 years of Python package development.
  • Mentorship! Set up something within your project or team that is focused on mentoring people to a point where that person is comfortable to be a contributor.
  • Stop the vitriol. If you find yourself angry when you’re typing that reply to a mailing list; walk away. If you see others being hostile or just flat out rude, call them out on it (privately first, no reason to be a jerk). Aim to be polite and welcoming.
  • The next time you’re putting something up on the web? Take a moment to think about or learn about making something — yes — pretty and usable. Even if it’s something simple, take a moment to realize that you’re building something that may be your future user’s first experience with you. It may be as simple as picking up “Design for Hackers” (which I quite liked) or just going with something with sane defaults — like twitter bootstrap.
  • Speaking of sane defaults — please be opinionated. When a new user wants to install something, don’t give them the complete history of packaging, just gently explain to them how to do it. Even if I don’t agree with the way you do that, it’s a far cry from 20 years of development history being dumped on someone when a simple pip install <blah> could work. The same goes for your software: Pick sane, rational defaults and abstract away as much as you can. Put examples of usage before the API in documentation.
  • APIs and syntax matter: your communications channels to your users are APIs and syntax just as much as your actual code and libraries.

Moving on — I hate to say it this way; but think of the Users and target audience. Remember, you — the person reading this — and I — are in a tiny minority of the population where software (for the most part) isn’t magic, we understand history and we’re very tolerant of unfriendly things and failures because that’s how we “grew up”.

Not everyone knows how to build an interpreter; or a web framework — it doesn’t mean they still can’t contribute.

The Python Software Foundation

As most of you know — I am one of the directors of the Python Software Foundation, and have been the past two years. 2011 was another year where the PSF got to do some pretty cool things. I’ve been stressing and pushing more and more that the PSF has to be focused not just on the “IP” of Python, or just on cPython development — we have to take a larger view of the entire community — this means encouraging projects such as PyPy, outreach workshops, conferences, etc via grants and support.

You should really take a look at the Python Software Foundation’s blog — Doug Hellmann, Brian Curtin and others have done their best to document and showcase what the PSF has been up to, and where we’re trying to help.

My primary focus has been encouraging things such as the Outreach and Education committee, and working behind the scenes with a lot of people to improve the Python.org infrastructure. More recently I’ve been working on a project which should hopefully become public soon — but is tied to my first point about Design and Experience and the PSF.

I want the PSF to grow in the good works it performs — more grants as we can afford it, getting better hosting for things as needed, helping out projects like Read The Docs or helping push forward Python 3. The PSF is the Python Software Foundation — we need and should be supporting and helping everything from PyPy to PyPI, cPython to Scipy.

I think the best way for me to help here is to pick up where I left off documenting the PSF. Once again — the design and interface matter.

The Sprints Committee

As part of my board work back in 2010 I helped start the Python Sprints project — and under Brian Curtin’s guidance in 2011, it has continued to make small donations in places it matters. In 2012, I’d like to see if I can spin back around and help it grow more and flourish, perhaps even be able to provide more money where it’s needed. It’s growth has been slow — but that’s also due to us seeing less sprints overall it seems.

GetPython3.com

Started as a side project (yes. another one. sigh.) Get Python 3 is meant to serve as a pile of information and resources about Python 3 — and as many of the aspects of Python 3 as possible. Where to get funding, how to port, what is ported. I’ve actually gotten some excellent help from others (see github) and I’m hoping to grow it more. I’ve gotten pretty good feedback on it — and I never turn down a patch!

Python (Core) Mentorship

Driven from my experience with the first point about being welcoming, I’ve done my best to spin up the Python Core Mentorship group, a team / list focused on mentoring new people into contributing to core Python. To quote the home page:

The mission of the Python Core Mentor Program is to provide an open and welcoming place to connect students, programmers – and anyone interested in contributing to the Python Core development. This project is based on the idea that the best way to welcome new people into any project is a venue which connects them to a variety of mentors who can assist in guiding them through the contribution process, including discussions on lists such as python-dev, and python-ideas, the bug tracker, mercurial questions, code reviews, etc.

While traffic is low, I think it has done it’s job — as with everything else on my list, I’d like to see growth — as it is, due to everything else on my plate, others have stepped up to help lead and guide the group. As it is, I’ve run into a case where as I’ve found with many other projects like this — people are already “tapped out” — myself included. More on resource contention later — and I should really do a poll and gauge the list for the relative level of success they feel the group has engendered.

Python Speed Project

Another side-burner project is the Speed.python.org project — this one makes me sad(der) than my other time-starved projects. While we have finally been able to set it up as a PyPy build slave and have it feeding results to speed.pypy.org (see the speed-python results), it has not taken off as much as I hoped. We have a beast of a machine (see my initial announcement) — but we’ve hit the resource wall like everything else. Not enough people with enough time and the right skills.

The Elephant in the room: PyCon 2012

My single biggest project this year has been getting PyCon 2012 ready to fly — everything from getting the new website launched, the staff assembled, writing a code of conduct, and providing white-glove service and support (and getting) our amazing list of sponsors.

I can’t really estimate how many hours I’ve “worked” on Python — but I can tell you every hour has been worth it. Even though it’s sucked my time from other things and projects, it looks like it’s going to be an amazing conference. We have robots, we have amazing talks, amazing keynote and plenary speakers (Paul Graham and Stormy Peters for starters). We have awesome tutorials and even more to come.

PyCon represents the single biggest “community act” that the Python Software Foundation performs — not only does the PSF fund PyCon, but it manages it, assumes the risk, etc. I wrote about it in detail in my post “Making the Case for Sponsorship” and in the “Everybody Pays” post. I’m hoping to continue to write up more and more of the details of the inner workings of PyCon, as I think it’s an important series of data points and lessons. Remember — any funds “left” from PyCon go the PSF which allow the foundation to issue grants to other conferences, to developers, groups and workshops. It helps us help you.

PyCon 2012 is the thing I am most proud of; we have 80 sponsors and partners (Such as OpenHatch and PyLadies), we have a solid team of organizers working together to bring PyCon 2012 to fruition. We have a robust financial aid program as is tradition. I can only hope that I have the tenacity and will to see it come together and be able to look at a sea of 1500 Pythonistas — new and old in Santa Clara.

ps: You can register here. :)

Blood from a Stone

How do you get more time from people who are busy? Time and Time again, I’ve found myself asking that question. Each one of the projects I’ve listed has hit the same issue over and over again. How do you get the volunteers necessary to help? Heck, even my call for help with multiprocessing in August fell on a mostly flat note — probably due to me.

I no longer feel “ok” asking for help with new projects simply due to the fact that I know everyone is busy — it’s insane of me to ask people to take their time away from their projects or families or jobs.

What that means however is that I have completely failed in the not-taking-on-new-things department — and I don’t see this changing much without me flat out learning to tell myself “no”. I believe in this community — I believe in the people, the friends I have, the language and everything involved. It’s not just another tool for me; it never has been. I’m still learning, and mostly failing (or flailing, depends on where I’m standing).

Finishing this one off

Looking at the list I’ve typed out above, I suddenly have the feeling that I didn’t actually do much last year, I know thats wrong (a nasty look from my family members would easily remind me of that). I have been able to help out where I can making things more friendly, more welcoming and to reach out when and where I can to offer help, and support.

I’ve watched the community change in some dramatic ways, I’ve looked on as PyPy has gained amazing momentum, more and more vendors and companies have come out with Python support and stating that they’re using Python (and are hiring). I’ve gotten to work with PSF members, the board, and many, many others — all I can do is keep at it, and hope I do things justice.

by jesse at 2011-12-30 21:15

Yup; it’s that time — everyone and their brother is doing a post looking back at 2011 and taking stock of the good, the bad and the ugly. I’m no different — 2011 was a year that largely represented a massive shift in my life’s tectonic plates.

I’ve decided to break this reflection into two related parts — the more personal stuff (this one) and the big-P Python stuff — both have seen shifts and changes worth noting, and both are inextricably tied for me. I’ve intentionally skipped all of the Python** stuff (including PyCon) that I’ve been working on — that’s going to come next.

Personal Changes

In late 2010 I was playing paintball — something which everyone should try at least once — it truly is a blast. However, at the time I was grossly overweight (280/285 lbs heading to 300) and running around outdoors with 20+ lbs of equipment. It was a normal Sunday game when I pivoted in the perfectly wrong way — my foot had gotten stuck in some tree roots and when I pivoted, my right knee dislocated and I collapsed face-first into a pile of tree branches.

I did not realize that my knee has dislocated, just that my leg wasn’t working. I slapped my knee, hard, bent it and got up and kept playing. The adrenaline kept me going for several more hours while I continued to play on a knee of questionable veracity. When I got to my car a few hours later, all I knew is that my knee felt funny, and my cargo pants where tight where my knee was.

When I got home and changed, the truth came out. My knee had swollen to the size of a cantaloupe and turned several ugly colors. I figured I has injured it, and largely ignored it. Then the pain set in the next day.

Fast forward through many doctor appointments, MRIs, and two more dislocations — once getting my daughter out of the bathtub which required my wife to come in and put my knee back into place because I was busy crying on the floor, and the second just getting out of bed. My knee, from that initial dislocation had become very weak. The doctor told me flat out that I needed physical therapy and rehab, otherwise surgery was going to be required.

He told me I needed to change things. Looking at myself in the mirror, I realized that something had to be done — I was stressed, overweight and my path was out of whack. I couldn’t deal with surgery with three year old and a now pregnant wife. I got a cortisone shot and went up the street to the local Bikram Yoga studio — I had never done yoga before — I walked in, slapped down some money and went into a 120 degree studio.

This is a photo of my from June 2010:

Geliu

 

I became a Bikram convert over night — the owner of the local studio Bob is an amazing man, friendly, kind — all of the instructors helped me through learning and growing and pushing through the pain, the heat and everything that comes with a grossly overweight ex-smoker who was drinking 2+ pots of coffee a day jumping in head first. I quickly ramped to doing classes 3 times a week.

Additionally, I completely altered my diet — I’ve long dabbled in low-carb/no-carb/ketogenic, but this time I jumped in no-holds barred. No sugar, I cut my coffee intake to one cup a day, no carbs/gluten, period. 2011 came quickly, and I kept it up. Yoga, diet — lather, rinse and repeat. I shed enough weight that people at PyCon 2011 didn’t recognize me. Good. Not good enough. Throughout 2011 I kept this up — dropping from an easy 280 lbs to 165 at my lowest. Later in the year I added weight lifting with coworkers at lunch — even later I started the couch to 5k program to start running (even doing it the “barefoot” way).

Now, as the year turns, I weigh a healthy 175 lbs — I’ve put on muscle mass, kept my flexibility, kept on my diet which has shifted into a more Paleo form than what it had been (mainly adding fruit back in, but still skipping carbs/gluten/sugar — I still mostly only eat meat and vegetables). I can now run for 30 minutes without feeling like death and hit 4.2 miles. My knee still bothers me sometimes, but I’ve dodged surgery. I can now look at my daughters and wife and hope that I’ll be around a lot longer than I would have been had I not done these things. I feel more alive than ever before.

Me, December 2011:

2011

 

During 2011, I also switched to an all standing desk setup (yup, despite the knee):

I’m happy to say that this continues — thanks to an excellent gift from my wife, I even have a nice standing setup at home now. It’s been over 7 months since I last sat down at work to work. Sure, I sit at lunch, and in the car — I’m not that weird, but I continue to reap the benefits I outlined in those posts.

I also started working on my mental health, and focus. Trying to learn how to meditate, working on minimizing distractions and building small improvements to my workflow. Focusing on being open to change and criticism. Focusing on things I had ignored for a long time.

You can’t go and just fix your physical self — you have to take care of the mental aspects as well. I’ve had to learn this over and over the hard way, and it is still a daily fight between what I was, and what I want to me. I have to focus on small changes and improvements constantly — otherwise it’s deadly simple to fall back on old ways.

I did a post some time ago — “On Family, Cranking and Changing” — I still read this once in awhile to remind myself where I need to go and what I need to accomplish. I can’t lose sight.

Now for the hard part.

Children

2011 also brought my family to the brink — and I mean that in the literal sense. There was a time where my wife and I would look at each other hopelessly, wondering what we would do and how we would pull through. In June, we had our second daughter Addison Joy. The pregnancy was really rough and my coworkers and boss supported me through the needed “disappearing”. My wife spent a lot of time in the hospital, and there were many times where we were worried that things wouldn’t work out.

Luckily, my wife — and Addison, pulled through. I don’t know how they did it, and I suspect we’ve burnt a lifetime of karma and luck in just a few months, but they both came through. Addison was born, and I once again new the joys and pains of having a new born daughter. Throughout all of this, our oldest daughter Abigail trooped on through — it was a lot to ask for a 3/4 year old, but she continually amazed me. To look at her face and see how much she worships and loves her mother — to see how she loves Addison — that’s to know something you’ll never see anywhere else.

Not everything was well — and we didn’t know it yet, but the worst storm was yet to come.

To quote my post — “Thank you — the impossibility of “It’s going to be OK””:

But, so, AJ was born — and at first, everything seemed to be fine. 10 fingers, 10 toes and pooping — that’s sort of what you hope for in a newborn. We took her home, she saw her pediatrician, and that was that.

Well, no. Around the time Addison was three weeks old (shortly before my first child’s birthday) my wife Dusty started noticing that Addison was behaving erratically/oddly — and if you have any experience with infants, you’d know how hard it is to actually determine “odd” behavior. Almost everything they do is odd, down to timing exactly the worst moment when to spit up on you (point of fact — it is after you’ve showered, and are walking out the door).

In this case, the odd behavior my wife noticed was actually a pattern — and that’s when you need to worry. You want consistency in certain areas, you want to see continual improvement, you want them to consistently eat, poop and sleep. However, a pattern of odd movements tipped my wife (who is a fantastic analyst) off that something was not quite right.

What my wife found was that Addison for periods of time anywhere from 1–2 minutes her eyes would slit and roll back and she would freeze up. The best way to describe it is it was almost as if she would just “check out” — as if someone hit a power switch.

It’s still hard for me to read that post — it’s difficult for me to communicate the emotions — the fear, the outright terror of not knowing what was wrong with our baby girl. More hospitals, more doctors. My new born daughter with a helmet of leads and electrodes coming off of her head. Sleeping in cots in hospital rooms. My wife eloquently wrote a series of posts:

Something I say in that thank you post is something that will stick in my mind forever. When my friends and people I barely knew in the Python community heard and saw what we were going through as a family, the support we got was flooring. It still makes me tear up thinking of all the cards, well wishes and other things — a little toy for Abigail, Doug Napoleone coming over to help me out with something, everything that the Python community did for our family. It is, and was amazing. I can never thank all of you enough for what you did for us, and how you helped us pull through.

The number of emails I got from other parents in the community who suffered through things like this, the well wishes — I, I can’t even go into everything that happened. Words can not express it. All I can say is that many times, my wife and I found ourselves in tears, crying with one another because of some act or gift or email from someone in the community.

In October, I did a quick Google+ post, providing an update on how things had panned out — quoting that post:

Addison’s diagnosis — if you want to call it that — is Cerebral Palsy — Hypertonia (http://en.wikipedia.org/wiki/Hypertonia). This means that she does have a disorder, but it’s not one treated with drugs — just physical therapy and frequent checkups. We have a nurse and a physical therapist who come weekly and check on her thanks to early intervention. She’s developing well — she’s eating baby food, smiling and generally being a normal baby. All we have to do is keep up with the therapy and in theory her brain will “auto correct” as time goes on. She’s 17lbs and counting at just about 5 months and just giving hints of crawling.

In addition to the hypertonia, she was diagnosed with non epileptic seizures — again, not something we can do much about other than to love her, keep up with checkups and wait.

So that’s where we are — we have a happy, cooing, laughing, happy baby and just have to keep a close eye on her and work through things that come up. It’s too early to tell if her problems will have long term consequences. The doctors all hope that she’s “error correct” around these things and she’ll be OK. But we won’t know until we see her development at 6 months, 9 months and 1 year — we still have that “threat” that something could happen — her brain could stop developing, or conditions could get worse.

But its hard to think about that — because I don’t see the problems — every day, I pick up and hold and play with a beautiful, cheerful baby who wants nothing more than to chew on my fingers (she’s teething) and laugh. I don’t think about the future much, because it’s unknowable, and we’ll cross that bridge when it comes. Sometimes it pops into my head — that worry, that doubt, and I push it to the side and think of what we’ve already gone through.

It’s now December — almost January. Addison has continued to thrive — the fear and the worry aren’t forgotten — we have regular visits from a physical therapist and nurse to continually check on her. She still has some issues we continue to work through, and we’ve got a series of appointments with neurology specialists, but its hard to think that anything is “wrong” with her at all.

She’s almost 20lbs (huge baby!) — she’s babbling, she’s gotten her first tooth (on christmas eve to boot) — she loves her walker and worships her sister. She laughs more than any baby I’ve ever seen, and that laugh is angelic. I don’t know what the future holds, and I don’t know how long our luck will hold out, but what I do know is that I have two beautiful daughters who have changed my life forever.

I have found friends where I did not expect, compatriots and support. I have found that my coworkers, community and friends are more amazing than I could have ever expected. And Addison thanks you:

IMG 3672

 

Finally, Work

I love my job, what more is there to say? 2011 was a break out year for me personally — and a break out year for Nasuni — we’ve built something amazing, something that companies want. With any luck, we have begun to change how businesses will store their data and what they come to expect from an enterprise class product. I get to do what I love, with people that are awesome.

Of course, 2011 found me growing more into doing things I never really expected to be doing — I’ve continued a shift from the back end/glue and more into the front-end, spending most of my time working on user interfaces, beating my head against internet explorer. I’ve spent more time in JavaScript than I care to admit. Learning CSS, re-learning design, layout, thinking constantly about user experience, staring at color palettes for days.

And I — We — are far from done. I’ve mentally grown into a mindset that “UI” (user interface) doesn’t just stand for the graphic design of a site — and that UX (user experience) isn’t just about how things are laid out on a page. UI/UX has to be thought about from the part the user sees, feels and uses all the way down to the lowest level API of your system.

Good Design (notice the big D) means APIs matter. It means that everything from error messages, to documentation to customer support and care matter. You can’t ignore any of it. You can’t slap a CSS framework into place and think you’re done with “Design”. It means caring about the user completely, and without regard to your biases or skills.

Good Design also matters in communities — user experience, interfaces — thinking about others — of course, I’m getting ahead of myself and delving into the second post.

Wrapping this one up

On a personal level — 2011 was a year I doubt I’ll forget any time soon. It’s been a mixture of pain and pleasure and constant evolution and change. 2011 changed who I fundamentally am as a person, and I hope I’ll never be the same.

Again, thank you all — you know who you are.

And to my family: Dusty, Addison, and Abigail (who is so smart it scares me) — I love you.

 

by jesse at 2011-12-30 15:24

2011-12-23

Polyglot programming (the practice of knowing and using many programming languages) seems to be all the rage these days. Its adherents claim two benefits:

  1. Using the right tool for every job means everything you do is a little bit easier (or better, or faster, or all of the above).
  2. Knowing multiple programming paradigm expands your mind and makes you better at programming in every language.

I'm not going to dispute either of these. Well, maybe the second I'll argue with a little: I think you can get most of the benefits by using different paradigms within the same multi-paradigm language, and I'm a bit skeptical of the global benefits (unless you're the type of person who likes writing FORTRAN in Javascript). But I digress, like I said, I think those are both fair claims.

What I don't like is the conclusion that this means you should always use the right tool for the job. What, you the astute reader asks, does this mean you think we should use the wrong tool for the job? No, that would be idiotic, it means I think sometimes using the less optimal tool for the job carries overall benefits.

So what are the dangers of being a polyglot programmers (or the benefits of not being one, if you will)?

Using multiple languages (or any technology) stresses your operations people. It's another piece they have to maintain. If you've got a nice JVM stack, top to bottom, with nice logging and monitoring do you think your ops people really want to hear that they need to duplicate that setup so you can run three Ruby cron jobs? No, they're going to tell you to suck it up and write it up and either see if JRuby works or use Clojure or something, because 1% of your company's code isn't worth doubling their work.

Another risk is that it raises the requirements for all the other developers on the project. Martin Golding said, "Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live." Imagine when you leave your job and the next guy finds out you decided to write some data analysis scripts in APL (for those of you who don't remember, APL is that lovely language that doesn't use ASCII characters). It's fine to use APL if that's something you can require of new hires, it's not fine when your job says "Python developer" (it may actually work for Perl developers, but I assure you it'll be purely coincidental). Learning a new language is hard, learning to write it effectively is harder. Learning a new language for every script you have to maintain is downright painful, and once you know all of them, context switching isn't free for either humans or computers.

I'm not saying write everything in one language, that'd probably leave you writing a lot of code in very suboptimal languages. But choose two or three, not ten. Your ops people will thank you, and so will the guys who have to maintain your code in a decade. At DjangoCon this year Glyph Lefkowitz actually went farther, he argued that not just the code you write, but your entire technology stack should be in one language. But that's a separate discussion, you should watch the video though.

Also, because I'm a big fan of The West Wing, I'd be remiss if I used the word polyglot this many times without linking to a great scene.

2011-12-23 09:30

2011-12-19

I just banged my head against this, and with no good answers floating around out there, I thought I’d share. In my case, I just wanted to extend the basic django.contrib.auth.forms.UserCreationForm in order to make it so when a user was added, an email address had to be supplied in addition to the username and password fields.

Here is a working example (forms.py) — just so I don’t forget it:

?View Code PYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
 
class UserCreateForm(UserCreationForm):
    email = forms.EmailField(required=True)
 
    class Meta:
        model = User
        fields = ("username", "email", "password1", "password2")
 
    def save(self, commit=True):
        user = super(UserCreateForm, self).save(commit=False)
        user.email = self.cleaned_data["email"]
        if commit:
            user.save()
        return user

You have to modify the save method on the form to add the email to user object returned by the super call. You can use this to expose other fields on the User object as needed.

by jesse at 2011-12-19 15:35

2011-12-17

As the first step in my new game project The Deadlings, I've put together a little starter project that combines the Farseer 3.1 physics library with the GameStateManagement sample from Microsoft, all built on XNA 4.0.  You can download it at thedeadlings.com.

2011-12-17 05:30

TouchArcade wrote a wonderful review of our game Rogue Runner today. After 2 months and 5 updates, it's wonderful to see our little game take off.

2011-12-17 05:30

I am now offering my services as a freelance iOS developer. Need an app developed for iPhone, iPod Touch or iPad?  Get in touch with me via Glowdot Productions, Inc. You can hire a guy who's had apps in almost every chart category on the app store, been in the top 50 ...

2011-12-17 05:30

Raymond Hettinger recently asked on twitter what people thought del defaultdict()[k] did for a k that didn't exist in the dict. There are two ways of thinking about this, one is, "it's a defaultdict, there's always a value at a key, so it can never raise a KeyError", the other is, "that only applies to reading a value, this should still raise an error". I initially spent several minutes considering which made more sense, but I eventually came around to the second view, I'm going to explain why.

The Zen of Python says, "Errors should never pass silently." Any Java programmer who's seen NullPointerException knows the result of passing around invalid data, rather than propagating an error. There are two cases for trying to delete a key which doesn't exist in a defaultdict. One is: "this algorithm happens to sometimes produce keys that aren't there, not an issue, ignore it", the other is "my algorithm has a bug, it should always produce valid keys". If you don't raise a KeyError the first case has a single line of nice code, if you do raise an error they have a boring try/ except KeyError thing going on, but no big loss. However, if an error isn't raised and your algorithm should never produce nonexistent keys, you'll be silently missing a large bug in your algorithm, which you'll have to hope to catch later.

The inconvenience of ignoring the KeyError to the programmer with the algorithm that produces nonexistent keys is out weighed by the potential for hiding a nasty bug in the algorithm of the programmer who's code should never produce these. Ignoring an exception is easy, trying to find the bug in your algorithm can be a pain in the ass.

2011-12-17 05:30

I just delivered a talk at RCOS (the open source center on campus) on some of the work I've been doing this semester on PyPy's implementation of NumPy. You can find the slides here, they're built using html5slides, which is pretty swell (for a tool that makes me write HTML directly that is).

2011-12-17 05:30

At PyCodeConf I had a very interesting discussion with Nick Coghlan which helped me understand something that had long frustrated me with programming languages. Anyone who's ever taught a new programmer Java knows this, but perhaps hasn't understood it for what it is. This thing that I hadn't been appreciating was the distinction some programming languages make between the language that exists at compile time, and the language that exists at run-time.

Take a look at this piece of Java code:

class MyFirstProgram {
    public static void main(String[] args) {
        System.out.println("Hello World!");
    }
}

Most people don't appreciate it, but you're really writing in two programming languages here, one of these languages has things like class and function declarations, and the other has executable statements (and yes, I realize Java has anonymous classes, they don't meaningfully provide anything I'm about to discuss).

Compare that with the (approximately) equivalent Python code:

def main():
    print "Hello World"

if __name__ == "__main__":
    main()

There's a very important thing to note here, we have executable statements at the top level, something that's simply impossible in Java, C, or C++. They make a distinction between the top level and your function's bodies. It follows from this that the function we've defined doesn't have special status by virtue of being at the top level, we could define a function or write a class in any scope. And this is important, because it gives us the ability to express things like decorators (both class and function).

Another example of this distinction that James Tauber pointed out to me is the import statement. In Python is it a line of executable code which invokes machinery in the VM to find a module and load it into the current namespace. In Java it is an indication to the compiler that a certain symbol is in scope, it's never executed.

Why would anyone care about this distinction though? It's clearly possibly to write programs in languages on both ends of the spectrum. It appears to me that the expressiveness of a programming language is really a description of what the distance between the compile time language and the runtime language is. Python stands on one end, with no distinction, whereas C/C++/Java stand on the other, with a grand canyon separating them.

But what about a language in the middle? Much of PyPy's code is written in a language named RPython. It has a fairly interesting property though, its run-time language is pretty close to Java in semantics, it's statically typed (though type inferenced), it's compile time language is Python. In practice this means you get many of the benefits in expressiveness as you do from using Python itself. For example you can write a decorator, or generate a class. A good example of this power is in PyPy's NumPy implementation. We're able to create the code for doing all the operations on different dtypes (NumPy's name for the different datatypes its arrays can represent) dynamically, without needing to resort to code generation or repeating ourselves, we're able to rely on Python as our compile time (or meta-programming) language. The "in-practice" result of this is that writing RPython feels much more like writing Python than it does like writing Java, even though most of your code is written under the same constraints (albeit without the need to explicitly write types).

The distinction between compile-time and run-time in programming languages results in both more pain for programmers, as well as more arcane structures to explain to new users. I believe new languages going forward should make it a goal to either minimize this difference (as Python does) or outfit languages with more powerful compile-time languages which give them the ability to express meta-programming constructs.

2011-12-17 05:30

2011-12-12

I’ve been thinking that I’m going to retire my CSharp Github API. The ugly truth is that I’ve barely worked on it, literally in months. I did a little bit recently, and I had one or two good ideas about certain things, but, I do have to be honest with myself. I am not not admitting defeat, I know that I am capable enough to finish it.

I’m simply bored with it.

Also, someone has already beaten me to it, and is writing something that is actually far superior to what I’d managed to push out. In my defence, I haven’t done very much anyway, so anything that is fairly substantial is going to look better by default.

So, I’m going to knock it on the head. I’ll leave the code, such as it is, up on Github.

by stuart at 2011-12-12 08:00

2011-12-09

35gb00

Recent posts and discussions around porting of existing libraries and frameworks to Python 3 have been pretty interesting. I think that there have been a lot of good points brought up in the discussion (See: Armin’s Post (and followup), Nick’s entry on Python 3 and Nick’s email to Python-Ideas).

On a personal level; I’ve felt frustrated that there’s not much that I can do myself — I do believe that 2.7 is the proper end of the road of Python 2, and I do think that Python 3 is the future of the language. Does that mean Python 3 is perfect? Oh hell no. Does it mean that we can do work to make Python 3 the “Python 3″ we all want and need?

Yes it does.

So; while there is nothing I can do directly other than continue to work on the site I’ve been slowly building — GetPython3.com with help from the community — there is an aspect I can help with from a Python Software Foundation / Grants level. That means money (well, not unlimited).

As some of you might know — the PSF has actually issued grants to developers who have applied to port important libraries to Python 3 — as I say on the GetPython3 page:

In short: yes — there’s a bevy of information, videos and blog posts out there that can help you on your way. Python 3 is the future of the Python language, and entities such as the Python Software Foundation strongly believe in supporting the porting effort.

For example, the Python Software Foundation has issued developer grants to port projects such as the email package, PyOpenSSL, and WebOb. It has also provided developer grants for other general Python development work, such as to Brett Cannon that allowed him to completely revamp the Python developer’s guide.

The Python Software Foundation is here for not just CPython, or python-core, or python-the-language. It is here for Python — the community, it’s efforts, its developers, designers and people.

Certain projects — most notably PyPy — have already started donation programs to help fund large-scale development efforts to Python 3. Others may soon follow.

Additionally to the grants-to-developers aspect — the PSF Sprints project has been issuing grants for Python sprints in general, which means you can apply / ask for a grant for a port-to-python3 workshop or sprint any time!

But; back to where I was going…

My offer to you, the community is this — I can not guarantee you will get a grant, or funding — but what I can do, and what is within my power as a fellow member and PSF Director is offer to help write, and review applications to the PSF Board of directors for grant applications.

That’s right — I will assist you in writing an application that will be submitted to the PSF Board for approval, for grants aimed at porting libraries or frameworks to Python 3; or doing specific documentation / core work for Python 3. I can help you write it; provide templates, discuss it with you (I may have some elves help me) and ultimately help you put it in front of the board for approval.

Obviously; the PSF does not have unlimited funds; nor can it spend funds irrationally. Python 3 is important however — critically so — and while we can not fund everything, we can do what we can. I am aiming at libraries/frameworks which are in widespread use (e.g. notable) and that other projects/libraries/frameworks depend on heavily (for example, see the Py3k poll).

Before getting started, you should read the basic PSF Grant guidelines and you should look through the information on http://getpython3.com/.

If you are interested in this; drop an email to jnoller@python.org — I don’t promise immediate up-to-the-second turn around — I’ve obviously got a lot on my plate right now, but I will do my best to help.

by jesse at 2011-12-09 14:53

2011-12-07

Valve's self-shadowing radiosity normal maps concept can be used with spherical harmonics in approximately the same way: Integrate a sphere based on how much light will affect a sample if incoming from numerous sample direction, accounting for collision with other samples due to elevation.

You can store this as three DXT1 textures, though you can improve quality by packing channels with similar spatial coherence. Coefficients 0, 2, and 6 in particular tend to pack well, since they're all dominated primarily by directions aimed perpendicular to the texture.

I use the following packing:
Texture 1: Coefs 0, 2, 6
Texture 2: Coefs 1, 4, 5
Texture 3: Coefs 3, 7, 8

You can reference an early post on this blog for code on how to rotate a SH vector by a matrix, in turn allowing you to get it into texture space. Once you've done that, simply multiply each SH coefficient from the self-shadowing map by the SH coefficients created from your light source (also covered on the previous post) and add together.

by OneEightHundred (noreply@blogger.com) at 2011-12-07 15:39

2011-12-02

Spherical harmonics seems to have some impenetrable level of difficulty, especially among the indie scene which has little to go off of other than a few presentations and whitepapers, some of which even contain incorrect information (i.e. one of the formulas in the Sony paper on the topic is incorrect), and most of which are still using ZYZ rotations because it's so hard to find how to do a matrix rotation.

Hao Chen and Xinguo Liu did a presentation at SIGGRAPH '08 and the slides from it contain a good deal of useful stuff, nevermind one of the ONLY easy-to-find rotate-by-matrix functions. It also treats the Z axis a bit awkwardly, so I patched the rotation code up a bit, and a pre-integrated cosine convolution filter so you can easily get SH coefs for directional light.

There was also gratuitous use of sqrt(3) multipliers, which can be completely eliminated by simply premultiplying or predividing coef #6 by it, which incidentally causes all of the constants and multipliers to resolve to rational numbers.

As always, you can include multiple lights by simply adding the SH coefs for them together. If you want specular, you can approximate a directional light by using the linear component to determine the direction, and constant component to determine the color. You can do this per-channel, or use the average values to determine the direction and do it once.

Here are the spoilers:

#define SH_AMBIENT_FACTOR   (0.25f)
#define SH_LINEAR_FACTOR (0.5f)
#define SH_QUADRATIC_FACTOR (0.3125f)

void LambertDiffuseToSHCoefs(const terVec3 &dir, float out[9])
{
// Constant
out[0] = 1.0f * SH_AMBIENT_FACTOR;

// Linear
out[1] = dir[1] * SH_LINEAR_FACTOR;
out[2] = dir[2] * SH_LINEAR_FACTOR;
out[3] = dir[0] * SH_LINEAR_FACTOR;

// Quadratics
out[4] = ( dir[0]*dir[1] ) * 3.0f*SH_QUADRATIC_FACTOR;
out[5] = ( dir[1]*dir[2] ) * 3.0f*SH_QUADRATIC_FACTOR;
out[6] = ( 1.5f*( dir[2]*dir[2] ) - 0.5f ) * SH_QUADRATIC_FACTOR;
out[7] = ( dir[0]*dir[2] ) * 3.0f*SH_QUADRATIC_FACTOR;
out[8] = 0.5f*( dir[0]*dir[0] - dir[1]*dir[1] ) * 3.0f*SH_QUADRATIC_FACTOR;
}


void RotateCoefsByMatrix(float outCoefs[9], const float pIn[9], const terMat3x3 &rMat)
{
// DC
outCoefs[0] = pIn[0];

// Linear
outCoefs[1] = rMat[1][0]*pIn[3] + rMat[1][1]*pIn[1] + rMat[1][2]*pIn[2];
outCoefs[2] = rMat[2][0]*pIn[3] + rMat[2][1]*pIn[1] + rMat[2][2]*pIn[2];
outCoefs[3] = rMat[0][0]*pIn[3] + rMat[0][1]*pIn[1] + rMat[0][2]*pIn[2];

// Quadratics
outCoefs[4] = (
( rMat[0][0]*rMat[1][1] + rMat[0][1]*rMat[1][0] ) * ( pIn[4] )
+ ( rMat[0][1]*rMat[1][2] + rMat[0][2]*rMat[1][1] ) * ( pIn[5] )
+ ( rMat[0][2]*rMat[1][0] + rMat[0][0]*rMat[1][2] ) * ( pIn[7] )
+ ( rMat[0][0]*rMat[1][0] ) * ( pIn[8] )
+ ( rMat[0][1]*rMat[1][1] ) * ( -pIn[8] )
+ ( rMat[0][2]*rMat[1][2] ) * ( 3.0f*pIn[6] )
);

outCoefs[5] = (
( rMat[1][0]*rMat[2][1] + rMat[1][1]*rMat[2][0] ) * ( pIn[4] )
+ ( rMat[1][1]*rMat[2][2] + rMat[1][2]*rMat[2][1] ) * ( pIn[5] )
+ ( rMat[1][2]*rMat[2][0] + rMat[1][0]*rMat[2][2] ) * ( pIn[7] )
+ ( rMat[1][0]*rMat[2][0] ) * ( pIn[8] )
+ ( rMat[1][1]*rMat[2][1] ) * ( -pIn[8] )
+ ( rMat[1][2]*rMat[2][2] ) * ( 3.0f*pIn[6] )
);

outCoefs[6] = (
( rMat[2][1]*rMat[2][0] ) * ( pIn[4] )
+ ( rMat[2][2]*rMat[2][1] ) * ( pIn[5] )
+ ( rMat[2][0]*rMat[2][2] ) * ( pIn[7] )
+ 0.5f*( rMat[2][0]*rMat[2][0] ) * ( pIn[8])
+ 0.5f*( rMat[2][1]*rMat[2][1] ) * ( -pIn[8])
+ 1.5f*( rMat[2][2]*rMat[2][2] ) * ( pIn[6] )
- 0.5f * ( pIn[6] )
);

outCoefs[7] = (
( rMat[0][0]*rMat[2][1] + rMat[0][1]*rMat[2][0] ) * ( pIn[4] )
+ ( rMat[0][1]*rMat[2][2] + rMat[0][2]*rMat[2][1] ) * ( pIn[5] )
+ ( rMat[0][2]*rMat[2][0] + rMat[0][0]*rMat[2][2] ) * ( pIn[7] )
+ ( rMat[0][0]*rMat[2][0] ) * ( pIn[8] )
+ ( rMat[0][1]*rMat[2][1] ) * ( -pIn[8] )
+ ( rMat[0][2]*rMat[2][2] ) * ( 3.0f*pIn[6] )
);

outCoefs[8] = (
( rMat[0][1]*rMat[0][0] - rMat[1][1]*rMat[1][0] ) * ( pIn[4] )
+ ( rMat[0][2]*rMat[0][1] - rMat[1][2]*rMat[1][1] ) * ( pIn[5] )
+ ( rMat[0][0]*rMat[0][2] - rMat[1][0]*rMat[1][2] ) * ( pIn[7] )
+0.5f*( rMat[0][0]*rMat[0][0] - rMat[1][0]*rMat[1][0] ) * ( pIn[8] )
+0.5f*( rMat[0][1]*rMat[0][1] - rMat[1][1]*rMat[1][1] ) * ( -pIn[8] )
+0.5f*( rMat[0][2]*rMat[0][2] - rMat[1][2]*rMat[1][2] ) * ( 3.0f*pIn[6] )
);
}


... and to sample it in the shader ...


float3 SampleSHQuadratic(float3 dir, float3 shVector[9])
{
float3 ds1 = dir.xyz*dir.xyz;
float3 ds2 = dir*dir.yzx; // xy, zy, xz

float3 v = shVector[0];

v += dir.y * shVector[1];
v += dir.z * shVector[2];
v += dir.x * shVector[3];

v += ds2.x * shVector[4];
v += ds2.y * shVector[5];
v += (ds1.z * 1.5 - 0.5) * shVector[6];
v += ds2.z * shVector[7];
v += (ds1.x - ds1.y) * 0.5 * shVector[8];

return v;
}


For Monte Carlo integration, take sampling points, feed direction "dir" to the following function to get multipliers for each coefficient, then multiply by the intensity in that direction. Divide the total by the number of sampling points:


void SHForDirection(const terVec3 &dir, float out[9])
{
// Constant
out[0] = 1.0f;

// Linear
out[1] = dir[1] * 3.0f;
out[2] = dir[2] * 3.0f;
out[3] = dir[0] * 3.0f;

// Quadratics
out[4] = ( dir[0]*dir[1] ) * 15.0f;
out[5] = ( dir[1]*dir[2] ) * 15.0f;
out[6] = ( 1.5f*( dir[2]*dir[2] ) - 0.5f ) * 5.0f;
out[7] = ( dir[0]*dir[2] ) * 15.0f;
out[8] = 0.5f*( dir[0]*dir[0] - dir[1]*dir[1] ) * 15.0f;
}


... and finally, for a uniformly-distributed random point on a sphere ...


terVec3 RandomDirection(int (*randomFunc)(), int randMax)
{
float u = (((float)randomFunc()) / (float)(randMax - 1))*2.0f - 1.0f;
float n = sqrtf(1.0f - u*u);

float theta = 2.0f * M_PI * (((float)randomFunc()) / (float)(randMax));

return terVec3(n * cos(theta), n * sin(theta), u);
}

by OneEightHundred (noreply@blogger.com) at 2011-12-02 09:22

2011-12-01

Fresh install on OS X of ColdFusion Bulder 2 (TWO, the SECOND one). Typing a simple conditional, this is what I was given:



I also had to manually write the closing cfif tag. It's such a joke.

The absolute core purpose of an IDE is to be a text editor. Secondary to that are other features that are supposed to make you work better. ColdFusion Builder 2 (TWO!!!!!) completely fails on all levels as a text editor. It doesn't even function as well as notepad.exe!

Text search is finicky, Find & Replace is completely broken half the time, the UI is often unresponsive (yay Eclipse), the text cursor sometimes disappears, double-clicking folders or files in an FTP view pops up the Rename dialog every time, HTML / CF tag completion usually doesn't happen, indention is broken, function parameter tooltips obscure the place you are typing, # and " completion randomly breaks (often leaving you with a ###)...the list goes on and on.

Adobe has a big feature list on their site. I'm thinking maybe they should go back and use some resources to fix the parts where you type things into the computer, you know, the whole point of the thing.

by Ted (noreply@blogger.com) at 2011-12-01 15:14

2011-11-30

Preamble — Memories are strange.

Memories are strange things. You don’t quite know where they come from — or why a particular one is more powerful than another. They pop up unbidden — some subconscious trigger, a smell, a sound, an event causes them to come to the forefront of your mind and take over your brain and emotions.

Memories, once ingrained, are impossible to rid yourself of, good or bad. You don’t get to choose which ones fault in, and you don’t get to choose which ones are the most powerful one attached to a trigger.

Sometimes, no matter how much you try, no matter how many new memories you try to make to replace, or subsume a given one — one memory will always stick. It can be good — or it can be bad. You don’t get to choose. When that memory is a bad one, it doesn’t matter how much you stack on top of it, no matter how much you try to forget — when it comes to the forefront, that is what you see, what you feel.

We don’t get to control it. All we can do is try to forge new ones and hope that they are more powerful, more pertinent and more filled with love and hope than everything that came before it, so that even if the memory that comes up is a bad one — a horrible one — there’s something warm, loving and caring to fall back on and hold on to when we lay awake at night staring at the ceiling trapped in throes of the past.

A story about a boy.

This is a story about a boy. It doesn’t matter who the boy is — and it doesn’t matter who he is now. It is about a boy and a memory, and this story is meant to get you to think about the people around you in your life, your community and your family, neighbors and friends.

This boy was young — perhaps five, perhaps six — who knows, the exact age is lost in the morass of time — it doesn’t matter. This boy lived with some people who were bad, very, very bad. They were the most vile of people. This boy lived with them as, at this age, you don’t get to pick who you live with. This boy, and these evil people lived together in a home filled with stink, filth and pain.

The boy was alone; the boy wasn’t afraid in the common sense of the word — after all to understand fear you have to experience something other than that to appreciate the emotion itself. Loneliness however, is something all humans innately understand without context or teaching. We are social creatures, we crave attention — good or bad — we crave to walk in the lights of others eyes and be noticed.

The boy was not noticed.

The time was before Christmas time. More than anything in the world, the boy loved an old TV show — Fraggle Rock. This was something that brought him happiness no matter how brief. He loved that show more than anything else in the world.

One day, the boy was someplace else, with a different evil person. He was sitting on a bare floor in a bare apartment that stank of cigarette smoke and old people. He was watching the television — a cold, but constant friend — watching his favorite show.

An advertisement came on. This advertisement offered something magical, something special. It was something so exciting that he had to call now to take advantage of the special offer. It was a thing tied to his friend, his joy — Fraggle Rock.

The boy had no money or wealth, and inside he knew that the evil people around him were loath to give up that which they had. The boy knew that he must have the thing he saw, and while he had nothing he knew how to acquire it.

He calmly got up off the floor, knowing that no one was around to notice what he was about to do. He opened the purse of one of the people who ignored him — he may have been alone, and might have only known fear, but he was smart. He knew that the thing on TV asked for a credit card, and he knew where to get one. He stole it from the purse, and picked up the telephone.

Some how, perversely, that boy knew where he lived. Maybe it was because he had had to walk himself to school so often, or had to be driven home by the police or a teacher from the school he sometimes attended.

He called the number he had memorized in a span of seconds. The person at the other end of the telephone, again, in a strange alignment of perversion and oddity, did not question the fact that a child was on the other end of the phone.

The boy managed to order the magical thing on TV. Using a stolen credit card in an apartment that stank of cigarettes and old people.
Before you think the boy had gotten away with it — he hadn’t. As he hung up the phone, one of the bad people came into the room and saw him with the phone and credit card in his hand.

Evil people do bad things to boy; the screen goes dark and the curtains go down. The boy knew that his brief glimpse of hope and joy in acquiring that thing from the TV was gone.

The boy went back to darkness.

Christmas Day

The boy did not know, or remember the thing from the TV he had gotten so severely punished for. He knew that it was Christmas time only because other children talked so eagerly about it. The house he lived in was barren, and filthy and undecorated except for a small pine tree in a corner that stood, undecorated.

There was no party, no family get together on Christmas eve. Yet still the boy lay in his bed charged with hope that somehow, somewhere, a gift might appear for him under that barren and sad tree the next day. He might not know — he was locked in his room again, but that hope stood out.

Not because he knew what it was, but because he knew what others had told him, he knew the emotions that others had about this “special” time.

The boy didn’t sleep well — not just because it was Christmas. He never slept well.

Christmas morning, let’s say at five o’clock in the morning, the boy was awake as he always was. He got up with trepidation and fear for waking the evil people with whom he lived. He tested the door knob — it was unlocked.

He opened the door and looked around — none of the evil people were around, there were someplace else. He was alone — and given that this was a state much preferable to the alternative, he was temporarily happy.

He walked to the barren tree, past the trash and cat waste scattered through the house and stood in front of it. At first, his eyes didn’t perceive the box underneath it. He didn’t see a stack of jauntily wrapped gifts, or stockings hung with care. The boy was filled with sadness.

There was, however, a bag — the type you might get nowadays from a supermarket for reuse. The boy’s eyes caught the logo on that bad.

Fraggle. Rock.

Stunned beyond comprehension, the boy walked over slowly, he recognized the logo, and in fact, he recognized the bag from the commercial long forgotten. It was the magical thing he had been so severely punished for. He looked around, ensuring he was alone, and he pulled the thing out of the bag.

It was a Fraggle Rock record player. That was all — and a single, small record that contained but one song. Shaking, he opened the record player, and plugged it into the wall. Gingerly, he placed the record on the player and through trial and error, figured out how to make it turn on and play.

The boy cried as the first notes of the one song began to play. So joyful was he in this singular moment, listening to the theme song for a TV show that all the loneliness and pain he knew was forgotten, replaced with a joy so tangible he could hold it close.

In that moment, the boy knew sadness as well, as that joy was so powerful he knew the stark contrasts in the emotions he had known. He forgot loneliness, caught up in a moment so emotional that nothing else mattered.

In that moment, the boy was happy. The house was filled with that song for hours until the people he lived with came home, and took it away. In those hours, that boy knew nothing but joy, happiness and the dark contrast of sadness.

Back to the beginning.

The boy is now a man, which man is irrelevant. What is relevant is that when the first chords of the first Christmas song begin to play after Thanksgiving — when the first Christmas ornament go up that boy is thrown back to that memory of that single Christmas day.

No memories since that day matter; none of them come up and filter into his consciousness other than that one. It takes over his psyche at random, as said before — you don’t get to choose how this works.

So, why?

Why am I sharing this story about a boy, or rambling about memories? Because, despite knowing that once ingrained a memory can not be forgotten, I feel that it is true that you can override memories with stronger ones with a more powerful emotion.

I feel that joy, hope and love are more powerful emotions than fear, loneliness and pain.

I share this boy’s story so that I can get you to think for a moment about the people around you. Friends, colleagues, family — the person on the street, on the bus, the people in your community and the person you only know through email, IRC or on Twitter.

I share this to get you to think about those who you don’t think about all that closely. The children who live as that boy did, or those children and families that have little or nothing during this supposed time of joy.

I’m not asking you to give up wealth, or toys, or food — those are all fine things, but they are simply tangental aspects of how a memory might be created. I’m asking you to think about all of these people, even those whom you disagree with or hate, or those you never think about at all, and I ask you to take a moment to reach out to them in some way.

Perhaps a toy, a book, a warm coat or meal for those that you do not know well — something that can give them the same joy that that boy felt when that song played. Maybe an email to someone you haven’t heard from in a while, or warm words to someone who you normally spar with.

Thousands of people trudge through the holidays, no matter their faith, race or creed — their choice of forums, programming language, career or school depressed and alone during this time. They’re trapped by memories that should have been replaced long, long ago. Maybe they never will be replaced, but maybe they can be supplemented and temporarily displaced.

I am asking you to reach out in any way that you can to help them make new memories, ones of joy, love and caring — even if it is over the internet, or as fleeting as being polite to them and thinking of them when you bump into them on the street or in the mall.

Reach out in all the ways you can, despite times of strife and division and economic depression. Help everyone you can be filled with a memory of joy, love and caring, give them that moment the boy had even if bittersweet. Show them your grace, humility, kindness and caring.

I still cry when I hear Fraggle Rock.

by jesse at 2011-11-30 15:27

2011-11-27

While recently reviewing some topics in vector calculus, I became curious as to why violating seemingly innocuous conditions for some theorems leads to surprisingly wild results. In fact, I was struck by how these theorems resemble computer programs, not in some abstract way, but in how the lack of “input validation” leads to non-obvious behavior in the face of erroneous input.

There are actually whole books dedicated to counterexamples. They make good bathroom reading material.

I found that understanding why these counterexamples lead to wild results deepened my understanding of the theorems involved and their proofs. Besides, pathological examples are more interesting than well-behaved ones!

First, let's look at a “counterexample” to Green's theorem:

1. Two functions \(L, M \colon \RR^2 \to \RR\) and a positively-oriented, piecewise smooth, simple closed curve \(C\) in \(\RR^2\) enclosing the region \(D\) such that \[ \oint_C L \,dx + M \,dy \ne \iint_D \left( \pd{M}{x} - \pd{L}{y} \right) \,dx \,dy \text{.} \]

The vector field \((L, M)\) also serves as the canonical “counterexample” to the gradient theorem.

Let \begin{align*} L &= -\frac{y}{x^2+y^2} \text{, } M = \frac{x}{x^2+y^2} \text{,} \end{align*} and \(C\) be a curve going clockwise around the rectangle \(D = [-1, 1]^2\). Then the integral of \(L \,dx + M \, dy\) around \(C\) is \(2 \pi\) since it encloses the origin. But \[ \pd{M}{x} = \pd{L}{y} = \frac{y^2-x^2}{x^2+y^2} \] so the difference of the two vanishes everywhere but the origin, where neither function is defined. Therefore, the (improper) integral over \(D\) also vanishes, proving the inequality. ∎

Of course, the easy explanation is that the discontinuity of \(L\) and \(M\) at the origin violates a condition of Green's theorem. But that doesn't really tell us anything, so let's break down the theorem and see where exactly it fails.

Green's theorem is usually proved first for rectangles \([a, b] \times [c, d]\), which suffices for our purpose. If \(C\) is a curve that goes counter-clockwise around such a rectangle \(D\), then we can easily show that \[ \oint_C L \,dx = - \iint_D \pd{L}{y} \,dx \,dy \] and \[ \oint_C M \,dy = \iint_D \pd{M}{x} \,dx \,dy \text{,} \] with the sum of these two formulas proving the theorem.

So the first sign of trouble is that the theorem freely interchanges addition and integration. Since the partial derivatives of our functions diverge at the origin, if \(D\) contains the origin then the integrals of those partial derivatives over \(D\) may not even be defined, even if the integral of their difference is.

But the problem arises even before that. The statements above are proved by showing \[ \oint_C L \,dx = - \int_a^b \left( \int_c^d \pd{L}{y} \,dy \right) \,dx \] and \[ \oint_C M \,dy = \int_c^d \left( \int_a^b \pd{M}{x} \,dx \right) \,dy \text{.} \] both of which hold for our example. But notice that in one case we integrate with respect to \(y\) first, and in the other case we integrate with respect to \(x\) first. Therefore, we have to interchange the order of integration or convert to a double integral in order to get them to a form where we can add them. And there's the rub: if \(D\) contains the origin, switching the order of integration for either integral above switches the sign of the result!

This fully explains the discrepancy; since the result of both integrals above (with the iteration order preserved) is \(\pi\), adding them together as-is gives the expected result of \(2 \pi\). But if we switch the iteration order of one of the iterated integrals as done in the proof of Green's theorem, then we switch the result of that integral to \(-\pi\), which cancels with the result of the other unchanged integral to produce \(0\).

So now let's examine this strange behavior of the sign of an integration's result depending on the iteration order. This leads us to our next “counterexample,” this time for Fubini's theorem:

2. A function \(f \colon \RR^2 \to \RR\) whose iterated integrals over a rectangle \(D = [a, b] \times [c, d] \subset \RR^2\) differ.

Let \[ f(x, y) = \frac{x^2-y^2}{(x^2+y^2)^2} \quad \text{ and } \quad D = [-1, 1]^2\text{.} \] The two iterated integrals of \(f\) over \(D\) are usually written as \[ \int_{-1}^1 \left( \int_{-1}^1 f(x, y) \,dy \right) \,dx \qquad \text{ and } \qquad \int_{-1}^1 \left( \int_{-1}^1 f(x, y) \,dx \right) \,dy \] but let's define them more carefully to make it easier to justify our calculations.

Let \begin{align*} u_k &= y \mapsto f(k, y) \\ v_l &= x \mapsto f(x, l) \text{.} \end{align*} In other words, given the real constants \(k\) and \(l\), construct the (possibly partial) real functions \(u_k(y)\) and \(v_l(x)\) by partially-evaluating \(f\) at \(x = k\) and \(y = l\), respectively.

\(U(x)\) and \(V(y)\) are also (partial) real functions.

Then, if we also let \[ U(x) = \int_{-1}^1 u_x(y) \,dy \qquad \text{ and } \qquad V(y) = \int_{-1}^1 v_y(x) \,dx \text{,} \] we can write the iterated integrals as \[ \int_{-1}^1 U(x) \,dx \qquad \text{ and } \qquad \int_{-1}^1 V(y) \,dy \text{.} \]

We're justified in applying standard integration techniques here since \(u_k(y)\) for \(k > 0\) is defined and bounded for all \(y\).

Computing \(U(x)\) for \(x \neq 0\), we get \begin{align*} U(x) &= \int_{-1}^1 \pd{}{y} \left( -\frac{y}{x^2+y^2} \right) \,dy \\ &= \left. -\frac{y}{x^2+y^2} \right|_{y=-1}^{y=1} \\ &= -\frac{2}{x^2+1} \text{.} \end{align*}

Attempting to evaluate \(U(0)\), we see that \begin{align*} U(0) &= \int_{-1}^1 \frac{0^2-y^2}{(0^2+y^2)^2} \,dy \\ &= - \int_{-1}^1 \frac{dy}{y^2} \end{align*} which diverges. So \[ U(x) = -\frac{2}{x^2+1} \text{ for } x \ne 0 \text{.} \]

Note that \(U(x)\) and \(V(y)\) differ only in variable name and sign.

By a similar computation, we find that \[ V(y) = \frac{2}{y^2+1} \text{ for } y \ne 0 \text{.} \]

Since \(U(x)\) isn't defined at \(0\), we have to treat it as an improper integral, although doing so poses no real difficulty: \begin{align*} \int_{-1}^1 U(x)\,dx &= \lim_{a \nearrow 0} \left( \int_{-1}^a -\frac{2}{x^2+1} \,dx \right) + \lim_{a \searrow 0} \left( \int_{a}^1 -\frac{2}{x^2+1} \,dx \right) \\ &= \lim_{a \nearrow 0} \Bigl( \left. -2 \arctan(x) \right|_{-1}^{a} \Bigr) + \lim_{a \searrow 0} \Bigl( \left. -2 \arctan(x) \right|_{a}^{1} \Bigr) \\ &= \left. -2 \arctan(x) \right|_{-1}^{0} + \left. -2 \arctan(x) \right|_{0}^{1} \\ &= \left. -2 \arctan(x) \right|_{-1}^{1} \\ &= -\pi \text{.} \end{align*}

Similarly, \[ \int_{-1}^1 V(y)\,dy = \pi \text{,} \] so the iterated integrals of \(f(x, y)\) over \([-1, 1]^2\) differ; in fact, as we claimed above, switching the iteration order switches the sign of the result. ∎

We can repeat the above calculations for an arbitrary rectangle to see that the iterated integrals of \(f(x, y)\) differ if \(D\) contains the origin either as an interior point or a corner. But there's an easier way to prove that statement and also gain some insight as to why \(f(x, y)\) has this strange property.

Note that the key facts in the above calculations were that \(U(x) \lt 0\) for any \(x \ne 0\) and \(V(y) \gt 0\) for any \(y \ne 0\). Therefore, integrating \(U(x)\) over any interval on the \(x\)-axis would produce a negative result and integrating \(V(x)\) over any interval on the \(y\)-axis would produce a positive result, leading to the difference in iterated integrals. This holds more generally; for any \(m, n \gt 0\): \[ \int_{-n}^n f(x, y) \,dy 0 \qquad \text{ and } \qquad \int_{-m}^m f(x, y) \,dx > 0 \text{.} \] Therefore, \[ \int_{-m}^m \left( \int_{-n}^n f(x, y) \,dy \right) \,dx 0 \qquad \text{ and } \qquad \int_{-n}^n \left( \int_{-m}^m f(x, y) \,dx \right) \,dy > 0 \] so the iterated integrals of \(f(x, y)\) differ over the rectangles \([-m, m] \times [-n, n]\). Since any rectangle \(D\) containing the origin as an interior point must contain some smaller rectangle \(E = [-m, m] \times [-n, n]\), the iterated integrals of \(f(x, y)\) over \(E\) differ and therefore must also differ over \(D\).

Furthermore, since \(f(x, y)\) is even in both \(x\) and \(y\), you can carry out a similar argument to the above with intervals of the form \([0, m]\) or \([-m, 0]\) to show that the iterated integrals of \(f(x, y)\) must also differ over any rectangle with the origin as a corner.

So the essential property of \(f(x, y)\) is that slicing it along the \(x\)-axis gives a function which has positive area under the curve on any interval symmetric around \(0\) or with \(0\) as an endpoint, and that slicing it similarly along the \(y\)-axis gives a function with has negative area. Therefore, on a rectangle symmetric around the origin or with the origin as a corner, one can choose the sign of the iterated integral by choosing which axis to slice first.

The next thing to investigate is how exactly the iterated integrals of \(f(x, y)\) over the rectangle \(D\) are expressed such that they differ only when \(D\) contains the origin, especially considering that the \(f(x, y)\) is expressed in quite a simple form. To do that, let's consider the simple case of a rectangle \(D = [\delta, 1] \times [\epsilon, 1]\) where we can vary \(\delta\) and \(\epsilon\) at will.

Let \begin{align*} I_{yx}(\delta, \epsilon) &= \int_{\delta}^1 \left( \int_{\epsilon}^1 f(x, y) \,dy \right) \,dx \\ I_{xy}(\delta, \epsilon) &= \int_{\epsilon}^1 \left( \int_{\delta}^1 f(x, y) \,dx \right) \,dy \text{.} \end{align*} Then, for \(\epsilon \neq 0\): \begin{align*} I_{yx}(\delta, \epsilon) &= \int_{\delta}^1 \left( \int_{\epsilon}^1 \frac{y^2-x^2}{(x^2+y^2)^2} \,dy \right) \,dx \\ &= \int_{\delta}^1 \left( \left. -\frac{y}{x^2+y^2} \right|_{y=\epsilon}^{y=1} \right) \,dx \\ &= \int_{\delta}^1 \Biggl( -\frac{1}{1+x^2} - \left( -\frac{\epsilon}{\epsilon^2+x^2} \right) \Biggr) \,dx \\ &= \int_{\delta}^1 \frac{dx/\epsilon}{1+(x/\epsilon)^2} - \int_{\delta}^1 \frac{dx}{1+x^2} \\ &= \arctan\left(\frac{1}{\epsilon}\right) - \arctan\left(\frac{\delta}{\epsilon}\right) - \frac{\pi}{4} + \arctan(\delta) \text{,} \end{align*} and for \(\epsilon = 0\): \[ I_{yx}(\delta, 0) = -\frac{\pi}{4} + \arctan(\delta) \text{.} \] Similarly, for \(\delta \neq 0\): \begin{align*} I_{xy}(\delta, \epsilon) &= \int_{\epsilon}^1 \left( \int_{\delta}^1 \frac{y^2-x^2}{(x^2+y^2)^2} \,dx \right) \,dy \\ &= \int_{\epsilon}^1 \left( \left. \frac{x}{x^2+y^2} \right|_{x=\delta}^{x=1} \right) \,dy \\ &= \int_{\epsilon}^1 \left( \frac{1}{1+y^2} - \frac{\delta}{\delta^2+x^2} \right) \,dy \\ &= \int_{\epsilon}^1 \frac{dy}{1+y^2} - \int_{\epsilon}^1 \frac{dy/\delta}{1+(y/\delta)^2} \\ &= \frac{\pi}{4} - \arctan(\epsilon) - \arctan\left(\frac{1}{\delta}\right) + \arctan\left(\frac{\epsilon}{\delta}\right) \text{,} \end{align*} and for \(\delta = 0\): \[ I_{xy}(0, \epsilon) = \frac{\pi}{4} - \arctan(\epsilon) \text{.} \] Then let \(\Delta = I_{xy} - I_{yx}\) be the difference between the two iterated integrals. We can use the identity \[ \arctan(x) + \arctan\left(\frac{1}{x}\right) = \frac{\pi}{2} \sgn{x} \] to simplify \(\Delta(\delta, \epsilon)\) if neither \(\delta\) nor \(\epsilon\) is zero: \begin{align*} \Delta(\delta, \epsilon) &= \bigl( \pi/4 - \arctan(\epsilon) - \arctan(1/\delta) + \arctan(\epsilon/\delta) \bigr) \\ & \quad \mathbin{-} \bigl( \arctan(1/\epsilon) - \arctan(\delta/\epsilon) - \pi/4 + \arctan(\delta) \bigr) \\ &= \pi/2 - \bigl( \arctan(\epsilon) + \arctan(1/\epsilon) \bigr) \\ & \quad \mathbin{-} \bigl( \arctan(\delta) + \arctan(1/\delta) \bigr) \\ & \quad \mathbin{+} \bigl( \arctan(\delta/\epsilon) + \arctan(\epsilon/\delta) \bigr) \\ &= \frac{\pi}{2} \bigl( 1 - \sgn(\epsilon) - \sgn(\delta) + \sgn(\delta/\epsilon) \bigr) \text{.} \end{align*}

Using the properties of \(\sgn(x)\), we can simplify this to the final expression: \[ \Delta(\delta, \epsilon) = \frac{\pi}{2} \bigl( 1 - \sgn(\delta) \bigr) \bigl( 1 - \sgn(\epsilon) \bigr) \] which we can prove still holds if either \(\delta\) or \(\epsilon\) is zero (or both).

So with the simplified expression for \(\Delta(\delta, \epsilon)\), it becomes apparent how \(\sgn(x)\) is used to control the value of \(\Delta(\delta, \epsilon)\); as long as either \(\delta\) or \(\epsilon\) is positive, \(1 - \sgn(x)\) zeroes out the entire expression.

by Fred Akalin at 2011-11-27 08:00

2011-11-19

There exists this disease on the Internet which is the set of people who react badly to things such as this:

void main() {
    printf("Hello, world!\n");
}

They’ll flare their hoods and puff out, “main should be declared to return int and not void because that’s what the C++ standard says, your code is non-standard and thus it’s bad!” Well gee, it would be just horrid if somebody porting the code had to change the return value of one function.

The problem with you, if you’re one of these people, is that you have nothing better to say.  This is the sort of thing where you sit in your chair at your computer like the little smugnesty you are and find some petty fault in the way other people do things.  Guess what: you’re unimportant and you don’t matter.  You should find something more important to bitch about, like problems with people’s code that actually matter.  Or problems with people bitching about code that doesn’t matter.

by shrughes at 2011-11-19 05:38

2011-11-14

Whilst writing some blog posts about the authentication which I’ve been implementing, I’ve come to the realization, almost as an after thought, that even though I’ve still not got a lot of the main Github API implemented, the way I’ve envisioned the API working, from a user point of view, is sort of a bit shit.

var userApi = new UserApi();
var user = userApi.GetUser("example");

Pretty straightforward, right? What about the as yet unwritten GistApi?

var gistApi = new GistApi();
var gist = gistApi.GetGist(...);

Again, pretty straightforward. But I see a pattern forming. What am I going to do when it comes to the RepositoryApi?

var reposApi = new RepositoryApi();
var repo = reposApi.GetRepository(...);

Well, that is starting to look pretty fucking stupid. I’ve caught myself doing it with other methods as well. I simply cannot believe I’ve let myself be so stupid. At least there is not that much implemented where I’ll have to refactor loads of shit.

by stuart at 2011-11-14 08:00

2011-11-12

Is it hard having a female avatar on the internet? Do people take your arguments less seriously or sexually harass you. Never fear! The internet has the cure.

Mustachify.me! Now you too can be normal manpeople!

I am normal manpeople

Problem solved! You will no longer distract and enrage men on the internet with your female face.

2011-11-12 22:01

2011-11-08

Iron Trotter without sound:



OSGCC was absolutely amazing this year. The games made for it were all impressive. We had a kinect game, a tablet game, a vimscript game, and even a Dart game. The source for all of the games will be available on github and you can learn more about previous competitions on the osgcc website.

2011-11-08 18:03

2011-11-07

The Basics

The typical way that you’d make a request with RestSharp:

  1. Create a RestRequest
  2. Create a RestClient
  3. Execute the request with the client
  4. Do something with the response.

It’s at the point that you get the client object that you may wish to add authentication. For example with a REST API such as Githubs, certain methods behave differently if the request is authenticated or not, so being able to magically turn on authentication is desirable.

To authenticate a request with RestSharp, it is a simple case of creating a RestRequest, RestClient and an IAuthenticator instance for the authenticating mechanism you want to use. For example:

var client = new RestClient
             {
                 BaseUrl ="https://api.github.com",
                 Authenticator = new HttpBasicAuthenticator(username, password)
             };

This is pretty straightforward and standard RestSharp usage. You may have a class to encapsulate this functionality, with a method which returns the RestClient instance, probably in a base class in order to inherit this common functionality in other classes.

public abstract class BaseApi
{
    RestClient GetRestClient()
    {
        ...
    }
}

Options

There are several methods which we can use to add authentication dynamically to the RestClient instance, ranging from the trivial to the more involved.

The trivial solution is to add the IAuthenticator as a parameter to the method, which is then assigned to the RestClient when it is created. Easy. Also fairly easy is just make it abstract or virtual and override it in an inheriting class, although this breaks SRP.

Alternatively, we can implement the Decorator pattern, and introduce the authentication in a class which is solely responsible for handing it. I’m not going to go into this in too much detail, there is a wealth of information on implementing this pattern already available on the web. Using a Decorator is valid in a lot of situations, particularly when re-factoring someone else’s mess, as you can adhere to the same interface and not risk breaking some important business function. In other cases, it is better to intercept.

Interception

A pattern which lends itself to this is called Proxy, and if you spend any time with Google and search terms like “c# proxy pattern” you’ll quickly end up finding a lot of information about implementing it. You’ll also find interesting stuff about Castle.DynamicProxy, and you may quickly realise this is an excellent way of adding the ability to dynamically intercept a method to add additional functionality on the fly. I’ve implemented an interceptor in the Github API library, with the core magic being:

public void Intercept(IInvocation invocation)
{
    invocation.Proceed(); // let the RestClient be instantiated as normal.
    var restClient = (RestClient)invocation.ReturnValue;
    restClient.Authenticator = _authenticator; // add the authenticator
    invocation.ReturnValue = restClient;
 }

I then wrap the interception up in a static class, which is a technique I saw on another website, which I then wrap in a extension method which with a little bit of generics hangs off API classes in a fluent manner.

var api = new UserApi(GitHubUrl).WithAuthentication(authenticator);

I feel like it is the best way to do this sort of thing, and I will certainly starting using more of it, where necessary, in all my projects.

by stuart at 2011-11-07 08:00

2011-10-28

While reading about proper tail recursion in Scheme, I encountered a similar but obscure optimization called evlis tail recursion. In the paper where it was first described, the author claims it "dramatically improve the space performance of many programs," which sounded promising.

However, the few places where its mentioned don't do much more than state its definition and claim its usefulness. Hopefully I can provide a more detailed analysis here.

Consider the straightforward factorial implementation in Scheme:

(define (fact n) (if (= n 1) 1 (* n (fact (- n 1)))))
Assume a left-to-right evaluation order for now.

It is not tail-recursive, since the recursive call is nested in another procedure call. However, it's almost tail-recursive; the call to * is a tail call, and the recursive call is its last subexpression, so it will be the last subexpression to be evaluated.

The function that takes a list of expressions, evaluates them, and returns the results as a list is traditionally called evlis, hence the name of the optimization.

Recall what happens when a procedure call (represented as a list of subexpressions) is evaluated: each subexpression is evaluated, and the first result (the procedure) is passed the other results as arguments.

This assumes that the calling environment isn't stored somewhere else.

Evlis tail recursion can be described as follows: when performing a procedure call and during the evaluation of the last subexpression, the calling environment is discarded as soon as it is not required. The distinction between evlis tail recursion and proper tail recursion is subtle. Proper tail recursion requires only that the calling environment be discarded before the actual procedure call; evlis tail recursion discards the calling environment even sooner, if possible.

An example will help to clarify things. Given fact as defined above, say you evaluate (fact 10) and you're in the procedure call with n = 5. The call stack of a properly tail-recursive interpreter would look like this:

evalExpr
--------
env = { n: 10 } -> <top-level environment>
expr = '(* n (fact (- n 1)))'
proc = <native function: *>
args = [10, <pending evalExpr('(fact (- n 1))', env)>]

evalExpr
--------
env = { n: 9 } -> <top-level environment>
expr = '(* n (fact (- n 1)))'
proc = <native function: *>
args = [9, <pending evalExpr('(fact (- n 1))', env)>]

...

evalExpr
--------
env = { n: 6 } -> <top-level environment>
expr = '(* n (fact (- n 1)))'
proc = <native function: *>
args = [6, <pending evalExpr('(fact (- n 1))', env)>]

evalExpr
--------
env = { n: 5 } -> <top-level environment>
expr = '(if ...)'

whereas the call stack of an evlis tail-recursive interpreter would look like this:

evalExpr
--------
env = { n: 5 } -> <top-level environment>
pendingProcedureCalls = [
  [<native function: *>, 10],
  [<native function: *>, 9],
  ...
  [<native function: *>, 6]
]
expr = (if ...)

In this implementation, the last subexpression of a procedure call is evaluated exactly like a tail expression, but the procedure call and non-last subexpressions are pushed onto a stack. Whenever an expression is reduced to a simple one and the stack is non-empty, a pending procedure call with its other args are popped off, and it is called with the reduced expression as the final argument.

Note that this didn't change the asymptotic behavior of the procedure; it still takes $\Theta(n)$ memory to evaluate. However, only the bare minimum of information is saved: the list of pending functions and their arguments. Other auxiliary variables, and crucially the nested calling environments, aren't preserved, leading to a significant constant-factor reduction in memory.

This raises the question: Are there cases where evlis tail recursion leads to better asymptotic behavior? In fact, yes; consider the following (contrived) implementation of factorial:

This was adapted from an example in Proper Tail Recursion and Space Efficiency.
(define (fact2 n)
  (define v (make-vector n))
  (* (n (fact2 (- n 1)))))

Before the main body of the function, a vector of size $n$ is defined. This means that the environments in the call stack of a properly tail-recursive interpreter would look like this:

Assume that the interpreter isn't smart enough to deduce that $v$ can be optimized out since it's never used.
env = { n: 10, v: <vector of size 10> } -> <top-level environment>
env = { n: 9, v: <vector of size 9> } -> <top-level environment>
env = { n: 8, v: <vector of size 8> } -> <top-level environment>
env = { n: 7, v: <vector of size 7> } -> <top-level environment>
...

whereas the an evlis tail-recursive interpreter would keep around only the current environment. Therefore, the properly tail-recursive interpreter would require $\Theta(n^2)$ memory to evaluate (fact2 n) while the evlis tail-recursive interpreter would require only $\Theta(n)$

Studying examples like the one above enabled me to finally understand how evlin tail recursion worked and what sort of savings it gives. However, I have yet to find a practical example where evlis tail recursion gives the same sort of asymptotic gains as described above, and I'd be interested to receive some. But perhaps the "large gains" mentioned in the various papers describing it are only constant-factor reductions in memory.

In any case, another important difference in Scheme between proper tail recursion and evlis tail recursion is that the former is a language feature and the latter is an optimization. That means that it is acceptable and even encouraged to write Scheme programs that take advantage of proper tail recursion, but it would be unwise to rely on evlis tail recursion for the asymptotic performance of your function. Instead, one should treat it just as a nice constant-factor speed gain.

Note that it is easy to make evlis tail recursion "smarter." Since Scheme doesn't specify the order of argument evaluation, an interpreter could evaluate arguments to maximize the gains from evlis tail recursion. As an easy example, if we had switched the arguments to + in fact above, making it non-evlis-tail-recursive, a smart compiler could still treat it as such. A possible rule of thumb would be to pick a non-trivial function call to evaluate last.

To complete the picture, I will outline below the evaluation function for a simple evlis tail-recursive Scheme interpreter in Javascript. All of the sources I've found describe it in terms of compilers, so I think it'll be useful to have a reference implementation for an interpreter.

Let's say we already have a properly tail-recursive interpreter:

Adapted from Peter Norvig's excellent lis.py.
function evalExpr(expr, env) {
  // Fake tail calls with a while loop and continue.
  while (true) {
    // Symbols, constants, quoted expressions, and lambdas.
    if (isSimpleExpr(expr)) {
      // The only exit point.
      return evalSimpleExpr(expr, env);
    }
    // (if test conseq alt)
    if (isSpecialForm(expr, 'if')) {
      expr = evalExpr(expr[1], env) ? expr[2] : expr[3];
      continue;
    }
    // (set! var expr)
    if (isSpecialForm(expr, 'set!')) {
      env.set(expr[1], evalExpr(expr[2], env));
      expr = null;
      continue;
    }
    // (define var expr?)
    if (isSpecialForm(expr, 'define')) {
      env.define(expr[1], evalExpr(expr[2], env));
      expr = null;
      continue;
    }
    // (begin expr*)
    if (isSpecialForm(expr, 'begin')) {
      if (expr.length == 1) {
        expr = null;
        continue;
      }
      // Evaluate all but the last subexpression.
      for (var i = 1; i < expr.length - 1; ++i) {
        evalExpr(expr[i], env);
      }
      expr = expr[expr.length - 1];
      continue;
    }
    // (proc expr*)
    var proc = evalExpr(expr.shift(), env);
    var args = expr.map(function(subExpr) { return evalExpr(subExpr, env); });
    // proc.run() returns its body in result.expr and the environment
    // in which to evaluate it (with all its arguments bound) in
    // result.env.
    var result = proc.run(args);
    expr = result.expr;
    // The only time when env is changed.
    env = result.env;
    continue;
  }
}

Then implementing evlis tail recursion requires only a few changes:

function evalExpr(expr, env) {
  // This is a stack of procedures and their non-final arguments that
  // are waiting for their final argument to be evaluated.
  var pendingProcedureCalls = [];
  while (true) {
    if (isSimpleExpr(expr)) {
      expr = evalSimpleExpr(expr, env);
      // Discard calling environment.
      env = null;
      if (pendingProcedureCalls.length == 0) {
        // No pending procedure calls, so we're done (the only exit
        // point).
        return expr;
      }
      var args = pendingProcedureCalls.pop();
      var proc = args.shift();
      args.push(expr);
      var result = proc.run(args);
      expr = result.expr;
      // Change to new environment (the only time when env is
      // changed).
      env = result.env;
      continue;
    }
    ...
    // Everything else remains the same.
    ...
    // (proc expr*)
    var nonFinalSubExprs =
      exprs.slice(0, -1).map(
        function(subExpr) { return evalExpr(subExpr, env); });
    pendingProcecureCalls.push(nonFinalSubExprs);
    // Evaluate the last subexpression as a tail call.
    expr = expr[expr.length - 1];
    continue;
  }
}

by Fred Akalin at 2011-10-28 07:00

2011-10-18

Has it really been a year since the last update?

Well, things have been chugging along with less discovery and more actual work. However, development on TDP is largely on hold due to the likely impending release of the Doom 3 source code, which has numerous architectural improvements like rigid-body physics and much better customization of entity networking.


In the meantime, however, a component of TDP has been spun off into its own project: The RDX extension language. Initially planned as a resource manager, it has evolved into a full-fledged programmability API. The main goal was to have a runtime with very straightforward integration, to the point that you can easily use it for managing your C++ resources, but also to be much higher performance than dynamically-typed interpreted languages, especially when dealing with complex data types such as float vectors.

Features are still being implemented, but the compiler seems to be stable and load-time conversion to native x86 code is functional. Expect a real release in a month or two.

The project now has a home on Google Code.

by OneEightHundred (noreply@blogger.com) at 2011-10-18 22:37

2011-09-29

The deadline for PyCon 2012 tutorial, talk, and poster proposals is under 14 days away, so be sure to get your submissions in by October 12, 2011 (as always, if it’s October 12th anywhere in the world, submissions are still open!).

Whether you’re a first-timer or an experienced veteran, PyCon is depends on you, the community, coming together to build the best conference schedule possible — PyCon is first and foremost about the community, driven by volunteers both on an organizational level, and by speakers.

Our call for proposals lays out the details it takes to be included in the lineup for the conference in Santa Clara, CA on March 7–15, 2012. I should note that this year we have gone away from consistent groups of invited speakers — meaning, all talks, regardless of who submits them are not guaranteed a speaking slot. All talks are judged and reviewed on the merits of the talk and the speakers themselves.

If you’re unsure of what to write about, our recent survey yielded a large list of potential talk topics, and plenty of ideas for tutorials. We’ve also come up with general tips on proposal writing to ensure everyone has the most complete proposal when it comes time for review. As always, the program committee wants to put together an incredible conference, so they’ll be working with submitters to fine tune proposal details and help you produce the best submissions. Even if you are still incubating a talk idea: submit the proposal now in rough form and we can assist you in fleshing out and refining the proposal during the review process.

We’ve had plenty of great news to share since we first announced the call for proposals. Paul Graham of Y Combinator was recently announced as a keynote speaker, making his return after a 2003 keynote. David Beazley, famous for his mind-blowing talks on CPython’s Global Interpreter Lock, was added to the plenary talk series.

Sponsors can now list their job openings on the “Job Fair” section of the PyCon site as we previously announced — providing an excellent resource for job seekers, and providers.

We’re hard at work to bring you the best conference yet, so stay tuned to PyCon news at the PyCon blog and on Twitter at https://twitter.com/#!/pycon.

We recently eclipsed last year’s sponsorship count of 40 and are currently at a record 54 organizations supporting PyCon. If you or your organization are interested in sponsoring PyCon, we’d love to hear from you, so check out our sponsorship page.

And as always — quick thanks to all of our awesome PyCon 2012 Sponsors:

Thank you — and as always, feel free to reach out to the team or any of the staff with any questions you might have.

by jesse at 2011-09-29 12:55