Caesar Shifting

22

3

A Caesar shift is probably something we're all familiar with.

(You might be even doing it as a homework task. If so, please don't copy these answers, your teacher almost certainly doesn't want anything like the answers here.)

Just in case you aren't, a Caesar shift is a very simple form of cipher. It takes a string to be ciphered and an integer. Then for every alphabetical character in the string, perform the following transformation:

  1. Work out the character's position in the alphabet (0 based).
  2. Add to that number the integer received at the beginning.
  3. While the number is bigger than 25, subtract 26 from it.
  4. Work out the position of the alphabet it is in.

Leave the rest of the characters unshifted.

Capital letters have to be respected because what is English without capital letters?

Examples:

abcdefghijklmnopqrstuvwxyz 1 -> bcdefghijklmnopqrstuvwxyza
Spam spam spam sausage and spam! 13 -> Fcnz fcnz fcnz fnhfntr naq fcnz!
abcdefghijklmnopqrstuvwxyz 52 -> abcdefghijklmnopqrstuvwxyz
abcdefghijklmnopqrstuvwxyz -1 -> zabcdefghijklmnopqrstuvwxy
ABCxyz 3 -> DEFabc

Assumptions

  • You may receive any printable ASCII character
  • The input number can be negative and will always be bigger than -128 and less then 128 (-128<x<128)
  • You must be able to encode capital letters and non-capital letters reversibly.
  • You must create a full program, not just a function or snippet
  • You will get your input from STDIN or closest alternate
  • You may choose the format for your input, please state this in your answer
  • The characters that need to be shifted are ASCII codepoints 0x41 - 0x5A and 0x61-0x7A - upper and lower case letters

    • Upper case letters should stay upper
    • Lower case letters should stay lower
    • Characters not in this range should be left as they are
  • Note for this challenge, you only have to cipher strings, you don't have to be able to solve them automatically (but giving -x will reverse the cipher)


Since this is a catalog, languages created after this challenge are allowed to compete. Note that there must be an interpreter so the submission can be tested. It is allowed (and even encouraged) to write this interpreter yourself for a previously unimplemented language. Other than that, all the standard rules of must be obeyed. Submissions in most languages will be scored in bytes in an appropriate preexisting encoding (usually UTF-8).

Catalog

The Stack Snippet at the bottom of this post generates the catalog from the answers a) as a list of shortest solution per language and b) as an overall leaderboard.

To make sure that your answer shows up, please start your answer with a headline, using the following Markdown template:

## Language Name, N bytes

where N is the size of your submission. If you improve your score, you can keep old scores in the headline, by striking them through. For instance:

## Ruby, <s>104</s> <s>101</s> 96 bytes

If there you want to include multiple numbers in your header (e.g. because your score is the sum of two files or you want to list interpreter flag penalties separately), make sure that the actual score is the last number in the header:

## Perl, 43 + 2 (-p flag) = 45 bytes

You can also make the language name a link which will then show up in the snippet:

## [<><](https://esolangs.org/wiki/Fish), 121 bytes

