Now that we know about proc, let's implement the Caesar cipher again, this time with an alphabet of our choosing.
First, we define a global variable Alphabet that lists all the characters we intend to encode. We would like to be able to distinguish all characters which print out, as well as blanks (that is, those which you have keys for on your keyboard). To avoid typing in the full list of characters we want4.13, we generate a list of all the integers less than 256 and convert it to a character string with convert(,bytes). Then we use Select and IsPrintable to pick out the usual printable characters. Finally, since we would like to have a space and a newline as characters in our alphabet, we add them to the resulting string with cat.
>
Alphabet := cat("", Select(IsPrintable,convert([seq(i,i=1..255)],bytes)));
We will use this particular alphabet regularly in the next few sections.
Next, we define two functions which perform the analogue of
convert(,bytes), but with our special alphabet.
ListToString will take a list of integers and replace it
with the character from that position in Alphabet, and
StringToList does the reverse process.
For each character in the input string, StringToList uses
SearchText to find its position in Alphabet. We need to
subtract one from this position because we would like our characters to be
numbered from 0 rather than 1. Thus, in the Alphabet above,
\t is character 0, and the numeric code for is 96.
Alphabet is declared as a global variable.
>
StringToList := proc(text::string)
local i;
global Alphabet;
[seq(SearchText(text[i],Alphabet)-1, i=1..length(text))];
end:
>
ListToString := proc(numlist::list(nonnegint))
local i;
global Alphabet;
cat(seq(Alphabet[numlist[i]+1], i=1..nops(numlist)));
end:
Now the Caesar cipher is written in terms of these functions. Note that since 0 corresponds to a printable character, we don't have to add and subtract 1 as before.
p := length(Alphabet);
textnum := StringToList(plaintext);
codenum := [seq( modp(textnum[i]+shift, p), i=1..length(plaintext)) ];
ListToString(codenum);
end:
>
Caesar2:= proc(plaintext::string, shift::integer)
local textnum,codenum,i,p;
global Alphabet;
It works as follows.
Let's try it out.
>
Caesar2("Veni, Vidi, Vici",3);
To decode, we can just use the negative of the shift amount.
>
Caesar2(
>
text:="I have heard the mermaids singing, each to each.
I do not think that they will sing to me.";
In this sample, we have used a text which contains a newline character. Maple
represents this as
\n inside a text string. Since we will treat
the newline as a regular character, it too will encode to something.
If you look carefully at the output below, you can probably pick it out.
>
Caesar2(text,64);
>
Caesar2(
If we would like to see the decrypted text with the newlines expanded, we need to use printf.
>
printf(
One thing that we should check is what happens if our message should contain a
character that is not in our alphabet. Rather than changing our text, we'll
dramatically shorten the alphabet.
>
Alphabet:="abcdefghijklmnop_";
Now let us go then, you and I, and re-encode the test message using the new alphabet, but with a shift of zero. Ordinarily, a shift by 0 would not change the text at all. But with the shortened alphabet, something happens to the characters that aren't mentioned.
>
Caesar2(text,0);
Can you explain why this happens?