Pushing concatenated character ranges
The uppercase ASCII letters (A-Z) can be pushed as
'[,65>
which generates the string of all characters up to Z, then discards the first 65 (up to @).
All ASCII letters (A-Za-z) can be pushed as
'[,65>_el+
which works as above, then creates a copy, converts to lowercase and appends.
But there's a shorter way to do it!
Then often overlooked
^
operator (symmetric differences for lists) allows to create the same ranges while saving three bytes:'[,_el^
'[,
creates the range of all ASCII characters up to z,_el
creates a lowercase copy and^
keep only characters of both strings that appear in one but not both.Since all letters in the first string are uppercase, all in the second are lowercase and all non-letter characters are in both strings, the result in the string of letters.
The RFC 1642 Base64 alphabet (A-Za-z0-9+/) can be pushed using the above technique and appending the non-letters:
'[,_el^A,s+"+/"+
An equally short way of pushing this string makes use solely of symmetric differences:
"+,/0:[a{A0":,:^
How can we find the string at the beginning?
All used character ranges (A-Z, a-z, 0-9, +, /) can be pushed as the symmetric difference of to range that start at the null byte, namely
'A,'[,^
,'a,'{,^
,'0,':,^
,'+,',,^
and'/,'0,^
.Therefore, executing
:,:^
on"A[a{):+,/0"
will push the desired characters, but not in the right order.How do we find the right order? Brute force to the rescue! The program
"0:A[a{+,/0"e! {:,:^"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"=}=
iterates over all possible permutations of the string, applies
:,:^
and compares the result to the desired output.The radix-64 alphabet used, e.g, by crypt (.-9A-Za-z) can be generated using the above method:
".:A[a{":,:^
This is the shortest method I know.
Since all characters in the desired output are in ASCII order, iterating over permutations isn't needed.
Not all concatenated character ranges can be pushed in the desired order using
:,:^
.(Examples and workarounds to be added.)