あらたまの年も新たに去年の月

image

今年はいよいよどっからもカレンダーが来ない… まぁ,いっか.コロナ時代だ!一旦ここまでの修正をフィックスしてから作業に掛かることにしよう.仕掛りのオプションには以下がある.

  1. DecidePrimaryNodeを書き換え@20201226 1箇所
  2. mergeUpperSegmentで移動禁止@20201227 1箇所
  3. 最大区間ノード対を端点共有の先頭に置く@20210101 25箇所

源氏物語全系譜6.1.ZELの全体図を#18 左兵衛督で開いて,PAIRBOX::CheckPairBoxで不良ノード対エラーで停止.「最大区間ノード対の逆転」が起きている.PAIRBOX:#84668:#23457 弘徽殿大后(1)→#12718 (0) と#50680:#23481 致仕太政大臣の北の方(1)→#13143(0)だ.NAMEBOX::makePairBoxではノード対を生成するだけで,端点共有の調整までは実施していない.ただし,端点共有はチェックされていて,端点共有束には追加されている.PAIRBOX:CalcPairBoxではNOCOMMONの場合には調整しているが,すべての場合をカバーしていない.ここで調整しておくべきだろう.

源氏物語全系譜6.1.ZELの傍系血族図を#5 紫の上(若紫)でソートしてLIST::nextで(PHASE > INITIALSTATE)という理由で停止した.系統並び替えでTribeRelocation→MakePairListCleanを実行しているところだ.PAIRLIST::SearchSamePointPairの中で起きている.この関数は「端点を共有するノード対を探す」ためのものだが,探しているノード対リストに属していないノード対をピックアップしてきたように思われる.IsBindablePairの中で書き換わっているのではないだろうか?

IsBindablePairの中でPAIRBOX::CalcPairBoxが実行され,「最大区間ノード対の逆転を解決」によってリストが再編されている.このため,直列リスト上にあったPAIRBOX:#35362:#21617 雲井の雁(1)→#12378(0)が端点共有リストの下位に移ってしまっている.IsBindablePairの中でPAIRBOX::CalcPairBoxが実行されること自体問題だが,最大区間先頭の調整が遅れていることも問題だ.いや,この動作はPAIRLIST::RepairPairBoxGenerationの中で起きているので,状態がある程度流動的になるのは避けられないのではないか?

IsBindablePairの中でCalcPairBoxを実行しないことでこのエラーは解消したが,雲井の雁と玉鬘の端点共有が崩れてしまっている.なぜか?⇒明石中宮と雲井の雁が同一チャンネルに入ったため,玉鬘が加わる余地がなくなったためと考えられる.(最終出力では明石中宮と雲井の雁は同一チャンネルにはなっていない… )最適解を求めるとすれば,玉鬘と雲井の雁の端点共有を成立させた上で,明石中宮は別チャンネルに移るというのが正解と思われるが,そこまでの動作にはなっていない.これで見ると,最優先すべき条件は端点共有であるということが分かる.

▲源氏物語全系譜6.1.ZELの全体図を#127 摂政太政大臣でソートしてPAIRBOX:repairCommonEndPointの出口でGENELIST:CheckPairBoxの不良ノード対エラーにより停止.CheckSamePointで「端点一致でsamepointゼロ」というエラーが起きている.問題のノード対はPAIRBOX:#49223:#23382 蛍兵部卿宮の前の北の方(1)→#13126 蛍兵部卿宮の前の北の方(0).

ノード対の「端点共有で始点不一致」エラー

多重カードを削減する基本的な方法は二つのカードを連結線で接続して片方のカードを削除することだ.このような連結線のうち,親から出て子どもに入るように見える親子連結線の水平部分をノード対と呼んでいる.これはノード対の両端に同一人名のカード(一方は可視,他方は不可視)が接続していることによる.ノード対の処理はZTシステムのコンポーネントの中でももっとも基本的なパートとなっている.

これらの水平連結線を交叉しない範囲でできるだけ簡潔に描画するというのはかなり難しい課題で,達成目標はベストエフォート(できる限り)ということになっているのだが,どうもあまりできがよくないので見直す必要が出てきた.おおまかな方針を決定し,すでに実装も進んでいるので,バグを潰しながら整理してゆくことにする.

NAMEBOX::makePairBoxでCheckSamePointの「端点共有で始点不一致」エラー.障害ノード対は#1226:#1099 雲井の雁(1)→#587 雲井の雁(0)と#1225:#1098 玉鬘(1)→#419 玉鬘(0)だ.⇒解決済

源氏物語全系譜6.1.ZELの傍系血族図の基準ノードを#13 春宮から#1光源氏に変えて,系統並び替えの冒頭でノード対リストのcancelを実行中,PAIRBOXの削除で参照カウントが負になった.⇒系統並び替えしなくても,アプリ終了だけで再現できる.⇒完全参照リスト管理をオンにしてテストしてみよう.⇒nodule::CheckSansyoで被参照カウント不一致が多発している.#34442 ノード対:蛍兵部卿宮の前の北の方(1)→(0)で参照元ノードリスト不記載,#34134 ノード対:玉鬘(1)→(0)でも同じエラーが発生している.これらは起動時にすでに発生している.LIST::bottomlistへの書き込みだ.PAIRLIST:swapで直接書き込んでいるためだろう.Bottomlist関数を使うようにした.

同上サンプルの全体図:#5 紫の上(若紫)でソートしてMakePairListCleanでループカウントオーバーになった.「最大区間ノード対の逆転」がPAIRBOX:#1492439:#1462069 致仕太政大臣の北の方(1)→#2449 (0)で起きている.親族図:傍系血族図 基準ノード=#5 紫の上(若紫)を開くと,NAMEBOX::makePairBoxでCheckPairBoxのエラーで停止する.⇒エラーを無視して描画は可能.全体図は問題なく開けた.紫の上の全体図を開いて,基準ノードを変えずに反復ソートしたら,3回目でMakePairListCleanの障害が出た.これは初期化が不完全ということを意味している.かなりまずい.

まず,先に最大区間ノード対の逆転がなぜ止まらないのかを見ておこう.PAIRBOX 蛍兵部卿宮の前の北の方(1)→(0)と競合しているが,これを端点共有と認定しているところが誤りだ.(3434, 3293)と(3434, 3293)で向きが逆になっている.⇒PAIRLIST::takeoutで共有解除,PAIRBOX::RepairCommonEndPointとPAIRBOX::MoveSamePointで共有を設定している.CheckCommonEndPointが間違った値を返している.⇒比較対象の矩形領域を正規化していなかった.

これで問題は解決したが,系統並び替えを反復すると動作が変化するというのはとてもまずいので,シューティングしておかなくてはならない.正規化すれば正しく動作するのだから,正規化しなくても動いているというのは,「たまたま」動いていただけと解釈するしかない.しかし,何がどうなっているとそのような動作になるのかを解析するのは難しい.類似のバグがまだ潜んでいる可能性はある.複数のバグが競合して動いたり動かなかったりということもありそうだ.⇒系統並び替えの冒頭で描画要素を完全にクリーンアップすることでソートを反復しても動作には変化が見られないようになった.

