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 · Basics

Arithmetic and conditionals

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 arithmetic.py · 43 lines
Viv arithmetic.viv · 51 lines
py2viv translated from arithmetic.py
What py2viv adds before the program
(nothing)
(nothing)

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

1###
2Translated from arithmetic.py by py2viv 0.2.0 (numeric Python -> Viv).
3Python source sha256: 0d7c38d08864ea11af9328254559dedca99aa038248f48bfb57c532a83553144
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#| Python's round(x): halves go to the even neighbor (round(2.5) is 2, round(-2.5) is -2).
9def py_round(x) {
10 if x != x {
11 stop("ValueError: cannot convert float NaN to integer")
12 }
13 if abs(x) == 1.0 / 0.0 {
14 stop("OverflowError: cannot convert float infinity to integer")
15 }
16 return py_rint(x) + 0.0
17}
18
19#| Round half to even (C's rint, NumPy's round); a zero result comes back +0.
20def py_rint(x) {
21 if abs(x) >= 4503599627370496 or x != x {
22 return x
23 }
24 f = floor(x)
25 d = x - f
26 if d > 0.5 {
27 return f + 1
28 }
29 if d < 0.5 {
30 return f
31 }
32 if f % 2 == 0 {
33 return f
34 }
35 return f + 1
36}
37
38#| An int result, as Python's int: a -0.0 becomes 0 (an int has no sign on zero), and the run
39#| stops at 2^53, past which a double no longer holds every integer (Python's int does).
40def py_int(x) {
41 if abs(x) >= 9007199254740992 {
42 stop("py2viv: an integer reached 2^53; Python's int is exact there and a double is not")
43 }
44 return x + 0.0
45}
46
47#| 0.0 with the sign of w (C's copysign(0.0, w)).
48def py_zero_f(w) {
49 if w < 0 or (w == 0 and 1.0 / w < 0) {
50 return -0.0
51 }
52 return 0.0
53}
54
55#| Floored division (k = 0) or remainder (k = 1) of doubles, as CPython's float_divmod and
56#| NumPy's npy_divmod compute them (fmod first, then the sign fix and the rounding fix).
57def py_fdm(a, b, k) {
58 m = mod(a, b)
59 d = (a - m) / b
60 if m != 0 {
61 if (b < 0) != (m < 0) {
62 m = m + b
63 d = d - 1.0
64 }
65 }
66 else {
67 m = py_zero_f(b)
68 }
69 if k == 1 {
70 return m
71 }
72 if d != 0 {
73 f = floor(d)
74 if d - f > 0.5 {
75 f = f + 1.0
76 }
77 return f
78 }
79 return py_zero_f(a / b)
80}
81
82#| a / b of two ints: ZeroDivisionError on a zero divisor.
83def py_div_i(a, b) {
84 if b == 0 {
85 stop("ZeroDivisionError: division by zero")
86 }
87 return a / b
88}
89
90#| a // b of two ints.
91def py_floordiv_i(a, b) {
92 if b == 0 {
93 stop("ZeroDivisionError: integer division or modulo by zero")
94 }
95 return py_int(py_fdm(a, b, 0))
96}
97
98#| a % b of two ints.
99def py_mod_i(a, b) {
100 if b == 0 {
101 stop("ZeroDivisionError: integer modulo by zero")
102 }
103 return py_int(py_fdm(a, b, 1))
104}
105
106#| a ** b of two ints, b >= 0: exact (by squaring, every step checked below 2^53).
107def py_ipow(a, b) {
108 if b < 0 {
109 stop("py2viv: an int to a negative power is a float in Python; this translation typed it as an int")
110 }
111 r = 1
112 base = a
113 e = b
114 while e > 0 {
115 if e % 2 == 1 {
116 r = py_int(r * base)
117 }
118 e = floor(e / 2)
119 if e > 0 {
120 base = py_int(base * base)
121 }
122 }
123 return r
124}
125
126#| a ** b with a float, as CPython's float_pow: ZeroDivisionError for 0.0 to a negative
127#| power, OverflowError when a finite pair overflows; a negative base to a fractional power
128#| (a complex number in Python) stops.
129def py_fpow(a, b) {
130 if a == 0 and b < 0 {
131 stop("ZeroDivisionError: 0.0 cannot be raised to a negative power")
132 }
133 if a < 0 and abs(a) < 1.0 / 0.0 and floor(b) != b and abs(b) < 1.0 / 0.0 {
134 stop("py2viv: a negative number to a fractional power is a complex number in Python")
135 }
136 r = a ^ b
137 if abs(r) == 1.0 / 0.0 and abs(a) < 1.0 / 0.0 and abs(b) < 1.0 / 0.0 {
138 stop("OverflowError: (34, 'Result too large')")
139 }
140 return r
141}
142
143#| math.sqrt(x).
144def py_msqrt(x) {
145 if x < 0 {
146 stop("ValueError: math domain error")
147 }
148 return sqrt(x)
149}
150
151#| math.exp(x): OverflowError when a finite x overflows.
152def py_mexp(x) {
153 r = exp(x)
154 if r == 1.0 / 0.0 and x < 1.0 / 0.0 {
155 stop("OverflowError: math range error")
156 }
157 return r
158}
159
160#| math.floor(x), an int.
161def py_mfloor(x) {
162 if x != x {
163 stop("ValueError: cannot convert float NaN to integer")
164 }
165 if abs(x) == 1.0 / 0.0 {
166 stop("OverflowError: cannot convert float infinity to integer")
167 }
168 return floor(x) + 0.0
169}
170
171#| math.ceil(x), an int.
172def py_mceil(x) {
173 if x != x {
174 stop("ValueError: cannot convert float NaN to integer")
175 }
176 if abs(x) == 1.0 / 0.0 {
177 stop("OverflowError: cannot convert float infinity to integer")
178 }
179 return ceil(x) + 0.0
180}
181
182#| math.isclose(a, b, rel_tol=r, abs_tol=t), as CPython's math_isclose_impl computes it.
183def py_isclose(a, b, r, t) {
184 if r < 0 or t < 0 {
185 stop("ValueError: tolerances must be non-negative")
186 }
187 if a == b {
188 return true
189 }
190 if abs(a) == 1.0 / 0.0 or abs(b) == 1.0 / 0.0 {
191 return false
192 }
193 d = abs(b - a)
194 return d <= abs(r * b) or d <= abs(r * a) or d <= t
195}
1 imports
4import math
2PI = 3.141592653589793
(nothing)
2 variables and the four operations
7a = 17
8b = 5
9print(a + b, a - b, a * b)
10print(a / b)
5a = 17
6b = 5
7print(a + b, a - b, a * b)
8print(fmt(a / b))
201a = 17
202b = 5
203print(fmt((py_int(a + b)) + 0.0, "d"), fmt((py_int(a - b)) + 0.0, "d"), fmt((py_int(a * b)) + 0.0, "d"))
204print(fmt(py_div_i(a, b)))
3 floor division, remainder, power
13print(a // b, a % b, -a // b, -a % b)
14print(2 ** 10, 2 ** -1, 2 ** 3 ** 2)
11print(a // b, a % b, -a // b, -a % b)
12print(2 ^ 10, fmt(2 ^ -1), 2 ^ 3 ^ 2)
206print(fmt((py_floordiv_i(a, b)) + 0.0, "d"), fmt((py_mod_i(a, b)) + 0.0, "d"), fmt((py_floordiv_i(0 - a, b)) + 0.0, "d"), fmt((py_mod_i(0 - a, b)) + 0.0, "d"))
207print(fmt((py_ipow(2, 10)) + 0.0, "d"), fmt(py_fpow(2, -1)), fmt((py_ipow(2, py_ipow(3, 2))) + 0.0, "d"))
4 math functions
17print(f"{math.sqrt(2.0):.6f} {math.exp(1.0):.6f} {math.pi:.6f}")
18print(abs(-3.5), max(a, b), min(a, b))
19print(math.floor(-3.5), math.ceil(-3.5))
15print(f"{sqrt(2.0):.6f} {exp(1.0):.6f} {PI:.6f}")
16print(fmt(abs(-3.5)), max(a, b), min(a, b))
17print(floor(-3.5), ceil(-3.5))
209print(fmt(py_msqrt(2.0), ".6f"), fmt(py_mexp(1.0), ".6f"), fmt(3.141592653589793, ".6f"))
210print(fmt(abs(-3.5)), fmt((max(a, b)) + 0.0, "d"), fmt((min(a, b)) + 0.0, "d"))
211print(fmt((py_mfloor(-3.5)) + 0.0, "d"), fmt((py_mceil(-3.5)) + 0.0, "d"))
5 rounding a tie
22print("round(2.5) =", round(2.5))
20print("round(2.5) =", round(2.5))
213print("round(2.5) =", fmt((py_round(2.5)) + 0.0, "d"))
6 conditionals
25def sign_word(x):
26 if x > 0:
27 return "positive"
28 elif x < 0:
29 return "negative"
30 else:
31 return "zero"
32
33for x in [3, -2, 0]:
34 print(x, sign_word(x))
23def sign_word(x) {
24 if x > 0 {
25 return "positive"
26 }
27 else if x < 0 {
28 return "negative"
29 }
30 else {
31 return "zero"
32 }
33}
34
35for x in [3, -2, 0] {
36 print(x, sign_word(x))
37}
215def sign_word(x) {
216 if x > 0 {
217 return "positive"
218 }
219 else if x < 0 {
220 return "negative"
221 }
222 else {
223 return "zero"
224 }
225}
226py_v1 = [3, -2, 0]
227for py_i2 to 3 {
228 x = py_v1[py_i2]
229 print(fmt(x + 0.0, "d"), sign_word(x))
230}
7 a value chosen by a condition
37parity = "even" if a % 2 == 0 else "odd"
38print(a, "is", parity)
39print("comparisons:", 0 < a < 20, a > 0 and b > 10, not a == b)
40parity = "even" if a % 2 == 0 else "odd"
41print(a, "is", parity)
42print("comparisons:", 0 < a and a < 20, a > 0 and b > 10, not a == b)
232parity = ("even" if py_mod_i(a, 2) == 0 else "odd")
233print(fmt(a + 0.0, "d"), "is", parity)
234py_v3 = 0 < a and a < 20
235py_v4 = a > 0 and b > 10
236py_v5 = not (a == b)
237if py_v3 {
238 if py_v4 {
239 if py_v5 {
240 print("comparisons: True True True")
241 }
242 else {
243 print("comparisons: True True False")
244 }
245 }
246 else {
247 if py_v5 {
248 print("comparisons: True False True")
249 }
250 else {
251 print("comparisons: True False False")
252 }
253 }
254}
255else {
256 if py_v4 {
257 if py_v5 {
258 print("comparisons: False True True")
259 }
260 else {
261 print("comparisons: False True False")
262 }
263 }
264 else {
265 if py_v5 {
266 print("comparisons: False False True")
267 }
268 else {
269 print("comparisons: False False False")
270 }
271 }
272}
8 checks
42assert a == (a // b) * b + a % b
43assert math.isclose(math.sqrt(2.0) ** 2, 2.0)
45test "arithmetic" {
46 assert_true(a == (a // b) * b + a % b)
47 assert_near(sqrt(2.0) ^ 2, 2.0, 1e-12)
48}
274if not (a == py_int(py_int(py_floordiv_i(a, b) * b) + py_mod_i(a, b))) {
275 stop("AssertionError")
276}
277if not (py_isclose(py_fpow(py_msqrt(2.0), 2), 2.0, 1e-09, 0.0)) {
278 stop("AssertionError")
279}

What they print

differs — Two lines print differently and are left out of the comparison. Python's round(2.5) is 2, because Python rounds a tie to the even neighbor; Viv's round(2.5) is 3, because Viv rounds a tie away from zero. Python prints a boolean as True or False, and Viv prints it as true or false.

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. The remaining lines match.

$ python3 arithmetic.py

22 12 85
3.4
3 2 -4 3
1024 0.5 512
1.414214 2.718282 3.141593
3.5 17 5
-4 -3
round(2.5) = 2
3 positive
-2 negative
0 zero
17 is odd
comparisons: True False True

$ viv run arithmetic.viv

22 12 85
3.4
3 2 -4 3
1024 0.5 512
1.414214 2.718282 3.141593
3.5 17 5
-4 -3
round(2.5) = 3
3 positive
-2 negative
0 zero
17 is odd
comparisons: true false true
PASS arithmetic

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

The translation prints exactly what the Python prints.

22 12 85
3.4
3 2 -4 3
1024 0.5 512
1.414214 2.718282 3.141593
3.5 17 5
-4 -3
round(2.5) = 2
3 positive
-2 negative
0 zero
17 is odd
comparisons: True False True