# 機能設計書 107-Unicode

## 概要

本ドキュメントは、Julia標準ライブラリ `Unicode` モジュールが提供するUnicode文字カテゴリ判定・正規化機能の設計を記述する。Unicode文字列の正規化（NFC/NFD/NFKC/NFKD）、書記素クラスタ（grapheme）分割、大文字小文字変換、コードポイント割り当て判定、等価性判定等を提供する。

### 本機能の処理概要

**業務上の目的・背景**：多言語テキスト処理において、Unicode文字の正規化と操作は不可欠である。Juliaのパーサー自体がUnicode識別子をサポートしており、正規化処理はJuliaの言語仕様の一部でもある。科学技術分野ではギリシャ文字等のUnicode記号が変数名として多用される。

**機能の利用シーン**：文字列の正規化比較、書記素クラスタ単位のテキスト処理、大文字小文字を無視した比較、アクセント記号の除去、Juliaパーサーの文字変換。

**主要な処理内容**：
1. `normalize` -- Unicode正規化（NFC/NFD/NFKC/NFKD + カスタムオプション）
2. `graphemes` -- 書記素クラスタ分割イテレータ
3. `isequal_normalized` -- 正規化等価性判定（メモリ効率的な比較）
4. `isassigned` -- コードポイント割り当て判定
5. `julia_chartransform` -- Juliaパーサー用文字変換

**関連システム・外部連携**：utf8proc（Unicode処理Cライブラリ）、Base.Unicode（コア実装）。

**権限による制御**：特になし。

## 関連画面

本機能に直接関連する画面はない。

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 文字列処理のバックエンド機能として利用 |

## 機能種別

テキスト変換 / 文字処理

## 入力仕様

### 入力パラメータ（normalize）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| s | AbstractString | Yes | 正規化対象の文字列 | - |
| normalform | Symbol | No | 正規化形式（:NFC, :NFD, :NFKC, :NFKD） | - |
| compose | Bool | No | 正規合成の有無（デフォルトtrue） | - |
| decompose | Bool | No | 正規分解の有無 | - |
| compat | Bool | No | 互換等価の正規化 | - |
| casefold | Bool | No | 大文字小文字の折り畳み | - |
| stripmark | Bool | No | ダイアクリティカルマークの除去 | - |
| stripignore | Bool | No | 「既定で無視される」文字の除去 | - |
| stripcc | Bool | No | 制御文字の除去 | - |
| rejectna | Bool | No | 未割り当てコードポイントでエラー | - |
| stable | Bool | No | Unicodeバージョン安定性の保証 | - |
| chartransform | Function | No | カスタム文字変換関数 | - |

### 入力パラメータ（graphemes）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| s | AbstractString | Yes | 対象文字列 | - |
| r | AbstractUnitRange{Integer} | No | 書記素クラスタの範囲指定 | m > 0 |

### 入力データソース

ユーザーが指定する文字列。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| normalized_string | String | 正規化済み文字列 |
| grapheme_iterator | GraphemeIterator | 書記素クラスタイテレータ |
| is_equal | Bool | 正規化等価性判定結果 |
| is_assigned | Bool | コードポイント割り当て判定結果 |

### 出力先

戻り値。

## 処理フロー

### 処理シーケンス

```
1. normalize(s; kwargs...) / normalize(s, :NFC)
   └─ Base.Unicode.normalize() に委譲
       └─ utf8proc ライブラリ経由で正規化
2. isequal_normalized(s1, s2; kwargs...)
   ├─ 両文字列を1文字ずつデコンポーズ
   ├─ combining classでソート
   └─ コードポイント列を順次比較
3. graphemes(s)
   └─ GraphemeIterator(s) を返却
       └─ isgraphemebreak!() で書記素境界判定
```

### フローチャート