その上でCheckCommonEndPointの修正(比較対象矩形領域の正規化)を行うと,連結線のもつれはほぼ完全に消えた.これまでは端点共有と通常ノード対の区間交叉がかなり見られたが,一見したところ見当たらなくなった.EraseTreeViewで行っている描画要素のクリーンアップは多少時間コストは掛かるが,これを実行しておけば間違いはない.

類似した関数にInitTreeViewというのがあり,修正履歴では「InitCoupling>InitTreeViewを廃止@20201208 余分な処理になっている → FIXED@20201209」となっているので,多分使わなくてよいのではないかと思う.この関数は,現在,①FAMILYTREE:callSendCard,②FAMILYTREE::getNewCardで使われている.

「InitTreeViewをCOUPLINGのコンストラクタに移動@20170731」というコメントもあるが,COUPLING()では「ここではTREEVIEWの初期化は実行しない@20170804」となっている.⇒タイトル枠を初期化しているのが気になるが,各種フラグのリセットなどをやっているので,実行しておいた方がよいと思う.

▲源氏物語全系譜6.1.ZELの全体図を#18 左兵衛督でソートしてNAMEBOX::makePairBox→ PAIRBOX::CheckPairBoxエラーが出た.

春宮の傍系血族図で端点共有の始点不一致

SUPPORTSIMPLEGRAPHオフのバージョンで,源氏物語全系譜6.1.ZELの傍系血族図を#13 春宮で開いて,repairCommonEndPointで停止した.CheckSamePointでエラーが出ている.「端点共有で始点不一致」が起きている.repairCommonEndPointではPAIRBOX:#1237:#1120 藤壷の宮(2)→(0)を端点共有リストに移動しているが,最初から対象を間違えている.⇒いや,違う.これは誤差の累積だ.

この端点共有束には藤壷の宮と承香殿女御が入っている.区間は(4299, 1364)と(4302, 1680)だ.この結果,端点共有束としての区間は(4302, 1680)となり,明石の上の(4305, 1496)も誤差の範囲で一致する.これを避けるための唯一の方法は,端点共有リストの先頭につねに最大区間のノード対を置くようにすることだろう.それにはどうしたらよいか?

問題は区間を比較するとき,端点共有の場合は「正規化区間」というのを使っている点にある.正規化区間というのは言ってみれば「最大区間」と同じだが,誤差を含んでいるため許容誤差を3として±3以上の誤差を許容してしまうことになる.これをまず,廃止する必要がある.

#90 雲井の雁の出口検査で水平スプリット

#90 雲井の雁で出口検査で水平スプリットを検出 TRIBEBOX #1082 :先祖 #843 ※218でスプリットが生じている

TRIBELIST::DetectHorizontalSplitではこのスプリットを検出できない.DetectSplitの対象となるのは,FreeTribeBoxと呼ばれる束縛されていない系列に限定されている.⇒これを暫定的に無差別に検査を実行するようにしても状態は変わらない.この仮修正の下で,「系列間衝突が発生で移動量ゼロ」という事象が起きているので見ておこう.

TRIBEBOX::DetectSplitでERR_CROSSINTRIBESを返しているが,MoveLeftUptoLimitは移動量ゼロを返している.障害が起きているのは,RIBEBOX: #1058 先祖=#427 一院(0)だ.T1=#1070 [系列枠]:先祖=大臣(橋姫)とT2=#1062 [系列枠]:先祖=先帝の衝突が検出されているのだが… TRIBEBOX::CheckConflictでは水平交叉量を返しているが,エラーコードを返す場合もある.また,この関数を呼び出しているTRIBEBOX::DetectSplitでは,「厳密値を適用する@20180729」という決定がなされている.

今の場合は交叉量は1なので許容誤差範囲と推定されるのだが… 2018年当時のログを読み直してみるとこの項目に直接言及した記述はないが,「checkBondedTribeCollision 束縛系列ないし下位系列を持たない系列を対象とする.対象系列枠の主系列およびその下流系列(つまり系列ブロック)と対象系列との衝突検定を実施する 下位系列を持たない場合は系列ブロック核のみを検査する 厳密値を適用する 」とある.

「衝突検定はすべて厳密値を適用するものとし,実動作が完了してから丸めるものとする」というコメントもあるので,この方針に従ったものだろう.ただし,TRIBEBOX::DetectSplitでは交叉量ではなく,エラーコードを返しているので丸めようがない.TRIBEBOX::DetectSplitで誤差を見るようにしておこう.⇒「系列間衝突が発生で移動量ゼロ」エラーは解消した.⇒おかしい.系列枠リストから※218系列が消えてしまっているように見える.第14位にいるはずなのだが…

いや,そもそも当初は96系列あったはずなのに,64系列になっている.32個もどこかに消えてしまっている.⇒MakeUpTreeの出口近く(系列グループ(系統)を構成するの段)ではすでにそのカウントだ.96の中には単身先祖系列で抹消されるものもあるから,カウントが減少するのは不思議ではないが… ※218が先祖となる系列は消えている.⇒※218は一院系列に取り込まれている.つまり,これは系列内スプリットだ.@58 大宮と@68桃園式部卿宮の間が開いている点が問題になる.

image

一院系列のTRIBEBOX::CheckGeneSplitでスプリットが検出されていない.⇒CheckGeneSplitとInvestigateHSplitsで微妙に動作が異なる点が問題だ.InvestigateHSplitsでは許容範囲としてmax(Metrix.nfontW, Metrix.marghgap)という値を使い,これを1ふかした定規で区間検査を行っている.いま,marghgapは12という値が適用されているので,delta=13となっている.上の図でイエローの帯の幅は13で,これはほぼ結婚枠間のギャップに等しい.

CheckGeneSplitでは結婚枠矩形領域を集積するとき,結婚枠間ギャップを加味した値を取っている上に,さらに計算誤差を回避するためとして+1しているため,大宮の入っているボックスと桃園式部卿の入っているボックスは接触していると認定されている.⇒InvestigateHSplitsの許容範囲を拡げてこの矛盾を回避することも可能だが,CheckGeneSplitの検査を厳格にして,1単位でも移動するように修正した.

基準ノード#121 明石の尼君で開いて,TRIBEBOX:DecidePrimaryNodeで系列優先実ノード不在が起きる.⇒障害が起きているのはTRIBEBOX: #1064 先祖=#635 先帝(0)[0] 優先=紫の上(若紫)だ.TRIBEBOX::DecidePrimaryNodeの書き換えがまだ完結していない.現行ではほとんどをGetAlternativePrimeNodeに委ねるという論理になっているが,やり過ぎだ.すでにGoDownStreamでほぼ確定している条件があるのだから,それを活かすようにしなくてはならない.