<style>body { text-align: left !important} #answer-list { padding: 10px; width: 290px; float: left; } #language-list { padding: 10px; width: 290px; float: left; } table thead { font-weight: bold; } table td { padding: 5px; }</style><script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <link rel="stylesheet" type="text/css" href="//cdn.sstatic.net/codegolf/all.css?v=83c949450c8b"> <div id="language-list"> <h2>Shortest Solution by Language</h2> <table class="language-list"> <thead> <tr><td>Language</td><td>User</td><td>Score</td></tr> </thead> <tbody id="languages"> </tbody> </table> </div> <div id="answer-list"> <h2>Leaderboard</h2> <table class="answer-list"> <thead> <tr><td></td><td>Author</td><td>Language</td><td>Size</td></tr> </thead> <tbody id="answers"> </tbody> </table> </div> <table style="display: none"> <tbody id="answer-template"> <tr><td>{{PLACE}}</td><td>{{NAME}}</td><td>{{LANGUAGE}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr> </tbody> </table> <table style="display: none"> <tbody id="language-template"> <tr><td>{{LANGUAGE}}</td><td>{{NAME}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr> </tbody> </table><script>var QUESTION_ID = 67044; var ANSWER_FILTER = "!t)IWYnsLAZle2tQ3KqrVveCRJfxcRLe"; var COMMENT_FILTER = "!)Q2B_A2kjfAiU78X(md6BoYk"; var OVERRIDE_USER = 32686; var answers = [], answers_hash, answer_ids, answer_page = 1, more_answers = true, comment_page; function answersUrl(index) { return "https://api.stackexchange.com/2.2/questions/" + QUESTION_ID + "/answers?page=" + index + "&pagesize=100&order=desc&sort=creation&site=codegolf&filter=" + ANSWER_FILTER; } function commentUrl(index, answers) { return "https://api.stackexchange.com/2.2/answers/" + answers.join(';') + "/comments?page=" + index + "&pagesize=100&order=desc&sort=creation&site=codegolf&filter=" + COMMENT_FILTER; } function getAnswers() { jQuery.ajax({ url: answersUrl(answer_page++), method: "get", dataType: "jsonp", crossDomain: true, success: function (data) { answers.push.apply(answers, data.items); answers_hash = []; answer_ids = []; data.items.forEach(function(a) { a.comments = []; var id = +a.share_link.match(/\d+/); answer_ids.push(id); answers_hash[id] = a; }); if (!data.has_more) more_answers = false; comment_page = 1; getComments(); } }); } function getComments() { jQuery.ajax({ url: commentUrl(comment_page++, answer_ids), method: "get", dataType: "jsonp", crossDomain: true, success: function (data) { data.items.forEach(function(c) { if (c.owner.user_id === OVERRIDE_USER) answers_hash[c.post_id].comments.push(c); }); if (data.has_more) getComments(); else if (more_answers) getAnswers(); else process(); } }); } getAnswers(); var SCORE_REG = /<h\d>\s*([^\n,<]*(?:<(?:[^\n>]*>[^\n<]*<\/[^\n>]*>)[^\n,<]*)*),.*?(\d+)(?=[^\n\d<>]*(?:<(?:s>[^\n<>]*<\/s>|[^\n<>]+>)[^\n\d<>]*)*<\/h\d>)/; var OVERRIDE_REG = /^Override\s*header:\s*/i; function getAuthorName(a) { return a.owner.display_name; } function process() { var valid = []; answers.forEach(function(a) { var body = a.body; a.comments.forEach(function(c) { if(OVERRIDE_REG.test(c.body)) body = '<h1>' + c.body.replace(OVERRIDE_REG, '') + '</h1>'; }); var match = body.match(SCORE_REG); if (match) valid.push({ user: getAuthorName(a), size: +match[2], language: match[1], link: a.share_link, }); else console.log(body); }); valid.sort(function (a, b) { var aB = a.size, bB = b.size; return aB - bB }); var languages = {}; var place = 1; var lastSize = null; var lastPlace = 1; valid.forEach(function (a) { if (a.size != lastSize) lastPlace = place; lastSize = a.size; ++place; var answer = jQuery("#answer-template").html(); answer = answer.replace("{{PLACE}}", lastPlace + ".") .replace("{{NAME}}", a.user) .replace("{{LANGUAGE}}", a.language) .replace("{{SIZE}}", a.size) .replace("{{LINK}}", a.link); answer = jQuery(answer); jQuery("#answers").append(answer); var lang = a.language; lang = jQuery('<a>'+lang+'</a>').text(); languages[lang] = languages[lang] || {lang: a.language, lang_raw: lang, user: a.user, size: a.size, link: a.link}; }); var langs = []; for (var lang in languages) if (languages.hasOwnProperty(lang)) langs.push(languages[lang]); langs.sort(function (a, b) { if (a.lang_raw > b.lang_raw) return 1; if (a.lang_raw < b.lang_raw) return -1; return 0; }); for (var i = 0; i < langs.length; ++i) { var language = jQuery("#language-template").html(); var lang = langs[i]; language = language.replace("{{LANGUAGE}}", lang.lang) .replace("{{NAME}}", lang.user) .replace("{{SIZE}}", lang.size) .replace("{{LINK}}", lang.link); language = jQuery(language); jQuery("#languages").append(language); } }</script>

