Function: nnvirtual-map-article

nnvirtual-map-article is a byte-compiled function defined in nnvirtual.el.gz.

Signature

(nnvirtual-map-article ARTICLE)

Documentation

Return the component group and article corresponding to virtual ARTICLE.

Value is a cons of the component group and article corresponding to the given virtual ARTICLE.

Source Code

;; Defined in /usr/src/emacs/lisp/gnus/nnvirtual.el.gz
;; We map between virtual articles and real articles in a manner
;; which keeps the size of the virtual active list the same as the
;; sum of the component active lists.

;; To achieve fair mixing of the groups, the last article in each of
;; N component groups will be in the last N articles in the virtual
;; group.

;; If you have 3 components A, B and C, with articles 1-8, 1-5, and
;; 6-7 respectively, then the virtual article numbers look like:
;;
;;  1  2  3  4  5  6  7  8  9  10 11 12 13 14 15
;;  A1 A2 A3 A4 B1 A5 B2 A6 B3 A7 B4 C6 A8 B5 C7

;; To compute these mappings we generate a couple tables and then
;; do some fast operations on them.  Tables for the example above:
;;
;; Offsets - [(A 0) (B -3) (C -1)]
;;
;;               a  b  c  d  e
;; Mapping - ([  3  0  1  3  0 ]
;;            [  6  3  2  9  3 ]
;;            [  8  6  3 15  9 ])
;;
;; (note column 'e' is different in real algorithm, which is slightly
;;  different than described here, but this gives you the methodology.)
;;
;; The basic idea is this, when going from component->virtual, apply
;; the appropriate offset to the article number.  Then search the first
;; column of the table for a row where 'a' is less than or equal to the
;; modified number.  You can see that only group A can therefore go to
;; the first row, groups A and B to the second, and all to the last.
;; The third column of the table is telling us the number of groups
;; which might be able to reach that row (it might increase by more than
;; 1 if several groups have the same size).
;; Then column 'b' provides an additional offset you apply when you have
;; found the correct row.  You then multiply by 'c' and add on the groups
;; _position_ in the offset table.  The basic idea here is that on
;; any given row we are going to map back and forth using X'=X*c+Y and
;; X=(X'/c), Y=(X' mod c).  Then once you've done this transformation,
;; you apply a final offset from column 'e' to give the virtual article.
;;
;; Going the other direction, you instead search on column 'd' instead
;; of 'a', and apply everything in reverse order.

;; Convert component -> virtual:
;; set num = num - Offset(group)
;; find first row in Mapping where num <= 'a'
;; num = (num-'b')*c + Position(group) + 'e'

;; Convert virtual -> component:
;; find first row in Mapping where num <= 'd'
;; num = num - 'e'
;; group_pos = num mod 'c'
;; num = (num / 'c') + 'b' + Offset(group_pos)

;; Easy no? :)
;;
;; Well actually, you need to keep column e offset smaller by the 'c'
;; column for that line, and always add 1 more when going from
;; component -> virtual.  Otherwise you run into a problem with
;; unique reverse mapping.

(defun nnvirtual-map-article (article)
  "Return the component group and article corresponding to virtual ARTICLE.
Value is a cons of the component group and article corresponding to the given
virtual ARTICLE."
  (let ((table nnvirtual-mapping-table)
	entry group-pos)
    (while (and table
		(> article (aref (car table) 3)))
      (setq table (cdr table)))
    (when (and table
	       (> article 0))
      (setq entry (car table))
      (setq article (- article (aref entry 4) 1))
      (setq group-pos (mod article (aref entry 2)))
      (cons (car (aref nnvirtual-mapping-offsets group-pos))
	    (+ (/ article (aref entry 2))
	       (aref entry 1)
	       (cdr (aref nnvirtual-mapping-offsets group-pos)))
	    ))
    ))