もっともシンプルな解決として,優先ノードの持つ仮ノードリストの中で自系列に属するノードがあれば,それを優先仮ノードとし,他系列で自系列の下位系列に属さないものがあれば,優先実ノードとするというロジックを組み込んだ.基本的にはこれでよいのではないかと思う.

#18 左兵衛督で「系列間衝突が発生で移動量ゼロ」が起きた.ERR_CROSSINTRIBESが起きている.交叉量は18あるので無視できる値ではない.衝突はTRIBEBOX: #1058 先祖=#661 摂政太政大臣(0)と#1064 先祖=#507 ※3(0)の間で起きている.どちらも一院系列の従系列だ.関数MoveLeftUptoLimitで,ERR_CROSSINTRIBES,ERR_SPLITINTRIBES,ERR_WRONGSENZOORDERのケースを処理している.検査対象は始系列の#1056 先祖=#791 右大臣(明石)(0)だ.これはかなりおかしい.⇒仮修正がそのまま残っていた.

源氏の全体図テストが通った!

image

身を捨ててこそ浮かぶ瀬もあれ

野菜のストックがしおれかけて来たとき,水を掛けたり,水栽培したりして延命させることができるが,それでもしおれっ放しで戻らないときは,野菜が浸るまで水を注いでやるとよい.野菜が水に浮いてくるまでじゃぶじゃぶに水を入れてやると翌日には完全に復活している.

image

水をたっぷり張って野菜が謂わば無重力状態のように水中に浮いているようにするというのが肝心なところだ.捨身,身を捨てるということもときには必要なときがある.

SUPPORTSIMPLEGRAPHオフ,「基準ノードの配偶者の配偶者側で展開する@20201230」オフのバージョンで源氏物語全系譜6.1.ZELの全体図を#57 女三宮で開いてい,MARGBOX::GetUpperNodeで親ノード不正エラーが発生する.障害ノードはMARGBOX:#401:#1262 女三宮(1)+#1263 柏木(1)→で,親ノードの位置にNAMEBOX:#1267 小宰相の君(1)が現れている.これはかなりおかしい.

image

相互関係を確認するためにインストール済の別アプリで開いてみた.どう見てもこの結婚枠の上位に来るようなノードではない.TribeRelocationの【7.2】完全木検定:すべての系列を完全系列として正準化するステージでMakePairListClean中に起きている.NAMEBOX:GetGoldenCoupleでZTYW婚を削除するタイミングで発生している.削除対象のZTYW婚はMARGBOX:#2006:#1262 女三宮(1)+#1263 柏木(1)→#1265 按察の君(薫付)(1)だ.

削除対象のZTYW婚の本人ノードは按察の君ではなくて,薫 #1204だ.按察の君と小宰相の君はいずれも薫の配偶者で道連れでZTYW婚に連れ出されたものだ.手順の中では本人の薫の仮ノードは削除されているが,配偶者が取り残されて孤立している.⇒ZTYW婚を始末する完結したルーチンRestoreErasedYoungWifeがあるので,それと差し替えることにしよう.これで問題は解決したが,まだ不具合が残っている.

MARGBOX::RestoreErasedYoungWife→ NAMEBOX:RestoreExtractBox→ RetrieveGhost→ RestoreYoungWife→ RestoreExtractBoxで処理後の位置関係の検査で不一致が出ている.⇒検査の意味が不明.抽出枠を復元したとき,親の結婚枠の位置とサイズが不変であることを仮定しているようだが… この論理は廃止でよい.

▲#90 雲井の雁で出口検査で水平スプリットを検出.#91 左衛門督(寄生)でも発生.確かに#218 ※系列でスプリットが生じている.

image

検査ルーチンが実動作している可能性があるので,まず,それを見てみよう.検査ルーチンは動作にまったく影響していないようだ.このスプリットはAccidentalCollision→DetectHorizontalSplitでは検出されていない.2018-07-13の決定で「HORIZONTALORDERフェーズまではAccidentalCollisionを実行しない」となっている.InvestigateHSplitsはこのスプリットを検出できるが,この関数は描画ステージ(絶対座標系)でなければ動作しないため,適用できない.また,適用したとしても,系統並び替えの出口でHeapTribeBoxesが実行されるまでは,系統間の距離が定まっていないので検査しても意味がない.

▲#121 明石の尼君で系列優先実ノード不在が出た.#150 ※,#169 中務宮でも起きる.

系列接続問題を「人間関係の解析」から「図形の配置問題」に転換する

ZTシステムの作図法の基本は,系図を系列に分解しそれらの相互位置関係を定めることにある.系列とは先祖ノードの直系血族とその配偶者の集合であり,自ずと木を構成するから系列単体を描画するというのは初歩的な課題だが,入り組んだ系列の相互関係を定めるというのは必ずしも容易ではない.この部分に関してはこれまでにも何度も試行錯誤的な作り替えを実施してきた.このような試行錯誤に終止符を打つために提案されたEstablishMajorTribeChainが廃止されたのは2019/02/04だ.系列優先ノードの選択基準を緩和するという方向に動き始めたのはこの頃からと思われる.緩和の方向性は最終的には,系列の接続関係を「人間関係の解析」から「図形の配置問題」に転換するという方向を目指していたのではないかと思う.この方向性はまだ完結したとは言えないが,大幅に緩いものになってきていることは確かだ.

SUPPORTSIMPLEGRAPHオフのシステムで源氏物語全系譜6.1.ZELの全体図を#4 明石中宮で開いてTribeRelocationの【5.2】重婚同類グラフ検定の事後処理を行なうステージでLIST::nextで停止した.前にこのエラーが発生したときには,対象リストを取り違えているという論理ミスがあったが,今回はどうなっているのだろう?

今回は事前にplist->find(pbox)を試しているにも関わらず発生している.このエラーが発生する一つ前に,「ノード対世代不一致」というのが起きて,PairBoxGeneChangeが実行されている.この関数の中でノード対が削除される場合がある.いや,そもそもエラーは(!find(listnod))ではなく,(PHASE > INITIALSTATE)という理由だ.

明らかにPAIRLIST #1443のbottomlistの値が間違っている.⇒PairBoxGeneChangeの実行で不良が発生している.NAMEBOX:RetrieveGhostでノード対を破棄したときに起きているようだ.⇒plist->deleteElement(pairbox, true);で発生している.PAIRLISTはdeleteElementを持っていない.⇒PAIRLIST::dataCountDownでリスト終端ノードが端点共有である場合を見落としていた.

同上サンプルを開いて,TribeRelocationの【7.8】多重カードゼロを検査して変化なしなら検定打ち切りステージでTRIBEBOX:SetMinorTribeのエラーが発生した.DisConnectedGraphを強制モードで実行しているところだ.障害系列は#1120 先祖=#801 左大臣(梅枝)(0)で優先仮ノードはNAMEBOX:#1336 今上(2),関係は配偶者,優先実ノードは#415 明石中宮(0)で関係は養子,所属系列は#1056 先祖=#427 一院(0)の始系列だ.この接続関係は「逆婚姻関係」と呼ばれるものに相当する.DecideTribeTypeはPRIME_BTWLEFTを返している.確かに,今上(2)はDOUBLYBLESSED,明石中宮(0)はRIGHTHUSBANDだから,PRIME_BTWLEFTという判定は正しい.