Blue

Posted 2015-12-18T13:33:17.590

Reputation: 26 661

8*"You might be even doing it as a homework task. If so, please don't copy these answers, your teacher almost certainly doesn't want anything like the answers here."* I wonder what would happen if you handed a teacher a 90-byte kludge of messy characters and shortcuts... – ASCIIThenANSI – 2015-12-19T01:43:24.710

Answers

9

Pyth, 13 bytes

uXGH.<HQrBG1z

Test suite

Basically, we start with the two strings we want to caesar shift, the lowercase and uppercase alphabets. The list containing both of these is generated by rBG1, bifurcate on uppercase. Then, we reduce over this list, starting with the input string and translating first lowercase, then uppercase letters by the appropriate shift.

isaacg

Posted 2015-12-18T13:33:17.590

Reputation: 39 268

Very nice, I keep forgetting bifurcation exists... :P – FryAmTheEggman – 2015-12-18T15:30:30.513

7

Pyth, 16

XXzG.<GQJrG1.<JQ

Try it online or run a Test Suite

FryAmTheEggman

Posted 2015-12-18T13:33:17.590

Reputation: 16 206

1+1 for Star Wars input, even though I hate esolangs. – Codefun64 – 2015-12-18T16:06:14.410

5

JavaScript (ES6), 122 118 114 111 bytes

alert((p=prompt)().replace(/[a-z]/gi,c=>String.fromCharCode((x=c.charCodeAt(),a=x&96,x-a+n+129)%26-~a),n=+p()))

Saved 4 bytes thanks to @Neil!

Explanation

First prompt takes the input string. The second is the number to shift each letter by.

alert(
  (p=prompt)()              // get input string
    .replace(/[a-z]/gi,c=>  // for each letter
      String.fromCharCode((
        x=c.charCodeAt(),   // x = code of character
        a=x&96,             // a = index of letter a (-1) in same capitalisation
        x-a+n+129)%26-~a    // add N to the letter code and wrap at 26
      ),                    // (+129 is needed to make the % work with negative numbers)
      n=+p()                // get number to shift by
    )
)

user81655

Posted 2015-12-18T13:33:17.590

Reputation: 10 181

1Very nice! But it doesn't work on all inputs; try "abcdefg", -26. This can be fixed by changing the formula to (x-a+n+130)%26. – ETHproductions – 2015-12-18T16:15:12.393

@ETHproductions Thanks for catching that! – user81655 – 2015-12-18T21:36:51.167

"You must create a full program, not just a function or snippet" – LegionMammal978 – 2015-12-19T00:58:58.560

@LegionMammal978 Thanks, I didn't notice that. – user81655 – 2015-12-19T01:13:32.040

Does a=x&96,(x-a+n+129)%26+a+1 help? – Neil – 2015-12-19T01:26:10.997

@Neil Yes it does. :) Thanks! – user81655 – 2015-12-19T01:41:40.893

I changed the 130 to 129 but you made that unnecessary with the ++a. When you change it back, there might be another byte saving you can make - instead of n+130 write n=130+p(). – Neil – 2015-12-20T10:44:09.943

@Neil Oh, I'm not sure what I was thinking there. I think 130+p() would cast n to a string (entering 3 would make n equal 1303!). But I had another idea to save a few bytes. – user81655 – 2015-12-20T11:26:52.260

I can never remember those coercion rules, but I like the +a+1 to -~a change. – Neil – 2015-12-21T00:50:01.670

5

Bash + bsd-games package, 21

caesar $[($1+130)%26]

Builtins FTW! Almost feels like Mathematica. Pyth answers are still shorter though.

Input string read from STDIN and integer from command-line. e.g.:

$ ./caesar.sh 13 <<< "Spam spam spam sausage and spam!"
Fcnz fcnz fcnz fnhfntr naq fcnz!
$

