12243点のCollatz4000X.ZELが解けた!

何か打つ手はあるのだろうか?現在のアルゴリズムで,時間は掛かっても最終的には解けているのかどうかを知りたい.Collatz12146.ZELを放置して,最後までやらせてみたい.どのくらい時間が掛かるのか予測も付かないが,もし,最終的にはつねに解けるというのであれば,アルゴリズムの正否の問題ではなく,効率の問題ということになる…

「結婚枠だけをセグメント検定の対象とする@20230125」というオプションをもう一度試してみた.これは結構おもしろいかもしれない.これだけでかなり高速化する.Collatz4000X.ZEL(12243点)を開いて,系統並び替えを154秒でこなすことができた.3分掛かっていない.スレッドとスレッドの間が,相当開いてしまっているが,これはこれで対処できるのではないだろうか?

image

あるいは,処理を二段階に分けて,①結婚枠のみで概略ソート,②人名枠を含めた詳細ソートのような手順も考えられる.というか,たとえ結婚枠だけのソートであっても,これほどスプリットが出るというのはおかしいので,まず,そこに注力すべきではないだろうか?⇒「結婚枠だけをセグメント検定の対象とする」と言っても,実際には人名オブジェクトもすべて検定の対称となっている.修正箇所は,TRIBEBOX:JoinAdjacentBoxで人名枠が接触している場合のみマージしていたのを,同一ボックス内では無差別にマージしているという違いだけだ.従って,人名枠が結婚枠内でどれほど開いていても圧縮が掛からない.

暫定的にTRIBEBOXにフラグを設けて,二段階処理を組み込んでみよう.⇒組み込みは簡単に終わったが,53直系血族図.ZELではループを30回回すまでフラグが立ってこない.つまり,「結婚枠のみ検定」でもほとんど動作には変化が見られないということになる.Collatz4000Xなどではおそらく,かなり顕著な効果はあるものと思われるが,問題が解決しているかどうかは走らせてみないと分からない.⇒解けた!結婚点一致と衝突検定を独立にループしてerrcntとcollisionがゼロになってからセグメント検定に掛けるという手続きに変更してみた結果,134回ループして解を得ることができた.従って,アルゴリズム的には閉じていると言っても間違いではないと思う.

そうなると,あとは効率の問題ということになる.もう少し,というかもっともっと改善の余地はあるのではないだろうか?

image

ともかく,この版を一応バックアップしておこう.


昨日は1000人に近い来訪者があった

午前0時を回るところだが,スナップショットを取っておこう.1000に近い来訪者があったということになっている.この理由は不明だが,かなり驚異的な数字だ.

image

今日のペースはまた通常に戻っているようなので,一過性の現象だったようだが… 何はともあれ,数字が上がるというのは悪い話ではない.⇒今日も順調にカウントが上がっている.午前5時15分で343人の来訪者があった.今日中にトータル95000人を超えることは確実なので,一週間以内に10万人の大台を超えそうだ.⇒完全な勘違い!343人というのは昨日の数字だ.今日は午前6時半でまだ,46人しかいない.

実験的に「結婚枠だけをセグメント検定の対象とする@20230125」を試してみたが,無理そうなので解除.⇒試みに「MakePairListCleanを一時止める@20220215」を解除してみたら,解けてしまった.

image

MakePairListCleanという処理は,ノード対の配置を最適化するための操作であり,今の場合にはノード対はまったく使われていないはずなので,このような変化が起きているとすれば,「基本的に必要な処理」がノード対処理と分離されずに混交していたということになる.この部分を切り出して,基本処理に統合する必要がある.まず,ノード対が生成されていないことを確認しておこう.ノード対の発行数をどこかで確認できるだろうか?⇒Nringには生成されたすべてのオブジェクトが登録されているはずだから,CheckNringCountでカウントできるのではないか?⇒オブジェクトの総数32460のうち,ノード対はゼロだ

MakePairListCleanはTRIBELIST::MainExprerimentに入って始めて呼び出される.この関数は2度呼び出されているが,ループしないで一度で脱出している.ただし,内部にもループがあるのでそこでループしている可能性はある.この関数の中では複数の処理が実行されているが,何かしらの作用がある場合にはconflictが立ってループになるはずなので,ループしないで抜けているとすれば,「何事もなかった」ということになってしまうのだが,だとすれば動作が変化しているという理由が立たない.おそらく,どこかで,暗黙に何かが操作されているのだろう.

MakePairListCleanの内部で実行されているBuildCompleteTreeを止めると無限ループしてしまうので,この操作は必須処理になっているが,conflictは起きていない.BuildCompleteTreeは,CheckMultiCardsとStackTribeGeneを実行しているだけだ.⇒MakePairListCleanを暫定修正してBuildCompleteTreeを実行しただけで抜けるようにしてみたが,解けている.つまり,BuildCompleteTreeを実行することはノード対のあるなしに関わらず必須ということになる.

いや,MakePairListCleanを実行するか否かの問題ではない.単にCompleteTribeBoxの収束が遅いというだけの問題だ.実際,REDLINEを32まで引き上げたら正常描画できた.Collatz12146.ZELを読み込んでみよう.ループカウント上限32では,結婚点不一致84件, 衝突346件で収束には程遠いものがある.いずれにしても,三極検定をもう少し強化しないことにはどうにもならない問題であるようだ.とりあえず,ここで一旦打ち切って出直すことにしよう.

CompleteTribeBoxで結婚点不一致と結婚枠衝突を先行処理し,これらをクリアしてからセグメント検定に入るように修正して,一発でループを抜けられるようになったが,セグメント検定で動作不良が起きている.つまり,先祖ノードの「5」とその子どもの「53」が孤立した状態でループを脱出している.明らかにこれはセグメント検定に欠陥があることを示している.先祖ノードは#84230で,当初のセグメント番号は1.最終的にすべてのオブジェクトのセグメント番号は1724になる.

