﻿RC_Match 自然言語仕様

【この文書の目的】
この自然言語仕様に従い、Windowsのデスクトップ版Wordで動くVBAの標準モジュールを作成する。対象モジュール名はこの文書の先頭に示す。関数名、引数の順序、辞書キーを変更しない。変数宣言を必須にし、手動で標準モジュールへ貼り付ける形式で作成する。属性行やThisDocument用のイベントは不要。
他モジュールの公開処理を呼び出す部分は、その処理の存在を前提とする。同じ公開関数を各モジュールへ重複実装しない。不明な会社書式は確認してから記入し、勝手に推測しない。

【ツール全体の契約】
・旧規程と新規程を読み取り専用で解析し、新欄が左、旧欄が右の編集可能なWord表を別ファイルへ保存する。入力を上書きしない。
・外部AI、外部通信、追加ソフト、外部テンプレートを実行時に使わない。Wordの標準オブジェクトと、遅延バインディングによるScripting.Dictionary、Scripting.FileSystemObject、VBScript.RegExpを使う。追加の参照設定を要求しない。
・通常の成功時はdocxだけを出力する。確認レポート、対応表TSV、手動対応を読み込んで再生成する機能は作らない。RC_Reportモジュールも作らない。
・issuesは処理中だけの内部診断。致命的な失敗時の画面表示と実行失敗TXTは残す。RC_Testsを明示実行した場合のテストTXTは別扱い。
・フォントや余白の変更は本文の変更とみなさない。原文の数字、否定語、句読点、空白、タブを言い換えたり削除したりしない。照合専用の正規化を原文や下線位置へ適用しない。
・Office、Wordのマクロ設定、OSのセキュリティ設定は変更しない。自分が開いた入力や作成した文書だけを閉じる。

【共通データ構造】
以下のキーは大文字小文字を含めて統一する。辞書中のオブジェクトはオブジェクトとして格納・差し替えし、文字列化しない。存在しない片側ノードは空文字ではなくNothingとする。Collectionの添字と文字位置は1始まり。

1. 解析文書：sourceは入力パスまたは識別文字列、titleは文書名、articlesは条ノードのCollection、historyは末尾履歴ノードのCollection、extrasは条に属さない保全ノードのCollection。
2. ノード：id、kind、prefix、gap、body、raw、heading、line、children。
  idは解析順ID。条はA001等、項はA001.P001等、号はA001.P001.I001等、履歴はH001等、未知段落はUに元段落番号を続ける。
  kindはarticle、paragraph、item、history、unknownのいずれか。prefixは元の番号文字列、gapは番号と本文の間の元の空白等、bodyは番号を除く本文、rawはprefix・gap・bodyをそのまま連結したもの。headingは条の直前の括弧見出し、lineは元入力の段落位置、childrenは子ノードのCollection。
  条ノード自身のbodyは空文字。その条の先頭本文は第1項ノードP001へ入れる。P001のprefixには元の第n条の番号を入れ、項番号「1」を新たに補わない。条ノードのrawは元番号とgapだけとなる。条にはheadingLineも加え、見出しの元段落位置を記録する。
3. 対応ペア：newとoldは新旧ノードまたはNothing、scoreはDoubleの類似度、statusはauto、review、new、deletedのいずれか、childrenは子対応ペアのCollection。
4. 内部診断：kind、location、reason、rawの4文字列を持つ辞書をissuesのCollectionへ追加する。利用者向けの確認レポートへ書き出さない。
5. 差分結果：newとoldは各側の下線区間Collection、fallbackは長文の代替計算を使ったBoolean。区間はstartという1始まり文字位置とlengthという文字数を持つ辞書。改行や空白を含む原文字列上の位置を使う。
6. 出力行：newTextとoldText、newHeadingとoldHeading、newHeadingBlankとoldHeadingBlank、newPrefixとoldPrefix、newArticleとoldArticle、newNoteとoldNote、historyGap、newSpansとoldSpans。
  TextとNoteは文字列。HeadingとPrefixは文字数のLong。HeadingBlank、Article、historyGapはBoolean。Spansは上記の下線区間Collection。初期値は文字列が空、文字数が0、BooleanがFalse、Collectionは空。