ここの論理は,優先仮ノードが配偶者で系列種別が婚姻関係でも逆婚姻関係でもない場合で,この系列が始系列であった場合の代替ノードを探すというものになっているが,始系列でない場合は無動作なので当然配偶者のままになっているから,明らかにおかしい.また,始系列の場合は通常は基準ノードが最初から確定しているのだから,代替を探すということ自体かなり奇妙な感じがする.複数系統の場合にはこのようなことが起こり得るかもしれないが,いずれにしても,始系列以外でここで停止するというのは誤りだろう.

同上サンプルを#48 四宮(桐壷院の)で開いてTRIBEBOX:DecidePrimaryNodeで停止した.優先仮ノード不在エラーが起きている.障害が起きているのはTRIBEBOX: #1068 先祖=#811 大臣(橋姫)(0)で,優先ノードはCARDLINK:#546 @70浮舟[0].浮舟は仮ノードを4つ持っているが,うち3つは一院系列,他の一つは常陸介系列で,橋姫系列というのは存在しない.

浮舟の母は2つ結婚を持っている.相手は八宮と常陸介で,浮舟は両方に属しているがどちらも相手方の系列で展開されているのだろう.従って,大臣(橋姫)系列の優先ノードとなり得るのは浮舟の母だけだ.DecidePrimaryNodeではこの後,TRIBEBOX:GetAlternativePrimeNodeで代替候補の浮舟の母を見つけているので,問題はない.つまり,ここでは停止しなくてよい.

予想されていたことだが,非連結系列が出てしまった.

同上サンプルを#81 弘徽殿女御で開いて,TribeRelocation【11】系列包括矩形領域を集積して系統を並列配置するステージで非連結系列が発生した.「始系列参照パスが途切れている系列」が3件.

  1. #110566 先祖=#789 左大臣(真木柱)(0)[27] 優先=#653 左大臣の女御(0)→#425 冷泉院(0) →主系列#110502:摂政太政大臣 type=婚姻関係
  2. #110568 先祖=#853 宰相の娘(0)[28] 優先=#853 宰相の娘(0)→ #425 冷泉院(0) → 主系列#110502:摂政太政大臣 type=婚姻関係
  3. #110570 先祖=#857 中納言の娘(0)[29] 優先=#857 中納言の娘(0)→#425 冷泉院(0)→主系列#110502:摂政太政大臣 type=婚姻関係

少しおかしい.これら3系列はすべて摂政太政大臣系列を参照しているが,摂政太政大臣系列は始系列ではなかったのか?⇒Majortribeが設定されていないためのようだ.いや,設定されているが,TRIBEBOX:GetMajorTribeでNULLを返している.GetMajorTribeでは

  1. 始系列である場合
  2. 仮ノードがノード対を持っている場合
  3. 仮ノードがBTW右手本人の場合
  4. 仮ノードがBTW左手本人の場合
  5. 仮ノードがノード対参照先実ノードで系列種別が逆婚姻関係の場合

以外は有効な系列接続関係と認めていない.しかし,現行ではもっと多様な関係が許されている.これには,親子関係による接続が含まれていないし,配偶者→配偶者の参照も認められていない.これを以下のように整理した.

  1. 仮ノード:本人 x 実ノード:本人 → 親元関係
  2. 仮ノード:本人 x 実ノード:配偶者 → 婚姻関係
  3. 仮ノード:配偶者 x 実ノード:本人 → 逆婚姻関係
  4. 仮ノード:配偶者 x 実ノード:配偶者 → BTW左接続関係/BTW右接続関係

すべてこの区分で解決した.

▲同上サンプルを#94 明石の上で開いて,DecidePrimaryNodeを実行中,MARGBOX::GetUpperNodeで「親ノード不正」エラーが発生

抽象グラフ検証系を外したベーシックなシステムの動作を再確認する

抽象グラフ検証系を外したベーシックなシステムの動作を再確認しておこう.グラフ検証系はZTシステムの肝とも言うべき重婚同類循環検定で用いられている機構で,ZTシステムの必須コンポーネントではあるが,それなしでも通常の系図を描画することは可能であり,どのような系図もたとえそれが最適なものではなかったとしても少なくともエラーなしで描画できなくてはならない.グラフ検証系がベーシックシステム自体の不良をカバーするものであってはならない.

MakeUpTreeの中で系列優先ノードを決定するDecidePrimaryNodeを全面的に書き換えた.これはある意味で,系列接続関係の仕様を根底から修正するものになっている.従来仕様では,従系列の系列優先仮ノードとそれが参照する主系列の優先実ノードは大概の場合は異なるカードの人名枠であることが仮定されていたが,改訂DecidePrimaryNodeではこの制限を大幅に緩和して,仮ノードと実ノードが同一人名であることがむしろノーマルであるような仕様になっている.

要は2系列の接点となるノードが決定できれば,その接続関係は問わないという立ち位置にシフトした.実際,系列優先ノード決定の時点では接続関係が確定していない配偶者ポジションにある2つのノードを結合するということが行われている.この関係は最終的にはBTWによって解決されているが,従来論理ではこのような選択は不可能だった.つまり,接続関係は後付けてよいというポリシー変更がなされたと解釈してよい.

グラフ検証系を外したZTベーシックシステムで,源氏物語全系譜6.1.ZELの全体図を#1 光源氏で開いて,出口検査のCheckPerfectTreeで「結婚枠内兄弟ノードの衝突」が検出されている.実際,NAMEBOX #615 藤壷の宮(0)と#605 末摘花(0)の人名枠が交叉している.水平交叉量は6で計算誤差として許容できる範囲を超えている.しかし,エラーを無視して描画するとこの交叉は解消してノーマルな状態で描画されている.これは検査関数の中で使われているCheckBrotherOrderが検査モードで呼び出されているにも関わらず,実動作を伴うものになっているためと考えられる.これはそれ自体不良なので調べておこう.

MARGBOX::CheckBrotherOrderは実行関数としてMARGBOX:checkBrotherOrderを呼び出している.この関数から呼び出しているCheckBrotherCrushがモードに関わりなく移動を実行している.⇒対処した.次の問題はなぜ,このエラーがメインループで検出されなかったか?という点だ.⇒どうも,検査ルーチンの中に実動作を伴うものがあるように思われる.⇒原因はTRIBELIST::CheckMaximalSegmentにある.セグメント検定はTRIBEBOX::CheckMaximalSegmentによって系列単位に実行される.障害が起きているのは一院系列 #1056なので,ここだけ見ればよいだろう.いや,障害はどうも複合的なもののようだ.