CompleteTribeBoxのループ出口でIsTribeCompleteの再検査を実行すると,結局31回ループするという動作になってしまう.少なくとも今の論理ではこれだけ回らないと解けないということのようだ.これは,セグメント検定で前詰めするとき,結婚点不一致が発生してしまうことが原因と考えられる.やはり,結婚点を維持しながら前詰めする方法を編み出すしかないのではないだろうか?⇒MAXIMALGRAPH:MoveSegmentBlockではBobject::MoveNeutralでオブジェクトを移動しているためと考えられる.

MoveSegmentBlockでMoveNeutralを使わないように仕様変更してみたが,31回ループという状況にはまったく変化がなかった.⇒「コラッツ特注版ではつねに中吊り@20211217」をオフにしたところ,HorizontalSegmentでまったく停止しなくなってしまった.⇒とりあえず,この論理は元に戻すしかなさそうだ.

午前9時半の訪問者数が590を超えている!

どうなっているのだろう?まだ午前6時43分というのに,今日の訪問者数が340を超えている.カウンタが壊れてしまったのだろうか?

image

少なくとも時計は壊れていない.3つのPCで同じ時刻を示している.いや,BlackHawkの時計は1:39になっている.どうも,このマシーンはフリーズしてしまっているようだ.今日は動いているが,メイン機も朝になるとたいがいフリーズしている.どちらもリブートしないと動き始めない.つまり,どちらもほとんど棺桶に足を突っ込んだ格好でいつご臨終になっても不思議ではない状態だ.中古のノートを一台送ってもらうことになっているが,ロックが掛かった状態になっているので,果たして使えるかどうかも定かではない.

一週間ほどコンソール卓から離れてしまっていたので,ペースが戻ってこない.当面の課題はコラッツ木ジェネレータの出力ファイルを安定に表示できるようにすることなので,その方向で作業することにしよう.「コラッツ対応版応急措置@20211219」と「コラッツ対応版応急措置@20211223」という2つの応急措置はとりあえず,解除してある.現在テスト用サンプルとして開いている「53直系血族図.ZEL」は1931点という,それほど大きくはないが,小さくもないファイルだが,REDLINE=25という設定でレッドラインオーバーが発生する.

REDLINEの上限をもっと大きくすると正常描画できるようだが,25でもすでに十分大きいと思われるので,この状態でシューティングを試みることにする.上層で孤立点が発生しているというのが不良の原因だ.

image

さて,どう対策すればよいだろう?明らかにこれは,「極大セグメント検定」と呼んでいる「圧縮処理」の機能不全であることは確かなのだが,どうやって強化すればよいか?少し考える必要がある.孤立しているのは,先祖ノードの「5」とその子どもの「53」だ.「5」は子ども1人,「53」の下には141, 2261, 35, 565の 4人の子どもがいる.この先祖グループの2人を除いた部分はまともな配置になっているのに,これだけが孤立していて,それが矯正できないというところが不審な点だ.

理由として考えられるのは,「5」と「53」という2つのノードがどちらも,結婚枠という殻を持たない剥き出しの状態になっているという点だ.⇒いや,それも違う.少なくとも「53」はそれを囲う結婚枠の中に入っている.「5」を世代0とすると,「53」の子ども世代である世代2には上記の子ども4人が入った結婚枠が一つあるだけだ.これを「53」の直下に配置すればそれで完了というはずなのだが…

▲系図画面下部のステータスバー:基準カードの値が0になっている.系統並び替えを実行しても変化しない.現参照カードの表示もおかしい.⇒ダンプでは正しく表示されている.

レッドラインオーバーが発生した時点で,結婚点不一致が8件発生.

▲ShowUnderWearが動作しない.というか,画面が出てこない.

どうも,かなり悪い.最後のラウンドで結婚点不一致を処理した直後にブレークするようにして,下図のようになった.

image

三極がバラバラでまったく協調動作していないように見える.衝突検定の直後にブレークしてみたが,状態はほぼ同じだ.結婚点の一致検定を実施する前の状態はまだ,大分まともな状態になっているのだが…

image

極大セグメント検定と結婚点一致検定のどちらが悪いかというのも一概には言えない.いずれにしても,設計上の致命的弱点がどこかにあるというしかない.「MAXIMALGRAPHを共有する@20220414」と「MAXIMALGRAPHを縮小する@20220221」という2つのオプションを解除してみたが,動作には基本的な変化はない.セグメント検定から人名枠を除外して,結婚枠だけの検定を行おうとしたが,うまくゆかなかったので,便宜的に結婚枠内のすべての人名枠に結婚枠と同じセグメント番号を付与するようにしたところ,描画はできたが,完全に緩んだ「loose box」になってしまった.まぁ,当然と言えば当然なのだが.というのは,そのようなloose boxを締めてタイトにするというのが,セグメント検定の目的であったのだから…

image

これはどうも,一筋縄ではゆきそうもない…

レコード番号と参照番号の切り分けをクリア

