1from sympy.core import (S, pi, oo, symbols, Function, Rational, Integer,
2                        Tuple, Symbol, EulerGamma, GoldenRatio, Catalan,
3                        Lambda, Mul, Pow, Mod, Eq, Ne, Le, Lt, Gt, Ge)
4from sympy.codegen.matrix_nodes import MatrixSolve
5from sympy.functions import (arg, atan2, bernoulli, beta, ceiling, chebyshevu,
6                             chebyshevt, conjugate, DiracDelta, exp, expint,
7                             factorial, floor, harmonic, Heaviside, im,
8                             laguerre, LambertW, log, Max, Min, Piecewise,
9                             polylog, re, RisingFactorial, sign, sinc, sqrt,
10                             zeta, binomial, legendre)
11from sympy.functions import (sin, cos, tan, cot, sec, csc, asin, acos, acot,
12                             atan, asec, acsc, sinh, cosh, tanh, coth, csch,
13                             sech, asinh, acosh, atanh, acoth, asech, acsch)
14from sympy.testing.pytest import raises, XFAIL
15from sympy.utilities.lambdify import implemented_function
16from sympy.matrices import (eye, Matrix, MatrixSymbol, Identity,
17                            HadamardProduct, SparseMatrix, HadamardPower)
18from sympy.functions.special.bessel import (jn, yn, besselj, bessely, besseli,
19                                            besselk, hankel1, hankel2, airyai,
20                                            airybi, airyaiprime, airybiprime)
21from sympy.functions.special.gamma_functions import (gamma, lowergamma,
22                                                     uppergamma, loggamma,
23                                                     polygamma)
24from sympy.functions.special.error_functions import (Chi, Ci, erf, erfc, erfi,
25                                                     erfcinv, erfinv, fresnelc,
26                                                     fresnels, li, Shi, Si, Li,
27                                                     erf2)
28from sympy import octave_code
29from sympy import octave_code as mcode
30
31x, y, z = symbols('x,y,z')
32
33
34def test_Integer():
35    assert mcode(Integer(67)) == "67"
36    assert mcode(Integer(-1)) == "-1"
37
38
39def test_Rational():
40    assert mcode(Rational(3, 7)) == "3/7"
41    assert mcode(Rational(18, 9)) == "2"
42    assert mcode(Rational(3, -7)) == "-3/7"
43    assert mcode(Rational(-3, -7)) == "3/7"
44    assert mcode(x + Rational(3, 7)) == "x + 3/7"
45    assert mcode(Rational(3, 7)*x) == "3*x/7"
46
47
48def test_Relational():
49    assert mcode(Eq(x, y)) == "x == y"
50    assert mcode(Ne(x, y)) == "x != y"
51    assert mcode(Le(x, y)) == "x <= y"
52    assert mcode(Lt(x, y)) == "x < y"
53    assert mcode(Gt(x, y)) == "x > y"
54    assert mcode(Ge(x, y)) == "x >= y"
55
56
57def test_Function():
58    assert mcode(sin(x) ** cos(x)) == "sin(x).^cos(x)"
59    assert mcode(sign(x)) == "sign(x)"
60    assert mcode(exp(x)) == "exp(x)"
61    assert mcode(log(x)) == "log(x)"
62    assert mcode(factorial(x)) == "factorial(x)"
63    assert mcode(floor(x)) == "floor(x)"
64    assert mcode(atan2(y, x)) == "atan2(y, x)"
65    assert mcode(beta(x, y)) == 'beta(x, y)'
66    assert mcode(polylog(x, y)) == 'polylog(x, y)'
67    assert mcode(harmonic(x)) == 'harmonic(x)'
68    assert mcode(bernoulli(x)) == "bernoulli(x)"
69    assert mcode(bernoulli(x, y)) == "bernoulli(x, y)"
70    assert mcode(legendre(x, y)) == "legendre(x, y)"
71
72
73def test_Function_change_name():
74    assert mcode(abs(x)) == "abs(x)"
75    assert mcode(ceiling(x)) == "ceil(x)"
76    assert mcode(arg(x)) == "angle(x)"
77    assert mcode(im(x)) == "imag(x)"
78    assert mcode(re(x)) == "real(x)"
79    assert mcode(conjugate(x)) == "conj(x)"
80    assert mcode(chebyshevt(y, x)) == "chebyshevT(y, x)"
81    assert mcode(chebyshevu(y, x)) == "chebyshevU(y, x)"
82    assert mcode(laguerre(x, y)) == "laguerreL(x, y)"
83    assert mcode(Chi(x)) == "coshint(x)"
84    assert mcode(Shi(x)) ==  "sinhint(x)"
85    assert mcode(Ci(x)) == "cosint(x)"
86    assert mcode(Si(x)) ==  "sinint(x)"
87    assert mcode(li(x)) ==  "logint(x)"
88    assert mcode(loggamma(x)) ==  "gammaln(x)"
89    assert mcode(polygamma(x, y)) == "psi(x, y)"
90    assert mcode(RisingFactorial(x, y)) == "pochhammer(x, y)"
91    assert mcode(DiracDelta(x)) == "dirac(x)"
92    assert mcode(DiracDelta(x, 3)) == "dirac(3, x)"
93    assert mcode(Heaviside(x)) == "heaviside(x, 1/2)"
94    assert mcode(Heaviside(x, y)) == "heaviside(x, y)"
95    assert mcode(binomial(x, y)) == "bincoeff(x, y)"
96    assert mcode(Mod(x, y)) == "mod(x, y)"
97
98
99def test_minmax():
100    assert mcode(Max(x, y) + Min(x, y)) == "max(x, y) + min(x, y)"
101    assert mcode(Max(x, y, z)) == "max(x, max(y, z))"
102    assert mcode(Min(x, y, z)) == "min(x, min(y, z))"
103
104
105def test_Pow():
106    assert mcode(x**3) == "x.^3"
107    assert mcode(x**(y**3)) == "x.^(y.^3)"
108    assert mcode(x**Rational(2, 3)) == 'x.^(2/3)'
109    g = implemented_function('g', Lambda(x, 2*x))
110    assert mcode(1/(g(x)*3.5)**(x - y**x)/(x**2 + y)) == \
111        "(3.5*2*x).^(-x + y.^x)./(x.^2 + y)"
112    # For issue 14160
113    assert mcode(Mul(-2, x, Pow(Mul(y,y,evaluate=False), -1, evaluate=False),
114                                                evaluate=False)) == '-2*x./(y.*y)'
115
116
117def test_basic_ops():
118    assert mcode(x*y) == "x.*y"
119    assert mcode(x + y) == "x + y"
120    assert mcode(x - y) == "x - y"
121    assert mcode(-x) == "-x"
122
123
124def test_1_over_x_and_sqrt():
125    # 1.0 and 0.5 would do something different in regular StrPrinter,
126    # but these are exact in IEEE floating point so no different here.
127    assert mcode(1/x) == '1./x'
128    assert mcode(x**-1) == mcode(x**-1.0) == '1./x'
129    assert mcode(1/sqrt(x)) == '1./sqrt(x)'
130    assert mcode(x**-S.Half) == mcode(x**-0.5) == '1./sqrt(x)'
131    assert mcode(sqrt(x)) == 'sqrt(x)'
132    assert mcode(x**S.Half) == mcode(x**0.5) == 'sqrt(x)'
133    assert mcode(1/pi) == '1/pi'
134    assert mcode(pi**-1) == mcode(pi**-1.0) == '1/pi'
135    assert mcode(pi**-0.5) == '1/sqrt(pi)'
136
137
138def test_mix_number_mult_symbols():
139    assert mcode(3*x) == "3*x"
140    assert mcode(pi*x) == "pi*x"
141    assert mcode(3/x) == "3./x"
142    assert mcode(pi/x) == "pi./x"
143    assert mcode(x/3) == "x/3"
144    assert mcode(x/pi) == "x/pi"
145    assert mcode(x*y) == "x.*y"
146    assert mcode(3*x*y) == "3*x.*y"
147    assert mcode(3*pi*x*y) == "3*pi*x.*y"
148    assert mcode(x/y) == "x./y"
149    assert mcode(3*x/y) == "3*x./y"
150    assert mcode(x*y/z) == "x.*y./z"
151    assert mcode(x/y*z) == "x.*z./y"
152    assert mcode(1/x/y) == "1./(x.*y)"
153    assert mcode(2*pi*x/y/z) == "2*pi*x./(y.*z)"
154    assert mcode(3*pi/x) == "3*pi./x"
155    assert mcode(S(3)/5) == "3/5"
156    assert mcode(S(3)/5*x) == "3*x/5"
157    assert mcode(x/y/z) == "x./(y.*z)"
158    assert mcode((x+y)/z) == "(x + y)./z"
159    assert mcode((x+y)/(z+x)) == "(x + y)./(x + z)"
160    assert mcode((x+y)/EulerGamma) == "(x + y)/%s" % EulerGamma.evalf(17)
161    assert mcode(x/3/pi) == "x/(3*pi)"
162    assert mcode(S(3)/5*x*y/pi) == "3*x.*y/(5*pi)"
163
164
165def test_mix_number_pow_symbols():
166    assert mcode(pi**3) == 'pi^3'
167    assert mcode(x**2) == 'x.^2'
168    assert mcode(x**(pi**3)) == 'x.^(pi^3)'
169    assert mcode(x**y) == 'x.^y'
170    assert mcode(x**(y**z)) == 'x.^(y.^z)'
171    assert mcode((x**y)**z) == '(x.^y).^z'
172
173
174def test_imag():
175    I = S('I')
176    assert mcode(I) == "1i"
177    assert mcode(5*I) == "5i"
178    assert mcode((S(3)/2)*I) == "3*1i/2"
179    assert mcode(3+4*I) == "3 + 4i"
180    assert mcode(sqrt(3)*I) == "sqrt(3)*1i"
181
182
183def test_constants():
184    assert mcode(pi) == "pi"
185    assert mcode(oo) == "inf"
186    assert mcode(-oo) == "-inf"
187    assert mcode(S.NegativeInfinity) == "-inf"
188    assert mcode(S.NaN) == "NaN"
189    assert mcode(S.Exp1) == "exp(1)"
190    assert mcode(exp(1)) == "exp(1)"
191
192
193def test_constants_other():
194    assert mcode(2*GoldenRatio) == "2*(1+sqrt(5))/2"
195    assert mcode(2*Catalan) == "2*%s" % Catalan.evalf(17)
196    assert mcode(2*EulerGamma) == "2*%s" % EulerGamma.evalf(17)
197
198
199def test_boolean():
200    assert mcode(x & y) == "x & y"
201    assert mcode(x | y) == "x | y"
202    assert mcode(~x) == "~x"
203    assert mcode(x & y & z) == "x & y & z"
204    assert mcode(x | y | z) == "x | y | z"
205    assert mcode((x & y) | z) == "z | x & y"
206    assert mcode((x | y) & z) == "z & (x | y)"
207
208
209def test_KroneckerDelta():
210    from sympy.functions import KroneckerDelta
211    assert mcode(KroneckerDelta(x, y)) == "double(x == y)"
212    assert mcode(KroneckerDelta(x, y + 1)) == "double(x == (y + 1))"
213    assert mcode(KroneckerDelta(2**x, y)) == "double((2.^x) == y)"
214
215
216def test_Matrices():
217    assert mcode(Matrix(1, 1, [10])) == "10"
218    A = Matrix([[1, sin(x/2), abs(x)],
219                [0, 1, pi],
220                [0, exp(1), ceiling(x)]]);
221    expected = "[1 sin(x/2) abs(x); 0 1 pi; 0 exp(1) ceil(x)]"
222    assert mcode(A) == expected
223    # row and columns
224    assert mcode(A[:,0]) == "[1; 0; 0]"
225    assert mcode(A[0,:]) == "[1 sin(x/2) abs(x)]"
226    # empty matrices
227    assert mcode(Matrix(0, 0, [])) == '[]'
228    assert mcode(Matrix(0, 3, [])) == 'zeros(0, 3)'
229    # annoying to read but correct
230    assert mcode(Matrix([[x, x - y, -y]])) == "[x x - y -y]"
231
232
233def test_vector_entries_hadamard():
234    # For a row or column, user might to use the other dimension
235    A = Matrix([[1, sin(2/x), 3*pi/x/5]])
236    assert mcode(A) == "[1 sin(2./x) 3*pi./(5*x)]"
237    assert mcode(A.T) == "[1; sin(2./x); 3*pi./(5*x)]"
238
239
240@XFAIL
241def test_Matrices_entries_not_hadamard():
242    # For Matrix with col >= 2, row >= 2, they need to be scalars
243    # FIXME: is it worth worrying about this?  Its not wrong, just
244    # leave it user's responsibility to put scalar data for x.
245    A = Matrix([[1, sin(2/x), 3*pi/x/5], [1, 2, x*y]])
246    expected = ("[1 sin(2/x) 3*pi/(5*x);\n"
247                "1        2        x*y]") # <- we give x.*y
248    assert mcode(A) == expected
249
250
251def test_MatrixSymbol():
252    n = Symbol('n', integer=True)
253    A = MatrixSymbol('A', n, n)
254    B = MatrixSymbol('B', n, n)
255    assert mcode(A*B) == "A*B"
256    assert mcode(B*A) == "B*A"
257    assert mcode(2*A*B) == "2*A*B"
258    assert mcode(B*2*A) == "2*B*A"
259    assert mcode(A*(B + 3*Identity(n))) == "A*(3*eye(n) + B)"
260    assert mcode(A**(x**2)) == "A^(x.^2)"
261    assert mcode(A**3) == "A^3"
262    assert mcode(A**S.Half) == "A^(1/2)"
263
264
265def test_MatrixSolve():
266    n = Symbol('n', integer=True)
267    A = MatrixSymbol('A', n, n)
268    x = MatrixSymbol('x', n, 1)
269    assert mcode(MatrixSolve(A, x)) == "A \\ x"
270
271def test_special_matrices():
272    assert mcode(6*Identity(3)) == "6*eye(3)"
273
274
275def test_containers():
276    assert mcode([1, 2, 3, [4, 5, [6, 7]], 8, [9, 10], 11]) == \
277        "{1, 2, 3, {4, 5, {6, 7}}, 8, {9, 10}, 11}"
278    assert mcode((1, 2, (3, 4))) == "{1, 2, {3, 4}}"
279    assert mcode([1]) == "{1}"
280    assert mcode((1,)) == "{1}"
281    assert mcode(Tuple(*[1, 2, 3])) == "{1, 2, 3}"
282    assert mcode((1, x*y, (3, x**2))) == "{1, x.*y, {3, x.^2}}"
283    # scalar, matrix, empty matrix and empty list
284    assert mcode((1, eye(3), Matrix(0, 0, []), [])) == "{1, [1 0 0; 0 1 0; 0 0 1], [], {}}"
285
286
287def test_octave_noninline():
288    source = mcode((x+y)/Catalan, assign_to='me', inline=False)
289    expected = (
290        "Catalan = %s;\n"
291        "me = (x + y)/Catalan;"
292    ) % Catalan.evalf(17)
293    assert source == expected
294
295
296def test_octave_piecewise():
297    expr = Piecewise((x, x < 1), (x**2, True))
298    assert mcode(expr) == "((x < 1).*(x) + (~(x < 1)).*(x.^2))"
299    assert mcode(expr, assign_to="r") == (
300        "r = ((x < 1).*(x) + (~(x < 1)).*(x.^2));")
301    assert mcode(expr, assign_to="r", inline=False) == (
302        "if (x < 1)\n"
303        "  r = x;\n"
304        "else\n"
305        "  r = x.^2;\n"
306        "end")
307    expr = Piecewise((x**2, x < 1), (x**3, x < 2), (x**4, x < 3), (x**5, True))
308    expected = ("((x < 1).*(x.^2) + (~(x < 1)).*( ...\n"
309                "(x < 2).*(x.^3) + (~(x < 2)).*( ...\n"
310                "(x < 3).*(x.^4) + (~(x < 3)).*(x.^5))))")
311    assert mcode(expr) == expected
312    assert mcode(expr, assign_to="r") == "r = " + expected + ";"
313    assert mcode(expr, assign_to="r", inline=False) == (
314        "if (x < 1)\n"
315        "  r = x.^2;\n"
316        "elseif (x < 2)\n"
317        "  r = x.^3;\n"
318        "elseif (x < 3)\n"
319        "  r = x.^4;\n"
320        "else\n"
321        "  r = x.^5;\n"
322        "end")
323    # Check that Piecewise without a True (default) condition error
324    expr = Piecewise((x, x < 1), (x**2, x > 1), (sin(x), x > 0))
325    raises(ValueError, lambda: mcode(expr))
326
327
328def test_octave_piecewise_times_const():
329    pw = Piecewise((x, x < 1), (x**2, True))
330    assert mcode(2*pw) == "2*((x < 1).*(x) + (~(x < 1)).*(x.^2))"
331    assert mcode(pw/x) == "((x < 1).*(x) + (~(x < 1)).*(x.^2))./x"
332    assert mcode(pw/(x*y)) == "((x < 1).*(x) + (~(x < 1)).*(x.^2))./(x.*y)"
333    assert mcode(pw/3) == "((x < 1).*(x) + (~(x < 1)).*(x.^2))/3"
334
335
336def test_octave_matrix_assign_to():
337    A = Matrix([[1, 2, 3]])
338    assert mcode(A, assign_to='a') == "a = [1 2 3];"
339    A = Matrix([[1, 2], [3, 4]])
340    assert mcode(A, assign_to='A') == "A = [1 2; 3 4];"
341
342
343def test_octave_matrix_assign_to_more():
344    # assigning to Symbol or MatrixSymbol requires lhs/rhs match
345    A = Matrix([[1, 2, 3]])
346    B = MatrixSymbol('B', 1, 3)
347    C = MatrixSymbol('C', 2, 3)
348    assert mcode(A, assign_to=B) == "B = [1 2 3];"
349    raises(ValueError, lambda: mcode(A, assign_to=x))
350    raises(ValueError, lambda: mcode(A, assign_to=C))
351
352
353def test_octave_matrix_1x1():
354    A = Matrix([[3]])
355    B = MatrixSymbol('B', 1, 1)
356    C = MatrixSymbol('C', 1, 2)
357    assert mcode(A, assign_to=B) == "B = 3;"
358    # FIXME?
359    #assert mcode(A, assign_to=x) == "x = 3;"
360    raises(ValueError, lambda: mcode(A, assign_to=C))
361
362
363def test_octave_matrix_elements():
364    A = Matrix([[x, 2, x*y]])
365    assert mcode(A[0, 0]**2 + A[0, 1] + A[0, 2]) == "x.^2 + x.*y + 2"
366    A = MatrixSymbol('AA', 1, 3)
367    assert mcode(A) == "AA"
368    assert mcode(A[0, 0]**2 + sin(A[0,1]) + A[0,2]) == \
369           "sin(AA(1, 2)) + AA(1, 1).^2 + AA(1, 3)"
370    assert mcode(sum(A)) == "AA(1, 1) + AA(1, 2) + AA(1, 3)"
371
372
373def test_octave_boolean():
374    assert mcode(True) == "true"
375    assert mcode(S.true) == "true"
376    assert mcode(False) == "false"
377    assert mcode(S.false) == "false"
378
379
380def test_octave_not_supported():
381    assert mcode(S.ComplexInfinity) == (
382        "% Not supported in Octave:\n"
383        "% ComplexInfinity\n"
384        "zoo"
385    )
386    f = Function('f')
387    assert mcode(f(x).diff(x)) == (
388        "% Not supported in Octave:\n"
389        "% Derivative\n"
390        "Derivative(f(x), x)"
391    )
392
393
394def test_octave_not_supported_not_on_whitelist():
395    from sympy import assoc_laguerre
396    assert mcode(assoc_laguerre(x, y, z)) == (
397        "% Not supported in Octave:\n"
398        "% assoc_laguerre\n"
399        "assoc_laguerre(x, y, z)"
400    )
401
402
403def test_octave_expint():
404    assert mcode(expint(1, x)) == "expint(x)"
405    assert mcode(expint(2, x)) == (
406        "% Not supported in Octave:\n"
407        "% expint\n"
408        "expint(2, x)"
409    )
410    assert mcode(expint(y, x)) == (
411        "% Not supported in Octave:\n"
412        "% expint\n"
413        "expint(y, x)"
414    )
415
416
417def test_trick_indent_with_end_else_words():
418    # words starting with "end" or "else" do not confuse the indenter
419    t1 = S('endless');
420    t2 = S('elsewhere');
421    pw = Piecewise((t1, x < 0), (t2, x <= 1), (1, True))
422    assert mcode(pw, inline=False) == (
423        "if (x < 0)\n"
424        "  endless\n"
425        "elseif (x <= 1)\n"
426        "  elsewhere\n"
427        "else\n"
428        "  1\n"
429        "end")
430
431
432def test_hadamard():
433    A = MatrixSymbol('A', 3, 3)
434    B = MatrixSymbol('B', 3, 3)
435    v = MatrixSymbol('v', 3, 1)
436    h = MatrixSymbol('h', 1, 3)
437    C = HadamardProduct(A, B)
438    n = Symbol('n')
439    assert mcode(C) == "A.*B"
440    assert mcode(C*v) == "(A.*B)*v"
441    assert mcode(h*C*v) == "h*(A.*B)*v"
442    assert mcode(C*A) == "(A.*B)*A"
443    # mixing Hadamard and scalar strange b/c we vectorize scalars
444    assert mcode(C*x*y) == "(x.*y)*(A.*B)"
445
446    # Testing HadamardPower:
447    assert mcode(HadamardPower(A, n)) == "A.**n"
448    assert mcode(HadamardPower(A, 1+n)) == "A.**(n + 1)"
449    assert mcode(HadamardPower(A*B.T, 1+n)) == "(A*B.T).**(n + 1)"
450
451
452def test_sparse():
453    M = SparseMatrix(5, 6, {})
454    M[2, 2] = 10;
455    M[1, 2] = 20;
456    M[1, 3] = 22;
457    M[0, 3] = 30;
458    M[3, 0] = x*y;
459    assert mcode(M) == (
460        "sparse([4 2 3 1 2], [1 3 3 4 4], [x.*y 20 10 30 22], 5, 6)"
461    )
462
463
464def test_sinc():
465    assert mcode(sinc(x)) == 'sinc(x/pi)'
466    assert mcode(sinc(x + 3)) == 'sinc((x + 3)/pi)'
467    assert mcode(sinc(pi*(x + 3))) == 'sinc(x + 3)'
468
469
470def test_trigfun():
471    for f in (sin, cos, tan, cot, sec, csc, asin, acos, acot, atan, asec, acsc,
472              sinh, cosh, tanh, coth, csch, sech, asinh, acosh, atanh, acoth,
473              asech, acsch):
474        assert octave_code(f(x) == f.__name__ + '(x)')
475
476
477def test_specfun():
478    n = Symbol('n')
479    for f in [besselj, bessely, besseli, besselk]:
480        assert octave_code(f(n, x)) == f.__name__ + '(n, x)'
481    for f in (erfc, erfi, erf, erfinv, erfcinv, fresnelc, fresnels, gamma):
482        assert octave_code(f(x)) == f.__name__ + '(x)'
483    assert octave_code(hankel1(n, x)) == 'besselh(n, 1, x)'
484    assert octave_code(hankel2(n, x)) == 'besselh(n, 2, x)'
485    assert octave_code(airyai(x)) == 'airy(0, x)'
486    assert octave_code(airyaiprime(x)) == 'airy(1, x)'
487    assert octave_code(airybi(x)) == 'airy(2, x)'
488    assert octave_code(airybiprime(x)) == 'airy(3, x)'
489    assert octave_code(uppergamma(n, x)) == '(gammainc(x, n, \'upper\').*gamma(n))'
490    assert octave_code(lowergamma(n, x)) == '(gammainc(x, n).*gamma(n))'
491    assert octave_code(z**lowergamma(n, x)) == 'z.^(gammainc(x, n).*gamma(n))'
492    assert octave_code(jn(n, x)) == 'sqrt(2)*sqrt(pi)*sqrt(1./x).*besselj(n + 1/2, x)/2'
493    assert octave_code(yn(n, x)) == 'sqrt(2)*sqrt(pi)*sqrt(1./x).*bessely(n + 1/2, x)/2'
494    assert octave_code(LambertW(x)) == 'lambertw(x)'
495    assert octave_code(LambertW(x, n)) == 'lambertw(n, x)'
496
497
498def test_MatrixElement_printing():
499    # test cases for issue #11821
500    A = MatrixSymbol("A", 1, 3)
501    B = MatrixSymbol("B", 1, 3)
502    C = MatrixSymbol("C", 1, 3)
503
504    assert mcode(A[0, 0]) == "A(1, 1)"
505    assert mcode(3 * A[0, 0]) == "3*A(1, 1)"
506
507    F = C[0, 0].subs(C, A - B)
508    assert mcode(F) == "(A - B)(1, 1)"
509
510
511def test_zeta_printing_issue_14820():
512    assert octave_code(zeta(x)) == 'zeta(x)'
513    assert octave_code(zeta(x, y)) == '% Not supported in Octave:\n% zeta\nzeta(x, y)'
514
515
516def test_automatic_rewrite():
517    assert octave_code(Li(x)) == 'logint(x) - logint(2)'
518    assert octave_code(erf2(x, y)) == '-erf(x) + erf(y)'
519