TRIBELIST::CheckMaximalSegmentの単独実行では発現しない.それに加えてtribelist->IsTribeCompleteを実行する必要があるようだ.この2つは実行順序を問わず,どちらを先に実行した場合でも発現する※.TRIBEBOX::CheckMaximalSegmentから呼び出されるTRIBEBOX:CheckMaximalCompactionは検査モードというのを持っていないが,それから呼び出されるMaximalCompactionには検査モードがある.便宜的にCheckMaximalCompactionの引数が空の場合は実行モード,そうでないときは検査モードであるとしてみよう.⇒この修正は意味があるが,現在の障害には影響を与えない.

※藤壷の宮(0)と末摘花(0)の人名枠の交叉はTribeRelocationの出口ですでに存在している.ただし,交叉量は4で許容誤差の範囲内にある.下記のMoveNeutralによる移動は実際にはわずか1しか移動していないが,IsTribeCompleteの中でもCheckMaximalSegmentが実行されているため,移動量1の動作が複数回実行されることにより,傷口が6まで拡大して発現に至るという状況になっている.

MAXIMALGRAPH::mergeUpperSegmentでBobject::MoveNeutralが実行されていた.MaximalCompactionから呼び出されるCountUpLooseBeltは検査モードを持っていない.この関数は本来検査専用と考えられていたものと思われるが,CountUpLooseBeltからMoveNeutralまではかなりの段数がある.

CountUpLooseBelt→ TRIBEBOX::CountLooseBelt→ MAXIMALGRAPH:JoinUpperSegment→ MergeUpperSegment→ mergeUpperSegment→ Bobject::MoveNeutral

中間の各関数は本来検査専用で,検査/実行を区別していない.mergeUpperSegmentでは対象結婚枠がZTYW婚の場合に限り,隣接人名ノードを接触位置まで移動するという操作を行っている.この動作を廃止するというのも一つの考え方であるかもしれない.

この処理を停止したら,今度はMARGBOX::CheckMarchainで「結婚鎖拡張チェーン不正」というエラーが起きるようになってしまった.上記修正とは本来まったく無関係であるようなところだが… 上の修正が妥当であるか否かは別として,これはこれでまた別の障害と考えるべきだろう.まず,こちらを先に片付けることにする.

上記のような条件の下で,TribeRelocationの【9】先祖並び自動オフで系列枠を線形物理配置というステージの「ZTYW婚の適正配置を実施する」という段で,CheckZeroPositionを実行して「結婚鎖拡張チェーン不正」というエラーが発生している.結婚チェーン上に並んだ2つの結婚枠が同じ配偶者を持っているとき,後ろの結婚枠のsiblingsが前方結婚枠を指していないというエラーだ.実際には空が入っている.siblingsというのは,「前方にある同じ母の子ども枠への参照:チェーンを構成する」というものなので,確かにどこかで間違えているように思われる.

  1. MARGBOX:#1725:#409 光源氏(0)+#603 夕顔(0)→#419 玉鬘(0)
  2. MARGBOX:#343:#409 光源氏(0)+#603 夕顔(0)→

どちらも光源氏(0)+夕顔(0)の子ども枠で本人と配偶者が同じ結婚枠が子どもも1人しかいないのに2つあるという点が不審だが… 属性を見ると,前者はTOOYOUNGWIFE|ERASEDYOUNGWIFE,つまり,ZTYW婚,後者はERASEDZEROGHOST|GOODSIBLINGとなっている.つまり,「ZTYW本人を参照する仮ノードが存在する元の親枠」だ.

この2つの結婚枠は元々は一つのものだったのだから,「拡張子ども枠」のようなものでないことは明らかであり,siblingsを持っていないのも当然のように思われるが,このようなエラーはこれまで見たことがないような気がする.ZTYW婚というのはそれほどレアなものではないので,もっと頻発してもよさそうな気もするのだが…

最終的にはこのZTYW婚は消えてオリジナルのMARGBOX:#343だけが残り,玉鬘は夕顔の直下に配置されている.mergeUpperSegment→ Bobject::MoveNeutral の問題に戻ろう.下図で赤く塗りつぶしたところはZTYW婚で,この図面だけでもかなりの個数が使われている.

image

ZTYW婚の位置は不定なので,親ノードと同一セグメントにするのが難しいため,強制的に親ノードを隣接ノードの接触位置まで移動するという便法を取っているのだが,そのような強行手段(小細工)を取らなくても事実上問題は解決されているように思われる.この解法はひとまず保留として様子を見ることにしたい.しかし,これ以外でも検査モードで実動作が起きている可能性があるので,点検が必要だ.

予防措置としてTRIBEBOX::CheckMaximalSegment実行中はOnCheckMaximalSegmentが立っているので,このフラグがONのときは停止するようにしておこう.OnCheckMaximalSegmentを拡張してTRIBELIST::CheckMaximalSegmentの範囲まで広げておく.Bobject::MoveNeutral,Bobject::RelativeとBobject::originateでこのフラグをチェックするようにした.

すべてのオブジェクトの固有データ部を完全に殺菌(ゼロクリア)する

条件コンパイルをフィックスするときの手続きにミスがあったため,ファイルをクローズしたときのシステムのクリーンアップに失敗していたが,ようやく保健所の調理場検査にパスできる状態になった.理想的には存続するすべてのオブジェクトの固有データ部を完全に殺菌(ゼロクリア)してしまうことが望ましいが,一部環境パラメータを保持しているオブジェクトがあり,そのようなものに関しては別途措置が必要になる.環境パラメータと一般データを形式的に識別することはできないので個別のクラスで対応することになるが,保全の必要な環境パラメータであることを明示するためにこれらを一度ローカル変数に退避しておいて,処理的には全領域をゼロクリアするというのがわかり易いのではないかと思う.保全されるべき環境パラメータには以下がある.

  1. long BASETABLE::tablesize テーブルの物理サイズ
  2. HWND COUPLING::hwnd ウィンドウハンドル
  3. long DATALIST::datasize リスト要素のデータサイズ
  4. QUICKDB KAKEIZU::carddata, marriage
  5. CSize TREEVIEW::WindowSize 親ウィンドウサイズ,物理単位 CPoint WindowPost ノーマル画面で親フレームの左上点物理座標 CPoint PreviewPost プレビュー画面で親フレームの左上点物理座標

NODULE::signatureという変数は使われていないので廃止する.

ZTシステム構成図7.BAD2.ZELを開いて,LIST::nextで停止する.(!find(listnod))が起きている.TribeRelocationでSIMPLEGRAPH:BuildTightHasseDiagramを実行しているところだ.障害ノードは#2408 NODELISTでSIMPLENODE #14742の次の要素を求めているが,#14742がリスト上に存在しないというエラーになっている.このリストはかなり複雑な構造になっているが,該当要素は存在していないようだ.重婚同類検定では枝グラフを3つ使っている.

SIMPLEGRAPH::TightenHasseDiagramは主語のグラフの他,引数でもう一つのグラフtajugraph3への参照が渡されている.問題の箇所にはこの2つのグラフの節点リストを取り違えるというミスがあった.重婚グラフ検定の論理は一度総点検する必要がある.もし,複数のグラフを併用する必要があるのであれば,2つのグラフを引数で渡して,関数自体はクラスの静的関数とした方がよいと思う.一応現状で正しい論理が実装されているという仮定で修正を入れてみよう.以下の関数を改造した.

  1. BuildTightHasseDiagram
  2. TightenHasseDiagram
  3. sortComponentList