Or if you don't like the builtin:

Bash + coreutils, 63

printf -va %s {a..z}
t=${a:$1%26}${a:0:$1%26}
tr A-Z$a ${t^^}$t

Digital Trauma

Posted 2015-12-18T13:33:17.590

Reputation: 64 644

It seems to me that the coreutils version doesn't work with -127 and/or 127? – Neil – 2015-12-19T01:29:54.740

@Neil Yes. Good catch. Fixed. – Digital Trauma – 2015-12-19T22:54:45.333

3

CJam, 34 22 21 20 bytes

Thanks to FryAmTheEggman for saving 1 byte.

l'[,_el^_26/l~fm<ser

Test it here.

Input is the string to be shifte on the first line and the shift on the second.

Explanation

l    e# Read the first line of input.
'[,  e# Push a string with all ASCII characters up to and including Z.
_el  e# Duplicate and convert to lower case. This only affects the letters.
^    e# Symmetric set-difference: except for the letters, each character appears in both
     e# sets and will be omitted from the difference, but all the letters will be included.
     e# This gives us "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".
_26/ e# Duplicate and split into chunks of 26 characters, separating lower and upper case.
l~   e# Read the second line of input and evaluate.
fm<  e# Shift each of the two substrings by that many characters to the left.
s    e# Convert to a single string, joining both substrings back together.
     e# On the stack are now the input, the letters in alphabetical order and the letters
     e# in shifted order.
er   e# Character transliteration: replace each occurrence of a letter with the character
     e# at the corresponding position in the shifted string.

Martin Ender

Posted 2015-12-18T13:33:17.590

Reputation: 184 808

@FryAmTheEggman The '[,_el^ is a tip from Dennis. I don't know what you mean about f though, it seems like fairly normal usage? – Martin Ender – 2015-12-18T14:35:07.213

I guess I just haven't read enough CJam answers :P It just seems really neat to use it as like a map but change argument order. – FryAmTheEggman – 2015-12-18T14:40:02.500

@FryAmTheEggman actually, I don't need the @ at all. :) – Martin Ender – 2015-12-18T14:42:32.667

2

Java, 249 Bytes

This is as short as I could get it. Reading from stdin eats a ton of bytes. A solution using command line args is noticeably shorter but, this task specified stdin for input.

Input format is the String first followed by the shift number on a new line.

interface C{static void main(String[]a){java.util.Scanner r=new java.util.Scanner(System.in);String s=r.nextLine();int i=(r.nextInt()+26)%26;s.chars().forEach(c->System.out.print((char)(c>64&c<91|c>96&c<123?c<91?65+(c+i-65)%26:97+(c+i-97)%26:c)));}}

Using command line Arguments this solution is only 188 bytes. Input is the String as the first argument and the shift as the second.

interface C{static void main(String[]a){int i=(Integer.parseInt(a[1])+26)%26;a[0].chars().forEach(c->System.out.print((char)(c>64&c<91|c>96&c<123?c<91?65+(c+i-65)%26:97+(c+i-97)%26:c)));}}

ankh-morpork

Posted 2015-12-18T13:33:17.590

Reputation: 1 350

1

R, 111 bytes

code

n=scan();s=scan(,"");for(l in as.numeric(sapply(s,charToRaw))){v=97;if(l<97)v=65;cat(intToUtf8((l+n-v)%%26+v))}

ungolfed

n <- scan()                           # input integer
s <- scan(,"")                        # input string letter by letter
z <- as.numeric(sapply(s,charToRaw))  # get ASCII index of character
for (l in z){                         # loop through chars
  v=97                                # base index of not capitalized chars
  if(l<97)v=65                        # base index of capitalized chars
  cat(intToUtf8((l+n-v)%%26+v))       # paste the char of the shifted index
}

This program takes the user input from STDIN, first the integer shifter and then the string, character by character.

Mutador

Posted 2015-12-18T13:33:17.590

Reputation: 1 361

1

Perl, 81 bytes

(+1 for the -p flag)

