diff --git a/README.md b/README.md index 9f04ed7..19e7ce1 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # 🔐 Caesar Cipher -A beginner-friendly Python program that encodes messages with the Caesar cipher. +A beginner-friendly Python program that encodes and decodes messages with the Caesar cipher. ## 📚 Table of Contents @@ -14,7 +14,7 @@ A beginner-friendly Python program that encodes messages with the Caesar cipher. ## 🧭 Overview -The Caesar cipher shifts every letter by a fixed number of places (with a shift of 3, `E` becomes `H`). This project, from Udemy's *100 Days of Code: The Complete Python Pro Bootcamp* (Day 8), finds letter positions with `list.index` and wraps around the alphabet with the modulo operator. Part 1 covers encryption; decryption and a combined program follow in later parts. Code lives in `src/`, tests in `tests/`, docs in `docs/`. +The Caesar cipher shifts every letter by a fixed number of places (with a shift of 3, `E` becomes `H`). This project, from Udemy's *100 Days of Code: The Complete Python Pro Bootcamp* (Day 8), finds letter positions with `list.index` and wraps around the alphabet with the modulo operator. Parts 1 and 2 cover encryption and decryption; a combined program follows in Part 3. Code lives in `src/`, tests in `tests/`, docs in `docs/`. ## 📋 Requirements diff --git a/docs/index.md b/docs/index.md index f24a920..ad5c79d 100644 --- a/docs/index.md +++ b/docs/index.md @@ -3,7 +3,7 @@ Each letter is moved `shift` places through the alphabet: `new_position = (ALPHABET.index(letter) + shift) % 26`. The modulo keeps positions in range for shifts past `z`. -Non-letters are left untouched and letter case is preserved. +Decryption is encryption with `-shift`. Non-letters are left untouched and letter case is preserved. API reference: run `doxygen Doxyfile` from the repository root and open `docs/doxygen/html/index.html`. The step-by-step plan is in [plan.md](plan.md). diff --git a/src/caesar_cipher/__init__.py b/src/caesar_cipher/__init__.py index 4c507e6..ac439e9 100644 --- a/src/caesar_cipher/__init__.py +++ b/src/caesar_cipher/__init__.py @@ -1,6 +1,6 @@ ## @file __init__.py # @brief Public API of the caesar_cipher package. -from caesar_cipher.cipher import encrypt +from caesar_cipher.cipher import decrypt, encrypt -__all__ = ["encrypt"] +__all__ = ["encrypt", "decrypt"] diff --git a/src/caesar_cipher/cipher.py b/src/caesar_cipher/cipher.py index 738283e..06449fd 100644 --- a/src/caesar_cipher/cipher.py +++ b/src/caesar_cipher/cipher.py @@ -1,5 +1,5 @@ ## @file cipher.py -# @brief Caesar cipher encryption. +# @brief Caesar cipher encryption and decryption. from caesar_cipher.constants import ALPHABET @@ -19,3 +19,11 @@ def encrypt(text: str, shift: int) -> str: else: result.append(char) return "".join(result) + + +def decrypt(text: str, shift: int) -> str: + ## @brief Decrypt a Caesar-cipher text. + # @param text Encrypted text. + # @param shift Shift that was used to encrypt. + # @return The original text. + return encrypt(text, -shift) diff --git a/tests/test_cipher.py b/tests/test_cipher.py index 9a631df..7332899 100644 --- a/tests/test_cipher.py +++ b/tests/test_cipher.py @@ -1,4 +1,4 @@ -from caesar_cipher import encrypt +from caesar_cipher import decrypt, encrypt from caesar_cipher.env import load_env @@ -24,3 +24,16 @@ def test_load_env(tmp_path): f.write_text("# c\nA=1\nB='two'\n", encoding="utf-8") assert load_env(f) == {"A": "1", "B": "two"} assert load_env(tmp_path / "missing") == {} + + +def test_decrypt_basic(): + assert decrypt("mjqqt", 5) == "hello" + + +def test_decrypt_roundtrip(): + assert decrypt(encrypt("Hello, World!", 7), 7) == "Hello, World!" + + +def test_decrypt_wraps_around_alphabet(): + assert decrypt("abc", 3) == "xyz" + assert decrypt("def", 29) == "abc"