branch: externals/org-mathsheet
commit d241eb01d50e3fc9f8ac25effc9ac4e49c8d0194
Author: Ian Martins <ia...@jhu.edu>
Commit: Ian Martins <ia...@jhu.edu>

    Fixed field index and var-list reference, cleanup
---
 example.org   |  80 ++++++++++++++++------------
 mathsheet.org | 168 +++++++++++++++++++++++++++++++++-------------------------
 2 files changed, 143 insertions(+), 105 deletions(-)

diff --git a/example.org b/example.org
index 53315935e0..3abcae6d71 100644
--- a/example.org
+++ b/example.org
@@ -1,38 +1,52 @@
 * add and subtract
 
-#+name: first-set
-| weight | order | template                      | descr                  |
-|--------+-------+-------------------------------+------------------------|
-|      3 |     1 | [1..10] + [0..10]             | simple                 |
-|      2 |     2 | [1..10] + [8..15]             | second number bigger   |
-|      1 |     2 | [a=3..10] - [0..$a]           | subtraction            |
-|      1 |     3 | [1..10] + [1..7] + [1..5]     | three numbers          |
-|      1 |     4 | [a=1..10] + [0..10] - [0..$a] | three with subtraction |
-|      0 |     0 | [$a*[1..5]] / [a=1..10]       | division               |
+#+name: add-sub-1
+| weight | order | template            | descr                                 
    |
+|--------+-------+---------------------+-------------------------------------------|
+|      3 |     1 | [1..10] + [0..10]   | simple                                
    |
+|      2 |     2 | [1..10] + [8..12]   | second number bigger                  
    |
+|      2 |     2 | [1..12] + [0..10]   | first number bigger                   
    |
+|      1 |     3 | [a=2..10] - [1..$a] | small number subtraction, positive 
result |
 
-#+BEGIN: problem-set :templates "first-set" :count 21 :name "Noble"
-| problem   | answer |
-|-----------+--------|
-| 2 + 7     |      9 |
-| 1 + 3     |      4 |
-| 4 + 6     |     10 |
-| 7 + 0     |      7 |
-| 9 + 1     |     10 |
-| 6 + 7     |     13 |
-| 6 + 8     |     14 |
-| 3 + 13    |     16 |
-| 9 + 11    |     20 |
-| 9 - 2     |      7 |
-| 4 - 1     |      3 |
-| 5 + 14    |     19 |
-| 5 + 11    |     16 |
-| 1 + 10    |     11 |
-| 8 - 3     |      5 |
-| 7 + 5 + 2 |     14 |
-| 2 + 5 + 4 |     11 |
-| 9 + 4 + 2 |     15 |
-| 5 + 8 - 4 |      9 |
-| 5 + 8 - 3 |     10 |
-| 7 + 9 - 2 |     14 |
+#+name: add-sub-2
+| weight | order | template                      | descr                       
            |
+|--------+-------+-------------------------------+-----------------------------------------|
+|      3 |     1 | [1..10] + [0..10]             | simple                      
            |
+|      2 |     2 | [1..10] + [8..15]             | second number bigger        
            |
+|      1 |     2 | [a=3..10] - [0..$a]           | subtraction                 
            |
+|      1 |     3 | [1..10] + [1..7] + [1..5]     | three numbers               
            |
+|      1 |     4 | [a=1..10] + [0..10] - [0..$a] | three with subtraction, 
positive result |
+|      0 |     0 | [$a*[1..5]] / [a=1..10]       | division                    
            |