「完全参照リスト管理をサポートする」というオプションをOPTIONSからSPECIFICATIONに格上げし,REFERENCELISTCONTROLとリネームした.⇒裏オプションでも動作することを確認しておこう.⇒参照リスト管理は現在,黒子としての存在に留まり表では活動していない.

SUPPORTSIMPLEGRAPH「SIMPLEGRAPH抽象グラフ検証系をサポートする」というオプションで抽象グラフ検証系を止めてみる.⇒実装した.グラフ検証系なしでも思ったより動作している.ZTシステム構成図7.ZELは難なく開けた.ただし,一部の検査論理は停止している.抽象グラフ検証系でサポートしている機能には以下がある.

  1. GENEBOX::CheckInverseCycle 危険対枝グラフの循環検定
  2. TOPOLOGY::TestInevitableMultiZero 重婚同類グラフの循環検定
  3. TRIBELIST::ShiftDirectAbsolute 絶対世代番号に基づきカードシフト
  4. TOPOLOGY::FindJikusenAncestor 血糖軸線図のサポート
  5. TOPOLOGY::MakeLinearEdgeList 系列枠の全順序を決定する

SUPPORTSIMPLEGRAPHオフのバージョンで渋沢一族8.ZELの全体図を#211 大川平兵衛の息子で開こうとして,TRIBEBOX:GetAlternativePrimeNodeで停止した.系列優先ノードが決定できない.この系列は#2501 先祖 #950 渋沢 長登(宗安)で,最初は#1038 尾高 平九郎を優先ノードとする親元関係で決定していたはずなのだが,#956 渋沢 やへの婚姻関係に切り替わっている.

最初のGetAlternativePrimeNodeで切り替わり,2回目で失敗しているという構図だ.何が変わったというのだろう?⇒いや,変化していないために停止している.これはこのカードがTRIBEBOX:DecidePrimaryNodeでリジェクトされていることを意味する.つまり,DecidePrimaryNodeとGetAlternativePrimeNodeが相反している.⇒DecidePrimaryNodeに逆婚姻関係を導入して解決した.

どうもDecidePrimaryNodeのできが悪過ぎる.今度は源氏:明石中宮で停止した.この関数は全面的に書き換えた方がよい.この関数では現在の優先ノードのチェックだけを行い,不可のときは自分で代替候補を探すのではなく,GetAlternativePrimeNodeに任せるようにした方がよい.優先ノードチェックの要点は,①GetRealnodeで優先実ノードを確保できるかどうか?②主系列と従系列の基準ノードに対する位置関係,③系列種別が合っているかどうか,だけでよい.

CARDLINK::selectflagオフという障害がぶり返している

BUG20-12-25 17-30-25.ZELの全体図を#5 pagesetupで開いて,TRIBEBOX::GetMajorTribeChainでASSERT_NEVER(upper == major) 「主系列チェーンがループしている」が起きる.このサンプルはZTシステム構成図7.ZELでカード削除を複数回実行→UNDO→REDOを繰り替えして発生したものだ.

障害が発生しているのはTRIBEBOX #7837,先祖#670 familytree,優先#2083 noduleだ.TribeRelocationのステージ【7.9】仮ノード消去と両手に花を適用して多重カードを削減の段で,TOPOLOGY:CheckAbsorbMarriage→TOPOLOGY::CheckAbsorbMarriageを実行しているところだ.この処理は「人名ノードpersonの右枝リストの内容をすべて本ノードの右枝リストに移転する」というもので,多重カード削減の一手段として実行されている.

この関数では系列優先実ノードの切り替えが常套的に起こっているようだが,好ましくない.多重カードの削減手段は複数あるので,あえて系列優先実ノードを対象とする必要はないと考えられる.⇒NAMEBOX:AbsorbMarriageの冒頭で対象ノードがIsPrimaryRealのときはゼロ復帰するようにした.⇒結果,多重カードも発生していないようなのでこれでよいのではないかと思う.

上記サンプルを開いてアプリ終了で,MARGBOX::MargPointOffsetのエラー(IsTooYoungWife())で(!upper || !person)が起きる.⇒フェーズがTOPOLOGICALSORT以下ではゼロ復帰するようにした.この後,NAMEBOX::RestoreExtractBoxで系列枠不在で停止した.この関数はINITIALSTATEではゼロ復帰するようになっているが,CHAOTICSTATE(INITIALIZED)でも同様とする.

ファイルを開き直したとき,CARDLINK::selectflagがオフになっているという障害がぶり返している.これは2020/12/09に確認された問題で,原因はファイルをクローズしたときselectcardlistがクリアされていないため,対策として仮想関数Cleanを導入して完全に解決しいていたはずだったのだが… しかし,現行版の実装は予定していたものより,かなり後退しているように見える.オプションをフィックスする手順で何か失敗していたのではないかと思われるが,ともかく修復を試みることにしよう.まず,現状を見てみることにしよう.

  1. NODULE::Clean 仮想関数で仮想関数cleanを呼び出している
  2. NODULE::clean フラグを2つクリアしているだけ
  3. COUPLING::CloseFamilyBase Clean関数の実行
  4. COUPLINGクラスの共通ヘッダ:COMMONHEADERSHORT
  5. KAKEIZUクラスの共通ヘッダ:COMMONHEADERSHORT
  6. NODULE::doClean 純粋仮想関数
  7. KAKEIZU::doClean cleanの呼び出し
  8. nodule::doClean 固有データ部を0クリア
  9. doCleanの呼び出し元:TRASHCAN::ReuseWaste

Clean仮想関数は「接続されたすべての下流オブジェクトをクリーンアップする」という目的で20201210に導入され,20201216にFIXしている.しかし,現状はまったくそのようなものにはなっていない.原因としては,「Disposeの設置を義務化@20201211」というオプションと交叉してしまったことが考えられる.このオプションは廃止されているが,そのあおりで必要なロジックが消滅してしまったのではないだろうか?⇒復元して動作するようになったが,おそらく以前のロジックは元々動作していなかったのではないかと思う.doCleanは固有データ部をまるごとゼロクリアする関数だが,その中で使っているdataptrとdatalenという2つの関数が仮想関数になっていなかった.

これでほぼ問題なく動作するようになったが,完璧であるとは言えない.doCleanは自クラスの固有データ部を完全にゼロクリアすることができるが,noduleクラスから派生したクラスからさらに派生したクラスの場合には,それぞれの固有データ部は縞模様で接続し,必ずしも連続なものにはなっていない.つまり,それぞれのクラスごとにdoCleanを実行する必要がある.ZTの骨格木を構成するクラスの中ではこのようなクラスには以下がある.

  1. CARDTABLE→ BASETABLE→ ARRAY
  2. GENELIST→ NLIST→ LIST→ DATALIST
  3. LIST→ DATALIST
  4. nlist→ LIST→ DATALIST
  5. TREEVIEW→ Bobject
  6. MARGTABLE→ BASETABLE→ ARRAY

