---
title: "Investment–loan–bridge gateway"
description: >
  Tier 3 (gateway). Runs the investment–loan–bridge gateway, producing the eight
  `tstm_*` gateway datasets consumed by the downstream investment and timing
  analyses (issue #32).
vignette: >
  %\VignetteIndexEntry{Investment–loan–bridge gateway}
  %\VignetteEngine{quarto::html}
  %\VignetteEncoding{UTF-8}
knitr:
  opts_chunk:
    collapse: true
    comment: "#>"
---

This vignette implements [PrjThaiHFID-#32](https://github.com/FanWangEcon/PrjThaiHFID/issues/32).

It runs the investment–loan–bridge gateway (`ffp_hfid_invest_loan_linked_abc_investloan_char_gateway`), optionally writes eight regenerated `tstm_*` objects to `data/` or `data-temp/`, and produces summary tables from gateway outputs.

**Unit of observation:** investment-loan-bridge linkages — investment-level `investloan_type_*` categories keyed on household `hhid_Num` x asset `ivars` x investment counter `hh_inv_asset_ctr`.

## Outputs at a glance

This vignette regenerates eight gateway datasets, written to `data/` when `bl_replace_data_output <- TRUE`, otherwise to the gitignored `data-temp/` for safe validation. Use the links below to jump to each rendered data documentation page:

| Object | Data page | Role |
|--------|-----------|------|
| `tstm_loans_pn_nd` | [reference](../reference/tstm_loans_pn_nd.html) | Non-duplicate monthly loan panel (Set A loans) |
| `tstm_loans_hooks` | [reference](../reference/tstm_loans_hooks.html) | Hooked loan pairs (Set A + B) |
| `tstm_loans_bridges_type` | [reference](../reference/tstm_loans_bridges_type.html) | Loan bridges with formal/informal type |
| `tstm_invdates_uniq` | [reference](../reference/tstm_invdates_uniq.html) | Unique investment dates |
| `tstm_invest` | [reference](../reference/tstm_invest.html) | Investment spells by asset `ivars` |
| `tstm_roster_invest_loan_linked` | [reference](../reference/tstm_roster_invest_loan_linked.html) | Investment-to-loan roster |
| `tstm_roster_invest2loan2bridge` | [reference](../reference/tstm_roster_invest2loan2bridge.html) | Investment-to-loan-to-bridge roster |
| `tstm_invest2loan2bridge_chars` | [reference](../reference/tstm_invest2loan2bridge_chars.html) | Investment-level linkage characteristics |

These objects are documented in [`R/data-res.R`](https://github.com/FanWangEcon/PrjThaiHFID/blob/main/R/data-res.R). The two inputs [`tstm_loans_panel`](../reference/tstm_loans_panel.html) and [`tstm_asset_loan`](../reference/tstm_asset_loan.html) are documented in [`R/data.R`](https://github.com/FanWangEcon/PrjThaiHFID/blob/main/R/data.R) and [`R/data-res.R`](https://github.com/FanWangEcon/PrjThaiHFID/blob/main/R/data-res.R).

**Tables and figures (not saved as data):** an investment-type-by-asset wide table with paper-style row labels, investment-level counts and shares for `investloan_type_m4` / `investloan_type_brg_m5` / `investloan_type_m8`, and roster diagnostic tallies. Each table is gated by its own `ls_save_res[["<name>"]]` switch (all `FALSE` by default); flip one on to write its LaTeX/CSV (including `example_table`) to `res/res_bridge_type/` via `ffp_save_res_table()`.

::: {.callout-note collapse="true"}
## What ships vs. what is generated locally

- **Shipped with the installed package:** the packaged datasets in `data/` (lazy-loaded inputs `tstm_loans_panel`, `tstm_asset_loan`).
- **Generated locally when this vignette runs** (neither pushed to GitHub nor shipped in the package): the eight gateway `.rda` written to `data/` (or `data-temp/`); their `inst/extdata/*.csv` / `*.dta` exports; and any tables written to `res/res_bridge_type/` (`.tex`/`.csv`, only when the matching `ls_save_res` control is `TRUE`). The `inst/extdata/` and `res/` artifacts are git-ignored, saved only for local convenience.
:::

## Required packaged inputs

The gateway loads only two objects from the package `data/` folder (must exist locally when knitting):

| Object | Role |
|--------|------|
| `tstm_loans_panel` | Monthly loan panel (Group A) |
| `tstm_asset_loan` | Household-month asset and loan aggregates (Group B) |

All other pipeline objects are computed in this run.

## Pipeline structure

```{mermaid}
flowchart TD
  subgraph inputs [Packaged data inputs]
    loansPanel[tstm_loans_panel]
    assetLoan[tstm_asset_loan]
  end
  subgraph gateway [Gateway run]
    gw[ffp_hfid_invest_loan_linked_abc_investloan_char_gateway]
  end
  loansPanel --> gw
  assetLoan --> gw
  subgraph outputs [Eight regenerated tstm objects]
    o1[tstm_loans_pn_nd]
    o2[tstm_loans_hooks]
    o3[tstm_loans_bridges_type]
    o4[tstm_invdates_uniq]
    o5[tstm_invest]
    o6[tstm_roster_invest_loan_linked]
    o7[tstm_roster_invest2loan2bridge]
    o8[tstm_invest2loan2bridge_chars]
  end
  gw --> o1
  gw --> o2
  gw --> o3
  gw --> o4
  gw --> o5
  gw --> o6
  gw --> o7
  gw --> o8
  subgraph deliver [Save and tabulate]
    saveRda[save to data or data-temp]
    tabs[investloan_type m4 brg_m5 m8 tables]
    tex[res_bridge_type latex tables]
  end
  o1 --> saveRda
  o8 --> saveRda
  o8 --> tabs
  o7 --> tabs
  o8 --> tex
```

## Control parameters

```{r setup}
library(PrjThaiHFID)
library(dplyr)
library(tidyr)
library(glue)
library(kableExtra)

bs_style <- c("striped", "hover", "condensed", "responsive")
options(kable_styling_bootstrap_options = bs_style)

verbose <- TRUE
it_file_code <- 494318

# TRUE: overwrite canonical data/*.rda; FALSE: write to data-temp/ (gitignored)
bl_replace_data_output <- FALSE

st_kableformat <- "html"

# MBF timing and size cuts (same as R-script/ffs_bridge_count/ffs_bridge_count_mbf.R)
it_mth_inv_start_min_cut <- 14
it_mth_inv_start_max_cut <- 144
fl_min_invest_size_cut <- 10000

# Three paper asset variables (Table 4)
ar_st_vars_to_keep <- c("agg_BS_1021", "agg_BS_1012", "agg_BS_1011")
ar_ivars_paper <- ar_st_vars_to_keep

# here::here() anchors on the temporary _quarto.yml pkgdown writes under
# vignettes/ during a quarto render; find the package root explicitly so paths
# resolve under both Quarto and knitr.
spn_pkg_root <- rprojroot::find_root(rprojroot::has_file("DESCRIPTION"))
spn_res_bridge_type <- file.path(
  spn_pkg_root, "res", "res_bridge_type",
  fsep = .Platform$file.sep
)
spt_res <- file.path(spn_pkg_root, "res", "res_bridge_type")
st_data_out <- file.path(
  spn_pkg_root,
  if (bl_replace_data_output) "data" else "data-temp",
  fsep = .Platform$file.sep
)
st_extdata_dir <- file.path(spn_pkg_root, "inst", "extdata")

# Per-table .tex/.csv save switches; all FALSE by default (display always shown).
ls_save_res <- list(
  example_table = FALSE,
  tab_inv_m4_counts = FALSE,
  tab_inv_m4_shares = FALSE,
  tab_inv_brg_m5_counts = FALSE,
  tab_inv_brg_m5_shares = FALSE,
  tab_inv_m8_counts = FALSE,
  tab_inv_m8_shares = FALSE,
  tab_roster_ivars = FALSE,
  tab_chars_m8 = FALSE,
  tab_roster_bl_informal = FALSE,
  tab_roster_m8_informal = FALSE
)

dir.create(spn_res_bridge_type, recursive = TRUE, showWarnings = FALSE)
for (st_dir in c(st_data_out, st_extdata_dir)) {
  if (!dir.exists(st_dir)) {
    dir.create(st_dir, recursive = TRUE)
  }
}

ar_tstm_save <- c(
  "tstm_loans_pn_nd",
  "tstm_loans_hooks",
  "tstm_loans_bridges_type",
  "tstm_invdates_uniq",
  "tstm_invest",
  "tstm_roster_invest_loan_linked",
  "tstm_roster_invest2loan2bridge",
  "tstm_invest2loan2bridge_chars"
)

ffv_save_gateway_tstm <- function(obj, name, out_dir, extdata_dir = st_extdata_dir) {
  if (!dir.exists(out_dir)) {
    dir.create(out_dir, recursive = TRUE)
  }
  if (!dir.exists(extdata_dir)) {
    dir.create(extdata_dir, recursive = TRUE)
  }

  st_rda <- file.path(out_dir, paste0(name, ".rda"))
  st_csv <- file.path(extdata_dir, paste0(name, ".csv"))
  st_dta <- file.path(extdata_dir, paste0(name, ".dta"))

  tmp_env <- new.env(parent = emptyenv())
  tmp_env[[name]] <- obj
  save(list = name, file = st_rda, envir = tmp_env)

  readr::write_csv(obj, st_csv)

  # Local-only .dta export (git-ignored). Stata caps variable names at 32
  # characters, so truncate long names (uniquely) for the .dta copy only; the
  # canonical .rda and the full-name .csv are unaffected. Never let a Stata
  # name/format edge case break the knit -> skip the .dta with a message.
  if (requireNamespace("haven", quietly = TRUE)) {
    tryCatch(
      {
        obj_dta <- obj
        nm <- names(obj_dta)
        if (any(nchar(nm) > 32)) {
          nm <- substr(nm, 1, 32)
          nm <- make.unique(nm, sep = "_")
          nm <- substr(nm, 1, 32)
          names(obj_dta) <- nm
        }
        haven::write_dta(obj_dta, st_dta)
      },
      error = function(e) {
        message(glue::glue(
          "skip .dta for {name}: {conditionMessage(e)} (csv + rda still written)"
        ))
        st_dta <<- NA_character_
      }
    )
  } else {
    message("package 'haven' not installed: skipping .dta export (csv + rda still written)")
    st_dta <- NA_character_
  }

  invisible(list(rda = st_rda, csv = st_csv, dta = st_dta))
}

ffv_inv_type_by_ivars <- function(df, type_var) {
  df %>%
    dplyr::group_by(ivars, .data[[type_var]]) %>%
    dplyr::tally(name = "n") %>%
    tidyr::pivot_wider(names_from = ivars, values_from = n, values_fill = 0)
}

ffv_inv_type_share_by_ivars <- function(df, type_var) {
  df %>%
    dplyr::group_by(ivars, .data[[type_var]]) %>%
    dplyr::tally(name = "n") %>%
    dplyr::group_by(ivars) %>%
    dplyr::mutate(share = n / sum(n)) %>%
    dplyr::select(-n) %>%
    tidyr::pivot_wider(
      id_cols = dplyr::all_of(type_var),
      names_from = ivars,
      values_from = share
    )
}

ffv_m8_cell <- function(tb, m8, ivar, kind = c("n", "pct")) {
  kind <- match.arg(kind)
  col <- paste0(ivar, if (kind == "n") "_n" else "_share")
  if (!m8 %in% tb$investloan_type_m8) {
    return(if (kind == "n") "0" else "0.0\\%")
  }
  val <- tb[[col]][tb$investloan_type_m8 == m8][1]
  if (kind == "n") {
    as.character(as.integer(val))
  } else {
    sprintf("%.1f\\%%", val * 100)
  }
}

ffv_m8_data_row <- function(tb, m8) {
  ivars <- c("agg_BS_1021", "agg_BS_1012", "agg_BS_1011")
  vals <- unlist(lapply(ivars, function(iv) {
    c(ffv_m8_cell(tb, m8, iv, "n"), ffv_m8_cell(tb, m8, iv, "pct"))
  }))
  paste0("& ", paste(vals, collapse = " \n& "), "\\\\")
}

# Write example_table.tex with fixed layout from res/res_bridge_type/example_table_ori.tex
ffv_write_example_table_tex <- function(tb, name = "example_table",
                                        spn_out = spt_res, df = NULL,
                                        bl_save = FALSE) {
  rows <- list(
    "1-investment-no-loan",
    "2-investment-loan-a",
    "3-investment-loan-hook",
    "4-brg-single-lender",
    "5-brg-baac-vilfund-combo",
    "6-brg-informal-quasifor-combo",
    "7-brg-has-informal-bridge",
    "8-brg-has-informal-in-bridge"
  )
  body_rows <- vapply(rows, function(m8) ffv_m8_data_row(tb, m8), character(1))

  tex <- c(
    "% Auto-generated from vignettes/ffv_invest_loan_bridge.qmd",
    "% Styling matches res/res_bridge_type/example_table_ori.tex",
    "%  it_gap_LBL_IL_min <- -6 horizon",
    "\\begin{tabular}[t]{",
    ">{\\centering\\arraybackslash}p{3.0cm}",
    ">{\\centering\\arraybackslash}p{2.0cm}",
    ">{\\centering\\arraybackslash}p{2.0cm}",
    ">{\\centering\\arraybackslash}p{2.0cm}",
    ">{\\centering\\arraybackslash}p{2.0cm}",
    ">{\\centering\\arraybackslash}p{2.0cm}",
    ">{\\centering\\arraybackslash}p{2.0cm}}",
    "\\toprule",
    "& \\multicolumn{6}{c}{",
    "\\textbf{Alternative investment definitions}",
    "}\\\\",
    "\\cmidrule(l{3pt}r{3pt}){2-7}",
    "\\multicolumn{1}{c}{ } & ",
    "\\multicolumn{2}{p{4cm}}{",
    "\\centering\\small Agricultural and business assets investments",
    "} & ",
    "\\multicolumn{2}{p{4cm}}{",
    "\\centering\\small Agricultural, business, and land assets investments",
    "} & ",
    "\\multicolumn{2}{p{4cm}}{",
    "\\centering\\small Agricultural, business, land, and household assets investments",
    "}\\\\",
    "\\cmidrule(l{3pt}r{3pt}){2-3} \\cmidrule(l{3pt}r{3pt}){4-5} \\cmidrule(l{3pt}r{3pt}){6-7}",
    "& \\small{\\#} & \\multicolumn{1}{p{2cm}}{\\centering\\small{\\%}} ",
    "& \\small{\\#} & \\multicolumn{1}{p{2cm}}{\\centering\\small{\\%}}",
    "& \\small{\\#} & \\multicolumn{1}{p{2cm}}{\\centering\\small{\\%}}\\\\",
    "\\midrule",
    "\\addlinespace[0.5em]",
    "\\multicolumn{7}{l}{\\textbf{",
    "Investments \\emph{not} time-linked to loan bridges",
    "}}\\\\",
    "\\multicolumn{7}{l}{",
    "\\hspace{1em}",
    "Invest. \\emph{not} time-linked any loans",
    "}\\\\",
    "\\addlinespace[0.1em]",
    body_rows[1],
    "\\addlinespace[0.2em]",
    "\\multicolumn{7}{l}{",
    "\\hspace{1em}",
    "Invest. time-linked to \\emph{standalone} loans (set A loans only)",
    "}\\\\",
    "\\addlinespace[0.1em]",
    body_rows[2],
    "\\addlinespace[0.2em]",
    "\\multicolumn{7}{l}{",
    "\\hspace{1em}",
    "Invest. time-linked to \\emph{``hooked''} loans (set A and B loans only)",
    "}\\\\",
    "\\addlinespace[0.1em]",
    body_rows[3],
    "\\addlinespace[0.2em]",
    "\\cmidrule(l{10pt}r{3pt}){1-7}",
    "\\multicolumn{7}{l}{\\textbf{",
    "Investments time-linked to \\emph{only} formal \\emph{or} \\emph{only} informal loan bridges",
    "}}\\\\",
    "\\multicolumn{7}{l}{",
    "\\hspace{1.0em}",
    "\\emph{Single lender} type only investment-loan bridge",
    "}\\\\",
    "\\addlinespace[0.1em]",
    body_rows[4],
    "\\addlinespace[0.2em]",
    "\\multicolumn{7}{l}{",
    "\\hspace{1.0em}",
    "\\emph{Multiple formal} lenders investment-loan bridge",
    "}\\\\",
    "\\addlinespace[0.1em]",
    body_rows[5],
    "\\addlinespace[0.2em]",
    "\\multicolumn{7}{l}{",
    "\\hspace{1.0em}",
    "\\emph{Multiple informal} lender types investment-loan bridge",
    "}\\\\",
    "\\addlinespace[0.1em]",
    body_rows[6],
    "\\addlinespace[0.2em]",
    "\\cmidrule(l{10pt}r{3pt}){1-7}",
    "\\multicolumn{7}{l}{\\textbf{",
    "Investments time-linked to \\emph{joint} formal \\emph{and} informal investment-loan bridges",
    "}}\\\\",
    "\\multicolumn{7}{l}{",
    "\\hspace{1.0em}",
    "Contains \\emph{formal---informal---formal} loan bridges",
    "}\\\\",
    "\\addlinespace[0.1em]",
    body_rows[7],
    "\\addlinespace[0.2em]",
    "\\multicolumn{7}{l}{",
    "\\hspace{1.0em}",
    "Do not contain formal---informal---formal loan bridges, but includes \\emph{informal links} in bridges",
    "}\\\\",
    "\\addlinespace[0.1em]",
    body_rows[8],
    "\\addlinespace[0.2em]",
    "\\bottomrule",
    "\\end{tabular}"
  )
  ffp_save_res_table(tex, name, spn_out, df = df, bl_save = bl_save)
  invisible(tex)
}

ffv_render_table <- function(
    df,
    caption,
    name = NULL,
    bl_save = FALSE,
    spn_res = spt_res,
    st_format = st_kableformat) {
  kt <- kbl(
    df,
    format = st_format,
    booktabs = TRUE,
    caption = caption,
    digits = 4
  ) %>%
    kable_styling(
      bootstrap_options = bs_style,
      full_width = FALSE,
      position = "left"
    )
  if (isTRUE(bl_save) && !is.null(name)) {
    tex <- knitr::kable(
      df,
      format = "latex",
      booktabs = TRUE,
      caption = caption,
      label = name,
      digits = 4
    )
    ffp_save_res_table(tex, name, spn_res, df = df, bl_save = bl_save)
  }
  kt
}

if (verbose) {
  print(glue::glue("f-{it_file_code}, controls"))
  print(glue::glue("  bl_replace_data_output: {bl_replace_data_output} -> {st_data_out}/"))
  print(glue::glue("  st_extdata_dir: {st_extdata_dir}"))
  print(glue::glue("  ls_save_res TRUE: {paste(names(which(unlist(ls_save_res))), collapse = ', ')}"))
  print(glue::glue("  it_mth_inv_start_min_cut: {it_mth_inv_start_min_cut}"))
  print(glue::glue("  it_mth_inv_start_max_cut: {it_mth_inv_start_max_cut}"))
  print(glue::glue("  fl_min_invest_size_cut: {fl_min_invest_size_cut}"))
  print(glue::glue("  ar_st_vars_to_keep: {paste(ar_st_vars_to_keep, collapse = ', ')}"))
}
```

## Run gateway

Same call as `R-script/ffs_bridge_count/ffs_bridge_count_mbf.R`.

```{r gateway}
if (verbose) {
  print(glue::glue("f-{it_file_code}, gateway: calling ffp_hfid_invest_loan_linked_abc_investloan_char_gateway"))
}

ls_gateway_result <- PrjThaiHFID::ffp_hfid_invest_loan_linked_abc_investloan_char_gateway(
  svr_lender_var = "forinfm4",
  svr_principal = "bf5klm_bm6h_joint",
  svr_principal_last = "bm6h",
  svr_principal_interest_sum = "bm6b",
  bl_filter_bridge_grvgr0 = TRUE,
  it_ll_grv_min = -1,
  bl_filter_loan_duration_a = FALSE,
  bl_filter_loan_duration_b = FALSE,
  bl_filter_lender_type = FALSE,
  bl_filter_bridge_informal = FALSE,
  bl_filter_loan_size = FALSE,
  bl_filter_loan_duration_more = FALSE,
  fl_sd_ithres = stats::qnorm(0.99),
  it_thres_invest_mth_gap = 2,
  it_gap_LBL_IL_min = -6,
  ar_st_vars_to_keep = ar_st_vars_to_keep,
  fl_min_invest_size = fl_min_invest_size_cut,
  it_mth_inv_start_min = it_mth_inv_start_min_cut,
  it_mth_inv_start_max = it_mth_inv_start_max_cut,
  bl_drop_afrombc = TRUE,
  bl_drop_cfromb = TRUE,
  bl_compare2baserda = FALSE,
  verbose = verbose,
  verbose_detail = FALSE,
  it_verbose_detail_nrow = 100
)

for (nm in ar_tstm_save) {
  assign(nm, ls_gateway_result[[nm]], envir = .GlobalEnv)
}

tstm_loans_bridges_type <- ls_gateway_result$tstm_loans_bridges_type

if (verbose) {
  print(glue::glue("f-{it_file_code}, gateway: unpacked {length(ar_tstm_save)} tstm objects"))
  for (nm in ar_tstm_save) {
    obj <- ls_gateway_result[[nm]]
    print(glue::glue("  {nm}: {nrow(obj)} rows"))
  }
}
```

## Save regenerated `tstm_*` objects

```{r save-tstm}
for (nm in ar_tstm_save) {
  ffv_save_gateway_tstm(
    obj = ls_gateway_result[[nm]],
    name = nm,
    out_dir = st_data_out
  )
}

if (verbose) {
  if (bl_replace_data_output) {
    print(glue::glue("f-{it_file_code}, save: replaced canonical outputs under {st_data_out}/"))
  } else {
    print(glue::glue("f-{it_file_code}, save: wrote regenerated outputs to {st_data_out}/ (data/ unchanged)"))
  }
}
```

## Table: investment type by asset (wide)

Reproduces the summary in `R-dev/ffs_hfid_gateway_ilhb.R` (lines 477–511): counts and shares of `investloan_type_m8` by `ivars`, pivoted wide.

```{r table-wide-m8}
tb_inv_m8_wide <- tstm_invest2loan2bridge_chars %>%
  dplyr::select(
    hhid_Num, ivars, hh_inv_asset_ctr,
    investloan_type_m8
  ) %>%
  dplyr::distinct() %>%
  dplyr::filter(ivars %in% ar_ivars_paper) %>%
  dplyr::group_by(ivars, investloan_type_m8) %>%
  dplyr::tally(name = "n") %>%
  dplyr::group_by(ivars) %>%
  dplyr::mutate(share = n / sum(n)) %>%
  dplyr::ungroup() %>%
  tidyr::pivot_wider(
    names_from = ivars,
    values_from = c(n, share),
    names_glue = "{ivars}_{.value}",
    values_fill = list(n = 0, share = 0)
  ) %>%
  dplyr::mutate(
    n_total = agg_BS_1021_n + agg_BS_1012_n + agg_BS_1011_n
  ) %>%
  dplyr::mutate(
    dplyr::across(dplyr::ends_with("_share"), ~ round(.x, 4))
  ) %>%
  dplyr::select(
    investloan_type_m8,
    n_total,
    agg_BS_1021_n,
    agg_BS_1021_share,
    agg_BS_1012_n,
    agg_BS_1012_share,
    agg_BS_1011_n,
    agg_BS_1011_share
  ) %>%
  dplyr::arrange(investloan_type_m8)

# HTML preview: paper-style row labels (same order as example_table_ori.tex)
ar_m8_paper_rows <- data.frame(
  investloan_type_m8 = c(
    "1-investment-no-loan",
    "2-investment-loan-a",
    "3-investment-loan-hook",
    "4-brg-single-lender",
    "5-brg-baac-vilfund-combo",
    "6-brg-informal-quasifor-combo",
    "7-brg-has-informal-bridge",
    "8-brg-has-informal-in-bridge"
  ),
  row_label = c(
    "Invest. not time-linked any loans",
    "Invest. time-linked to standalone loans (set A only)",
    "Invest. time-linked to hooked loans (set A and B only)",
    "Single lender type only investment-loan bridge",
    "Multiple formal lenders investment-loan bridge",
    "Multiple informal lender types investment-loan bridge",
    "Contains formal-informal-formal loan bridges",
    "Informal links in bridges (not formal-informal-formal)"
  ),
  stringsAsFactors = FALSE
)

tb_inv_m8_html <- tb_inv_m8_wide %>%
  dplyr::left_join(ar_m8_paper_rows, by = "investloan_type_m8") %>%
  dplyr::transmute(
    Category = row_label,
    `Ag+biz #` = agg_BS_1021_n,
    `Ag+biz %` = sprintf("%.1f", agg_BS_1021_share * 100),
    `Ag+biz+land #` = agg_BS_1012_n,
    `Ag+biz+land %` = sprintf("%.1f", agg_BS_1012_share * 100),
    `Ag+biz+land+hh #` = agg_BS_1011_n,
    `Ag+biz+land+hh %` = sprintf("%.1f", agg_BS_1011_share * 100)
  )

kbl(
  tb_inv_m8_html,
  format = st_kableformat,
  booktabs = TRUE,
  caption = "Investment–loan–bridge types by asset definition (counts and %)"
) %>%
  kable_styling(
    bootstrap_options = bs_style,
    full_width = FALSE,
    position = "left"
  )

ffv_write_example_table_tex(
  tb_inv_m8_wide,
  name = "example_table",
  spn_out = spt_res,
  df = tb_inv_m8_wide,
  bl_save = ls_save_res[["example_table"]]
)
```

## Tables: investment-level m4, brg_m5, and m8

Investment-level tabulations (same structure as `R-script/ffs_bridge_type/ffs_bridge_type.R`, Section A, `it_ctr == 1`).

### Four categories (`investloan_type_m4`)

```{r table-inv-m4}
tb_inv_m4_counts <- ffv_inv_type_by_ivars(
  tstm_invest2loan2bridge_chars, "investloan_type_m4"
)
tb_inv_m4_shares <- ffv_inv_type_share_by_ivars(
  tstm_invest2loan2bridge_chars, "investloan_type_m4"
)

ffv_render_table(
  tb_inv_m4_counts,
  caption = "Investment-level counts by investloan_type_m4 and ivars",
  name = "tab_inv_m4_counts",
  bl_save = ls_save_res[["tab_inv_m4_counts"]]
)
ffv_render_table(
  tb_inv_m4_shares,
  caption = "Investment-level shares by investloan_type_m4 and ivars",
  name = "tab_inv_m4_shares",
  bl_save = ls_save_res[["tab_inv_m4_shares"]]
)
```

### Five bridge categories (`investloan_type_brg_m5`)

```{r table-inv-brg-m5}
tb_inv_brg_m5_counts <- ffv_inv_type_by_ivars(
  tstm_invest2loan2bridge_chars, "investloan_type_brg_m5"
)
tb_inv_brg_m5_shares <- ffv_inv_type_share_by_ivars(
  tstm_invest2loan2bridge_chars, "investloan_type_brg_m5"
)

ffv_render_table(
  tb_inv_brg_m5_counts,
  caption = "Investment-level counts by investloan_type_brg_m5 and ivars",
  name = "tab_inv_brg_m5_counts",
  bl_save = ls_save_res[["tab_inv_brg_m5_counts"]]
)
ffv_render_table(
  tb_inv_brg_m5_shares,
  caption = "Investment-level shares by investloan_type_brg_m5 and ivars",
  name = "tab_inv_brg_m5_shares",
  bl_save = ls_save_res[["tab_inv_brg_m5_shares"]]
)
```

### Eight categories (`investloan_type_m8`)

```{r table-inv-m8}
tb_inv_m8_counts <- ffv_inv_type_by_ivars(
  tstm_invest2loan2bridge_chars, "investloan_type_m8"
)
tb_inv_m8_shares <- ffv_inv_type_share_by_ivars(
  tstm_invest2loan2bridge_chars, "investloan_type_m8"
)

ffv_render_table(
  tb_inv_m8_counts,
  caption = "Investment-level counts by investloan_type_m8 and ivars",
  name = "tab_inv_m8_counts",
  bl_save = ls_save_res[["tab_inv_m8_counts"]]
)
ffv_render_table(
  tb_inv_m8_shares,
  caption = "Investment-level shares by investloan_type_m8 and ivars",
  name = "tab_inv_m8_shares",
  bl_save = ls_save_res[["tab_inv_m8_shares"]]
)
```

## Tables: roster diagnostics

Light tallies from gateway outputs (no triply-linked `df_wrk` prep). Merge investment-level types onto the loan-level roster for `investloan_type_m8` tabulations.

```{r table-roster}
tstm_roster_with_types <- tstm_roster_invest2loan2bridge %>%
  dplyr::left_join(
    tstm_invest2loan2bridge_chars %>%
      dplyr::select(
        hhid_Num, ivars, hh_inv_asset_ctr,
        investloan_type_m4, investloan_type_m8
      ),
    by = c("hhid_Num", "ivars", "hh_inv_asset_ctr")
  )

tb_roster_ivars <- tstm_roster_invest2loan2bridge %>%
  dplyr::group_by(ivars) %>%
  dplyr::tally(name = "n") %>%
  dplyr::arrange(ivars)

tb_chars_m8 <- tstm_invest2loan2bridge_chars %>%
  dplyr::group_by(investloan_type_m8) %>%
  dplyr::tally(name = "n") %>%
  dplyr::arrange(investloan_type_m8)

tb_roster_bl_informal <- tstm_roster_invest2loan2bridge %>%
  dplyr::group_by(bl_bridge_informal) %>%
  dplyr::tally(name = "n")

tb_roster_m8_informal <- tstm_roster_with_types %>%
  dplyr::group_by(investloan_type_m8, bl_bridge_informal) %>%
  dplyr::tally(name = "n") %>%
  dplyr::arrange(investloan_type_m8, bl_bridge_informal)

ffv_render_table(
  tb_roster_ivars,
  caption = "Roster row counts by ivars",
  name = "tab_roster_ivars",
  bl_save = ls_save_res[["tab_roster_ivars"]]
)
ffv_render_table(
  tb_chars_m8,
  caption = "Bridge-chars investment counts by investloan_type_m8",
  name = "tab_chars_m8",
  bl_save = ls_save_res[["tab_chars_m8"]]
)
ffv_render_table(
  tb_roster_bl_informal,
  caption = "Roster counts by bl_bridge_informal",
  name = "tab_roster_bl_informal",
  bl_save = ls_save_res[["tab_roster_bl_informal"]]
)
ffv_render_table(
  tb_roster_m8_informal,
  caption = "Roster counts by investloan_type_m8 and bl_bridge_informal",
  name = "tab_roster_m8_informal",
  bl_save = ls_save_res[["tab_roster_m8_informal"]]
)
```

## Session info

```{r session-info}
sessionInfo()
```
