anchor.diff:               (:conc-name cr-)
                (:constructor make-conjugation-rule (pos conj neg fml onum stem okuri euphr euphk)))
     pos conj neg fml onum stem okuri euphr euphk)
  +
   
   (csv-hash *conj-rules* ("conjo.csv" :skip-first t)
             ((pos-id conj-id neg fml onum stem okuri euphr euphk pos2)
  
positive.code:   (!foreign 'sense 'sense-id 'id))
  
  (defclass conjugation ()
    ((seq :reader seq :col-type integer :initarg :seq)
     (from :reader seq-from :col-type integer :initarg :from)
     (pos :reader pos :col-type string :initarg :pos)
     )
    (:documentation "conjugation link")
    (:metaclass dao-class)
    (:keys seq))
  
  (deftable conjugation
    (!dao-def)
    (!index 'from)
    (!index 'pos)
    (!foreign 'entry 'seq)
    (!foreign 'entry 'from 'seq))
  
  (defclass conj-prop ()
    ((id :reader id :col-type serial)
     (seq :reader seq :col-type integer :initarg :seq)
     (neg :reader conj-neg :col-type boolean :initarg :neg)
     (fml :reader conj-fml :col-type boolean :initarg :fml))
    (:metaclass dao-class)
    (:keys id))
  
  (deftable conj-prop
    (!dao-def)
    (!index 'seq)
    (!foreign 'entry 'seq))
  
  
  (defmacro csv-hash (hash-name (filename &key skip-first) loader-opts &rest accessor-opts-list)
    (let ((base-name (string-trim "*" hash-name))
  
negative.code:     (loop for rule in rules
           collect (cons rule (construct-conjugation word rule)))))
  
  (defparameter *do-not-conjugate* '("n" "vs"))
  
  (defun conjugate-entry-inner (seq)
    (let ((posi (query (:select 'text :distinct :from 'sense-prop
                                :where (:and (:= 'tag "pos") (:= 'seq seq))) :column)))
      (loop with conj-matrix = (make-hash-table :test 'equal) ;; (cons pos-id conj-id) -> 2x2 array
         for pos in posi
         for pos-id = (get-pos-index pos)
         for rules = (get-conj-rules pos-id)
         if (and rules (not (member pos *do-not-conjugate*)))
           do (loop 
                 for (reading ord kanji-flag) in (query (:union (:select 'text 'ord 1 :from 'kanji-text :where (:= 'seq seq))
                                                                (:select 'text 'ord 0 :from 'kana-text :where (:= 'seq seq))))
                 do (loop for rule in rules
                         for conj-id = (cr-conj rule)
                         for key = (list pos-id conj-id)
                         for conj-text = (construct-conjugation reading rule)
                       do (unless (gethash key conj-matrix)
                            (setf (gethash key conj-matrix)
                                  (make-array '(2 2) :initial-element nil)))
                         (push (list conj-text kanji-flag ord (cr-onum rule))
                               (aref (gethash key conj-matrix)
                                     (if (cr-neg rule) 1 0)
                                     (if (cr-fml rule) 1 0)))))
           finally (return conj-matrix))))
  
  (defun conjugate-entry-outer (seq)
    (let* ((conj-matrix (conjugate-entry-inner seq))
           (max-seq (query (:select (:max 'seq) :from 'entry) :single))
           (next-seq (1+ max-seq)))
      (loop for (pos-id conj-id) being the hash-key of conj-matrix using (hash-value matrix)
         for ignore-neg = (not (or (aref matrix 1 0) (aref matrix 1 1)))
         for ignore-fml = (not (or (aref matrix 0 1) (aref matrix 1 1)))
         for pos = (get-pos pos-id)
           do (loop for ii from 0 below 4
                 for neg = (>= ii 2)
                 for fml = (oddp ii)
                 for readings = (row-major-aref matrix ii)
                 when readings
                 do (when (insert-conjugation readings :seq next-seq
                                              :from seq :pos pos
                                              :conj-id conj-id
                                              :neg (if ignore-neg :null neg)
                                              :fml (if ignore-fml :null fml))
                      (incf next-seq))))))
  
  (defun lex-compare (predicate)
    "Only can sort sequences of equal length"
    (lambda (seq1 seq2)
      (block nil
        (map nil 
             (lambda (e1 e2) 
               (cond ((funcall predicate e1 e2) (return t))
                     ((funcall predicate e2 e1) (return nil))))
             seq1 seq2))))
  
  (defun insert-conjugation (readings &key seq from pos conj-id neg fml)
    "returns true if new entry is created, nil otherwise"
    (loop for (reading kanji-flag) in (sort readings (lex-compare #'<) :key #'cddr)
       if (= kanji-flag 1) collect reading into kanji-readings
       else collect reading into kana-readings
       finally
         (let* ((kanji-readings (remove-duplicates kanji-readings :test #'equal))
                (kana-readings (remove-duplicates kana-readings :test #'equal))
                (seq-candidates
                 (if kanji-readings
                     (query (:intersect 
                             (:select 'seq :from 'kanji-text
                                      :where (:in 'text (:set kanji-readings))
                                      :group-by 'seq
                                      :having (:= (:count 'id) (length kanji-readings)))
                             (:select 'seq :from 'kana-text
                                   :where (:in 'text (:set kana-readings))
                                   :group-by 'seq
                                   :having (:= (:count 'id) (length kana-readings))))
                            :column)
                     (query (:select 'r.seq
                                     :from (:as 'kana-text 'r)
                                     :left-join (:as 'kanji-text 'k) :on (:= 'r.seq 'k.seq)
                                     :where (:and (:is-null 'k.text)
                                                  (:in 'r.text (:set kana-readings)))
                                     :group-by 'r.seq
                                     :having (:= (:count 'r.id) (length kana-readings)))
                            :column))))
           (when (member from seq-candidates)
             (return nil))
           (if seq-candidates
               (setf seq (car seq-candidates))
               (progn
                 (make-dao 'entry :seq seq :content "")
                 (loop for kr in kanji-readings
                    for ord from 0
                    do (make-dao 'kanji-text :seq seq :text kr :ord ord :common :null))
                 (loop for kr in kana-readings
                    for ord from 0
                    do (make-dao 'kana-text :seq seq :text kr :ord ord :common :null))))
           
           (let* ((old-conj (select-dao 'conjugation (:and (:= 'from from) (:= 'seq seq))))
                  (conj (or (car old-conj) (make-dao 'conjugation :seq seq :from from))))
             (make-dao 'conj-prop :conj-id (id conj) :conj-type conj-id :pos pos :neg neg :fml fml))
           (return (not seq-candidates)))))
  
  (defparameter *pos-with-conj-rules*
   '("adj-i" "adj-na" "adj-ix" "cop-da" "v1" "v1-s" "v5aru" 
     "v5b" "v5g" "v5k" "v5k-s" "v5m" "v5n" "v5r" "v5r-i" "v5s"
     "v5t" "v5u" "v5u-s" "vk" "vs-s" "vs-i"))
  
  (defun load-conjugations ()
    (with-connection *connection*
      (let ((seqs (query (:select 'seq :distinct :from 'sense-prop
                                  :where (:and (:= 'tag "pos")
                                               (:in 'text (:set *pos-with-conj-rules*))))
                         :column)))
        (loop for cnt from 1
             for seq in seqs
             do (conjugate-entry-outer seq)
             if (zerop (mod cnt 500)) do (format t "~a entries processed~%" cnt)))))
  
  
  ;;; end conjugations
  
  (defun find-word (word)
    (select-dao 
  