【モジュール間の公開処理】
RC_Configは設定定数、RC_Color、RC_Navy、RC_HeaderFill、RC_Regex、RC_TrimSpace、RC_MatchText、RC_NumberKeyを提供する。
RC_ParseはRC_Dict、RC_Node、RC_ParseText、RC_ReadDocument、RC_ParseLines、RC_NodeText、RC_AddIssueを提供する。
RC_MatchはRC_Similarity、RC_MatchScore、RC_NewPair、RC_Align、RC_PairChangedを提供する。
RC_CharDiffはRC_Diff、RC_AddSpanを提供する。
RC_OutputはRC_OutputDocumentを提供する。
RC_MainはCreateComparisonTable、RC_ShowOutput、RC_RunFiles、RC_UniqueBase、RC_WriteTextを提供する。
RC_Testsは任意の開発用RC_TestAllを提供する。通常の入口からテストを実行しない。
主な呼出し：MainがParseで旧・新を読み、Matchへ新articles・旧articles・issues・locationの順で渡し、Outputへpairs・新解析文書・旧解析文書・issuesの順で渡す。OutputがCharDiffを呼び、各処理がConfigとParseの共通処理を使う。

【他モジュールを呼ぶ際の引数契約】
次の一覧は呼出し互換性のために示す。この文書の対象以外のモジュールの実装を重複して作らない。
RC_Main
・CreateComparisonTable：公開。引数はなし。戻り値なし。
・RC_ShowOutput：公開。引数はoutput（String、値渡し）。戻り値なし。
・RC_RunFiles：公開。引数はoldPath（String、値渡し）、newPath（String、値渡し）、outputFolder（String、値渡し）。戻り値はString。
・RC_UniqueBase：公開。引数はfolder（String、値渡し）、stem（String、値渡し）。戻り値はString。
・RC_WriteText：公開。引数はpath（String、値渡し）、text（String、値渡し）。戻り値なし。
RC_Config
・RC_Color：公開。引数はhexRGB（String、値渡し）。戻り値はLong。
・RC_Navy：公開。引数はなし。戻り値はLong。
・RC_HeaderFill：公開。引数はなし。戻り値はLong。
・RC_Regex：公開。引数はpattern（String、値渡し）。戻り値はObject。
・RC_TrimSpace：公開。引数はtext（String、値渡し）。戻り値はString。
・RC_MatchText：公開。引数はtext（String、値渡し）。戻り値はString。
・RC_NumberKey：公開。引数はprefix（String、値渡し）。戻り値はString。
RC_Parse
・RC_Dict：公開。引数はなし。戻り値はObject。
・RC_Node：公開。引数はnodeId（String、値渡し）、kind（String、値渡し）、prefix（String、値渡し）、gap（String、値渡し）、body（String、値渡し）、line（Long、値渡し）。戻り値はObject。
・RC_ParseText：公開。引数はtext（String、値渡し）、source（String、値渡し）、issues（Collection、値渡し）。戻り値はObject。
・RC_ReadDocument：公開。引数はpath（String、値渡し）、issues（Collection、値渡し）。戻り値はObject。
・RC_ParseLines：公開。引数はlines（Variant、値渡し）、source（String、値渡し）、issues（Collection、値渡し）。戻り値はObject。
・RC_NodeText：公開。引数はn（Object、値渡し）、includePrefix（Boolean、値渡し、省略可能・既定値False）。戻り値はString。
・RC_AddIssue：公開。引数はissues（Collection、値渡し）、kind（String、値渡し）、location（String、値渡し）、reason（String、値渡し）、raw（String、値渡し）。戻り値なし。
RC_Match
・RC_Similarity：公開。引数はleftText（String、値渡し）、rightText（String、値渡し）。戻り値はDouble。
・RC_MatchScore：公開。引数はnn（Object、値渡し）、oo（Object、値渡し）、ni（Long、値渡し）、oi（Long、値渡し）、nc（Long、値渡し）、oc（Long、値渡し）。戻り値はDouble。
・RC_NewPair：公開。引数はnn（Object、値渡し）、oo（Object、値渡し）、score（Double、値渡し）、status（String、値渡し）。戻り値はObject。
・RC_Align：公開。引数はnews（Collection、値渡し）、olds（Collection、値渡し）、issues（Collection、値渡し）、location（String、値渡し）。戻り値はCollection。
・RC_PairChanged：公開。引数はpair（Object、値渡し）。戻り値はBoolean。
RC_CharDiff
・RC_Diff：公開。引数はnews（String、値渡し）、olds（String、値渡し）。戻り値はObject。
・RC_AddSpan：公開。引数はspans（Collection、値渡し）、first（Long、値渡し）、length（Long、値渡し）。戻り値なし。
RC_Output
・RC_OutputDocument：公開。引数はpairs（Collection、値渡し）、newDoc（Object、値渡し）、oldDoc（Object、値渡し）、issues（Collection、値渡し）。戻り値はDocument。
RC_Tests
・RC_TestAll：公開。引数はoutputFolder（String、値渡し）。戻り値はString。

