/++
  document abstraction:
  abstraction of sisu markup for downstream processing
  ao_abstract_doc_source.d
+/
template SiSUdocAbstraction() {
  private:
  struct Abstraction {
    /+ ↓ abstraction imports +/
    import
      ao_defaults,                  // sdp/ao_defaults.d
      ao_object_setter,             // sdp/ao_object_setter.d
      ao_rgx;                       // sdp/ao_rgx.d
    /+ ↓ abstraction mixins +/
    mixin ObjectSetter;
    mixin InternalMarkup;
    /+ ↓ abstraction struct init +/
    /+ initialize +/
    auto rgx = Rgx();
    ObjComposite[] contents_the_objects;
    string[string] an_object, processing;
    string[] anchor_tags;
    string segment_object_belongs_to;
    auto set_abstract_object = ObjectAbstractSet();
    auto note_section = NotesSection();
    /+ enum +/
    enum State { off, on }
    enum TriState { off, on, closing } // make aware, possibility of third state
    enum DocStructMarkupHeading {
      h_sect_A,
      h_sect_B,
      h_sect_C,
      h_sect_D,
      h_text_1,
      h_text_2,
      h_text_3,
      h_text_4,
      h_text_5, // extra level, drop
      content_non_header
    } // header section A-D; header text 1-4
    enum DocStructCollapsedHeading { lv0, lv1, lv2, lv3, lv4, lv5, lv6, lv7 } // not yet used
    /+ biblio variables +/
    string biblio_tag_name, biblio_tag_entry, st;
    string[] biblio_arr_json;
    string biblio_entry_str_json;
    JSONValue[] bib_arr_json;
    int bib_entry;
    /+ counters +/
    long counter, previous_count;
    int[string] line_occur;
    int verse_line, heading_pointer;
    /+ paragraph attributes +/
    string[string] indent;
    bool bullet = true;
    string content_non_header = "8";
    auto obj_im = ObjInlineMarkup();
    auto obj_att = ObjAttributes();
    /+ ocn +/
    int obj_cite_number, obj_cite_number_;
    auto object_citation_number = OCNemitter();
    int obj_cite_number_emit(int obj_cite_number_status_flag) {
      return object_citation_number.obj_cite_number_emitter(obj_cite_number_status_flag);
    }
    /+ book index variables +/
    string book_idx_tmp;
    string[][string][string] bookindex_unordered_hashes;
    auto bookindex_extract_hash = BookIndexNuggetHash();
    string[][string][string] bkidx_hash(string bookindex_section, int obj_cite_number) {
      return bookindex_extract_hash.bookindex_nugget_hash(bookindex_section, obj_cite_number);
    }
    /+ node +/
    string _node;
    auto node_construct = NodeStructureMetadata();
    /+ ↓ abstract marked up document +/
    auto abstract_doc_source(
      char[][] markup_sourcefile_content,
      string[string][string] dochead_make_aa,
      string[string][string] dochead_meta_aa
    ) {
      /+ ↓ abstraction init +/
      scope(success) {
      }
      scope(failure) {
      }
      scope(exit) {
        destroy(contents_the_objects);
        destroy(an_object);
        destroy(processing);
        destroy(biblio_arr_json);
      }
      line_occur = [
        "heading" : 0,
        "para"    : 0,
      ];
      auto type = flags_type_init;
      void tell_lo(int obj_cite_number, in char[] line) {
        writefln(
          "* %s %s",
          to!string(obj_cite_number),
          to!string(line)
        );
      }
      string[string] obj_cite_number_poem = [
        "start" : "",
        "end"   : ""
      ];
      int[string] lv = [
        "lv" : State.off,
        "h0" : State.off,
        "h1" : State.off,
        "h2" : State.off,
        "h3" : State.off,
        "h4" : State.off,
        "h5" : State.off,
        "h6" : State.off,
        "h7" : State.off,
        "lev_collapsed_number" : 0,
      ];
      int[string] collapsed_lev = [
        "h0" : State.off,
        "h1" : State.off,
        "h2" : State.off,
        "h3" : State.off,
        "h4" : State.off,
        "h5" : State.off,
        "h6" : State.off,
        "h7" : State.off
      ];
      string[string] heading_match_str = [
        "h_A": "^(none)",
        "h_B": "^(none)",
        "h_C": "^(none)",
        "h_D": "^(none)",
        "h_1": "^(none)",
        "h_2": "^(none)",
        "h_3": "^(none)",
        "h_4": "^(none)"
      ];
      auto heading_match_rgx = [
        "h_A": regex(r"^(none)"),
        "h_B": regex(r"^(none)"),
        "h_C": regex(r"^(none)"),
        "h_D": regex(r"^(none)"),
        "h_1": regex(r"^(none)"),
        "h_2": regex(r"^(none)"),
        "h_3": regex(r"^(none)"),
        "h_4": regex(r"^(none)")
      ];
      /+ abstraction init ↑ +/
      /+ ↓ loop markup document/text line by line +/
      srcDocLoop:
      foreach (line; markup_sourcefile_content) {
        /+ ↓ markup document/text line by line +/
        /+ scope +/
        scope(exit) {
        }
        scope(failure) {
          stderr.writefln(
            "%s\n%s\n%s:%s failed here:\n  line: %s",
            __MODULE__, __FUNCTION__,
            __FILE__, __LINE__,
            line,
          );
        }
        line = replaceAll(line, rgx.true_dollar, "$$$$");
          // dollar represented as $$ needed to stop submatching on $
          // (substitutions using ${identifiers} must take into account (i.e. happen earlier))
        debug(source) {                                  // source lines
          writeln(line);
        }
        debug(srclines) {
          if (!line.empty) {                             // source lines, not empty
            writefln(
              "* %s",
              line
            );
          }
        }
        if (!line.empty) {
          _check_obj_cite_number_status_(line, type);
        }
        if (type["code"] == TriState.on) {
          /+ block object: code +/
          _code_block_(line, an_object, type);
          continue;
        } else if (!matchFirst(line, rgx.skip_from_regular_parse)) {
          /+ object other than "code block" object
             (includes regular text paragraph, headings & blocks other than code) +/
          if ((matchFirst(line, rgx.heading_biblio)
          || (type["heading_biblio"] == State.on))
          && (!matchFirst(line, rgx.heading))
          && (!matchFirst(line, rgx.comment))) {
            /+ within block object: biblio +/
            _biblio_block_(line, type, bib_entry, biblio_entry_str_json, biblio_arr_json);
            debug(bibliobuild) {
              writeln("-  ", biblio_entry_str_json);
              writeln("-> ", biblio_arr_json.length);
            }
            continue;
          } else if (type["poem"] == TriState.on) {
            /+ within block object: poem +/
            _poem_block_(line, an_object, type, counter, obj_cite_number_poem, dochead_make_aa);
            continue;
          /+ within block object: group +/
          } else if (type["group"] == TriState.on) {
            /+ within block object: group +/
            _group_block_(line, an_object, type);
            continue;
          } else if (type["block"] == TriState.on) {
            /+ within block object: block +/
            _block_block_(line, an_object, type);
            continue;
          } else if (type["quote"] == TriState.on) {
            /+ within block object: quote +/
            _quote_block_(line, an_object, type);
            continue;
          } else if (type["table"] == TriState.on) {
            /+ within block object: table +/
            _table_block_(line, an_object, type);
            continue;
          } else {
            /+ not within a block group +/
            assert(
              (type["blocks"] == TriState.off)
              || (type["blocks"] == TriState.closing),
              "block status: none or closed"
            );
            assertions_flag_types_block_status_none_or_closed(type);
            if (matchFirst(line, rgx.block_open)) {
              if (matchFirst(line, (rgx.block_poem_open))) {
                /+ poem to verse exceptions! +/
                object_reset(an_object);
                processing.remove("verse");
                obj_cite_number_poem["start"] = to!string(obj_cite_number);
              }
              _start_block_(line, type, obj_cite_number_poem);
              continue;
            } else if (!line.empty) {
              /+ line not empty +/
              /+ non blocks (headings, paragraphs) & closed blocks +/
              assert(
                !line.empty,
                "line tested, line not empty surely"
              );
              assert(
                (type["blocks"] == TriState.off)
                || (type["blocks"] == TriState.closing),
                "code block status: none or closed"
              );
              if (type["blocks"] == TriState.closing) {
                // blocks closed, unless followed by book index
                debug(check) {                           // block
                  writeln(__LINE__);
                  writeln(line);
                }
                assert(
                  matchFirst(line, rgx.book_index)
                  || matchFirst(line, rgx.book_index_open)
                  || type["book_index"] == State.on
                );
              }
              if ((matchFirst(line, rgx.book_index))
              || (matchFirst(line, rgx.book_index_open))
              || (type["book_index"] == State.on ))  {
                /+ book_index +/
                _book_index_(line, book_idx_tmp, an_object, type);
              } else {
                /+ not book_index +/
                if (auto m = matchFirst(line, rgx.comment)) {
                  /+ matched comment +/
                  debug(comment) {
                    writeln(line);
                  }
                  an_object["obj"] ~= line ~= "\n";
                  contents_the_objects ~=
                    set_abstract_object.contents_comment(strip(an_object["obj"]));
                  _header_set_common_(line_occur, an_object, type);
                  processing.remove("verse");
                  ++counter;
                } else if (((line_occur["para"] == State.off)
                && (line_occur["heading"] == State.off))
                && ((type["para"] == State.off)
                && (type["heading"] == State.off))) {
                  /+ heading or para but neither flag nor line exists +/
                  if ((dochead_make_aa["make"]["headings"].length > 2)
                  && (type["make_headings"] == State.off)) {
                    /+ heading found +/
                    _heading_found_(line, dochead_make_aa["make"]["headings"], heading_match_str, heading_match_rgx, type);
                  }
                  if ((type["make_headings"] == State.on)
                  && ((line_occur["para"] == State.off)
                  && (line_occur["heading"] == State.off))
                  && ((type["para"] == State.off)
                  && (type["heading"] == State.off))) {
                    /+ heading make set +/
                    _heading_make_set_(line, line_occur, heading_match_rgx, type);
                  }
                  if (matchFirst(line, rgx.heading)) {
                    /+ heading match +/
                    _heading_matched_(line, line_occur, an_object, lv, collapsed_lev, type, dochead_meta_aa);
                  } else if (line_occur["para"] == State.off) {
                    /+ para match +/
                    _para_match_(line, an_object, indent, bullet, type, line_occur);
                  }
                } else if (line_occur["heading"] > State.off) {
                  /+ heading +/
                  debug(heading) {                         // heading
                    writeln(line);
                  }
                  an_object["obj"] ~= line ~= "\n";
                  ++line_occur["heading"];
                } else if (line_occur["para"] > State.off) {
                  /+ paragraph +/
                  debug(para) {
                    writeln(line);
                  }
                  an_object["obj"] ~= line;
                  ++line_occur["para"];
                }
              }
            } else if (type["blocks"] == TriState.closing) {
              /+ line empty, with blocks flag +/
              _block_flag_line_empty_(line, an_object, contents_the_objects, bookindex_unordered_hashes, obj_cite_number, _node, counter, type, obj_cite_number_poem, dochead_make_aa); // watch
            } else {
              /+ line empty +/
              /+ line.empty, post contents, empty variables: +/
              assert(
                line.empty,
                "line should be empty"
              );
              assert(
                (type["blocks"] == State.off),
                "code block status: none"
              );
              if ((type["heading"] == State.on)
              && (line_occur["heading"] > State.off)) {
                /+ heading object (current line empty) +/
                obj_cite_number = obj_cite_number_emit(type["obj_cite_number_status"]);
                an_object["bookindex"] =
                  ("bookindex" in an_object) ? an_object["bookindex"] : "";
                bookindex_unordered_hashes =
                  bkidx_hash(an_object["bookindex"], obj_cite_number);
                an_object["is"] = "heading";
                auto substantive_object_and_anchor_tags_tuple =
                  obj_im.obj_inline_markup_and_anchor_tags(an_object, dochead_make_aa); // tuple this with anchor tags?
                an_object["substantive"] = substantive_object_and_anchor_tags_tuple[0];
                anchor_tags = substantive_object_and_anchor_tags_tuple[1];
                if (to!int(an_object["lev_markup_number"]) == 4) {
                  segment_object_belongs_to = anchor_tags[0];
                } else if (to!int(an_object["lev_markup_number"]) < 4) {
                  segment_object_belongs_to = "";
                }
                _node =
                  node_construct.node_emitter_heading(
                    an_object["lev_markup_number"],
                    an_object["lev_collapsed_number"],
                    segment_object_belongs_to,
                    obj_cite_number,
                    counter,
                    heading_pointer,
                    an_object["is"]
                  ); // heading
                an_object["attrib"] =
                  obj_att.obj_attributes(an_object["is"], an_object["obj"], _node);
                ++heading_pointer;
                debug(segments) {
                  writeln(an_object["lev_markup_number"]);
                  writeln(segment_object_belongs_to);
                }
                contents_the_objects ~=
                  set_abstract_object.contents_heading(
                    an_object["substantive"],
                    an_object["attrib"],
                    obj_cite_number,
                    anchor_tags,
                    to!string(an_object["lev"]),
                    to!int(an_object["lev_markup_number"]),
                    to!int(an_object["lev_collapsed_number"]),
                  );
                // track previous heading and make assertions
                debug(objectrelated1) { // check
                  writeln(line);
                  // writeln(an_object["obj"]);
                  // writeln(contents_am[counter]["obj_cite_number"], " ", contents_am[counter]["obj"]);
                  // writeln(m.hit, "\n");
                }
                _header_set_common_(line_occur, an_object, type);
                an_object.remove("lev");
                an_object.remove("lev_markup_number");
                // an_object["lev_markup_number"]="9";
                processing.remove("verse");
                ++counter;
              } else if ((type["para"] == State.on) && (line_occur["para"] > State.off)) {
                /+ paragraph object (current line empty) +/
                obj_cite_number = obj_cite_number_emit(type["obj_cite_number_status"]);
                an_object["bookindex"] =
                  ("bookindex" in an_object) ? an_object["bookindex"] : "";
                bookindex_unordered_hashes =
                  bkidx_hash(an_object["bookindex"], obj_cite_number);
                an_object["is"] = "para";
                _node =
                  node_construct.node_emitter(
                    content_non_header,
                    segment_object_belongs_to,
                    obj_cite_number,
                    counter,
                    heading_pointer-1,
                    an_object["is"]
                  );
                auto substantive_object_and_anchor_tags_tuple =
                  obj_im.obj_inline_markup_and_anchor_tags(an_object, dochead_make_aa);
                an_object["substantive"] = substantive_object_and_anchor_tags_tuple[0];
                anchor_tags = substantive_object_and_anchor_tags_tuple[1];
                an_object["attrib"] =
                  obj_att.obj_attributes(an_object["is"], an_object["obj"], _node);
                contents_the_objects ~=
                  set_abstract_object.contents_para(
                    an_object["is"],
                    an_object["substantive"],
                    an_object["attrib"],
                    obj_cite_number,
                    indent,
                    bullet
                  );
                // contents_the_objects ~=
                //   set_abstract_object.contents_para(
                //     an_object,
                //     obj_cite_number,
                //     indent,
                //     bullet
                //   );
                _header_set_common_(line_occur, an_object, type);
                indent["hang_position"] = "0";
                indent["base_position"] = "0";
                bullet = false;
                processing.remove("verse");
                ++counter;
              } else {
                assert(
                  line == null,
                  "line variable should be empty, should not occur"
                );
                // check what happens when paragraph separated by 2 newlines
              }
            } // close else for line empty
          } // close else for not the above
        } // close after non code, other blocks or regular text
        /+ unless (contents_the_objects.length == 0) ? +/
        if (contents_the_objects.length > 0) {
          if (((contents_the_objects[$-1].is_a == "para")
          || (contents_the_objects[$-1].is_a == "heading"))
          && (counter-1 > previous_count)) {
            if (match(contents_the_objects[$-1].object,
            rgx.inline_notes_delimiter_al_regular_number_note)) {
              previous_count=contents_the_objects.length -1;
              note_section.gather_notes_for_endnote_section(
                contents_the_objects,
                segment_object_belongs_to,
                contents_the_objects.length -1
              );
            }
          }
        }
      } /+ ← closed: loop markup document/text line by line +/
      /+ ↓ post loop markup document/text +/
      debug(objectrelated2) { // check
          writeln(line);
      }
      /+
        Backmatter:
        * endnotes
        * glossary
        * references / bibliography
        * book index
      +/
      // TODO FIGURE OUT, you need this possibility
      // obj_im.obj_inline_markup_and_anchor_tags("doc_end_reset", "", dochead_make_aa);
      auto en_tuple =
        note_section.endnote_objects(obj_cite_number);
      static assert(!isTypeTuple!(en_tuple));
      auto endnotes_section = en_tuple[0];
      obj_cite_number = en_tuple[1];
      debug(endnotes) {
        writefln(
          "%s %s",
          __LINE__,
          endnotes_section.length
        );
        foreach (n; endnotes_section) {
          writeln(n);
        }
      }
      auto biblio_unsorted_incomplete = biblio_arr_json.dup;
      // destroy(biblio_arr_json);
      auto biblio = Bibliography();
      auto biblio_ordered =
        biblio._bibliography_(biblio_unsorted_incomplete, bib_arr_json);
      auto bi = BookIndexReportSection();
      auto bi_tuple =
        bi.bookindex_build_section(bookindex_unordered_hashes, obj_cite_number, segment_object_belongs_to);
      static assert(!isTypeTuple!(bi_tuple));
      auto bookindex_section = bi_tuple[0];
      obj_cite_number = bi_tuple[1];
      debug(bookindex) {                         // bookindex
        foreach (bi_entry; bookindex_section) {
          writeln(bi_entry);
        }
      }
      auto document_the =
        contents_the_objects ~ endnotes_section ~ bookindex_section;
      auto t = tuple(
        document_the,
        bookindex_unordered_hashes,
        biblio_ordered
      );
      return t;
      /+ post loop markup document/text ↑ +/
    } /+ ← closed: abstract doc source +/
    /+ ↓ abstraction functions +/
    auto object_reset(ref string[string] an_object) {
      an_object.remove("obj");
      an_object.remove("substantive");
      an_object.remove("is");
      an_object.remove("attrib");
      an_object.remove("bookindex");
    }
    auto _header_set_common_(
      ref int[string] line_occur,
      ref string[string] an_object,
      ref int[string] type
    ) {
      // line_occur["header"] = State.off;
      line_occur["heading"] = State.off;
      line_occur["para"]= State.off;
      type["heading"] = State.off;
      type["para"] = State.off;
      object_reset(an_object);
    }
    void _check_obj_cite_number_status_(
      char[] line,
      ref int[string] type
    ) {
      if ((!line.empty) && (type["obj_cite_number_status_multi_obj"] == TriState.off)) {
        /+ not multi-line object, check whether obj_cite_number is on or turned off +/
        if (matchFirst(line, rgx.obj_cite_number_block_marks)) {
          /+ switch off obj_cite_number +/
          if (matchFirst(line, rgx.obj_cite_number_off_block)) {
            type["obj_cite_number_status_multi_obj"] = TriState.on;
            debug(ocnoff) {
              writeln(line);
            }
          }
          if (matchFirst(line, rgx.obj_cite_number_off_block_dh)) {
            type["obj_cite_number_status_multi_obj"] = TriState.closing;
            debug(ocnoff) {
              writeln(line);
            }
          }
        } else {
          if (type["obj_cite_number_status_multi_obj"] == TriState.off) {
            if (matchFirst(line, rgx.obj_cite_number_off)) {
              type["obj_cite_number_status"] = TriState.on;
            } else if (matchFirst(line, rgx.obj_cite_number_off_dh)) {
              type["obj_cite_number_status"] = TriState.closing;
            } else {
              // type["obj_cite_number_status"] = TriState.closing;
              type["obj_cite_number_status"] = TriState.off;
            }
          } else {
            type["obj_cite_number_status"] =
              type["obj_cite_number_status_multi_obj"];
          }
        }
      } else if ((!line.empty) && (type["obj_cite_number_status_multi_obj"] > TriState.off)) {
        if (matchFirst(line, rgx.obj_cite_number_off_block_close)) {
          type["obj_cite_number_status_multi_obj"] = TriState.off;
          type["obj_cite_number_status"] = TriState.off;
          debug(ocnoff) {
            writeln(line);
          }
        }
      }
    }
    void _start_block_(
      char[] line,
      ref int[string] type,
      string[string] obj_cite_number_poem
    ) {
      if (matchFirst(line, rgx.block_curly_code_open)) {
        /+ curly code open +/
        debug(code) {                              // code (curly) open
          writefln(
            "* [code curly] %s",
            line
          );
        }
        type["blocks"] = TriState.on;
        type["code"] = TriState.on;
        type["curly_code"] = TriState.on;
      } else if (matchFirst(line, rgx.block_curly_poem_open)) {
        /+ curly poem open +/
        debug(poem) {                              // poem (curly) open
          writefln(
            "* [poem curly] %s",
            line
          );
        }
        obj_cite_number_poem["start"] =
          to!string(obj_cite_number);
        type["blocks"] = TriState.on;
        type["verse_new"] = State.on;
        type["poem"] = TriState.on;
        type["curly_poem"] = TriState.on;
      } else if (matchFirst(line, rgx.block_curly_group_open)) {
        /+ curly group open +/
        debug(group) {                             // group (curly) open
          writefln(
            "* [group curly] %s",
            line
          );
        }
        type["blocks"] = TriState.on;
        type["group"] = TriState.on;
        type["curly_group"] = TriState.on;
      } else if (matchFirst(line, rgx.block_curly_block_open)) {
        /+ curly block open +/
        debug(block) {                             // block (curly) open
          writefln(
            "* [block curly] %s",
            line
          );
        }
        type["blocks"] = TriState.on;
        type["block"] = TriState.on;
        type["curly_block"] = TriState.on;
      } else if (matchFirst(line, rgx.block_curly_quote_open)) {
        /+ curly quote open +/
        debug(quote) {                             // quote (curly) open
          writefln(
            "* [quote curly] %s",
            line
          );
        }
        type["blocks"] = TriState.on;
        type["quote"] = TriState.on;
        type["curly_quote"] = TriState.on;
      } else if (matchFirst(line, rgx.block_curly_table_open)) {
        /+ curly table open +/
        debug(table) {                             // table (curly) open
          writefln(
            "* [table curly] %s",
            line
          );
        }
        type["blocks"] = TriState.on;
        type["table"] = TriState.on;
        type["curly_table"] = TriState.on;
      } else if (matchFirst(line, rgx.block_tic_code_open)) {
        /+ tic code open +/
        debug(code) {                              // code (tic) open
          writefln(
            "* [code tic] %s",
            line
          );
        }
        type["blocks"] = TriState.on;
        type["code"] = TriState.on;
        type["tic_code"] = TriState.on;
      } else if (matchFirst(line, rgx.block_tic_poem_open)) {
        /+ tic poem open +/
        debug(poem) {                              // poem (tic) open
          writefln(
            "* [poem tic] %s",
            line
          );
        }
        obj_cite_number_poem["start"] = to!string(obj_cite_number);
        type["blocks"] = TriState.on;
        type["verse_new"] = State.on;
        type["poem"] = TriState.on;
        type["tic_poem"] = TriState.on;
      } else if (matchFirst(line, rgx.block_tic_group_open)) {
        /+ tic group open +/
        debug(group) {                             // group (tic) open
          writefln(
            "* [group tic] %s",
            line
          );
        }
        type["blocks"] = TriState.on;
        type["group"] = TriState.on;
        type["tic_group"] = TriState.on;
      } else if (matchFirst(line, rgx.block_tic_block_open)) {
        /+ tic block open +/
        debug(block) {                             // block (tic) open
          writefln(
            "* [block tic] %s",
            line
          );
        }
        type["blocks"] = TriState.on;
        type["block"] = TriState.on;
        type["tic_block"] = TriState.on;
      } else if (matchFirst(line, rgx.block_tic_quote_open)) {
        /+ tic quote open +/
        debug(quote) {                             // quote (tic) open
          writefln(
            "* [quote tic] %s",
            line
          );
        }
        type["blocks"] = TriState.on;
        type["quote"] = TriState.on;
        type["tic_quote"] = TriState.on;
      } else if (matchFirst(line, rgx.block_tic_table_open)) {
        /+ tic table open +/
        debug(table) {                             // table (tic) open
          writefln(
            "* [table tic] %s",
            line
          );
        }
        type["blocks"] = TriState.on;
        type["table"] = TriState.on;
        type["tic_table"] = TriState.on;
      }
    }
    void _code_block_(
      char[] line,
      ref string[string] an_object,
      ref int[string] type
    ) {
      if (type["curly_code"] == TriState.on) {
        if (matchFirst(line, rgx.block_curly_code_close)) {
          debug(code) {                              // code (curly) close
            writeln(line);
          }
          type["blocks"] = TriState.closing;
          type["code"] = TriState.closing;
          type["curly_code"] = TriState.off;
        } else {
          debug(code) {                              // code (curly) line
            writeln(line);
          }
          an_object["obj"] ~= line ~= "\n";          // code (curly) line
        }
      } else if (type["tic_code"] == TriState.on) {
        if (matchFirst(line, rgx.block_tic_close)) {
          debug(code) {                              // code (tic) close
            writeln(line);
          }
          type["blocks"] = TriState.closing;
          type["code"] = TriState.closing;
          type["tic_code"] = TriState.off;
        } else {
          debug(code) {                              // code (tic) line
            writeln(line);
          }
          an_object["obj"] ~= line ~= "\n";          // code (tic) line
        }
      }
    }
    final string biblio_tag_map(string abr) {
      auto btm = [
        "au"                               : "author_raw",
        "ed"                               : "editor_raw",
        "ti"                               : "fulltitle",
        "lng"                              : "language",
        "jo"                               : "journal",
        "vol"                              : "volume",
        "edn"                              : "edition",
        "yr"                               : "year",
        "pl"                               : "place",
        "pb"                               : "publisher",
        "pub"                              : "publisher",
        "pg"                               : "pages",
        "pgs"                              : "pages",
        "sn"                               : "short_name"
      ];
      return btm[abr];
    }
    void _biblio_block_(
      char[] line,
      ref int[string] type,
      ref int bib_entry,
      ref string biblio_entry_str_json,
      ref string[] biblio_arr_json
    ) {
      if (matchFirst(line, rgx.heading_biblio)) {
        type["heading_biblio"] = TriState.on;
      }
      if (line.empty) {
        debug {
          debug(biblioblock) {
            writeln("---");
          }
          debug(biblioblockinclude) {
            writeln(biblio_entry_str_json.length);
          }
        }
        if ((bib_entry == State.off)
        && (biblio_entry_str_json.empty)) {
          bib_entry = State.on;
          biblio_entry_str_json = biblio_entry_tags_jsonstr;
        } else if (!(biblio_entry_str_json.empty)) {
          bib_entry = State.off;
          if (!(biblio_entry_str_json == biblio_entry_tags_jsonstr)) {
            auto biblio_entry = parseJSON(biblio_entry_str_json);
            if (biblio_entry["fulltitle"].str.empty) {
              writeln("check problem entry (Title missing): ", biblio_entry_str_json);
            } else if ((biblio_entry["author_raw"].str.empty) && (biblio_entry["editor_raw"].str.empty)) {
              writeln("check problem entry (No author and no editor): ", biblio_entry_str_json);
            // } else if (biblio_entry["sortby_deemed_author_year_title"].str.empty) {
            //   writeln("check problem entry (Sort Field missing): ", biblio_entry_str_json);
            } else {
              biblio_arr_json ~= biblio_entry_str_json;
            }
            biblio_entry_str_json = biblio_entry_tags_jsonstr;
          }
        } else { // CHECK ERROR
          writeln("?? 2. ERROR ", biblio_entry_str_json, "??");
          biblio_entry_str_json = "";
        }
      } else if (matchFirst(line, rgx.biblio_tags)) {
        debug(biblioblock) {
          writeln(line);
        }
        auto bt = match(line, rgx.biblio_tags);
        bib_entry = State.off;
        st=to!string(bt.captures[1]);
        auto header_tag_value=to!string(bt.captures[2]);
        JSONValue j = parseJSON(biblio_entry_str_json);
        biblio_tag_name = (match(st, rgx.biblio_abbreviations))
          ? (biblio_tag_map(st))
          : st;
        j.object[biblio_tag_name] = header_tag_value;
        debug(bibliounsortedcheckduplicates) {
          writeln(biblio_tag_name, ": ", header_tag_value);
          writeln("--");
        }
        switch (biblio_tag_name) {
        case "author_raw": // author_arr author (fn sn)
          j["author_arr"] =
            split(header_tag_value, rgx.arr_delimiter);
          string tmp;
          biblioAuthorLoop:
          foreach (au; j["author_arr"].array) {
            if (auto x = match(au.str, rgx.name_delimiter)) {
              tmp ~= x.captures[2] ~ " " ~ x.captures[1] ~ ", ";
            } else {
              tmp ~= au.str;
            }
          }
          tmp = replace(tmp, rgx.trailing_comma, "");
          // tmp = replace(tmp, regex(r"(,[ ]*)$","g"), "");
          j["author"].str = tmp;
          goto default;
        case "editor_raw": // editor_arr editor (fn sn)
          j["editor_arr"] =
            split(header_tag_value, rgx.arr_delimiter);
          string tmp;
          biblioEditorLoop:
          foreach (ed; j["editor_arr"].array) {
            if (auto x = match(ed.str, rgx.name_delimiter)) {
              tmp ~= x.captures[2] ~ " " ~ x.captures[1] ~ ", ";
            } else {
              tmp ~= ed.str;
            }
          }
          tmp = replace(tmp, rgx.trailing_comma, "");
          // tmp = replace(tmp, regex(r"(,[ ]*)$","g"), "");
          j["editor"].str = tmp;
          goto default;
        case "fulltitle": // title & subtitle
          goto default;
        default:
          break;
        }
        auto s = j.toString();
        debug(biblio1) {
          writefln(
            "* %s: %s\n%s",
            biblio_tag_name,
            biblio_tag_entry,
            j[biblio_tag_name]
          );
        }
        if ((match(line, rgx.comment))) {
          writeln("ERROR", line, "COMMENT");
          writeln("ERROR", s, "%%");
        }
        if (!(match(line, rgx.comment))) {
          debug(biblioblockinclude) {
            writeln(line);
          }
          biblio_entry_str_json = s;
        } else {
          biblio_entry_str_json = "";
        }
        header_tag_value="";
      }
    }
    void _poem_block_(
      char[] line,
      ref string[string] an_object,
      ref int[string] type,
      ref long counter,
      string[string] obj_cite_number_poem,
      string[string][string] dochead_make_aa,
    ) {
      if (type["curly_poem"] == TriState.on) {
        if (matchFirst(line, rgx.block_curly_poem_close)) {
          an_object["obj"]="verse"; // check that this is as you please
          debug(poem) {                            // poem (curly) close
            writefln(
              "* [poem curly] %s",
              line
            );
          }
          if (processing.length > 0) {
            an_object["obj"] = processing["verse"];
          }
          debug(poem) {                            // poem (curly) close
            writeln(__LINE__);
            writefln(
              "* %s %s",
              obj_cite_number,
              line
            );
            // writeln(an_object.keys);
            // writeln(an_object.length);
          }
          if (an_object.length > 0) {
            debug(poem) {                            // poem (curly) close
              writeln(
                obj_cite_number,
                an_object["obj"]
              );
            }
            an_object["is"] = "verse";
            auto substantive_object_and_anchor_tags_tuple =
              obj_im.obj_inline_markup_and_anchor_tags(an_object, dochead_make_aa);
            an_object["substantive"] = substantive_object_and_anchor_tags_tuple[0];
            anchor_tags = substantive_object_and_anchor_tags_tuple[1];
            an_object["attrib"] =
              obj_att.obj_attributes(an_object["is"], an_object["obj"], _node);
            contents_the_objects ~=
              set_abstract_object.contents_block(
                an_object["is"],
                an_object["substantive"],
                an_object["attrib"],
                obj_cite_number
              );
            object_reset(an_object);
            processing.remove("verse");
            ++counter;
          }
          obj_cite_number_poem["end"] =
            to!string(obj_cite_number);
          type["blocks"] = TriState.closing;
          type["poem"] = TriState.closing;
          type["curly_poem"] = TriState.off;
        } else {
          processing["verse"] ~= line ~= "\n";
          if (type["verse_new"] == State.on) {
            obj_cite_number =
              obj_cite_number_emit(type["obj_cite_number_status"]);
            type["verse_new"] = State.off;
          } else if (matchFirst(line, rgx.line_delimiter_only)) {
            verse_line = TriState.off;
            type["verse_new"] = State.on;
          }
          if (type["verse_new"] == State.on) {
            verse_line=1;
            an_object["obj"] = processing["verse"];
            debug(poem) {                          // poem verse
              writefln(
                "* %s curly\n%s",
                obj_cite_number,
                an_object["obj"]
              );
            }
            processing.remove("verse");
            an_object["is"] = "verse";
            _node = node_construct.node_emitter(
              content_non_header,
              segment_object_belongs_to,
              obj_cite_number,
              counter,
              heading_pointer-1,
              an_object["is"]
            );
            auto substantive_object_and_anchor_tags_tuple =
              obj_im.obj_inline_markup_and_anchor_tags(an_object, dochead_make_aa);
            an_object["substantive"] = substantive_object_and_anchor_tags_tuple[0];
            anchor_tags = substantive_object_and_anchor_tags_tuple[1];
            an_object["attrib"] =
              obj_att.obj_attributes(an_object["is"], an_object["obj"], _node);
            contents_the_objects ~=
              set_abstract_object.contents_block(
                an_object["is"],
                an_object["substantive"],
                an_object["attrib"],
                obj_cite_number
              );
            object_reset(an_object);
            processing.remove("verse");
            ++counter;
          }
        }
      } else if (type["tic_poem"] == TriState.on) {
        if (auto m = matchFirst(line, rgx.block_tic_close)) { // tic_poem_close
          an_object["obj"]="verse"; // check that this is as you please
          debug(poem) {                            // poem (curly) close
            writefln(
              "* [poem tic] %s",
              line
            );
          }
          if (processing.length > 0) {       // needs looking at
            an_object["obj"] = processing["verse"];
          }
          if (an_object.length > 0) {
            debug(poem) {                            // poem (tic) close
              writeln(__LINE__);
              writeln(obj_cite_number, line);
            }
            processing.remove("verse");
            an_object["is"] = "verse";
            auto substantive_object_and_anchor_tags_tuple =
              obj_im.obj_inline_markup_and_anchor_tags(an_object, dochead_make_aa);
            an_object["substantive"] = substantive_object_and_anchor_tags_tuple[0];
            anchor_tags = substantive_object_and_anchor_tags_tuple[1];
            an_object["attrib"] =
              obj_att.obj_attributes(an_object["is"], an_object["obj"], _node);
            contents_the_objects ~=
              set_abstract_object.contents_block(
                an_object["is"],
                an_object["substantive"],
                an_object["attrib"],
                obj_cite_number
              );
            obj_cite_number_poem["end"] = to!string(obj_cite_number);
            object_reset(an_object);
            processing.remove("verse");
            ++counter;
          }
          type["blocks"] = TriState.closing;
          type["poem"] = TriState.closing;
          type["tic_poem"] = TriState.off;
        } else {
          processing["verse"] ~= line ~= "\n";
          if (type["verse_new"] == State.on) {
            obj_cite_number =
              obj_cite_number_emit(type["obj_cite_number_status"]);
            type["verse_new"] = State.off;
          } else if (matchFirst(line, rgx.line_delimiter_only)) {
            type["verse_new"] = State.on;
            verse_line = TriState.off;
          }
          if (type["verse_new"] == State.on) {
            verse_line=1;
            an_object["obj"] = processing["verse"];
            debug(poem) {                            // poem (tic) close
              writefln(
                "* %s tic\n%s",
                obj_cite_number,
                an_object["obj"]
              );
            }
            processing.remove("verse");
            an_object["is"] = "verse";
            _node =
              node_construct.node_emitter(
                content_non_header,
                segment_object_belongs_to,
                obj_cite_number,
                counter,
                heading_pointer-1,
                an_object["is"]
              );
            auto substantive_object_and_anchor_tags_tuple =
              obj_im.obj_inline_markup_and_anchor_tags(an_object, dochead_make_aa);
            an_object["substantive"] = substantive_object_and_anchor_tags_tuple[0];
            anchor_tags = substantive_object_and_anchor_tags_tuple[1];
            an_object["attrib"] =
              obj_att.obj_attributes(an_object["is"], an_object["obj"], _node);
            contents_the_objects ~=
              set_abstract_object.contents_block(
                an_object["is"],
                an_object["substantive"],
                an_object["attrib"],
                obj_cite_number
              );
            object_reset(an_object);
            processing.remove("verse");
            ++counter;
          }
        }
      }
    }
    void _group_block_(
      char[] line,
      ref string[string] an_object,
      ref int[string] type
    ) {
      if (type["curly_group"] == State.on) {
        if (matchFirst(line, rgx.block_curly_group_close)) {
          debug(group) {                           // group (curly) close
            writeln(line);
          }
          type["blocks"] = TriState.closing;
          type["group"] = TriState.closing;
          type["curly_group"] = TriState.off;
        } else {
          debug(group) {                           // group
            writeln(line);
          }
          an_object["obj"] ~= line ~= "\n";           // build group array (or string)
        }
      } else if (type["tic_group"] == TriState.on) {
        if (matchFirst(line, rgx.block_tic_close)) {
          debug(group) {                           // group (tic) close
            writeln(line);
          }
          type["blocks"] = TriState.closing;
          type["group"] = TriState.closing;
          type["tic_group"] = TriState.off;
        } else {
          debug(group) {                           // group
            writeln(line);
          }
          an_object["obj"] ~= line ~= "\n";           // build group array (or string)
        }
      }
    }
    void _block_block_(
      char[] line,
      ref string[string] an_object,
      ref int[string] type
    ) {
      if (type["curly_block"] == TriState.on) {
        if (matchFirst(line, rgx.block_curly_block_close)) {
          debug(block) {                           // block (curly) close
            writeln(line);
          }
          type["blocks"] = TriState.closing;
          type["block"] = TriState.closing;
          type["curly_block"] = TriState.off;
        } else {
          debug(block) {                           // block
            writeln(line);
          }
          an_object["obj"] ~= line ~= "\n";           // build block array (or string)
        }
      } else if (type["tic_block"] == TriState.on) {
        if (matchFirst(line, rgx.block_tic_close)) {
          debug(block) {                           // block (tic) close
            writeln(line);
          }
          type["blocks"] = TriState.closing;
          type["block"] = TriState.closing;
          type["tic_block"] = TriState.off;
        } else {
          debug(block) {                           // block
            writeln(line);
          }
          an_object["obj"] ~= line ~= "\n";           // build block array (or string)
        }
      }
    }
    void _quote_block_(
      char[] line,
      ref string[string] an_object,
      ref int[string] type
    ) {
      if (type["curly_quote"] == TriState.on) {
        if (matchFirst(line, rgx.block_curly_quote_close)) {
          debug(quote) {                           // quote (curly) close
            writeln(line);
          }
          type["blocks"] = TriState.closing;
          type["quote"] = TriState.closing;
          type["curly_quote"] = TriState.off;
        } else {
          debug(quote) {                           // quote
            writeln(line);
          }
          an_object["obj"] ~= line ~= "\n";           // build quote array (or string)
        }
      } else if (type["tic_quote"] == TriState.on) {
        if (matchFirst(line, rgx.block_tic_close)) {
          debug(quote) {                           // quote (tic) close
            writeln(line);
          }
          type["blocks"] = TriState.closing;
          type["quote"] = TriState.closing;
          type["tic_quote"] = TriState.off;
        } else {
          debug(quote) {                           // quote
            writeln(line);
          }
          an_object["obj"] ~= line ~= "\n";           // build quote array (or string)
        }
      }
    }
    void _table_block_(
      char[] line,
      ref string[string] an_object,
      ref int[string] type
    ) {
      if (type["curly_table"] == TriState.on) {
        if (matchFirst(line, rgx.block_curly_table_close)) {
          debug(table) {                           // table (curly) close
            writeln(line);
          }
          type["blocks"] = TriState.closing;
          type["table"] = TriState.closing;
          type["curly_table"] = TriState.off;
        } else {
          debug(table) {                           // table
            writeln(line);
          }
          an_object["obj"] ~= line ~= "\n";           // build table array (or string)
        }
      } else if (type["tic_table"] == TriState.on) {
        if (matchFirst(line, rgx.block_tic_close)) {
          debug(table) {                           // table (tic) close
            writeln(line);
          }
          type["blocks"] = TriState.closing;
          type["table"] = TriState.closing;
          type["tic_table"] = TriState.off;
        } else {
          debug(table) {                           // table
            writeln(line);
          }
          an_object["obj"] ~= line ~= "\n";           // build table array (or string)
        }
      }
    }
    void _block_flag_line_empty_(
      char[] line,
      ref string[string] an_object,
      ref ObjComposite[] contents_the_objects,
      ref string[][string][string] bookindex_unordered_hashes,
      ref int obj_cite_number,
      ref string _node,
      ref long counter,
      ref int[string] type,
      string[string] obj_cite_number_poem,
      string[string][string] dochead_make_aa,
    ) {
      // line.empty, post contents, empty variables ---------------
      assert(
        line.empty,
        "line should be empty"
      );
      assert(
        (type["blocks"] == TriState.closing),
        "code block status: closed"
      );
      assertions_flag_types_block_status_none_or_closed(type);
      if (type["code"] == TriState.closing) {
        obj_cite_number =
          obj_cite_number_emit(type["obj_cite_number_status"]);
        an_object["bookindex"] =
          ("bookindex" in an_object) ? an_object["bookindex"] : "";
        bookindex_unordered_hashes =
          bkidx_hash(an_object["bookindex"], obj_cite_number);
        an_object["is"] = "code";
        _node =
          node_construct.node_emitter(
            content_non_header,
            segment_object_belongs_to,
            obj_cite_number,
            counter,
            heading_pointer-1,
            an_object["is"]
          );
        auto substantive_object_and_anchor_tags_tuple =
          obj_im.obj_inline_markup_and_anchor_tags(an_object, dochead_make_aa);
        an_object["substantive"] = substantive_object_and_anchor_tags_tuple[0];
        anchor_tags = substantive_object_and_anchor_tags_tuple[1];
        an_object["attrib"] =
          obj_att.obj_attributes(an_object["is"], an_object["obj"], _node);
        contents_the_objects ~=
          set_abstract_object.contents_block_code(
            an_object["is"],
            an_object["substantive"],
            an_object["attrib"],
            obj_cite_number
          );
        object_reset(an_object);
        processing.remove("verse");
        ++counter;
        type["blocks"] = TriState.off;
        type["code"] = TriState.off;
      } else if (type["poem"] == TriState.closing) {
        an_object["bookindex"] =
          ("bookindex" in an_object) ? an_object["bookindex"] : "";
        bookindex_unordered_hashes =
          bkidx_hash(an_object["bookindex"], obj_cite_number);
        // obj_cite_number = obj_cite_number_emit(type["obj_cite_number_status"]);
        an_object["is"] = "verse"; // check also
        _node =
          node_construct.node_emitter(
            content_non_header,
            segment_object_belongs_to,
            obj_cite_number,
            counter,
            heading_pointer-1,
            an_object["is"]
            // "verse"
          );
        contents_the_objects ~=
          set_abstract_object.contents_block_obj_cite_number_string(
            "poem",
            "",
            (obj_cite_number_poem["start"], obj_cite_number_poem["end"]),
            _node
          ); // bookindex
        object_reset(an_object);
        processing.remove("verse");
        // ++obj_cite_number;
        type["blocks"] = TriState.off;
        type["poem"] = TriState.off;
      } else if (type["table"] == TriState.closing) {
        obj_cite_number =
          obj_cite_number_emit(type["obj_cite_number_status"]);
        an_object["bookindex"] =
          ("bookindex" in an_object) ? an_object["bookindex"] : "";
        bookindex_unordered_hashes =
          bkidx_hash(an_object["bookindex"], obj_cite_number);
        an_object["is"] = "table";
        _node =
          node_construct.node_emitter(
            content_non_header,
            segment_object_belongs_to,
            obj_cite_number,
            counter,
            heading_pointer-1,
            an_object["is"]
          );
        auto substantive_object_and_anchor_tags_tuple =
          obj_im.obj_inline_markup_and_anchor_tags(an_object, dochead_make_aa);
        an_object["substantive"] = substantive_object_and_anchor_tags_tuple[0];
        anchor_tags = substantive_object_and_anchor_tags_tuple[1];
        an_object["attrib"] =
          obj_att.obj_attributes(an_object["is"], an_object["obj"], _node);
        contents_the_objects ~=
          set_abstract_object.contents_block(
            an_object["is"],
            an_object["substantive"],
            an_object["attrib"],
            obj_cite_number
          );
        object_reset(an_object);
        processing.remove("verse");
        ++counter;
        type["blocks"] = TriState.off;
        type["table"] = TriState.off;
      } else if (type["group"] == TriState.closing) {
        obj_cite_number =
          obj_cite_number_emit(type["obj_cite_number_status"]);
        an_object["bookindex"] =
          ("bookindex" in an_object) ? an_object["bookindex"] : "";
        bookindex_unordered_hashes =
          bkidx_hash(an_object["bookindex"], obj_cite_number);
        an_object["is"] = "group";
        _node =
          node_construct.node_emitter(
            content_non_header,
            segment_object_belongs_to,
            obj_cite_number,
            counter,
            heading_pointer-1,
            an_object["is"]
          );
        auto substantive_object_and_anchor_tags_tuple =
          obj_im.obj_inline_markup_and_anchor_tags(an_object, dochead_make_aa);
        an_object["substantive"] = substantive_object_and_anchor_tags_tuple[0];
        anchor_tags = substantive_object_and_anchor_tags_tuple[1];
        an_object["attrib"] =
          obj_att.obj_attributes(an_object["is"], an_object["obj"], _node);
        contents_the_objects ~=
          set_abstract_object.contents_block(
            an_object["is"],
            an_object["substantive"],
            an_object["attrib"],
            obj_cite_number
          );
        object_reset(an_object);
        processing.remove("verse");
        ++counter;
        type["blocks"] = TriState.off;
        type["group"] = TriState.off;
      } else if (type["block"] == TriState.closing) {
        obj_cite_number = obj_cite_number_emit(type["obj_cite_number_status"]);
        an_object["bookindex"] =
          ("bookindex" in an_object) ? an_object["bookindex"] : "";
        bookindex_unordered_hashes =
          bkidx_hash(an_object["bookindex"], obj_cite_number);
        an_object["is"] = "block";
        _node =
          node_construct.node_emitter(
            content_non_header,
            segment_object_belongs_to,
            obj_cite_number,
            counter,
            heading_pointer-1,
            an_object["is"]
           );
        auto substantive_object_and_anchor_tags_tuple =
          obj_im.obj_inline_markup_and_anchor_tags(an_object, dochead_make_aa);
        an_object["substantive"] = substantive_object_and_anchor_tags_tuple[0];
        anchor_tags = substantive_object_and_anchor_tags_tuple[1];
        an_object["attrib"] =
          obj_att.obj_attributes(an_object["is"], an_object["obj"], _node);
        contents_the_objects ~=
          set_abstract_object.contents_block(
            an_object["is"],
            an_object["substantive"],
            an_object["attrib"],
            obj_cite_number
          );
        object_reset(an_object);
        processing.remove("verse");
        ++counter;
        type["blocks"] = TriState.off;
        type["block"] = TriState.off;
      } else if (type["quote"] == TriState.closing) {
        obj_cite_number =
          obj_cite_number_emit(type["obj_cite_number_status"]);
        an_object["bookindex"] =
          ("bookindex" in an_object) ? an_object["bookindex"] : "";
        bookindex_unordered_hashes =
          bkidx_hash(an_object["bookindex"], obj_cite_number);
        an_object["is"] = "quote";
        _node =
          node_construct.node_emitter(
            content_non_header,
            segment_object_belongs_to,
            obj_cite_number,
            counter,
            heading_pointer-1,
            an_object["is"]
          );
        auto substantive_object_and_anchor_tags_tuple =
          obj_im.obj_inline_markup_and_anchor_tags(an_object, dochead_make_aa);
        an_object["substantive"] = substantive_object_and_anchor_tags_tuple[0];
        anchor_tags = substantive_object_and_anchor_tags_tuple[1];
        an_object["attrib"] =
          obj_att.obj_attributes(an_object["is"], an_object["obj"], _node);
        contents_the_objects ~=
          set_abstract_object.contents_block(
            an_object["is"],
            an_object["substantive"],
            an_object["attrib"],
            obj_cite_number
          );
        object_reset(an_object);
        processing.remove("verse");
        ++counter;
        type["blocks"] = TriState.off;
        type["quote"] = TriState.off;
      }
    }
    auto _book_index_(
      char[] line,
      ref string book_idx_tmp,
      ref string[string] an_object,
      ref int[string] type
    ) {
      if (auto m = match(line, rgx.book_index)) {
        /+ match book_index +/
        debug(bookindexmatch) {                       // book index
          writefln(
            "* [bookindex] %s\n",
            to!string(m.captures[1]),
          );
          // writeln(scr_txt_marker["blue"], to!string(m.captures[1]), "\n");
        }
        an_object["bookindex"] = to!string(m.captures[1]);
      } else if (auto m = match(line, rgx.book_index_open))  {
        /+ match open book_index +/
        type["book_index"] = State.on;
        book_idx_tmp = to!string(m.captures[1]);
        debug(bookindexmatch) {                       // book index
          writefln(
            "* [bookindex] %s\n",
            book_idx_tmp,
          );
          // writeln(scr_txt_marker["blue"], book_idx_tmp, "\n");
        }
      } else if (type["book_index"] == State.on )  {
        /+ book_index flag set +/
        if (auto m = match(line, rgx.book_index_close))  {
          type["book_index"] = State.off;
          an_object["bookindex"] = book_idx_tmp ~ to!string(m.captures[1]);
          debug(bookindexmatch) {                     // book index
            writefln(
              "* [bookindex] %s\n",
              book_idx_tmp,
            );
            // writeln(scr_txt_marker["blue"], book_idx_tmp, "\n");
          }
          book_idx_tmp = "";
        } else {
          book_idx_tmp ~= line;
        }
      }
    }
    auto _heading_found_(
      char[] line,
      string dochead_make_identify_unmarked_headings,
      ref string[string] heading_match_str,
      ref Regex!(char)[string] heading_match_rgx,
      ref int[string] type
    ) {
      if ((dochead_make_identify_unmarked_headings.length > 2)
      && (type["make_headings"] == State.off)) {
        /+ headings found +/
        debug(headingsfound) {
          writeln(dochead_make_identify_unmarked_headings);
        }
        char[][] make_headings_spl =
          split(
            cast(char[]) dochead_make_identify_unmarked_headings,
            rgx.make_heading_delimiter);
        debug(headingsfound) {
          writeln(make_headings_spl.length);
          writeln(make_headings_spl);
        }
        switch (make_headings_spl.length) {
        case 7 :
          if (!empty(make_headings_spl[6])) {
            heading_match_str["h_4"] =
              "^(" ~ to!string(make_headings_spl[6]) ~ ")";
            heading_match_rgx["h_4"] =
              regex(heading_match_str["h_4"]);
          }
          goto case;
        case 6 :
          if (!empty(make_headings_spl[5])) {
            heading_match_str["h_3"] =
              "^(" ~ to!string(make_headings_spl[5]) ~ ")";
            heading_match_rgx["h_3"] =
              regex(heading_match_str["h_3"]);
          }
          goto case;
        case 5 :
          if (!empty(make_headings_spl[4])) {
            heading_match_str["h_2"] =
              "^(" ~ to!string(make_headings_spl[4]) ~ ")";
            heading_match_rgx["h_2"] =
              regex(heading_match_str["h_2"]);
          }
          goto case;
        case 4 :
          if (!empty(make_headings_spl[3])) {
            heading_match_str["h_1"] =
              "^(" ~ to!string(make_headings_spl[3]) ~ ")";
            heading_match_rgx["h_1"] =
              regex(heading_match_str["h_1"]);
          }
          goto case;
        case 3 :
          if (!empty(make_headings_spl[2])) {
            heading_match_str["h_D"] =
              "^(" ~ to!string(make_headings_spl[2]) ~ ")";
            heading_match_rgx["h_D"] =
              regex(heading_match_str["h_D"]);
          }
          goto case;
        case 2 :
          if (!empty(make_headings_spl[1])) {
            heading_match_str["h_C"] =
              "^(" ~ to!string(make_headings_spl[1]) ~ ")";
            heading_match_rgx["h_C"] =
              regex(heading_match_str["h_C"]);
          }
          goto case;
        case 1 :
          if (!empty(make_headings_spl[0])) {
            heading_match_str["h_B"] =
              "^(" ~ to!string(make_headings_spl[0]) ~ ")";
            heading_match_rgx["h_B"] =
              regex(heading_match_str["h_B"]);
          }
          break;
        default:
          break;
        }
        type["make_headings"] = State.on;
      }
    }
    auto _heading_make_set_(
      ref char[] line,
      ref int[string] line_occur,
      ref Regex!(char)[string] heading_match_rgx,
      ref int[string] type
    ) {
      if ((type["make_headings"] == State.on)
      && ((line_occur["para"] == State.off)
      && (line_occur["heading"] == State.off))
      && ((type["para"] == State.off)
      && (type["heading"] == State.off))) {
        /+ heading make set +/
        if (matchFirst(line, heading_match_rgx["h_B"])) {
          line = "B~ " ~ line;
          debug(headingsfound) {
            writeln(line);
          }
        }
        if (matchFirst(line, heading_match_rgx["h_C"])) {
          line = "C~ " ~ line;
          debug(headingsfound) {
            writeln(line);
          }
        }
        if (matchFirst(line, heading_match_rgx["h_D"])) {
          line = "D~ " ~ line;
          debug(headingsfound) {
            writeln(line);
          }
        }
        if (matchFirst(line, heading_match_rgx["h_1"])) {
          line = "1~ " ~ line;
          debug(headingsfound) {
            writeln(line);
          }
        }
        if (matchFirst(line, heading_match_rgx["h_2"])) {
          line = "2~ " ~ line;
          debug(headingsfound) {
            writeln(line);
          }
        }
        if (matchFirst(line, heading_match_rgx["h_3"])) {
          line = "3~ " ~ line;
          debug(headingsfound) {
            writeln(line);
          }
        }
        if (matchFirst(line, heading_match_rgx["h_4"])) {
          line = "4~ " ~ line;
          debug(headingsfound) {
            writeln(line);
          }
        }
      }
    }
    auto _heading_matched_(
      char[] line,
      ref int[string] line_occur,
      ref string[string] an_object,
      ref int[string] lv,
      ref int[string] collapsed_lev,
      ref int[string] type,
      ref string[string][string] dochead_meta_aa
    ) {
      if (auto m = match(line, rgx.heading)) {
        /+ heading match +/
        type["heading"] = State.on;
        type["heading_biblio"] = State.off;
        type["para"] = State.off;
        ++line_occur["heading"];
        an_object["obj"] ~= line ~= "\n";
        an_object["lev"] ~= m.captures[1];
        // writeln("an object level: ", an_object);
        assertions_doc_structure(an_object, lv); // includes most of the logic for collapsed levels
        switch (an_object["lev"]) {
        case "A":
          an_object["obj"]=replaceFirst(an_object["obj"], rgx.variable_doc_title, dochead_meta_aa["title"]["main"]);
          an_object["obj"]=replaceFirst(an_object["obj"], rgx.variable_doc_author, dochead_meta_aa["creator"]["author"]);
          collapsed_lev["h0"] = 1;
          an_object["lev_collapsed_number"] =
            to!string(collapsed_lev["h0"]);
          lv["lv"] = DocStructMarkupHeading.h_sect_A;
          ++lv["h0"];
          lv["h1"] = State.off;
          lv["h2"] = State.off;
          lv["h3"] = State.off;
          lv["h4"] = State.off;
          lv["h5"] = State.off;
          lv["h6"] = State.off;
          lv["h7"] = State.off;
          goto default;
        case "B":
          collapsed_lev["h1"] = collapsed_lev["h0"] + 1;
          an_object["lev_collapsed_number"] =
            to!string(collapsed_lev["h1"]);
          lv["lv"] = DocStructMarkupHeading.h_sect_B;
          ++lv["h1"];
          lv["h2"] = State.off;
          lv["h3"] = State.off;
          lv["h4"] = State.off;
          lv["h5"] = State.off;
          lv["h6"] = State.off;
          lv["h7"] = State.off;
          goto default;
        case "C":
          collapsed_lev["h2"] = collapsed_lev["h1"] + 1;
          an_object["lev_collapsed_number"] =
            to!string(collapsed_lev["h2"]);
          lv["lv"] = DocStructMarkupHeading.h_sect_C;
          ++lv["h2"];
          lv["h3"] = State.off;
          lv["h4"] = State.off;
          lv["h5"] = State.off;
          lv["h6"] = State.off;
          lv["h7"] = State.off;
          goto default;
        case "D":
          collapsed_lev["h3"] = collapsed_lev["h2"] + 1;
          an_object["lev_collapsed_number"] =
            to!string(collapsed_lev["h3"]);
          lv["lv"] = DocStructMarkupHeading.h_sect_D;
          ++lv["h3"];
          lv["h4"] = State.off;
          lv["h5"] = State.off;
          lv["h6"] = State.off;
          lv["h7"] = State.off;
          goto default;
        case "1":
          if (lv["h3"] > State.off) {
            collapsed_lev["h4"] = collapsed_lev["h3"] + 1;
          } else if (lv["h2"] > State.off) {
            collapsed_lev["h4"] = collapsed_lev["h2"] + 1;
          } else if (lv["h1"] > State.off) {
            collapsed_lev["h4"] = collapsed_lev["h1"] + 1;
          } else if (lv["h0"] > State.off) {
            collapsed_lev["h4"] = collapsed_lev["h0"] + 1;
          }
          an_object["lev_collapsed_number"] =
            to!string(collapsed_lev["h4"]);
          lv["lv"] = DocStructMarkupHeading.h_text_1;
          ++lv["h4"];
          lv["h5"] = State.off;
          lv["h6"] = State.off;
          lv["h7"] = State.off;
          goto default;
        case "2":
          if (lv["h5"] > State.off) {
            an_object["lev_collapsed_number"] =
              to!string(collapsed_lev["h5"]);
          } else if (lv["h4"] > State.off) {
            collapsed_lev["h5"] = collapsed_lev["h4"] + 1;
            an_object["lev_collapsed_number"] =
              to!string(collapsed_lev["h5"]);
          }
          lv["lv"] = DocStructMarkupHeading.h_text_2;
          ++lv["h5"];
          lv["h6"] = State.off;
          lv["h7"] = State.off;
          goto default;
        case "3":
          if (lv["h6"] > State.off) {
            an_object["lev_collapsed_number"] =
              to!string(collapsed_lev["h6"]);
          } else if (lv["h5"] > State.off) {
            collapsed_lev["h6"] = collapsed_lev["h5"] + 1;
            an_object["lev_collapsed_number"] =
              to!string(collapsed_lev["h6"]);
          }
          lv["lv"] = DocStructMarkupHeading.h_text_3;
          ++lv["h6"];
          lv["h7"] = State.off;
          goto default;
        case "4":
          if (lv["h7"] > State.off) {
            an_object["lev_collapsed_number"] =
              to!string(collapsed_lev["h7"]);
          } else if (lv["h6"] > State.off) {
            collapsed_lev["h7"] = collapsed_lev["h6"] + 1;
            an_object["lev_collapsed_number"] =
              to!string(collapsed_lev["h7"]);
          }
          lv["lv"] = DocStructMarkupHeading.h_text_4;
          ++lv["h7"];
          goto default;
        default:
          an_object["lev_markup_number"] = to!string(lv["lv"]);
        }
        debug(heading) {                         // heading
          // writeln(m.captures[1], " ", m.captures[2], "\n");      // figure inclusion of post capture text
          // writeln(m.hit, "\n");
          writeln(strip(line));
        }
      }
    }
    auto _para_match_(
      char[] line,
      ref string[string] an_object,
      ref string[string] indent,
      ref bool bullet,
      ref int[string] type,
      ref int[string] line_occur
    ) {
      if (line_occur["para"] == State.off) {
        /+ para matches +/
          // paragraphs
          // (fl  ag_type["heading"] = true) &&
        if (auto m = matchFirst(line, rgx.para_indent)) {
          debug(paraindent) {                    // para indent
            writeln(line);
          }
          type["para"] = State.on;
          an_object["obj"] ~= line ~= "\n";
          indent["hang_position"] = to!string(m.captures[1]);
          indent["base_position"] = "0";
          bullet = false;
        } else if (matchFirst(line, rgx.para_bullet)) {
          debug(parabullet) {                    // para bullet
            writeln(line);
          }
          type["para"] = State.on;
          an_object["obj"] ~= line;
          indent["hang_position"] = "0";
          indent["base_position"] = "0";
          bullet = true;
        } else if (auto m = matchFirst(line, rgx.para_indent_hang)) {
          debug(paraindenthang) {                // para indent hang
            writeln(line);
          }
          type["para"] = State.on;
          an_object["obj"] ~= line;
          indent["hang_position"] = to!string(m.captures[1]);
          indent["base_position"] = to!string(m.captures[2]);
          bullet = false;
        } else if (auto m = matchFirst(line, rgx.para_bullet_indent)) {
          debug(parabulletindent) {              // para bullet indent
            writeln(line);
          }
          type["para"] = State.on;
          an_object["obj"] ~= line;
          indent["hang_position"] = to!string(m.captures[1]);
          indent["base_position"] = "0";
          bullet = true;
        } else {
          // !line.empty
          type["para"] = State.on;
          an_object["obj"] ~= line;
          indent["hang_position"] = "0";
          indent["base_position"] = "0";
          bullet = false;
        }
        ++line_occur["para"];
      }
    }
    /+ abstraction functions ↑ +/
    /+ ↓ abstraction function emitters +/
    struct OCNemitter {
      int obj_cite_number, obj_cite_number_;
      int obj_cite_number_emitter(int obj_cite_number_status_flag)
      in { assert(obj_cite_number_status_flag <= 2); }
      body {
        obj_cite_number=(obj_cite_number_status_flag == 0)
        ? ++obj_cite_number_
        : 0;
        assert(obj_cite_number >= 0);
        return obj_cite_number;
      }
      invariant() {
      }
    }
    struct ObjInlineMarkupMunge {
    // struct ObjInlineMarkupMunge : AssertObjInlineMarkup {
      string[string] obj_txt;
      int n_foot, n_foot_reg, n_foot_sp_asterisk, n_foot_sp_plus;
      string obj_txt_out, tail, note;
      private auto initialize_note_numbers() {
        n_foot = 0;
        n_foot_reg = 0;
        n_foot_sp_asterisk = 0;
        n_foot_sp_plus = 0;
      }
      private auto object_notes_(string obj_txt_in)
      in { }
      body {
        auto rgx = Rgx();
        auto mkup = InlineMarkup();
        obj_txt_out = "";
        tail = "";
        obj_txt_in = replaceAll(
          obj_txt_in,
          rgx.inline_notes_curly_sp_asterisk,
          (mkup.en_a_o ~ "*" ~ " $1" ~ mkup.en_a_c)
        );
        obj_txt_in =
          replaceAll(
            obj_txt_in,
            rgx.inline_notes_curly_sp_plus,
            (mkup.en_a_o ~ "+" ~ " $1" ~ mkup.en_a_c)
          );
        obj_txt_in =
          replaceAll(
            obj_txt_in,
            rgx.inline_notes_curly,
            (mkup.en_a_o ~ " $1" ~ mkup.en_a_c)
          );
        if (match(obj_txt_in, rgx.inline_notes_al_gen)) {
          foreach(m; matchAll(obj_txt_in, rgx.inline_text_and_note_al)) {
            if (match(obj_txt_in, rgx.inline_al_delimiter_open_asterisk)) {
              ++n_foot_sp_asterisk;
              n_foot=n_foot_sp_asterisk;
            } else if (match(obj_txt_in, rgx.inline_al_delimiter_open_plus)) {
              ++n_foot_sp_plus;
              n_foot=n_foot_sp_plus;
            } else {
              ++n_foot_reg;
              n_foot=n_foot_reg;
            }
            obj_txt_out ~= replaceFirst(
              m.hit,
              rgx.inline_al_delimiter_open_regular,
              (mkup.en_a_o ~ to!string(n_foot))
            );
            tail = m.post;
            // if (!empty(m.post)) {
            //   tail = m.post;
            // } else {
            //   tail = "";
            // }
          }
        } else {
          obj_txt_out = obj_txt_in;
        }
        debug(footnotes) {
          writeln(obj_txt_out, tail);
        }
        obj_txt_out = obj_txt_out ~ tail;
        debug(footnotesdone) {
          foreach(m; matchAll(obj_txt_out,
          (mkup.en_a_o ~ `\s*(.+?)` ~ mkup.en_a_c))) {
            writeln(m.captures[1]);
            writeln(m.hit);
          }
        }
        return obj_txt_out;
      }
      string para(string obj_txt_in)
      in { }
      body {
        auto rgx = Rgx();
        obj_txt["munge"]=obj_txt_in;
        obj_txt["munge"]=replaceFirst(obj_txt["munge"], rgx.para_attribs, "");
        obj_txt["munge"]=replaceFirst(obj_txt["munge"], rgx.obj_cite_number_off_all, "");
        obj_txt["munge"]=object_notes_(obj_txt["munge"]);
        debug(munge) {
          writeln(__LINE__);
          writeln(obj_txt_in);
          writeln(__LINE__);
          writeln(to!string(obj_txt["munge"]));
        }
        return obj_txt["munge"];
      }
      string heading(string obj_txt_in)
      in { }
      body {
        auto rgx = Rgx();
        obj_txt["munge"]=obj_txt_in;
        obj_txt["munge"]=replaceFirst(obj_txt["munge"], rgx.heading, "");
        obj_txt["munge"]=replaceFirst(obj_txt["munge"], rgx.obj_cite_number_off_all, "");
        obj_txt["munge"]=object_notes_(obj_txt["munge"]);
        debug(munge) {
          writeln(__LINE__);
          writeln(obj_txt_in);
          writeln(__LINE__);
          writeln(to!string(obj_txt["munge"]));
        }
        return obj_txt["munge"];
      }
      invariant() {
      }
      /+ revisit +/
      // string header_make(string obj_txt_in)
      // in { }
      // body {
      //   obj_txt["munge"]=obj_txt_in;
      //   return obj_txt["munge"];
      // }
      // invariant() {
      // }
      // string header_meta(string obj_txt_in)
      // in { }
      // body {
      //   obj_txt["munge"]=obj_txt_in;
      //   return obj_txt["munge"];
      // }
      // invariant() {
      // }
      string code(string obj_txt_in)
      in { }
      body {
        obj_txt["munge"]=obj_txt_in;
        return obj_txt["munge"];
      }
      invariant() {
      }
      string group(string obj_txt_in)
      in { }
      body {
        obj_txt["munge"]=obj_txt_in;
        obj_txt["munge"]=object_notes_(obj_txt["munge"]);
        return obj_txt["munge"];
      }
      invariant() {
      }
      string block(string obj_txt_in)
      in { }
      body {
        obj_txt["munge"]=obj_txt_in;
        obj_txt["munge"]=object_notes_(obj_txt["munge"]);
        return obj_txt["munge"];
      }
      invariant() {
      }
      string verse(string obj_txt_in)
      in { }
      body {
        obj_txt["munge"]=obj_txt_in;
        obj_txt["munge"]=object_notes_(obj_txt["munge"]);
        return obj_txt["munge"];
      }
      invariant() {
      }
      string quote(string obj_txt_in)
      in { }
      body {
        obj_txt["munge"]=obj_txt_in;
        return obj_txt["munge"];
      }
      invariant() {
      }
      string table(string obj_txt_in)
      in { }
      body {
        obj_txt["munge"]=obj_txt_in;
        return obj_txt["munge"];
      }
      invariant() {
      }
      string comment(string obj_txt_in)
      in { }
      body {
        obj_txt["munge"]=obj_txt_in;
        return obj_txt["munge"];
      }
      invariant() {
      }
    }
    struct ObjInlineMarkup {
      auto munge = ObjInlineMarkupMunge();
      string[string] obj_txt;
      auto obj_inline_markup_and_anchor_tags(string[string] obj_, string[string][string] dochead_make_aa)
      in { }
      body {
        obj_txt["munge"]=obj_["obj"].dup;
        obj_txt["munge"]=(match(obj_["is"], ctRegex!(`verse|code`)))
          ? obj_txt["munge"]
          : strip(obj_txt["munge"]);
        static __gshared string[] anchor_tags_ = [];
        switch (obj_["is"]) {
        case "heading":
          static __gshared string anchor_tag = "";
          // TODO WORK ON, you still need to ensure that level 1 anchor_tags are unique
          obj_txt["munge"]=_configured_auto_heading_numbering_and_segment_anchor_tags(obj_txt["munge"], obj_, dochead_make_aa);
          obj_txt["munge"]=_make_segment_anchor_tags_if_none_provided(obj_txt["munge"], obj_["lev"]);
          if (auto m = match(obj_txt["munge"], rgx.heading_anchor_tag)) {
            anchor_tag = m.captures[1];
            anchor_tags_ ~=anchor_tag;
          } else if (obj_["lev"] == "1") {
            writeln("heading anchor tag missing: ", obj_txt["munge"]);
          }
          obj_txt["munge"]=munge.heading(obj_txt["munge"]);
          break;
        case "para":
          obj_txt["munge"]=munge.para(obj_txt["munge"]);
          break;
        case "code":
          obj_txt["munge"]=munge.code(obj_txt["munge"]);
          break;
        case "group":
          obj_txt["munge"]=munge.group(obj_txt["munge"]);
          break;
        case "block":
          obj_txt["munge"]=munge.block(obj_txt["munge"]);
          break;
        case "verse":
          obj_txt["munge"]=munge.verse(obj_txt["munge"]);
          break;
        case "quote":
          obj_txt["munge"]=munge.quote(obj_txt["munge"]);
          break;
        case "table":
          obj_txt["munge"]=munge.table(obj_txt["munge"]);
          break;
        case "comment":
          obj_txt["munge"]=munge.comment(obj_txt["munge"]);
          break;
        case "doc_end_reset":
          munge.initialize_note_numbers();
          break;
        default:
          break;
        }
        auto t = tuple(
         obj_txt["munge"],
         anchor_tags_,
        );
        anchor_tags_=[];
        return t;
      }
      invariant() {
      }
    private:
      static string _configured_auto_heading_numbering_and_segment_anchor_tags(string munge_, string[string] obj_, string[string][string] dochead_make_aa) {
        if (dochead_make_aa["make"]["num_top"].length > 0) {
          if (!(match(munge_, rgx.heading_anchor_tag))) {
            static __gshared uint heading_num_top_level=9;
            static __gshared uint heading_num_depth=2;
            static __gshared uint heading_num_0 = 0;
            static __gshared uint heading_num_1 = 0;
            static __gshared uint heading_num_2 = 0;
            static __gshared uint heading_num_3 = 0;
            static __gshared string heading_number_auto_composite = "";
            if (heading_num_top_level==9) {
              if (dochead_make_aa["make"]["num_depth"].length > 0) {
                heading_num_depth = to!uint(dochead_make_aa["make"]["num_depth"]);
              }
              switch (dochead_make_aa["make"]["num_top"]) {
              case "A":
                break;
              case "B":
                heading_num_top_level=1;
                break;
              case "C":
                heading_num_top_level=2;
                break;
              case "D":
                heading_num_top_level=3;
                break;
              case "1":
                heading_num_top_level=4;
                break;
              case "2":
                heading_num_top_level=5;
                break;
              case "3":
                heading_num_top_level=6;
                break;
              case "4":
                heading_num_top_level=7;
                break;
              default:
                break;
              }
            }
            /+ num_depth minimum 0 (1.) default 2 (1.1.1) max 3 (1.1.1.1) implement +/
            if (heading_num_top_level > to!uint(obj_["lev_markup_number"])) {
              heading_num_0 = 0;
              heading_num_1 = 0;
              heading_num_2 = 0;
              heading_num_3 = 0;
            } else if (heading_num_top_level == to!uint(obj_["lev_markup_number"])) {
              heading_num_0 ++;
              heading_num_1 = 0;
              heading_num_2 = 0;
              heading_num_3 = 0;
            } else if (heading_num_top_level == (to!uint(obj_["lev_markup_number"]) - 1)) {
              heading_num_1 ++;
              heading_num_2 = 0;
              heading_num_3 = 0;
            } else if (heading_num_top_level == (to!uint(obj_["lev_markup_number"]) - 2)) {
              heading_num_2 ++;
              heading_num_3 = 0;
            } else if (heading_num_top_level == (to!uint(obj_["lev_markup_number"]) - 3)) {
              heading_num_3 ++;
            } else {
              //
            }
            if (heading_num_3 > 0) {
              heading_number_auto_composite =
                (heading_num_depth == 3)
                ? ( to!string(heading_num_0) ~ "." ~
                      to!string(heading_num_1) ~ "." ~
                      to!string(heading_num_2) ~ "." ~
                      to!string(heading_num_3)
                    )
                : "";
            } else if (heading_num_2 > 0) {
              heading_number_auto_composite =
                ((heading_num_depth >= 2)
                && (heading_num_depth <= 3))
                ?  ( to!string(heading_num_0) ~ "." ~
                      to!string(heading_num_1) ~ "." ~
                      to!string(heading_num_2)
                    )
                : "";
            } else if (heading_num_1 > 0) {
              heading_number_auto_composite =
                ((heading_num_depth >= 1)
                && (heading_num_depth <= 3))
                ? ( to!string(heading_num_0) ~ "." ~
                      to!string(heading_num_1)
                    )
                : "";
            } else if (heading_num_0 > 0) {
              heading_number_auto_composite =
                ((heading_num_depth >= 0)
                && (heading_num_depth <= 3))
                ?  (to!string(heading_num_0))
                : "";
            } else {
              heading_number_auto_composite = "";
            }
            debug(heading_number_auto) {
              writeln(heading_number_auto_composite);
            }
            if (!empty(heading_number_auto_composite)) {
              munge_=replaceFirst(munge_, rgx.heading,
                "$1~$2 " ~ heading_number_auto_composite ~ ". ");
              munge_=replaceFirst(munge_, rgx.heading_marker_missing_tag,
                "$1~" ~ heading_number_auto_composite ~ " ");
            }
          }
        }
        return munge_;
      }
    
    
      static string _make_segment_anchor_tags_if_none_provided(string munge_, string lev_) {
        if (!(match(munge_, rgx.heading_anchor_tag))) { // if (anchor_tags_.length == 0) {
          if (match(munge_, rgx.heading_identify_anchor_tag)) {
            if (auto m = match(munge_, rgx.heading_extract_named_anchor_tag)) {
              munge_=replaceFirst(munge_, rgx.heading_marker_missing_tag,
                "$1~" ~ toLower(m.captures[1]) ~ "_"  ~ m.captures[2] ~ " ");
            } else if (auto m = match(munge_, rgx.heading_extract_unnamed_anchor_tag)) {
              munge_=replaceFirst(munge_, rgx.heading_marker_missing_tag,
                "$1~" ~ "s" ~ m.captures[1] ~ " ");
            }
          } else if (lev_ == "1") { // (if not successful) manufacture a unique anchor tag for lev=="1"
            static __gshared uint heading_num_lev1 = 0;
            heading_num_lev1 ++;
            munge_=replaceFirst(munge_, rgx.heading_marker_missing_tag,
              "$1~" ~ "x" ~ to!string(heading_num_lev1) ~ " ");
          }
        }
        return munge_;
      }
      unittest {
        string txt_lev="1";
        string txt_in, txt_out;
    
        txt_in = "1~copyright Copyright";
        txt_out ="1~copyright Copyright";
        assert(_make_segment_anchor_tags_if_none_provided(txt_in, txt_lev) == txt_out);
        // assert(ObjInlineMarkup._make_segment_anchor_tags_if_none_provided(txt_in, txt_lev) == txt_out);
    
        txt_in = "1~ 6. Writing Copyright Licenses";
        txt_out ="1~s6 6. Writing Copyright Licenses";
        assert(_make_segment_anchor_tags_if_none_provided(txt_in, txt_lev) == txt_out);
    
        txt_in= "1~ 1. Reinforcing trends";
        txt_out= "1~s1 1. Reinforcing trends";
        assert(_make_segment_anchor_tags_if_none_provided(txt_in, txt_lev) == txt_out);
    
        txt_in= "1~ 11 SCIENCE AS A COMMONS";
        txt_out= "1~s11 11 SCIENCE AS A COMMONS";
        assert(_make_segment_anchor_tags_if_none_provided(txt_in, txt_lev) == txt_out);
    
        txt_in= "1~ Chapter 1";
        txt_out="1~chapter_1 Chapter 1";
        assert(_make_segment_anchor_tags_if_none_provided(txt_in, txt_lev) == txt_out);
    
        txt_in= "1~ Chapter 1.";
        txt_out="1~chapter_1 Chapter 1.";
        assert(_make_segment_anchor_tags_if_none_provided(txt_in, txt_lev) == txt_out);
    
        txt_in= "1~ Chapter 1: Done";
        txt_out="1~chapter_1 Chapter 1: Done";
        assert(_make_segment_anchor_tags_if_none_provided(txt_in, txt_lev) == txt_out);
    
        txt_in=  "1~ Chapter 11 - The Battle Over the Institutional Ecology of the Digital Environment";
        txt_out= "1~chapter_11 Chapter 11 - The Battle Over the Institutional Ecology of the Digital Environment";
        assert(_make_segment_anchor_tags_if_none_provided(txt_in, txt_lev) == txt_out);
    
        txt_in= "1~ CHAPTER I.";
        txt_out="1~x1 CHAPTER I.";
        assert(_make_segment_anchor_tags_if_none_provided(txt_in, txt_lev) == txt_out);
    
        txt_in= "1~ CHAPTER II.";
        txt_out="1~x2 CHAPTER II.";
        assert(_make_segment_anchor_tags_if_none_provided(txt_in, txt_lev) == txt_out);
      }
    }
    struct ObjAttributes {
      string[string] _obj_attrib;
      string obj_attributes(string obj_is_, string obj_raw, string _node)
      in { }
      body {
        scope(exit) {
          destroy(obj_is_);
          destroy(obj_raw);
          destroy(_node);
        }
        _obj_attrib.remove("json");
        _obj_attrib["json"] ="{";
        switch (obj_is_) {
        case "heading":
          _obj_attrib["json"] ~= _heading(obj_raw); //
          break;
        case "para":
          _obj_attrib["json"] ~= _para_and_blocks(obj_raw)
          ~ _para(obj_raw);
          break;
        case "code":
          _obj_attrib["json"] ~= _code(obj_raw);
          break;
        case "group":
          _obj_attrib["json"] ~= _para_and_blocks(obj_raw)
          ~ _group(obj_raw);
          break;
        case "block":
          _obj_attrib["json"] ~= _para_and_blocks(obj_raw)
          ~ _block(obj_raw);
          break;
        case "verse":
          _obj_attrib["json"] ~= _verse(obj_raw);
          break;
        case "quote":
          _obj_attrib["json"] ~= _quote(obj_raw);
          break;
        case "table":
          _obj_attrib["json"] ~= _table(obj_raw);
          break;
        case "comment":
          _obj_attrib["json"] ~= _comment(obj_raw);
          break;
        default:
          _obj_attrib["json"] ~= _para(obj_raw);
          break;
        }
        _obj_attrib["json"] ~=" }";
        _obj_attrib["json"]=_set_additional_values_parse_as_json(_obj_attrib["json"], obj_is_, _node);
        debug(structattrib) {
          if (oa_j["is"].str() == "heading") {
            // writeln(__LINE__);
            writeln(_obj_attrib["json"]);
            // writeln(_node);
            writeln(
              "is: ", oa_j["is"].str(),
              "; obj_cite_number: ", oa_j["obj_cite_number"].integer()
            );
          }
        }
        return _obj_attrib["json"];
      }
      invariant() {
      }
      private:
      string _obj_attributes;
      string _para_and_blocks(string obj_txt_in)
      in { }
      body {
        auto rgx = Rgx();
        if (matchFirst(obj_txt_in, rgx.para_bullet)) {
          _obj_attributes =" \"bullet\": \"true\","
          ~ " \"indent_start\": 0,"
          ~ " \"indent_rest\": 0,";
        } else if (auto m = matchFirst(obj_txt_in, rgx.para_bullet_indent)) {
          _obj_attributes =" \"bullet\": \"true\","
          ~ " \"indent_start\": " ~ to!string(m.captures[1]) ~ ","
          ~ " \"indent_rest\": " ~ to!string(m.captures[1]) ~ ",";
        } else if (auto m = matchFirst(obj_txt_in, rgx.para_indent_hang)) {
          _obj_attributes =" \"bullet\": \"false\","
          ~ " \"indent_start\": " ~ to!string(m.captures[1]) ~ ","
          ~ " \"indent_rest\": " ~  to!string(m.captures[2]) ~ ",";
        } else if (auto m = matchFirst(obj_txt_in, rgx.para_indent)) {
          _obj_attributes =" \"bullet\": \"false\","
          ~ " \"indent_start\": " ~ to!string(m.captures[1]) ~ ","
          ~ " \"indent_rest\": " ~ to!string(m.captures[1]) ~ ",";
        } else {
          _obj_attributes =" \"bullet\": \"false\","
          ~ " \"indent_start\": 0,"
          ~ " \"indent_rest\": 0,";
        }
        return _obj_attributes;
      }
      string _para(string obj_txt_in)
      in { }
      body {
        _obj_attributes = " \"use\": \"content\","
        ~ " \"of\": \"para\","
        ~ " \"is\": \"para\"";
        return _obj_attributes;
      }
      invariant() {
      }
      string _heading(string obj_txt_in)
      in { }
      body {
        _obj_attributes = " \"use\": \"content\","
        ~ " \"of\": \"para\","
        ~ " \"is\": \"heading\"";
        return _obj_attributes;
      }
      invariant() {
      }
      string _code(string obj_txt_in)
      in { }
      body {
        _obj_attributes = " \"use\": \"content\","
        ~ " \"of\": \"block\","
        ~ " \"is\": \"code\"";
        return _obj_attributes;
      }
      invariant() {
      }
      string _group(string obj_txt_in)
      in { }
      body {
        _obj_attributes = " \"use\": \"content\","
        ~ " \"of\": \"block\","
        ~ " \"is\": \"group\"";
        return _obj_attributes;
      }
      invariant() {
      }
      string _block(string obj_txt_in)
      in { }
      body {
        _obj_attributes = " \"use\": \"content\","
        ~ " \"of\": \"block\","
        ~ " \"is\": \"block\"";
        return _obj_attributes;
      }
      invariant() {
      }
      string _verse(string obj_txt_in)
      in { }
      body {
        _obj_attributes = " \"use\": \"content\","
        ~ " \"of\": \"block\","
        ~ " \"is\": \"verse\"";
        return _obj_attributes;
      }
      invariant() {
      }
      string _quote(string obj_txt_in)
      in { }
      body {
        _obj_attributes = " \"use\": \"content\","
        ~ " \"of\": \"block\","
        ~ " \"is\": \"quote\"";
        return _obj_attributes;
      }
      invariant() {
      }
      string _table(string obj_txt_in)
      in { }
      body {
        _obj_attributes = " \"use\": \"content\","
        ~ " \"of\": \"block\","
        ~ " \"is\": \"table\"";
        return _obj_attributes;
      }
      invariant() {
      }
      string _comment(string obj_txt_in)
      in { }
      body {
        _obj_attributes = " \"use\": \"comment\","
        ~ " \"of\": \"comment\","
        ~ " \"is\": \"comment\"";
        return _obj_attributes;
      }
      invariant() {
      }
      string _set_additional_values_parse_as_json(string _obj_attrib, string obj_is_, string _node) {
        JSONValue oa_j = parseJSON(_obj_attrib);
        JSONValue node_j = parseJSON(_node);
        assert(
          (oa_j.type == JSON_TYPE.OBJECT) &&
          (node_j.type == JSON_TYPE.OBJECT)
        );
        if (obj_is_ == "heading") {
          oa_j.object["obj_cite_number"] = node_j["obj_cite_number"];
          oa_j.object["lev_markup_number"] = node_j["lev_markup_number"];
          oa_j.object["lev_collapsed_number"] = node_j["lev_collapsed_number"];
          oa_j.object["heading_pointer"] =
            node_j["heading_pointer"]; // check
          oa_j.object["doc_object_pointer"] =
            node_j["doc_object_pointer"]; // check
        }
        oa_j.object["parent_obj_cite_number"] = node_j["parent_obj_cite_number"];
        oa_j.object["parent_lev_markup_number"] = node_j["parent_lev_markup_number"];
        _obj_attrib = oa_j.toString();
        return _obj_attrib;
      }
    }
    struct BookIndexNuggetHash {
      string main_term, sub_term, sub_term_bits;
      int obj_cite_number_offset, obj_cite_number_endpoint;
      string[] obj_cite_numbers;
      string[][string][string] bi;
      string[][string][string] hash_nugget;
      string[] bi_main_terms_split_arr;
      string[][string][string] bookindex_nugget_hash(string bookindex_section, int obj_cite_number)
      in {
        debug(bookindexraw) {
          if (!bookindex_section.empty) {
            writeln(
              "* [bookindex] ",
              "[", to!string(obj_cite_number), "] ", bookindex_section
            );
          }
        }
      }
      body {
        auto rgx = Rgx();
        if (!bookindex_section.empty) {
          auto bi_main_terms_split_arr =
            split(bookindex_section, rgx.bi_main_terms_split);
          foreach (bi_main_terms_content; bi_main_terms_split_arr) {
            auto bi_main_term_and_rest =
              split(bi_main_terms_content, rgx.bi_main_term_plus_rest_split);
            if (auto m = match(
              bi_main_term_and_rest[0],
              rgx.bi_term_and_obj_cite_numbers_match)
            ) {
              main_term = strip(m.captures[1]);
              obj_cite_number_offset = to!int(m.captures[2]);
              obj_cite_number_endpoint=(obj_cite_number + obj_cite_number_offset);
              obj_cite_numbers ~= (to!string(obj_cite_number) ~ "-" ~ to!string(obj_cite_number_endpoint));
            } else {
              main_term = strip(bi_main_term_and_rest[0]);
              obj_cite_numbers ~= to!string(obj_cite_number);
            }
            bi[main_term]["_a"] ~= obj_cite_numbers;
            obj_cite_numbers=null;
            if (bi_main_term_and_rest.length > 1) {
              auto bi_sub_terms_split_arr =
                split(
                  bi_main_term_and_rest[1],
                  rgx.bi_sub_terms_plus_obj_cite_number_offset_split
                );
              foreach (sub_terms_bits; bi_sub_terms_split_arr) {
                if (auto m = match(sub_terms_bits, rgx.bi_term_and_obj_cite_numbers_match)) {
                  sub_term = strip(m.captures[1]);
                  obj_cite_number_offset = to!int(m.captures[2]);
                  obj_cite_number_endpoint=(obj_cite_number + obj_cite_number_offset);
                  obj_cite_numbers ~= (to!string(obj_cite_number) ~ " - " ~ to!string(obj_cite_number_endpoint));
                } else {
                  sub_term = strip(sub_terms_bits);
                  obj_cite_numbers ~= to!string(obj_cite_number);
                }
                if (!empty(sub_term)) {
                  bi[main_term][sub_term] ~= obj_cite_numbers;
                }
                obj_cite_numbers=null;
              }
            }
          }
        }
        hash_nugget = bi;
        return hash_nugget;
      }
      invariant() {
      }
    }
    struct BookIndexReportIndent {
      int mkn, skn;
      auto bookindex_report_indented(
        string[][string][string] bookindex_unordered_hashes
      ) {
        auto mainkeys=
          bookindex_unordered_hashes.byKey.array.sort().release;
        foreach (mainkey; mainkeys) {
          debug(bookindex) {
            writeln(mainkey);
          }
          auto subkeys=
            bookindex_unordered_hashes[mainkey].byKey.array.sort().release;
          foreach (subkey; subkeys) {
            debug(bookindex) {
              writeln("  ", subkey);
              writeln("    ", to!string(
                bookindex_unordered_hashes[mainkey][subkey]
              ));
            }
            // bookindex_the[mkn][mainkey][skn][subkey] ~= (bookindex_unordered_hashes[mainkey][subkey]);
            ++skn;
          }
          ++mkn;
        }
      }
    }
    struct BookIndexReportSection {
      int mkn, skn;
      auto rgx = Rgx();
      auto bookindex_write_section(
        string[][string][string] bookindex_unordered_hashes
      ) {
        auto mainkeys=bookindex_unordered_hashes.byKey.array.sort().release;
        foreach (mainkey; mainkeys) {
          write("_0_1 !{", mainkey, "}! ");
          foreach (ref_; bookindex_unordered_hashes[mainkey]["_a"]) {
            auto go = replaceAll(ref_, rgx.book_index_go, "$1");
            write(" {", ref_, "}#", go, ", ");
          }
          writeln(" \\\\");
          bookindex_unordered_hashes[mainkey].remove("_a");
          auto subkeys=
            bookindex_unordered_hashes[mainkey].byKey.array.sort().release;
          foreach (subkey; subkeys) {
            write("  ", subkey, ", ");
            foreach (ref_; bookindex_unordered_hashes[mainkey][subkey]) {
              auto go = replaceAll(ref_, rgx.book_index_go, "$1");
              write(" {", ref_, "}#", go, ", ");
            }
            writeln(" \\\\");
            ++skn;
          }
          ++mkn;
        }
      }
      auto bookindex_build_section(
        string[][string][string] bookindex_unordered_hashes,
        int obj_cite_number,
        string segment_object_belongs_to,
      ) {
        string type;
        string lev, lev_markup_number, lev_collapsed_number;
        string attrib;
        string[string] indent;
        auto set_abstract_object = ObjectAbstractSet();
        auto mainkeys =
          bookindex_unordered_hashes.byKey.array.sort().release;
        string bi_tmp;
        ObjComposite[] bookindex_section;
        // writeln(mainkeys.length);
        // B~ Book Index
        attrib="";
        lev="B";
        lev_markup_number="1";
        lev_collapsed_number="1";
        bookindex_section ~=
          set_abstract_object.contents_heading(
            "Book Index",
            attrib,
            obj_cite_number,
            [],
            to!string(lev),
            to!int(lev_markup_number),
            to!int(lev_collapsed_number)
          );
        ++obj_cite_number;
        ++mkn;
        // 1~ Index
        attrib="";
        lev="1";
        lev_markup_number="4";
        lev_collapsed_number="2";
        bookindex_section ~=
          set_abstract_object.contents_heading(
            "Index",
            attrib,
            obj_cite_number,
            ["book_index"],
            to!string(lev),
            to!int(lev_markup_number),
            to!int(lev_collapsed_number)
          );
        ++obj_cite_number;
        ++mkn;
        foreach (mainkey; mainkeys) {
          bi_tmp = "!{" ~ mainkey ~ "}! ";
          // bi_tmp = "_0_1 !{" ~ mainkey ~ "}! ";
          foreach (ref_; bookindex_unordered_hashes[mainkey]["_a"]) {
            auto go = replaceAll(ref_, rgx.book_index_go, "$1");
            bi_tmp ~= (segment_object_belongs_to.empty)
            ? (" {" ~ ref_ ~ "}#" ~ go ~ ", ")
            : (" {" ~ ref_ ~ "}[../" ~ segment_object_belongs_to ~ ".fn_suffix]#" ~ go ~ ", ");
          }
          bi_tmp ~= " \\\\\n    ";
          bookindex_unordered_hashes[mainkey].remove("_a");
          auto subkeys =
            bookindex_unordered_hashes[mainkey].byKey.array.sort().release;
          foreach (subkey; subkeys) {
            bi_tmp ~= subkey ~ ", ";
            foreach (ref_; bookindex_unordered_hashes[mainkey][subkey]) {
              auto go = replaceAll(ref_, rgx.book_index_go, "$1");
              bi_tmp ~= (segment_object_belongs_to.empty)
              ? (" {" ~ ref_ ~ "}#" ~ go ~ ", ")
              : (" {" ~ ref_ ~ "}[../" ~ segment_object_belongs_to ~ ".fn_suffix]#" ~ go ~ ", ");
            }
            bi_tmp ~= " \\\\\n    ";
            ++skn;
          }
          bi_tmp = replaceFirst(bi_tmp, rgx.trailing_linebreak, "");
          type="para";
          attrib="";
          indent["hang_position"] = "0";
          indent["base_position"] = "1";
          attrib="";
          // bookindex_section ~=
          //   set_abstract_object.contents_para(
          //     obj,
          //     obj_cite_number,
          //     indent,
          //     false
          //   );
          bookindex_section ~=
            set_abstract_object.contents_para(
              type,
              bi_tmp,
              attrib,
              obj_cite_number,
              indent,
              false
            );
          ++obj_cite_number;
          ++mkn;
        }
        auto t = tuple(bookindex_section, obj_cite_number);
        return t;
      }
    }
    struct NotesSection {
      string object_notes;
      long previous_count;
      int mkn;
      auto rgx = Rgx();
      private auto gather_notes_for_endnote_section(
        ObjComposite[] contents_am,
        string segment_object_belongs_to,
        ulong counter
      )
      in {
        // endnotes/ footnotes for
        // doc objects other than paragraphs & headings
        // various forms of grouped text
        assert((contents_am[counter].is_a == "para")
        || (contents_am[counter].is_a == "heading"));
        assert(counter > previous_count);
        previous_count=counter;
        assert(
          match(contents_am[counter].object,
          rgx.inline_notes_delimiter_al_regular_number_note)
        );
      }
      body {
        foreach(m;
        matchAll(contents_am[counter].object,
        rgx.inline_notes_delimiter_al_regular_number_note)) {
          debug(endnotes_build) {
            writeln(
              "{^{", m.captures[1], ".}^}[../", segment_object_belongs_to, ".fn_suffix]#noteref_\n  ", m.captures[1], " ",
              m.captures[2]); // sometimes need segment name (segmented html & epub)
            // writeln("{^{", m.captures[1], ".}^}#", contents_am[counter]["obj_cite_number"], " ", m.captures[2]);
          }
          object_notes ~= (segment_object_belongs_to.empty)
          ? ("{^{" ~ m.captures[1] ~ ".}^}#noteref_" ~
            m.captures[1] ~ " " ~ m.captures[2] ~ "』")
          : ("{^{" ~ m.captures[1] ~ ".}^}[../" ~ segment_object_belongs_to ~ ".fn_suffix]#noteref_" ~
            m.captures[1] ~ " " ~ m.captures[2] ~ "』");
        }
        return object_notes;
      }
      private auto gathered_notes()
      in {
      }
      body {
        string[] endnotes_;
        if (object_notes.length > 1) {
          endnotes_ = (split(object_notes, rgx.break_string))[0..$-1];
        }
        return endnotes_;
      }
      private auto endnote_objects(int obj_cite_number)
      in {
      }
      body {
        auto set_abstract_object = ObjectAbstractSet();
        ObjComposite[] endnotes_section;
        auto endnotes_ = gathered_notes();
        // auto endnotes_ = (split(object_notes, rgx.break_string))[0..$-1];
        string type;
        string lev, lev_markup_number, lev_collapsed_number;
        string attrib;
        string[string] indent;
        // B~ Endnotes
        attrib="";
        lev="B";
        lev_markup_number="1";
        lev_collapsed_number="1";
        endnotes_section ~=
          set_abstract_object.contents_heading(
            "Endnotes",
            attrib,
            obj_cite_number,
            [],
            to!string(lev),
            to!int(lev_markup_number),
            to!int(lev_collapsed_number)
          );
        ++obj_cite_number;
        ++mkn;
        // 1~ Endnotes
        attrib="";
        lev="1";
        lev_markup_number="4";
        lev_collapsed_number="2";
        endnotes_section ~=
          set_abstract_object.contents_heading(
            "Endnotes",
            attrib,
            obj_cite_number,
            ["endnotes"],
            to!string(lev),
            to!int(lev_markup_number),
            to!int(lev_collapsed_number)
          );
        ++obj_cite_number;
        ++mkn;
        foreach (endnote; endnotes_) {
          attrib="";
          // endnotes ~=
          //   set_abstract_object.contents_para(
          //     obj,
          //     obj_cite_number,
          //     indent,
          //     false
          //   );
          endnotes_section ~=
            set_abstract_object.contents_endnote(
              endnote,
            );
          ++mkn;
        }
        auto t = tuple(endnotes_section, obj_cite_number);
        return t;
      }
    }
    struct Bibliography {
      public JSONValue[] _bibliography_(
        ref string[] biblio_unsorted_incomplete,
        ref JSONValue[] bib_arr_json
      )
      in { }
      body {
        JSONValue[] biblio_unsorted =
          _biblio_unsorted_complete_(biblio_unsorted_incomplete, bib_arr_json);
        JSONValue[] biblio_sorted__ = biblio_sort(biblio_unsorted);
        biblio_debug(biblio_sorted__);
        debug(biblio0) {
          writeln("---");
          writeln("unsorted incomplete: ", biblio_unsorted_incomplete.length);
          writeln("json:                ", bib_arr_json.length);
          writeln("unsorted:            ", biblio_unsorted.length);
          writeln("sorted:              ", biblio_sorted__.length);
          //  writeln("0: ", biblio_sorted__[0]);
          int counter;
          int[7] x;
          while (counter < x.length) {
            writeln(counter, ": ", biblio_sorted__[counter]["fulltitle"]);
            counter++;
          }
        }
        return biblio_sorted__;
      }
      final private JSONValue[] _biblio_unsorted_complete_(
        string[] biblio_unordered,
        ref JSONValue[] bib_arr_json
      ) {
        foreach (bibent; biblio_unordered) {
          // update bib to include deemed_author, needed for:
          // sort_bibliography_array_by_deemed_author_year_title
          // either: sort on multiple fields, or; create such sort field
          JSONValue j = parseJSON(bibent);
          if (!empty(j["fulltitle"].str)) {
            if (!empty(j["author_raw"].str)) {
              j["deemed_author"]=j["author_arr"][0];
            } else if (!empty(j["editor_raw"].str)) {
              j["deemed_author"]=j["editor_arr"][0];
            }
            j["sortby_deemed_author_year_title"] = (
              j["deemed_author"].str ~
               "; " ~
               j["year"].str ~
               "; "  ~
               j["fulltitle"].str
            );
          }
          bib_arr_json ~= j;
        }
        JSONValue[] biblio_unsorted_array_of_json_objects =
          bib_arr_json.dup;
        return biblio_unsorted_array_of_json_objects;
      }
      final private JSONValue[] biblio_sort(JSONValue[] biblio_unordered) {
        JSONValue[] biblio_sorted_;
        biblio_sorted_ =
          sort!((a, b){
            return ((a["sortby_deemed_author_year_title"].str) < (b["sortby_deemed_author_year_title"].str));
          })(biblio_unordered).array;
        debug(bibliosorted) {
          foreach (j; biblio_sorted_) {
            if (!empty(j["fulltitle"].str)) {
              writeln(j["sortby_deemed_author_year_title"]);
              // writeln(j["deemed_author"], " (", j["author"], ") ",  j["fulltitle"]);
            }
          }
        }
        return biblio_sorted_;
      }
      void biblio_debug(JSONValue[] biblio_sorted) {
        debug(biblio0) {
          foreach (j; biblio_sorted) {
            if (!empty(j["fulltitle"].str)) {
              writeln(j["sortby_deemed_author_year_title"]);
            }
          }
        }
      }
    }
    struct NodeStructureMetadata {
      int lv, lv0, lv1, lv2, lv3, lv4, lv5, lv6, lv7;
      int obj_cite_number;
      int[string] p_; // p_ parent_
      string _node;
      string node_emitter(
        string lev_markup_number,
        string segment_anchor_tag,
        int obj_cite_number_,
        long counter_,
        int pointer_,
        string is_
      )
      in {
        auto rgx = Rgx();
        assert(is_ != "heading");
        assert(to!int(obj_cite_number_) >= 0);
      }
      body {
        // scope(failure) {
        //   writeln(__FILE__, ":", __LINE__, " failed here:");
        //   writeln("  is  : ", is_);
        //   writeln("  node: ", _node);
        // }
        assert(is_ != "heading"); // should not be necessary
        assert(to!int(obj_cite_number_) >= 0); // should not be necessary
        int obj_cite_number=to!int(obj_cite_number_);
        if (lv7 > State.off) {
          p_["lev_markup_number"] = DocStructMarkupHeading.h_text_4;
          p_["obj_cite_number"] = lv7;
        } else if (lv6 > State.off) {
          p_["lev_markup_number"] = DocStructMarkupHeading.h_text_3;
          p_["obj_cite_number"] = lv6;
        } else if (lv5 > State.off) {
          p_["lev_markup_number"] = DocStructMarkupHeading.h_text_2;
          p_["obj_cite_number"] = lv5;
        } else {
          p_["lev_markup_number"] = DocStructMarkupHeading.h_text_1;
          p_["obj_cite_number"] = lv4;
        }
        _node=("{ " ~
          "\"is\": \"" ~ is_ ~ "\"" ~
          ", \"heading_pointer\": " ~ to!string(pointer_) ~
          ", \"doc_object_pointer\": " ~ to!string(counter_) ~
          ", \"obj_cite_number\": " ~ to!string(obj_cite_number_) ~
          ", \"segment_anchor_tag\": \"" ~ segment_anchor_tag ~ "\"" ~
          ", \"parent_obj_cite_number\": " ~ to!string(p_["obj_cite_number"]) ~
          ", \"parent_lev_markup_number\": " ~ to!string(p_["lev_markup_number"]) ~
          " }"
        );
        debug(node) {
          if (match(lev_markup_number, rgx.levels_numbered_headings)) {
            writeln("* ", to!string(_node));
          } else {
            writeln("* ", to!string(_node));
          }
        }
        JSONValue j = parseJSON(_node);
        assert(j["parent_lev_markup_number"].integer >= 4);
        assert(j["parent_lev_markup_number"].integer <= 7);
        assert(j["parent_obj_cite_number"].integer >= 0);
        return _node;
      }
      invariant() {
      }
      string node_emitter_heading(
        string lev_markup_number,
        string lev_collapsed_number,
        string segment_anchor_tag,
        int obj_cite_number_,
        long counter_,
        int pointer_,
        string is_
      )
      in {
        auto rgx = Rgx();
        assert(is_ == "heading");
        assert(to!int(obj_cite_number_) >= 0);
        assert(
          match(lev_markup_number, rgx.levels_numbered),
          ("not a valid heading level: " ~ lev_markup_number ~ " at " ~ to!string(obj_cite_number_))
        );
        // assert(to!int(obj_cite_number_) >= 0);
        if (match(lev_markup_number, rgx.levels_numbered)) {
          if (to!int(lev_markup_number) == 0) {
            assert(to!int(obj_cite_number_) == 1);
            // writeln(lev_markup_number);
          }
        }
      }
      body {
        // scope(failure) {
        //   writeln(__FILE__, ":", __LINE__, " failed here:");
        //   writeln("  is  : ", is_);
        //   writeln("  node: ", _node);
        // }
        auto rgx = Rgx();
        int obj_cite_number = to!int(obj_cite_number_);
        switch (lev_markup_number) { // switch (to!string(lv)) {
        case "0":
          lv = DocStructMarkupHeading.h_sect_A;
          lv0 = obj_cite_number;
          lv1=0; lv2=0; lv3=0; lv4=0; lv5=0; lv6=0; lv7=0;
          p_["lev_markup_number"] = 0;
          p_["obj_cite_number"] = 0;
          break;
        case "1":
          lv = DocStructMarkupHeading.h_sect_B;
          lv1 = obj_cite_number;
          lv2=0; lv3=0; lv4=0; lv5=0; lv6=0; lv7=0;
          p_["lev_markup_number"] =
            DocStructMarkupHeading.h_sect_A;
          p_["obj_cite_number"] = lv0;
          break;
        case "2":
          lv = DocStructMarkupHeading.h_sect_C;
          lv2 = obj_cite_number;
          lv3=0; lv4=0; lv5=0; lv6=0; lv7=0;
          p_["lev_markup_number"] =
            DocStructMarkupHeading.h_sect_B;
          p_["obj_cite_number"] = lv1;
          break;
        case "3":
          lv = DocStructMarkupHeading.h_sect_D;
          lv3=obj_cite_number;
          lv4=0; lv5=0; lv6=0; lv7=0;
          p_["lev_markup_number"] =
            DocStructMarkupHeading.h_sect_C;
          p_["obj_cite_number"] = lv2;
          break;
        case "4":
          lv = DocStructMarkupHeading.h_text_1;
          lv4 = obj_cite_number;
          lv5=0; lv6=0; lv7=0;
          if (lv3 > State.off) {
            p_["lev_markup_number"] =
              DocStructMarkupHeading.h_sect_D;
            p_["obj_cite_number"] = lv3;
          } else if (lv2 > State.off) {
            p_["lev_markup_number"] =
              DocStructMarkupHeading.h_sect_C;
            p_["obj_cite_number"] = lv2;
          } else if (lv1 > State.off) {
            p_["lev_markup_number"] =
              DocStructMarkupHeading.h_sect_B;
            p_["obj_cite_number"] = lv1;
          } else {
            p_["lev_markup_number"] =
              DocStructMarkupHeading.h_sect_A;
            p_["obj_cite_number"] = lv0;
          }
          break;
        case "5":
          lv = DocStructMarkupHeading.h_text_2;
          lv5 = obj_cite_number;
          lv6=0; lv7=0;
          p_["lev_markup_number"] =
            DocStructMarkupHeading.h_text_1;
          p_["obj_cite_number"] = lv4;
          break;
        case "6":
          lv = DocStructMarkupHeading.h_text_3;
          lv6 = obj_cite_number;
          lv7=0;
          p_["lev_markup_number"] =
            DocStructMarkupHeading.h_text_2;
          p_["obj_cite_number"] = lv5;
          break;
        case "7":
          lv = DocStructMarkupHeading.h_text_4;
          lv7 = obj_cite_number;
          p_["lev_markup_number"] =
            DocStructMarkupHeading.h_text_3;
          p_["obj_cite_number"] = lv6;
          break;
        default:
          // if (lv7 > State.off) {
          //   p_["lev_markup_number"] = 7; p_["obj_cite_number"] = lv7;
          // } else if (lv6 > State.off) {
          //   p_["lev_markup_number"] = 6; p_["obj_cite_number"] = lv6;
          // } else if (lv5 > State.off) {
          //   p_["lev_markup_number"] = 5; p_["obj_cite_number"] = lv5;
          // } else {
          //   p_["lev_markup_number"] = 4; p_["obj_cite_number"] = lv4;
          // }
          break;
        }
        _node=("{ " ~
          "\"is\": \"" ~ is_ ~ "\"" ~
          ", \"heading_pointer\": " ~ to!string(pointer_) ~
          ", \"doc_object_pointer\": " ~ to!string(counter_) ~
          ", \"obj_cite_number\": " ~ to!string(obj_cite_number_) ~
          ",  \"lev_markup_number\": " ~ to!string(lev_markup_number) ~
          ",  \"lev_collapsed_number\": " ~ to!string(lev_collapsed_number) ~
          ", \"segment_anchor_tag\": \"" ~ segment_anchor_tag ~ "\"" ~
          ", \"parent_obj_cite_number\": " ~ to!string(p_["obj_cite_number"]) ~
          ", \"parent_lev_markup_number\": " ~ to!string(p_["lev_markup_number"]) ~
          " }"
        );
        debug(heading) {
          if (match(lev_markup_number, rgx.levels_numbered_headings)) {
            writeln("* ", to!string(_node));
          }
        }
        debug(node) {
          if (match(lev_markup_number, rgx.levels_numbered_headings)) {
            writeln("* ", to!string(_node));
          } else {
            writeln("* ", to!string(_node));
          }
        }
        JSONValue j = parseJSON(_node);
        assert(j["parent_lev_markup_number"].integer <= 7);
        assert(j["parent_obj_cite_number"].integer >= 0);
        if (match(lev_markup_number, rgx.levels_numbered_headings)) {
          assert(j["lev_markup_number"].integer <= 7);
          assert(j["obj_cite_number"].integer >= 0);
          if (j["parent_lev_markup_number"].integer > 0) {
            assert(j["parent_lev_markup_number"].integer < j["lev_markup_number"].integer);
            if (j["obj_cite_number"].integer != 0) {
              assert(j["parent_obj_cite_number"].integer < j["obj_cite_number"].integer);
            }
          }
          if (j["lev_markup_number"].integer == 0) {
            assert(j["parent_lev_markup_number"].integer == DocStructMarkupHeading.h_sect_A);
          } else if  (j["lev_markup_number"].integer == DocStructMarkupHeading.h_sect_B) {
            assert(j["parent_lev_markup_number"].integer == DocStructMarkupHeading.h_sect_A);
          } else if  (j["lev_markup_number"].integer == DocStructMarkupHeading.h_sect_C) {
            assert(j["parent_lev_markup_number"].integer == DocStructMarkupHeading.h_sect_B);
          } else if  (j["lev_markup_number"].integer == DocStructMarkupHeading.h_sect_D) {
            assert(j["parent_lev_markup_number"].integer == DocStructMarkupHeading.h_sect_C);
          } else if  (j["lev_markup_number"].integer == DocStructMarkupHeading.h_text_1) {
            assert(j["parent_lev_markup_number"].integer <= DocStructMarkupHeading.h_sect_D);
          } else if  (j["lev_markup_number"].integer == DocStructMarkupHeading.h_text_2) {
            assert(j["parent_lev_markup_number"].integer == DocStructMarkupHeading.h_text_1);
          } else if  (j["lev_markup_number"].integer == DocStructMarkupHeading.h_text_3) {
            assert(j["parent_lev_markup_number"].integer == DocStructMarkupHeading.h_text_2);
          } else if  (j["lev_markup_number"].integer == DocStructMarkupHeading.h_text_4) {
            assert(j["parent_lev_markup_number"].integer == DocStructMarkupHeading.h_text_3);
          } else if  (j["lev_markup_number"].integer == DocStructMarkupHeading.h_text_5) {
            // writeln(j["parent_lev_markup_number"].integer);
            // assert(j["parent_lev_markup_number"].integer >= 4);
            // assert(j["parent_lev_markup_number"].integer <= 7);
          }
        }
        return _node;
      }
      invariant() {
      }
    }
    /+ abstraction functions emitters ↑ +/
    /+ ↓ abstraction functions assertions +/
    auto assertions_doc_structure(string[string] an_object, int[string] lv) {
      if (lv["h3"] > State.off) {
        assert(lv["h0"] > State.off);
        assert(lv["h1"] > State.off);
        assert(lv["h2"] > State.off);
      } else if (lv["h2"] > State.off) {
        assert(lv["h0"] > State.off);
        assert(lv["h1"] > State.off);
        assert(lv["h3"] == State.off);
      } else if (lv["h1"] > State.off) {
        assert(lv["h0"] > State.off);
        assert(lv["h2"] == State.off);
        assert(lv["h3"] == State.off);
      } else if (lv["h0"] > State.off) {
        assert(lv["h1"] == State.off);
        assert(lv["h2"] == State.off);
        assert(lv["h3"] == State.off);
      } else {
        assert(lv["h0"] == State.off);
        assert(lv["h1"] == State.off);
        assert(lv["h2"] == State.off);
        assert(lv["h3"] == State.off);
      }
      if (lv["h7"] > State.off) {
        assert(lv["h4"] > State.off);
        assert(lv["h5"] > State.off);
        assert(lv["h6"] > State.off);
      } else if (lv["h6"] > State.off) {
        assert(lv["h4"] > State.off);
        assert(lv["h5"] > State.off);
        assert(lv["h7"] == State.off);
      } else if (lv["h5"] > State.off) {
        assert(lv["h4"] > State.off);
        assert(lv["h6"] == State.off);
        assert(lv["h7"] == State.off);
      } else if (lv["h4"] > State.off) {
        assert(lv["h5"] == State.off);
        assert(lv["h6"] == State.off);
        assert(lv["h7"] == State.off);
      } else {
        assert(lv["h4"] == State.off);
        assert(lv["h5"] == State.off);
        assert(lv["h6"] == State.off);
        assert(lv["h7"] == State.off);
      }
      if (lv["h0"] == State.off) {
        assert(lv["h1"] == State.off);
        assert(lv["h2"] == State.off);
        assert(lv["h3"] == State.off);
        assert(lv["h4"] == State.off);
        assert(lv["h5"] == State.off);
        assert(lv["h6"] == State.off);
        assert(lv["h7"] == State.off);
      }
      if (lv["h1"] == State.off) {
        assert(lv["h2"] == State.off);
        assert(lv["h3"] == State.off);
      }
      if (lv["h2"] == State.off) {
        assert(lv["h3"] == State.off);
      }
      if (lv["h3"] == State.off) {
      }
      if (lv["h4"] == State.off) {
        assert(lv["h5"] == State.off);
        assert(lv["h6"] == State.off);
        assert(lv["h7"] == State.off);
      }
      if (lv["h5"] == State.off) {
        assert(lv["h6"] == State.off);
        assert(lv["h7"] == State.off);
      }
      if (lv["h6"] == State.off) {
        assert(lv["h7"] == State.off);
      }
      if (lv["h7"] == State.off) {
      }
      switch (to!string(an_object["lev"])) {
      case "A":
        if (lv["h0"] == State.off) {
          assert(lv["h1"] == State.off);
          assert(lv["h2"] == State.off);
          assert(lv["h3"] == State.off);
          assert(lv["h4"] == State.off);
          assert(lv["h5"] == State.off);
          assert(lv["h6"] == State.off);
          assert(lv["h7"] == State.off);
        } else {  // (lv["h0"] > State.off)
          assert(lv["h0"] == State.off,"error should not enter level A a second time");
        }
        break;
      case "B":
        if (lv["h1"] == State.off) {
          assert(lv["h0"] > State.off);
          assert(lv["h2"] == State.off);
          assert(lv["h3"] == State.off);
        } else {                 // (lv["h1"] > State.off)
          assert(lv["h0"] > State.off);
          assert(lv["h1"] > State.off);  //
        }
        break;
      case "C":
        if (lv["h2"] == State.off) {
          assert(lv["h0"] > State.off);
          assert(lv["h1"] > State.off);
          assert(lv["h3"] == State.off);
        } else {                 // (lv["h2"] > State.off)
          assert(lv["h0"] > State.off);
          assert(lv["h1"] > State.off);
          assert(lv["h2"] > State.off);  //
        }
        break;
      case "D":
        if (lv["h3"] == State.off) {
          assert(lv["h0"] > State.off);
          assert(lv["h1"] > State.off);
          assert(lv["h2"] > State.off);
        } else {                 // (lv["h3"] > State.off)
          assert(lv["h0"] > State.off);
          assert(lv["h1"] > State.off);
          assert(lv["h2"] > State.off);
          assert(lv["h3"] > State.off);
        }
        break;
      case "1":
        if (lv["h4"] == State.off) {
          assert(lv["h0"] > State.off);
        } else {                 // (lv["h4"] > State.off)
          assert(lv["h0"] > State.off);
          assert(lv["h4"] > State.off);  //
        }
        break;
      case "2":
        if (lv["h5"] == State.off) {
          assert(lv["h0"] > State.off);
          assert(lv["h4"] > State.off);
        } else {                 // (lv["h5"] > State.off)
          assert(lv["h0"] > State.off);
          assert(lv["h4"] > State.off);
          assert(lv["h5"] > State.off);  //
        }
        break;
      case "3":
        if (lv["h6"] == State.off) {
          assert(lv["h0"] > State.off);
          assert(lv["h4"] > State.off);
          assert(lv["h5"] > State.off);
        } else {                 // (lv["h6"] > State.off)
          assert(lv["h0"] > State.off);
          assert(lv["h4"] > State.off);
          assert(lv["h5"] > State.off);
          assert(lv["h6"] > State.off);  //
        }
        break;
      case "4":
        if (lv["h7"] == State.off) {
          assert(lv["h0"] > State.off);
          assert(lv["h4"] > State.off);
          assert(lv["h5"] > State.off);
          assert(lv["h6"] > State.off);
        } else {                 // (lv["h7"] > State.off)
          assert(lv["h0"] > State.off);
          assert(lv["h4"] > State.off);
          assert(lv["h5"] > State.off);
          assert(lv["h6"] > State.off);
          assert(lv["h7"] > State.off);  //
        }
        break;
      default:
        break;
      }
    }
    auto assertions_flag_types_block_status_none_or_closed(int[string] type) {
      assert(
        (type["code"] == TriState.off)
        || (type["code"] == TriState.closing),
        "code block status: off or closing");
      assert(
        (type["poem"] == TriState.off)
        || (type["poem"] == TriState.closing),
        "poem status: off or closing");
      assert(
        (type["table"] == TriState.off)
        || (type["table"] == TriState.closing),
        "table status: off or closing");
      assert(
        (type["group"] == TriState.off)
        || (type["group"] == TriState.closing),
        "group block status: off or closing");
      assert(
        (type["block"] == TriState.off)
        || (type["block"] == TriState.closing),
        "block status: off or closing");
    }
    /+ abstraction functions assertions ↑ +/
  } /+ ← closed: struct Abstraction +/
} /+ ← closed: template SiSUdocAbstraction +/