2. Equations 1 step diagrams

The python file to make a 1-step invop diagram is below.
The required LaTeX files are below.
The 2 custom python modules required are:
The python file, invop_diagram_maker.py, when run, will ask for these inputs:
Choose the arithmetic process:
"Enter 1, 2, 3, 4 or 5 for +, -, X, /, random for the 1st process"
Choose the file name base:
"Enter the base filename to be added to the prefix :"
The prefix will be β€œinvolp1” for standard operations.
The filename will have β€œ_q” added for the question diagram and β€œ_ans” for the answer diagram.

2.1. Example 1-step invop diagram

question

answer

question

answer

question

answer

question

answer


2.2. 1-step invop diagram: python

  1from pathlib import Path
  2import subprocess
  3import os
  4import time
  5import magick_pdf_to_png
  6import invop_functions as iof
  7
  8
  9currfile_dir = Path(__file__).parent
 10tex_template_path = currfile_dir / "invop_template.tex"
 11texans_template_path = currfile_dir / "invop_template.tex"
 12tex_diagram_template_path = currfile_dir / "invop_diagram_template.tex"
 13
 14
 15def convert_to_pdf(tex_path, outputdir):
 16    tex_path = Path(tex_path).resolve()
 17    outputdir = Path(outputdir).resolve()
 18    # for testing
 19    # print(f"tex_path: {tex_path}")
 20    # print(f"outputdir: {outputdir}")
 21    try:
 22        # Generate the PDF
 23        subprocess.run(["latexmk", "-pdf", "-outdir=" + str(outputdir), str(tex_path)], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
 24        # # Clean auxiliary files after successful PDF generation
 25        subprocess.run(["latexmk", "-c", "-outdir=" + str(outputdir), str(tex_path)], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
 26        # for hosted remove stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL for debugging any errors
 27        # Remove the .tex file manually
 28        if tex_path.exists():
 29            os.remove(tex_path)
 30    except subprocess.CalledProcessError as e:
 31        print(f"Error: {e}")
 32
 33
 34# % end modify values for invop
 35# tex_keys_q = ['line1_LHS', 'line1_RHS', 'line2_LHSq', 'line2_RHSq', 'line3_LHS', 'line3_RHSq']
 36# keys without q are in template
 37tex_keys_q = ['line2_LHS', 'line2_RHS', 'line3_RHS']
 38
 39
 40def make1_diagram(tex_diagram_template_txt, num):
 41    tex_diagram_template_txt_ans = tex_diagram_template_txt
 42    kv = iof.get_1step_process_dict(num)
 43    for key, value in kv.items():
 44        tex_diagram_template_txt_ans = tex_diagram_template_txt_ans.replace(
 45            "<<" + key + ">>", value
 46        )
 47    for key, value in kv.items():
 48        if key not in tex_keys_q:
 49            tex_diagram_template_txt = tex_diagram_template_txt.replace(
 50                "<<" + key + ">>", value
 51            )
 52        else:
 53            tex_diagram_template_txt = tex_diagram_template_txt.replace(
 54                "<<" + key + ">>", kv[f'{key}q']
 55            )
 56    return tex_diagram_template_txt, tex_diagram_template_txt_ans
 57
 58
 59def main():
 60    num = input("Enter 1, 2, 3, 4 or 5 for +, -, X, /, random \n")
 61    if num.strip().isdigit():
 62        num = int(num)
 63        if not num in [1, 2, 3, 4, 5]:
 64            num = 5  # random by default
 65    else:
 66        num = 5  # random by default
 67    filename = input("Enter the base filename to be added to the prefix invop1_: \n")
 68    if not filename:
 69        filename = "1"  # "invop1_1_q and invop1_1_ans as default file"
 70    # set names of files that are made
 71    # questions
 72    tex_output_path = currfile_dir / f"invop1_{filename}_q.tex"
 73    pdf_path = currfile_dir / f"invop1_{filename}_q.pdf"
 74    png_path = currfile_dir / f"invop1_{filename}_q.png"
 75
 76    # answers
 77    tex_output_path_ans = currfile_dir / f"invop1_{filename}_ans.tex"
 78    pdf_path_ans = currfile_dir / f"invop1_{filename}_ans.pdf"
 79    png_path_ans = currfile_dir / f"invop1_{filename}_ans.png"
 80
 81    # Read in the LaTeX template file
 82    with open(tex_template_path, "r") as infile:
 83        tex_template_txt = infile.read()
 84    # Read in the LaTeX template file for answers
 85    with open(texans_template_path, "r") as infile:
 86        tex_template_txt_ans = infile.read()
 87    # Read in the LaTeX diagram template file
 88    with open(tex_diagram_template_path, "r") as infile:
 89        tex_diagram_template_txt = infile.read()
 90
 91    # Generate the <<diagram>> replacement tex
 92    diagram_text, diagram_text_ans = make1_diagram(tex_diagram_template_txt, num)
 93    # Replace the <<diagram>> placeholder in the LaTeX template
 94    tex_template_txt = tex_template_txt.replace("<<diagram>>", diagram_text)
 95    tex_template_txt_ans = tex_template_txt_ans.replace("<<diagram>>", diagram_text_ans)
 96    # Write the question diagram tex to an output file
 97    with open(tex_output_path, "w") as outfile:
 98        outfile.write(tex_template_txt)
 99    # Write the answer diagram tex to an output file
100    with open(tex_output_path_ans, "w") as outfile:
101        outfile.write(tex_template_txt_ans)
102
103    # Wait for the files to be created
104    time.sleep(1)
105    # convert to pdf
106    convert_to_pdf(tex_output_path, currfile_dir)
107    convert_to_pdf(tex_output_path_ans, currfile_dir)
108
109    # Wait for the files to be created
110    time.sleep(1)
111    # convert to png
112    magick_pdf_to_png.convert_pdf_to_png(pdf_path, png_path)
113    magick_pdf_to_png.convert_pdf_to_png(pdf_path_ans, png_path_ans)
114
115
116if __name__ == "__main__":
117    print("starting")
118    main()
119    print("finished")
The custom python module:
  1"""
  2Module of functions to return diagram dictionary for LaTeX
  3# escape {} in f strings by doubling them up {{}}
  4"""
  5import random
  6
  7
  8def get_1step_process_dict(num):
  9    if num is None or num == 5:
 10        num = random.randint(1, 4)
 11    match num:
 12        case 1:
 13            return add_dict()
 14        case 2:
 15            return sub_dict()
 16        case 3:
 17            return times_dict()
 18        case 4:
 19            return div_dict()
 20
 21
 22
 23def add_dict():
 24    # x + nx = na
 25    nx = random.randint(1, 10)
 26    na = random.randint(1, 10)
 27    xval = na - nx
 28    kv = dict()
 29    kv["line1_LHS"] = f"x + {nx}"
 30    kv["line2_LHS"] = f"x + {nx} - {nx}"
 31    kv["line2_LHSq"] = f"x + {nx} - {r'\dotuline{\hspace{5mm}}'}"
 32    kv["line3_LHS"] = f"x"
 33    kv["line1_RHS"] = f"{na}"
 34    kv["line2_RHS"] = f"{na} - {nx}"
 35    kv["line2_RHSq"] = f"{na} - {r'\dotuline{\hspace{5mm}}'}"
 36    kv["line3_RHS"] = f"{xval}"
 37    kv["line3_RHSq"] = f"{r'\dotuline{\hspace{5mm}}'}"
 38    return kv
 39
 40
 41def sub_dict():
 42    # x - nx = na
 43    nx = random.randint(1, 10)
 44    na = random.randint(1, 10)
 45    xval = na + nx
 46    kv = dict()
 47    kv["line1_LHS"] = f"x - {nx}"
 48    kv["line2_LHS"] = f"x - {nx} + {nx}"
 49    kv["line2_LHSq"] = f"x - {nx} + {r'\dotuline{\hspace{5mm}}'}"
 50    kv["line3_LHS"] = f"x"
 51    kv["line1_RHS"] = f"{na}"
 52    kv["line2_RHS"] = f"{na} + {nx}"
 53    kv["line2_RHSq"] = f"{na} + {r'\dotuline{\hspace{5mm}}'}"
 54    kv["line3_RHS"] = f"{xval}"
 55    kv["line3_RHSq"] = f"{r'\dotuline{\hspace{5mm}}'}"
 56    return kv
 57
 58
 59def times_dict():
 60    # x * nx = na
 61    nx = random.randint(2, 10)
 62    xval = random.randint(2, 10)
 63    na = xval * nx
 64
 65    kv = dict()
 66    kv["line1_LHS"] = f"{nx}x"
 67    kv["line2_LHS"] = f"\\frac{{{nx}x}}{{{nx}}}"
 68    #  kv["line2_LHSq"] = f"\\frac{{{nx}x}}{{{r'\dotuline{\hspace{5mm}}'}}}"
 69
 70    kv["line2_LHSq"] = f"\\frac{{{nx}x}}{{{r'\dotuline{\hspace{5mm}}'}}}"
 71    kv["line3_LHS"] = f"x"
 72    kv["line1_RHS"] = f"{na}"
 73    kv["line2_RHS"] = f"\\frac{{{na}}}{{{nx}}}"
 74    kv["line2_RHSq"] = f"\\frac{{{na}}}{{{r'\dotuline{\hspace{5mm}}'}}}"
 75    kv["line3_RHS"] = f"{xval}"
 76    kv["line3_RHSq"] = f"{r'\dotuline{\hspace{5mm}}'}"
 77    return kv
 78
 79
 80def div_dict():
 81    # x / nx = na
 82    nx = random.randint(2, 10)
 83    na = random.randint(2, 10)
 84    xval = na * nx
 85
 86    kv = dict()
 87    kv["line1_LHS"] = f"\\frac{{x}}{{{nx}}}"
 88    kv["line2_LHS"] = f"\\frac{{x}}{{{nx}}} \\times{nx}"
 89    kv["line2_LHSq"] = f"\\frac{{x}}{{{nx}}} \\times{{{r'\dotuline{\hspace{5mm}}'}}}"
 90    kv["line3_LHS"] = f"x"
 91    kv["line1_RHS"] = f"{na}"
 92    kv["line2_RHS"] = f"{na} \\times{nx}"
 93    kv["line2_RHSq"] = f"{na} \\times{{{r'\dotuline{\hspace{5mm}}'}}}"
 94    kv["line3_RHS"] = f"{xval}"
 95    kv["line3_RHSq"] = f"{r'\dotuline{\hspace{5mm}}'}"
 96    return kv
 97
 98
 99
100# ############################################
101
102def val_in_list_exclude(low, high, exclude):
103    # random 2 step: avoid xx, x/, /x, //, ++, --, +-, -+
104    vals = list(range(low, high + 1))
105    if exclude in vals:
106        if exclude in [1, 2]:
107            vals.remove(1)
108            vals.remove(2)
109        elif exclude in [3, 4]:
110            vals.remove(3)
111            vals.remove(4)
112    return random.choice(vals)
113
114
115def get_2step_process_dict(num1, num2):
116    if num1 is None or num1 == 5:
117        num1 = random.randint(1, 4)
118    if num2 is None or num2 == 5:
119        num2 = val_in_list_exclude(1, 4, num1)
120    processes = (num1, num2)
121    # processes = (3,3)
122    match processes:
123        case (1, 1):
124            return add_add_dict()
125        case (1, 2):
126            return add_sub_dict()
127        case (1, 3):
128            return add_times_dict()
129        case (1, 4):
130            return add_div_dict()
131        case (2, 1):
132            return sub_add_dict()
133        case (2, 2):
134            return sub_sub_dict()
135        case (2, 3):
136            return sub_times_dict()
137        case (2, 4):
138            return sub_div_dict()
139        case (3, 1):
140            return times_add_dict()
141        case (3, 2):
142            return times_sub_dict()
143        case (3, 3):
144            return times_times_dict()
145        case (3, 4):
146            return times_div_dict()
147        case (4, 1):
148            return div_add_dict()
149        case (4, 2):
150            return div_sub_dict()
151        case (4, 3):
152            return div_times_dict()
153        case (4, 4):
154            return div_div_dict()
155        case _:
156            return add_add_dict()
157
158
159def add_add_dict():
160    # bc = nx + na + nb
161    nx = random.randint(1, 10)
162    na = random.randint(1, 10)
163    nb = random.randint(1, 10)
164    bb = nx + na
165    bc = bb + nb
166
167    kv = dict()
168    kv["stepAB"] = f"+{na}"
169    kv["stepABrev"] = f"-{na}"
170    kv["stepBC"] = f"+{nb}"
171    kv["stepBCrev"] = f"-{nb}"
172
173    kv["boxA"] = f"x"
174    kv["boxB"] = f"x+{na}"
175    kv["boxC"] = f"x+{na + nb}"
176    kv["boxCrev"] = f"{bc}"
177    kv["boxBrev"] = f"{bb}"
178    kv["boxArev"] = f"{nx}"
179    return kv
180
181
182def add_sub_dict():
183    # bc = nx + na - nb
184    nx = random.randint(1, 10)
185    na = random.randint(1, 10)
186    bb = nx + na
187    if bb > 10:
188        nb = random.randint(1, 10)
189    else:
190        nb = random.randint(1, bb)
191    bc = bb - nb
192
193    kv = dict()
194    kv["stepAB"] = f"+{na}"
195    kv["stepABrev"] = f"-{na}"
196    kv["stepBC"] = f"-{nb}"
197    kv["stepBCrev"] = f"+{nb}"
198
199    kv["boxA"] = f"x"
200    kv["boxB"] = f"x+{na}"
201    if na - nb > 0:
202        kv["boxC"] = f"x+{na - nb}"
203    else:
204        kv["boxC"] = f"x-{nb - na}"
205    kv["boxCrev"] = f"{bc}"
206    kv["boxBrev"] = f"{bb}"
207    kv["boxArev"] = f"{nx}"
208    return kv
209
210
211def add_times_dict():
212    # bc = nx * na * nb
213    nx = random.randint(1, 10)
214    na = random.randint(1, 10)
215    nb = random.randint(1, 10)
216    bb = nx + na
217    bc = bb * nb
218
219    kv = dict()
220    kv["stepAB"] = f"+{na}"
221    kv["stepABrev"] = f"-{na}"
222    kv["stepBC"] = f"\\times{nb}"
223    kv["stepBCrev"] = f"\\div{nb}"
224
225    kv["boxA"] = f"x"
226    kv["boxB"] = f"x+{na}"
227    kv["boxC"] = f"{nb}(x + {na})"
228    kv["boxCrev"] = f"{bc}"
229    kv["boxBrev"] = f"{bb}"
230    kv["boxArev"] = f"{nx}"
231    return kv
232
233
234def add_div_dict():
235    # bc = (nx + na) / nb
236    # nx = (bc * nb) - na
237    bc = random.randint(1, 10)
238    nb = random.randint(1, 10)
239    bb = bc * nb
240    if bb > 10:
241        na = random.randint(1, 10)
242    else:
243        na = random.randint(1, bb)
244    nx = bb - na
245
246    kv = dict()
247    kv["stepAB"] = f"+{na}"
248    kv["stepABrev"] = f"-{na}"
249    kv["stepBC"] = f"\\div{nb}"
250    kv["stepBCrev"] = f"\\times{nb}"
251
252    kv["boxA"] = f"x"
253    kv["boxB"] = f"x+{na}"
254    kv["boxC"] = f"\\frac{{(x+{na})}}{{{nb}}}"
255    kv["boxCrev"] = f"{bc}"
256    kv["boxBrev"] = f"{bb}"
257    kv["boxArev"] = f"{nx}"
258    return kv
259
260
261def sub_add_dict():
262    # bc = nx - na + nb
263    na = random.randint(1, 10)
264    nx = na + random.randint(1, 10)
265    nb = random.randint(1, 10)
266    bb = nx - na
267    bc = bb + nb
268
269    kv = dict()
270    kv["stepAB"] = f"-{na}"
271    kv["stepABrev"] = f"+{na}"
272    kv["stepBC"] = f"-{nb}"
273    kv["stepBCrev"] = f"+{nb}"
274
275    kv["boxA"] = f"x"
276    kv["boxB"] = f"x-{na}"
277    kv["boxC"] = f"x-{na + nb}"
278    kv["boxCrev"] = f"{bc}"
279    kv["boxBrev"] = f"{bb}"
280    kv["boxArev"] = f"{nx}"
281    return kv
282
283
284def sub_sub_dict():
285    # bc = nx - na - nb
286    # nx = bc + nb + na
287    bc = random.randint(1, 10)
288    na = random.randint(1, 10)
289    nb = random.randint(1, 10)
290    bb = bc + nb
291    nx = bb + na
292
293    kv = dict()
294    kv["stepAB"] = f"-{na}"
295    kv["stepABrev"] = f"+{na}"
296    kv["stepBC"] = f"-{nb}"
297    kv["stepBCrev"] = f"+{nb}"
298
299    kv["boxA"] = f"x"
300    kv["boxB"] = f"x-{na}"
301    kv["boxC"] = f"x-{na + nb}"
302    kv["boxCrev"] = f"{bc}"
303    kv["boxBrev"] = f"{bb}"
304    kv["boxArev"] = f"{nx}"
305    return kv
306
307
308def sub_times_dict():
309    # bc = (nx - na) * nb
310    # nx = (bc + nb) * na
311    na = random.randint(1, 10)
312    nx = na + random.randint(1, 10)
313    nb = random.randint(1, 10)
314    bb = nx - na
315    bc = bb * nb
316
317    kv = dict()
318    kv["stepAB"] = f"-{na}"
319    kv["stepABrev"] = f"+{na}"
320    kv["stepBC"] = f"\\times{nb}"
321    kv["stepBCrev"] = f"\\div{nb}"
322
323    kv["boxA"] = f"x"
324    kv["boxB"] = f"x-{na}"
325    kv["boxC"] = f"{nb}(x-{na})"
326    kv["boxCrev"] = f"{bc}"
327    kv["boxBrev"] = f"{bb}"
328    kv["boxArev"] = f"{nx}"
329    return kv
330
331
332def sub_div_dict():
333    # bc = nx - na / nb
334    # nx = (bc + nb) * na
335    bc = random.randint(1, 10)
336    na = random.randint(1, 10)
337    nb = random.randint(1, 10)
338    bb = bc * nb
339    nx = bb + na
340
341    kv = dict()
342    kv["stepAB"] = f"-{na}"
343    kv["stepABrev"] = f"+{na}"
344    kv["stepBC"] = f"\\div{nb}"
345    kv["stepBCrev"] = f"\\times{nb}"
346
347    kv["boxA"] = f"x"
348    kv["boxB"] = f"x-{na}"
349    kv["boxC"] = f"\\frac{{(x-{na})}}{{{nb}}}"
350    kv["boxCrev"] = f"{bc}"
351    kv["boxBrev"] = f"{bb}"
352    kv["boxArev"] = f"{nx}"
353    return kv
354
355
356def times_add_dict():
357    # bc = nx * na + nb
358    nx = random.randint(2, 10)
359    na = random.randint(2, 10)
360    nb = random.randint(2, 10)
361    bb = nx * na
362    bc = nx * na + nb
363
364    kv = dict()
365    kv["stepAB"] = f"\\times{na}"
366    kv["stepABrev"] = f"\\div{na}"
367    kv["stepBC"] = f"+{nb}"
368    kv["stepBCrev"] = f"-{nb}"
369
370    kv["boxA"] = f"x"
371    kv["boxB"] = f"{na}x"
372    kv["boxC"] = f"{na}x + {nb}"
373    kv["boxCrev"] = f"{bc}"
374    kv["boxBrev"] = f"{bb}"
375    kv["boxArev"] = f"{nx}"
376    return kv
377
378
379def times_sub_dict():
380    # bc = nx * na - nb
381    nx = random.randint(2, 10)
382    na = random.randint(2, 10)
383    bb = nx * na
384    if bb > 10:
385        nb = random.randint(2, 10)
386    else:
387        nb = random.randint(2, bb)
388    bc = bb - nb
389
390    kv = dict()
391    kv["stepAB"] = f"\\times{na}"
392    kv["stepABrev"] = f"\\div{na}"
393    kv["stepBC"] = f"-{nb}"
394    kv["stepBCrev"] = f"+{nb}"
395
396    kv["boxA"] = f"x"
397    kv["boxB"] = f"{na}x"
398    kv["boxC"] = f"{na}x - {nb}"
399    kv["boxCrev"] = f"{bc}"
400    kv["boxBrev"] = f"{bb}"
401    kv["boxArev"] = f"{nx}"
402    return kv
403
404
405def times_times_dict():
406    # bc = nx * na * nb
407    nx = random.randint(2, 10)
408    na = random.randint(2, 10)
409    nb = random.randint(2, 10)
410    bb = nx * na
411    bc = nx * na * nb
412
413    kv = dict()
414    kv["stepAB"] = f"\\times{na}"
415    kv["stepABrev"] = f"\\div{na}"
416    kv["stepBC"] = f"\\times{nb}"
417    kv["stepBCrev"] = f"\\div{nb}"
418
419    kv["boxA"] = f"x"
420    kv["boxB"] = f"{na}x"
421    kv["boxC"] = f"{na * nb}x"
422    kv["boxCrev"] = f"{bc}"
423    kv["boxBrev"] = f"{bb}"
424    kv["boxArev"] = f"{nx}"
425    return kv
426
427
428def times_div_dict():
429    # bc = nx * na / nb
430    nb = random.randint(2, 3)
431    na = nb * random.randint(2, 4)
432    nx = random.randint(2, 10)
433    bb = nx * na
434    bc = int(bb / nb)
435
436    kv = dict()
437    kv["stepAB"] = f"\\times{na}"
438    kv["stepABrev"] = f"\\div{na}"
439    kv["stepBC"] = f"\\div{nb}"
440    kv["stepBCrev"] = f"\\times{nb}"
441
442    kv["boxA"] = f"x"
443    kv["boxB"] = f"{na}x"
444    kv["boxC"] = f"{int(na / nb)}x"
445    kv["boxCrev"] = f"{bc}"
446    kv["boxBrev"] = f"{bb}"
447    kv["boxArev"] = f"{nx}"
448    return kv
449
450
451def div_add_dict():
452    # bc = (nx / na) + nb
453    # nx = (bc - nb) * na
454    na = random.randint(2, 10)
455    nx = na * random.randint(1, 10)
456    nb = random.randint(1, 10)
457    bb = int(nx / na)
458    bc = bb + nb
459
460    kv = dict()
461    kv["stepAB"] = f"\\div{na}"
462    kv["stepABrev"] = f"\\times{na}"
463    kv["stepBC"] = f"+{nb}"
464    kv["stepBCrev"] = f"-{nb}"
465    kv["boxA"] = f"x"
466    kv["boxB"] = f"\\frac{{x}}{{{na}}}"
467    kv["boxC"] = f"\\frac{{x}}{{{na}}} + {nb}"
468    kv["boxCrev"] = f"{bc}"
469    kv["boxBrev"] = f"{bb}"
470    kv["boxArev"] = f"{nx}"
471    return kv
472
473
474def div_sub_dict():
475    # bc = (nx / na) - nb
476    # nx = (bc + nb) * na
477    bc = random.randint(2, 10)
478    na = random.randint(2, 10)
479    nb = random.randint(2, 10)
480    bb = bc + nb
481    nx = bb * na
482
483    kv = dict()
484    kv["stepAB"] = f"\\div{na}"
485    kv["stepABrev"] = f"\\times{na}"
486    kv["stepBC"] = f"-{nb}"
487    kv["stepBCrev"] = f"+{nb}"
488    kv["boxA"] = f"x"
489    kv["boxB"] = f"\\frac{{x}}{{{na}}}"
490    kv["boxC"] = f"\\frac{{x}}{{{na}}} - {nb}"
491    kv["boxCrev"] = f"{bc}"
492    kv["boxBrev"] = f"{bb}"
493    kv["boxArev"] = f"{nx}"
494    return kv
495
496
497def div_times_dict():
498    # bc = (nx / na) * nb
499    na = random.randint(2, 10)
500    nx = na * random.randint(2, 10)
501    nb = random.randint(2, 10)
502    bb = int(nx / na)
503    bc = bb * nb
504
505    kv = dict()
506    kv["stepAB"] = f"\\div{na}"
507    kv["stepABrev"] = f"\\times{na}"
508    kv["stepBC"] = f"\\div{nb}"
509    kv["stepBCrev"] = f"\\times{nb}"
510    kv["boxA"] = f"x"
511    kv["boxB"] = f"\\frac{{x}}{{{na}}}"
512    kv["boxC"] = f"\\frac{{x}}{{{na * nb}}}"
513    kv["boxCrev"] = f"{bc}"
514    kv["boxBrev"] = f"{bb}"
515    kv["boxArev"] = f"{nx}"
516    return kv
517
518
519def div_div_dict():
520    # bc = (nx / na) / nb
521    # escape {} in f strings by doubling them up {{}}
522    bc = random.randint(2, 10)
523    na = random.randint(2, 10)
524    nb = random.randint(2, 10)
525    bb = bc * nb
526    nx = bb * na
527
528    kv = dict()
529    kv["stepAB"] = f"\\div{na}"
530    kv["stepABrev"] = f"\\times{na}"
531    kv["stepBC"] = f"\\div{nb}"
532    kv["stepBCrev"] = f"\\times{nb}"
533    kv["boxA"] = f"x"
534    kv["boxB"] = f"\\frac{{x}}{{{na}}}"
535    kv["boxC"] = f"\\frac{{x}}{{{na * nb}}}"
536    kv["boxCrev"] = f"{bc}"
537    kv["boxBrev"] = f"{bb}"
538    kv["boxArev"] = f"{nx}"
539    return kv

2.3. 1-step invop diagram: LaTeX

 1% inv op template
 2\documentclass[varwidth]{standalone}
 3\usepackage{amsmath}
 4\usepackage[normalem]{ulem} % [normalem] prevents the package from changing the default behavior of `\\emph` to underline.
 5\usepackage[active,tightpage]{preview}
 6\PreviewEnvironment{align*}
 7
 8\begin{document}
 9    <<diagram>>
10\end{document}
1\begin{varwidth}{0.25\paperwidth}
2    \begin{align*}
3    <<line1_LHS>> &= <<line1_RHS>>\\
4    <<line2_LHS>> &= <<line2_RHS>>\\
5    <<line3_LHS>> &= <<line3_RHS>>\\
6\end{align*}
7\end{varwidth}