#include <iostream>
#include <string>
#include <vector>
#include <cstdint>
#include <algorithm>
// Dictionary for Base-36 digits
const std::string BASE36_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
/**
* Encodes an arbitrary string into a Base-36 string.
* It treats the entire string as a large byte array (big-endian base-256 number)
* and performs arbitrary-precision division to convert it to base-36.
*/
std::string encodeToBase36(const std::string& input) {
if (input.empty()) return "";
// Copy input bytes into a vector for in-place manipulation
std::vector<uint8_t> bytes(input.begin(), input.end());
std::string result = "";
// Count leading zeros to preserve them in the output
size_t leadingZeros = 0;
while (leadingZeros < bytes.size() && bytes[leadingZeros] == 0) {
leadingZeros++;
}
// Convert base-256 (bytes) to base-36 using large-integer division
size_t startIdx = leadingZeros;
while (startIdx < bytes.size()) {
uint32_t remainder = 0;
// Divide the large number by 36
for (size_t i = startIdx; i < bytes.size(); ++i) {
uint32_t current = remainder * 256 + bytes[i];
bytes[i] = current / 36;
remainder = current % 36;
}
// Append the character corresponding to the remainder
result += BASE36_CHARS[remainder];
// Advance startIdx if the leading bytes have become 0
while (startIdx < bytes.size() && bytes[startIdx] == 0) {
startIdx++;
}
}
// Append '0' for each preserved leading zero byte
for (size_t i = 0; i < leadingZeros; ++i) {
result += '0';
}
// Because we collected remainders from least-significant to most-significant, reverse it
std::reverse(result.begin(), result.end());
return result;
}
/**
* Decodes a Base-36 string back to its original string format.
* It treats the base-36 string as a giant number and converts it back to base-256.
*/
std::string decodeFromBase36(const std::string& input) {
if (input.empty()) return "";
std::vector<uint8_t> bytes;
bytes.push_back(0); // Start with a value of 0
// Count leading '0' characters to preserve padding
size_t leadingZeros = 0;
while (leadingZeros < input.size() && input[leadingZeros] == '0') {
leadingZeros++;
}
// Process each base-36 character
for (size_t i = leadingZeros; i < input.size(); ++i) {
char c = std::toupper(input[i]);
size_t value = BASE36_CHARS.find(c);
if (value == std::string::npos) {
throw std::invalid_argument("Invalid character in Base-36 string.");
}
// Multiply existing bytes by 36 and add the new value
uint32_t carry = value;
for (size_t j = 0; j < bytes.size(); ++j) {
uint32_t current = bytes[j] * 36 + carry;
bytes[j] = current % 256;
carry = current / 256;
}
// If there is still a carry left, append new bytes
while (carry > 0) {
bytes.push_back(carry % 256);
carry /= 256;
}
}
// The bytes are accumulated in little-endian order, reverse to get big-endian back
std::reverse(bytes.begin(), bytes.end());
// Construct the final string, prepending the preserved zero bytes
std::string result(leadingZeros, '\0');
result.append(bytes.begin(), bytes.end());
// If the loop only processed zeros (or nothing), strip the initial placeholder zero
if (result.size() > leadingZeros && result[leadingZeros] == '\0' && bytes.size() == 1) {
result.pop_back();
}
return result;
}
int main() {
std::string text = "Hello Universe!";
// Encoding
std::string encoded = encodeToBase36(text);
std::cout << "Original: " << text << "\n";
std::cout << "Encoded : " << encoded << "\n";
// Decoding
std::string decoded = decodeFromBase36(encoded);
std::cout << "Decoded : " << decoded << "\n";
}
/*
run:
Original: Hello Universe!
Encoded : LP4N024HJ1YVBVD84Y8TYQP
Decoded : Hello Universe!
*/