```mermaid
flowchart TD
    A["Unicode.normalize(s)"] --> B["Base.Unicode.normalize()"]
    B --> C["utf8proc_decompose / utf8proc_normalize"]
    C --> D["正規化済み文字列を返却"]
    E["isequal_normalized(s1, s2)"] --> F["_isequal_normalized!()"]
    F --> G["decompose_next_chars!() x 2"]
    G --> H{"コードポイント比較"}
    H -->|一致| I["true"]
    H -->|不一致| J["false"]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-107-1 | デフォルト正規化 | compose=true, compat=false がデフォルト | normalize呼び出し時 |
| BR-107-2 | NFKC等価 | :NFKC は compose=true, compat=true, stable=true に対応 | normalform=:NFKC指定時 |
| BR-107-3 | Julia文字変換 | julia_chartransformはU+00B5(micro)をU+03BC(mu)に変換する等のJuliaパーサー互換変換 | パーサー互換正規化時 |
| BR-107-4 | 書記素境界判定 | UAX #29に準拠した拡張書記素クラスタ境界判定 | graphemes呼び出し時 |
| BR-107-5 | メモリ効率的等価比較 | isequal_normalizedは両文字列の正規化済みコピーを作成せず、1文字ずつ比較 | isequal_normalized呼び出し時 |

### 計算ロジック

- `_decompose_char!`: utf8procの `utf8proc_decompose_char` を呼び出し、コードポイントを分解
- `combining_class`: utf8procの `utf8proc_get_property` からcombining classを取得
- `_isequal_normalized!`: 挿入ソート（n<32）またはsort!（n>=32）でcombining classの正規順序に並べ替え
- ASCIIの高速パス: 0x80未満のコードポイントはutf8procを呼ばずに直接処理

## データベース操作仕様

該当なし。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | ArgumentError | graphemes(s, m:n)で m <= 0 | 正の整数を指定 |
| - | BoundsError | graphemes(s, m:n)で n > 書記素クラスタ数 | 範囲内の値を指定 |
| - | utf8proc_error | utf8proc内部エラー | 入力文字列を確認 |

### リトライ仕様

該当なし。

## トランザクション仕様

該当なし。

## パフォーマンス要件

- ASCII文字（0x80未満）のコードポイントは高速パスで処理（utf8proc呼び出しを回避）
- combining classのソートは通常n<32のため挿入ソート（O(n^2)だが小さなnに高速）
- isequal_normalizedは事前割り当てバッファ（4要素）を使用し、必要に応じてresizeで拡張

## セキュリティ考慮事項

- Unicode正規化はホモグラフ攻撃の緩和に利用可能
- casefold=trueによるケースインセンシティブ比較はセキュリティ関連の文字列比較に有用

## 備考

- `julia_chartransform` はJulia 1.8で追加
- `isequal_normalized` はJulia 1.8で追加
- `graphemes(s, m:n)` はJulia 1.9で追加

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | Unicode.jl | `stdlib/Unicode/src/Unicode.jl` | `julia_chartransform`（52-55行目）: Juliaパーサー用文字変換。Base.Unicode._julia_charmapを参照 |

**読解のコツ**: Unicodeモジュールの多くの関数はBase.Unicodeの薄いラッパーであり、実際の処理はBaseモジュール側にある。

#### Step 2: 正規化処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | Unicode.jl | `stdlib/Unicode/src/Unicode.jl` | `normalize`（122-124行目）: Base.Unicode.normalizeへの委譲 |
| 2-2 | Unicode.jl | `stdlib/Unicode/src/Unicode.jl` | `isassigned`（140行目）: Base.Unicode.isassignedへの委譲 |

#### Step 3: 書記素クラスタ処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | Unicode.jl | `stdlib/Unicode/src/Unicode.jl` | `graphemes(s)`（150行目）: GraphemeIterator生成 |
| 3-2 | Unicode.jl | `stdlib/Unicode/src/Unicode.jl` | `graphemes(s, r)`（186-213行目）: 範囲指定書記素クラスタ。isgraphemebreak!で境界判定 |

#### Step 4: 正規化等価性判定を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | Unicode.jl | `stdlib/Unicode/src/Unicode.jl` | `isequal_normalized`（270-271行目）: エントリーポイント |
| 4-2 | Unicode.jl | `stdlib/Unicode/src/Unicode.jl` | `_isequal_normalized!`（274-346行目）: コアアルゴリズム |
| 4-3 | Unicode.jl | `stdlib/Unicode/src/Unicode.jl` | `decompose_next_chars!`（277-327行目）: 1文字ずつのデコンポーズとcombining classソート |
| 4-4 | Unicode.jl | `stdlib/Unicode/src/Unicode.jl` | `combining_class`（226-228行目）: utf8proc_get_propertyからcombining class取得 |
| 4-5 | Unicode.jl | `stdlib/Unicode/src/Unicode.jl` | `_decompose_char!`（217-221行目）: utf8proc_decompose_char呼び出し |

**主要処理フロー**:
1. **274行目**: 両文字列をイテレート開始
2. **277-327行目**: decompose_next_chars!で1文字をデコンポーズ、combining classソート
3. **284-288行目**: ASCII高速パス（0x80未満はutf8proc呼び出し回避）
4. **307-323行目**: combining classによるソート（n<32は挿入ソート、n>=32はsort!）
5. **343行目**: d1[j1] == d2[j2] で比較

### プログラム呼び出し階層図

```
Unicode.normalize(s)
    └─ Base.Unicode.normalize(s)
           └─ ccall utf8proc_*

Unicode.isequal_normalized(s1, s2)
    └─ _isequal_normalized!(s1, s2, d1, d2, chartransform)
           ├─ decompose_next_chars!(state, d, options, s)
           │      ├─ chartransform(UInt32(c))
           │      ├─ _decompose_char!(c, d, offset, options)
           │      │      └─ ccall utf8proc_decompose_char
           │      └─ combining_class(d[n])
           │             └─ ccall utf8proc_get_property
           └─ d1[j1] == d2[j2]  (コードポイント比較)

Unicode.graphemes(s)
    └─ Base.Unicode.GraphemeIterator(s)
           └─ Base.Unicode.isgraphemebreak!()
```

### データフロー図

```
[入力]                [処理]                      [出力]

文字列s ──────▶ normalize(s) ──────────▶ 正規化済み文字列

文字列s1, s2 ──▶ isequal_normalized()
                     │
                     ├─ decompose_next_chars!(s1)
                     ├─ decompose_next_chars!(s2)
                     └─ コードポイント列比較 ──▶ Bool

文字列s ──────▶ graphemes(s) ──────────▶ GraphemeIterator
                     └─ isgraphemebreak!()
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| Unicode.jl | `stdlib/Unicode/src/Unicode.jl` | ソース | Unicodeモジュール全体 |
| base/strings/unicode.jl | `base/strings/unicode.jl` | ソース | Base.Unicode（コア実装） |