レコード番号と参照番号の切り分けという課題はほぼクリアできたのではないかと思う.2021年の末にコラッツ対応版を初めた時期に,「コラッツ対応版応急措置」というのを何件か入れている.この辺りが今どうなっているのかを調べてみよう.作業を開始したのは,2021/12/11だ.この頃は,コラッツ木サンプルのデータ入力をやっている.発生していたエラーには以下のようなものがある.

  1. 先祖ノード絶対世代番号不一致
  2. LINKTABLE::MargTakeChild 結婚リンクが消えていない!⇒このエラーは,子ども氏名を消去した時に発生する
  3. 選択状態が残ってしまう,他のカードを選択しても消えない
  4. ページ設定パネルが開けない,印刷もできない
  5. 先祖並び替えが動作しない,ボタンが効かない,ドラッグ移動もできない
  6. インポート・エクスポートが動作しているバージョン:Ver 2.2.0.030 Rel 2021-03-17
  7. 最終版からリスタート,2021-04-10
  8. 氏名欄で左右カーソルキーを押すと入力文字が消える
  9. カード画面上部ツールバーの氏名欄で名前の後ろに余分な空白が入る
  10. カード数1000点のコラッツ木のインポートに時間が掛かる,1点処理するのに25秒掛かっている
  11. CARDLINK::makelinkで時間が掛かっている,UNDOは止めておく
  12. ZTYW処理が作動している,暫定的にgoodsonを空としている
  13. 人名枠高の計算がおかしい,フォントサイズを大きくすると人名枠が世代境界を超えてしまう
  14. コラッツ1000.zelを開くのにかなりの時間が掛かり,マネージド・デバッグ・アシスタントが作動して停止する
  15. 親の垂線が兄弟連結線から突き出ている
  16. TOPOLOGY::ClearTableで参照番号不整合が発生したとき ⇒ すでに止めてあるが,「コラッツ対応版応急措置@20211219」というリテラルで明示する
  17. NAMEBOX::Drawで人名枠高が世代高を超えた ⇒ コラッツ対応版応急措置@20211219」で止める
  18. MARGBOX::makeGoodSonの冒頭で「つねに中吊りする@20211216」でゼロ復帰 ⇒ コラッツ対応版応急措置@20211219」で対処
  19. TRIBEBOX::CheckAbsoluteGenerationで系列優先ノードのbreakupがオフという理由でエラーが起きている.「コラッツ対応版応急措置@20211223」で止めておく
  20. 基準番号を1~1000に振り直すことにした
  21. 垂直親子連結線が兄弟連結水平線を突き出ている.

Collatz12146.ZELというサンプルを開いてみた.名前の通り,12146点のデータが登録されている.系統並び替えだけ700秒掛かっているので,全体では15分くらい掛かったのではないだろうか?上の方でかなりひどいスプリットが起きている.多分,これが「極小反例の自動生成」ということに取り組むことになった原因だろう.

一番気がかりなところでもあるので,まず,これをシューティングしてしまいたいところだが,その前に,「コラッツ対応版応急措置@20211219」と「コラッツ対応版応急措置@20211223」を解除するところから始めよう.今後の作業の目標は,「コラッツ特注版をノーマライズして公開版に向けて統合する」ことだ.

コンパイルエラーが出ている.CARDDATA::refnumが存在しないというエラーだ.CARDTABLE::tablesizeという値でも不在が起きている.前者は多分「setRefnumはCARDBASEで実行する@20230107」に関係しているものだろう.暫定的にrefnumをRefnumに変更している.後者はBASETABLE::tablesizeを暫定的にtablesize2としためだ.元に戻しておこう

KAKEIZU::getmarriageでは参照番号を指定してデータの取り出しを実行している.レコード番号でアクセスできるようにした方がよい.⇒KAKEIZU::getmarriage/getcarddataでは,num ないし refnum という語を使っているが,これは参照番号ではなくデータベースのレコード番号である.参照番号はデータベース内部では「登録番号」と呼ばれている.紛らわしいので,引数をrecnのように書き換えておこう.

KAKEIZUのメンバー関数の中でnumないしrefnumを引数とする別の関数がある.これらもチェックしておこう.QUICKDBのcurnumもcurrecnに改めておく.savemarriage,savecarddata,makemargfile@もrecnに変更した.



Renumberではリンクを移動しない

参照番号のハッシュ化は,コラッツ対応修正により,登録カード数の上限が1000のオーダーから10000を超えるようになったことを直接のきっかけとしている.しかし,そのことによって,「リンクがどこに置かれていてもまったく問題なく動作する」というNシステムの最大の特長が著しく損なわれるか,ないし制約を受けるようになった.テーブルにハッシュでアクセスするためには,そのレコードはテーブルの固定位置に配置されることが前提となる.それと,Nシステムの「リンク配置の自由度」は明らかに背反している.

参照番号のハッシュ化が後戻りのできない決定であるとすれば,どこかで折り合いを付けなくてはならない.参照番号のハッシュ化はゼルコバの木の最大構成要素である人名テーブルと結婚テーブルに関わるものではあるが,それ以外の要素ではこれまでと同様,配置の自由度は損なわれてはいない.テーブル並び替えというのは一覧画面出力に関わるものであり,人名/結婚テーブルのレコード並びとは独立であるはずなので,人名/結婚テーブル上のリンクが固定位置にあったとしても,実行可能であるはずだ.⇒どうもlookupテーブルを作り損なっているようだ.⇒NAMESORT::SortNameSubで「lookupを前詰めする@20230113」の論理にミスがあった.⇒修正して動作するようになった.

基準ソートの実行で選択カード数不一致が発生する.TOPOLOGY:RebuildCardListではUndoStartFlagがオンのときには選択リストから選択フラグを再設定し,オフのときにはフラグから選択リストを再構築している.どちらの場合も,フラグのカウントとリストのカウントは一致しなくてはならないのだが… リナンバーを実施した場合には,選択リストは無効にしなくてはならない.⇒いや,そういう動作になっているはずだ.⇒CARDTABLE::Renumberが間違っているのではないか?lookupに格納しているレコード番号を書き直している.⇒「Renumberではリンクを移動しない@20230116」で収まったようだ.

58点削除→ 基準ソート→ Undo→ Undoで大量のエラーが発生する.親リンク不整合,カード参照番号の重複,子インデックスが負,親番号不一致などの後,どこかで例外を起こしている.⇒MARGLINK:CheckMargLinkのエラー出力に誤りがあった.