doClean自体は仮想関数なので,nodule::doCleanを呼び出せばよいだけなのだが… ここまでやらなくても実行上はほとんど問題ないと思われるが,一応実装しておこう.上記にはテンプレートクラスなども含まれているが,このようなクラスでもdataptrとdatalenは正しく動作しているだろうか?⇒問題なさそうだ.

ZTシステム構成図7.ZELを開いて,「画面に合わせてズーム」を実行してTREEVIEW::GetAutoZoomRateで停止した.(!WindowSize.cx || !WindowSize.cy)が起きている.ただし,再現しない.⇒先に別ファイル(ZTシステム構成図7.BAD.ZEL)を開いた状態から,開く→ズームで再現するようだ.これは上記の修正に関係あるかもしれない.WindowSizeは「親ウィンドウサイズ,物理単位」なので,アプリ起動時に取得したものだろう.TREEVIEW::doCleanではTREEVIEW:Clearを呼んでおくことにしよう.

ZTシステム構成図7.ZELを開いて,次にZTシステム構成図7.BAD.ZELを開こうとして,Bobject::getdrawsizeでエラーになった.(coordinate() == ABSOLUTE)で停止している.PHASEはINITIALSTATEだ.TREEVIEW::Clearでgetdrawsizeしている.getdrawsizeはBobjectの関数で,相対座標系であることを前提としているので,TREEVIEW::Clearでリセットするのが筋だろう.⇒ダメだ.まだ同じエラー(!WindowSize.cx || !WindowSize.cy)が起きる.⇒doCleanが思ったような動作になっていない.この関数を仮想関数化するというのが間違いなのかもしれない.いや,むしろ,dataptrとdatalenを仮想関数化したのが間違いなのではないか?

固有データ部というのは,そのクラスに固有の位置とサイズを持っているのだから,仮想関数化するというのはまったく意味がない.逆に言えば,個別クラスごとに実装する必要がある.共通ヘッダ部でdataclearという関数を用意することはできるだろう.doCleanは共通ヘッダ部には関数宣言だけを置いて,個別クラスに実装を義務付けるというのがわかり易いのではないか?たとえば,こんな感じだ.

void dataclear(void){ memset(dataptr(), 0, datalen()); }

この定義は現行ではnodule::doCleanの定義とまったく同一だ.doCleanの実装例を示すと以下のようになる.

void MYCLASS::doClean(void){
  dataclear();
  BASECLASS::doClean();
}

既存の仮想関数にcleanというのがあるので,dataclearではなく,datacleanとしておこう.ほとんどの場合は,cleanはdatacleanを呼び出すだけで済むのではないかと思う.方式的にはこれで正しいとは思われるが,数あるnoduleの派生クラスでdoCleanが未定義になってしまう.仮想関数としてのdataPtr, dataLen, dataCleanがあってもよいのではないか?その上で,

nodule::doClean(void){ dataClean(); }

とすればよい.このようにすれば,noduleから直接派生したクラスでは何もしなくて済む.cleanという関数はdoCleanを呼び出すのが正しいのではないだろうか?もちろん,コンストラクタから呼び出される場合は段階的に処理されるので,そのクラスの固有処理をやればよいのだが… いや,これだけではまだ不十分だ.TREEIEW→Bobject→noduleの場合, Bobjectにも固有のdoCleanを実装しないと,dataCleanが実行されてしまう.BASETABLEも固有データ部にtablesizeという不変パラメータを持っている.また,longtableというクラスはかなり融通の利かないクラスでサイズは固定でそのサイズも持っていない…

売春処女プアプアが家庭的アイウエオを行う

完全参照リスト管理とUNDOシステムが共存する体勢を確立することは,参照リスト管理を恒常的なシステムの一部として仕様化するための第一歩だ.「UNDOで参照リスト管理と参照カウントの不一致が発生する」という問題が起きているが,いよいよ大詰めの段階に差し掛かった.これを解決できればZTは「超クリーンなシステム」に変容するための準備が整ったことになる.「超クリーンなシステム」とは「グラスクリーンな世界」であり,ZTが追求する誰にも踏まれていない純白な処女雪に覆われた「スノーホワイトの世界」である.

プログラミングの世界には「オリジナルバージョンに戻る」ことを意味するvirginize(処女化/童貞化)という用語が存在する(わたしも知らなかった).virginizeとは具体的にはバックアップに戻ること,ないし条件コンパイルマクロをオフにして修正前の状態に戻すことに相当するが,基本的には「修正が取り返しが付かない程度に間違った方向に進んでいるとき,それまでの経緯をすべて捨てて振り出しに戻ること」と解釈するのが妥当である.実際,プログラミングでは「これを修正してすべてのバグを取り除くより一から作り直した方が早い」という局面があることは広く認知されている(最初にそれを言い出したのはIBMだ).

いま出ている障害の一因としてTRASHCAN::ReuseWasteで廃棄オブジェクトをリサイクルするときの初期化が不十分というより,まったく実行されていないという問題が出てきた.これを解決するために,新規メモリブロックを使い始めるときと同様に固有データ部を完全にゼロクリアし,すべてのスロットを空にすることにした.これは言ってみればrevirginize(処女膜再生)の施術に相当する.プログラマというのは言ってみれば白雪姫に仕える七人のこびとであったのかもしれない…

ZTシステム構成図7.ZELの全体図を#1 couplingで開き,この基準カードを削除すると,UNDONODE #115359の持っているNODEREFLIST #115361がMARGLINK #311に乗り移るという事象が起きている.これは,カード削除コマンド実行後のUNDOBASE::CommandEndで起きている.何が起きているのか?実に興味深いところだ.

障害はUNDOBASE::SetUndoList→ UNDOBASE::SetUndoList→ ReferenceControlで起きている.UNDONODE #115245の枝2にUNDONODE #115359への参照を設定しようとしているところだ.#115359は参照リストを持っていないので,新たに生成された参照リストがNODEREFLIST #11536だ.このオブジェクトは再生品だ.⇒しかし,NODEREFLISTがリサイクルに回っているということ自体おかしい.NODEREFLISTはENDOFAPPLICATIONフェーズ以外では削除されないことになっているからだ.この時点ではMARGLINK #311はPROLONG状態でNring上に存在する.参照リストは持っていない.

いや,ちょっと間違えていた.SWO(SearchWrongObject)の条件設定でANDとすべきところがORになっていた.⇒障害はやはり,UNDOBASE::UndoCopyで起きている.ここではShadowから実ノードにオブジェクトイメージをコピーしているので,確かにそのようなことは起こり得る.実ノードが参照リストを持たないとしても,イメージにはそれが残っている可能性はある.というか,実際そのような動作になっている.しかし,上記のようにNODEREFLIST #11536はリサイクルされているので,ダブリが生じたということだろう.