+
+#+name: algebra-1
+| weight | order | template                   | descr  |
+|--------+-------+----------------------------+--------|
+|      3 |     1 | x/2 + [a=0..20] = [$a..21] | simple |
+
+#+BEGIN: problem-set :templates "algebra-1" :count 21
+| problem       | answer                |
+|---------------+-----------------------|
+| x/2 + 12 = 13 | x / 2. = 1            |
+| x/2 + 11 = 18 | x / 14. = 1           |
+| x/2 + 11 = 16 | x / 10. = 1           |
+| x/2 + 6 = 19  | x / 26. = 1           |
+| x/2 + 3 = 5   | x / 4. = 1            |
+| x/2 + 19 = 19 | x / 2 = 0             |
+| x/2 + 19 = 20 | x / 2. = 1            |
+| x/2 + 2 = 9   | x / 14. = 1           |
+| x/2 + 12 = 20 | x / 16. = 1           |
+| x/2 + 0 = 11  | x / 22. = 1           |
+| x/2 + 2 = 18  | x / 32. = 1           |
+| x/2 + 7 = 15  | x / 16. = 1           |
+| x/2 + 0 = 9   | x / 18. = 1           |
+| x/2 + 17 = 17 | x / 2 = 0             |
+| x/2 + 13 = 17 | x / 8. = 1            |
+| x/2 + 0 = 6   | x / 12. = 1           |
+| x/2 + 3 = 4   | x / 2. = 1            |
+| x/2 + 8 = 12  | x / 8. = 1            |
+| x/2 + 16 = 19 | x / 5.99999999999 = 1 |
+| x/2 + 7 = 13  | x / 12. = 1           |
+| x/2 + 9 = 20  | x / 22. = 1           |
 #+END:
+
 * bigger addition and multiplications
diff --git a/mathsheet.org b/mathsheet.org
index d30683cc33..17b962d067 100644
--- a/mathsheet.org
+++ b/mathsheet.org
@@ -5,7 +5,7 @@ distribution and order is also configurable.
 
 Similar to https://www.math-aids.com.
 * Problem Templates
-** Description
+** Overview
 This section contains some example templates. Each table defines a
 worksheet. Each time the worksheet is generated the problems are
 re-randomized.
@@ -31,23 +31,40 @@ in square brackets.
 
 ** Examples
 
+We label the table so that we can refer to it from the dynamic block
+that generates the worksheet. Only the first three columns are used.
+
 #+name: firstset
-| weight | order | template                      | descr                  |
-|--------+-------+-------------------------------+------------------------|
-|      3 |     1 | [1..10] + [0..10]             | simple                 |
-|      2 |     2 | [1..10] + [8..15]             | second number bigger   |
-|      1 |     2 | [a=3..10] - [0..$a]           | subtraction            |
-|      1 |     3 | [1..10] + [1..7] + [1..5]     | three numbers          |
-|      1 |     4 | [a=1..10] + [0..10] - [0..$a] | three with subtraction |
-|      0 |     0 | [$a*[1..5]] / [a=1..10]       | division               |
-
-* script
-** problem generation
-*** variables
-Need ~var-list~ to keep track of the variables between fields.
+| weight | order | template                      | descr                       
 |
+|--------+-------+-------------------------------+------------------------------|
+|      3 |     1 | [1..10] + [0..10]             | simple                      
 |
+|      2 |     2 | [1..10] + [8..15]             | second number bigger        
 |
+|      1 |     2 | [a=3..10] - [0..$a]           | subtraction                 
 |
+|      1 |     3 | [1..10] + [1..7] + [1..5]     | three terms                 
 |
+|      1 |     4 | [a=1..10] + [0..10] - [0..$a] | three terms with 
subtraction |
+|      0 |     0 | [$a*[1..5]] / [a=1..10]       | division                    
 |
