import Foundation
/*
Base‑36 encoding/decoding in idiomatic Swift (manual big integer)
----------------------------------------------------------------
Base‑36 digits: 0–9, A–Z
Encoding:
- Convert string → big integer (base‑256) stored as [UInt32] "limbs"
- Convert big integer → base‑36 string
Decoding:
- Convert base‑36 string → big integer (base‑36 → [UInt32])
- Convert big integer → original bytes (base‑256)
*/
let base36Digits = Array("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ")
// Helper: convert Base‑36 character → numeric value
func base36Value(_ c: Character) -> Int {
if let idx = base36Digits.firstIndex(of: c) {
return idx
}
return 0
}
// Encode string → Base‑36
func encodeToBase36(_ input: String) -> String {
// Big integer stored as little‑endian 32‑bit limbs
var number: [UInt32] = [0]
// Convert string → big integer (base‑256)
for byte in input.utf8 {
var carry = UInt32(byte)
for i in 0..<number.count {
let value = UInt64(number[i]) * 256 + UInt64(carry)
number[i] = UInt32(value & 0xFFFF_FFFF)
carry = UInt32(value >> 32)
}
if carry > 0 {
number.append(carry)
}
}
// Convert big integer → Base‑36
var encoded = ""
while !(number.count == 1 && number[0] == 0) {
var remainder: UInt32 = 0
// Divide big integer by 36
for i in stride(from: number.count - 1, through: 0, by: -1) {
let value = (UInt64(remainder) << 32) + UInt64(number[i])
number[i] = UInt32(value / 36)
remainder = UInt32(value % 36)
}
// Remove leading zeros
while number.count > 1 && number.last == 0 {
number.removeLast()
}
encoded.append(base36Digits[Int(remainder)])
}
return String(encoded.reversed())
}
// Decode Base‑36 → original string
func decodeFromBase36(_ encoded: String) -> String {
// Big integer stored as little‑endian 32‑bit limbs
var number: [UInt32] = [0]
// Convert Base‑36 → big integer
for c in encoded {
let digit = UInt32(base36Value(c))
var carry = digit
for i in 0..<number.count {
let value = UInt64(number[i]) * 36 + UInt64(carry)
number[i] = UInt32(value & 0xFFFF_FFFF)
carry = UInt32(value >> 32)
}
if carry > 0 {
number.append(carry)
}
}
// Convert big integer → original bytes (base‑256)
var outputBytes: [UInt8] = []
while !(number.count == 1 && number[0] == 0) {
var remainder: UInt32 = 0
// Divide big integer by 256
for i in stride(from: number.count - 1, through: 0, by: -1) {
let value = (UInt64(remainder) << 32) + UInt64(number[i])
number[i] = UInt32(value / 256)
remainder = UInt32(value % 256)
}
// Remove leading zeros
while number.count > 1 && number.last == 0 {
number.removeLast()
}
outputBytes.append(UInt8(truncatingIfNeeded: remainder))
}
outputBytes.reverse()
return String(bytes: outputBytes, encoding: .utf8) ?? ""
}
// Demo
func main() {
let text = "Hello Universe!"
let encoded = encodeToBase36(text)
let decoded = decodeFromBase36(encoded)
print("Original: \(text)")
print("Base-36 encoded: \(encoded)")
print("Decoded: \(decoded)")
}
main()
/*
run:
Original: Hello Universe!
Base-36 encoded: LP4N024HJ1YVBVD84Y8TYQP
Decoded: Hello Universe!
*/