【共通の境界条件】
自動採番、複雑な表、画像、脚注、本文外の図形、変更履歴の解釈は全面対応しない。内部診断と入力原本を保全するが、抽出できないものを架空の条文へ変換しない。
分割・統合の意味を自動で確定しない。残った原文を新設・削除として保全する。一般的な1対多分割の検出を保証しない。条の順序逆転を見やすく示す改善も別課題であり、この再構築で勝手に照合方式を変えない。
この文書は現行処理を再実装するための仕様。AIが生成したVBAのコンパイル・動作・原文保全は、Wordで別途確認する。

【このモジュールの入口・引数・戻り値】
・RC_Similarity：公開。引数はleftText（String、値渡し）、rightText（String、値渡し）。戻り値はDouble。
・RC_MatchScore：公開。引数はnn（Object、値渡し）、oo（Object、値渡し）、ni（Long、値渡し）、oi（Long、値渡し）、nc（Long、値渡し）、oc（Long、値渡し）。戻り値はDouble。
・RC_NewPair：公開。引数はnn（Object、値渡し）、oo（Object、値渡し）、score（Double、値渡し）、status（String、値渡し）。戻り値はObject。
・RC_Align：公開。引数はnews（Collection、値渡し）、olds（Collection、値渡し）、issues（Collection、値渡し）、location（String、値渡し）。戻り値はCollection。
・RC_PairChanged：公開。引数はpair（Object、値渡し）。戻り値はBoolean。

【担当範囲】
解析された同階層の新旧単位を1対1で照合し、子階層にも同じ処理を適用する。条番号だけで対応を決めない。原文自体を編集しない。手動指定やTSVの入出力は実装しない。

【RC_Similarity】
RC_MatchTextで両文字列を照合用に正規化する。完全一致なら空文字同士を含め1。異なり、どちらかが2文字未満なら0。
左の隣接2文字の組を出現回数付き辞書へ数える。右の各組について左の残回数が正なら共通数を1増やし、残回数を1減らす。重複組を集合にして失わない。
結果は共通数の2倍を、左の長さと右の長さの合計から2を引いた値で割ったDice類似度。

【RC_MatchScore】
kindが異なる組合せは0。番号を含めないRC_NodeTextから本文類似度を求める。位置近接度は1から、新位置を新件数+1で割った値と旧位置を旧件数+1で割った値の差の絶対値を引く。
両側に見出しがあるarticleは本文55%、見出し40%、位置5%。片側でも見出しのないarticleは本文95%、位置5%。それ以外は本文97%、位置3%。

