# Vigenère cipher

**URL:** <https://discourse.julialang.org/t/vigenere-cipher/69692>\
**Category:** General Usage\
**Tags:** cryptography\
**Created:** [October 13, 2021, 3:28pm UTC](https://discourse.julialang.org/t/vigenere-cipher/69692 "2021-10-13T15:28:52Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![weymouth](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/weymouth/32/15839_2.png) [@weymouth](https://discourse.julialang.org/u/weymouth)\
**Post date:** [October 13, 2021, 3:28pm UTC](https://discourse.julialang.org/t/vigenere-cipher/69692/1 "2021-10-13T15:28:52Z")

</div>

My daughter is learning about the Vigenère cipher in school and I was helping out (after [reading the wiki](https://en.wikipedia.org/wiki/Vigen%C3%A8re_cipher)). They are doing it by hand in school, but it seemed like it might be a good example to start showing her code, since it’s very tedious and error prone by hand.

Here’s a quick implementation, but it’s a little clunky for a kid to follow. In particular, the encode `→` and decode `←` are just modulo addition and subtraction, but this is obfuscated by all the shifting by `'A'`. The conversion from `String`s to vectors of `Char` and back is also hiding the important part of that line, which is repeating the code word to match the message length.

Any suggestions?

```julia
message = "VIGENERE"
codeword = "GREEN"

→(a::Char,b::Char) = 'A'+((a-'A')+(b-'A'))%26
←(a::Char,b::Char) = 'A'+((a-'A')-(b-'A')+26)%26

→(message::String,codeword::String) = String(collect(message) .→ collect(match_length(codeword,length(message))))
←(message::String,codeword::String) = String(collect(message) .← collect(match_length(codeword,length(message))))
match_length(word,len) = (word^len)[1:len]

```

---

<div class="post-metadata">

**Author:** ![rafael.guerra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael.guerra/32/216610_2.png) [@rafael.guerra](https://discourse.julialang.org/u/rafael.guerra)\
**Post date:** [October 13, 2021, 5:51pm UTC](https://discourse.julialang.org/t/vigenere-cipher/69692/2 "2021-10-13T17:51:58Z")

</div>

Thanks for sharing this brilliant code.  
You should post it at the [Rosetta site](https://rosettacode.org/wiki/Vigen%C3%A8re_cipher#Julia), as it looks much better than the old Julia code posted there (_would be tempted to say the same applies when we compare it to most other languages"_)

Not sure how old your daughter is but it might be appropriate to also provide an implementation where the Vigenère table is built, like their exercise on paper and the description on [Wikipedia](https://en.wikipedia.org/wiki/Vigen%C3%A8re_cipher#Description).

Tried this below, but simpler would probably be better.

```julia
function encrypt(message::String, codeword::String)
	message, codeword = uppercase.((message, codeword))
	message = join([c for c in message if 'A' <= c <= 'Z'])
	codeword = match_length(codeword,length(message))
	join(vsqr[c - 'A' + 1, m - 'A' + 1] for (m,c) in zip(message, codeword))
end

function decrypt(ciphertext::String, codeword::String)
	ciphertext, codeword = uppercase.((ciphertext, codeword))
	codeword = match_length(codeword,length(ciphertext))
	join('A' - 1 + findfirst(t, join(vsqr[c - 'A' + 1, :])) for (t,c) in zip(ciphertext, codeword))
end

match_length(word,len) = (word^len)[1:len]

# Vigenère square (or tabula recta)
const vsqr = reshape(permutedims(hcat([circshift('A':'Z',-i) for i in 0:25]...)), 26, 26)

message = "VIGENERE"
codeword = "GREEN"
ciphertext = encrypt(message, codeword)
decrypt(ciphertext, codeword)

```

_ **NB:** added the `uppercase()` function and removed all non-alphabetic characters from input._

---

<div class="post-metadata">

**Author:** ![weymouth](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/weymouth/32/15839_2.png) [@weymouth](https://discourse.julialang.org/u/weymouth)\
**Post date:** [October 13, 2021, 7:05pm UTC](https://discourse.julialang.org/t/vigenere-cipher/69692/3 "2021-10-13T19:05:37Z")

</div>

I think the table should be an _result_ of the shifting alphabet concept! I do like the idea of being more robust to input strings.

Here’s version 2:

```julia
number(letter) = letter-'A' # number of letters between `letter` and 'A'
letter(number) = 'A'+(number+26)%26 # the `number`th letter after 'A' (negative `number` means it should be before 'A')
# Loop around if you run out of letters in the alphabet. So "A-1=Z" and "A+27=B".

→(a::Char,b::Char) = letter(number(a)+number(b)) # encode using +
←(a::Char,b::Char) = letter(number(a)-number(b)) # decode using -

isletter(a) = a in 'A':'Z' # is `a` inside the letters from 'A' to 'Z'?
cleanup(str::String) = filter(isletter, collect(uppercase.(str))) # make all letters uppercase and filter out non-letters
match_length(word,len) = repeat(word,len)[1:len] # repeat the codeword until you match the length

function →(message::String,codeword::String, ↔ = →)
    plaintext = cleanup(message)
    key = match_length(cleanup(codeword),length(plaintext))
    return String(plaintext .↔ key)
end
←(message::String,codeword::String) = →(message,codeword,←)

```

Trying to slow down a little and add comments to help her (she’s 12 and hasn’t taken any coding yet).

```julia
julia> number('A'),number('L')
(0, 11)

julia> letter(2),letter(-2)
('C', 'Y')

julia> alpha = 'A':'Z';
julia> alpha .→ permutedims(alpha)
26×26 Matrix{Char}:
 'A' 'B' 'C' 'D' 'E' 'F' 'G' 'H' 'I' 'J' 'K' 'L' 'M' 'N' 'O' 'P' 'Q' 'R' 'S' 'T' 'U' 'V' 'W' 'X' 'Y' 'Z'
 'B' 'C' 'D' 'E' 'F' 'G' 'H' 'I' 'J' 'K' 'L' 'M' 'N' 'O' 'P' 'Q' 'R' 'S' 'T' 'U' 'V' 'W' 'X' 'Y' 'Z' 'A'
 'C' 'D' 'E' 'F' 'G' 'H' 'I' 'J' 'K' 'L' 'M' 'N' 'O' 'P' 'Q' 'R' 'S' 'T' 'U' 'V' 'W' 'X' 'Y' 'Z' 'A' 'B'
 'D' 'E' 'F' 'G' 'H' 'I' 'J' 'K' 'L' 'M' 'N' 'O' 'P' 'Q' 'R' 'S' 'T' 'U' 'V' 'W' 'X' 'Y' 'Z' 'A' 'B' 'C'
 ⋮ ⋮ ⋮ ⋮ ⋮ ⋮
 'W' 'X' 'Y' 'Z' 'A' 'B' 'C' 'D' 'E' 'F' 'G' 'H' 'I' 'J' 'K' 'L' 'M' 'N' 'O' 'P' 'Q' 'R' 'S' 'T' 'U' 'V'
 'X' 'Y' 'Z' 'A' 'B' 'C' 'D' 'E' 'F' 'G' 'H' 'I' 'J' 'K' 'L' 'M' 'N' 'O' 'P' 'Q' 'R' 'S' 'T' 'U' 'V' 'W'
 'Y' 'Z' 'A' 'B' 'C' 'D' 'E' 'F' 'G' 'H' 'I' 'J' 'K' 'L' 'M' 'N' 'O' 'P' 'Q' 'R' 'S' 'T' 'U' 'V' 'W' 'X'
 'Z' 'A' 'B' 'C' 'D' 'E' 'F' 'G' 'H' 'I' 'J' 'K' 'L' 'M' 'N' 'O' 'P' 'Q' 'R' 'S' 'T' 'U' 'V' 'W' 'X' 'Y'

julia> cleanup("a & b & z")
3-element Vector{Char}:
 'A': ASCII/Unicode U+0041 (category Lu: Letter, uppercase)
 'B': ASCII/Unicode U+0042 (category Lu: Letter, uppercase)
 'Z': ASCII/Unicode U+005A (category Lu: Letter, uppercase)

julia> match_length("funrun",10)
"funrunfunr"

julia> cyphertext = "I want spearmint gum" → "Gimme!"
"OEMZXYXQMVSQZFKAU"

julia> cyphertext ← "Gimme!"
"IWANTSPEARMINTGUM"

```

---

<div class="post-metadata">

**Author:** ![rafael.guerra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael.guerra/32/216610_2.png) [@rafael.guerra](https://discourse.julialang.org/u/rafael.guerra)\
**Post date:** [October 13, 2021, 7:20pm UTC](https://discourse.julialang.org/t/vigenere-cipher/69692/4 "2021-10-13T19:20:37Z")

</div>

From [this post](https://discourse.julialang.org/t/check-for-letters-only-string/41091/9), one could also clean the non-alphabetic characters in the input `message` as follows:

```julia
join([c for c in message if 'a' <= c <= 'z' || 'A' <= c <= 'Z'])

```

_ **NB:** updated the encrypt function above with half of that because added it after uppercasing._

---

<div class="post-metadata">

**Author:** ![weymouth](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/weymouth/32/15839_2.png) [@weymouth](https://discourse.julialang.org/u/weymouth)\
**Post date:** [October 13, 2021, 7:25pm UTC](https://discourse.julialang.org/t/vigenere-cipher/69692/5 "2021-10-13T19:25:18Z")

</div>

Yeah, it is more clear to stick with letters. I’ve updated it above.

---

<div class="post-metadata">

**Author:** ![StefanKarpinski](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stefankarpinski/32/24_2.png) [@StefanKarpinski](https://discourse.julialang.org/u/StefanKarpinski)\
**Post date:** [October 13, 2021, 8:20pm UTC](https://discourse.julialang.org/t/vigenere-cipher/69692/6 "2021-10-13T20:20:41Z")

</div>

I think this version is really clear. I was doing something along these lines to post but yours is better. One thing to note is that if you do a Vigenère cipher with bytes, then the whole dance goes away and you get what looks like junk for your cypher text, which could be good or bad depending on your preferences.

```julia
encrypt(message::String, codeword::String, + = +) =
    String([a + b for (a, b) in zip(
        codeunits(message),
        Iterators.cycle(codeunits(codeword))
    )])

decrypt(message::String, codeword::String) =
    encrypt(message, codeword, -)

```

```julia
julia> c = encrypt("This is a message", "code")
"\xb7\xd7\xcd\u603\xd8ׅď\xd1\xca\xd6\xe2\xc5\xcc\xc8"

julia> decrypt(c, "code")
"This is a message"

```

---

<div class="post-metadata">

**Author:** ![weymouth](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/weymouth/32/15839_2.png) [@weymouth](https://discourse.julialang.org/u/weymouth)\
**Post date:** [October 13, 2021, 8:48pm UTC](https://discourse.julialang.org/t/vigenere-cipher/69692/7 "2021-10-13T20:48:02Z")

</div>

That’s cool!

I also like the operator-as-argument to avoid copy-pasting the encoder code over to decoder. I’ve edited that to the “teaching” version above. Applying it to the “minimalist” version:

```julia
→(a::Char,b::Char, ± = +) = 'A'+((a-'A')±(b-'A')+26)%26
←(a::Char,b::Char) = →(a,b,-)

→(message::String,codeword::String, ↔ = →) = String(collect(message) .↔ collect(match_length(codeword,length(message))))
←(message::String,codeword::String) = →(message,codeword,←)
match_length(word,len) = (word^len)[1:len]

```

---

<div class="post-metadata">

**Author:** ![rafael.guerra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael.guerra/32/216610_2.png) [@rafael.guerra](https://discourse.julialang.org/u/rafael.guerra)\
**Post date:** [October 13, 2021, 8:53pm UTC](https://discourse.julialang.org/t/vigenere-cipher/69692/8 "2021-10-13T20:53:13Z")

</div>

> [@weymouth](#):
>
> ```julia
> julia> alpha .→ permutedims(alpha)
> 
> ```

Could you please shed some light on the magic in this code line?  
What is the name for this in the manual?

Do not understand in particular: (a) The `→` function takes two chars as input but where are they? (b) how the matrix of chars is then generated from the input StepRange.

Thank you.

---

<div class="post-metadata">

**Author:** ![weymouth](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/weymouth/32/15839_2.png) [@weymouth](https://discourse.julialang.org/u/weymouth)\
**Post date:** [October 13, 2021, 9:19pm UTC](https://discourse.julialang.org/t/vigenere-cipher/69692/9 "2021-10-13T21:19:38Z")

</div>

It’s exactly the same as this:

```julia
julia> num = 0:9;
julia> num .+ num'
10×10 Matrix{Int64}:
 0 1 2 3 4 5 6 7 8 9
 1 2 3 4 5 6 7 8 9 10
 2 3 4 5 6 7 8 9 10 11
 3 4 5 6 7 8 9 10 11 12
 ⋮ ⋮
 6 7 8 9 10 11 12 13 14 15
 7 8 9 10 11 12 13 14 15 16
 8 9 10 11 12 13 14 15 16 17
 9 10 11 12 13 14 15 16 17 18

```

So you have a collection `num` which acts like a column vector, and a transposed collection `num'` which acts like a row vector. Broadcasting the `+` operator fills out the full matrix over all columns and rows. And even though I could write `+.(num,num')` and get the same thing, it looks a little nicer to put the operator in the middle.

---

<div class="post-metadata">

**Author:** ![rafael.guerra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael.guerra/32/216610_2.png) [@rafael.guerra](https://discourse.julialang.org/u/rafael.guerra)\
**Post date:** [October 13, 2021, 9:55pm UTC](https://discourse.julialang.org/t/vigenere-cipher/69692/10 "2021-10-13T21:55:56Z")

</div>

Several fuses were blown. Thank you for helping me restore power to almost every floor 💡

Would the [binary operators that support infix notation](https://docs.julialang.org/en/v1/base/math/#Customizable-binary-operators) be the right section in the manual covering this topic?

---

<div class="post-metadata">

**Author:** ![baggepinnen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/baggepinnen/32/693_2.png) [@baggepinnen](https://discourse.julialang.org/u/baggepinnen)\
**Post date:** [October 14, 2021, 5:01am UTC](https://discourse.julialang.org/t/vigenere-cipher/69692/11 "2021-10-14T05:01:06Z")

</div>

Is this what you’re looking for?  
[https://docs.julialang.org/en/v1/manual/arrays/#Broadcasting](https://docs.julialang.org/en/v1/manual/arrays/#Broadcasting)  
or  
[https://docs.julialang.org/en/v1/manual/mathematical-operations/#man-dot-operators](https://docs.julialang.org/en/v1/manual/mathematical-operations/#man-dot-operators)  
Admittedly, it took me a while to find broadcasting in the manual since it’s a subtopic only.

---

<div class="post-metadata">

**Author:** ![rafael.guerra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael.guerra/32/216610_2.png) [@rafael.guerra](https://discourse.julialang.org/u/rafael.guerra)\
**Post date:** [October 14, 2021, 6:29am UTC](https://discourse.julialang.org/t/vigenere-cipher/69692/12 "2021-10-14T06:29:47Z")

</div>

@baggepinnen, thanks for the feedback. My difficulties were not so much with broadcasting as with the `→(a,b)` function. It started with a mistaken assumption that it was just a visual decoration to help @weymouth’s daughter, and that a function name like `f(a,b)` would do the same job.

It seems that for a number of symbols used in function names with two arguments, Julia’s interpreter allows them to be used as binary operators as well: `a → b` then becomes another way to call the function `→(a,b)`. We would not have had this extra magic if the function had been defined as `f(a,b)` in the first place.

---

<div class="post-metadata">

**Author:** ![RogerP](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rogerp/32/17496_2.png) [@RogerP](https://discourse.julialang.org/u/RogerP)\
**Post date:** [October 17, 2021, 9:19am UTC](https://discourse.julialang.org/t/vigenere-cipher/69692/13 "2021-10-17T09:19:25Z")

</div>

Dreaming: Perhaps include something from [Julia’s unicode set](https://docs.julialang.org/en/v1/manual/unicode-input/) – good for funny output. Or encode an expression so that it is runnable when decrypted?

---

<div class="post-metadata">

**Author:** ![weymouth](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/weymouth/32/15839_2.png) [@weymouth](https://discourse.julialang.org/u/weymouth)\
**Post date:** [October 17, 2021, 8:46pm UTC](https://discourse.julialang.org/t/vigenere-cipher/69692/14 "2021-10-17T20:46:22Z")

</div>

Haha. That’s a great idea!

```julia
→(a::Char,b::Char) = '🌽'+((a-'A')+(b-'A'))%26
←(a::Char,b::Char) = 'A'+((a-'🌽')-(b-'A')+26)%26

```

gives:

```julia
julia> cypher = "ATTACKATDAWN" → "TURING"
"🍐🍊🍇🍅🍌🍍🍐🍊🍑🍅🍆🍐"

julia> cypher ← "TURING"
"ATTACKATDAWN"

julia> 'A':'Z' .→ permutedims('A':'Z')
26×26 Matrix{Char}:
 '🌽' '🌾' '🌿' '🍀' '🍁' '🍂' '🍃' '🍄' '🍅' '🍆' '🍇' '🍈' '🍉' '🍊' '🍋' '🍌' '🍍' '🍎' '🍏' '🍐' '🍑' '🍒' '🍓' '🍔' '🍕' '🍖'
 '🌾' '🌿' '🍀' '🍁' '🍂' '🍃' '🍄' '🍅' '🍆' '🍇' '🍈' '🍉' '🍊' '🍋' '🍌' '🍍' '🍎' '🍏' '🍐' '🍑' '🍒' '🍓' '🍔' '🍕' '🍖' '🌽'
 '🌿' '🍀' '🍁' '🍂' '🍃' '🍄' '🍅' '🍆' '🍇' '🍈' '🍉' '🍊' '🍋' '🍌' '🍍' '🍎' '🍏' '🍐' '🍑' '🍒' '🍓' '🍔' '🍕' '🍖' '🌽' '🌾'
 '🍀' '🍁' '🍂' '🍃' '🍄' '🍅' '🍆' '🍇' '🍈' '🍉' '🍊' '🍋' '🍌' '🍍' '🍎' '🍏' '🍐' '🍑' '🍒' '🍓' '🍔' '🍕' '🍖' '🌽' '🌾' '🌿'
 '🍁' '🍂' '🍃' '🍄' '🍅' '🍆' '🍇' '🍈' '🍉' '🍊' '🍋' '🍌' '🍍' '🍎' '🍏' '🍐' '🍑' '🍒' '🍓' '🍔' '🍕' '🍖' '🌽' '🌾' '🌿' '🍀'
 '🍂' '🍃' '🍄' '🍅' '🍆' '🍇' '🍈' '🍉' '🍊' '🍋' '🍌' '🍍' '🍎' '🍏' '🍐' '🍑' '🍒' '🍓' '🍔' '🍕' '🍖' '🌽' '🌾' '🌿' '🍀' '🍁'
 ⋮ ⋮ ⋮ ⋮ ⋮ ⋮
 '🍒' '🍓' '🍔' '🍕' '🍖' '🌽' '🌾' '🌿' '🍀' '🍁' '🍂' '🍃' '🍄' '🍅' '🍆' '🍇' '🍈' '🍉' '🍊' '🍋' '🍌' '🍍' '🍎' '🍏' '🍐' '🍑'
 '🍓' '🍔' '🍕' '🍖' '🌽' '🌾' '🌿' '🍀' '🍁' '🍂' '🍃' '🍄' '🍅' '🍆' '🍇' '🍈' '🍉' '🍊' '🍋' '🍌' '🍍' '🍎' '🍏' '🍐' '🍑' '🍒'
 '🍔' '🍕' '🍖' '🌽' '🌾' '🌿' '🍀' '🍁' '🍂' '🍃' '🍄' '🍅' '🍆' '🍇' '🍈' '🍉' '🍊' '🍋' '🍌' '🍍' '🍎' '🍏' '🍐' '🍑' '🍒' '🍓'
 '🍕' '🍖' '🌽' '🌾' '🌿' '🍀' '🍁' '🍂' '🍃' '🍄' '🍅' '🍆' '🍇' '🍈' '🍉' '🍊' '🍋' '🍌' '🍍' '🍎' '🍏' '🍐' '🍑' '🍒' '🍓' '🍔'
 '🍖' '🌽' '🌾' '🌿' '🍀' '🍁' '🍂' '🍃' '🍄' '🍅' '🍆' '🍇' '🍈' '🍉' '🍊' '🍋' '🍌' '🍍' '🍎' '🍏' '🍐' '🍑' '🍒' '🍓' '🍔' '🍕'

```

which may be my favorite matrix ever…

---

<div class="post-metadata">

**Author:** ![rafael.guerra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael.guerra/32/216610_2.png) [@rafael.guerra](https://discourse.julialang.org/u/rafael.guerra)\
**Post date:** [October 17, 2021, 9:04pm UTC](https://discourse.julialang.org/t/vigenere-cipher/69692/15 "2021-10-17T21:04:16Z")

</div>

That is what they call the _Green’s algebra_?

---

<div class="post-metadata">

**Author:** ![ptoche](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ptoche/32/23554_2.png) [@ptoche](https://discourse.julialang.org/u/ptoche)\
**Post date:** [October 17, 2021, 9:04pm UTC](https://discourse.julialang.org/t/vigenere-cipher/69692/16 "2021-10-17T21:04:45Z")

</div>

This is a wonderful topic for teaching school kids and young undergraduates too. I showed the basic Caesar cypher to my 12 year-old son and some basic code/pseudo-code to go with it. One thing that we had fun doing was investigating which language had the hardest alphabet to crack for the Caesar cypher: while English and French have 26 letters, Italian has 21 letters, Spanish 27 (used to be 30 until not so long ago), Portuguese and classic latin have 23. Which is the hardest to crack? A similar question can be asked of Vigenère/Bellaso cypher.

**Edit:** The wikipedia pages on Vigenère and Bellaso makes for a fascinating read. Vigenère attributed the original cypher to Bellaso and improved it. The poor Bellaso has been forgotten a little since, but he was a superstar then. The cypher fascinated the great intellects of the time and took centuries to crack.

There’s an interesting math article listed there (sadly it is “gated”) and a more accessible blog post:

Biermann, Norbert (2018). “Analysis of Giouan Battista Bellaso’s cipher challenges of 1555”. _Cryptologia_ . **42** (5): 381–407

> **[Blog reader Norbert Biermann solves famous 16th century crypto mystery](https://scienceblogs.de/klausis-krypto-kolumne/2018/02/17/blog-reader-norbert-biermann-solves-famous-16th-century-crypto-mystery/)**
>
> The challenges of Giouan Battista Bellaso were one of the top unsolved crypto mysteries. Norbert Biermann has now published the last two parts of the solution. When I wrote my article series about the top 25 unsolved crypto mysteries in 2013 (in...

---

<div class="post-metadata">

**Author:** ![StevenSiew](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevensiew/32/218393_2.png) [@StevenSiew](https://discourse.julialang.org/u/StevenSiew)\
**Post date:** [October 17, 2021, 9:19pm UTC](https://discourse.julialang.org/t/vigenere-cipher/69692/17 "2021-10-17T21:19:38Z")

</div>

> One thing that we had fun doing was investigating which language had the hardest alphabet to crack for the Caesar cypher: while English and French have 26 letters, Italian has 21 letters, Spanish 27 (used to be 30 until not so long ago), Portuguese and classic latin have 23. Which is the hardest to crack?

Chinese with 56,000 letters

[https://studycli.org/chinese-characters/number-of-characters-in-chinese/](https://studycli.org/chinese-characters/number-of-characters-in-chinese/)

[https://www.hutong-school.com/how-many-chinese-characters-are-there](https://www.hutong-school.com/how-many-chinese-characters-are-there)

Or would you prefer Chinese with 65,535 letters plus NULL

---

<div class="post-metadata">

**Author:** ![ptoche](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ptoche/32/23554_2.png) [@ptoche](https://discourse.julialang.org/u/ptoche)\
**Post date:** [October 17, 2021, 9:25pm UTC](https://discourse.julialang.org/t/vigenere-cipher/69692/18 "2021-10-17T21:25:10Z")

</div>

> [@StevenSiew](#):
>
> Chinese with 56,000 letters

I wish discourse had a laughing-emoji button. 🤣

---

<div class="post-metadata">

**Author:** ![RogerP](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rogerp/32/17496_2.png) [@RogerP](https://discourse.julialang.org/u/RogerP)\
**Post date:** [October 18, 2021, 5:16pm UTC](https://discourse.julialang.org/t/vigenere-cipher/69692/19 "2021-10-18T17:16:58Z")

</div>

Awesome! We should call it the “Weymouth Matrix”. What are its eigenfruits?

---

<div class="post-metadata">

**Author:** ![rafael.guerra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael.guerra/32/216610_2.png) [@rafael.guerra](https://discourse.julialang.org/u/rafael.guerra)\
**Post date:** [October 18, 2021, 5:26pm UTC](https://discourse.julialang.org/t/vigenere-cipher/69692/20 "2021-10-18T17:26:20Z")

</div>

There’s one MIT PhD on the case [here.](http://fab.cba.mit.edu/classes/864.05/people/dgreensp/eigen/fruit.html)

[Next page](https://discourse.julialang.org/t/vigenere-cipher/69692.md?page=2)
