3. Equations 2-step booklet python

The python file to make 2-step invop booklets is below.
The 2 custom python modules required are:
The python file, invop_2step_booklet_maker.py, when run, will ask for these inputs:
Choose the first arithmetic process: "Enter 1, 2, 3, 4 or 5 for +, -, X, /, random.
Choose the second arithmetic process: "Enter 1, 2, 3, 4 or 5 for +, -, X, /, random.
Choose the number of questions from 1 to 80: "Enter the number of questions from 1 to 80"
Choose the file name base: "Enter the base filename to be added to the prefix invop_2Bk_:". The filename will have β€œ_q” added for the question booklet and β€œ_ans” for the answer booklet.

3.1. Sample 2-step invop booklet

random_q

random_ans


3.2. 2-step invop: python

  1from pathlib import Path
  2import subprocess
  3import os
  4import time
  5# import magick_pdf_to_png
  6import invop_functions as iof
  7
  8currfile_dir = Path(__file__).parent
  9tex_template_path = currfile_dir / "invop_2step_booklet_template.tex"
 10texans_template_path = currfile_dir / "invop_2step_booklet_ans_template.tex"
 11tex_diagram_template_path = currfile_dir / "invop_2step_booklet_diagram_template.tex"
 12
 13q_per_column = 5
 14q_per_page = q_per_column * 2
 15max_q = 100
 16
 17def convert_to_pdf(tex_path, outputdir):
 18    tex_path = Path(tex_path).resolve()
 19    outputdir = Path(outputdir).resolve()
 20    # for testing
 21    # print(f"tex_path: {tex_path}")
 22    # print(f"outputdir: {outputdir}")
 23    try:
 24        # Generate the PDF
 25        subprocess.run(["latexmk", "-pdf", "-outdir=" + str(outputdir), str(tex_path)], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
 26        # # Clean auxiliary files after successful PDF generation
 27        subprocess.run(["latexmk", "-c", "-outdir=" + str(outputdir), str(tex_path)], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
 28        # for hosted remove stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL for debugging any errors
 29        # Remove the .tex file manually
 30        if tex_path.exists():
 31            os.remove(tex_path)
 32    except subprocess.CalledProcessError as e:
 33        print(f"Error: {e}")
 34
 35
 36# % end modify values for invop
 37#['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']
 38# get the full keys above then get those with q, then delete the q
 39tex_keys_q = ['line2_LHS', 'line4_LHS', 'line2_RHS', 'line3_RHS', 'line4_RHS', 'line5_RHS']
 40
 41
 42def make1_diagram(tex_diagram_template_txt, num1, num2):
 43    tex_diagram_template_txt_ans = tex_diagram_template_txt
 44    kv = iof.get_2step_process_dict(num1, num2)
 45    # do ans sheet
 46    for key, value in kv.items():
 47        tex_diagram_template_txt_ans = tex_diagram_template_txt_ans.replace(
 48            "<<" + key + ">>", value
 49        )
 50    # do q sheet
 51    for key, value in kv.items():
 52        if key not in tex_keys_q:
 53            tex_diagram_template_txt = tex_diagram_template_txt.replace(
 54                "<<" + key + ">>", value
 55            )
 56        else:
 57            tex_diagram_template_txt = tex_diagram_template_txt.replace(
 58                "<<" + key + ">>", kv[f'{key}q']
 59            )
 60    return tex_diagram_template_txt, tex_diagram_template_txt_ans
 61
 62
 63def main():
 64    num1 = input("Enter 1, 2, 3, 4 or 5 for +, -, X, /, random for the 1st process \n")
 65    if num1.strip().isdigit():
 66        num1 = int(num1)
 67        if not num1 in [1, 2, 3, 4, 5]:
 68            num1 = 5  # random by default
 69    else:
 70        num1 = 5  # random by default
 71    #
 72    num2 = input("Enter 1, 2, 3, 4 or 5 for +, -, X, /, random for the 2nd process \n")
 73    if num2.strip().isdigit():
 74        num2 = int(num2)
 75        if not num2 in [1, 2, 3, 4, 5]:
 76            num2 = 5  # random by default
 77    else:
 78        num2 = 5  # random by default
 79    #
 80    numq = input("Enter the number of questions from 1 to 100, with 10 per page \n")
 81    if numq.strip().isdigit():
 82        numq = int(numq)
 83        if not numq in range(1, max_q + 1):
 84            numq = max_q  # max
 85    else:
 86        numq = q_per_page  # by default fits on one page
 87    #
 88    filename = input(
 89        "Enter the base filename to be added to the prefix invop_2Bk_: \n")
 90    if not filename:
 91        filename = "1"  # "invop_2Bk_1_q and invop_2Bk_1_ans as default file"
 92    # set names of files that are made
 93    # questions
 94    tex_output_path = currfile_dir / f"invop_2Bk_{filename}_q.tex"
 95    pdf_path = currfile_dir / f"invop_2Bk_{filename}_q.pdf"
 96
 97    # answers
 98    tex_output_path_ans = currfile_dir / f"invop_2Bk_{filename}_ans.tex"
 99    pdf_path_ans = currfile_dir / f"invop_2Bk_{filename}_ans.pdf"
100
101    # Read in the LaTeX template file
102    with open(tex_template_path, "r") as infile:
103        tex_template_txt = infile.read()
104    # Read in the LaTeX template file for answers
105    with open(texans_template_path, "r") as infile:
106        tex_template_txt_ans = infile.read()
107    # Read in the LaTeX diagram template file
108    with open(tex_diagram_template_path, "r") as infile:
109        tex_diagram_template_txt = infile.read()
110
111    # Generate the <<diagram>> replacement tex
112    # diagram_text, diagram_text_ans = make1_diagram(tex_diagram_template_txt, num1)
113
114    # <<diagrams>>
115    # generate diagrams text and text for answers
116    diagrams_text = ""
117    diagrams_text_ans = ""
118    # add the headtext
119    # must have no space in \end{minipage}\columnbreak for column break to occur at correct place.
120    headtext_col = r'''\columnbreak
121    '''
122    headtext_page = r'''\newpage
123    '''
124    # headtext_page = r'''\newpage ~ \newline ~ \newline'''
125
126    for i in range(1, numq + 1):
127        img_tex, img_tex_ans = make1_diagram(tex_diagram_template_txt, num1, num2)
128        diagrams_text += img_tex
129        diagrams_text_ans += img_tex_ans
130        if i % q_per_page == 0 and numq + 1 > i:
131            diagrams_text += headtext_page
132            diagrams_text_ans += headtext_page
133        elif i % q_per_column == 0 and i > 1 and numq + 1 > i:
134            diagrams_text += headtext_col
135            diagrams_text_ans += headtext_col
136
137    # Replace the <<diagrams>> placeholder in the LaTeX template
138    tex_template_txt = tex_template_txt.replace("<<diagrams>>", diagrams_text)
139    tex_template_txt_ans = tex_template_txt_ans.replace(
140        "<<diagrams>>", diagrams_text_ans)
141    # Write the question diagrams tex to an output file
142    with open(tex_output_path, "w") as outfile:
143        outfile.write(tex_template_txt)
144    # Write the answer diagrams tex to an output file
145    with open(tex_output_path_ans, "w") as outfile:
146        outfile.write(tex_template_txt_ans)
147
148    # Wait for the files to be created
149    time.sleep(1)
150    # convert to pdf
151    convert_to_pdf(tex_output_path, currfile_dir)
152    convert_to_pdf(tex_output_path_ans, currfile_dir)
153
154    # Wait for the files to be created
155    # time.sleep(1)
156    # convert to png
157    # magick_pdf_to_png.convert_pdf_to_png(pdf_path, png_path)
158    # magick_pdf_to_png.convert_pdf_to_png(pdf_path_ans, png_path_ans)
159
160
161if __name__ == "__main__":
162    print("starting")
163    main()
164    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

3.3. 2-step invop: LaTeX

 1\refstepcounter{minipagecount} % increments the counter minipagecount by one.
 2\noindent{(\theminipagecount)}\hspace{0.1mm} % By default, LaTeX indents the first line of a new paragraph, but \noindent overrides this
 3% and inserts the current value of the minipagecount counter, enclosed in parentheses
 4\begin{minipage}[t]{0.45\textwidth} % The [t] option aligns the top of the minipage with the baseline of the surrounding text.
 5    \vspace{-26pt}  % moves the content of the minipage up, reducing the space between the minipage content and the preceding text.
 6    \raggedright %  the text lines up on the left side, but the right side will be ragged.
 7    \begin{align*} % The * align environment does not number the equations- Each line is aligned at the & symbol
 8        <<line1_LHS>> &= <<line1_RHS>>\\
 9        <<line2_LHS>> &= <<line2_RHS>>\\
10        <<line3_LHS>> &= <<line3_RHS>>\\
11        <<line4_LHS>> &= <<line4_RHS>>\\
12        <<line5_LHS>> &= <<line5_RHS>>\\
13    \end{align*}
14\end{minipage}
 1\documentclass[12pt]{article}
 2\usepackage{tikz}
 3\usepackage{amsmath}
 4% Underlining package
 5\usepackage[normalem]{ulem} % [normalem] prevents the package from changing the default behavior of \emph to underline.
 6\usepackage[a4paper, portrait, margin=1cm]{geometry}
 7\usepackage{multicol}
 8\usepackage{fancyhdr}
 9
10\def \HeadingQuestions {\section*{\Large Name: \underline{\hspace{8cm}} \hfill Date: \underline{\hspace{3cm}}} \vspace{-3mm}
11{Inverse operations: Questions} \vspace{1pt}\hrule}
12
13% only in Q due to increased line height with dotted underline
14\linespread{0.9} % Adjust line spacing factor to 0.9 if needed to fit 5 in column for 2 div steps
15
16% raise footer with page number; no header
17\fancypagestyle{myfancypagestyle}{
18  \fancyhf{}% clear all header and footer fields
19  \renewcommand{\headrulewidth}{0pt} % no rule under header
20  \fancyfoot[C] {\thepage} \setlength{\footskip}{14.5pt} % raise page number allowed min 14.5pt
21}
22\pagestyle{myfancypagestyle}  % apply myfancypagestyle
23\newcounter{minipagecount}
24\begin{document}
25\HeadingQuestions
26\vspace{1mm}
27\begin{multicols}{2}
28<<diagrams>>
29\end{multicols}
30\end{document}
 1\documentclass[12pt]{article}
 2\usepackage{tikz}
 3\usepackage{amsmath}
 4% Underlining package
 5\usepackage[normalem]{ulem} % [normalem] prevents the package from changing the default behavior of `\\emph` to underline.
 6\usepackage[a4paper, portrait, margin=1cm]{geometry}
 7\usepackage{multicol}
 8\usepackage{fancyhdr}
 9
10\def \HeadingAnswers {\section*{\Large Name: \underline{\hspace{8cm}} \hfill Date: \underline{\hspace{3cm}}} \vspace{-3mm}
11{Inverse operations: Answers} \vspace{1pt}\hrule}
12
13% raise footer with page number; no header
14\fancypagestyle{myfancypagestyle}{
15  \fancyhf{}% clear all header and footer fields
16  \renewcommand{\headrulewidth}{0pt} % no rule under header
17  \fancyfoot[C] {\thepage} \setlength{\footskip}{14.5pt} % raise page number allowed min 14.5pt
18}
19\pagestyle{myfancypagestyle}  % apply myfancypagestyle
20\newcounter{minipagecount}
21\begin{document}
22\HeadingAnswers
23\vspace{1mm}
24\begin{multicols}{2}
25  <<diagrams>>
26\end{multicols}
27\end{document}