]> git.pld-linux.org Git - packages/gtk+.git/blob - gtk+-ahiguti.patch
- [Gg]tk+ -> GTK+
[packages/gtk+.git] / gtk+-ahiguti.patch
1 Return-Path: a-higuti@math.sci.hokudai.ac.jp
2 Delivery-Date: Thu Sep 23 14:52:43 1999
3 Return-Path: <a-higuti@math.sci.hokudai.ac.jp>
4 Received: from localhost (IDENT:otaylor@localhost [127.0.0.1])
5         by fresnel.labs.redhat.com (8.9.3/8.9.3) with ESMTP id OAA00891
6         for <otaylor@localhost>; Thu, 23 Sep 1999 14:52:41 -0400
7 Received: from lacrosse.redhat.com
8         by localhost with POP3 (fetchmail-5.0.0)
9         for otaylor@localhost (single-drop); Thu, 23 Sep 1999 14:52:42 -0400 (EDT)
10 Received: from mail.redhat.com (mail.redhat.com [199.183.24.239])
11         by lacrosse.corp.redhat.com (8.9.3/8.9.3) with ESMTP id OAA19205
12         for <otaylor@lacrosse.redhat.com>; Thu, 23 Sep 1999 14:01:27 -0400
13 Received: from math.sci.hokudai.ac.jp (seki.math.sci.hokudai.ac.jp [133.50.152.8])
14         by mail.redhat.com (8.8.7/8.8.7) with ESMTP id OAA13383
15         for <otaylor@redhat.com>; Thu, 23 Sep 1999 14:01:49 -0400
16 Received: from heathcliff (a-higuti@hilbert.math.sci.hokudai.ac.jp [133.50.152.11])
17         by math.sci.hokudai.ac.jp (8.8.8/3.6W01/06/98) with SMTP id DAA23889
18         for <otaylor@redhat.com>; Fri, 24 Sep 1999 03:01:10 +0900 (JST)
19 Date: Fri, 24 Sep 1999 03:01:10 +0900 (JST)
20 Message-Id: <199909231801.DAA23889@math.sci.hokudai.ac.jp>
21 From: a-higuti@math.sci.hokudai.ac.jp (Akira Higuchi)
22 To: otaylor@redhat.com
23 Subject: Adding more gdk_isw* macros
24 Mime-Version: 1.0
25 Content-Type: text/plain; charset=US-ASCII
26 Status:  O
27 Lines: 528
28 Xref: fresnel.labs.redhat.com prog-gtk:648
29
30 Hello Owen,
31
32 I'm working on adding CJK support to gnome apps, and I need your advice.
33
34 As you know, gtk+-1.2 has some support for CJK. It's sufficient for most
35 gnome apps to be internationalized, but some problems remain. The most
36 problem is that there is no way of doing word wrapping for CJK strings.
37
38 For example, gtk-xmhtml shows Japanese text very uglily, because Japanese
39 sentences are recognized as very long words. (a Japanese sentence 
40 contain any spaces in most cases.) The same problem is in gtk+ itself, too;
41 Word wrapping in GtkLabel and GtkText doesn't work correctly for CJK text,
42 because of the same reason as above.
43
44 In order to fix it, we need more gdk_isw* functions than we already have
45 in gdki18n.h. (I regret I didn't add them before gtk+-1.2 was released.)
46 I attach herewith a patch which fixes it, but I think it's not acceptable
47 to adding a new macro to a stable version of gtk+. (is it?)
48
49 The question I want to ask you is: what's the best way of fixing the
50 problem without adding these macros to gdki18n.h? A solution is to add
51 these macros to each *.c files (in GtkLabel, GtkText, and gtkxmhtml etc.)
52 rather than gtki18n.h, but it's very ugly I think. 
53
54 Please don't say "wait until gscript is completed" ;-(  I don't want to
55 wait for a long time. (Please let me know if I can help you with gscript
56 BTW. I've not hacked gscript yet, because it seems to be too early to be
57 hacked by other than you.)
58
59 Thanks,
60 Akira Higuchi
61 ---------------- x8 ---------------- x8 ---------------- x8 ----------------
62 diff -ur gtk+-1.2.5-pre2/gdk/gdki18n.h gtk+-1.2.5-pre2.new/gdk/gdki18n.h
63 --- gtk+-1.2.5-pre2/gdk/gdki18n.h       Wed Jun  9 21:07:51 1999
64 +++ gtk+-1.2.5-pre2.new/gdk/gdki18n.h   Thu Sep 23 14:59:38 1999
65 @@ -50,4 +50,32 @@
66  #  define gdk_iswspace(c) ((wchar_t)(c) <= 0xFF && isspace(c))
67  #endif
68  
69 +/* The following 9 macros are added in gtk+ 1.2.X. Don't use them without
70 + * checking GTK_CHECK_VERSION. For example,
71 + *     #if GTK_CHECK_VERSION (1,2,X)
72 + *         ... code which uses gdk_iswalpha(), gdk_iswcntrl(), etc. ...
73 + *      #endif
74 + */
75 +#if !defined(G_HAVE_BROKEN_WCTYPE) && (defined(G_HAVE_WCTYPE_H) || defined(G_HAVE_WCHAR_H)) && !defined(X_LOCALE)
76 +#  define gdk_iswalpha(c)  iswalpha(c)
77 +#  define gdk_iswcntrl(c)  iswcntrl(c)
78 +#  define gdk_iswdigit(c)  iswdigit(c)
79 +#  define gdk_iswlower(c)  iswlower(c)
80 +#  define gdk_iswgraph(c)  iswgraph(c)
81 +#  define gdk_iswprint(c)  iswprint(c)
82 +#  define gdk_iswpunct(c)  iswpunct(c)
83 +#  define gdk_iswupper(c)  iswupper(c)
84 +#  define gdk_iswxdigit(c) iswxdigit(c)
85 +#else
86 +#  define gdk_iswalpha(c)  ((wchar_t)(c) <= 0xFF && isalpha(c))
87 +#  define gdk_iswcntrl(c)  ((wchar_t)(c) <= 0xFF && iscntrl(c))
88 +#  define gdk_iswdigit(c)  ((wchar_t)(c) <= 0xFF && isdigit(c))
89 +#  define gdk_iswlower(c)  ((wchar_t)(c) <= 0xFF && islower(c))
90 +#  define gdk_iswgraph(c)  ((wchar_t)(c) >  0xFF || isgraph(c))
91 +#  define gdk_iswprint(c)  ((wchar_t)(c) >  0xFF || isprint(c))
92 +#  define gdk_iswpunct(c)  ((wchar_t)(c) <= 0xFF && ispunct(c))
93 +#  define gdk_iswupper(c)  ((wchar_t)(c) <= 0xFF && isupper(c))
94 +#  define gdk_iswxdigit(c) ((wchar_t)(c) <= 0xFF && isxdigit(c))
95 +#endif
96 +
97  #endif /* __GDK_I18N_H__ */
98 diff -ur gtk+-1.2.5-pre2/gtk/gtkentry.c gtk+-1.2.5-pre2.new/gtk/gtkentry.c
99 --- gtk+-1.2.5-pre2/gtk/gtkentry.c      Wed Aug 18 14:23:53 1999
100 +++ gtk+-1.2.5-pre2.new/gtk/gtkentry.c  Thu Sep 23 00:22:21 1999
101 @@ -1943,11 +1943,21 @@
102      gtk_move_backward_word (GTK_ENTRY (editable));
103  }
104  
105 +static gboolean
106 +alnum_or_ideogram (GtkEntry *entry, guint index)
107 +{
108 +  GdkWChar ch;
109 +  ch = entry->text[index];
110 +  if (entry->use_wchar)
111 +    return !(gdk_iswpunct (ch) || gdk_iswcntrl (ch) || gdk_iswspace (ch));
112 +  else
113 +    return !(ispunct (ch) || iscntrl (ch) || isspace (ch));
114 +}
115 +
116  static void
117  gtk_move_forward_word (GtkEntry *entry)
118  {
119    GtkEditable *editable;
120 -  GdkWChar *text;
121    gint i;
122  
123    editable = GTK_EDITABLE (entry);
124 @@ -1961,21 +1971,12 @@
125  
126    if (entry->text && (editable->current_pos < entry->text_length))
127      {
128 -      text = entry->text;
129 -      i = editable->current_pos;
130 -         
131 -      if ((entry->use_wchar) ? (!gdk_iswalnum (text[i])) : (!isalnum (text[i])))
132 -       for (; i < entry->text_length; i++)
133 -         {
134 -           if ((entry->use_wchar) ? gdk_iswalnum (text[i]) : isalnum (text[i]))
135 -             break;
136 -         }
137 -
138 +      for (i = editable->current_pos; i < entry->text_length; i++)
139 +       if (alnum_or_ideogram (entry, i))
140 +         break;
141        for (; i < entry->text_length; i++)
142 -       {
143 -         if ((entry->use_wchar) ? (!gdk_iswalnum (text[i])) : (!isalnum (text[i])))
144 -           break;
145 -       }
146 +       if (!alnum_or_ideogram (entry, i))
147 +         break;
148  
149        editable->current_pos = i;
150      }
151 @@ -1985,7 +1986,6 @@
152  gtk_move_backward_word (GtkEntry *entry)
153  {
154    GtkEditable *editable;
155 -  GdkWChar *text;
156    gint i;
157  
158    editable = GTK_EDITABLE (entry);
159 @@ -1999,26 +1999,19 @@
160  
161    if (entry->text && editable->current_pos > 0)
162      {
163 -      text = entry->text;
164 -      i = editable->current_pos - 1;
165 -      if ((entry->use_wchar) ? (!gdk_iswalnum (text[i])) : (!isalnum (text[i])))
166 -       for (; i >= 0; i--)
167 +      for (i = editable->current_pos - 1; i >= 0; i--)
168 +       if (alnum_or_ideogram (entry, i))
169 +         break;
170 +      for (; i >= 0; i--)
171 +       if (!alnum_or_ideogram (entry, i))
172           {
173 -           if ((entry->use_wchar) ? gdk_iswalnum (text[i]) : isalnum (text[i]))
174 -             break;
175 +           i++;
176 +           break;
177           }
178 -      for (; i >= 0; i--)
179 -       {
180 -         if ((entry->use_wchar) ? (!gdk_iswalnum (text[i])) : (!isalnum (text[i])))
181 -           {
182 -             i++;
183 -             break;
184 -           }
185 -       }
186 -         
187 +
188        if (i < 0)
189         i = 0;
190 -         
191 +  
192        editable->current_pos = i;
193      }
194  }
195 diff -ur gtk+-1.2.5-pre2/gtk/gtklabel.c gtk+-1.2.5-pre2.new/gtk/gtklabel.c
196 --- gtk+-1.2.5-pre2/gtk/gtklabel.c      Wed Jun  9 20:40:13 1999
197 +++ gtk+-1.2.5-pre2.new/gtk/gtklabel.c  Thu Sep 23 11:29:25 1999
198 @@ -56,6 +56,7 @@
199    GtkLabelWord *next;
200    gint uline_y;
201    GtkLabelULine *uline;
202 +  gboolean paragraph_break;
203  };
204  
205  struct _GtkLabelULine
206 @@ -396,6 +397,7 @@
207    word->beginning = NULL;
208    word->next = NULL;
209    word->uline = NULL;
210 +  word->paragraph_break = FALSE;
211  
212    return word;
213  }
214 @@ -441,6 +443,7 @@
215        if (str == label->label_wc || str[-1] == '\n')
216         {
217           /* Paragraph break */
218 +         word->paragraph_break = TRUE;
219           word->space = 0;
220           
221           max_line_width = MAX (line_width, max_line_width);
222 @@ -488,6 +491,7 @@
223      {
224        word = gtk_label_word_alloc ();
225        
226 +      word->paragraph_break = TRUE;
227        word->space = 0;
228        word->beginning = str;
229        word->length = 0;
230 @@ -500,6 +504,13 @@
231    return MAX (line_width, max_line_width);
232  }
233  
234 +static gboolean
235 +is_ideogram (GdkWChar wc)
236 +{
237 +  return !(gdk_iswalnum (wc) || gdk_iswspace (wc) ||
238 +          gdk_iswpunct (wc) || gdk_iswcntrl (wc));
239 +}
240 +
241  /* this needs to handle white space better. */
242  static gint
243  gtk_label_split_text_wrapped (GtkLabel *label)
244 @@ -526,6 +537,7 @@
245        if (str == label->label_wc || str[-1] == '\n')
246         {
247           /* Paragraph break */
248 +         word->paragraph_break = TRUE;
249           word->space = 0;
250           
251           max_line_width = MAX (line_width, max_line_width);
252 @@ -546,24 +558,30 @@
253           else
254             word->space = space_width * nspaces;
255         }
256 -      else
257 +      else if (gdk_iswspace (str[-1]))
258         {
259           /* Regular inter-word space */
260           word->space = space_width;
261         }
262 +      else
263 +       {
264 +         word->space = 0;
265 +       }
266        
267        word->beginning = str;
268        word->length = 0;
269        p = word->beginning;
270        while (*p && !gdk_iswspace (*p))
271         {
272 +         if (word->length > 0 && (is_ideogram (p[-1]) || is_ideogram (*p)))
273 +           break;
274           word->length++;
275           p++;
276         }
277        word->width = gdk_text_width_wc (GTK_WIDGET (label)->style->font, str, word->length);
278        
279        str += word->length;
280 -      if (*str)
281 +      if (*str && gdk_iswspace (*str))
282         str++;
283        
284        line_width += word->space + word->width;
285 @@ -600,7 +618,7 @@
286    width = 0;
287    for (word = label->words; word; word = word->next)
288      {
289 -      if (word->space == 0
290 +      if (word->paragraph_break
291           || (line_width
292               && (line_width >= min_width
293                   || line_width + word->width + word->space > max_width)))
294 @@ -716,7 +734,8 @@
295    GtkLabelWord *word, *line, *next_line;
296    GtkWidget *widget;
297    gchar *ptrn;
298 -  gint x, y, space, extra_width, add_space, baseline_skip;
299 +  gint x, y, space, num_words, extra_width, add_space, baseline_skip;
300 +  gboolean deliver_equivalently;
301    
302    g_return_if_fail (label->wrap);
303    
304 @@ -724,20 +743,24 @@
305    y = 0;
306    baseline_skip = (GTK_WIDGET (label)->style->font->ascent +
307                    GTK_WIDGET (label)->style->font->descent + 1);
308 +  deliver_equivalently = FALSE;
309    
310    for (line = label->words; line != 0; line = next_line)
311      {
312 -      space = 0;
313 +      space = num_words = 0;
314        extra_width = max_line_width - line->width;
315        
316        for (next_line = line->next; next_line; next_line = next_line->next)
317         {
318 -         if (next_line->space == 0)
319 +         if (next_line->paragraph_break)
320             break;              /* New paragraph */
321           if (next_line->space + next_line->width > extra_width)
322             break;
323 +         if (next_line->space == 0)
324 +           deliver_equivalently = TRUE; /* An ideogram is found. */
325           extra_width -= next_line->space + next_line->width;
326           space += next_line->space;
327 +         num_words++;
328         }
329        
330        line->x = 0;
331 @@ -747,14 +770,18 @@
332        
333        for (word = line->next; word != next_line; word = word->next)
334         {
335 -         if (next_line && next_line->space)
336 +         if (next_line && !next_line->paragraph_break &&
337 +             label->jtype == GTK_JUSTIFY_FILL &&
338 +             (deliver_equivalently ? num_words : space) > 0)
339             {
340 -             /* Not last line of paragraph --- fill line if needed */
341 -             if (label->jtype == GTK_JUSTIFY_FILL) {
342 +             /* Not last line of paragraph --- fill line */
343 +             if (deliver_equivalently)
344 +               add_space = (extra_width + num_words / 2) / num_words;
345 +             else
346                 add_space = (extra_width * word->space + space / 2) / space;
347 -               extra_width -= add_space;
348 -               space -= word->space;
349 -             }
350 +             extra_width -= add_space;
351 +             space -= word->space;
352 +             num_words--;
353             }
354           
355           word->x = x + word->space + add_space;
356 @@ -925,7 +952,7 @@
357  
358        for (word = label->words; word; word = word->next)
359         {
360 -         gchar save = word->beginning[word->length];
361 +         GdkWChar save = word->beginning[word->length];
362           word->beginning[word->length] = '\0';
363           gtk_label_paint_word (label, x, y, word, &event->area);
364           word->beginning[word->length] = save;
365 diff -ur gtk+-1.2.5-pre2/gtk/gtktext.c gtk+-1.2.5-pre2.new/gtk/gtktext.c
366 --- gtk+-1.2.5-pre2/gtk/gtktext.c       Sat Sep  4 08:50:38 1999
367 +++ gtk+-1.2.5-pre2.new/gtk/gtktext.c   Thu Sep 23 00:21:40 1999
368 @@ -101,6 +101,13 @@
369    ARG_WORD_WRAP
370  };
371  
372 +typedef enum {
373 +  CHAR_CLASS_SPACE,
374 +  CHAR_CLASS_ALNUM,
375 +  CHAR_CLASS_IDEOGRAM,
376 +  CHAR_CLASS_OTHERS     /* punct, cntrl */
377 +} CharClass;
378 +
379  typedef struct _TextProperty          TextProperty;
380  typedef struct _TabStopMark           TabStopMark;
381  typedef struct _PrevTabCont           PrevTabCont;
382 @@ -298,6 +305,8 @@
383                                     const GtkPropertyMark *mark,
384                                     const PrevTabCont *tab_cont,
385                                     PrevTabCont *next_cont);
386 +static void find_word_wrap_position (GtkText* text, LineParams *lp);
387 +static CharClass char_class (GtkText* text, guint index);
388  static void recompute_geometry (GtkText* text);
389  static void insert_expose (GtkText* text, guint old_pixels, gint nchars, guint new_line_count);
390  static void delete_expose (GtkText* text,
391 @@ -4084,27 +4093,21 @@
392    
393    undraw_cursor (text, FALSE);
394    
395 -  if (text->use_wchar)
396 +  while (!LAST_INDEX (text, text->cursor_mark))
397      {
398 -      while (!LAST_INDEX (text, text->cursor_mark) && 
399 -            !gdk_iswalnum (GTK_TEXT_INDEX(text, text->cursor_mark.index)))
400 -       advance_mark (&text->cursor_mark);
401 -      
402 -      while (!LAST_INDEX (text, text->cursor_mark) && 
403 -            gdk_iswalnum (GTK_TEXT_INDEX(text, text->cursor_mark.index)))
404 -       advance_mark (&text->cursor_mark);
405 +      CharClass cc = char_class (text, text->cursor_mark.index);
406 +      if (cc == CHAR_CLASS_ALNUM || cc == CHAR_CLASS_IDEOGRAM)
407 +       break;
408 +      advance_mark (&text->cursor_mark);
409      }
410 -  else
411 +  while (!LAST_INDEX (text, text->cursor_mark))
412      {
413 -      while (!LAST_INDEX (text, text->cursor_mark) && 
414 -            !isalnum (GTK_TEXT_INDEX(text, text->cursor_mark.index)))
415 -       advance_mark (&text->cursor_mark);
416 -      
417 -      while (!LAST_INDEX (text, text->cursor_mark) && 
418 -            isalnum (GTK_TEXT_INDEX(text, text->cursor_mark.index)))
419 -       advance_mark (&text->cursor_mark);
420 +      CharClass cc = char_class (text, text->cursor_mark.index);
421 +      if (cc != CHAR_CLASS_ALNUM && cc != CHAR_CLASS_IDEOGRAM)
422 +       break;
423 +      advance_mark (&text->cursor_mark);
424      }
425 -  
426 +
427    find_cursor (text, TRUE);
428    draw_cursor (text, FALSE);
429  }
430 @@ -4116,25 +4119,19 @@
431    
432    undraw_cursor (text, FALSE);
433    
434 -  if (text->use_wchar)
435 +  while (text->cursor_mark.index > 0)
436      {
437 -      while ((text->cursor_mark.index > 0) &&
438 -            !gdk_iswalnum (GTK_TEXT_INDEX(text, text->cursor_mark.index-1)))
439 -       decrement_mark (&text->cursor_mark);
440 -      
441 -      while ((text->cursor_mark.index > 0) &&
442 -            gdk_iswalnum (GTK_TEXT_INDEX(text, text->cursor_mark.index-1)))
443 -       decrement_mark (&text->cursor_mark);
444 +      CharClass cc = char_class (text, text->cursor_mark.index - 1);
445 +      if (cc == CHAR_CLASS_ALNUM || cc == CHAR_CLASS_IDEOGRAM)
446 +       break;
447 +      decrement_mark (&text->cursor_mark);
448      }
449 -  else
450 +  while (text->cursor_mark.index > 0)
451      {
452 -      while ((text->cursor_mark.index > 0) &&
453 -            !isalnum (GTK_TEXT_INDEX(text, text->cursor_mark.index-1)))
454 -       decrement_mark (&text->cursor_mark);
455 -      
456 -      while ((text->cursor_mark.index > 0) &&
457 -            isalnum (GTK_TEXT_INDEX(text, text->cursor_mark.index-1)))
458 -       decrement_mark (&text->cursor_mark);
459 +      CharClass cc = char_class (text, text->cursor_mark.index - 1);
460 +      if (cc != CHAR_CLASS_ALNUM && cc != CHAR_CLASS_IDEOGRAM)
461 +       break;
462 +      decrement_mark (&text->cursor_mark);
463      }
464    
465    find_cursor (text, TRUE);
466 @@ -4755,27 +4752,8 @@
467                       GtkPropertyMark saved_mark = lp.end;
468                       guint saved_characters = lp.displayable_chars;
469                       
470 -                     lp.displayable_chars += 1;
471 -                     
472 -                     if (text->use_wchar)
473 -                       {
474 -                         while (!gdk_iswspace (GTK_TEXT_INDEX (text, lp.end.index)) &&
475 -                                (lp.end.index > lp.start.index))
476 -                           {
477 -                             decrement_mark (&lp.end);
478 -                             lp.displayable_chars -= 1;
479 -                           }
480 -                       }
481 -                     else
482 -                       {
483 -                         while (!isspace(GTK_TEXT_INDEX (text, lp.end.index)) &&
484 -                                (lp.end.index > lp.start.index))
485 -                           {
486 -                             decrement_mark (&lp.end);
487 -                             lp.displayable_chars -= 1;
488 -                           }
489 -                       }
490 -                     
491 +                     find_word_wrap_position (text, &lp);
492 +
493                       /* If whole line is one word, revert to char wrapping */
494                       if (lp.end.index == lp.start.index)
495                         {
496 @@ -4821,6 +4799,54 @@
497    lp.tab_cont_next = *next_cont;
498    
499    return lp;
500 +}
501 +
502 +static CharClass
503 +char_class (GtkText* text, guint index)
504 +{
505 +  GdkWChar wc;
506 +  wc = GTK_TEXT_INDEX (text, index);
507 +  if (text->use_wchar)
508 +    {
509 +      if (gdk_iswspace (wc))
510 +       return CHAR_CLASS_SPACE;
511 +      else if (gdk_iswalnum (wc))
512 +       return CHAR_CLASS_ALNUM;
513 +      else if (gdk_iswpunct (wc) || gdk_iswcntrl (wc))
514 +       return CHAR_CLASS_OTHERS;
515 +      else
516 +       return CHAR_CLASS_IDEOGRAM;
517 +    }
518 +  else
519 +    {
520 +      if (isspace (wc))
521 +       return CHAR_CLASS_SPACE;
522 +      else if (isalnum (wc))
523 +       return CHAR_CLASS_ALNUM;
524 +      else if (ispunct (wc) || iscntrl (wc))
525 +       return CHAR_CLASS_OTHERS;
526 +      else
527 +       return CHAR_CLASS_IDEOGRAM;
528 +    }
529 +}
530 +
531 +static void
532 +find_word_wrap_position (GtkText* text, LineParams *lp)
533 +{
534 +  while (lp->end.index > lp->start.index &&
535 +        char_class (text, lp->end.index) != CHAR_CLASS_SPACE &&
536 +        char_class (text, lp->end.index) != CHAR_CLASS_IDEOGRAM &&
537 +        char_class (text, lp->end.index - 1) != CHAR_CLASS_IDEOGRAM)
538 +    {
539 +      decrement_mark (&lp->end);
540 +      lp->displayable_chars -= 1;
541 +    }
542 +
543 +  /* lp->end.index points the position to be cut just now. If it's not a
544 +   * space, move it to the next display line. */
545 +  if (lp->end.index > lp->start.index &&
546 +      char_class (text, lp->end.index) != CHAR_CLASS_SPACE)
547 +    decrement_mark (&lp->end);
548  }
549  
550  static void
551 ---------------- x8 ---------------- x8 ---------------- x8 ----------------
552
553 --------------------------------------
554 Akira Higuchi
555 Dept. of Mathematics, Hokkaido Univ.
556 Hokkaido, Japan
557 Email: a-higuti@math.sci.hokudai.ac.jp
This page took 0.101911 seconds and 3 git commands to generate.