decoding base64 encoded text with POSIX awk - awk
In a bash script that I'm writing for Linux/Solaris I need to decode more than a hundred thousand base64-encoded text strings, and, because I don't wanna massively fork a non-portable base64 binary from awk, I wrote a function that does the decoding.
Here's the code of my base64_decode function:
function base64_decode(str, out,i,n,v) {
out = ""
if ( ! ("A" in _BASE64_DECODE_c2i) )
for (i = 1; i <= 64; i++)
_BASE64_DECODE_c2i[substr("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",i,1)] = i-1
i = 0
n = length(str)
while (i <= n) {
v = _BASE64_DECODE_c2i[substr(str,++i,1)] * 262144 + \
_BASE64_DECODE_c2i[substr(str,++i,1)] * 4096 + \
_BASE64_DECODE_c2i[substr(str,++i,1)] * 64 + \
_BASE64_DECODE_c2i[substr(str,++i,1)]
out = out sprintf("%c%c%c", int(v/65536), int(v/256), v)
}
return out
}
Which works fine:
printf '%s\n' SmFuZQ== amRvZQ== |
LANG=C command -p awk '
{ print base64_decode($0) }
function base64_decode(...) {...}
'
Jane
jdoe
SIMPLIFIED REAL-LIFE EXAMPLE THAT DOESN'T WORK AS EXPECTED
I want to get the givenName of the users that are members of GroupCode = 025496 from the output of ldapsearch -LLL -o ldif-wrap=no ... '(|(uid=*)(GroupCode=*))' uid givenName sn GroupCode memberUid:
dn: uid=jsmith,ou=users,dc=example,dc=com
givenName: John
sn: SMITH
uid: jsmith
dn: uid=jdoe,ou=users,dc=example,dc=com
uid: jdoe
givenName:: SmFuZQ==
sn:: RE9F
dn: cn=group1,ou=groups,dc=example,dc=com
GroupCode: 025496
memberUid:: amRvZQ==
memberUid: jsmith
Here would be an awk for doing so:
LANG=C command -p awk -F '\n' -v RS='' -v GroupCode=025496 '
{
delete attrs
for (i = 2; i <= NF; i++) {
match($i,/::? /)
key = substr($i,1,RSTART-1)
val = substr($i,RSTART+RLENGTH)
if (RLENGTH == 3)
val = base64_decode(val)
attrs[key] = ((key in attrs) ? attrs[key] SUBSEP val : val)
}
if ( /\nuid:/ )
givenName[ attrs["uid"] ] = attrs["givenName"]
else
memberUid[ attrs["GroupCode"] ] = attrs["memberUid"]
}
END {
n = split(memberUid[GroupCode],uid,SUBSEP)
for ( i = 1; i <= n; i++ )
print givenName[ uid[i] ]
}
function base64_decode(...) { ... }
'
On BSD and Solaris the result is:
Jane
John
While on Linux it is:
John
I don't know where the issue might be; is there something wrong with the base64_decode function and/or the code that uses it?
Your function generates NUL bytes when its argument (encoded string) ends with padding characters (=s). Below is a corrected version of your while loop:
while (i < n) {
v = _BASE64_DECODE_c2i[substr(str,1+i,1)] * 262144 + \
_BASE64_DECODE_c2i[substr(str,2+i,1)] * 4096 + \
_BASE64_DECODE_c2i[substr(str,3+i,1)] * 64 + \
_BASE64_DECODE_c2i[substr(str,4+i,1)]
i += 4
if (v%256 != 0)
out = out sprintf("%c%c%c", int(v/65536), int(v/256), v)
else if (int(v/256)%256 != 0)
out = out sprintf("%c%c", int(v/65536), int(v/256))
else
out = out sprintf("%c", int(v/65536))
}
Note that if the decoded bytes contains an embedded NUL then this approach may not work properly.
Problem is within base64_decode function that outputs some junk characters on gnu-awk.
You can use this awk code that uses system provided base64 utility as an alternative:
{
delete attrs
for (i = 2; i <= NF; i++) {
match($i,/::? /)
key = substr($i,1,RSTART-1)
val = substr($i,RSTART+RLENGTH)
if (RLENGTH == 3) {
cmd = "echo " val " | base64 -di"
cmd | getline val # should also check exit code here
}
attrs[key] = ((key in attrs) ? attrs[key] SUBSEP val : val)
}
if ( /\nuid:/ )
givenName[ attrs["uid"] ] = attrs["givenName"]
else
memberUid[ attrs["GroupCode"] ] = attrs["memberUid"]
}
END {
n = split(memberUid[GroupCode],uid,SUBSEP)
for ( i = 1; i <= n; i++ )
print givenName[ uid[i] ]
}
I have tested this on gnu and BSD awk versions and I am getting expected output in all the cases.
If you cannot use external base64 utility then I suggest you take a look here for awk version of base64 decode.
This answer is for reference
Here's a working base64_decode function (thanks #MNejatAydin for pointing out the issue(s) in the original one):
function base64_decode(str, out,bits,n,i,c1,c2,c3,c4) {
out = ""
# One-time initialization during the first execution
if ( ! ("A" in _BASE64) )
for (i = 1; i <= 64; i++)
# The "_BASE64" array associates a character to its base64 index
_BASE64[substr("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",i,1)] = i-1
# Decoding the input string
n = length(str)
i = 0
while ( i < n ) {
c1 = substr(str, ++i, 1)
c2 = substr(str, ++i, 1)
c3 = substr(str, ++i, 1)
c4 = substr(str, ++i, 1)
bits = _BASE64[c1] * 262144 + _BASE64[c2] * 4096 + _BASE64[c3] * 64 + _BASE64[c4]
if ( c4 != "=" )
out = out sprintf("%c%c%c", bits/65536, bits/256, bits)
else if ( c3 != "=" )
out = out sprintf("%c%c", bits/65536, bits/256)
else
out = out sprintf("%c", bits/65536)
}
return out
}
WARNING: the function requires LANG=C
It also doesn't check that the input is a valid base64 string; for that you can add a simple condition like:
match( str, "^([a-zA-Z/-9+]{4})*([a-zA-Z/-9+]{2}[a-zA-Z/-9+=]{2})?$" )
Interestingly, the code is 2x faster than base64decode.awk, but it's only 3x faster than forking the base64 binary from inside awk.
notes:
In a base64 encoded string, 4 bytes represent 3 bytes of data; the input have to be processed by groups of 4 characters.
Multiplying and dividing an integer by a power of two is equivalent to do bitwise left and right shifts operations.
262144 is 2^18, so N * 262144 is equivalent to N << 18
4096 is 2^12, so N * 4096 is equivalent to N << 12
64 id 2^6, so N * 4096 is equivalent to N << 6
65536 is 2^16, so N / 65536 (integer division) is equivalent to N >> 16
256 is 2^8, so N / 256 (integer division) is equivalent to N >> 8
What happens in printf "%c", N:
N is first converted to an integer (if need be) and then, WITH LANG=C, the 8 least significant bits are taken in for the %c formatting.
How the possible padding of one or two trailing = characters at the end of the encoded string is handled:
If the 4th char isn't = (i.e. there's no padding) then the result should be 3 bytes of data.
If the 4th char is = and the 3rd char isn't = then there's 2 bytes of of data to decode.
If the fourth char is = and the third char is = then there's only one byte of data.
Related
Building FLOAT64 out of the FLOAT32 IEEE 754 hex representation in Bigquery
I would like to build a FLOAT64 out of a FLOAT32 IEEE 754 hex representation in Bigquery. Here's what I've done so far. Is there a better performing, more integrated, safer alternative? WITH T1 AS ( SELECT 0x443dd04f float32_repr -- hex repr of 759.25482177734375 ), T2 AS ( SELECT IF (float32_repr>> 31=0, 1, -1) my_sign, (float32_repr& 0x7f800000) >> 23 my_exponent, float32_repr& 0x007fffff my_mantissa, FROM T1 ) SELECT my_sign*POW(2,my_exponent- 127)* (1+my_mantissa/(1<<23)) my_value FROM T2 -- returns 759.25482177734375 I would also like to know how to do it for FLOAT16 and FLOAT64 representations.
Using the javascript implementation on below link, I think you can define BigQuery UDF and parse FLOAT32 IEEE 754 hex representation by using it. https://gist.github.com/laerciobernardo/498f7ba1c269208799498ea8805d8c30 CREATE TEMP FUNCTION parseFloat(str STRING) RETURNS FLOAT64 LANGUAGE js AS r""" var float = 0, sign, order, mantiss,exp, int = 0, multi = 1; if (/^0x/.exec(str)) { int = parseInt(str,16); }else{ for (var i = str.length -1; i >=0; i -= 1) { if (str.charCodeAt(i)>255) { console.log('Wrong string parametr'); return false; } int += str.charCodeAt(i) * multi; multi *= 256; } } sign = (int>>>31)?-1:1; exp = (int >>> 23 & 0xff) - 127; mantissa = ((int & 0x7fffff) + 0x800000).toString(2); for (i=0; i<mantissa.length; i+=1){ float += parseInt(mantissa[i])? Math.pow(2,exp):0; exp--; } return float*sign; """; SELECT parseFloat('0x443dd04f'); +-----+--------------------+ | Row | f0_ | +-----+--------------------+ | 1 | 759.25482177734375 | +-----+--------------------+
AWK Convert Decimal to Binary
I want to use AWK to convert a list of decimal numbers in a file to binary but there seems to be no built-in method. Sample file is as below: 134218506 134218250 134217984 1610612736 16384 33554432
Here is an awk way, functionized for your pleasure: awk ' function d2b(d, b) { while(d) { b=d%2b d=int(d/2) } return(b) } { print d2b($0) }' file Output of the first three records: 1000000000000000001100001010 1000000000000000001000001010 1000000000000000000100000000
You can try Perl one-liner $ cat hamdani.txt 134218506 134218250 134217984 134217984 1610612736 16384 33554432 $ perl -nle ' printf("%b\n",$_) ' hamdani.txt 1000000000000000001100001010 1000000000000000001000001010 1000000000000000000100000000 1000000000000000000100000000 1100000000000000000000000000000 100000000000000 10000000000000000000000000 $
You can try with dc : # -f infile : Use infile for data # after -e , it is there are the dc command dc -f infile -e ' z # number of values sa # keep in register a 2 o # set the output radix to 2 : binary [ Sb # keep all the value of infile in the register b # ( b is use here as a stack) z 0 <M # until there is no more value ] sM # define macro M in [ and ] lMx # execute macro M to populate stack b [ Lb # get all values one at a time from stack b p # print this value in binary la # get the number of value 1 - # decremente it d # duplicate sa # keep one in register a 0<N # the other is use here ]sN # define macro N lNx' # execute macro N to print each values in binary
Here's an approach that works by first converting the decimal to hex and then converting each hex character to it's binary equivalent: $ cat dec2bin.awk BEGIN { h2b["0"] = "0000"; h2b["8"] = "1000" h2b["1"] = "0001"; h2b["9"] = "1001" h2b["2"] = "0010"; h2b["a"] = "1010" h2b["3"] = "0011"; h2b["b"] = "1011" h2b["4"] = "0100"; h2b["c"] = "1100" h2b["5"] = "0101"; h2b["d"] = "1101" h2b["6"] = "0110"; h2b["e"] = "1110" h2b["7"] = "0111"; h2b["f"] = "1111" } { print dec2bin($0) } function hex2bin(hex, n,i,bin) { n = length(hex) for (i=1; i<=n; i++) { bin = bin h2b[substr(hex,i,1)] } sub(/^0+/,"",bin) return bin } function dec2bin(dec, hex, bin) { hex = sprintf("%x\n", dec) bin = hex2bin(hex) return bin } $ awk -f dec2bin.awk file 1000000000000000001100001010 1000000000000000001000001010 1000000000000000000100000000 1100000000000000000000000000000 100000000000000 10000000000000000000000000
# gawk binary number functions # RPC 09OCT2022 # convert an 8 bit binary number to an integer function bin_to_n(i) { n = 0; #printf(">> %s:", i); for (k = 1; k < 9; k++) { n = n * 2; b = substr(i, k, 1); if (b == "1") { n = n + 1; } } return (n); } # convert a number to a binary number function dectobin(n) { printf("dectobin: n in %d ",n); binstring = "0b"; # some c compilers allow 0bXXXXXXXX format numbers bn = 128; for(k=0;k<8;k++) { if (n >= bn) { binstring = binstring "1"; n = n - bn; } else { binstring = binstring "0" } printf(" bn %d",bn); bn = bn / 2; } return binstring; } BEGIN { FS = " "; # gawk (I think) has no atoi() funciton or equiv. So a table of all # chars (well 256 ascii) can be used with the index function to get # round this for (i = 0; i < 255; i++) { table = sprintf("%s%c", table, i); } } { # assume on stdin a buffer of 8 bit binary numbers "01000001 01000010" is AB etc for (i = 1; i <= NF; i++) printf("bin-num#%d: %x --> %c\n", i, bin_to_n($i), bin_to_n($i)); s = "ABC123string to test"; for (i = 0; i < length(s); i++) { nn = index(table, substr(s,i+1,1))-1; printf("substr :%s:%x:",ss,nn); printf(" :%d: %s\n", i, dectobin(nn)); } }
on top of what others have already mentioned, this function has a rapid shortcut for non-negative integer powers of 2 —- (since they always have a binary pattern of /^[1][0]*$/ ) version 1 : processing in 3-bit chunks instead of bit-by-bit : {m,g}awk ' BEGIN { 1 CONVFMT="%.250g" 1 _^=OFMT="%.25g" } ($++NF=________v1($_))^!_ function ________v1(__,___,_,____,_____) { 6 if (+__==(_+=_^=____="")^(___=log(__)/log(_))) { # 2 2 return \ ___<=_^_^_ \ ? (_+_*_*_)^___ \ : sprintf("%.f%0*.f",--_,___,--_) } 4 ___=(!_!_!_!!_) (_^((_____=_*_*_)+_)-_^_^_+(++_)) 4 gsub("..", "&0&1", ___) 41 while(__) { 41 ____ = substr(___, __%_____*_+(__=int(__/_____))^!_,_)____ } 4 return substr(__=____, index(__, _^(! _))) }' version 2 : first use sprintf() to convert to octals, before mapping to binary function ________v2(__,___,_,____,_____) { 6 if (+__==(_+=_^=____="")^(___=log(__)/log(_))) { # 2 2 return \ ___<=_^_^_ \ ? (_+_*_*_)^___ \ : sprintf("%.f%0*.f",--_,___,--_) } 4 ___=(!_!_!_!!_) (_^((_____=_*_*_)+_)-_^_^_+(++_)) 4 gsub("..", "&0&1", ___) 4 _____=___ 4 __=sprintf("%o%.*o", int(__/(___=++_^(_*--_+_))), _*_+!!_, __%___) 4 sub("^[0]+", "", __) 41 for (___=length(__); ___; ___--) { 41 ____ = substr(_____, substr(__, ___,!!_)*_ + !!_,_)____ } 4 return substr(____, index(____,!!_)) } | 134218506 1000000000000000001100001010 134218250 1000000000000000001000001010 134217984 1000000000000000000100000000 1610612736 1100000000000000000000000000000 16384 100000000000000 33554432 10000000000000000000000000 version 3 : reasonably zippy (29.5 MB/s throughput on mawk2) version by using a caching array and processing 8-bits each round ouputs are zero-padded to minimum 8 binary digits wide . {m,g,n}awk ' 1 function ________(_______,_, __,____,______) { 1 split(_=__=____=______="", _______, _) 2 for (_^=_<_; -_<=+_; _--) { 4 for (__^=_<_; -__<=+__; __--) { 8 for (____^=_<_; -____<=+____; ____--) { 16 for (______^=_<_; -______<=+______; ______--) { 16 _______[_+_+_+_+_+_+_+_+__+__+\ __+__+____+____+______]=\ (_)__ (____)______ } } } } 1 return _^(_<_) } BEGIN { 1 CONVFMT = "%." ((_+=(_^=_<_)+(_+=_))*_)(!_)"g" 1 OFMT = "%." (_*_) "g" 1 _ = ________(_____) } ($++NF=___($_))^!_ function ___(__,____,_,______) { 6 if ((__=int(__))<(______=\ (_*=_+=_+=_^=____="")*_)) { return _____[int(__/_)]_____[__%_] } 16 do { ____=_____[int(__/_)%_]_____[__%_]____ } while (______<=(__=int(__/______))) 6 return int(_____[int(__/_)%_]\ _____[ (__) %_])____ }
You should not use awk for this but bc: $ bc <<EOF ibase=10 obase=2 $(cat file) EOF or bc <<< $(awk 'BEGIN{ print "ibase=10; obase=2"}1' file)
Why do Perl 6 state variable behave differently for different files?
I have 2 test files. In one file, I want to extract the middle section using a state variable as a switch, and in the other file, I want to use a state variable to hold the sum of numbers seen. File one: section 0; state 0; not needed = start section 1 = state 1; needed = end section 1 = section 2; state 2; not needed File two: 1 2 3 4 5 Code to process file one: cat file1 | perl6 -ne 'state $x = 0; say " x is ", $x; if $_ ~~ m/ start / { $x = 1; }; .say if $x == 1; if $_ ~~ m/ end / { $x = 2; }' and the result is with errors: x is (Any) Use of uninitialized value of type Any in numeric context in block at -e line 1 x is (Any) = start section 1 = x is 1 state 1; needed x is 1 = end section 1 = x is 2 x is 2 And the code to process file two is cat file2 | perl6 -ne 'state $x=0; if $_ ~~ m/ \d+ / { $x += $/.Str; } ; say $x; ' and the results are as expected: 1 3 6 10 15 What make the state variable fail to initialize in the first code, but okay in the second code? I found that in the first code, if I make the state variable do something, such as addition, then it works. Why so? cat file1 | perl6 -ne 'state $x += 0; say " x is ", $x; if $_ ~~ m/ start / { $x = 1; }; .say if $x == 1; if $_ ~~ m/ end / { $x = 2; }' # here, $x += 0 instead of $x = 0; and the results have no errors: x is 0 x is 0 = start section 1 = x is 1 state 1; needed x is 1 = end section 1 = x is 2 x is 2 Thanks for any help.
This was answered in smls's comment: Looks like a Rakudo bug. Simpler test-case: echo Hello | perl6 -ne 'state $x = 42; dd $x'. It seems that top-level state variables are not initialized when the -n or -p switch is used. As a work-around, you can manually initialize the variable in a separate statement, using the //= (assign if undefined) operator: state $x; $x //= 42;
How to print lines of text with date older than two days
I have the following text file that I am working with and must be able to parse only the object name value when the creationdatetime is older than two days. objectname ...........................: \Path\to\file\hpvss-LUN-22May12 22.24.11\hpVSS-LUN-29Aug12 22.39.15 creationdatetime .....................: 01-Sep-2012 02:17:43 objectname ...........................: \Path\to\file\hpVSS-LUN-22May12 22.24.11\hpVSS-LUN-28Aug12 22.16.19 creationdatetime .....................: 03-Sep-2012 10:18:09 objectname ...........................: \Path\to\file\hpVSS-LUN-22May-12 22.24.11\hpVSS-LUN-27Aug12 22.01.52 creationdatetime .....................: 03-Sep-2012 10:18:33 An output of the command for the above would be: \Path\to\file\hpvss-LUN-22May12 22.24.11\hpVSS-LUN-29Aug12 22.39.15 Any help will be greatly appreciated. Prem
Date parsing in awk is a bit tricky but it can be done using mktime. To convert the month name to numeric, an associative translation array is defined. The path names have space in them so the best choice for field separator is probably : (colon followed by space). Here's a working awk script: older_than_two_days.awk BEGIN { months2num["Jan"] = 1; months2num["Feb"] = 2; months2num["Mar"] = 3; months2num["Apr"] = 4; months2num["May"] = 5; months2num["Jun"] = 6; months2num["Jul"] = 7; months2num["Aug"] = 8; months2num["Sep"] = 9; months2num["Oct"] = 10; months2num["Nov"] = 11; months2num["Dec"] = 12; now = systime() two_days = 2 * 24 * 3600 FS = ": " } $1 ~ /objectname/ { path = $2 } $1 ~ /creationdatetime/ { split($2, ds, " ") split(ds[1], d, "-") split(ds[2], t, ":") date = d[3] " " months2num[d[2]] " " d[1] " " t[1] " " t[2] " " t[3] age_in_seconds = mktime(date) if(now - age_in_seconds > two_days) print path } All the splitting in the last block is to pick out the date bits and convert them into a format that mktime accepts. Run it like this: awk -f older_than_two_days.awk infile Output: \Path\to\file\hpvss-LUN-22May12 22.24.11\hpVSS-LUN-29Aug12 22.39.15
I would do it in 2 phases: 1) reformat you input file awk '/objectname/{$1=$2="";file=$0;getline;$1=$2="";print $0" |"file}' inputfile > inputfile2 This way you would deal with 01-Sep-2012 02:17:43 | \Path\to\file\hpvss-LUN-22May12 22.24.11\hpVSS-LUN-29Aug12 22.39.15 03-Sep-2012 10:18:09 | \Path\to\file\hpVSS-LUN-22May12 22.24.11\hpVSS-LUN-28Aug12 22.16.19 03-Sep-2012 10:18:33 | \Path\to\file\hpVSS-LUN-22May-12 22.24.11\hpVSS-LUN-27Aug12 22.01.52 2) filter on dates: COMPARDATE=$(($(date +%s)-2*24*3600)) # 2 days off IFS='|' while read d f do [[ $(date -d "$d" +%s) < $COMPARDATE ]] && printf "%s\n" "$f" done < inputfile2
Code Golf: Automata
Locked. This question and its answers are locked because the question is off-topic but has historical significance. It is not currently accepting new answers or interactions. I made the ultimate laugh generator using these rules. Can you implement it in your favorite language in a clever manner? Rules: On every iteration, the following transformations occur. H -> AH A -> HA AA -> HA HH -> AH AAH -> HA HAA -> AH n = 0 | H n = 1 | AH n = 2 | HAAH n = 3 | AHAH n = 4 | HAAHHAAH n = 5 | AHAHHA n = 6 | HAAHHAAHHA n = 7 | AHAHHAAHHA n = 8 | HAAHHAAHHAAHHA n = 9 | AHAHHAAHAHHA n = ...
Lex/Flex 69 characters. In the text here, I changed tabs to 8 spaces so it would look right, but all those consecutive spaces should be tabs, and the tabs are important, so it comes out to 69 characters. #include <stdio.h> %% HAA|HH|H printf("AH"); AAH|AA|A printf("HA"); For what it's worth, the generated lex.yy.c is 42736 characters, but I don't think that really counts. I can (and soon will) write a pure-C version that will be much shorter and do the same thing, but I feel that should probably be a separate entry. EDIT: Here's a more legit Lex/Flex entry (302 characters): char*c,*t; #define s(a) t=c?realloc(c,strlen(c)+3):calloc(3,1);if(t)c=t,strcat(c,#a); %% free(c);c=NULL; HAA|HH|H s(AH) AAH|AA|A s(HA) %% int main(void){c=calloc(2,1);if(!c)return 1;*c='H';for(int n=0;n<10;n++)printf("n = %d | %s\n",n,c),yy_scan_string(c),yylex();return 0;}int yywrap(){return 1;} This does multiple iterations (unlike the last one, which only did one iteration, and had to be manually seeded each time, but produced the correct results) and has the advantage of being extremely horrific-looking code. I use a function macro, the stringizing operator, and two global variables. If you want an even messier version that doesn't even check for malloc() failure, it looks like this (282 characters): char*c,*t; #define s(a) t=c?realloc(c,strlen(c)+3):calloc(3,1);c=t;strcat(c,#a); %% free(c);c=NULL; HAA|HH|H s(AH) AAH|AA|A s(HA) %% int main(void){c=calloc(2,1);*c='H';for(int n=0;n<10;n++)printf("n = %d | %s\n",n,c),yy_scan_string(c),yylex();return 0;}int yywrap(){return 1;} An even worse version could be concocted where c is an array on the stack, and we just give it a MAX_BUFFER_SIZE of some sort, but I feel that's taking this too far. ...Just kidding. 207 characters if we take the "99 characters will always be enough" mindset: char c[99]="H"; %% c[0]=0; HAA|HH|H strcat(c, "AH"); AAH|AA|A strcat(c, "HA"); %% int main(void){for(int n=0;n<10;n++)printf("n = %d | %s\n",n,c),yy_scan_string(c),yylex();return 0;}int yywrap(){return 1;} My preference is for the one that works best (i.e. the first one that can iterate until memory runs out and checks its errors), but this is code golf. To compile the first one, type: flex golf.l gcc -ll lex.yy.c (If you have lex instead of flex, just change flex to lex. They should be compatible.) To compile the others, type: flex golf.l gcc -std=c99 lex.yy.c Or else GCC will whine about ‘for’ loop initial declaration used outside C99 mode and other crap. Pure C answer coming up.
MATLAB (v7.8.0): 73 characters (not including formatting characters used to make it look readable) This script ("haha.m") assumes you have already defined the variable n: s = 'H'; for i = 1:n, s = regexprep(s,'(H)(H|AA)?|(A)(AH)?','${[137-$1 $1]}'); end ...and here's the one-line version: s='H';for i=1:n,s = regexprep(s,'(H)(H|AA)?|(A)(AH)?','${[137-$1 $1]}');end Test: >> for n=0:10, haha; disp([num2str(n) ': ' s]); end 0: H 1: AH 2: HAAH 3: AHAH 4: HAAHHAAH 5: AHAHHA 6: HAAHHAAHHA 7: AHAHHAAHHA 8: HAAHHAAHHAAHHA 9: AHAHHAAHAHHA 10: HAAHHAAHHAHAAHHA
A simple translation to Haskell: grammar = iterate step where step ('H':'A':'A':xs) = 'A':'H':step xs step ('A':'A':'H':xs) = 'H':'A':step xs step ('A':'A':xs) = 'H':'A':step xs step ('H':'H':xs) = 'A':'H':step xs step ('H':xs) = 'A':'H':step xs step ('A':xs) = 'H':'A':step xs step [] = [] And a shorter version (122 chars, optimized down to three derivation rules + base case): grammar=iterate s where{i 'H'='A';i 'A'='H';s(n:'A':m:x)|n/=m=m:n:s x;s(n:m:x)|n==m=(i n):n:s x;s(n:x)=(i n):n:s x;s[]=[]} And a translation to C++ (182 chars, only does one iteration, invoke with initial state on the command line): #include<cstdio> #define o putchar int main(int,char**v){char*p=v[1];while(*p){p[1]==65&&~*p&p[2]?o(p[2]),o(*p),p+=3:*p==p[1]?o(137-*p++),o(*p++),p:(o(137-*p),o(*p++),p);}return 0;}
Javascript: 120 stripping whitespace and I'm leaving it alone now! function f(n,s){s='H';while(n--){s=s.replace(/HAA|AAH|HH?|AA?/g,function(a){return a.match(/^H/)?'AH':'HA'});};return s} Expanded: function f(n,s) { s = 'H'; while (n--) { s = s.replace(/HAA|AAH|HH?|AA?/g, function(a) { return a.match(/^H/) ? 'AH' : 'HA' } ); }; return s } that replacer is expensive!
Here's a C# example, coming in at 321 bytes if I reduce whitespace to one space between each item. Edit: In response to #Johannes Rössel comment, I removed generics from the solution to eek out a few more bytes. Edit: Another change, got rid of all temporary variables. public static String E(String i) { return new Regex("HAA|AAH|HH|AA|A|H").Replace(i, m => (String)new Hashtable { { "H", "AH" }, { "A", "HA" }, { "AA", "HA" }, { "HH", "AH" }, { "AAH", "HA" }, { "HAA", "AH" } }[m.Value]); } The rewritten solution with less whitespace, that still compiles, is 158 characters: return new Regex("HAA|AAH|HH|AA|A|H").Replace(i,m =>(String)new Hashtable{{"H","AH"},{"A","HA"},{"AA","HA"},{"HH","AH"},{"AAH","HA"},{"HAA","AH"}}[m.Value]); For a complete source code solution for Visual Studio 2008, a subversion repository with the necessary code, including unit tests, is available below. Repository is here, username and password are both 'guest', without the quotes.
Ruby This code golf is not very well specified -- I assumed that function returning n-th iteration string is best way to solve it. It has 80 characters. def f n a='h' n.times{a.gsub!(/(h(h|aa)?)|(a(ah?)?)/){$1.nil?? "ha":"ah"}} a end Code printing out n first strings (71 characters): a='h';n.times{puts a.gsub!(/(h(h|aa)?)|(a(ah?)?)/){$1.nil?? "ha":"ah"}}
Erlang 241 bytes and ready to run: > erl -noshell -s g i -s init stop AHAHHAAHAHHA -module(g). -export([i/0]). c("HAA"++T)->"AH"++c(T); c("AAH"++T)->"HA"++c(T); c("HH"++T)->"AH"++c(T); c("AA"++T)->"HA"++c(T); c("A"++T)->"HA"++c(T); c("H"++T)->"AH"++c(T); c([])->[]. i(0,L)->L; i(N,L)->i(N-1,c(L)). i()->io:format(i(9,"H")) Could probably be improved.
Perl 168 characters. (not counting unnecessary newlines) perl -E' ($s,%m)=qw[H H AH A HA AA HA HH AH AAH HA HAA AH]; sub p{say qq[n = $_[0] | $_[1]]};p(0,$s); for(1..9){$s=~s/(H(AA|H)?|A(AH?)?)/$m{$1}/g;p($_,$s)} say q[n = ...]' De-obfuscated: use strict; use warnings; use 5.010; my $str = 'H'; my %map = ( H => 'AH', A => 'HA', AA => 'HA', HH => 'AH', AAH => 'HA', HAA => 'AH' ); sub prn{ my( $n, $str ) = #_; say "n = $n | $str" } prn( 0, $str ); for my $i ( 1..9 ){ $str =~ s( ( H(?:AA|H)? # HAA | HH | H | A(?:AH?)? # AAH | AA | A ) ){ $map{$1} }xge; prn( $i, $str ); } say 'n = ...'; Perl 150 characters. (not counting unnecessary newlines) perl -E' $s="H"; sub p{say qq[n = $_[0] | $_[1]]};p(0,$s); for(1..9){$s=~s/(?|(H)(?:AA|H)?|(A)(?:AH?)?)/("H"eq$1?"A":"H").$1/eg;p($_,$s)} say q[n = ...]' De-obfuscated #! /usr/bin/env perl use strict; use warnings; use 5.010; my $str = 'H'; sub prn{ my( $n, $str ) = #_; say "n = $n | $str" } prn( 0, $str ); for my $i ( 1..9 ){ $str =~ s{(?| (H)(?:AA|H)? # HAA | HH | H | (A)(?:AH?)? # AAH | AA | A )}{ ( 'H' eq $1 ?'A' :'H' ).$1 }egx; prn( $i, $str ); } say 'n = ...';
Python (150 bytes) import re N = 10 s = "H" for n in range(N): print "n = %d |"% n, s s = re.sub("(HAA|HH|H)|AAH|AA|A", lambda m: m.group(1) and "AH" or "HA",s) Output n = 0 | H n = 1 | AH n = 2 | HAAH n = 3 | AHAH n = 4 | HAAHHAAH n = 5 | AHAHHA n = 6 | HAAHHAAHHA n = 7 | AHAHHAAHHA n = 8 | HAAHHAAHHAAHHA n = 9 | AHAHHAAHAHHA
Here is a very simple C++ version: #include <iostream> #include <sstream> using namespace std; #define LINES 10 #define put(t) s << t; cout << t #define r1(o,a,c0) \ if(c[0]==c0) {put(o); s.unget(); s.unget(); a; continue;} #define r2(o,a,c0,c1) \ if(c[0]==c0 && c[1]==c1) {put(o); s.unget(); a; continue;} #define r3(o,a,c0,c1,c2) \ if(c[0]==c0 && c[1]==c1 && c[2]==c2) {put(o); a; continue;} int main() { char c[3]; stringstream s; put("H\n\n"); for(int i=2;i<LINES*2;) { s.read(c,3); r3("AH",,'H','A','A'); r3("HA",,'A','A','H'); r2("AH",,'H','H'); r2("HA",,'A','A'); r1("HA",,'A'); r1("AH",,'H'); r1("\n",i++,'\n'); } } It's not exactly code-golf (it could be made a lot shorter), but it works. Change LINES to however many lines you want printed (note: it will not work for 0). It will print output like this: H AH HAAH AHAH HAAHHAAH AHAHHA HAAHHAAHHA AHAHHAAHHA HAAHHAAHHAAHHA AHAHHAAHAHHA
ANSI C99 Coming in at a brutal 306 characters: #include <stdio.h> #include <string.h> char s[99]="H",t[99]={0};int main(){for(int n=0;n<10;n++){int i=0,j=strlen(s);printf("n = %u | %s\n",n,s);strcpy(t,s);s[0]=0;for(;i<j;){if(t[i++]=='H'){t[i]=='H'?i++:t[i+1]=='A'?i+=2:1;strcat(s,"AH");}else{t[i]=='A'?i+=1+(t[i+1]=='H'):1;strcat(s,"HA");}}}return 0;} There are too many nested ifs and conditional operators for me to effectively reduce this with macros. Believe me, I tried. Readable version: #include <stdio.h> #include <string.h> char s[99] = "H", t[99] = {0}; int main() { for(int n = 0; n < 10; n++) { int i = 0, j = strlen(s); printf("n = %u | %s\n", n, s); strcpy(t, s); s[0] = 0; /* * This was originally just a while() loop. * I tried to make it shorter by making it a for() loop. * I failed. * I kept the for() loop because it looked uglier than a while() loop. * This is code golf. */ for(;i<j;) { if(t[i++] == 'H' ) { // t[i] == 'H' ? i++ : t[i+1] == 'A' ? i+=2 : 1; // Oh, ternary ?:, how do I love thee? if(t[i] == 'H') i++; else if(t[i+1] == 'A') i+= 2; strcat(s, "AH"); } else { // t[i] == 'A' ? i += 1 + (t[i + 1] == 'H') : 1; if(t[i] == 'A') if(t[++i] == 'H') i++; strcat(s, "HA"); } } } return 0; } I may be able to make a shorter version with strncmp() in the future, but who knows? We'll see what happens.
In python: def l(s): H=['HAA','HH','H','AAH','AA','A'] L=['AH']*3+['HA']*3 for i in [3,2,1]: if s[:i] in H: return L[H.index(s[:i])]+l(s[i:]) return s def a(n,s='H'): return s*(n<1)or a(n-1,l(s)) for i in xrange(0,10): print '%d: %s'%(i,a(i)) First attempt: 198 char of code, I'm sure it can get smaller :D
REBOL, 150 characters. Unfortunately REBOL is not a language conducive to code golf, but 150 characters ain't too shabby, as Adam Sandler says. This assumes the loop variable m has already been defined. s: "H" r: "" z:[some[["HAA"|"HH"|"H"](append r "AH")|["AAH"|"AA"|"A"](append r "HA")]to end]repeat n m[clear r parse s z print["n =" n "|" s: copy r]] And here it is with better layout: s: "H" r: "" z: [ some [ [ "HAA" | "HH" | "H" ] (append r "AH") | [ "AAH" | "AA" | "A" ] (append r "HA") ] to end ] repeat n m [ clear r parse s z print ["n =" n "|" s: copy r] ]
F#: 184 chars Seems to map pretty cleanly to F#: type grammar = H | A let rec laugh = function | 0,l -> l | n,l -> let rec loop = function |H::A::A::x|H::H::x|H::x->A::H::loop x |A::A::H::x|A::A::x|A::x->H::A::loop x |x->x laugh(n-1,loop l) Here's a run in fsi: > [for a in 0 .. 9 -> a, laugh(a, [H])] |> Seq.iter (fun (a, b) -> printfn "n = %i: %A" a b);; n = 0: [H] n = 1: [A; H] n = 2: [H; A; A; H] n = 3: [A; H; A; H] n = 4: [H; A; A; H; H; A; A; H] n = 5: [A; H; A; H; H; A] n = 6: [H; A; A; H; H; A; A; H; H; A] n = 7: [A; H; A; H; H; A; A; H; H; A] n = 8: [H; A; A; H; H; A; A; H; H; A; A; H; H; A] n = 9: [A; H; A; H; H; A; A; H; A; H; H; A]