Caesar cipher — shift letters using an alphabet lookup
Builds the shifted output by finding each character's position in the lowercase alphabet with index_of, rotating by the shift, and indexing back in. Non-letters pass through unchanged. Decrypt = encrypt with (26 - shift). Run: soma run caesar.cell --signal encrypt "hello" 3
A Soma program from the text part of the verified corpus: 49 lines, cells Caesar, CaesarTest. It passes soma check with soma 3.2.4.
How it is built
The cell Caesar declares 2 signals in its face: encrypt(text: String, shift: Int) -> String and decrypt(text: String, shift: Int) -> String. Handlers: encrypt (line 17) and decrypt (line 34).
The file also holds 1 test cell (CaesarTest) with 6 assertions, run by soma test.
The source
caesar.cell
// ============================================================================
// Caesar cipher — shift letters using an alphabet lookup
// ============================================================================
//
// Builds the shifted output by finding each character's position in the
// lowercase alphabet with index_of, rotating by the shift, and indexing back
// in. Non-letters pass through unchanged. Decrypt = encrypt with (26 - shift).
//
// Run: soma run caesar.cell --signal encrypt "hello" 3
cell Caesar {
face {
signal encrypt(text: String, shift: Int) -> String
signal decrypt(text: String, shift: Int) -> String
}
on encrypt(text: String, shift: Int) {
let alpha = "abcdefghijklmnopqrstuvwxyz"
let k = ((shift % 26) + 26) % 26
let low = lowercase(text)
let out = ""
for i in range(0, len(low)) {
let c = low[i]
let pos = index_of(alpha, c)
if pos >= 0 {
out = out + alpha[(pos + k) % 26]
} else {
out = out + c
}
}
return out
}
on decrypt(text: String, shift: Int) {
return encrypt(text, 26 - (shift % 26))
}
}
cell test CaesarTest {
rules {
assert encrypt("hello", 3) == "khoor"
assert encrypt("xyz", 5) == "cde"
assert decrypt("khoor", 3) == "hello"
assert encrypt("abc 123", 1) == "bcd 123"
assert encrypt("hello", 0) == "hello"
assert decrypt(encrypt("attack at dawn", 13), 13) == "attack at dawn"
}
}What the toolchain says
$ soma check caesar.cell
warning: `out` grows by concatenation inside a loop — each step copies the whole value, so the loop is quadratic (8 MB took 32 s, a list + join() 30 ms): push the pieces to a List and `join(parts, "")` once
--> caesar.cell:26:17
|
26 | out = out + alpha[(pos + k) % 26]
| ^
✓ 1 warning(s), no errors.$ soma test caesar.cell
test CaesarTest ...
✓ assert encrypt("hello", 3) == "khoor"
✓ assert encrypt("xyz", 5) == "cde"
✓ assert decrypt("khoor", 3) == "hello"
✓ assert encrypt("abc 123", 1) == "bcd 123"
✓ assert encrypt("hello", 0) == "hello"
✓ assert decrypt(encrypt("attack at dawn", 13), 13) == "attack at dawn"
6 tests: 6 passed, 0 failed$ soma verify caesar.cell
Verifying caesar.cell...
No state machine in this program: nothing to prove beyond `soma check` (invariants and lifecycles are what verify proves).
VERIFY OK (vacuous — no state machine)