Welcome back! In the previous chapter, Numerical Methods & Math, we explored the mathematical engines powering computers, like bit manipulation and prime numbers.
Now, we will use those mathematical tools to solve two of the most critical problems in the digital age:
This is the world of Cryptography and Hashing.
Imagine you are a spy. You have a digital notebook containing sensitive mission details.
We will use Ciphers to solve Problem 1, and Hashing to solve Problems 2 and 3.
The simplest form of cryptography is the Cipher. It swaps letters to hide the meaning.
The Caesar Cipher is thousands of years old. To encrypt a message, you simply "shift" every letter down the alphabet by a fixed number.
If the shift is 1:
A becomes BB becomes CH E L L O becomes I F M M P
We treat letters as numbers (A=0, B=1... Z=25).
The formula is: (Current Letter + Shift) % 26.
Note: The % 26 (modulo) wraps the alphabet around, so Z shifts back to A.
Here is how we implement the encryption loop in C++.
// Simplified from ciphers/caesar_cipher.cpp
std::string encrypt(std::string text, int shift) {
std::string result = "";
for (char c : text) {
// 1. Convert char to number (0-25)
int val = c - 'A';
// 2. Apply shift and wrap around
val = (val + shift) % 26;
// 3. Convert back to char and append
result += (char)(val + 'A');
}
return result;
}
A Hash Function takes any amount of data (a word, a book, a movie) and crushes it into a fixed-size string of characters.
Think of it like a Blender.
Crucial Rule: You cannot turn the juice back into an Apple. Hashing is a one-way street.
SHA-256 is a famous hashing algorithm. It generates a unique 256-bit signature. We use it to check Integrity. If even one bit of a file changes, the Hash changes completely.
We don't usually write SHA-256 from scratch (it's complex math!), but here is how it behaves.
// Simplified usage of hashing/sha256.cpp
#include "sha256.cpp"
int main() {
std::string input = "Hello World";
// Create the fingerprint
std::string fingerprint = hashing::sha256::sha256(input);
// Output: a591a6d40bf420404a011733cfb7b190...
std::cout << fingerprint << std::endl;
}
Remember in Chapter 1 when we talked about arrays? Arrays are fast if you know the index (arr[5]). But what if you only know the name of the data, like "John Smith"?
We can use Hashing to turn "John Smith" into an index number!
Hash("John Smith") might equal 5. So we store John's data at arr[5].
What if Hash("John Smith") is 5, and Hash("Lisa") is also 5?
This is called a Collision. Two items want the same shelf.
Solution: Linear Probing. If shelf 5 is taken, try shelf 6. If 6 is taken, try 7. Keep walking until you find an empty spot.
Here is how we find a spot for a key using Linear Probing.
// Simplified from hashing/linear_probing_hash_table.cpp
void add(int key) {
// 1. Calculate ideal spot (Hash)
int index = hashFxn(key) % totalSize;
// 2. If spot is taken, keep looking (Probing)
while (table[index].key != empty) {
// Move to next spot
index = (index + 1) % totalSize;
}
// 3. Found empty spot!
table[index].key = key;
}
How does SHA-256 actually work? It relies heavily on Bit Manipulation (which we learned in Chapter 6).
It takes the input message and performs thousands of operations: shifting bits left, shifting bits right, and flipping bits (XOR). This "shuffles" the data so thoroughly that it creates a unique fingerprint.
One of the core moves in SHA-256 is the "Right Rotate". It takes bits falling off the right side and pastes them back onto the left side.
// From hashing/sha256.cpp
uint32_t right_rotate(uint32_t n, size_t rotate) {
// 1. Shift right by 'rotate'
// 2. Shift left by (32 - 'rotate')
// 3. Combine them with OR (|)
return (n >> rotate) | (n << (32 - rotate));
}
0001 rotated right by 1 becomes 1000.Cryptography and Hashing allow us to trust our data.
Now that we can secure and organize data, let's look at one of the most common data types in the world: Text. How do we search inside paragraphs or verify patterns in words?
Next Chapter: String Algorithms
Generated by Code IQ