カード削除単独,ないし,他のタイプのソートとの組み合わせではエラーは生じないので,原因が基準ソートにあることは明らかだ.おそらく,それもリナンバーしていることが最大の原因と思われる.⇒確かにそのようだ.ということは,リナンバーをUndoしたとき,元の状態に戻っていないということなのだろう.

リナンバーでは「リンクを移動しない」ことになっているから,変化しているのはカードの参照番号だけだ.⇒カードの参照番号はCardNumListのリストアで復元できているはずなのだが,参照番号を参照しているすべてのリストが復元されているかどうかが問題だ.いや,そもそもCardNumListが保全されていないのではないか?

UNDOSYSTEM::BackupPointDataのSORTREFNUMにSetUndoListを実行するようにしたが,エラーは収まらない.CardNumListを生成しているタイミングが悪いのではないか?⇒確かにそのようだ.⇒NAMESORT::NameNarabekaeの入口でBuildNumListを実行するようにして解決した.多分これで,ほぼ問題は解決したのではないかと思う.まだ,何か見落としがあるだろうか?


参照番号のハッシュ化とリンク移動の自由は相反する

昨日の修正では,基準ソートでRenumberを実行し,その代わりにUNDOをリセットするという作りになっているが,基準ソート時にlookupを保全しておけば,UNDOできるのではないだろうか?仕様的にはまるごと廃止という方向に向かっているところだが,試しておいてもよいのではないか?⇒lookupでは復元できない.lookupにはリンクテーブルのレコード番号しか入っていないから,参照番号を保全したことにはならない.もし,やるのであればやはり,CardNumListを生成・保全するしかない.

仕様的には参照番号のリナンバーというのは廃止で決定なのではないか?もし,なんらかの理由でリナンバーする必要が出てきたら,その場合には,内部処理として独立に再構築する以外ないと思う.ただし,アプリとのインタフェース上参照番号は不可欠であり,一覧表のテーブルにその情報が記載されていなければそもそも動作しない.ただし,それを公開情報として表に出さないというだけだ.あと腐れのないように実装しておこう.⇒いや,CardNumListを復活させて,基準ソートでもUNDOできることを確認しておくべきだ.

仕様がぐらついているが,CardNumListの操作を復活させた.これで一応基準ソートを実施してもUNDOできるようになったが,エラーが発生している.カード削除とテーブルソートを複数回実行→ Undo→…でRestoreShadowのエラーが起きた.CARDTABLE::InsertLinkで例外が発生している.再現できるだろうか?

カードをブロック削除→ 基準ソート→ 氏名でソート→ 肩書でソート→ Undo→ Undo→ Undo→ Undo→ で再現した.カード削除の復元に失敗している.リンクを戻そうとした位置に先住者がいたためだ.recn=243.RestoreShadowではshadowの親番からレコード番号を割り出している.この操作は(元の状態を完全に復元するのだとすれば)正しいと思われるのだが… ブロック削除→ 基準ソート→ Undo→ Undoで再現できる.基準ソートの動作自体おかしいところがある.ソートしただけで,系図画面に表示されているカードが減ってしまう.設定は「親族図:直系血族図」で一覧画面はすべてのカードだ.

全カード数=307のうち,親族図には278点が含まれる.このうち58点を選択して削除すると,画面には37/249点のカードが残る.選択したカードはすべて親族図に含まれるものだから,278-58=220点が残りそうに思われるが,37点まで減ってしまうというのは,「直系血族図」であるためと考えられので,間違ってはいないはずだ.これを並び替えても表示カード数は変化しないが,Undoで7点にまで減ってしまう.これはかなりおかしい.基準ソートのUndoでやり損なっているのだろう.

これは,おそらく基準カードが変化してしまったためと思われる.⇒全体図ではこの事象は起きない.ただし,InsertLinkのエラー(先住者の存在)は発生する.レコード番号は246.⇒Renumberの中でリンクを移動(前詰め)していた.⇒Renumberではリンクの移動は行わないように修正した.これで一応解決したはずだが,エラーはまだ続く.

▲ブロック削除→ 基準ソート→ Undodeで,TopologicalSort→ MakeActiveListを実行して,(treeview->validcard != ActiveList->count)のエラーになった.validcard=249に対し,countは198のままだ.フェーズはDECOMPOSITIONなので,すでにフィルタリングは完了しているはずだ.⇒PDBのlookupのカウントが198しかない.これはかなりまずい.いや,こちらの数字の方が正しいのではないか?PDBのrecordcountは249になっている.なぜだろう?

元々のカード数は307点だ.それから58点削除しているので,249点でなくてはならない.198というのはどこで起きているのか?基準ソートの入口では249になっている.⇒RebuildCardListで変化しているようだ.UNDOSYSTEM::CommandEnd→ RebuildCardListで変化している.RebuildCardListの中でもう一度NameSortが呼び出され,その中でcountの変化が起こっている.ソートは_MAXITEMモードで実行されている.⇒確かにlookupを前詰めする@20230113という処理が入っている.これはカード削除などでできた空欄を前詰めするための処理だが,誤動作しているようだ.

どうも,かなりまずい設計になっているような気がする.というか,元々はそういう設計になっていたのだろう.参照番号=レコード番号という設計だ.確かに,リンクを移動しないで参照番号の付け替えだけを実施すれば,参照番号〰レコード番号という前提が崩れてしまうのは当然だ.やはり,参照番号のリナンバーというのはバッドアイディアというしかないような気がする.現行では,参照番号というより,ハッシュ値というべきものに変化してきているので,それに対応して変化するしかないのではないだろうか?

参照番号をハッシュ値として扱うというのはそれ自体は悪い発想ではないが,逆にNシステムの本来の思想である,リンクがどこに移動しても問題なくアクセスできるという理念からはかなり大きな後退であるような気もする.この2つを両立させることは不可能なのだろうか?

参照番号を永久コード化する@20230109