s/[^ ]+ //;$n=$&%26;eval"y/a-zA-Z/".($x=chr(97+$n)."-za-".chr$n+96).uc$x."/"if$n

Still working on golfing it down...

Test:

llama@llama:...code/perl/ppcg67044caesar$ printf '1 abcdefghijklmnopqrstuvwxyz\n13 Spam spam spam sausage and spam!\n52 abcdefghijklmnopqrstuvwxyz\n-1 abcdefghijklmnopqrstuvwxyz\n3 ABCxyz' | perl -p caesar.pl; echo
bcdefghijklmnopqrstuvwxyza
Fcnz fcnz fcnz fnhfntr naq fcnz!
abcdefghijklmnopqrstuvwxyz
zabcdefghijklmnopqrstuvwxy
DEFabc

Doorknob

Posted 2015-12-18T13:33:17.590

Reputation: 68 138

1

Python 2, 163 160 bytes

Not sure if I can still golf it down..

import sys;k=sys.argv
def f(x,n):r=chr((ord(x.lower())-97+n)%26+97);return(x,[r,r.upper()][x.isupper()])
print''.join(f(x,int(k[2]))[x.isalpha()] for x in k[1])

Since it's pretty unreadable, here is an ungolfed version:

import sys

def shift(x,n):
    # shift character x by n (all in lowercase)
    r = chr((ord(x.lower())-97+n)%26+97)
    if x.isalpha() and x.islower():
        return r
    elif x.isalpha() and x.isupper():
        return r.upper()
    else:
        return x

# 'map' the function shift to each character of the input   
output = ''.join(shift(x,int(sys.argv[2])) for x in sys.argv[1])
print(output)

Concerning the input: It expects two arguments, the first must be a string and the second an integer (the shift amount). Examples (file is called csr.py):

$ python csr.py gnu 9
pwd
$ python csr.py "Spam spam spam sausage and spam\!" 13
Fcnz fcnz fcnz fnhfntr naq fcnz!

Note: In the second example an escape character and "" are needed

ბიმო

Posted 2015-12-18T13:33:17.590

Reputation: 15 345

1

Japt, 45 43 bytes

Ur"[^\\W_]"_c +V+#¶-(A=Zc <#a?#A:#a)%26+A d

Try it online!

ETHproductions

Posted 2015-12-18T13:33:17.590

Reputation: 47 880

1

Python 2, 118 116 bytes

s,n=input()
print''.join([[c,chr((ord(c)-97+n)%26+97)]['`'<c<'{'],chr((ord(c)-65+n)%26+65)]['@'<c<'[']for c in s)

TFeld

Posted 2015-12-18T13:33:17.590

Reputation: 19 246

