# The Improved Caesar Pig Latin Cipher

The problem with the Caesar cipher is the resulting words are often unpronounceable. The problem with Pig Latin is that it is easy to decode. Why not combine them?

Input

A word consisting of the 26 english letters.

Output

First, change every consonant in the word to the next consonant in the alphabet. So, b goes to c, d goes to f, and z goes to b. Then, change every vowel to the next vowel in the alphabet (u goes to a). Last, only if the first letter of the word is a consonant, move that letter to the end of the word and add "ay" to the end of the word.

Examples:

cat -> evday
dog -> uhfay
eel -> iim
• This is , so the shortest code wins.
• Case does not matter.
• Vowels that will be used are A, E, I, O, and U
• no, but you can if you want – qazwsx Sep 16 '18 at 21:04
• Welcome to PPCG! Very nice challenge, clearly presented. – Jonathan Allan Sep 16 '18 at 21:07
• Suggested test case: z → bay – Arnauld Sep 16 '18 at 21:16
• jot ot e wisz opvisitvoph dupdiqv cav o fu puv vjopl ov jimqt xovj vji ecomovz vu qsupuapdi vji xusftvay – Shadow Sep 17 '18 at 4:36
• Suggested test case: the → jivay? (That is, if the word starts with multiple consonants, do we only move one of them?) – DLosc Sep 17 '18 at 17:03

# Stax, 20 bytes

ù≥±╘├*Lâ■8O±âΓ║Θæ%º•

Run and debug it

Explanation

Vc:tVv:tBcVc#{sL"ay"+}ML             #Full program, unpacked, implicit input
Vc:t                                 #Push consonants and ring translate input
Vv:t                             #Push vowels and ring translate input
BCvc#                        #Push first letter and tail of word, find number
#of occurrences to consonants
{sL"ay"+}M              #If truthy (word starts with consonant)

I went through a few iterations and finally got it down to 20. My original solution was 53 bytes.

# Ruby, 96 69 bytes

