2. Equations 2 step diagrams

The python file to make a 2-step invop diagram is below.
The required LaTeX files are below.
The 2 custom python modules required are:
The python file, invop_2step_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 arithmetic process:
"Enter 1, 2, 3, 4 or 5 for +, -, X, /, random for the 2nd process"
Choose the file name base:
"Enter the base filename to be added to the prefix :"
The prefix will be β€œinvop2” for standard operations.
The filename will have β€œ_q” added for the question diagram and β€œ_ans” for the answer diagram.

2.1. Example 2-step invop diagram

question

answer

question

answer


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

2.3. 2-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    <<line4_LHS>> &= <<line4_RHS>>\\
7    <<line5_LHS>> &= <<line5_RHS>>\\
8\end{align*}
9\end{varwidth}