You might want to use lists instead of the if/else instances (http://codegolf.stackexchange.com/a/62/36885 ). For example, print''.join([[c,chr((ord(c)-97+n)%26+97)]['~'<c<'{'],chr((ord(c)-65+n)%26+65)]['@'<c<'[']for c in s) is a little shorter, and should work the same. (Except change the tilde to a backtick like you had before--I couldn't get the backtick to display right.)

– mathmandan – 2015-12-18T17:20:03.693

1

Mathematica, 117 bytes

Echo[InputString[]~StringReplace~Thread[Join[a=Alphabet[],b=ToUpperCase@a]->(c=RotateLeft)[a,d=Input[]]~Join~c[b,d]]]

Takes the string, followed by a newline, followed by the shifting factor. Might still be golfable...

LegionMammal978

Posted 2015-12-18T13:33:17.590

Reputation: 15 731

1

Perl 6, 73+1 = 74 bytes

$ perl6 -pe 's:g:i/<[a..z]>/{chr ((my$o=ord ~$/)-(my$a=$o+&96+1)+BEGIN get%26)%26+$a}/' # 73+1

The first line of input is the number of characters to shift the letters up by.

Usage:

$ perl6 -pe 's:g:i/<[a..z]>/{...}/' <<< \
'1
abcdefghijklmnopqrstuvwxyz'
bcdefghijklmnopqrstuvwxyza
$ perl6 -pe 's:g:i/<[a..z]>/{...}/' <<< \
'13
Spam spam spam sausage and spam!'
Fcnz fcnz fcnz fnhfntr naq fcnz!
$ perl6 -pe 's:g:i/<[a..z]>/{...}/' <<< \
'52
abcdefghijklmnopqrstuvwxyz'
abcdefghijklmnopqrstuvwxyz
$ perl6 -pe 's:g:i/<[a..z]>/{...}/' <<< \
'-1
abcdefghijklmnopqrstuvwxyz'
zabcdefghijklmnopqrstuvwxy
$ perl6 -pe 's:g:i/<[a..z]>/{...}/' <<< \
'3
ABCxyz'
DEFabc
$ perl6 -pe 's:g:i/<[a..z]>/{...}/' <<< \
'1000000000000000000000000000000000000000
abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ'
mnopqrstuvwxyzabcdefghijkl
MNOPQRSTUVWXYZABCDEFGHIJKL

Brad Gilbert b2gills

Posted 2015-12-18T13:33:17.590

Reputation: 12 713

1

C++, 163 154 152 bytes

#include<cstdio>
#include<cstdlib>
int main(int x,char**a){for(int c,b,s=atoi(a[1]);1+(c=getchar());putchar(c<b|c>b+26?c:(c+s-b+26)%26+b))b=c<97?65:97;}

Usage:

$ ./caesar -1 <<< "123 a A z Z aBcDeFgHiKlMnOpQrStUvWxYz"
123 z Z y Y zAbCdEfGhJkLmNoPqRsTuVwXy

Simon D.

Posted 2015-12-18T13:33:17.590

Reputation: 11

0

k4, 80 bytes

The program accepts the shift number as a command-line argument and reads text from stdin.

Due to a technical constraint, negative shifts must be encoded with an underscore instead of a hyphen-minus. (Without the parser for interpreting this encoding, the solution would be 64 bytes.)

% wc -c c.k
80 c.k
% cat c.k
c:{x;,/x{y!(x_y),x#y}'.Q`a`A}
.z.pi:{1@x^c[.q.mod[.*{x^((!).$"_-")x}.z.x]26]x;}
% 

Here's the examples executed:

% echo abcdefghijklmnopqrstuvwxyz|q c.k 1
bcdefghijklmnopqrstuvwxyza
% echo 'Spam spam spam sausage and spam!'|q c.k 13
Fcnz fcnz fcnz fnhfntr naq fcnz!
% echo abcdefghijklmnopqrstuvwxyz|q c.k 52
abcdefghijklmnopqrstuvwxyz
% echo abcdefghijklmnopqrstuvwxyz|q c.k _1
zabcdefghijklmnopqrstuvwxy
% echo ABCxyz|q c.k 3
DEFabc
%

And here's a silly little test harness that verifies both encode and decode. (This is zsh; for bash or ksh, change the for loop indexing to ((i=0;i<5;i++)). One-based arrays, ugh....)

% a=(abcdefghijklmnopqrstuvwxyz 'Spam spam spam sausage and spam!' abcdefghijklmnopqrstuvwxyz abcdefghijklmnopqrstuvwxyz ABCxyz)
% b=(1 13 52 _1 3)
% c=(bcdefghijklmnopqrstuvwxyza 'Fcnz fcnz fcnz fnhfntr naq fcnz!' abcdefghijklmnopqrstuvwxyz zabcdefghijklmnopqrstuvwxy DEFabc)
% for ((i=1;i<=5;i++))
for> do
for>     r=$(echo "${a[i]}"|q c.k "${b[i]}")
for>     s=$(echo "$r"|if [[ ${b[i]} == _* ]]; then q c.k "${b[i]/_}"; else q c.k "_${b[i]}"; fi)
for>     printf '%s\t%s\n' "$([[ ${c[i]} == $r ]] && echo good || echo bad)" "$([[ ${a[i]} == $s ]] && echo good || echo bad)"
for> done
good    good
good    good
good    good
good    good
good    good
% 

Aaron Davies

Posted 2015-12-18T13:33:17.590

Reputation: 881