For encrypting strings this region of chars is given (in this order!):

* all letters (ascending, first all UpperCase, then all LowerCase)
* all digits (ascending)
* the following chars: `.,:;-?! '()$%&"` 

These are 77 chars! (This region is zero-based.)

Write two methods: 
```python
def encrypt(text)
def decrypt(encrypted_text)
```

Prechecks:
1. If the input-string has chars, that are not in the region, throw an Exception(C#, Python) or Error(JavaScript).
2. If the input-string is null or empty return exactly this value!

For building the encrypted string:
1. For every second char do a switch of the case.
2. For every char take the index from the region. Take the difference from the region-index of the char before (from the input text! Not from the fresh encrypted char before!). (Char2 = Char1-Char2)
Replace the original char by the char of the difference-value from the region. In this step the first letter of the text is unchanged.
3. Replace the first char by the mirror in the given region. (`'A' -> '"'`, `'B' -> '&'`, ...)

Simple example:

* Input:  `"Business"`
* Step 1: `"BUsInEsS"`
* Step 2: `"B61kujla"`
  * `B -> U`
    * `B (1) - U (20) = -19`
    * `-19 + 77 = 58`
    * `Region[58] = "6"`
  * `U -> s`
    * `U (20) - s (44) = -24`
    * `-24 + 77 = 53`
    * `Region[53] = "1"`
* Step 3: `"&61kujla"`

This kata is part of the Simple Encryption Series:
Simple Encryption #1 - Alternating Split
Simple Encryption #2 - Index-Difference
Simple Encryption #3 - Turn The Bits Around
Simple Encryption #4 - Qwerty

Have fun coding it and please don't forget to vote and rank this kata! :-)

Difficulty: introductory

Starter code:

def decrypt(encrypted_text):
	

Solutions:
region = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789.,:;-?! '()$%&" + '"'

def decrypt(encrypted_text):
    if not encrypted_text: return encrypted_text
    
    letters = list(encrypted_text)
    letters[0] = region[-(region.index(letters[0]) + 1)]
    for i in range(1, len(letters)):
        letters[i] = region[region.index(letters[i - 1]) - region.index(letters[i])]
    
    for i in range(1, len(letters), 2):
        letters[i] = letters[i].swapcase()

    return "".join(letters)
    


def encrypt(text):
    if not text: return text
    
    letters = list(text)
    for i in range(1, len(letters), 2):
        letters[i] = text[i].swapcase()
        
    swapped = letters[:]
    for i in range(1, len(letters)):
        letters[i] = region[region.index(swapped[i - 1]) - region.index(swapped[i])]
        
    letters[0] = region[-(region.index(swapped[0]) + 1)]
    return "".join(letters)


def encrypt(text):

    import string

    #prechecks

    char_filter = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789.,:;-?! '()$%&" + '"'
    char_filter = [char for char in char_filter]
    
    if text == "" or text is None:
        return text
    else:
        for char in text:
            if char not in char_filter:
                raise Exception("text contains invalid character")

        #turn text to list
        txt_list = [char for char in text]

        #step 1
        for charIndex in range(1, len(txt_list), 2):

            if txt_list[charIndex] in string.ascii_uppercase:
                txt_list[charIndex] = txt_list[charIndex].lower()

            elif txt_list[charIndex] in string.ascii_lowercase:
                txt_list[charIndex] = txt_list[charIndex].upper()

        #step 2
        for i in range(len(txt_list)-1):
            #x = len-i makes it go through list in reverse order
            x = len(txt_list) - 1 - i
            char2 = char_filter.index(txt_list[x])
            char1 = char_filter.index(txt_list[x - 1])
            txt_list[x] = char_filter[char1-char2]

        #step 3
        char = char_filter.index(txt_list[0])
        txt_list[0] = char_filter[char * - 1 - 1]

        #convert list to text
        text = ""
        for char in txt_list:
            text += char

        return text
    

def decrypt(text):

    import string

    #prechecks

    char_filter = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789.,:;-?! '()$%&" + '"'
    char_filter = [char for char in char_filter]
    if text == "" or text is None:
        return text
    else:
        for char in text:
            if char not in char_filter:
                raise Exception("text contains invalid character")
    
        #turn text to list
        txt_list = [char for char in text]

        #step 3 reverse
        txt_list[0] = char_filter[77 - char_filter.index(txt_list[0]) - 1]

        #step 2 reverse
        for i in range(1, len(txt_list)):
            txt_list[i] = char_filter[char_filter.index(txt_list[i - 1]) - char_filter.index(txt_list[i])]

        #step 1 reverse

        for charIndex in range(1, len(txt_list), 2):

            if txt_list[charIndex] in string.ascii_uppercase:
                txt_list[charIndex] = txt_list[charIndex].lower()

            elif txt_list[charIndex] in string.ascii_lowercase:
                txt_list[charIndex] = txt_list[charIndex].upper()

        #convert list to text
        text = ""
        for char in txt_list:
            text += char

        return text

from string import ascii_uppercase,ascii_lowercase,digits
from itertools import chain

r=ascii_uppercase+ascii_lowercase+digits+'.,:;-?! \'()$%&"'
rr={c:i for i,c in enumerate(r)}

def decrypt(t):
    if t is None: return None
    if t=='': return ''
    o=[r[76-rr[t[0]]]]
    for c in t[1:]:
        o.append(r[(rr[o[-1]]-rr[c])%77])
    return ''.join(c.swapcase() if i%2 else c for i,c in enumerate(o))

def encrypt(t):
    if t is None: return None
    if t=='': return ''
    t=''.join(c.swapcase() if i%2 else c for i,c in enumerate(t))
    return r[76-rr[t[0]]]+''.join(r[(rr[a]-rr[b])%77] for a,b in zip(t,t[1:]))


REGION = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789.,:;-?! '()$%&" + '"'
def decrypt(encrypted_text):
    if not encrypted_text: return encrypted_text
    if any([x not in REGION for x in encrypted_text]): raise Exception   
    three = REGION[::-1][REGION.index(encrypted_text[0])] + encrypted_text[1:]
    two = three[0]
    for i in range(1, len(three)): two += REGION[(-REGION.index(three[i]) + REGION.index(two[i-1]))]
    return ''.join([x.swapcase() if i%2 else x for i,x in enumerate(two)])
    
def encrypt(text):
    if not text: return text
    if any([x not in REGION for x in text]): raise Exception
    one = ''.join([x.swapcase() if i%2 else x for i,x in enumerate(text)])
    two = ''.join([REGION[REGION.index(one[i-1]) - REGION.index(x)] if i != 0 else x for i,x in enumerate(one)])
    return REGION[::-1][REGION.index(two[0])] + two[1:]

import string
reg = string.ascii_uppercase + string.ascii_lowercase + string.digits + '''.,:;-?! '()$%&"'''

def decrypt(text):
    if not text: return text
    for i in text:
        if i not in reg: raise Exception

    dec = [reg[reg[::-1].index(text[0])]]

    for i in range(1, len(text)):
        tem = reg.index(text[i])
        ad = abs(tem - 77) + reg.index(dec[-1])
        if ad < 77: dec.append(reg[ad])
        else: dec.append(reg[ad - 77])
        
    for i in range(1, len(dec), 2): dec[i] = dec[i].swapcase()
    return ''.join(dec) 


def encrypt(text):
    if not text: return text
    for i in text:
        if i not in reg: raise Exception

    a, enc = list(text), [reg[-(reg.index(text[0]) + 1)]]

    for i in range(1, len(a), 2): a[i] = a[i].swapcase()

    for i in range(1, len(a)):
        ind = (reg.index(a[i-1]) - reg.index(a[i]))
        enc.append(reg[ind])

    return ''.join(enc)


template_chars_region = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789.,:;-?! '()$%&"
template_chars_region += '"'

def decrypt(encrypted_text):
    result = ''
    if(not encrypted_text or 0 == len(encrypted_text)):
        return encrypted_text
    template_length = len(template_chars_region)
    first_original_char = template_chars_region[(template_length - template_chars_region.index(encrypted_text[0]) - 1)]
    result = first_original_char
    for index, single_char in enumerate(''.join(encrypted_text[1:])):
        previous_template_index = template_chars_region.index(first_original_char)
        difference_template_index = template_chars_region.index(single_char)
        
        result += template_chars_region[(previous_template_index - difference_template_index + template_length)%template_length]
        first_original_char = result[-1]
    result_copy = ''
    for index, single_char in enumerate(result):
        if(0 != index % 2):
            if(single_char.islower()):
                single_char = single_char.upper()
            else:
                single_char = single_char.lower()
        result_copy += single_char
    result = result_copy
    return result


def encrypt(text):
    result = ''
    if(not text or 0 == len(text)):
        return text
    text_copy = ''
    template_length = len(template_chars_region)
    for index, single_char in enumerate(text):
        char_template_index = template_chars_region.index(single_char)
        if(0 != index % 2 and single_char.isalpha()):
            if(single_char.islower()):
                single_char = single_char.upper()
            else:
                single_char = single_char.lower()
        text_copy += single_char
    for index, char in enumerate(text_copy):
        if(0 == index):
            continue
        previous_template_index = template_chars_region.index(text_copy[index - 1])
        current_template_index = template_chars_region.index(text_copy[index])
        result += template_chars_region[(previous_template_index - current_template_index + template_length)%template_length]
    result = template_chars_region[template_length - template_chars_region.index(text[0]) - 1] + result
    return result


from string import ascii_uppercase, ascii_lowercase, digits

s_Base = ascii_uppercase + ascii_lowercase + digits + '.,:;-?! \'()$%&"'

def decrypt(text):
    if not text:
        return text
        
    text = s_Base[-(s_Base.index(text[0]) + 1)] + text[1:]

    for index, letter in enumerate(text[1:]):
        x = s_Base.index(text[index])
        y = s_Base.index(letter)
        diff = x - y if x - y >= 0 else x - y + 77 
        text = text[:index + 1] + s_Base[diff] + text[index + 2:]

    return ''.join(letter.swapcase() if index % 2 != 0 else letter for index, letter in enumerate(text))

def encrypt(text): 
    if not text:
        return text

    for letter in text:
        if letter not in s_Base:
            raise ValueError
    
    text = ''.join(letter.swapcase() if index % 2 != 0 else letter for index, letter in enumerate(text))
    
    for index, letter in enumerate(text[1:]):
        if index == 0:
            x = s_Base.index(text[index])            
        y = s_Base.index(letter)
        
        diff = x - y if x - y >= 0 else x - y + 77 
        x = y
        
        text = text[:index + 1] + s_Base[diff] + text[index + 2:]
  
    text = s_Base[-(s_Base.index(text[0]) + 1)] + text[1:]

    return text

from string import ascii_lowercase as l, ascii_uppercase as u, digits as di
encrypt=lambda s:doall(s)
decrypt=lambda s:doall(s,True)
def doall(s,d=False):
    if not s:return s
    all_char = list(u+l+di+".,:;-?! '()$%&" + '"')
    s = "".join([j.swapcase() if i & 1 else j for i, j in enumerate(s)]) if not d else s
    new,well = [],[all_char[-1-all_char.index(s[0])]]
    for i in range(len(s) - 1):
        c_ = well[-1] if d else s[i]
        t_ = s[i + 1]
        diff = all_char.index(c_) - all_char.index(t_)
        well.append(all_char[diff])
        new.append(all_char[diff])
    encode = all_char[-1 - all_char.index(s[0])] + "".join(new)
    decode = "".join([j.swapcase()if i&1 else j for i,j in enumerate("".join(well))])
    return [encode,decode][d]

import string

region = string.ascii_uppercase + string.ascii_lowercase + ''.join(str(x) for x in range(10)) + ".,:;-?! '()$%&\""

def switch_case(char):
    return char.lower() if char.isupper() else char.upper() if char.islower() else char

def decrypt(encrypted_text):
    if (encrypted_text is None) or (encrypted_text == ''): return encrypted_text
    current_char = region[-region.index(encrypted_text[0]) - 1]
    ret = current_char
    for char in list(encrypted_text)[1:]:
        ret += region[(region.index(ret[-1]) - region.index(char)) % 77]
    ret = ''.join([switch_case(x) if i % 2 != 0 else x for i, x in enumerate(ret)])
    return ret


def encrypt(text):
    if (text is None) or (text == ''): return text
    text = ''.join([switch_case(x) if i % 2 != 0 else x for i, x in enumerate(text)])
    index_char, index_next = [region.index(char) for char in text], [region.index(char) for char in text[1:]]
    text = region[-region.index(text[0]) - 1] + ''.join([region[(c - n) % 77] for c, n in zip(index_char, index_next)])
    return text