【RC_NewPair】
new、old、score、statusを格納し、childrenを新しい空Collectionとする。対応のない側はNothing。

【RC_Align：準備】
新件数nc、旧件数ocを取得。新から旧へのmapping、旧の使用済みtaken、新のstatus、全組合せscoresを作る。ゼロ件の側にも対応できるよう添字0を持つ配列にする。
最初の存在する新または旧ノードがarticleなら対応閾値を0.69、それ以外は0.4とする。残余の類似候補診断閾値はarticleが0.59、それ以外は0.72。全組合せのRC_MatchScoreを事前計算する。

【RC_Align：相互最良を先に確保】
未対応の新を元順に走査し、未使用の旧の最高点・次点を求める。最高候補の得点が閾値以上、次点との差が0.08以上の場合だけ、その旧から未対応の新への最高点・次点も調べる。
逆側の最良が今の新で、逆側の次点差も0.08以上なら対応を確定し、旧を使用済み、statusをautoにする。
同点の場合は最高点の更新を厳密な大小比較で行い、走査順を維持する。1件でも確保したら同じ走査を繰り返し、変化がなくなるまで行う。これで途中追加の番号ずれや一意な移動候補を先に確保する。

【RC_Align：残りを順序付きDPで暫定対応】
未対応の新・旧の元添字をそれぞれ順序付きCollectionへ入れる。空の境界を持つ得点行列dpと行動行列actionを作る。
各セルはまず新を飛ばす上のセルを採用。旧を飛ばす左のセルが厳密に大きければそちらへ変更。候補得点が閾値以上なら、斜めのdpに候補得点から閾値を引いて0.01を加えた値を比較し、厳密に大きい場合だけ対応の行動を採用する。
末尾から戻り、斜めの行動は対応を設定しstatusをreviewとして旧を使用済みにする。競合候補の暫定1対1であること、新旧IDと双方の番号付き全文を内部診断へ残す。旧を飛ばす行動は旧位置だけ、新を飛ばす行動は新位置だけ減らす。片側が0になるまで戻る。

【RC_Align：残余診断と出力順】
未対応の新について、閾値以上に類似する旧が全候補内に残る場合、新設で全文保全するが分割・統合・対応漏れの可能性があると診断する。旧が既に対応済みでも候補確認の対象にする。未使用の旧も逆向きに同様の確認をする。各残余につき最初の候補1件を記録する。
掲載順は新の元順を基本とする。各未使用の旧について、旧順で後にある最初の対応済み旧がどの新に対応するかを探す。その新の直前へ削除ペアを入れる。後続対応がない削除は新列挙の後へ入れる。同じ挿入位置の削除は旧順を保つ。
削除ペアは新Nothing、旧ノード、得点0、status deleted。新設ペアは新ノード、旧Nothing、得点0、status new。対応ペアは双方のノード、得点、autoまたはreview。
すべてのペアで子にもRC_Alignを再帰適用する。片側がないときはその側に空Collectionを渡す。対応した子のlocationには親の新IDを加える。各元ノードが1回だけ使われるCollectionを返す。

【RC_PairChanged】
片側がNothingならTrue。両側のheading、prefix、gap、bodyのいずれかが原文比較で異なればTrue。子ペアのどれかが再帰的に変更ありならTrue。どれもなければFalse。書式情報、score、statusだけを変更判定にしない。

【確認例と限界】
同じ内容で番号だけずれた項を対応させる。途中に新しい項が入っても後の項を別物にしない。内容が無関係なら同じ条番号でも新設と削除に分ける。曖昧候補はreviewと診断へ残し、原文の重複・欠落を起こさない。
現在の診断は1対1候補の類似度を用いており、複数の新条を合成して一般的な分割を検出する仕組みではない。分割・統合の意味的な確定や順序逆転の表示改善を追加した仕様と誤認しない。