▲Undo処理中にRestoreShadow→ InsertLinkでエラーが出た.挿入しようとしている位置にすでにリンクが存在しているというエラーだ.⇒再現しないが,別のエラーが出た.

linkの参照番号は@233でこれからrecnの233が計算されたものと推定されるが,その場所には@233の別のカードが存在している.削除されたカードの参照番号は基準ソート以前のものと推定される.この状況で間違っているのはどちらのリンクと言えるか?

UNDOSYSTEM::RestoreShadowでは,レコード番号をshadowの親番から取っている.これは正しいのではないか?基準ソートのUndoが正しく動作していない可能性が考えられる.⇒従前では,NAMESORTはCardNumListとMargNumListを保持していたが,現行では完全に抹消されている.これらのリストには有効な参照番号の写しが格納されている.これは必要だったのではないだろうか?⇒確かに基準ソートでリナンバーしなければこのエラーは発生しない.

カードを70点選択→ 単点削除→ 単点削除→ 単点削除→一括削除→ 基準ソート→ Undoを実行して,UpdateCardBaseで基準ノード不在エラー(!basenum && CHARTTYPE != DISP_IMPORT)が起きた.⇒*basecardは0だが,*basepartには237が入っている.表示モードは部分図だ.呼び出し元のmZelkova::mUpdateBaseRefnumでは,BaseDispAll=3079,BasePartial=134が入り,それを引数で渡している.「参照番号が一致しない場合は更新とする@20180129」という論理で*basecard = 0に変わっている.⇒エラーを無視して続行で,上のエラーが再現した.

完全に抹消してしまっているので,かなり大変だが,CardNumListを復活させておこう.BuildNumListという関数も必要だ.「参照番号を永久コード化する@20230109」というオプションでパージした部分だ.⇒「参照番号を永久コード化する」というのは覆されてはならない決定である.抹消したコードを復活させることは不可能ではないが,必ずしもベストソリューションとは言えないのではないか?むしろ,この方向を突き詰めて,「基準ソートを実行しない」,「参照番号をユーザから隠蔽する」という方向に進むべきなのではないか?

基準ソートを実行すると,参照番号が1から始まる通し番号になるというのは悪くないが,たとえば,結婚参照番号はまったくユーザに公開されていないが,それによる不都合は基本的に存在しない.もし,仮にカードの参照番号をソートするのであれば,その時点ですべてのUNDOを一旦破棄せざるを得ないだろう.そこまでして,実現するだけの意味があるだろうか?基準ソートを実行するか否かは今のところペンディング事項だが,それを実行した場合にはUNDOをリセットするという作りにしておいた方が安全だと思う.⇒一応動作チェックしておこう.

基準ソート→ Renumberを実行→ UNDOリセットを実装して,基準ソートを繰り返したところ,CARDTABLE::Renumberで(max && max == maxrefnum)のエラーになった.maxrefnumは基本的に単調に増加すべきものと考えられるので,同じ値をセットする操作はイレギュラー動作とみなされる.しかし,Renumberは参照番号の振り直しなので,そうなることは避けられない.⇒Renumber時には,maxrefnumをリセットしておけばよいのではないか?⇒解決

部分図から全体図に切り替えて,どこかでハングしてしまった.⇒かなりまずいことになった.系統並び替え→ 三極検定→ 極大セグメント検定でハングしている.maxloopが302という大きな数字になっている.REDLINEの値は50だ.⇒どうも,UNDOのインタフェースが壊れてしまったようだ.VB側で,LineageSort→ ChangeBaseRefnum→ UpDating→ UndoStatus→ RefreshSub→ LineageSortで無限ループに陥っている.ChangeBaseRefnumで誤動作しているようだ.

Z.BaseRefnumが3079なのに対し,Zelkova1.TopologicalSortが171という数を返しているためだ.画面は部分図になっているのに,DispPartialMapがFALSEになっている.⇒DispPartialMapを強制的にオンにしたら,抜けてきたが,エラーパネルが出ている.

image

画面はいつの間にか全体図になっている.カード画面は「系図画面上のカード」で,全体図なら300が正しい.部分図なら271のはずだ.どうも,かなり話がこじれてしまったような感じだが,再現できるだろうか?⇒基準ソートとおそらくそれに伴うUNDOリセットの影響と思われる.起動→ 基準ソート→ 部分図⇒全体図で再現できる.UNDOチェーンのリセットだけでそれほどの影響が出るというのはほとんど考えられないのだが… 基準ソート実行後は,VBで基準カードの切り替えを行っているが,それほど致命的な操作とは思えない.戻り値の参照番号は176だ.障害は部分図⇒全体図の切り替えで起こるので,どこかで何かしらの齟齬が発生しているものと思われる.基準ソートを実施していなければ,この切り替えは問題なく通る.

何かパラメータが落ちているのだろうか?このエラーは初回限りで,別の操作を行ったあとの,基準ソート→ 部分図⇒全体図では(操作を反復しても),起きない.⇒ChangeBaseRefnumの出口でやっていた操作(Z.BaseRefnum,Z.BasePartial,Z.BaseDispAllの更新)をUpDatingの前に実行するようにして解決した.

TOPOLOGY::GetRecordを包括的に使うように修正した.

  1. 修正した関数:
  2. CARDTABLE::GetCardBaseRecord
  3. FAMILYTREE::GetRecord
  4. TOPOLOGY::UpdateRecordFunc
  5. TOPOLOGY::GetActiveRecord ⇒ 廃止
  6. PARTIALNAME::GetPartialRecord ⇒ 廃止
  7. FAMILYTREE::GetAllRecord ⇒ 廃止

▲テーブル並び替えでは「現ソート列」を管理しているが,Undo/Redoしたとき,アプリに伝達されていない.


Your Daily Epsilon of Math を毎日更新

