This site is version 0.0.1. Most of the code on this site is drafted by an agent and needs review. I apologize for the AI-speak, we are working on rewriting it!
viv-lang.org

Python and Viv · Strings

Caesar cipher

What to notice

Open the Viv in the workbench The atlas of the Viv py2viv translates this Python; the translation prints the same output.
Python caesar.py · 32 lines
Viv caesar.viv · 41 lines
py2viv translated from caesar.py
What py2viv adds before the program
(nothing)
(nothing)

58 lines: a header and the guard functions its translation calls

1###
2Translated from caesar.py by py2viv 0.2.0 (numeric Python -> Viv).
3Python source sha256: 8794b0db02c862e7537951cf15792b62b1bf3ea970d2328f2a3f1a4c0be9902a
4Each print(...) is one print(...) line; fmt(x) writes a float as Python's repr does.
5A `raise`, a failed assert and every guard `stop` with Python's exception line.
6###
7
8#| An int result, as Python's int: a -0.0 becomes 0 (an int has no sign on zero), and the run
9#| stops at 2^53, past which a double no longer holds every integer (Python's int does).
10def py_int(x) {
11 if abs(x) >= 9007199254740992 {
12 stop("py2viv: an integer reached 2^53; Python's int is exact there and a double is not")
13 }
14 return x + 0.0
15}
16
17#| 0.0 with the sign of w (C's copysign(0.0, w)).
18def py_zero_f(w) {
19 if w < 0 or (w == 0 and 1.0 / w < 0) {
20 return -0.0
21 }
22 return 0.0
23}
24
25#| Floored division (k = 0) or remainder (k = 1) of doubles, as CPython's float_divmod and
26#| NumPy's npy_divmod compute them (fmod first, then the sign fix and the rounding fix).
27def py_fdm(a, b, k) {
28 m = mod(a, b)
29 d = (a - m) / b
30 if m != 0 {
31 if (b < 0) != (m < 0) {
32 m = m + b
33 d = d - 1.0
34 }
35 }
36 else {
37 m = py_zero_f(b)
38 }
39 if k == 1 {
40 return m
41 }
42 if d != 0 {
43 f = floor(d)
44 if d - f > 0.5 {
45 f = f + 1.0
46 }
47 return f
48 }
49 return py_zero_f(a / b)
50}
51
52#| a % b of two ints.
53def py_mod_i(a, b) {
54 if b == 0 {
55 stop("ZeroDivisionError: integer modulo by zero")
56 }
57 return py_int(py_fdm(a, b, 1))
58}
1 shift one letter
4ALPHABET = 26
5
6def shift_char(c, k):
7 if "a" <= c <= "z":
8 return chr((ord(c) - ord("a") + k) % ALPHABET + ord("a"))
9 if "A" <= c <= "Z":
10 return chr((ord(c) - ord("A") + k) % ALPHABET + ord("A"))
11 return c
2ALPHABET = 26
3
4def shift_char(c, k) {
5 if "a" <= c and c <= "z" {
6 return chr((ord(c) - ord("a") + k) % ALPHABET + ord("a"))
7 }
8 if "A" <= c and c <= "Z" {
9 return chr((ord(c) - ord("A") + k) % ALPHABET + ord("A"))
10 }
11 return c
12}
63ALPHABET = 26
64def shift_char(c, k) {
65 if "a" <= c and c <= "z" {
66 return chr(py_int(py_mod_i(py_int(py_int(ord(c) - ord("a")) + k), ALPHABET) + ord("a")))
67 }
68 if "A" <= c and c <= "Z" {
69 return chr(py_int(py_mod_i(py_int(py_int(ord(c) - ord("A")) + k), ALPHABET) + ord("A")))
70 }
71 return c
72}
2 shift a message
14def caesar(text, k):
15 return "".join(shift_char(c, k) for c in text)
15def caesar(text, k) {
16 return "".join(shift_char(c, k) for c in text)
17}
74def caesar(text_, k) {
75 return join("", [shift_char(py_c3, k) for py_c3 in text_])
76}
3 encode and decode
18KEY = 3
19message = "Veni, vidi, vici. Zebra!"
20secret = caesar(message, KEY)
21print(secret)
22print(caesar(secret, -KEY))
20KEY = 3
21message = "Veni, vidi, vici. Zebra!"
22secret = caesar(message, KEY)
23print(secret)
24print(caesar(secret, -KEY))
78KEY = 3
79message = "Veni, vidi, vici. Zebra!"
80secret = caesar(message, KEY)
81print(secret)
82print(caesar(secret, 0 - KEY))
4 break it by trying every key
25for k in range(ALPHABET):
26 guess = caesar(secret, -k)
27 if "vidi" in guess:
28 print("key", k, "gives", guess)
27for k to ALPHABET {
28 guess = caesar(secret, -k)
29 if "vidi" in guess {
30 print("key", k, "gives", guess)
31 }
32}
84for k to ALPHABET {
85 guess = caesar(secret, 0 - k)
86 if "vidi" in guess {
87 print("key", fmt(k + 0.0, "d"), "gives", guess)
88 }
89}
5 checks
31assert caesar(caesar(message, 11), -11) == message
32assert caesar("xyz", KEY) == "abc"
35test "round trip" {
36 assert_true(caesar(caesar(message, 11), -11) == message)
37 assert_true(caesar("xyz", KEY) == "abc")
38}
91if not (caesar(caesar(message, 11), -11) == message) {
92 stop("AssertionError")
93}
94if not (caesar("xyz", KEY) == "abc") {
95 stop("AssertionError")
96}

What they print

identical — the two programs print the same text.

Dimmed lines are left out of the comparison. Each Viv test block prints one PASS line; the Python checks the same facts with assert, which prints nothing.

$ python3 caesar.py

Yhql, ylgl, ylfl. Cheud!
Veni, vidi, vici. Zebra!
key 3 gives Veni, vidi, vici. Zebra!

$ viv run caesar.viv

Yhql, ylgl, ylfl. Cheud!
Veni, vidi, vici. Zebra!
key 3 gives Veni, vidi, vici. Zebra!
PASS round trip

$ viv run caesar.py (py2viv’s translation)

The translation prints exactly what the Python prints.

Yhql, ylgl, ylfl. Cheud!
Veni, vidi, vici. Zebra!
key 3 gives Veni, vidi, vici. Zebra!