;;; org-drill.el - Self-testing using spaced repetition

;;; Author: Paul Sexton <eeeickythump@gmail.com>

;;; Repository at http://bitbucket.org/eeeickythump/org-drill/

+(defcustom org-drill-sm5-initial-interval

+ "In the SM5 algorithm, the initial interval after the first

+successful presentation of an item is always 4 days. If you wish to change

+this, you can do so here."

(defcustom org-drill-add-random-noise-to-intervals-p

"If true, the number of days until an item's next repetition

+(defcustom org-drill-cloze-text-weight

+ "For card types 'hide1_firstmore', 'show1_lastmore' and 'show1_firstless',

+this number determines how often the 'less favoured' situation

+should arise. It will occur 1 in every N trials, where N is the

+For example, with the hide1_firstmore card type, the first piece

+of clozed text should be hidden more often than the other

+pieces. If this variable is set to 4 (default), the first item

+will only be shown 25% of the time (1 in 4 trials). Similarly for

+show1_lastmore, the last item will be shown 75% of the time, and

+for show1_firstless, the first item would only be shown 25% of the

+If the value of this variable is NIL, then weighting is disabled, and

+all weighted card types are treated as their unweighted equivalents."

+ :type '(choice integer (const nil)))

(defcustom org-drill-cram-hours

"When in cram mode, items are considered due for review if

(put 'org-drill-hide-item-headings-p 'safe-local-variable 'booleanp)

(put 'org-drill-spaced-repetition-algorithm 'safe-local-variable

'(lambda (val) (memq val '(simple8 sm5 sm2))))

+(put 'org-drill-sm5-initial-interval 'safe-local-variable 'floatp)

(put 'org-drill-add-random-noise-to-intervals-p 'safe-local-variable 'booleanp)

(put 'org-drill-adjust-intervals-for-early-and-late-repetitions-p

'safe-local-variable 'booleanp)

(put 'org-drill-scope 'safe-local-variable

'(lambda (val) (or (symbolp val) (listp val))))

(put 'org-drill-save-buffers-after-drill-sessions-p 'safe-local-variable 'booleanp)

+(put 'org-drill-cloze-text-weight 'safe-local-variable

+ '(lambda (val) (or (null val) (integerp val))))

;;;; Utilities ================================================================

;;; SM5 Algorithm =============================================================

+(defun initial-optimal-factor-sm5 (n ef)

+ org-drill-sm5-initial-interval

+(defun get-optimal-factor-sm5 (n ef of-matrix)

+ (let ((factors (assoc n of-matrix)))

+ (let ((ef-of (assoc ef (cdr factors))))

+ (and ef-of (cdr ef-of))))

+ (initial-optimal-factor-sm5 n ef))))

(defun inter-repetition-interval-sm5 (last-interval n ef &optional of-matrix)

- (let ((of (get-optimal-factor n ef (or of-matrix

- org-drill-optimal-factor-matrix))))

+ (let ((of (get-optimal-factor-sm5 n ef (or of-matrix

+ org-drill-optimal-factor-matrix))))

(let ((next-ef (modify-e-factor ef quality))

- (new-of (modify-of (get-optimal-factor n ef of-matrix)

+ (new-of (modify-of (get-optimal-factor-sm5 n ef of-matrix)

quality org-drill-learn-fraction))

(when (and org-drill-adjust-intervals-for-early-and-late-repetitions-p

delta-days (minusp delta-days))

(setq new-of (org-drill-early-interval-factor

- (get-optimal-factor n ef of-matrix)

+ (get-optimal-factor-sm5 n ef of-matrix)

(inter-repetition-interval-sm5

last-interval n ef of-matrix)

(set-optimal-factor n next-ef of-matrix

- (round-float new-of 3)))~~ ~~ ; round OF to 3 d.p.

+ (round-float new-of 3))) ; round OF to 3 d.p.

((<= quality org-drill-failure-quality)

(list -1 1 old-ef (1+ failures) meanq (1+ total-repeats)

of-matrix)) ; Not clear if OF matrix is supposed to be

;; For a zero-based quality of 4 or 5, don't repeat

;; (not org-learn-always-reschedule))

(setq next-interval days-ahead))

- (org-drill-store-item-data next-interval repetitions failures

- total-repeats meanq ease)

(if (and (null days-ahead)

(numberp weight) (plusp weight)

(not (minusp next-interval)))

- (setq next-interval (max 1.0 (/ next-interval weight))))

+ (max 1.0 (+ last-interval

+ (/ (- next-interval last-interval) weight)))))

+ (org-drill-store-item-data next-interval repetitions failures

+ total-repeats meanq ease)

(if (eql 'sm5 org-drill-spaced-repetition-algorithm)

(setq org-drill-optimal-factor-matrix new-ofmatrix))

((not (plusp next-interval))

((and (numberp weight) (plusp weight))

- (max 1.0 (/ next-interval weight)))

+ (max 1.0 (/ (- next-interval last-interval) weight))))

(defun org-drill-present-multicloze-hide1-firstmore ()

- "Three out of every four repetitions, hides the FIRST piece of

-text that is marked for cloze deletion. One out of every four

-repetitions, hide one of the other pieces of text, chosen at

+ "Commonly, hides the FIRST piece of text that is marked for

+cloze deletion. Uncommonly, hide one of the other pieces of text,

+The definitions of 'commonly' and 'uncommonly' are determined by

+the value of `org-drill-cloze-text-weight'."

;; The 'firstmore' and 'lastmore' functions used to randomly choose whether

;; to hide the 'favoured' piece of text. However even when the chance of

;; hiding it was set quite high (80%), the outcome was too unpredictable over

;; the small number of repetitions where most learning takes place for each

;; item. In other words, the actual frequency during the first 10 repetitions

;; was often very different from 80%. Hence we use modulo instead.

- (if (zerop (mod (1+ (org-drill-entry-total-repeats 0)) 4))

- ;; 25% of time, hide any item except the first

- (org-drill-present-multicloze-hide-n 1 t)

- ;; 75% of time, hide first item

- (org-drill-present-multicloze-hide-first)))

+ ((null org-drill-cloze-text-weight)

+ ;; Behave as hide1cloze

+ (org-drill-present-multicloze-hide1))

+ ((not (and (integerp org-drill-cloze-text-weight)

+ (plusp org-drill-cloze-text-weight)))

+ (error "Illegal value for org-drill-cloze-text-weight: %S"

+ org-drill-cloze-text-weight))

+ ((zerop (mod (1+ (org-drill-entry-total-repeats 0))

+ org-drill-cloze-text-weight))

+ ;; Uncommonly, hide any item except the first

+ (org-drill-present-multicloze-hide-n 1 t))

+ ;; Commonly, hide first item

+ (org-drill-present-multicloze-hide-first))))

(defun org-drill-present-multicloze-show1-lastmore ()

- "Three out of every four repetitions, hides all pieces except

-the last. One out of every four repetitions, shows any random

-piece. The effect is similar to 'show1cloze' except that the last

-item is much less likely to be the item that is visible."

- (if (zerop (mod (1+ (org-drill-entry-total-repeats 0)) 4))

- ;; 25% of time, show any item except the last

- (org-drill-present-multicloze-hide-n -1 nil nil t)

- ;; 75% of time, show the LAST item

- (org-drill-present-multicloze-hide-n -1 nil t)))

+ "Commonly, hides all pieces except the last. Uncommonly, shows

+any random piece. The effect is similar to 'show1cloze' except

+that the last item is much less likely to be the item that is

+The definitions of 'commonly' and 'uncommonly' are determined by

+the value of `org-drill-cloze-text-weight'."

+ ((null org-drill-cloze-text-weight)

+ ;; Behave as show1cloze

+ (org-drill-present-multicloze-show1))

+ ((not (and (integerp org-drill-cloze-text-weight)

+ (plusp org-drill-cloze-text-weight)))

+ (error "Illegal value for org-drill-cloze-text-weight: %S"

+ org-drill-cloze-text-weight))

+ ((zerop (mod (1+ (org-drill-entry-total-repeats 0))

+ org-drill-cloze-text-weight))

+ ;; Uncommonly, show any item except the last

+ (org-drill-present-multicloze-hide-n -1 nil nil t))

+ ;; Commonly, show the LAST item

+ (org-drill-present-multicloze-hide-n -1 nil t))))

(defun org-drill-present-multicloze-show1-firstless ()

- "Three out of every four repetitions, hides all pieces except

-one, where the shown piece is guaranteed NOT to be the first

-piece. One out of every four repetitions, shows any random

-piece. The effect is similar to 'show1cloze' except that the

-first item is much less likely to be the item that is visible."

- (if (zerop (mod (1+ (org-drill-entry-total-repeats 0)) 4))

- ;; 25% of time, show the first item

- (org-drill-present-multicloze-hide-n -1 t)

- ;; 75% of time, show any item, except the first

- (org-drill-present-multicloze-hide-n -1 nil nil t)))

+ "Commonly, hides all pieces except one, where the shown piece

+is guaranteed NOT to be the first piece. Uncommonly, shows any

+random piece. The effect is similar to 'show1cloze' except that

+the first item is much less likely to be the item that is

+The definitions of 'commonly' and 'uncommonly' are determined by

+the value of `org-drill-cloze-text-weight'."

+ ((null org-drill-cloze-text-weight)

+ ;; Behave as show1cloze

+ (org-drill-present-multicloze-show1))

+ ((not (and (integerp org-drill-cloze-text-weight)

+ (plusp org-drill-cloze-text-weight)))

+ (error "Illegal value for org-drill-cloze-text-weight: %S"

+ org-drill-cloze-text-weight))

+ ((zerop (mod (1+ (org-drill-entry-total-repeats 0))

+ org-drill-cloze-text-weight))

+ ;; Uncommonly, show the first item

+ (org-drill-present-multicloze-hide-n -1 t))

+ ;; Commonly, show any item, except the first

+ (org-drill-present-multicloze-hide-n -1 nil nil t))))

(defun org-drill-present-multicloze-show1 ()

(org-drill-save-optimal-factor-matrix))

(if org-drill-save-buffers-after-drill-sessions-p

+ (message "Drill session finished!")