# How can I create a better cryptogram environment?

I put together a macro that takes two arguments. The first argument is a string of letters which defines a cipher. The second argument is a message to be encrypted using this cipher.

\documentclass{article}
\usepackage{geometry}
\setlength{\parindent}{0pt}
\setlength{\parskip}{2ex}
\makeatletter

%%---------------------------------------------------------------------
%% #1=replacement cipher
%% #2=text to be enciphered
%% starred version will group letters into collections
%% of five letters (stripping out previous space and punctuation)
\newif\if@regroup@letters@by@fives@
\def\makecryptogram{%%
\@ifstar{\@regroup@letters@by@fives@true\@makecryptogram}
{\@regroup@letters@by@fives@false\@makecryptogram}}
\def\@makecryptogram#1#2{%%
\bgroup
\@build@cipher{#1}%%
\if@regroup@letters@by@fives@
\expandafter\@build@groups@of@five\expandafter{#2}%%
\else
\edef\ae@encrypted@message{#2}%%
\fi
\ttfamily
\raggedright
\uppercase\expandafter{\ae@encrypted@message}%%
\par
\egroup}

%%---------------------------------------------------------------------
%% counter to assist in tracking and encoding letters of the
%% alphabet.
\newcounter{@letter@code}
%% #1=replacement cipher
\def\@build@cipher#1{%%
\setcounter{@letter@code}{64}%%
\@@build@cipher#1\relax}
%% #1=current letter representing encryption of nth letter
%% #2=placeholder to test end of content
%%
%% No error should be raised if the cipher has fewer than 26 letters.
%% In that case, only the initial portion of the alphabet will be
%% encrypted.  If there are more than 26 letters for the cipher, do
%% nothing with the extras.
%%
%% Ignore the difference between upper and lower case letters of input
%% characters.  Regardless of input, output is all upper case.
%%
\def\@@build@cipher#1#2{%%
\stepcounter{@letter@code}%%
\ifnum\value{@letter@code}<91\relax
\ifnum#1>\number\numexpra-1\relax
\uccode\value{@letter@code}=\number\numexpr#1-32\relax
\uccode\number\numexpr\value{@letter@code}+32\relax=\number\numexpr#1-32\relax
\else
\uccode\value{@letter@code}=\number\numexpr#1\relax
\uccode\number\numexpr\value{@letter@code}+32\relax=\number\numexpr#1\relax
\fi
\ifx\relax#2\relax
\let\ae@continue@building@cipher\relax
\else
\let\ae@continue@building@cipher\@@build@cipher
\fi
\else
\let\ae@continue@building@cipher\relax
\fi
\ae@continue@building@cipher#2%%
}

%%---------------------------------------------------------------
\newcounter{@ae@letter@counter}
\newif\if@spaceOrPunctuation@
\def\@build@groups@of@five#1{%%
\def\ae@encrypted@message{}%%
\setcounter{@ae@letter@counter}{0}%%
\@@build@groups@of@five#1\relax}
%% <space>s are ignored merely by the manner in which TeX
%% grabs the next token for the arguments of a macro.
%% When gathering into groups of five letters, all punctuation
%% should be suppressed and dropped.
\def\@@build@groups@of@five#1#2{%%
\@spaceOrPunctuation@false
\if-#1\relax\@spaceOrPunctuation@true\fi
\if,#1\relax\@spaceOrPunctuation@true\fi
\if;#1\relax\@spaceOrPunctuation@true\fi
\if:#1\relax\@spaceOrPunctuation@true\fi
\if.#1\relax\@spaceOrPunctuation@true\fi
\if?#1\relax\@spaceOrPunctuation@true\fi
\if!#1\relax\@spaceOrPunctuation@true\fi
\if@spaceOrPunctuation@
\else
\stepcounter{@ae@letter@counter}%%
\xdef\ae@encrypted@message{\ae@encrypted@message#1}%%
\ifnum\value{@ae@letter@counter}=5\relax
\setcounter{@ae@letter@counter}{0}%%
\xdef\ae@encrypted@message{\ae@encrypted@message\space}%%
\fi
\fi
\ifx\relax#2\relax
\let\ae@continue@building@blocks@of@five\relax
\else
\let\ae@continue@building@blocks@of@five\@@build@groups@of@five
\fi
\ae@continue@building@blocks@of@five#2%%
}

\makeatother

\usepackage{environ}
\makeatletter
\NewEnviron{cryptopgram}[1]
{\@build@cipher{#1}%%
\expandafter\@build@groups@of@five\expandafter{\BODY}%%
\ttfamily
\raggedright
\uppercase\expandafter{\ae@encrypted@message}%%
\par
}

\makeatother

\begin{document}

From Bertrand Russell's \emph{Problems of Philosophy}.

\def\mypassage{%%'
Is there any knowledge in the world which is so certain that no
reasonable man could doubt it? This question, which at first sight
might not seem difficult, is really one of the most difficult that
can be asked. When we have realized the obstacles in the way of a
straightforward and confident answer, we shall be well launched on
the study of philosophy--for philosophy is merely the attempt to
answer such ultimate questions, not carelessly and dogmatically, as
we do in ordinary life and even in the sciences, but critically,
after exploring all that makes such questions puzzling, and after
realizing all the vagueness and confusion that underlie our ordinary
ideas.}

Without changing word boundaries:\par
%%            --ABCDEFGHIJKLMNOPQRSTUVWXYZ--
\makecryptogram{BCDNXGHELOPUQASRVFWTYZIJKM}{\mypassage}

Collect letters into groups of five:\par
\makecryptogram*{BCDNXGHELOPUQASRVFWTYZIJKM}{\mypassage}

Pseudo-environment:\par
\begin{cryptopgram}{BCDNXGHELOPUQASRVFWTYZIJKM}
Is there any knowledge in the world which is so certain that no
reasonable man could doubt it? This question, which at first sight
might not seem difficult, is really one of the most difficult that
can be asked. When we have realized the obstacles in the way of a
straightforward and confident answer, we shall be well launched on
the study of philosophy--for philosophy is merely the attempt to
answer such ultimate questions, not carelessly and dogmatically, as
we do in ordinary life and even in the sciences, but critically,
after exploring all that makes such questions puzzling, and after
realizing all the vagueness and confusion that underlie our ordinary
ideas.
\end{cryptopgram}

\end{document}


There are a couple of things I would like to know how to do (if they're even feasible) which I'll raise as separately posted questions.

Here's the context for the first question:

My code works by taking the cipher, remapping the \uccodes for each the letters of the alphabet, then calling \uppercase with these new \uccode values on the message. (The material to be encrypted should just be plain text without any mark up except for possible punctuation.)

I would like to consider a different approach that will not rely on using \uppercase. I would like to be able to temporarily remap character codes within the confines of an environment. Of course, once the environment is over, the old character codes should be restored. So for example, instead of changing \uccodes, I would like to remap character code of C to that of N, D to O, etc. as in the above cryptograms. In other words, when TeX sees the token C with catcode=11, it behaves as though this were N with catcode=11, and so on for all the text tokens with catcode=11.

However, I can't find any such mechanism for remapping character codes. In other words, is there a way of encrypting a cipher that can avoid changing the upper case codes?

• there is no interface to the character code in classic tex. You can use math mode and use the mathcode (which is why url package sets url's in a fake internal math mode) But what have you against uccode? – David Carlisle Nov 10 '13 at 18:53
• @DavidCarlisle I have nothing against uccode per se. I was just wondering whether I could write this using something that behaves like a true environment. In the code I've provided, I'm using NewEnviron which just winds up acting like a macro with arguments masquerading as an environment. – A.Ellett Nov 10 '13 at 18:57
• @DavidCarlisle In part, this was an exercise in using uccodes. I was wondering whether there was something similar that could be done with character codes. – A.Ellett Nov 10 '13 at 18:59
• ah if you don't want to explicitly grab the text you can use mathcodes or you can make the characters active with an active C expanding to X or whatever, or you could look to luatex... – David Carlisle Nov 10 '13 at 19:12

Not sure if any of these are an improvement, but three alternatives none of which grabs the environment body.

foo uses \uppercase but one at a time.

fooo uses \mathcode

foooo uses active characters (so you could not embed this macro in another macro argument)

\documentclass{article}

\newenvironment{foo}[1]{\let\remap\remapxx\par#1\zz}{\par}
\def\zz#1{\ifx\end#1\expandafter\end\else\uppercase{#1}\expandafter\zz\fi}
\def\remapxx#1#2{\uccode#1=#2 }

\newenvironment{fooo}[1]{\let\remap\remapxxx\par#1\zzz}{\par}
\def\zzz#1{\ifx\end#1\expandafter\end\else$\mathrm{#1}$\expandafter\zzz\fi}
\def\remapxxx#1#2{\mathcode#1=#2 }

\newenvironment{foooo}[1]{\let\remap\remapxxxx\let\e\exxxx\begingroup\par#1}{\par}
\def\remapxxxx#1#2{\catcode#1\active\begingroup\uccode\~#1\uppercase{\endgroup\edef~}{\string#2}}
\def\exxxx#1#2{\endgroup\end}

\begin{document}

\begin{foo}{
\remap a x
\remap b y
\remap c z
\remap d r
\remap e s
\remap n 1
\remap o 2
\remap x a
\remap y b
\remap z c}
one abc and xyz
\end{foo}

\begin{fooo}{
\remap a x
\remap b y
\remap c z
\remap d r
\remap e s
\remap n 1
\remap o 2
\remap x a
\remap y b
\remap z c}
one abc and xyz
\end{fooo}

\begin{foooo}{
\remap a x
\remap b y
\remap c z
\remap d r
\remap e s
\remap n 1
\remap o 2
\remap x a
\remap y b
\remap z c
}
one abc and xyz
\end{foooo}

\end{document}
`
• Thank you for the example. Now I can set about posing my next and related question. – A.Ellett Nov 10 '13 at 21:16