charlie
charlie

Reputation: 187

How do I use modulo arithmetic to implement caesar cipher

I am trying to perform a wrap around of the ASCII alphabet characters in order to perform a shift from a key. For example, if the key is 2 then shift the letter A to become C. But, how do I wrap around Z in order to get to B using modulus arithmetic?

I am trying to implement the following formula:

ci = (pi + key) % 26;

Where ci is the ith ciphered letter and pi is the ith letter to be ciphered.

Upvotes: 0

Views: 3249

Answers (3)

chux
chux

Reputation: 153527

Deeper

 ci = ((pi - 'A' + key) % 26) + 'A';

as well answered by @Sourav Ghosh generates the expected encoded A-Z when key is non-negative and not too large. It reduces pi with - 'A' so the value is in the [0...25] range and the re-offsets it after the % calculation.

To work for the full int range of key takes a little more code.

  • Reduce key range [INT_MIN...INT_MAX] with the functional equivalent range of [-25 ... 25] with key % 26. This step is important to prevent intoverflow with pi - 'A' + key. This can be done once if a number of letters need encoding.

  • Add 26. This insures negative keys are moved to positive ones.

    ci = ((pi - 'A' + key%26 + 26) % 26 ) + 'A';
    

Note: In C, % is not the mod operator, but the remainder operator. Functional differences occur with a%b when a and/or b are negative. ref

Upvotes: 0

Toto
Toto

Reputation: 89

Others have already shown the basic concept of subtracting the offset of ASCII letters, but you have to be careful in when crossing from capital to non capital letters, as there are some symbols in between which I guess you want to avoid. I have added some logic that enables negative shifts and keeps the letter within capital letter or non capital letter space and tests whether it is a letter at all.

#include <stdio.h>

#define ASCII_CAP_LETTER_OFFS 65
#define ASCII_LETTER_OFFS 97
#define NUM_OF_LETTERS 26

char shift_letter (char letter, short shift)
{
  char ci;
  short shift_lcl = shift % NUM_OF_LETTERS;
  if (shift_lcl >= 0)
  { // shift in positive direction
  }
  else
  { // shift in negative direction 
   shift_lcl = NUM_OF_LETTERS + shift_lcl;
  }

  if (letter >= ASCII_CAP_LETTER_OFFS && letter < ASCII_CAP_LETTER_OFFS + NUM_OF_LETTERS)  
    {// its a capital letter
      ci =
    (letter + shift_lcl - ASCII_CAP_LETTER_OFFS) % NUM_OF_LETTERS +
    ASCII_CAP_LETTER_OFFS;
    }
  else if (letter >= ASCII_LETTER_OFFS && letter < ASCII_LETTER_OFFS + NUM_OF_LETTERS) 
    {// its a non capital letter
      ci =
    (letter + shift_lcl - ASCII_LETTER_OFFS) % NUM_OF_LETTERS +
    ASCII_LETTER_OFFS;
    }
  else
    {
      printf ("This was not a letter!\n");
      ci = 0;
    }
  return ci;
}

int main ()
{
  char test_letter = 'a';
  short test_shift = -53;
  char shifted_letter = 0;
  shifted_letter = shift_letter (test_letter, test_shift);
  printf("%c +  %d = %c", test_letter, test_shift, shifted_letter);
}

Upvotes: 2

Sourav Ghosh
Sourav Ghosh

Reputation: 134336

I believe you need to work with "relative" values, rather than absolute values.

Use something like

ci = ((pi - 'A' + key) % 26 ) + 'A';

Character integer constants stores the encoded values, in this case, ASCII. Here, 'A' starts from an offset (decimal value of 65), not from 0. So, before you can wrap the result using a % 26 operation, you need to get that offset out. Once the calculation is done, add the offset back to get the proper ASCII representation.

Upvotes: 2

Related Questions