Your Daily Epsilon of Math を毎日更新するのに結構時間が掛かっている.始業時バックアップの日付が0時を回って翌日の日付になってしまうようになった.もう,これが常態と認めるしかないだろう.参照番号とレコード番号の切り分けというのは,一通り収まったのではないかと思う.何か抜けているところはあるだろうか?とりあえず,フィックスできるところをすべてフィックスしてしまうことにしよう.「仮修正」が20箇所ある.⇒ここで,もう一度バックアップを取っておこう.

  1. lookupを前詰めする@20230113
  2. maxRecordではmaxrecordを返す@20230113
  3. lookupを自動更新する@20230112
  4. MakePartialListを廃止する@20230111
  5. lookupテーブルは一度だけ生成される@20230111
  6. MakeRecordでupdateflagをリセット@20230110
  7. UpdateRecordFuncの改造@20230110
  8. UNDOでリンクテーブルを保全しない@20230109
  9. CallGetRecordNumをlookup対応に変更@20230110
  10. SendUpdateDataの引数仕様変更@20230110
  11. 参照番号を永久コード化する@20230109

ここで一度バックアップを取り直す.

▲リンクテーブル全体にアクセスするとき,ダイレクトにアクセスするのと,lookupを経由するのとどちらが効率的か?

結婚テーブルのlookupの操作を確認する.⇒結婚リンクテーブルでも,lookupは生成されているが,有意な使い方はされていないのではないだろうか?実際,MARGTABLEではCARDTABLEと異なり,lookupの更新さえ行われていない.結婚テーブルからlookupを完全に排除してもよいのではないかと思うのだが…⇒MARGTABLE::MakingLookUpを完全にNOPにしても問題なく動作している.

lookup自体は基本クラスのBASETABLEのコンストラクタで生成しているので,作られてしまうのは避けられないが… MARGTABLE::MakingLookUpという関数は廃絶しても問題ないと思う.MARGTABLE::Renumberというのも不用なのではないか?⇒MARGTABLE::Renumberを廃止した上で,基準ソート時のRenumberを復活させてみよう.⇒FAMILYTREE::AppendFileではMARGTABLE::Renumberを使っている.これは必要なのではないか?ただし,この呼出には,「この論理はチェックを要する @@2017-09-22 」という書き込みがある…

基準ソート時にリンバーするというのは悪い仕様ではないので,復活させるのが妥当と思われるが,結婚テーブルではlookupを使わないという方針なので,もし,AppendFileで結婚参照番号のリナンバーが必要であるということになるとすれば,作り変える必要がある.ただし,アペンドした時点で重複しない参照番号が発行されているとすれば,あえてリナンバーする必要もないようにも思われる.この点に関しては現時点では保留としておく.

▲TOPOLOGY::GetRecordはlookup未対応@20230110なのではないか?⇒GetRecordに引数で渡されるlong* tableには2つのタイプがある.いずれもlong配列だが,lookupはレコード番号,他のテーブルは参照番号なので同一に操作することはできない.⇒これに対処するためには,TOPOLOGY::GetRecordの引数を仕様変更して,long*ではなく,longtable*を受け渡しするようにするしかない.⇒GetRecordの引数の仕様を変更したが,コンパイルエラーになったのは,TOPOLOGY::GetActiveRecordだけだ.

部分図やlookupでは何を使っているのだろう?COUPLING::MakeRecordを使っているのかもしれない.MakeRecordはGetRecordでも使われている.MakeRecordを呼び出している箇所をすべてGetRecordに統一するとよいのではないだろうか?

ようやく波に乗れたような気がする

ようやく波に乗れたような気がする.ともかく,lookupの更新を組み込んでおこう.⇒実装した.大体うまく動作しているように見える.テーブル並び替えは掛かってこないが,ソートは実行されている.なぜだろう?TOPOLOGY::RebuildCardListの中でNameSortが実行されている.⇒これでよい.昨日のエラーを人為的は発生させて動作を見ることにしよう.⇒新規カードはエラーなしで作れたがその後が悪い.いや,これは,わざとエラーを発生しているのだから,当然だ.

主なポイントは実装できたと思う.あとは,関数名などの整理だけだ.getmaxrecnではリンクテーブルサイズを返しているが,maxrecordを返すように変更できる.いや,何か失敗しているのではないだろうか?図面種別部分図で,1ブロックを一括削除した後,氏名でソートを2回繰り返すと表示範囲がまったく変わってしまう.一覧表の表示モードは<すべてのカード>となっているが,4点しか表示されていない.すべてのカードであれば,208点表示されなくてはならないのだが…

カードを70点一括削除して一覧画面の表示がおかしくなる.208点表示されなくてはならないところ,4点になり,氏名でソートするとその内容が変化してしまう.CommandEndの出口でNameSortを実行した時点でlookupのカウントは307のままだ.NAMESORT::SortNameSubには空欄を前詰めするという機構が備わっていない.⇒対処した.

getmaxrecnはmaxrecordを返すように仕様変更しているが,正しい使用法になっているかどうかをチェックしておこう.getmaxrecnは189箇所から参照されている.すべてforループのカウンタとしてつかわれていると見て間違いないだろう.⇒一応問題なさそうなので,予定通りmaxRecordにリネームしておこう.⇒その前に,バックアップ.

COUPLING::resetUpdateflagで(PDB()->updatecount < 0)になった.CARDLINKのupdateflagは重複してカウントアップされることがあるので,resetUpdateflagではPDB()->updatecount -= card->updateflagという計数を行っている.⇒以下の手順で再現できる.ブロック選択して,1点削除→ 1点削除→ 一括削除→ Undo→ Undo→ Undo.バックアップイメージにupdateflagが残っているのではないだろうか?⇒どうも,そのようだ.UNDOSYSTEM::UndoRestoreの中でフラグをリセットすることで解消した.

