(load(pytranslate), 'done);
'done$
(kill(all), 'done);
'done$

(temp_vars_map : ?\*maxima\-variables\-dictionary\-name\*,
  ?\*maxima\-variables\-dictionary\-name\* : "m_vars",
  temp_funcs_map : ?\*maxima\-function\-dictionary\-name\*,
  ?\*maxima\-function\-dictionary\-name\* : "m_funcs",
'done);
'done$

(preprocess(form) := block([ret],
    ret : regex_subst("m_vars",
      ?\*maxima\-variables\-dictionary\-name\*,
      form),
    ret: regex_subst("m_funcs",
      ?\*maxima\-function\-dictionary\-name\*, ret),
    ret: regex_subst("block_random(",
      "block[0-9]+?\\(",
      ret),
    ret: regex_subst("\"lambda_random\"",
      "\"lambda[0-9]+?\"",
      ret),
    ret: regex_subst("lambda_random(",
      "lambda[0-9]+?\\(",
      ret),
    ret: regex_subst(" lambda_random ",
      " lambda[0-9]+? ",
      ret),
    ret: regex_subst(" lambda_random",
      " lambda[0-9]+",
      ret),
    ret: regex_subst("block_random(",
      "block[0-9]+?\\(",
      ret),
    ret),
  'done);
'done$

/* Atoms */
pytranslate(1);
"
1"$
pytranslate("sdf");
"
\"sdf\""$
pytranslate(%i);
"
1j"$
pytranslate(%pi);
"
math.pi"$
pytranslate(inf);
"
math.inf"$
pytranslate(%e);
"
math.e"$
pytranslate(true);
"
True"$

/* Arithmetic operators */
preprocess(pytranslate(a + b));
"
(m_vars[\"b\"] + m_vars[\"a\"])"$
preprocess(pytranslate(a*b));
"
(m_vars[\"a\"] * m_vars[\"b\"])"$
preprocess(pytranslate(a/b));
"
(m_vars[\"a\"] / m_vars[\"b\"])"$
preprocess(pytranslate(a-b));
"
(m_vars[\"a\"] + (-m_vars[\"b\"]))"$
preprocess(pytranslate(a^b));
"
m_funcs[\"pow\"](m_vars[\"a\"], m_vars[\"b\"])"$
preprocess(pytranslate(a!));
"
m_funcs[\"factorial\"](m_vars[\"a\"])"$

/* Boolean Operators */
preprocess(pytranslate(a or b));
"
(m_vars[\"a\"] or m_vars[\"b\"])"$
preprocess(pytranslate(a and b));
"
(m_vars[\"a\"] and m_vars[\"b\"])"$
preprocess(pytranslate(not(a)));
"
not(m_vars[\"a\"])"$

/* Relational Operators */
preprocess(pytranslate(a >= b));
"
(m_vars[\"a\"] >= m_vars[\"b\"])"$
preprocess(pytranslate(a <= b));
"
(m_vars[\"a\"] <= m_vars[\"b\"])"$
preprocess(pytranslate(a > b));
"
(m_vars[\"a\"] > m_vars[\"b\"])"$
preprocess(pytranslate(a < b));
"
(m_vars[\"a\"] < m_vars[\"b\"])"$
preprocess(pytranslate(a # b));
"
(m_vars[\"a\"] != m_vars[\"b\"])"$
preprocess(pytranslate(a = b));
"
(m_vars[\"a\"] == m_vars[\"b\"])"$

/* List */
preprocess(pytranslate([a,b,c]));
"
[m_vars[\"a\"], m_vars[\"b\"], m_vars[\"c\"]]"$

/* Assignment */
preprocess(pytranslate('(a:b)));
"
m_vars[\"a\"] = m_vars[\"b\"]"$
preprocess(pytranslate('([a,b,c]:[1,2,3])));
"
[m_vars[\"a\"], m_vars[\"b\"], m_vars[\"c\"]] = [1, 2, 3]"$

(kill(allbut(preprocess, temp_vars_map, temp_funcs_map)), 'done);
'done$

/* Function Definition */
preprocess(pytranslate(f(x):=x^2));
"

def f(x, m_vars = m_vars):
    m_vars = Stack({}, m_vars)
    m_vars.ins({\"x\" : x})
    return(m_funcs[\"pow\"](m_vars[\"x\"], 2))
m_funcs[\"f\"] = f"$
preprocess(pytranslate(f(x, [y]):=[x, y]));
"

def f(x, *y, m_vars = m_vars):
    m_vars = Stack({}, m_vars)
    m_vars.ins({\"x\" : x, \"y\" : list(y)})
    return([m_vars[\"x\"], m_vars[\"y\"]])
m_funcs[\"f\"] = f"$

(kill(allbut(preprocess, temp_vars_map, temp_funcs_map)), 'done);
'done$

/* Array-definition */
preprocess(pytranslate('array(abc, 1, 2, 3)));
"
m_vars[\"abc\"] = ([([([None] * 3)] * 2)] * 1)"$
preprocess(pytranslate('array(abc, fixnum, 1, 2, 3)));
"
m_vars[\"abc\"] = ([([([None] * 3)] * 2)] * 1)"$
preprocess(pytranslate('array([abc,def,tre], 1, 2, 3)));
"

m_vars[\"abc\"] = ([([([None] * 3)] * 2)] * 1)
m_vars[\"def\"] = ([([([None] * 3)] * 2)] * 1)
m_vars[\"tre\"] = ([([([None] * 3)] * 2)] * 1)"$

/* Array reference */
preprocess(pytranslate('(abc[1,2,3,4])));
"
m_vars[\"abc\"][0][1][2][3]"$

(kill(allbut(preprocess, temp_vars_map, temp_funcs_map)), 'done);
'done$

/* Block statements */
/* Regex substitution is used to catch the random names generated by gensym and replace them with func_random */
preprocess(pytranslate(f(x):=block(a:10, b:x*20, x+a+b)));
"

def f(x, m_vars = m_vars):
    m_vars = Stack({}, m_vars)
    m_vars.ins({\"x\" : x})
    m_vars.ins({})
    m_vars[\"a\"] = 10
    m_vars[\"b\"] = (m_vars[\"x\"] * 20)
    return((m_vars[\"x\"] + m_vars[\"a\"] + m_vars[\"b\"]))
m_funcs[\"f\"] = f"$

(?\*symbols\-directly\-convert\*:[], 'done);
'done$
preprocess(pytranslate('(block(d : block(c : block(e : 20, f : e + 10, f), c : c + 1 + 2 + 3, c), d))));
"
def block_random(m_vars):
    m_vars = Stack({}, m_vars)
    m_vars.ins({})
    m_vars[\"d\"] = block_random(Stack({}, m_vars))
    return(m_vars[\"d\"])
def block_random(m_vars):
    m_vars = Stack({}, m_vars)
    m_vars.ins({})
    m_vars[\"c\"] = block_random(Stack({}, m_vars))
    m_vars[\"c\"] = (6 + m_vars[\"c\"])
    return(m_vars[\"c\"])
def block_random(m_vars):
    m_vars = Stack({}, m_vars)
    m_vars.ins({})
    m_vars[\"e\"] = 20
    m_vars[\"f\"] = (10 + m_vars[\"e\"])
    return(m_vars[\"f\"])
block_random(Stack({}, m_vars))"$


/* if-else-if */
preprocess(pytranslate('(if 1>2 then z:10)));
"

if (1 > 2):
    m_vars[\"z\"] = 10"$
preprocess(pytranslate('(if 1>2 then z:10 else z:30)));
"

if (1 > 2):
    m_vars[\"z\"] = 10
else:
    m_vars[\"z\"] = 30"$
preprocess(pytranslate('(if 1>2 then z:10 elseif 1<2 then z:30 else z:40)));
"

if (1 > 2):
    m_vars[\"z\"] = 10
elif (1 < 2):
    m_vars[\"z\"] = 30
else:
    m_vars[\"z\"] = 40"$
pytranslate('(f(x):=if x>2 then 10 elseif x<2 then 30 else 40));
"

def f(x, m_vars = m_vars):
    m_vars = Stack({}, m_vars)
    m_vars.ins({\"x\" : x})
    return((10 if (m_vars[\"x\"] > 2) else (30 if (m_vars[\"x\"] < 2) else 40)))
m_funcs[\"f\"] = f"$

(?\*symbols\-directly\-convert\*:[], 'done);
'done$
(kill(allbut(preprocess, temp_vars_map, temp_funcs_map)), 'done);
'done$

/* Lambda Form */
pytranslate(lambda([a,b], a+b));
"
lambda a, b, m_vars = Stack({}, m_vars): (a + b)"$
pytranslate('(xx:lambda([a,b], a+b)));
"
m_vars[\"xx\"] = lambda a, b, m_vars = Stack({}, m_vars): (a + b)"$
preprocess(pytranslate('(lmb:lambda([a,b,c], d:a+b, e:b^c, d+e))));
"

def lambda_random(a, b, c, m_vars = m_vars):
    m_vars = Stack({}, m_vars)
    m_vars.ins({\"a\" : a, \"b\" : b, \"c\" : c})
    m_vars[\"d\"] = (a + b)
    m_vars[\"e\"] = m_funcs[\"pow\"](b, c)
    return((m_vars[\"d\"] + m_vars[\"e\"]))
m_funcs[\"lambda_random\"] = lambda_random
m_vars[\"lmb\"] = lambda a, b, c, m_vars = Stack({}, m_vars): lambda_random(a, b, c, Stack({}, m_vars))"$

(kill(allbut(preprocess, temp_vars_map, temp_funcs_map)), 'done);
'done$

/*for loop*/
/* In List */
pytranslate('(for i in [1,2,3] do print(i)));
"
for m_vars[\"i\"] in [1, 2, 3]:
    m_funcs[\"print\"](m_vars[\"i\"])"$

pytranslate('(for i in [1,2,3] do (print(i), print(i*2))));
"
for m_vars[\"i\"] in [1, 2, 3]:
    m_funcs[\"print\"](m_vars[\"i\"])
    m_funcs[\"print\"]((2 * m_vars[\"i\"]))"$

/* Initial, increment and limit values specified */
pytranslate('(for i from 1 step 3 thru 5 do print(i)));
"
for m_vars[\"i\"] in range(1, 6, 3):
    m_funcs[\"print\"](m_vars[\"i\"])"$
pytranslate('(for i from 1 step 3 thru 5 do (print(i), a:20+i, print(i*a))));
"
for m_vars[\"i\"] in range(1, 6, 3):
    m_funcs[\"print\"](m_vars[\"i\"])
    m_vars[\"a\"] = (20 + m_vars[\"i\"])
    m_funcs[\"print\"]((m_vars[\"a\"] * m_vars[\"i\"]))"$

/* Initial, increment and condition specified */
/* while condition */
pytranslate('(for i:10 step 5 while i*5<70 do (print(i), print(5*i))));
"

m_vars[\"i\"] = 10
while not(((5 * m_vars[\"i\"]) >= 70)):
    m_funcs[\"print\"](m_vars[\"i\"])
    m_funcs[\"print\"]((5 * m_vars[\"i\"]))
    m_vars[\"i\"] = (m_vars[\"i\"] + 5)
del m_vars[\"i\"]"$
/* unless condition */    
pytranslate('(for i:8 step -1 unless i<3 do (print(i))));
"

m_vars[\"i\"] = 8
while not((m_vars[\"i\"] < 3)):
    m_funcs[\"print\"](m_vars[\"i\"])
    m_vars[\"i\"] = (m_vars[\"i\"] + -1)
del m_vars[\"i\"]"$
pytranslate('(for i:8 step -1 unless i<3 do print(i)));
"

m_vars[\"i\"] = 8
while not((m_vars[\"i\"] < 3)):
    m_funcs[\"print\"](m_vars[\"i\"])
    m_vars[\"i\"] = (m_vars[\"i\"] + -1)
del m_vars[\"i\"]"$

/* Function Calls */
pytranslate(fix(a));
"
math.floor(m_vars[\"a\"])"$
pytranslate('fix(a));
"
math.floor(m_vars[\"a\"])"$
pytranslate(sqrt(a));
"
math.sqrt(m_vars[\"a\"])"$
pytranslate(abs(a));
"
abs(m_vars[\"a\"])"$

/* endcons */
preprocess(pytranslate('endcons(a,b)));
"
[*m_vars[\"b\"], m_vars[\"a\"]]"$
pytranslate(endcons(a, f(b,c)));
"
m_funcs[\"f\"](m_vars[\"b\"], m_vars[\"c\"], m_vars[\"a\"])"$
pytranslate(endcons(a, [d,b,c]));
"
[m_vars[\"d\"], m_vars[\"b\"], m_vars[\"c\"], m_vars[\"a\"]]"$

/* Multiple Indentations */
preprocess( pytranslate('(for i:5 step 0 while i>=1 do
    ((for j from i step 0 while j>=1 do
    	 ((for k from j step 0 while k>=1 do
	      (print(k,j,i),
	      k:k-1)),
	  j:j-1)),
    i:i-1))));
"

m_vars[\"i\"] = 5
while not((m_vars[\"i\"] < 1)):
    
    m_vars[\"j\"] = m_vars[\"i\"]
    while not((m_vars[\"j\"] < 1)):
        
        m_vars[\"k\"] = m_vars[\"j\"]
        while not((m_vars[\"k\"] < 1)):
            m_funcs[\"print\"](m_vars[\"k\"], m_vars[\"j\"], m_vars[\"i\"])
            m_vars[\"k\"] = (-1 + m_vars[\"k\"])
            m_vars[\"k\"] = (m_vars[\"k\"] + 0)
        del m_vars[\"k\"]
        m_vars[\"j\"] = (-1 + m_vars[\"j\"])
        m_vars[\"j\"] = (m_vars[\"j\"] + 0)
    del m_vars[\"j\"]
    m_vars[\"i\"] = (-1 + m_vars[\"i\"])
    m_vars[\"i\"] = (m_vars[\"i\"] + 0)
del m_vars[\"i\"]"$

/* Numeric Tests */

/* Continued Fractions */
/* cf1 produces continued fraction as a list for any number */
preprocess(pytranslate(cf1(x):=block ([ q:[],  m: fix((x)), _rem:0, _num, _den, _prec: sqrt(ratepsilon) ],
     _rem: (x - m),
     k:fpprec,
     q:[ m],
     while k>0 and _rem>ratepsilon do (
         print(float(_rem/_prec)),
         _num:num(_rem),
         _den:denom(_rem),
         m: fix((_den/_num)),
         q: endcons (m, q),
         _rem: (_den/_num - m),
         k:k-1
      ),
     q
)));
"

def cf1(x, m_vars = m_vars):
    m_vars = Stack({}, m_vars)
    m_vars.ins({\"x\" : x})
    m_vars.ins({\"q\" : [], \"m\" : math.floor(m_vars[\"x\"]), \"_rem\" : 0, \"_num\" : None, \"_den\" : None, \"_prec\" : math.sqrt(m_vars[\"ratepsilon\"])})
    m_vars[\"_rem\"] = (m_vars[\"x\"] + (-m_vars[\"m\"]))
    m_vars[\"k\"] = m_vars[\"fpprec\"]
    m_vars[\"q\"] = [m_vars[\"m\"]]
    
    while ((m_vars[\"k\"] > 0) and (m_vars[\"_rem\"] > m_vars[\"ratepsilon\"])):
        m_funcs[\"print\"](m_funcs[\"float\"]((m_vars[\"_rem\"] / m_vars[\"_prec\"])))
        m_vars[\"_num\"] = m_funcs[\"num\"](m_vars[\"_rem\"])
        m_vars[\"_den\"] = m_funcs[\"denom\"](m_vars[\"_rem\"])
        m_vars[\"m\"] = math.floor((m_vars[\"_den\"] / m_vars[\"_num\"]))
        m_vars[\"q\"] = [*m_vars[\"q\"], m_vars[\"m\"]]
        m_vars[\"_rem\"] = ((m_vars[\"_den\"] / m_vars[\"_num\"]) + (-m_vars[\"m\"]))
        m_vars[\"k\"] = (m_vars[\"k\"] + (-1))
    return(m_vars[\"q\"])
m_funcs[\"cf1\"] = cf1"$

/* cfeval works on converting a continuous fraction list back to a number */
preprocess(pytranslate(
    cfeval(_a, [len]):=block ([ z:0, m:0, n:0, p1:1, p2:0, q1:0, q2:1 ] ,
      if listp(_a) then
      len:length(_a)
      else (
        len:first(len),
        if not numberp(len) then error("illegal argument: ", len, "  nan ")
        ),
      if _a[1]#0 then z:_a[1],
      for i:2 thru len do (
        m:p2, n:q2,
        p2:_a[i]*p2+p1,
        q2:_a[i]*q2+q1,
        p1:m, q1:n
        ),
      z+ (p2/q2)
      )));
"
def block_random(m_vars):
    m_vars = Stack({}, m_vars)
    m_vars[\"len\"] = m_funcs[\"first\"](m_vars[\"len\"])
    return((m_funcs[\"error\"](\"illegal argument: \", m_vars[\"len\"], \"  nan \") if not(m_funcs[\"numberp\"](m_vars[\"len\"])) else None))

def cfeval(_a, *len, m_vars = m_vars):
    m_vars = Stack({}, m_vars)
    m_vars.ins({\"_a\" : _a, \"len\" : list(len)})
    m_vars.ins({\"z\" : 0, \"m\" : 0, \"n\" : 0, \"p1\" : 1, \"p2\" : 0, \"q1\" : 0, \"q2\" : 1})
    
    if m_funcs[\"listp\"](m_vars[\"_a\"]):
            m_vars[\"len\"] = m_funcs[\"length\"](m_vars[\"_a\"])
    else:
            block_random(m_vars)
    
    if (m_vars[\"_a\"][0] != 0):
            m_vars[\"z\"] = m_vars[\"_a\"][0]
    for m_vars[\"i\"] in range(2, (m_vars[\"len\"] + 1)):
        m_vars[\"m\"] = m_vars[\"p2\"]
        m_vars[\"n\"] = m_vars[\"q2\"]
        m_vars[\"p2\"] = ((m_vars[\"_a\"][(m_vars[\"i\"] + -1)] * m_vars[\"p2\"]) + m_vars[\"p1\"])
        m_vars[\"q2\"] = ((m_vars[\"_a\"][(m_vars[\"i\"] + -1)] * m_vars[\"q2\"]) + m_vars[\"q1\"])
        m_vars[\"p1\"] = m_vars[\"m\"]
        m_vars[\"q1\"] = m_vars[\"n\"]
    return((m_vars[\"z\"] + (m_vars[\"p2\"] / m_vars[\"q2\"])))
m_funcs[\"cfeval\"] = cfeval"$

/* Cantor's Function - Calculates the value of Cantor's function at x in n iterations  */
preprocess(pytranslate(cantorr2(x, n):=block([ret:0, k:0],
     if not numberp(x) then 
        return('cantorr2(x, n)), 
    if x =0 then return(0),
    if x =1 then return(1),
        print(x,n),
    if n>0 then (
        if x>0 and x<= 1/3 then 
            ret: cantorr2(3*x, n-1),
        if 1/3<x and x<2/3 then 
            ret:1,
        if x>=2/3 and x< 1 then 
            ret:1 +  cantorr2(3*x-2, n-1),
        return(ret/2)
    ) else x
)));
"
def block_random(m_vars):
    m_vars = Stack({}, m_vars)
    
    if ((m_vars[\"x\"] > 0) and (m_vars[\"x\"] <= (1 / 3))):
            m_vars[\"ret\"] = cantorr2((3 * m_vars[\"x\"]), (m_vars[\"n\"] + (-1)))
    
    if (((1 / 3) < m_vars[\"x\"]) and (m_vars[\"x\"] < (2 / 3))):
            m_vars[\"ret\"] = 1
    
    if ((m_vars[\"x\"] >= (2 / 3)) and (m_vars[\"x\"] < 1)):
            m_vars[\"ret\"] = (1 + cantorr2(((3 * m_vars[\"x\"]) + (-2)), (m_vars[\"n\"] + (-1))))
    return((m_vars[\"ret\"] / 2))

def cantorr2(x, n, m_vars = m_vars):
    m_vars = Stack({}, m_vars)
    m_vars.ins({\"x\" : x, \"n\" : n})
    m_vars.ins({\"ret\" : 0, \"k\" : 0})
    
    if not(m_funcs[\"numberp\"](m_vars[\"x\"])):
            return(m_funcs[\"cantorr2\"](m_vars[\"x\"], m_vars[\"n\"]))
    
    if (m_vars[\"x\"] == 0):
            return(0)
    
    if (m_vars[\"x\"] == 1):
            return(1)
    m_funcs[\"print\"](m_vars[\"x\"], m_vars[\"n\"])
    return((block_random(m_vars) if (m_vars[\"n\"] > 0) else m_vars[\"x\"]))
m_funcs[\"cantorr2\"] = cantorr2"$

/* Tests for Plotting code */
preprocess(pytranslate('(plot3d(lambda([x, y], x^2 + y^(-1)), [x, 1, 10], [y, 1, 10]))));
"
m_funcs[\"plot3d\"](lambda x, y, m_vars = Stack({}, m_vars): (m_funcs[\"pow\"](x, 2) + m_funcs[\"pow\"](y, (-1))), [\"x\", 1, 10], [\"y\", 1, 10])"$
preprocess(pytranslate('(plot2d(lambda([x], x^2), [x,0,2]))));
"
m_funcs[\"plot2d\"](lambda x, m_vars = Stack({}, m_vars): m_funcs[\"pow\"](x, 2), [\"x\", 0, 2])"$

(?\*symbols\-directly\-convert\*:[], 'done);
'done$
(kill(allbut(preprocess, temp_vars_map, temp_funcs_map)), 'done);
'done$

(?\*maxima\-variables\-dictionary\-name\* : temp_vars_map,
  ?\*maxima\-function\-dictionary\-name\* : temp_funcs_map,
'done);
'done$