+
+*** TODO handle templates with variables
+can be done like this
+#+begin_export
+(calc-eval "solve(2x = 1 + 2, [x])")
+#+end_export
+
+* Code walkthrough
+** Problem generation
+*** Dependencies
+This package needs [[https://elpa.gnu.org/packages/peg.html][peg]].
+
+#+begin_src elisp :tangle mathsheet.el
+  (require 'peg)
+#+end_src
+
+*** Variables
+Need ~ianxm/var-list~ to keep track of the variables between fields.
 
 ~worksheet-template~ is the LaTeX template for the worksheet.
 
+#+name: variables
 #+begin_src elisp :tangle mathsheet.el :var page=page
   (defvar ianxm/var-list '()
     "List of variables used in a problem")
@@ -55,7 +72,7 @@ Need ~var-list~ to keep track of the variables between fields.
   (defconst ianxm/worksheet-template page
     "LaTeX template for worksheet")
 #+end_src
-*** scan problem
+*** Scan problem
 
 must call with point at the start of a problem. moves the point to the
 end of the problem. returns a list of fields, formatted as:
@@ -103,7 +120,7 @@ new field to the list when we close the current field.
                                    (intern (concat "_" (number-to-string 
field-index)))
                                    nil (copy-marker l) nil nil)
                                   open-fields)
-                            field-index (1+ field-index)
+                            (setq field-index (1+ field-index))
                             ".")))
            (assignment (region (substring letter)) "="
                        `(l v r -- (progn
@@ -124,7 +141,7 @@ new field to the list when we close the current field.
                              ".")))
            (letter [a-z])
            (digit [0-9])
-           (symbol (or "." "+" "-" "*" "/" "(" ")")))
+           (symbol (or "." "+" "-" "*" "/" "(" ")" "=")))
 
         (peg-run (peg stuff)
                  (lambda (x) (message "failed %s" x))
@@ -133,16 +150,33 @@ new field to the list when we close the current field.
                    closed-fields)))))
 #+end_src
 
-*** reduce field
+test scan
+#+begin_src elisp :noweb yes
+  <<scan-problem>>
+
+  (with-temp-buffer
+    (insert "y = [1..4] + [5..9]")
+    (goto-char (point-min))
+    (ianxm/scan-problem))
+#+end_src
+
+#+RESULTS:
+| _1 | nil | #<marker in no buffer> | #<marker (moves after insertion) in no 
buffer> | nil |
+| _0 | nil | #<marker in no buffer> | #<marker (moves after insertion) in no 
buffer> | nil |
+
+*** Reduce field
 
 This must be called with point at the start of a field. This moves the
 point to the end of the field. This returns a list containing the
-value to which the field reduces.
+value to which the field reduces. It is wrapped in a list because
+~peg-run~ returns its stack and the value is the last thing remaining on
+the stack when peg completes.
 
 This uses the peg package to parse the field. This time there
 shouldn't be any fields embedded within the field. We should have
 already evaluated and replaced them.
 
+#+name: reduce-field
 #+begin_src elisp :tangle mathsheet.el
   (defun ianxm/reduce-field ()
     (with-peg-rules
@@ -157,7 +191,7 @@ already evaluated and replaced them.
          (operation (substring (or "+" "-" "*" "/")))
          (assignment var-lhs space "=" space (or range sequence)
                      `(v r -- (progn
-                                (push (cons (intern v) r) var-list)
+                                (push (cons (intern v) r) ianxm/var-list)
                                 r)))
          (range value ".." value
                 `(min max -- (+ (random (- max min)) min)))
@@ -168,7 +202,7 @@ already evaluated and replaced them.
          (parenthetical "(" expression ")")
          (var-lhs (substring letter)) ; var for assignment
          (var-rhs "$" (substring letter) ; var for use
-                  `(v -- (let ((val (alist-get (intern v) var-list)))
+                  `(v -- (let ((val (alist-get (intern v) ianxm/var-list)))
                            (or val (error "var %s not set" v)))))
          (space (* [space]))
          (letter [a-z])
@@ -179,11 +213,24 @@ already evaluated and replaced them.
                (lambda (x) (funcall x)))))
 #+end_src
 
-*** replace field
+test with
+#+begin_src elisp :noweb yes :var page=page
+  <<variables>>
+  <<reduce-field>>
 
-Replace a field with the value returned from processing it.
+  (with-temp-buffer
+      (insert "[1..4]")
+      (goto-char (point-min))
+      (ianxm/reduce-field))
+#+end_src
+
+#+RESULTS:
+| 3 |
+
+*** Replace field
+
+Replace a field with the value returned from reducing it.
 
-#+name: replace-field
 #+begin_src elisp :tangle mathsheet.el
   (defun ianxm/replace-field (node)
     (let ((start (caddr node))
@@ -197,11 +244,12 @@ Replace a field with the value returned from processing 
it.
         (insert (number-to-string val)))))
 #+end_src
 
-*** dfs visit
+*** DFS visit
 
-check dependencies then visit the node
+This uses a depth first search to ensure that we visit (reduce and
+replace) the fields in dependency order. Check dependencies then visit
+the node.
 
-#+name: dfs-visit
 #+begin_src elisp :tangle mathsheet.el
   (defun ianxm/dfs-visit (node fields)
     (pcase (nth 4 node)
@@ -217,7 +265,7 @@ check dependencies then visit the node
        (ianxm/replace-field node) ; visit
        (setcar (cddddr node) 2)))) ; mark done
 #+end_src
-*** fill fields in problem
+*** Fill fields in problem
 
 processes all fields in a problem.
 
@@ -225,7 +273,6 @@ processes all fields in a problem.
 (full-problem (buffer-substring (point-at-bol) (point-at-eol)))
 #+end_example
 
-#+name: fill-problem
 #+begin_src elisp :tangle mathsheet.el
   (defun ianxm/fill-problem (full-problem)
       (interactive)
@@ -237,12 +284,11 @@ processes all fields in a problem.
 
           ;; find fields, assignments, dependencies
           (setq fields (ianxm/scan-problem))
-          ;;(message "fields %s" fields)
 
           ;; order fields according to dependencies
           (dolist (node fields)
             (ianxm/dfs-visit node fields))
-          (setq var-list '())
+          (setq ianxm/var-list '())
           (buffer-string))))
 #+end_src
 
@@ -279,7 +325,7 @@ other examples
   [0..[$a..$b]]
 #+end_example
 
-** generate problem set from templates
+*** Generate problem set from templates
 
 1. load table
 2. determine how many of each
@@ -326,14 +372,18 @@ other examples
                problem answer)
 
           (let ((added 0)
+                (dup-count 0)
                 problem-set
                 problem)
             (while (< added needed) ; add until "needed" are kept
               (setq problem (ianxm/fill-problem (caddr item)))
-              (when (not (member problem problem-set)) ; dedup problems
+              (if (member problem problem-set) ; dedup problems
+                  (setq dup-count (1+ dup-count))
                 (push problem problem-set)
                 (push (list problem (calc-eval problem) (cadr item)) problems)
-                (setq added (1+ added)))))))
+                (setq added (1+ added)))
+              (when (> dup-count 100)
+                (error "Giving up, too many dups"))))))
 
       ;; shuffle
       (dotimes (ii (- (length problems) 1))
@@ -350,38 +400,7 @@ other examples
        problems)))
 #+end_src
 
-test with this
-
-#+name: problem-set
-#+begin_src elisp :results table :noweb yes
-  <<full>>
-
-  (ianxm/generate-problems "firstset" 20)
-#+end_src
-
-#+RESULTS: problem-set
-| 1 + 6     |  7 |
-| 4 + 0     |  4 |
-| 1 + 3     |  4 |
-| 3 + 9     | 12 |
-| 2 + 8     | 10 |
-| 9 + 5     | 14 |
-| 2 + 5     |  7 |
-| 3 + 5     |  8 |
-| 7 + 8     | 15 |
-| 6 + 13    | 19 |
-| 4 + 13    | 17 |
-| 8 + 14    | 22 |
-| 9 + 11    | 20 |
-| 5 + 13    | 18 |
-| 6 - 0     |  6 |
-| 5 - 1     |  4 |
-| 9 + 6 + 2 | 17 |
-| 4 + 5 + 2 | 11 |
-| 6 + 6 - 2 | 10 |
-| 2 + 8 - 0 | 10 |
-
-** update problem-set block
+** Update problem-set block
 
 This generates a problem set and writes it to the dynamic block. This
 is triggered by C-c C-c on the dynamic block header.
@@ -423,14 +442,14 @@ I need to extract the values
          problems))))
 #+end_src
 
-* create worksheet
-** lay out page
+** Generate PDF
+*** Lay out page
 this wraps the problems with a tex header and footer.
 
 solution for how to enumerate with circled numbers from 
[[https://latex.org/forum/viewtopic.php?p=40006&sid=d202f756313add2391c3140fbeafe2ff#p40006][here]]
 
 #+name: page
-#+begin_src latex :results value silent :var student="Noble"
+#+begin_src latex :results value silent
   \documentclass[12pt]{article}
   \usepackage[top=1in, bottom=0.8in, left=0.8in, right=0.8in]{geometry}
   \usepackage{fancyhdr}
@@ -480,10 +499,15 @@ solution for how to enumerate with circled numbers from 
[[https://latex.org/foru
 
   \end{document}
 #+end_src
+*** Write PDF
+
+This writes the generated into a local file and runs ~texi2pdf~ to
+convert it to a pdf. We save it as ~worksheet.tex~ and the final
+worksheet is named ~worksheet.pdf~. Each execution will overwrite the
+same file.
+
+**** TODO get the base filename from the org buffer
 
-** generate pdf
-this writes the generated into a local file and runs ~texi2pdf~ to
-convert it to a pdf.
 
 #+begin_src elisp :results silent :tangle mathsheet.el
   (defun ianxm/gen-worksheet (problems)

Reply via email to