通常,コマンド実行時やUNDO実行時にはupdatecount はリセットされるので,イメージに残っていると不都合が生じる.CARDTABLE:cleanUpdateflagは,①TOPOLOGY:UpdateRecordFuncと②FAMILYTREE::cancelUpdateの2箇所のみ.UpdateRecordFuncは更新レコードの送り出しを実行する.cancelUpdateは外部呼び出しのCancelUpdatelistで実行される.いずれも一覧表の更新に関わっている.ただし,後者は現在使われていないものと思われる.一応これでよいのではないかと思う.⇒CancelUpdatelistを廃止は廃止した.

▲結婚テーブルのlookupの操作を確認する.

PARTIALNAME::MakePartialListを廃止

PARTIALNAME::MakePartialListは現在使われていないようなので,廃止しておこう.さて,リンクテーブルとlookupの同期に関してかなり深刻な問題が発生している.部分図リストのレコード数がlookupで数えた場合とPDBで数えた場合で一致しないという現象だ.lookupがゼロ発進に対し,PDBは1発進になっているという違いがある.ただし,lookupのレコード番号と言うときは,1発進の数として扱っているはずだ.確かにどこかで躓きそうな感じはする.

まず,テーブルのサイズを確認しておこう.lookupはlongtableのインスタンスで,MAXPDBというサイズのlong配列を保持している.lookupはゼロ発進だから,これで問題ない.PDBはどうか?CARDTABLEはBASETABLEの派生クラスで,MAXPDBサイズのCARDLINK*配列だ.BASETABLEはARRAYという配列クラスの派生クラスで,ARRAYは指定したサイズのスロットを持つオブジェクトだ.

確かに,これはまずいような気がする.BASETABLEはtablesizeというメンバー変数を持っているが,ここには,オブジェクト生成時の配列サイズ,つまりMAXPDBが入っている.現行ではgetmaxrecnはtablesizeを返すようになっている.⇒まず,BASETABLE::tablesizeをprivateに指定して外部から隠蔽してみよう.その上でアクセス関数gettablesizeを追加して,tablesize-1を返すようにしてみる.念のため,tablesize→ tablesize2にリネームしてクラス内部での参照もチェックしておこう.⇒NODECLASS *newlinkから参照されていた.getmaxrecn,doClean, getnod, emptyrecn からもアクセスされている.

一般には,getmaxrecnまでのレコードは許容されているが,refnum >= gettablesize()でtablesize-1を返すとおかしくなるところも出てくる.たとえば,refnum >= gettablesize()をエラーとみなしているところなど…TableSizeOverなども同様の仕様になっている.その逆に,CARDTABLE::makelinkなどではrecn <= tablesizeならば適法としている.⇒まだ,見直しは完了していないが,ざっくり修正して通るかと思ったが,TOPOLOGY::MakeActiveListで起きているエラーはまだ止まらない.デバッグするしかないのだろうか?

2023/01/09のリストに戻ってみよう.登録レコード数:recordcount→ recordCount, 最大レコード番号: maxrecord→ getmaxrecn, _maxrecn としているが,テーブルサイズを押さえている関数は予定されていない.現行のgetmaxrecnはtablesizeを返しているので,実質それに該当しているのだが,maxrecordはテーブルサイズではなく,現に登録されている有効なレコード番号中の最大のものという位置付けだ.tableSizeでもよいような気はするが,最大レコードというところを強調したい.上の修正でgettablesizeに相当する関数だ.中を取って,getTablesizeとしてみよう.この関数はtablesize-1を返すことにする.現行のgettablesizeを置き換えるものだ.この関数を使っているところで,使われ方が正しいかどうかを点検してみよう.

getTablesizeはレコード番号の取れる最大値を意味している.⇒TableSizeOverという関数名は状態を示す関数としては適切ではないので,LinkTableFullに変更する.LinkTableFullはmaxrecordではなく,recordcountがtableSizeと等しくなることを意味する.⇒すでにそういう実装になっている.

カードの肩書に文字を書き込んで登録して,TOPOLOGY:MakeActiveListで(treeview->validcard != ActiveList->count)のエラーになる.TREEVIEWの側の値が間違っているものと思われる.TREEVIEW::validcardは「有効なカード数(基準カードと親族関係にあるカード数)」で多分これは画面上のカード数と一致するはずだ.この値は,①SetKinship,②validcardnum,③ExtractPartialCardのいずれかの方法で決定される.今の場合は③に該当する.⇒validcardnumはTOPOLOGYのメンバー変数,SetKinshipはTOPOLOGY:FilteringKinshipの中で,「親族範囲を確定する」ために実行される.

どうも,カードの登録更新で部分図ノードが増加してしまっているようだ.このサンプルには現在部分図が3つ登録されているが,そのうちの2番目の「3077直系血族図278点」がデフォルトになっている.編集対象のカードはこれら3つの部分図のすべてに登録されているので,部分図の登録に変化があるというのはおかしいのだが…

PartialListのカウントは278のままで変化していない.partialflagというのはCARDLINKの固有情報であり,Undo/Redoは実行されていないのだから,誤って上書きされることも考え辛い.partialflagにTRUEを設定している箇所は7箇所ある.ファイルオープン時には,InitLinkTable→ PartialLoad で既定の部分図が読み込まれる.

カードの登録でmakenewcardが掛かってくる.なぜだろう?アプリから送付されたCarddataのRefnumが空欄になっている!Personalには入っている.これはかなり由々しき事態だ.MyCardというのは,アプリに常設されているカード情報の格納域だ.MyCardはGetNewCardでロードされているはずだが…OpenFileProc→ GetNewCardでは入ってくる.mCARDDATA::Putで転記されていない!