問題はNODEREFLISTがゴミ箱に入っているという点だ.#11536はリサイクル時に付番されているので,それ以前の通番は上書きされてしまっている.⇒#2726だったようだ.⇒nodule::~noduleの末尾に「countゼロの参照リストを削除する@20201214」というのが入っている.countゼロの参照リストは保持していても意味がないので始末するという趣旨と思われるが,PROLONGされたノードの場合は除外されなくてはならない.⇒対処した.これですべてのエラーは解消した.つまり,完全参照リスト管理とUNDOシステムの共存は実現された.

ただし,UNDO/REDOを実行したとき,~noduleで参照リストカウントが残っているという現象がある.~noduleの段階ではすべての参照はクリアされていることになっていたはずだ… ⇒現状ではCLEARTABLEフェーズでは参照管理を放棄した状態になっている.CLEARTABLEを含むすべてのフェーズで完全な参照管理を実現するのはまだ先の話だ.

UNDOでは参照カウント不一致のエラーはまったく表示されないのに,REDOではかなりのエラーが出ている.UNDO/REDOには直接関係しない描画要素(NAMEBOX, MARBOXなど)なので放置でも実害はないが… カウント差はほとんど1なので押さえることも可能ではないか?

REDOではMARGLINK:#311とCARDLINK:#689が削除されている.これらからの参照が処理されていないのではないか?⇒どうもそういうことのようだ.⇒この問題を解決するには,やはり参照管理を徹底する以外ない.まず,~noduleで参照リストカウントゼロの場合に(UNDO保全オブジェクト以外では)参照リストを削除するとしていたのを廃止し,参照リストは原則として「死んでも付いて回る」ものとした.

ただし,ゴミ箱に入っている参照リストの親オブジェクトがリサイクルされる場合には削除される.~NODEREFLISTでは参照リストカウントが残っている場合には停止するようにした.この結果,CARDLINK #689[6]→ NAMEBOX #690の参照が残っていることが判明した.

しかし,CARDLINK #689[6]にはNAMEBOX:#243582が入っている.NAMEBOX #690はすでにゴミ箱に入っているが,CARDLINK #689は生きている.というか,UNDOで復活したものと思われる.これは何が悪いのか?CARDLINK #689が削除されたとき,NAMEBOX #690が参照解除されていなかったものと思われる.いや,かなりおかしい.CARDLINK[6]というのは参照ではなく,接続のはずだ.

image

!解けた!一日一個の禁令を破ってここぞとばかり,全量投下の勝負を賭けてみた.有り金を賭ける賭博師の心境だ.チョコレートパワーの威力だね.いや,最初の一個で解けたよ!エラーは全部きれいに消えた!

UNDONODE::UndoCopyでは参照カウントと参照リストカウントの差分を調整するため,復元されたオブジェクトの全参照スロットを対象にReferenceControlで参照リスト登録を実施している.問題はこのとき,「接続」と「参照」を切り分けなくてはならないという点だ.

通常はリンクされているオブジェクトを見れば判定できるのだが,いまの場合,NAMEBOX #690はすでに死亡しているため,接続モードを確認することができない.このような場合にはそのノードの存否をチェックするようにして解決した.このスロットはこの後,UNDOBASE:RestoreShadowでアクティブな現物ノードによって置き換えられている.漏れを防ぐために~noduleでNODEREFLISTの参照リストカウントがゼロになっていることを確認するようにしておこう.

上記と同様の操作(#1削除→UNDO→REDO)でNODEREFLISTのデストラクタで参照リストカウントの残留により停止した.親オブジェクトはすでに死亡しているNAMEBOX #322244でCARDLINK #689からの参照が残っている.#689[6]は空になっている.

CARDLINK[6]は接続スロットだから,参照リストに入っていることがそもそもの間違いだ.⇒どうもかなり難しい話になってきた.NAMEBOX #322244はリサイクルされた再生品という以外は特に問題のないオブジェクトだ.DELETEDはEXISTINGになっているので,弾くこともできない.いや,確かにおかしいところはある.このノードの親はNAMEBOXになっている.確かに,NAMEBOXはリスト構造で保持されているので,親がNAMEBOXというのは当たり前だが,CARDLINK[6]にリンクするためには接続しなくてはならないはずだ.

UndoCopyではそうなっていないのは仕方ないとしても,事後の調整というのがあるはずだ.本当は,その事後調整を実施したあとで,ReferenceControlの調整を行うべきなのではないだろうか?多分それしかないような気はするが,問題はUNDONODE::UndoCopyを呼び出しているUndoRestoreはUNDOSYSTEMの関数だという点だ.つまり,アプリ依存コードなので,UNDOBASEでできる範囲がより狭まってしまうということになる.⇒UNDONODE::SetNodeRefListという関数を作ってUndoRestoreから呼び出すようにしてみよう.

いや,もっといい場所がある.CountupReferenceという関数がある.ここでは参照カウントのインクリメントを実施している.それと同時にReferenceControlをやればよい.というか,ここではSansyoの取り直しを実施するというのが一番適切なのではないか?⇒完璧だ!傷跡も残らないくらいの完璧なサージェリィ,これ以上付け加えるものも引き去るものもないという感じになってきた.バックアップを取っておこう.

以下のブロックを選択して最初のカードを削除してエラーが発生した

image

@4 treeviewを削除で再現できる.PAIRBOX::CalcPairBoxでエラーが起きている.「NOCOMMONPAIRノード対は端点共有束で唯一でなくてはならない@20180915」というエラーだ.NOCOMMONPAIRは「終点がgoodsonのノード対は最大区間を除き端点共有不可@2018-09-04」とされている.このトラブルは既出だが,もう少し整理する必要がある.エラーを無視して描画は可能.出口検査はパスしているので,ここでは停止しないようにしておこう.

▲NOCOMMONPAIRノード対の論理を整理する.端点共有ノード対の場合は,NOCOMMONPAIRをつねにリスト先頭に配置するというのがわかり易いのではないか?

複数回のカード削除の後,UNDOで「フェーズのイレギュラーな遷移」が起きた.TOPOLOGICALSORTからINITIALIZEDに遷移しようとしている.⇒COUPLING::TopologicalSortの冒頭でSetPhase(TOPOLOGICALSORT)の後にTREEVIEW::SetDispParmを実行している.これはAUTOCHANNELのときの既定チャンネル数を設定するためだが,SetDispParmの中でフェーズの切り替えが発生している.これを避けるために,この操作はRESTOREDCIRCULATIONの前に移動する.

同様の動作テスト中,Bobject::setparentでエラーを表示しようとして例外が発生した.bprintfの引数に修正ミスがあった.

仮修正をフィックスしておこう.3件ある.

  1. UndoCopyでSansyo動作を実行@20201222 → 廃止
  2. オブジェクト削除で参照カウントゼロを確認@20201223 2箇所
  3. Bobject:initializeでCleanSlotする@20201223 → 廃止

仮修正17箇所,#ifdef ,#ifndefも一掃した.