(U+6556): Use `->denotational' and `->subsumptive'.
[chise/xemacs-chise.git.1] / lisp / utf-2000 / ideograph-util.el
1 ;;; ideograph-util.el --- Ideographic Character Database utility
2
3 ;; Copyright (C) 1999,2000,2001,2002,2003,2004,2005 MORIOKA Tomohiko.
4
5 ;; Author: MORIOKA Tomohiko <tomo@kanji.zinbun.kyoto-u.ac.jp>
6 ;; Keywords: CHISE, Chaon model, ISO/IEC 10646, Unicode, UCS-4, MULE.
7
8 ;; This file is part of XEmacs CHISE.
9
10 ;; XEmacs CHISE is free software; you can redistribute it and/or
11 ;; modify it under the terms of the GNU General Public License as
12 ;; published by the Free Software Foundation; either version 2, or (at
13 ;; your option) any later version.
14
15 ;; XEmacs CHISE is distributed in the hope that it will be useful, but
16 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18 ;; General Public License for more details.
19
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with XEmacs CHISE; see the file COPYING.  If not, write to
22 ;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23 ;; Boston, MA 02111-1307, USA.
24
25 ;;; Code:
26
27 (require 'char-db-util)
28
29 ;;;###autoload
30 (defun expand-char-feature-name (feature domain)
31   (if domain
32       (intern (format "%s@%s" feature domain))
33     feature))
34
35 ;;;###autoload
36 (defun map-char-family (function char &optional ignore-sisters)
37   (let ((rest (list char))
38         ret checked)
39     (catch 'tag
40       (while rest
41         (unless (memq (car rest) checked)
42           (if (setq ret (funcall function (car rest)))
43               (throw 'tag ret))
44           (setq checked (cons (car rest) checked)
45                 rest (append rest
46                              (get-char-attribute (car rest) '->subsumptive)
47                              (get-char-attribute (car rest) '->denotational)
48                              (get-char-attribute (car rest) '->identical)))
49           (unless ignore-sisters
50             (setq rest (append rest
51                                (get-char-attribute (car rest) '<-subsumptive)
52                                (get-char-attribute (car rest) '<-denotational)))))
53         (setq rest (cdr rest))))))
54
55 (defun get-char-feature-from-domains (char feature domains
56                                            &optional tester arg
57                                            ignore-sisters)
58   (map-char-family
59    (lambda (ch)
60      (let (ret)
61        (catch 'tag
62          (dolist (domain domains)
63            (if (and (or (null tester)
64                         (equal (or (char-feature
65                                     ch (expand-char-feature-name
66                                         tester domain))
67                                    (char-feature ch tester))
68                                arg))
69                     (setq ret (or (char-feature
70                                    ch (expand-char-feature-name
71                                        feature domain))
72                                   (char-feature ch feature))))
73                (throw 'tag ret))))))
74    char ignore-sisters))
75
76
77 (defvar ideograph-radical-chars-vector
78   (make-vector 215 nil))
79
80 (defun char-ideographic-radical (char &optional radical ignore-sisters)
81   (let (ret)
82     (or (if radical
83             (get-char-feature-from-domains
84              char 'ideographic-radical (cons nil char-db-feature-domains)
85              'ideographic-radical radical ignore-sisters)
86           (get-char-feature-from-domains
87            char 'ideographic-radical (cons nil char-db-feature-domains)
88            ignore-sisters))
89         ;; (catch 'tag
90         ;;   (dolist (domain char-db-feature-domains)
91         ;;     (if (and (setq ret (char-feature
92         ;;                         char
93         ;;                         (intern
94         ;;                          (format "%s@%s"
95         ;;                                  'ideographic-radical domain))))
96         ;;              (or (eq ret radical)
97         ;;                  (null radical)))
98         ;;         (throw 'tag ret))))
99         (catch 'tag
100           (dolist (cell (get-char-attribute char 'ideographic-))
101             (if (and (setq ret (plist-get cell :radical))
102                      (or (eq ret radical)
103                          (null radical)))
104                 (throw 'tag ret))))
105         (get-char-feature-from-domains
106          char 'ideographic-radical (cons nil char-db-feature-domains))
107         ;; (char-feature char 'ideographic-radical)
108         (progn
109           (setq ret
110                 (or (get-char-attribute char 'daikanwa-radical)
111                     (get-char-attribute char 'kangxi-radical)
112                     (get-char-attribute char 'japanese-radical)
113                     (get-char-attribute char 'korean-radical)))
114           (when ret
115             (put-char-attribute char 'ideographic-radical ret)
116             ret)))))
117
118 (defvar ideograph-radical-strokes-vector
119   ;;0  1  2  3  4  5  6  7  8  9
120   [nil 1  1  1  1  1  1  2  2  2
121     2  2  2  2  2  2  2  2  2  2
122     2  2  2  2  2  2  2  2  2  2
123     3  3  3  3  3  3  3  3  3  3
124     3  3  3  3  3  3  3  3  3  3
125     3  3  3  3  3  3  3  3  3  3
126     3  4  4  4  3  4  4  4  4  4
127     4  4  4  4  4  4  4  4  4  4
128     4  4  4  4  4  3  4  4  4  4
129     4  4  4  4  3  5  4  5  5  5
130     ;; 100
131     5  5  5  5  5  5  5  5  5  5
132     5  5  5  5  5  5  5  5  6  6
133     6  6  6  6  6  6  6  6  6  6
134     4  6  6  6  6  6  6  6  6  6
135     4  6  6  6  6  6  6  7  7  7
136     7  7  7  7  7  7  7  7  7  7
137     7  7  4  3  7  7  7  8  7  8
138     3  8  8  8  8  8  9  9  9  9
139     9  9  9  9  8  9  9 10 10 10
140    10 10 10 10 10 11 11 11 11 11
141    ;; 200
142    11 12 12 12 12 13 13 13 13 14
143    14 15 16 16 17])
144
145 ;;;###autoload
146 (defun char-ideographic-strokes-from-domains (char domains &optional radical)
147   (if radical
148       (get-char-feature-from-domains char 'ideographic-strokes domains
149                                      'ideographic-radical radical)
150     (get-char-feature-from-domains char 'ideographic-strokes domains)))
151
152 ;;;###autoload
153 (defun char-ideographic-strokes (char &optional radical preferred-domains)
154   (let (ret)
155     (or (catch 'tag
156           (dolist (cell (get-char-attribute char 'ideographic-))
157             (if (and (setq ret (plist-get cell :radical))
158                      (or (eq ret radical)
159                          (null radical)))
160                 (throw 'tag (plist-get cell :strokes)))))
161         (char-ideographic-strokes-from-domains
162          char (append preferred-domains
163                       (cons nil
164                             char-db-feature-domains))
165          radical)
166         (get-char-attribute char 'daikanwa-strokes)
167         (let ((strokes
168                (or (get-char-attribute char 'kangxi-strokes)
169                    (get-char-attribute char 'japanese-strokes)
170                    (get-char-attribute char 'korean-strokes)
171                    (let ((r (char-ideographic-radical char))
172                          (ts (get-char-attribute char 'total-strokes)))
173                      (if (and r ts)
174                          (- ts (aref ideograph-radical-strokes-vector r))))
175                    )))
176           (when strokes
177             (put-char-attribute char 'ideographic-strokes strokes)
178             strokes)))))
179
180 ;;;###autoload
181 (defun char-total-strokes-from-domains (char domains)
182   (let (ret)
183     (catch 'tag
184       (dolist (domain domains)
185         (if (setq ret (char-feature
186                        char
187                        (intern
188                         (format "%s@%s"
189                                 'total-strokes domain))))
190             (throw 'tag ret))))))
191
192 ;;;###autoload
193 (defun char-total-strokes (char &optional preferred-domains)
194   (or (char-total-strokes-from-domains char preferred-domains)
195       (char-feature char 'total-strokes)
196       (char-total-strokes-from-domains char char-db-feature-domains)))
197
198 ;;;###autoload
199 (defun update-ideograph-radical-table ()
200   (interactive)
201   (let (ret rret radical script dest)
202     (dolist (feature
203              (cons 'ideographic-radical
204                    (progn
205                      (dolist (feature (char-attribute-list))
206                        (if (string-match "^ideographic-radical@[^@*]+$"
207                                          (symbol-name feature))
208                            (setq dest (cons feature dest))))
209                      dest)))
210       (map-char-attribute
211        (lambda (chr radical)
212          (dolist (char (append
213                         (if (setq ret
214                                   (get-char-attribute chr '<-subsumptive))
215                             (progn
216                               (setq dest nil)
217                               (dolist (pc ret)
218                                 (unless (eq (get-char-attribute
219                                              pc 'ideographic-radical)
220                                             radical)
221                                   (if (setq rret
222                                             (get-char-attribute
223                                              pc '<-subsumptive))
224                                       (setq ret (append ret rret))
225                                     (setq dest (cons pc dest)))))
226                               dest)
227                           (list chr))
228                         (let ((rest (append
229                                      (get-char-attribute chr '<-identical)
230                                      (get-char-attribute chr '->denotational)))
231                               pc)
232                           (setq dest nil)
233                           (while rest
234                             (setq pc (car rest))
235                             (if (memq pc dest)
236                                 (setq rest (cdr rest))
237                               (setq dest (cons pc dest))
238                               (setq rest
239                                     (append (cdr rest)
240                                             (get-char-attribute
241                                              pc '<-identical)
242                                             (get-char-attribute
243                                              pc '->denotational)))))
244                           dest)))
245            (when (and radical
246                       (or (eq radical
247                               (or (get-char-attribute
248                                    char 'ideographic-radical)
249                                   (char-ideographic-radical char radical)))
250                           (null (char-ideographic-radical char)))
251                       (or (null (setq script
252                                       (get-char-attribute char 'script)))
253                           (memq 'Ideograph script)))
254              (unless (memq char
255                            (setq ret
256                                  (aref ideograph-radical-chars-vector
257                                        radical)))
258                (char-ideographic-strokes char)
259                (aset ideograph-radical-chars-vector radical
260                      (cons char ret)))))
261          nil)
262        feature))
263     (map-char-attribute
264      (lambda (char data)
265        (dolist (cell data)
266          (setq radical (plist-get cell :radical))
267          (when (and radical
268                     (or (null (setq script (get-char-attribute char 'script)))
269                         (memq 'Ideograph script)))
270            (unless (memq char
271                          (setq ret
272                                (aref ideograph-radical-chars-vector radical)))
273              (char-ideographic-strokes char)
274              (aset ideograph-radical-chars-vector radical
275                    (cons char ret))))))
276      'ideographic-)))
277
278 (defun int-list< (a b)
279   (if (numberp (car a))
280       (if (numberp (car b))
281           (if (= (car a) (car b))
282               (int-list< (cdr a)(cdr b))
283             (< (car a) (car b)))
284         nil)
285     (numberp (car b))))
286
287 (defun morohashi-daikanwa< (a b)
288   (if (integerp a)
289       (setq a (list a)))
290   (if (integerp b)
291       (setq b (list b)))
292   (cond ((eq (car a) 'ho)
293          (if (eq (car b) 'ho)
294              (int-list< (cdr a)(cdr b))
295            nil))
296         ((numberp (car a))
297          (if (eq (car b) 'ho)
298              t
299            (int-list< a b)))
300         (t
301          (if (eq (car b) 'ho)
302              t
303            (int-list< a b)))))
304
305 ;; (defun nil=-int< (a b)
306 ;;   (cond ((null a) nil)
307 ;;         ((null b) nil)
308 ;;         (t (< a b))))
309
310 ;; (defun nil>-int< (a b)
311 ;;   (cond ((null a) nil)
312 ;;         ((null b) t)
313 ;;         (t (< a b))))
314
315 (defvar ideographic-radical nil)
316
317 ;;;###autoload
318 (defun char-representative-of-daikanwa (char &optional radical
319                                              ignore-default checked)
320   (unless radical
321     (setq radical ideographic-radical))
322   (if (or (null radical)
323           (eq (or (get-char-attribute char 'ideographic-radical)
324                   (char-ideographic-radical char radical t))
325               radical))
326       (let ((ret (or (encode-char char 'ideograph-daikanwa 'defined-only)
327                      (encode-char char '=daikanwa-rev2 'defined-only))))
328         (or (and ret char)
329             (if (setq ret (get-char-attribute char 'morohashi-daikanwa))
330                 (let ((m-m (car ret))
331                       (m-s (nth 1 ret))
332                       pat)
333                   (if (= m-s 0)
334                       (or (decode-char '=daikanwa-rev2 m-m 'defined-only)
335                           (decode-char 'ideograph-daikanwa m-m))
336                     (setq pat (list m-m m-s))
337                     (map-char-attribute (lambda (c v)
338                                           (if (equal pat v)
339                                               c))
340                                         'morohashi-daikanwa))))
341             (and (setq ret (get-char-attribute char '=>daikanwa))
342                  (if (numberp ret)
343                      (or (decode-char '=daikanwa-rev2 ret 'defined-only)
344                          (decode-char 'ideograph-daikanwa ret))
345                    (map-char-attribute (lambda (c v)
346                                          (if (equal ret v)
347                                              char))
348                                        'morohashi-daikanwa)))
349             (unless (memq char checked)
350               (catch 'tag
351                 (let ((rest
352                        (append (get-char-attribute char '->subsumptive)
353                                (get-char-attribute char '->denotational)))
354                       (i 0)
355                       sc)
356                   (setq checked (cons char checked))
357                   (while rest
358                     (setq sc (car rest))
359                     (if (setq ret (char-representative-of-daikanwa
360                                    sc radical t checked))
361                         (throw 'tag ret))
362                     (setq checked (cons sc checked)
363                           rest (cdr rest)
364                           i (1+ i)))
365                   (setq rest (get-char-attribute char '->identical))
366                   (while rest
367                     (setq sc (car rest))
368                     (when (setq ret (char-representative-of-daikanwa
369                                      sc radical t checked))
370                       (throw 'tag ret))
371                     (setq checked (cons sc checked)
372                           rest (cdr rest)))
373                   (setq rest
374                         (append (get-char-attribute char '<-subsumptive)
375                                 (get-char-attribute char '<-denotational)))
376                   (while rest
377                     (setq sc (car rest))
378                     (when (setq ret (char-representative-of-daikanwa
379                                      sc radical t checked))
380                       (throw 'tag ret))
381                     (setq checked (cons sc checked)
382                           rest (cdr rest))))))
383             (unless ignore-default
384               char)))))
385
386 (defun char-attributes-poly< (c1 c2 accessors testers defaulters)
387   (catch 'tag
388     (let (a1 a2 accessor tester dm)
389       (while (and accessors testers)
390         (setq accessor (car accessors)
391               tester (car testers)
392               dm (car defaulters))
393         (when (and accessor tester)
394           (setq a1 (funcall accessor c1)
395                 a2 (funcall accessor c2))
396           (cond ((null a1)
397                  (if a2
398                      (cond ((eq dm '<)
399                             (throw 'tag t))
400                            ((eq dm '>)
401                             (throw 'tag nil)))))
402                 ((null a2)
403                  (cond ((eq dm '<)
404                         (throw 'tag nil))
405                        ((eq dm '>)
406                         (throw 'tag t))))
407                 (t
408                  (cond ((funcall tester a1 a2)
409                         (throw 'tag t))
410                        ((funcall tester a2 a1)
411                         (throw 'tag nil))))))
412         (setq accessors (cdr accessors)
413               testers (cdr testers)
414               defaulters (cdr defaulters))))))
415
416 (defun char-daikanwa-strokes (char &optional radical)
417   (unless radical
418     (setq radical ideographic-radical))
419   (let ((drc (char-representative-of-daikanwa char radical))
420         (r (char-ideographic-radical char radical)))
421     (if (or (null r)
422             (= (char-ideographic-radical drc radical) r))
423         (setq char drc)))
424   (char-ideographic-strokes char radical '(daikanwa)))
425
426 ;;;###autoload
427 (defun char-daikanwa (char &optional radical checked)
428   (unless radical
429     (setq radical ideographic-radical))
430   (if (or (null radical)
431           (eq (or (get-char-attribute char 'ideographic-radical)
432                   (char-ideographic-radical char radical t))
433               radical))
434       (let ((ret (or (encode-char char 'ideograph-daikanwa 'defined-only)
435                      (encode-char char '=daikanwa-rev2 'defined-only)
436                      (get-char-attribute char 'morohashi-daikanwa))))
437         (or ret
438             (and (setq ret (get-char-attribute char '=>daikanwa))
439                  (if (numberp ret)
440                      (list ret 0)
441                    (append ret '(0))))
442             (unless (memq char checked)
443               (catch 'tag
444                 (let ((rest
445                        (append (get-char-attribute char '->subsumptive)
446                                (get-char-attribute char '->denotational)))
447                       (i 0)
448                       sc)
449                   (setq checked (cons char checked))
450                   (while rest
451                     (setq sc (car rest))
452                     (if (setq ret (char-daikanwa sc radical checked))
453                         (throw 'tag ret))
454                     (setq checked (cons sc checked)
455                           rest (cdr rest)
456                           i (1+ i)))
457                   (setq rest (get-char-attribute char '->identical))
458                   (while rest
459                     (setq sc (car rest))
460                     (when (setq ret (char-daikanwa sc radical checked))
461                       (throw 'tag
462                              (if (numberp ret)
463                                  (list ret 0)
464                                (append ret (list i)))))
465                     (setq checked (cons sc checked)
466                           rest (cdr rest)))
467                   (setq rest
468                         (append (get-char-attribute char '<-subsumptive)
469                                 (get-char-attribute char '<-denotational)))
470                   (while rest
471                     (setq sc (car rest))
472                     (when (setq ret (char-daikanwa sc radical checked))
473                       (throw 'tag
474                              (if (numberp ret)
475                                  (list ret 0 i)
476                                (append ret (list i)))))
477                     (setq checked (cons sc checked)
478                           rest (cdr rest))))))))))
479
480 ;;;###autoload
481 (defun char-ucs (char)
482   (or (encode-char char '=ucs 'defined-only)
483       (char-feature char '=>ucs)))
484
485 (defun char-id (char)
486   (logand (char-int char) #x3FFFFFFF))
487
488 (defun ideograph-char< (a b &optional radical)
489   (let ((ideographic-radical (or radical
490                                  ideographic-radical)))
491     (char-attributes-poly<
492      a b
493      '(char-daikanwa-strokes char-daikanwa char-ucs char-id)
494      '(< morohashi-daikanwa< < <)
495      '(> > > >))))
496
497 (defun insert-ideograph-radical-char-data (radical)
498   (let ((chars
499          (sort (copy-list (aref ideograph-radical-chars-vector radical))
500                (lambda (a b)
501                  (ideograph-char< a b radical))))
502         attributes ; ccss
503         )
504     (dolist (name (char-attribute-list))
505       (unless (memq name char-db-ignored-attributes)
506         ;; (if (find-charset name)
507         ;;     (push name ccss)
508         (push name attributes)
509         ;; )
510         ))
511     (setq attributes (sort attributes #'char-attribute-name<)
512           ;; ccss (sort ccss #'char-attribute-name<)
513           )
514     (aset ideograph-radical-chars-vector radical chars)
515     (dolist (char chars)
516       (when ;;(or
517           (not (some (lambda (atr)
518                        (get-char-attribute char atr))
519                      char-db-ignored-attributes))
520         ;; (some (lambda (ccs)
521         ;;         (encode-char char ccs 'defined-only))
522         ;;       ccss)
523         ;;)
524         (insert-char-data char nil attributes ;ccss
525                           )))))
526
527 (defun write-ideograph-radical-char-data (radical file)
528   (if (file-directory-p file)
529       (let ((name (char-feature (decode-char 'ucs (+ #x2EFF radical))
530                                 'name)))
531         (if (string-match "KANGXI RADICAL " name)
532             (setq name (capitalize (substring name (match-end 0)))))
533         (setq name (mapconcat (lambda (char)
534                                 (if (eq char ? )
535                                     "-"
536                                   (char-to-string char))) name ""))
537         (setq file
538               (expand-file-name
539                (format "Ideograph-R%03d-%s.el" radical name)
540                file))))
541   (with-temp-buffer
542     (insert (format ";; -*- coding: %s -*-\n"
543                     char-db-file-coding-system))
544     (insert-ideograph-radical-char-data radical)
545     (let ((coding-system-for-write char-db-file-coding-system))
546       (write-region (point-min)(point-max) file))))
547
548 (defun ideographic-structure= (char1 char2)
549   (if (char-ref-p char1)
550       (setq char1 (plist-get char1 :char)))
551   (if (char-ref-p char2)
552       (setq char2 (plist-get char2 :char)))
553   (let ((s1 (if (characterp char1)
554                 (get-char-attribute char1 'ideographic-structure)
555               (cdr (assq 'ideographic-structure char1))))
556         (s2 (if (characterp char2)
557                 (get-char-attribute char2 'ideographic-structure)
558               (cdr (assq 'ideographic-structure char2))))
559         e1 e2)
560     (if (or (null s1)(null s2))
561         (char-spec= char1 char2)
562       (catch 'tag
563         (while (and s1 s2)
564           (setq e1 (car s1)
565                 e2 (car s2))
566           (unless (ideographic-structure= e1 e2)
567             (throw 'tag nil))
568           (setq s1 (cdr s1)
569                 s2 (cdr s2)))
570         (and (null s1)(null s2))))))
571
572 ;;;###autoload
573 (defun ideographic-structure-find-char (structure)
574   (let (rest)
575     (map-char-attribute (lambda (char value)
576                           (setq rest structure)
577                           (catch 'tag
578                             (while (and rest value)
579                               (unless (ideographic-structure=
580                                        (car rest)(car value))
581                                 (throw 'tag nil))
582                               (setq rest (cdr rest)
583                                     value (cdr value)))
584                             (unless (or rest value)
585                               char)))
586                         'ideographic-structure)))
587
588 ;;;###autoload
589 (defun chise-string< (string1 string2 accessors)
590   (let ((len1 (length string1))
591         (len2 (length string2))
592         len
593         (i 0)
594         c1 c2
595         rest func
596         v1 v2)
597     (setq len (min len1 len2))
598     (catch 'tag
599       (while (< i len)
600         (setq c1 (aref string1 i)
601               c2 (aref string2 i))
602         (setq rest accessors)
603         (while (and rest
604                     (setq func (car rest))
605                     (setq v1 (funcall func c1)
606                           v2 (funcall func c2))
607                     (eq v1 v2))
608           (setq rest (cdr rest)))
609         (if v1
610             (if v2
611                 (cond ((< v1 v2)
612                        (throw 'tag t))
613                       ((> v1 v2)
614                        (throw 'tag nil)))
615               (throw 'tag nil))
616           (if v2
617               (throw 'tag t)))
618         (setq i (1+ i)))
619       (< len1 len2))))
620
621
622 (provide 'ideograph-util)
623
624 ;;; ideograph-util.el ends here