0

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.

charlie
  • 187
  • 4
  • 15

3 Answers3

2

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.

Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
2

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);
}
Toto
  • 79
  • 10
  • Note: Unexpected results with `test_shift < 0`. Also off-by-1 with `letter <= ASCII_CAP_LETTER_OFFS + NUM_OF_LETTERS` which should be `letter < ASCII_CAP_LETTER_OFFS + NUM_OF_LETTERS`. `<=` --> `<` in 2 places. – chux - Reinstate Monica Dec 21 '18 at 16:59
  • Thanks for that hint @chux. I've made some corrections. A negative `shift` is now possible ( by adding `NUM_OF_LETTERS` which makes it a shift in positive direction) – Toto Dec 26 '18 at 09:51
  • Good improvement. Also for your design review, why `short` in `short shift` vs `int`? Usually `short` makes sense for arrays to reduced size. `int` is the usual workhorse type. – chux - Reinstate Monica Dec 26 '18 at 20:12
0

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

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256