どうもこの関数の中で例外が発生しているようだ.この関数では例外が発生することを想定していないので,voidになっている.例外をトラップしたところでGCERROR_CARDDATAPUTをスローするようにしたが,何も応答はない.この関数を呼び出しているmZelkova:mSendCardでキャッチして,GCERROR_SENDCARDで復帰している.SendCarDataにはエラー処理は入っているが,ごく一部のエラー以外は処理されていない.ERR_CARDTABLEOVERとERR_MARGTABLEOVERでは黙って復帰する.ERR_NOMEMORYの場合はアプリ終了する.

それ以外では戻り値の上位ビットを取って,その部分がSAMENAMEEXISTの場合には,黙って新規カードを作成する.これは「本人氏名と同じときは黙って新規カードを生成する」というルールによる.この仕掛けはあまり感心できない.SAMENAMEEXIST As Integer = &HFFFF0000なので,ほとんどのエラーコードと合致してしまう.NAMERECURRENCE As Integer = &HEFEF0000はなんとか識別できるのではないか?SAMENAMEEXISTのコードをFEFE0000に変えておこう.いや,これはすでに使われているので,F8F80000とする.

これで「本人氏名と同じ」とは衝突しないようになったが,どう処理すればよいか?当面,公式リリースはないと見込まれるから,エラーパネルを出して異常終了でよいのではないか?⇒異常終了ではなく,ノーマル終了とした.これで一応例外が発生していることだけは検出できるようになった.あまりユーザフレンドリでないパネルだが…

image

戻り値を持っている関数はエラーリターンできるが,void関数ではエラートラップに落ちたことはコンソールへのダンプでしか知ることができない.ほとんど見落とされてしまうので,void関数では例外をスローするようにしておこう.⇒16箇所のトラップにthrow文を入れた.

さて,問題は,mCARDDATA::Putでなぜ例外が発生しているのかという理由だ.理由ははっきりしていて,(carddata->Refnum != refnum)のときには例外をスローするという論理になっているためだ.「参照番号の改変」とされる事象だが,

いや,その前に別のエラーが発生してしまった.「本人の氏名データがありません」というエラーが起きている.

image

MakeNewCard→ Myself.Getで-5007というエラーが返っている.参照番号は3079.-5007というのはGCERROR_GETPERSONALだ.⇒修正ミスがあった.catchの{}の外にthrow文を置いてしまっていた.

例外をスローしているのは以下の一文だ.

if (carddata->Refnum != refnum) throw “mCARDDATA::Put 参照番号の改変”;

しかし,これはかなり奇妙だ.mZelkova::mSendCardでは,引数で渡されたmCARDDATA^ mycard… 以下のデータをDLL側に送付するものであり,CARDDATA card… 以下のオブジェクトはアプリ側のデータをDLL向けに変換するための受け皿として用意された関数内オブジェクトであり,まったく空の状態になっているはずだから,carddata->Refnum と refnumが一致するようなことは起こり得ないと考えられるのだが… 「参照番号の改変」という文字列はここにしか現れない.⇒この文をコメントアウトしてエラーなしで動作するようになった.

「改変」という文字列は,2019/08/13に一箇所出てくるだけだ.もちろん,「参照番号の改変」とはまったく別の文脈だ.サイトにアップロードされていない以前のログを探せば何か手がかりになる情報を見つけられる可能性はあるが,今のところ,全文検索ができていないので,それもできない.もしかすると,以前はCARDDATAを関数内ではなく,静的領域に置いていた可能性はあるかもしれない.そうなっていれば,一致することはあり得るように思われるが…

さて,どこまで進んでいたのか?わからなくなってしまった.エラーはすでに解消しているが,不良の原因となった,「新規カードの生成」で起きる不具合について,原因と対策を考えておかなくてはならない.部分図モードのときに新規生成されたカードは自動的にその部分図に属するようになるというのが仕様だったような気がするので,部分図のメンバーが増加すること自体は正当だが,あちこちで不整合が発生するというのは問題だ.どんな事象が起きていたのか?

カード情報を更新しようとして,新規カードが生成されたという事象だ.不良の原因ははっきりしている.lookupテーブルが更新されていないためだ.いまのところ,ファイルオープン時に一度生成されただけで,それ以外の処理は何一つ組み込まれていないのだから当然だ.カードの生成と削除に同期してlookupを時点処理するというのが現在の方針であり,その手続きを実装しなくてはならない.

lookupではレコード番号は必ずしも連続した並びにはなっていない.場合によっては,順序が逆転していることすらあり得るし,また,そのような場合でも正しく操作できなくてはならない.逆に言えば,もし,それが可能ならレコードの追加・削除ではテーブル全体の詰め合わせや並び替えをやらなくてもよいということになる.系統並び替えにはテーブルソートが入っていたはずだから,そのような処理はそこに任せてよいはずだ.従って,lookupの保守はかなり簡単なもので済むはずだ.

いや,maxrecordというのは仮に中間に空きがあったとしても,テーブル上では必ず末尾に来なくてはならないのではないか?⇒これは解釈の問題ではないかと思う.maxrecordが有効レコードの末尾であるということは,その(リンクテーブル上の)レコード番号の最大値であるということを意味する.定義としては,これで十分なのではないだろうか?カード追加・削除時のlookup更新手続きを書いてみよう.

  1. 新規カードが追加された場合には,そのレコード番号をlookupの末尾に追加し,lookup.countを+1する
  2. 既存カードの削除では,lookup上のそのカードのセルを空欄とする.

つまり,lookupのサイズは単調に増加するものとする.⇒しかし,どこかで整序する必要があるのではないか?これは結局,MakingLookUpを実行するということに他ならない.おそらく,CommandEndとUndoProcessの出口でやっておけば十分なのではないかと思われるが,もし,RebuildCardListでlookupを参照しているとすれば,その前に実行する必要があるかもしれない.⇒MakingLookUpを実行する必要はない.系統並び替え時にはつねにテーブル並び替えを実施している.テーブル並び替えとはlookupの整列に他ならない.