->s{/^[^aeiou]/=~(r=s.tr'zb-yadehinotu','b-zefijopuva')?$'+$&+'ay':r}

Try it online!

Fun fact of the day: tr() matches strings right-to-left. I always assumed it was left-to-right.

# R, 86 85 bytes

Simple way. chartr has the charming and useful property that it can specify letter ranges, which save a few bytes.

-1 bytes by stealing @GB's Ruby solution's translation strings - upvote it!

Try it online!

# Java (JDK), 167 bytes

s->{String r="",a="aeiouabcdfghjklmnpqrstvwxyzb",c=s.split("")[0];s=a.indexOf(c)>5?s.substring(1)+c+"ux":s;for(var d:s.split(""))r+=a.charAt(a.indexOf(d)+1);return r;}

Try it online!

# 05AB1E, 21 bytes

žMDÀ‡žNDÀ‡¬žNsåiÀ…ÿay

Try it online!

Explanation

žMDÀ‡                   # replace each vowel with the next vowel in the alphabet
žNDÀ‡              # replace each consonant with the next consonant in the alphabet
¬žNsåi        # if the first letter is a consonant
À…ÿay   # rotate the word left and add "ay"

# Node.js 10.9.0, 121 116 bytes

Expects the input string in lower case.

s=>(v=n=>2130466>>n&1)((a=Buffer(s).map(n=>(g=i=>v(n=n%61?n+1:97)^i?g(i):n)(v(n))))[0],a+='')?a:a.slice(1)+a[0]+'ay'

Try it online!

### Identifying vowels

To identify vowels, the helper function $v$ uses the following bitmask:

2130466 = 000001000001000001000100010
^     ^     ^   ^   ^
zyxwvutsrqponmlkjihgfedcba-

And does:

(2130466 >> n) & 1

We don't need to mask out the relevant bits of $n$, because it is done implicitly. Below is the relevant quote from the ECMAScript specification:

Let shiftCount be the result of masking out all but the least significant 5 bits of rnum, that is, compute rnum & 0x1F.

### Commented

s =>                      // s = input string
( v = n =>              // v = helper function taking n = ASCII code in [97..122]
2130466 >> n & 1      //     and returning 1 if the corresponding character is a vowel
)                       //     or 0 otherwise (see the previous paragraph)
(                       // this statement will ultimately invoke v on a[0]
( a = Buffer(s)       //   convert s to a Buffer, holding ASCII codes
.map(n =>           //   for each ASCII code n in there:
( g = i =>        //     g = recursive function taking i = vowel flag
v(              //       update n and invoke v on the new value:
n = n % 61 ?  //         if n is less than 122 (meaning less than 'z'):
n + 1       //           increment n
:             //         else:
97          //           wrap around by setting n to 97 (meaning 'a')
) ^ i ?         //       if v(n) is not equal to i:
g(i)          //         do recursive calls until it is
:               //       else:
n             //         stop recursion and return the new n
)(v(n))           //     initial call to g with i = v(n)
)                   //   end of map()
)[0],                 //   invoke v on a[0]
a += ''               //   coerce the updated Buffer back to a string
) ?                     // if a[0] is a vowel:
a                     //   return the string as-is
:                       // else:
a.slice(1) + a[0]     //   move the leading consonant to the end
+ 'ay'                //   and append the 'ay' suffix

# Python 2, 15312111099 91 bytes

lambda s:[s[1:]+s[0]+"ux",s][s[0]in'aeiou'].translate(8*".ecdfighjoklmnpuqrstvawxyzb.....")

Try it online!

8 bytes shaved off due to a suggestion by Matthew Jensen

• You can save 8 bytes by using string.translate() function: lambda s:[s[1:]+s[0]+"ux",s][s[0]in'aeiou'].translate(8*".ecdfighjoklmnpuqrstvawxyzb.....") – Matthew Jensen Nov 13 '19 at 23:50
• @MatthewJensen Clever! Usually I avoid translate in Python 2... – Chas Brown Nov 14 '19 at 4:11
• Coincidentally, it now also works for upper case as well – Matthew Jensen Nov 17 '19 at 19:35

# T-SQL, 169 bytes

SELECT IIF(CHARINDEX(LEFT(a,1),'aeiou')=0,SUBSTRING(a,2,99)+LEFT(a,1)+'ay',a)FROM
(SELECT TRANSLATE(v,'aeioubcdfghjklmnpqrstvwxyz','eiouacdfghjklmnpqrstvwxyzb')a FROM t)s

Input is via a pre-existing table, per our IO standards.

Performs the characters substitution first, using the (new to SQL 2017) TRANSLATE function, then checks the first character.

Annoying long mostly due to SQL keyword length.

# Shell script, 70 bytes

tr a-z b-{|tr eiou{bfjpv fjpvbeioua|sed -E 's/^([^aeiou])(.*)/\2\1ay/'

Try it online!

h(c:r)|elem c"aeiou"=c:r|True=r++c:"ay"

Try it online!

# Retina 0.8.2, 50 bytes

Tuoaei\oub-df-hj-np-tv-zb
^([^aeiou])(.*)
$2$1ay

Try it online! Link includes test cases. Similar approach to the R answer. Explanation:

Tuoaei\oub-df-hj-np-tv-zb

o refers to the other set, i.e. aei\oub-df-hj-np-tv-zb, which expands to aeioubcdfghjlkmnpqrstvwxyzb, so uo expands to uaeioubcdfghjlkmnpqrstvwxyzb. This results in the following transliteration:

uaeioubcdfghjlkmnpqrstvwxyzb
aeioubcdfghjlkmnpqrstvwxyzb

The second u and b are ignored as they can never be matched, so this provides the desired cipher. (Note that in Retina 1 you can of course use v instead of aei\ou for a 5 byte saving.)

^([^aeiou])(.*)
$2$1ay

If the first letter is not a vowel, rotate it to the end and suffix ay.

# Jelly, 24 bytes

,ṙ¥1y

Try it online!

Saved 1 byte thanks to Jonathan Allan (use the two-char syntax rather than quotes).

• Unfortunately far longer sane, non-sneaky version (1-byte shorter sneaky equivalent). I won't be able to update this within the next few hours, but do point out if you find improvements. – Mr. Xcoder Sep 16 '18 at 22:01
• Use ⁾ay for a byte. The © could go after the e since that is what you are storing. I have ØẹØḄ,żṙ€¥1ẎyṙḢe©ØḄƊ®⁾ayẋ. – Jonathan Allan Sep 17 '18 at 1:09

# ><>, 94 92 bytes

i:0(?v"a"%
2%?!v\~r:5g
ol?!;>4g
ecdfighjoklmnpuqrstvawxyzb
1   1   1     1     1

Try it online!

Edit: Saved 2 bytes by taking input mod 97 rather than 32, so the dictionary could start at the beginning of the line. Previous version:

i:0(?v84*%
2%?!v\~r:5g
37r}<vr*46*
ol?!;>4g
ecdfighjoklmnpuqrstvawxyzb
1   1   1     1     1

# Red, 149 bytes

func[s][c:"aeioua.bcdfghjklmnpqrstvwxyzb"t: copy""foreach p s[append t c/(1
+ index? find c p)]if 7 < index? find c t/1[move t tail t append t"ay"]t]

Try it online!

As (almost) always, the longest entry

# F# (Mono), 197 bytes

let f(s:string)=
let a="aeiouabcdfghjklmnpqrstvwxyzb"
new string(s|>Seq.map(fun c->a.[a.IndexOf(c)+1])|>Seq.toArray)|>(fun x->if a.IndexOf(x.[0])>5 then x.Substring(1)+(string x.[0])+"ay"else x)

Try it online!

# Perl 5, 56 bytes

y/a-z/ecdfighjok-npuq-tvaw-zb/;s/^([^aeiou])(.*)/$2$1ay/

Try it online!

• Do you need the -s between c and d or g and h? – Neil Sep 18 '18 at 9:37
• @Neil no I don't. I wrote a program to compress the ranges... and got the logic wrong :) Thanks. – hobbs Sep 19 '18 at 2:56
• Normally in such cases you write <s>58</s> 56 bytes in the header and many people add a "Edit: Saved 2 bytes thanks to @Neil." or some such when they are helped in a comment. – Neil Sep 19 '18 at 8:14

# JavaScript (SpiderMonkey), 107 bytes

x=>x.replace(/./g,y=>(z='aeiouabcdfghjklmnpqrstvwxyzb')[z.search(y)+1]).replace(/^([^aeiou])(.+)/,'$2$1ay')

Try it online!

Expects input in lowercase.

Replaces each character of the string with a the one after it in the string 'aeiouabcdfghjklmnpqrstvwxyzb', and then piglatinifies anything with an initial consonant.

# PHP, 112 bytes

<?=strtr(($s=strstr(aeiou,($s=$argn)[0])?$s:substr($s,1).$s[0].ux),join(range(a,z)),ecdfighjoklmnpuqrstvawxyzb);

or

<?=strtr(($s=strstr(aeiou,($argn)[0])?$argn:substr($s,1).$s[0].ux),join(range(a,z)),ecdfighjoklmnpuqrstvawxyzb); assume lower case input. Run as pipe with -nR or try them online. You could as well use strtr($s,uzbcadfgehjklminpqrsotvwxy,range(a,z)) instead of
strtr(\$s,range(a,z),ecdfighjoklmnpuqrstvawxyzb).

# Dyalog APL (SBCS), 57 bytes

{a←⎕A{(⍺~⍵)⍵}'AEIOU'⋄('AY',⍨1⌽⊢)⍣(∊/⊃¨⍵a)⊢(⊂⍵⍳⍨∊a)⌷∊1⌽¨a}

Try it online!

Takes input in uppercase only! (Because ⎕A is uppercase alphabet)

• ⎕A{(⍺~⍵)⍵}'AEIOU': Vector of consonants and vowels
• (⊂⍵⍳⍨∊a)⌷∊1⌽¨a: Uses indices of each letter of the word in the normal alphabet (⊂⍵⍳⍨∊a) to index () into the cipher ∊1⌽¨a.
• ('AY',⍨1⌽⊢)⍣(∊/⊃¨⍵a): Moves first letter to the end and appends 'AY', if the first letter is a consonant.

Thanks for the cool challenge!