/*─────────────────────────────────────────────────────────────────────────────
  Beta and Sigma Convergence Across Countries: A Tutorial Using PWT 10.0

  This tutorial teaches the two fundamental concepts of economic convergence:
    - Beta (β) convergence: Do poorer countries grow faster than richer ones?
    - Sigma (σ) convergence: Is the dispersion of income narrowing over time?

  We start with the simplest possible code (two periods, OLS) and gradually
  build to advanced methods (NLS, rolling windows, heatmaps) as used in:
    Patel, Sandefur, and Subramanian (2021) "The New Era of Unconditional
    Convergence," Journal of Development Economics.

  Sample: 84-country balanced panel (countries with GDP data since 1960)
          Excludes oil producers (IMF classification) and pop < 1 million
  Data:   Penn World Tables version 10.0 (Feenstra, Inklaar, Timmer 2015)
  Usage:  do analysis.do
  Output: stata_convergence_*.png figures, *.csv tables, analysis.log

  References:
    - Barro and Sala-i-Martin (1992) "Convergence," JPE
    - Sala-i-Martin (1996) "The Classical Approach to Convergence Analysis"
    - Patel, Sandefur, and Subramanian (2021) "The New Era of Unconditional
      Convergence," Journal of Development Economics
    - Young, Higgins, and Levy (2008) "Sigma vs Beta Convergence," JMCB
─────────────────────────────────────────────────────────────────────────────*/

clear all
set more off
set seed 42
set scheme s2color

* ── Start log ─────────────────────────────────────────────────────────────
capture log close
log using "analysis.log", replace text

display as text ""
display as text "╔══════════════════════════════════════════════════════════════╗"
display as text "║  Beta and Sigma Convergence: A Tutorial with PWT 10.0      ║"
display as text "║  Balanced panel: 84 countries with data since 1960         ║"
display as text "╚══════════════════════════════════════════════════════════════╝"
display as text ""


*═══════════════════════════════════════════════════════════════════════════════
* SECTION 0: DATA PREPARATION
*
*   We load the Penn World Tables 10.0 and prepare a clean cross-country
*   dataset of GDP per capita. Following the literature, we exclude:
*   (1) oil-exporting countries (whose income is driven by resource rents)
*   (2) very small countries (population under 1 million)
*   (3) countries without GDP data in 1960 (balanced panel requirement)
*═══════════════════════════════════════════════════════════════════════════════

display as text ""
display as text "────────────────────────────────────────────────────────────────"
display as text "  SECTION 0: DATA PREPARATION"
display as text "────────────────────────────────────────────────────────────────"

* ── Load PWT 10.0 ──
use "https://raw.githubusercontent.com/cmg777/starter-academic-v501/master/content/post/stata_convergence/pwt100.dta", clear

* Keep only the variables we need
rename countrycode ccode
keep country ccode year pop rgdpe

* ── Compute GDP per capita (PPP) ──
*   rgdpe = expenditure-side real GDP at chained PPPs (in million 2017 US$)
*   pop   = population (in millions)
*   gdppc = GDP per capita in 2017 US$ PPP
gen gdppc = rgdpe / pop
label variable gdppc "Real GDP per capita (PPP, 2017 US$)"
drop if missing(gdppc)
drop if missing(pop)

* ── Identify and exclude oil-producing countries ──
*   These countries' incomes are driven by resource extraction rather than
*   the capital accumulation process that convergence theory describes.
*   List follows IMF classification, same as Patel et al. (2021).
gen oil = inlist(ccode, "DZA", "AGO", "AZE", "BHR", "BRN", "TCD", "COG") | ///
          inlist(ccode, "ECU", "GNQ", "GAB", "IRN", "IRQ", "KAZ", "KWT") | ///
          inlist(ccode, "NGA", "OMN", "QAT", "RUS", "SAU", "TTO", "TKM") | ///
          inlist(ccode, "ARE", "VEN", "YEM", "LBY", "TLS", "SDN")
display "Oil-producing countries excluded:"
tab ccode if oil == 1
drop if oil == 1
drop oil

* ── Exclude small countries (population < 1 million) ──
*   Small economies behave differently (e.g., tax havens, tourism-dependent)
gen small = pop < 1
drop if small == 1
drop small

* ── Restrict to 1960 onwards ──
*   Reliable cross-country data begins around 1960
drop if year < 1960

* ── Restrict to balanced panel: countries with data in 1960 ──
*   This ensures a consistent sample across ALL sections of the tutorial.
*   Without this restriction, the sample grows from 84 (1960) to 124 (2019)
*   as PWT coverage expands, introducing composition effects.
bys ccode: egen has1960 = max(year == 1960 & !missing(gdppc))
display as text ""
display as text "Countries WITHOUT 1960 data (excluded from balanced panel):"
tab ccode if has1960 == 0
display as text ""
display as text "Number of countries dropped for missing 1960 data: " _continue
count if has1960 == 0
keep if has1960 == 1
drop has1960

* ── Summary statistics ──
display as text ""
display as text "Summary of cleaned PWT dataset (balanced panel):"
summarize gdppc year pop, detail
display as text ""
display as text "Number of unique countries:"
codebook ccode

* ── Export cleaned data ──
export delimited ccode country year gdppc pop using "convergence_data_prepared.csv", replace
display as text "Cleaned data exported to: convergence_data_prepared.csv"

* ── Save working dataset ──
save "convergence_working.dta", replace


*═══════════════════════════════════════════════════════════════════════════════
* SECTION 1: BETA CONVERGENCE — THE SIMPLEST CASE (Two Periods)
*
*   The most fundamental test of convergence:
*     Do countries that START poorer GROW faster?
*
*   We use just two years: 1960 (initial) and 2019 (final).
*   If the answer is yes, we have "beta convergence."
*
*   The regression is:
*     growth_i = α + λ × ln(y_i,1960) + ε_i
*
*   where:
*     growth_i = (1/s) × ln(y_i,2019 / y_i,1960) = annualized growth rate
*     ln(y_i,1960) = log of initial GDP per capita
*
*   A NEGATIVE λ means convergence: poorer countries (low y_1960) grow faster.
*   A POSITIVE λ (or zero) means divergence or no convergence.
*═══════════════════════════════════════════════════════════════════════════════

display as text ""
display as text "────────────────────────────────────────────────────────────────"
display as text "  SECTION 1: BETA CONVERGENCE — THE SIMPLEST CASE"
display as text "  Two periods: 1960 and 2019"
display as text "────────────────────────────────────────────────────────────────"

use "convergence_working.dta", clear

* ── Reshape to wide format so each country is one row ──
keep ccode country year gdppc
reshape wide gdppc, i(ccode country) j(year)

* ── Calculate annualized growth rate and log initial income ──
*   growth = (1/59) × ln(GDP_2019 / GDP_1960)
*   This gives the average annual growth rate over 59 years.
local s = 2019 - 1960
gen growth = (1/`s') * ln(gdppc2019 / gdppc1960)
gen initial = ln(gdppc1960)

* Drop countries missing either endpoint
drop if missing(growth) | missing(initial)

* ── Display what we have ──
display as text ""
display as text "Countries with data for both 1960 and 2019: " _continue
count
display as text ""
display as text "Summary of variables:"
summarize growth initial

* ── Run the simplest beta-convergence regression ──
*   This is a simple OLS: growth = α + λ × initial + ε
*   We use robust standard errors to account for heteroskedasticity.
display as text ""
display as text "╔══════════════════════════════════════════════════════════════╗"
display as text "║  OLS Beta-Convergence Regression: 1960 to 2019             ║"
display as text "╚══════════════════════════════════════════════════════════════╝"
reg growth initial, robust

* ── Interpret the result ──
local beta_ols = _b[initial]
local se_ols   = _se[initial]
local t_ols    = `beta_ols' / `se_ols'
local p_ols    = 2 * ttail(e(df_r), abs(`t_ols'))

display as text ""
display as text "INTERPRETATION:"
if `beta_ols' < 0 & `p_ols' < 0.05 {
    display as result "  The coefficient is NEGATIVE and statistically significant."
    display as result "  This means poorer countries grew FASTER — evidence of CONVERGENCE."
}
else if `beta_ols' < 0 & `p_ols' >= 0.05 {
    display as result "  The coefficient is negative but NOT statistically significant."
    display as result "  Weak or no evidence of convergence over the full 1960-2019 period."
}
else {
    display as result "  The coefficient is positive — evidence of DIVERGENCE."
    display as result "  Richer countries grew faster, and the gap widened."
}

* ── Create scatter plot with fitted line ──
*   This is the classic convergence scatter plot: growth vs. initial income
#delimit ;
tw (lfitci growth initial, fcolor("106 155 204"%20) lcolor("106 155 204"))
   (scatter growth initial, mcolor("217 119 87"%70) msymbol(O) msize(small)
    mlabel(ccode) mlabsize(vsmall) mlabpos(0) mlabcolor("20 20 19"%60)),
   ytitle("Average Annual Growth Rate (1960-2019)")
   xtitle("Log GDP per Capita in 1960")
   title("Beta Convergence Test: 1960 to 2019", color("20 20 19"))
   subtitle("Do poorer countries grow faster?", color("20 20 19"))
   xlabel(6 "$403" 7 "$1,097" 8 "$2,981" 9 "$8,103" 10 "$22,026", angle(0))
   ylabel(, angle(horizontal))
   legend(off)
   plotregion(style(none) lcolor(none))
   graphregion(fcolor(white) lcolor(white))
   note("Data: Penn World Tables 10.0. 84-country balanced panel."
        "Excludes oil producers and countries with pop < 1M."
        "Shaded area: 95% confidence interval around fitted line.", size(vsmall))
   xsize(5) ysize(4)
;
#delimit cr
graph export "stata_convergence_scatter_1960_2019.png", replace width(2400)
display as text "Figure saved: stata_convergence_scatter_1960_2019.png"

* ── Export results ──
preserve
    clear
    set obs 1
    gen period = "1960-2019"
    gen method = "OLS"
    gen beta = `beta_ols'
    gen se = `se_ols'
    gen pvalue = `p_ols'
    gen n = `e(N)'
    export delimited using "convergence_beta_simple.csv", replace
restore


*═══════════════════════════════════════════════════════════════════════════════
* SECTION 2: BETA CONVERGENCE — COMPARING TWO ERAS
*
*   A single regression over 1960-2019 misses a crucial story:
*   the WORLD CHANGED in the mid-1990s.
*
*   - Era of DIVERGENCE (1960-2000): Poor countries did NOT catch up.
*   - Era of CONVERGENCE (2000-2019): Poor countries ARE catching up.
*
*   By splitting the sample, we can see this structural break.
*═══════════════════════════════════════════════════════════════════════════════

display as text ""
display as text "────────────────────────────────────────────────────────────────"
display as text "  SECTION 2: COMPARING TWO ERAS — Divergence vs. Convergence"
display as text "────────────────────────────────────────────────────────────────"

* ── Era of Divergence: 1960 to 2000 ──
display as text ""
display as text "--- Era 1: 1960 to 2000 (the 'divergence era') ---"
gen growth_era1 = (1/40) * ln(gdppc2000 / gdppc1960)
gen initial_era1 = ln(gdppc1960)
reg growth_era1 initial_era1 if !missing(growth_era1) & !missing(initial_era1), robust
local b_era1 = _b[initial_era1]
local se_era1 = _se[initial_era1]
local n_era1 = e(N)

* ── Era of Convergence: 2000 to 2019 ──
display as text ""
display as text "--- Era 2: 2000 to 2019 (the 'convergence era') ---"
gen growth_era2 = (1/19) * ln(gdppc2019 / gdppc2000)
gen initial_era2 = ln(gdppc2000)
reg growth_era2 initial_era2 if !missing(growth_era2) & !missing(initial_era2), robust
local b_era2 = _b[initial_era2]
local se_era2 = _se[initial_era2]
local n_era2 = e(N)

display as text ""
display as text "╔══════════════════════════════════════════════════════════════╗"
display as text "║  Comparison of Two Eras                                    ║"
display as text "╚══════════════════════════════════════════════════════════════╝"
display as text "  Era of Divergence (1960-2000):  λ = " %7.5f `b_era1' " (SE = " %7.5f `se_era1' ", N = " `n_era1' ")"
display as text "  Era of Convergence (2000-2019): λ = " %7.5f `b_era2' " (SE = " %7.5f `se_era2' ", N = " `n_era2' ")"
display as text ""
display as text "  The slope FLIPPED from positive (divergence) to negative (convergence)."
display as text "  This is the 'new era of unconditional convergence.'"

* ── Side-by-side scatter plots ──
#delimit ;
tw (lfitci growth_era1 initial_era1, fcolor("217 119 87"%15) lcolor("217 119 87"))
   (scatter growth_era1 initial_era1, mcolor("217 119 87"%60) msymbol(O) msize(small)
    mlabel(ccode) mlabsize(tiny) mlabpos(0) mlabcolor("20 20 19"%40)),
   ytitle("Average Annual Growth Rate")
   xtitle("Log Initial GDP per Capita")
   title("Era of Divergence: 1960 to 2000", color("20 20 19") size(medium))
   xlabel(6 "$403" 7 "$1,097" 8 "$2,981" 9 "$8,103" 10 "$22,026", angle(0))
   ylabel(-0.02 "-2%" 0 "0%" 0.02 "2%" 0.04 "4%" 0.06 "6%", angle(horizontal))
   legend(off)
   plotregion(style(none) lcolor(none))
   graphregion(fcolor(white) lcolor(white))
   xsize(4) ysize(4)
   saving("_era1.gph", replace)
;
tw (lfitci growth_era2 initial_era2, fcolor("106 155 204"%15) lcolor("106 155 204"))
   (scatter growth_era2 initial_era2, mcolor("106 155 204"%60) msymbol(O) msize(small)
    mlabel(ccode) mlabsize(tiny) mlabpos(0) mlabcolor("20 20 19"%40)),
   ytitle("Average Annual Growth Rate")
   xtitle("Log Initial GDP per Capita")
   title("Era of Convergence: 2000 to 2019", color("20 20 19") size(medium))
   xlabel(6 "$403" 7 "$1,097" 8 "$2,981" 9 "$8,103" 10 "$22,026" 11 "$59,874", angle(0))
   ylabel(-0.02 "-2%" 0 "0%" 0.02 "2%" 0.04 "4%" 0.06 "6%", angle(horizontal))
   legend(off)
   plotregion(style(none) lcolor(none))
   graphregion(fcolor(white) lcolor(white))
   xsize(4) ysize(4)
   saving("_era2.gph", replace)
;
#delimit cr
graph combine "_era1.gph" "_era2.gph", ///
    title("The Structural Break in Cross-Country Growth", color("20 20 19")) ///
    subtitle("The slope flipped from positive (divergence) to negative (convergence)", ///
             color("20 20 19") size(small)) ///
    note("Data: Penn World Tables 10.0. 84-country balanced panel.", ///
         size(vsmall)) ///
    graphregion(fcolor(white) lcolor(white)) ///
    xsize(8) ysize(4)
graph export "stata_convergence_scatter_two_eras.png", replace width(2400)
display as text "Figure saved: stata_convergence_scatter_two_eras.png"
capture erase "_era1.gph"
capture erase "_era2.gph"


*═══════════════════════════════════════════════════════════════════════════════
* SECTION 3: SPEED OF CONVERGENCE AND HALF-LIFE FROM OLS
*
*   The OLS slope coefficient (λ) tells us the direction of convergence,
*   but HOW FAST are poor countries catching up? Two key metrics:
*
*   (A) SPEED OF CONVERGENCE (β):
*       The fraction of the income gap that closes each year.
*       β = 0.02 means 2% of the gap is closed annually.
*       Classic benchmark: β ≈ 2% (Barro & Sala-i-Martin, 1992)
*       — but that was for CONDITIONAL convergence.
*
*   (B) HALF-LIFE (τ):
*       How many years to close HALF the gap to steady-state income?
*       τ = ln(2) / β
*       Classic benchmark: τ ≈ 35 years (conditional convergence)
*
*   The SIMPLEST way to get β from OLS:
*     The OLS coefficient λ and the structural parameter β are linked by:
*       λ = -((1 - exp(-β×s)) / s)
*     Solving for β:
*       β = -ln(1 + λ×s) / s
*     Then:
*       τ = ln(2) / β
*
*   This gives us speed and half-life using ONLY standard OLS output.
*═══════════════════════════════════════════════════════════════════════════════

display as text ""
display as text "────────────────────────────────────────────────────────────────"
display as text "  SECTION 3: SPEED OF CONVERGENCE AND HALF-LIFE FROM OLS"
display as text "────────────────────────────────────────────────────────────────"

display as text ""
display as text "  DERIVATION: OLS λ → structural β → half-life"
display as text ""
display as text "  Step 1: Run OLS and get the slope coefficient λ"
display as text "    growth_i = α + λ × ln(y_i,0) + ε_i"
display as text ""
display as text "  Step 2: The Barro-Sala-i-Martin model implies:"
display as text "    λ = -((1 - exp(-β×s)) / s)"
display as text ""
display as text "  Step 3: Solve for β:"
display as text "    λ×s = -(1 - exp(-β×s))"
display as text "    exp(-β×s) = 1 + λ×s"
display as text "    -β×s = ln(1 + λ×s)"
display as text "    β = -ln(1 + λ×s) / s"
display as text ""
display as text "  Step 4: Half-life:"
display as text "    τ = ln(2) / β"
display as text ""

* ── Define periods ──
local periods   "1960-2019 1960-2000 1980-2019 1990-2019 1995-2019 2000-2019"
local starts    "1960      1960      1980      1990      1995      2000"
local ends      "2019      2000      2019      2019      2019      2019"

* ── Create file to collect OLS results ──
tempfile ols_speed
preserve
    clear
    gen str20 period = ""
    gen double lambda_ols = .
    gen double se_lambda = .
    gen double pvalue = .
    gen double beta_ols = .
    gen double speed_ols = .
    gen double halflife_ols = .
    gen int    n = .
    save `ols_speed', replace
restore

* ── Loop over periods: OLS ──
local nperiods : word count `periods'
forval p = 1/`nperiods' {
    local period : word `p' of `periods'
    local sy : word `p' of `starts'
    local ey : word `p' of `ends'
    local s = `ey' - `sy'

    display as text ""
    display as text "--- Period: `period' (s = `s' years) ---"

    preserve
        use "convergence_working.dta", clear
        keep ccode country year gdppc
        reshape wide gdppc, i(ccode country) j(year)

        gen outcome = (1/`s') * ln(gdppc`ey' / gdppc`sy')
        gen initial_inc = ln(gdppc`sy')
        drop if missing(outcome) | missing(initial_inc)

        * ── OLS regression ──
        reg outcome initial_inc, robust
        local lambda = _b[initial_inc]
        local se = _se[initial_inc]
        local pval = 2 * ttail(e(df_r), abs(`lambda'/`se'))
        local n_obs = e(N)

        * ── Convert λ to β ──
        local lambda_s = `lambda' * `s'
        if (1 + `lambda_s') > 0 {
            local beta_speed = -ln(1 + `lambda_s') / `s'
        }
        else {
            local beta_speed = .
        }
        local speed = `beta_speed' * 100

        * ── Half-life ──
        if `beta_speed' > 0 & `beta_speed' < . {
            local halflife = ln(2) / `beta_speed'
        }
        else {
            local halflife = .
        }

        display as result "  OLS λ = " %8.5f `lambda' " (SE = " %8.5f `se' ", p = " %5.3f `pval' ")"
        display as result "  Structural β = -ln(1 + " %7.5f `lambda' " × `s') / `s' = " %8.5f `beta_speed'
        display as result "  Speed = " %5.2f `speed' "% per year"
        if `halflife' < . {
            display as result "  Half-life = ln(2) / " %7.5f `beta_speed' " = " %6.1f `halflife' " years"
        }
        else {
            display as result "  Half-life = not computable (no convergence)"
        }
        display as result "  N = " `n_obs' " countries"
    restore

    * Store results
    preserve
        clear
        set obs 1
        gen str20 period = "`period'"
        gen double lambda_ols = `lambda'
        gen double se_lambda = `se'
        gen double pvalue = `pval'
        gen double beta_ols = `beta_speed'
        gen double speed_ols = `speed'
        gen double halflife_ols = `halflife'
        gen int    n = `n_obs'
        append using `ols_speed'
        save `ols_speed', replace
    restore
}

* ── Display OLS results table ──
display as text ""
display as text "╔══════════════════════════════════════════════════════════════════════════════════╗"
display as text "║  Speed of Convergence from OLS: λ → β → Half-Life                              ║"
display as text "╠══════════════════════════════════════════════════════════════════════════════════╣"
display as text "║  Formula: β = -ln(1 + λ×s)/s     Half-life: τ = ln(2)/β                        ║"
display as text "║  Benchmark: β ≈ 2%/yr, τ ≈ 35 yrs (conditional, Barro & Sala-i-Martin 1992)    ║"
display as text "╚══════════════════════════════════════════════════════════════════════════════════╝"

preserve
    use `ols_speed', clear
    drop if missing(lambda_ols)
    sort period
    list period lambda_ols beta_ols speed_ols halflife_ols n, noobs clean
    export delimited using "convergence_speed_ols.csv", replace
    display as text "Results exported to: convergence_speed_ols.csv"
restore

* ── Create OLS speed bar chart ──
preserve
    use `ols_speed', clear
    drop if missing(lambda_ols)
    sort period
    gen id = _n
    gen speed_bench = 2

    #delimit ;
    tw (bar speed_ols id, barwidth(0.6) fcolor("217 119 87") lcolor("217 119 87"%80))
       (line speed_bench id, lcolor("106 155 204") lwidth(medthick) lpattern(dash)),
       xlabel(1 "1960-2000" 2 "1960-2019" 3 "1980-2019" 4 "1990-2019" 5 "1995-2019" 6 "2000-2019",
              angle(30) labsize(small))
       ylabel(, angle(horizontal))
       ytitle("Speed of Convergence (% per year)")
       xtitle("Period")
       title("Speed of Convergence from OLS ({&lambda} {&rarr} {&beta} conversion)", color("20 20 19"))
       subtitle("Benchmark: 2% per year (Barro & Sala-i-Martin 1992, conditional)", ///
                color("20 20 19") size(small))
       legend(order(1 "Unconditional {&beta} (from OLS)" 2 "Conditional benchmark (2%/yr)")
              pos(2) ring(0) region(lcolor(none) fcolor(none)) size(small))
       plotregion(style(none) lcolor(none))
       graphregion(fcolor(white) lcolor(white))
       note("Data: Penn World Tables 10.0. 84-country balanced panel."
            "Negative values indicate divergence. {&beta} = -ln(1 + {&lambda}×s)/s.", size(vsmall))
       xsize(6) ysize(4)
    ;
    #delimit cr
    graph export "stata_convergence_speed_ols.png", replace width(2400)
    display as text "Figure saved: stata_convergence_speed_ols.png"
restore


*═══════════════════════════════════════════════════════════════════════════════
* SECTION 4: WHAT IS NONLINEAR LEAST SQUARES (NLS)?
*
*   The OLS-to-β conversion in Section 3 works, but it goes BACKWARDS:
*   we estimate λ first and then convert to β. Can we estimate β DIRECTLY?
*
*   YES — using Nonlinear Least Squares (NLS).
*
*   WHY can't OLS estimate β directly?
*     The Barro-Sala-i-Martin (1992) convergence equation is:
*       (1/s) × ln(y_{t+s}/y_t) = α - ((1 - exp(-β×s))/s) × ln(y_t) + ε
*     The parameter β appears INSIDE an exponential: exp(-β×s).
*     OLS requires that parameters enter LINEARLY, but β is trapped inside
*     exp(), so OLS cannot estimate it directly.
*
*   WHAT does NLS do?
*     Like OLS, NLS minimizes the sum of squared residuals:
*       min_β Σᵢ [yᵢ - f(xᵢ; β)]²
*     But unlike OLS, the function f() can be ANY nonlinear function of β.
*     NLS uses an iterative algorithm:
*       1. Start with an initial guess for β (e.g., β₀ = 0.02)
*       2. Compute predicted values and residuals
*       3. Adjust β in the direction that reduces the sum of squared residuals
*       4. Repeat until the improvement is negligible (convergence)
*
*   HOW to estimate in Stata:
*     Stata's -nl- command does NLS. The syntax is:
*       nl (depvar = expression_with_{parameters}), vce(robust)
*     Parameters are specified in curly braces: {b1=initial_guess}
*
*   ADVANTAGE: β has a direct structural interpretation as the speed of
*   convergence, and standard errors apply directly to β (no conversion
*   needed, no delta method).
*═══════════════════════════════════════════════════════════════════════════════

display as text ""
display as text "────────────────────────────────────────────────────────────────"
display as text "  SECTION 4: WHAT IS NONLINEAR LEAST SQUARES (NLS)?"
display as text "────────────────────────────────────────────────────────────────"

display as text ""
display as text "  The Barro-Sala-i-Martin (1992) convergence equation:"
display as text ""
display as text "    (1/s) × ln(y_{t+s}/y_t) = α - ((1 - exp(-β×s))/s) × ln(y_t) + ε"
display as text ""
display as text "  Breaking down each term:"
display as text "    Left side:  annualized growth rate over s years"
display as text "    α:          intercept (related to the steady-state income)"
display as text "    β:          speed of convergence (fraction of gap closed per year)"
display as text "    s:          number of years between initial and final period"
display as text "    ln(y_t):    log of initial GDP per capita"
display as text "    ε:          error term (random shocks to growth)"
display as text ""
display as text "  The key expression is: (1 - exp(-β×s))/s"
display as text "    This is the 'convergence factor' — how much of the initial"
display as text "    income level feeds into the growth rate."
display as text "    When β > 0: convergence factor > 0, and the minus sign means"
display as text "                higher initial income → lower growth (convergence)"
display as text "    When β = 0: convergence factor = 0, no relationship"
display as text "    When β < 0: divergence"
display as text ""

* ── Demonstrate NLS for one period: 2000-2019 ──
display as text "--- NLS Demonstration: 2000-2019 ---"
display as text ""

preserve
    use "convergence_working.dta", clear
    keep ccode country year gdppc
    reshape wide gdppc, i(ccode country) j(year)

    local s = 19
    gen outcome = (1/`s') * ln(gdppc2019 / gdppc2000)
    gen initial_inc = ln(gdppc2000)
    drop if missing(outcome) | missing(initial_inc)

    display as text "  Stata command:"
    display as text "    nl (outcome = {b0=1} - (1 - exp(-1*{b1=0.02}*19))/19 * initial_inc), vce(robust)"
    display as text ""
    display as text "  Reading the syntax:"
    display as text "    {b0=1}    → intercept α, initial guess = 1"
    display as text "    {b1=0.02} → speed of convergence β, initial guess = 0.02 (the 2% benchmark)"
    display as text "    *19       → s = 19 years (2000 to 2019)"
    display as text "    initial_inc → ln(y_2000), our independent variable"
    display as text ""

    nl (outcome = {b0=1} - (1 - exp(-1*{b1=0.02}*`s'))/`s' * initial_inc), vce(robust)

    display as text ""
    display as text "  HOW TO READ THE OUTPUT:"
    display as text "    /b0 = " %8.5f _b[/b0] " → This is α (intercept)"
    display as text "    /b1 = " %8.5f _b[/b1] " → This is β (speed of convergence)"
    display as text ""
    display as text "    Speed = β × 100 = " %5.2f _b[/b1]*100 "% per year"
    display as text "    Half-life = ln(2)/β = " %6.1f ln(2)/_b[/b1] " years"
    display as text ""

    * Save NLS result before running OLS
    local nls_beta_demo = _b[/b1]

    * Compare with OLS conversion
    qui reg outcome initial_inc, robust
    local lambda_check = _b[initial_inc]
    local beta_check = -ln(1 + `lambda_check'*`s') / `s'
    display as text "  COMPARISON with OLS conversion (Section 3):"
    display as text "    OLS λ = " %8.5f `lambda_check'
    display as text "    OLS → β = -ln(1 + " %7.5f `lambda_check' " × `s') / `s' = " %8.5f `beta_check'
    display as text "    NLS β  = " %8.5f `nls_beta_demo'
    display as text "    Difference = " %10.7f abs(`beta_check' - `nls_beta_demo')
    display as text ""
    display as text "  The point estimates are nearly identical."
    display as text "  The advantage of NLS: standard errors and p-values apply directly to β."
restore


*═══════════════════════════════════════════════════════════════════════════════
* SECTION 5: SPEED OF CONVERGENCE AND HALF-LIFE FROM NLS
*
*   Now we estimate β directly via NLS for the same 6 periods as Section 3.
*   The results should be very close to the OLS conversion, confirming
*   that both methods give the same structural parameter.
*═══════════════════════════════════════════════════════════════════════════════

display as text ""
display as text "────────────────────────────────────────────────────────────────"
display as text "  SECTION 5: SPEED OF CONVERGENCE AND HALF-LIFE FROM NLS"
display as text "────────────────────────────────────────────────────────────────"

* ── Create file to collect NLS results ──
tempfile nls_speed
preserve
    clear
    gen str20 period = ""
    gen double beta_nls = .
    gen double se_nls = .
    gen double pvalue_nls = .
    gen double speed_nls = .
    gen double halflife_nls = .
    gen int    n = .
    save `nls_speed', replace
restore

* ── Loop over periods: NLS ──
forval p = 1/`nperiods' {
    local period : word `p' of `periods'
    local sy : word `p' of `starts'
    local ey : word `p' of `ends'
    local s = `ey' - `sy'

    display as text ""
    display as text "--- Period: `period' (s = `s' years) ---"

    preserve
        use "convergence_working.dta", clear
        keep ccode country year gdppc
        reshape wide gdppc, i(ccode country) j(year)

        gen outcome = (1/`s') * ln(gdppc`ey' / gdppc`sy')
        gen initial_inc = ln(gdppc`sy')
        drop if missing(outcome) | missing(initial_inc)

        * ── NLS estimation ──
        capture noisily nl (outcome = {b0=1} - (1 - exp(-1*{b1=0.00}*`s'))/`s' * initial_inc), ///
            vce(robust)

        if _rc == 0 {
            local beta_nls = _b[/b1]
            local se_nls   = _se[/b1]
            local n_obs    = e(N)
            local pval_nls = 2 * ttail(e(df_r), abs(`beta_nls'/`se_nls'))
            local speed = `beta_nls' * 100

            if `beta_nls' > 0 {
                local halflife = ln(2) / `beta_nls'
            }
            else {
                local halflife = .
            }

            display as text ""
            display as result "  NLS β = " %8.5f `beta_nls' " (SE = " %8.5f `se_nls' ", p = " %5.3f `pval_nls' ")"
            display as result "  Speed = " %5.2f `speed' "% per year"
            if `halflife' < . {
                display as result "  Half-life = " %6.1f `halflife' " years"
            }
            else {
                display as result "  Half-life = not computable (no convergence)"
            }
            display as result "  N = " `n_obs' " countries"
        }
        else {
            display as error "  NLS did not converge for period `period'"
            local beta_nls = .
            local se_nls = .
            local pval_nls = .
            local speed = .
            local halflife = .
            local n_obs = .
        }
    restore

    if `beta_nls' < . {
        preserve
            clear
            set obs 1
            gen str20 period = "`period'"
            gen double beta_nls = `beta_nls'
            gen double se_nls = `se_nls'
            gen double pvalue_nls = `pval_nls'
            gen double speed_nls = `speed'
            gen double halflife_nls = `halflife'
            gen int    n = `n_obs'
            append using `nls_speed'
            save `nls_speed', replace
        restore
    }
}

* ── Display NLS results table ──
display as text ""
display as text "╔══════════════════════════════════════════════════════════════════════════════════╗"
display as text "║  Speed of Convergence from NLS (Direct Estimation of β)                        ║"
display as text "╠══════════════════════════════════════════════════════════════════════════════════╣"
display as text "║  Benchmark: β ≈ 2%/yr, τ ≈ 35 yrs (conditional, Barro & Sala-i-Martin 1992)    ║"
display as text "╚══════════════════════════════════════════════════════════════════════════════════╝"

preserve
    use `nls_speed', clear
    drop if missing(beta_nls)
    sort period
    list period beta_nls se_nls speed_nls halflife_nls n, noobs clean
    export delimited using "convergence_speed_nls.csv", replace
    display as text "Results exported to: convergence_speed_nls.csv"
restore

* ── Create NLS speed bar chart ──
preserve
    use `nls_speed', clear
    drop if missing(beta_nls)
    sort period
    gen id = _n
    gen speed_bench = 2

    #delimit ;
    tw (bar speed_nls id, barwidth(0.6) fcolor("106 155 204") lcolor("106 155 204"%80))
       (line speed_bench id, lcolor("217 119 87") lwidth(medthick) lpattern(dash)),
       xlabel(1 "1960-2000" 2 "1960-2019" 3 "1980-2019" 4 "1990-2019" 5 "1995-2019" 6 "2000-2019",
              angle(30) labsize(small))
       ylabel(, angle(horizontal))
       ytitle("Speed of Convergence (% per year)")
       xtitle("Period")
       title("Speed of Convergence from NLS (Direct Estimation)", color("20 20 19"))
       subtitle("Benchmark: 2% per year (Barro & Sala-i-Martin 1992, conditional)", ///
                color("20 20 19") size(small))
       legend(order(1 "Unconditional {&beta} (NLS)" 2 "Conditional benchmark (2%/yr)")
              pos(2) ring(0) region(lcolor(none) fcolor(none)) size(small))
       plotregion(style(none) lcolor(none))
       graphregion(fcolor(white) lcolor(white))
       note("Data: Penn World Tables 10.0. 84-country balanced panel."
            "Negative values indicate divergence.", size(vsmall))
       xsize(6) ysize(4)
    ;
    #delimit cr
    graph export "stata_convergence_speed_nls.png", replace width(2400)
    display as text "Figure saved: stata_convergence_speed_nls.png"
restore


*═══════════════════════════════════════════════════════════════════════════════
* SECTION 6: OLS vs NLS COMPARISON
*
*   How do the two methods compare? The OLS conversion and NLS should give
*   nearly identical point estimates for β. The key difference:
*     - OLS: standard errors and p-values are for λ (must be transformed)
*     - NLS: standard errors and p-values are directly for β
*═══════════════════════════════════════════════════════════════════════════════

display as text ""
display as text "────────────────────────────────────────────────────────────────"
display as text "  SECTION 6: OLS vs NLS COMPARISON"
display as text "────────────────────────────────────────────────────────────────"

* ── Merge OLS and NLS results ──
preserve
    use `ols_speed', clear
    drop if missing(lambda_ols)
    sort period
    tempfile ols_sorted
    save `ols_sorted', replace

    use `nls_speed', clear
    drop if missing(beta_nls)
    sort period
    merge 1:1 period using `ols_sorted', keep(match) nogen

    gen diff = abs(beta_ols - beta_nls)

    display as text ""
    display as text "╔══════════════════════════════════════════════════════════════════════════════════════╗"
    display as text "║  OLS vs NLS: Side-by-Side Comparison                                              ║"
    display as text "╠══════════════════════════════════════════════════════════════════════════════════════╣"
    display as text "║  OLS:  estimate λ, then β = -ln(1+λs)/s                                           ║"
    display as text "║  NLS:  estimate β directly via iterative optimization                              ║"
    display as text "╚══════════════════════════════════════════════════════════════════════════════════════╝"

    list period lambda_ols beta_ols beta_nls diff speed_ols speed_nls n, noobs clean

    display as text ""
    display as text "  KEY OBSERVATIONS:"
    display as text "  1. The β estimates from OLS conversion and NLS are nearly identical."
    display as text "  2. Small differences arise from numerical optimization in NLS."
    display as text "  3. NLS advantage: SE and p-values apply directly to β."
    display as text "  4. OLS advantage: simpler, faster, no convergence issues."

    export delimited using "convergence_ols_vs_nls.csv", replace
    display as text "Results exported to: convergence_ols_vs_nls.csv"
restore


*═══════════════════════════════════════════════════════════════════════════════
* SECTION 7A: ROLLING WINDOW — OLS LAMBDA
*
*   Before computing rolling SPEED of convergence, we start with the
*   simplest rolling window: the raw OLS coefficient lambda.
*
*   Method: Fix the end year at 2019. For each start year from 1960 to 2010,
*   estimate the OLS regression and collect lambda and its 95% CI.
*   The CI for lambda is the standard OLS confidence interval:
*     lambda_hat ± t_{N-2, 0.025} × SE(lambda_hat)
*
*   This gives us a time series showing how the RAW convergence signal
*   has evolved before any nonlinear transformation.
*═══════════════════════════════════════════════════════════════════════════════

display as text ""
display as text "────────────────────────────────────────────────────────────────"
display as text "  SECTION 7A: ROLLING WINDOW — OLS LAMBDA"
display as text "────────────────────────────────────────────────────────────────"

* ── Load and reshape data ──
use "convergence_working.dta", clear
keep ccode country year gdppc
reshape wide gdppc, i(ccode country) j(year)

local j = 1
local lastyear = 2019

forval startyear = 1960(1)2010 {
    local s = `lastyear' - `startyear'

    capture drop outcome initial_inc
    gen outcome = (1/`s') * ln(gdppc`lastyear' / gdppc`startyear')
    gen initial_inc = ln(gdppc`startyear')

    qui reg outcome initial_inc if !missing(outcome) & !missing(initial_inc), robust
    local lambda = _b[initial_inc]
    local se_lambda = _se[initial_inc]
    local n_obs = e(N)

    * Standard OLS confidence interval: lambda ± t × SE
    local lambda_lb = `lambda' - invttail(e(df_r), 0.025) * `se_lambda'
    local lambda_ub = `lambda' + invttail(e(df_r), 0.025) * `se_lambda'

    preserve
        clear
        set obs 1
        tempfile lam_roll`j'
        gen startyear = `startyear'
        gen endyear = `lastyear'
        gen lambda = `lambda'
        gen se = `se_lambda'
        gen lower = `lambda_lb'
        gen upper = `lambda_ub'
        gen n = `n_obs'
        save `lam_roll`j''
    restore
    local ++j
}

* ── Combine rolling lambda results ──
local j_lam = `j' - 1
preserve
    clear
    forval i = 1/`j_lam' {
        capture append using `lam_roll`i''
    }

    display as text ""
    display as text "╔══════════════════════════════════════════════════════════════╗"
    display as text "║  Rolling OLS Lambda: Key Findings                          ║"
    display as text "╚══════════════════════════════════════════════════════════════╝"
    list startyear lambda se lower upper n if inlist(startyear, 1960, 1970, 1980, 1990, 1995, 2000, 2005, 2010), noobs clean

    export delimited using "convergence_rolling_lambda.csv", replace

    * ── Create rolling lambda plot ──
    #delimit ;
    tw (rcap lower upper startyear, lcolor("217 119 87"%30))
       (scatter lambda startyear, mcolor("217 119 87"%80) msymbol(O) msize(small)),
       yline(0, lcolor("20 20 19") lpattern(shortdash) lwidth(medium))
       xlabel(1960(5)2010, angle(45))
       ylabel(, angle(horizontal) format(%6.4f))
       ytitle("{&lambda} (OLS slope coefficient)", size(medlarge))
       xtitle("Initial Year")
       title("Rolling OLS Coefficient ({&lambda})", color("20 20 19"))
       subtitle("Each point: OLS {&lambda} from initial year to 2019", ///
                color("20 20 19") size(small))
       legend(off)
       plotregion(style(none) lcolor(none))
       graphregion(fcolor(white) lcolor(white))
       note("{&lambda} < 0: convergence. {&lambda} > 0: divergence."
            "Bars: 95% CI ({&lambda} ± t × SE). 84-country balanced panel.", size(vsmall))
       xsize(5) ysize(4)
    ;
    #delimit cr
    graph export "stata_convergence_rolling_lambda.png", replace width(2400)
    display as text "Figure saved: stata_convergence_rolling_lambda.png"
restore


*═══════════════════════════════════════════════════════════════════════════════
* SECTION 7B: ROLLING BETA CONVERGENCE — OLS AND NLS
*
*   Now we convert the rolling lambda to structural beta and compare
*   with NLS direct estimation.
*
*   Instead of just two snapshots, let's see the FULL MOVIE:
*     How has the convergence coefficient evolved year by year?
*
*   Method: Fix the end year at 2019. For each start year from 1960 to 2010,
*   estimate the convergence β using:
*     (A) OLS λ → β conversion
*     (B) NLS direct estimation
*
*   This gives us TWO rolling-window time series of β.
*
*   Convention:
*     β > 0 → convergence (poorer countries catching up)
*     β < 0 → divergence (gap widening)
*═══════════════════════════════════════════════════════════════════════════════

display as text ""
display as text "────────────────────────────────────────────────────────────────"
display as text "  SECTION 7: ROLLING BETA CONVERGENCE — OLS AND NLS"
display as text "────────────────────────────────────────────────────────────────"

* ── Load and reshape data ──
use "convergence_working.dta", clear
keep ccode country year gdppc
reshape wide gdppc, i(ccode country) j(year)

* ── (A) Rolling OLS ──
display as text ""
display as text "--- (A) Rolling OLS (λ → β conversion) ---"
local j = 1
local lastyear = 2019

forval startyear = 1960(1)2010 {
    local s = `lastyear' - `startyear'

    capture drop outcome initial_inc
    gen outcome = (1/`s') * ln(gdppc`lastyear' / gdppc`startyear')
    gen initial_inc = ln(gdppc`startyear')

    qui reg outcome initial_inc if !missing(outcome) & !missing(initial_inc), robust
    local lambda = _b[initial_inc]
    local se_lambda = _se[initial_inc]
    local n_obs = e(N)

    * Convert λ to β
    local lambda_s = `lambda' * `s'
    if (1 + `lambda_s') > 0 {
        local beta_val = -ln(1 + `lambda_s') / `s'
    }
    else {
        local beta_val = .
    }

    * Convert λ CI to β CI (bounds flip because the function is monotone decreasing)
    local lambda_lb = `lambda' - invttail(e(df_r), 0.025) * `se_lambda'
    local lambda_ub = `lambda' + invttail(e(df_r), 0.025) * `se_lambda'
    if (1 + `lambda_lb'*`s') > 0 {
        local beta_ub = -ln(1 + `lambda_lb'*`s') / `s'
    }
    else {
        local beta_ub = .
    }
    if (1 + `lambda_ub'*`s') > 0 {
        local beta_lb = -ln(1 + `lambda_ub'*`s') / `s'
    }
    else {
        local beta_lb = .
    }

    preserve
        clear
        set obs 1
        tempfile ols_roll`j'
        gen startyear = `startyear'
        gen endyear = `lastyear'
        gen beta = `beta_val'
        gen lower = `beta_lb'
        gen upper = `beta_ub'
        gen speed_pct = `beta_val' * 100
        gen halflife = .
        if `beta_val' > 0 & `beta_val' < . {
            replace halflife = ln(2) / `beta_val'
        }
        gen n = `n_obs'
        save `ols_roll`j''
    restore
    local ++j
}

* ── Combine OLS rolling results ──
local j_ols = `j' - 1
preserve
    clear
    forval i = 1/`j_ols' {
        capture append using `ols_roll`i''
    }

    display as text ""
    display as text "╔══════════════════════════════════════════════════════════════╗"
    display as text "║  Rolling OLS Beta Convergence: Key Findings                ║"
    display as text "╚══════════════════════════════════════════════════════════════╝"
    list startyear beta speed_pct halflife n if inlist(startyear, 1960, 1970, 1980, 1990, 1995, 2000, 2005, 2010), noobs clean

    export delimited using "convergence_rolling_beta_ols.csv", replace

    * ── Create OLS rolling beta plot ──
    #delimit ;
    tw (rcap lower upper startyear, lcolor("217 119 87"%30))
       (scatter beta startyear, mcolor("217 119 87"%80) msymbol(O) msize(small)),
       yline(0, lcolor("20 20 19") lpattern(shortdash) lwidth(medium))
       xlabel(1960(5)2010, angle(45))
       ylabel(-0.005(0.0025)0.01, angle(horizontal) format(%6.4f))
       ytitle("{&beta} (speed of convergence)", size(medlarge))
       xtitle("Initial Year")
       title("Rolling Unconditional Convergence: OLS", color("20 20 19"))
       subtitle("Each point: OLS {&lambda} {&rarr} {&beta}, from initial year to 2019", ///
                color("20 20 19") size(small))
       legend(off)
       plotregion(style(none) lcolor(none))
       graphregion(fcolor(white) lcolor(white))
       note("{&beta} > 0: convergence. {&beta} < 0: divergence."
            "Bars: 95% CI (converted from OLS). 84-country balanced panel.", size(vsmall))
       xsize(5) ysize(4)
    ;
    #delimit cr
    graph export "stata_convergence_rolling_beta_ols.png", replace width(2400)
    display as text "Figure saved: stata_convergence_rolling_beta_ols.png"
restore

* ── (B) Rolling NLS ──
display as text ""
display as text "--- (B) Rolling NLS (direct estimation) ---"
local j = 1

forval startyear = 1960(1)2010 {
    local s = `lastyear' - `startyear'

    capture drop outcome initial_inc
    gen outcome = (1/`s') * ln(gdppc`lastyear' / gdppc`startyear')
    gen initial_inc = ln(gdppc`startyear')

    capture qui nl (outcome = {b0=1} - (1 - exp(-1*{b1=0.00}*`s'))/`s' * initial_inc) ///
        if !missing(outcome) & !missing(initial_inc), vce(robust)

    if _rc == 0 {
        preserve
            clear
            set obs 1
            tempfile nls_roll`j'
            gen startyear = `startyear'
            gen endyear = `lastyear'
            gen beta = _b[/b1]
            gen se = _se[/b1]
            gen lower = _b[/b1] - invttail(`e(df_r)', 0.025) * _se[/b1]
            gen upper = _b[/b1] + invttail(`e(df_r)', 0.025) * _se[/b1]
            gen n = `e(N)'
            gen speed_pct = beta * 100
            gen halflife = .
            if _b[/b1] > 0 {
                replace halflife = ln(2) / _b[/b1]
            }
            save `nls_roll`j''
        restore
        local ++j
    }
    else {
        display as text "  Warning: NLS did not converge for start year `startyear'"
        local ++j
    }
}

* ── Combine NLS rolling results ──
local j_nls = `j' - 1
preserve
    clear
    forval i = 1/`j_nls' {
        capture append using `nls_roll`i''
    }

    display as text ""
    display as text "╔══════════════════════════════════════════════════════════════╗"
    display as text "║  Rolling NLS Beta Convergence: Key Findings                ║"
    display as text "╚══════════════════════════════════════════════════════════════╝"
    list startyear beta speed_pct halflife n if inlist(startyear, 1960, 1970, 1980, 1990, 1995, 2000, 2005, 2010), noobs clean

    export delimited using "convergence_rolling_beta_nls.csv", replace

    * ── Create NLS rolling beta plot ──
    #delimit ;
    tw (rcap lower upper startyear, lcolor("106 155 204"%30))
       (scatter beta startyear, mcolor("106 155 204"%80) msymbol(O) msize(small)),
       yline(0, lcolor("20 20 19") lpattern(shortdash) lwidth(medium))
       xlabel(1960(5)2010, angle(45))
       ylabel(-0.005(0.0025)0.01, angle(horizontal) format(%6.4f))
       ytitle("{&beta} (speed of convergence)", size(medlarge))
       xtitle("Initial Year")
       title("Rolling Unconditional Convergence: NLS", color("20 20 19"))
       subtitle("Each point: NLS {&beta} from initial year to 2019", ///
                color("20 20 19") size(small))
       legend(off)
       plotregion(style(none) lcolor(none))
       graphregion(fcolor(white) lcolor(white))
       note("{&beta} > 0: convergence. {&beta} < 0: divergence."
            "Bars: 95% confidence intervals. 84-country balanced panel.", size(vsmall))
       xsize(5) ysize(4)
    ;
    #delimit cr
    graph export "stata_convergence_rolling_beta_nls.png", replace width(2400)
    display as text "Figure saved: stata_convergence_rolling_beta_nls.png"
restore


*═══════════════════════════════════════════════════════════════════════════════
* SECTION 8: SIGMA CONVERGENCE — THE SIMPLEST CASE (Two Periods)
*
*   While beta convergence asks "do poor countries grow faster?",
*   sigma convergence asks a different question:
*     Is the SPREAD of income across countries getting NARROWER?
*
*   We measure the spread using the VARIANCE of log GDP per capita.
*   (Using logs means we measure proportional, not absolute, differences.)
*
*   If Var(ln y) decreases over time → σ-convergence (narrowing)
*   If Var(ln y) increases over time → σ-divergence (spreading out)
*═══════════════════════════════════════════════════════════════════════════════

display as text ""
display as text "────────────────────────────────────────────────────────────────"
display as text "  SECTION 8: SIGMA CONVERGENCE — THE SIMPLEST CASE"
display as text "  Two periods: 1960 and 2019"
display as text "────────────────────────────────────────────────────────────────"

* ── Load the long-format data ──
use "convergence_working.dta", clear

* ── Calculate variance of log GDP per capita in 1960 ──
display as text ""
display as text "--- Cross-country dispersion in 1960 ---"
gen logy = ln(gdppc)
ci variances logy if year == 1960
local var_1960 = r(Var)
local var_1960_lb = r(lb)
local var_1960_ub = r(ub)
local n_1960 = r(N)
local sd_1960 = sqrt(`var_1960')
display as result "  Variance of ln(GDP pc) in 1960 = " %6.4f `var_1960' " [" %6.4f `var_1960_lb' ", " %6.4f `var_1960_ub' "]"
display as result "  Std. Dev. of ln(GDP pc) in 1960 = " %6.4f `sd_1960'
display as result "  N = " `n_1960' " countries"

* ── Calculate variance of log GDP per capita in 2019 ──
display as text ""
display as text "--- Cross-country dispersion in 2019 ---"
ci variances logy if year == 2019
local var_2019 = r(Var)
local var_2019_lb = r(lb)
local var_2019_ub = r(ub)
local n_2019 = r(N)
local sd_2019 = sqrt(`var_2019')
display as result "  Variance of ln(GDP pc) in 2019 = " %6.4f `var_2019' " [" %6.4f `var_2019_lb' ", " %6.4f `var_2019_ub' "]"
display as result "  Std. Dev. of ln(GDP pc) in 2019 = " %6.4f `sd_2019'
display as result "  N = " `n_2019' " countries"

* ── Compare ──
display as text ""
display as text "╔══════════════════════════════════════════════════════════════╗"
display as text "║  Sigma Convergence Test: 1960 vs 2019                      ║"
display as text "╚══════════════════════════════════════════════════════════════╝"
local change = `var_2019' - `var_1960'
local pct_change = (`change' / `var_1960') * 100
display as result "  Change in variance: " %6.4f `change' " (" %5.1f `pct_change' "%)"
if `change' < 0 {
    display as result "  → Variance DECREASED: evidence of σ-convergence"
}
else {
    display as result "  → Variance INCREASED: evidence of σ-DIVERGENCE"
    display as result "    Even though beta convergence may exist, the spread widened!"
}

* ── Bar chart of variance in 1960 vs 2019 ──
preserve
    clear
    set obs 2
    gen year = 1960 in 1
    replace year = 2019 in 2
    gen variance = `var_1960' in 1
    replace variance = `var_2019' in 2
    gen var_lb = `var_1960_lb' in 1
    replace var_lb = `var_2019_lb' in 2
    gen var_ub = `var_1960_ub' in 1
    replace var_ub = `var_2019_ub' in 2

    #delimit ;
    tw (bar variance year if year == 1960, barwidth(8) fcolor("217 119 87") lcolor("217 119 87"%80))
       (bar variance year if year == 2019, barwidth(8) fcolor("106 155 204") lcolor("106 155 204"%80))
       (rcap var_lb var_ub year, lcolor("20 20 19")),
       xlabel(1960 "1960" 2019 "2019")
       ylabel(, angle(horizontal))
       ytitle("Variance of ln(GDP per capita)")
       title("Sigma Convergence: Income Dispersion", color("20 20 19"))
       subtitle("Has the spread of income narrowed?", color("20 20 19") size(small))
       legend(order(1 "1960 (N=`n_1960')" 2 "2019 (N=`n_2019')" 3 "95% CI") pos(2) ring(0)
              region(lcolor(none) fcolor(none)) size(small))
       plotregion(style(none) lcolor(none))
       graphregion(fcolor(white) lcolor(white))
       note("Data: Penn World Tables 10.0. 84-country balanced panel."
            "Variance of log GDP per capita (PPP).", size(vsmall))
       xsize(5) ysize(4)
    ;
    #delimit cr
    graph export "stata_convergence_sigma_two_periods.png", replace width(2400)
    display as text "Figure saved: stata_convergence_sigma_two_periods.png"
restore

drop logy


*═══════════════════════════════════════════════════════════════════════════════
* SECTION 9: THE RELATIONSHIP BETWEEN BETA AND SIGMA CONVERGENCE
*
*   A crucial insight from the convergence literature:
*
*     β-convergence is NECESSARY but NOT SUFFICIENT for σ-convergence.
*                                    (Young, Higgins, and Levy 2008)
*
*   WHY? Think of it like a race:
*     - β-convergence means the slowest runners are speeding up (catch-up)
*     - σ-convergence means the runners are getting closer together
*
*   Even if slower runners speed up ON AVERAGE, random shocks can keep
*   the pack spread out. The catch-up tendency (β) must be strong enough
*   to overcome the dispersing force of shocks.
*═══════════════════════════════════════════════════════════════════════════════

display as text ""
display as text "────────────────────────────────────────────────────────────────"
display as text "  SECTION 9: THE RELATIONSHIP BETWEEN BETA AND SIGMA"
display as text "────────────────────────────────────────────────────────────────"

display as text ""
display as text "  KEY INSIGHT:"
display as text "  β-convergence is NECESSARY but NOT SUFFICIENT for σ-convergence."
display as text ""
display as text "  Think of runners in a race:"
display as text "    β-convergence = the slowest runners speed up (catch-up)"
display as text "    σ-convergence = the pack of runners gets tighter"
display as text ""
display as text "  Even if the slow runners speed up on average, random shocks"
display as text "  (weather, injuries) can keep the pack spread out."

* ── Decade-by-decade β and σ ──
use "convergence_working.dta", clear
keep ccode country year gdppc
reshape wide gdppc, i(ccode country) j(year)

display as text ""
display as text "  EMPIRICAL DEMONSTRATION:"
display as text "  Decade-by-decade OLS λ and variance of log income:"
display as text ""
display as text "  ┌─────────────┬───────────┬───────────┬────────────────────────────┐"
display as text "  │ Decade      │ OLS λ     │ σ² start  │ Interpretation             │"
display as text "  ├─────────────┼───────────┼───────────┼────────────────────────────┤"

foreach decade in 1960 1970 1980 1990 2000 2010 {
    local ey = `decade' + 10
    if `ey' > 2019 local ey = 2019
    local s = `ey' - `decade'

    * Beta (OLS): regress growth on initial income
    capture drop g_temp i_temp logy_temp
    gen g_temp = (1/`s') * ln(gdppc`ey' / gdppc`decade')
    gen i_temp = ln(gdppc`decade')
    qui reg g_temp i_temp if !missing(g_temp) & !missing(i_temp), robust
    local b = _b[i_temp]

    * Sigma: variance of log income at the START of the decade
    gen logy_temp = ln(gdppc`decade')
    qui summarize logy_temp
    local v = r(Var)

    * Determine interpretation
    local interp = ""
    if `b' >= 0 {
        local interp = "λ≥0: divergence"
    }
    else {
        local interp = "λ<0: convergence"
    }

    display as text "  │ `decade'-`ey'  │ " %8.5f `b' " │ " %8.4f `v' "  │ `interp'     │"
    drop g_temp i_temp logy_temp
}

display as text "  └─────────────┴───────────┴───────────┴────────────────────────────┘"
display as text ""
display as text "  NOTICE: λ turns negative (convergence) around 2000, but σ² keeps"
display as text "  RISING until ~2008. This is the lag: β-convergence is necessary"
display as text "  but not sufficient for σ-convergence."


*═══════════════════════════════════════════════════════════════════════════════
* SECTION 10: SIGMA CONVERGENCE OVER TIME
*
*   Track the dispersion of income EVERY YEAR from 1960 to 2019.
*   With our balanced 84-country panel, the sample composition is
*   constant throughout — no need for a separate "fixed sample" series.
*═══════════════════════════════════════════════════════════════════════════════

display as text ""
display as text "────────────────────────────────────────────────────────────────"
display as text "  SECTION 10: SIGMA CONVERGENCE OVER TIME"
display as text "────────────────────────────────────────────────────────────────"

use "convergence_working.dta", clear
keep ccode year gdppc
reshape wide gdppc, i(ccode) j(year)

local j = 1
forval yr = 1960(1)2019 {
    gen logy = ln(gdppc`yr')
    ci variances logy
    preserve
        clear
        set obs 1
        tempfile sigma`j'
        gen year = `yr'
        gen variance = r(Var)
        gen var_lb = `r(lb)'
        gen var_ub = `r(ub)'
        gen n = `r(N)'
        save `sigma`j''
    restore
    drop logy
    local ++j
}

* ── Combine ──
local jminus1 = `j' - 1
clear
forval i = 1/`jminus1' {
    append using `sigma`i''
}

* ── Display key years ──
display as text ""
display as text "╔══════════════════════════════════════════════════════════════╗"
display as text "║  Sigma Convergence Over Time: Key Years                    ║"
display as text "╚══════════════════════════════════════════════════════════════╝"
list year variance n if inlist(year, 1960, 1970, 1980, 1990, 2000, 2008, 2010, 2019), noobs clean

* Export
export delimited using "convergence_sigma_evolution.csv", replace
display as text "Results exported to: convergence_sigma_evolution.csv"

* ── Create sigma convergence time series plot ──
#delimit ;
tw (rcap var_lb var_ub year, lcolor("106 155 204"%20))
   (scatter variance year, mcolor("106 155 204"%60) msymbol(O) msize(vsmall)),
   xlabel(1960(10)2020)
   ylabel(, angle(horizontal))
   ytitle("Variance of ln(GDP per capita)", size(medsmall))
   xtitle("Year")
   title("Sigma Convergence: Income Dispersion Over Time", color("20 20 19") size(medium))
   subtitle("Variance of log GDP per capita, 84-country balanced panel", ///
            color("20 20 19") size(small))
   legend(off)
   plotregion(style(none) lcolor(none))
   graphregion(fcolor(white) lcolor(white))
   note("Data: Penn World Tables 10.0. 84-country balanced panel."
        "Bars: 95% confidence intervals.", size(vsmall))
   xsize(6) ysize(4)
;
#delimit cr
graph export "stata_convergence_sigma_evolution.png", replace width(2400)
display as text "Figure saved: stata_convergence_sigma_evolution.png"


*═══════════════════════════════════════════════════════════════════════════════
* SECTION 11: THE CONVERGENCE HEATMAP — OLS AND NLS
*
*   The most comprehensive view of convergence:
*     For EVERY combination of start year and end year,
*     estimate the β coefficient and color-code it.
*
*   We produce TWO heatmaps:
*     (A) OLS-based: estimate λ, convert to β = -ln(1+λs)/s
*     (B) NLS-based: estimate β directly (reproduces Patel et al. 2021 Fig 2)
*
*   Blue = convergence (β > 0), Red = divergence (β < 0)
*═══════════════════════════════════════════════════════════════════════════════

display as text ""
display as text "────────────────────────────────────────────────────────────────"
display as text "  SECTION 11: THE CONVERGENCE HEATMAP — OLS AND NLS"
display as text "────────────────────────────────────────────────────────────────"

* ── Load and reshape ──
use "convergence_working.dta", clear
keep ccode country year gdppc
reshape wide gdppc, i(ccode country) j(year)

* ── Loop over ALL start/end year combinations ──
local j = 1
local firstyear = 1960
local lastyear = 2019
local endyear_loop = `lastyear' - 1

forval startyear = `firstyear'(1)`endyear_loop' {
    local startplus1 = `startyear' + 1
    forval outcomeyear = `startplus1'(1)`lastyear' {
        local s = `outcomeyear' - `startyear'

        capture drop outcome initial_inc
        gen outcome = (1/`s') * ln(gdppc`outcomeyear' / gdppc`startyear')
        gen initial_inc = ln(gdppc`startyear')

        * ── OLS ──
        local beta_ols_hm = .
        local n_hm = .
        capture qui reg outcome initial_inc if !missing(outcome) & !missing(initial_inc), robust
        if _rc == 0 {
            local lambda_hm = _b[initial_inc]
            local n_hm = e(N)
            local lambda_s_hm = `lambda_hm' * `s'
            if (1 + `lambda_s_hm') > 0 {
                local beta_ols_hm = -ln(1 + `lambda_s_hm') / `s'
            }
        }

        * ── NLS ──
        local beta_nls_hm = .
        capture qui nl (outcome = {b0=1} - (1 - exp(-1*{b1=0.00}*`s'))/`s' * initial_inc) ///
            if !missing(outcome) & !missing(initial_inc), vce(robust)
        if _rc == 0 {
            local beta_nls_hm = _b[/b1]
        }

        preserve
            clear
            set obs 1
            tempfile hm`j'
            gen startyear = `startyear'
            gen endyear = `outcomeyear'
            gen beta_ols = `beta_ols_hm'
            gen beta_nls = `beta_nls_hm'
            gen n = `n_hm'
            save `hm`j''
        restore

        local ++j
    }
    * Progress indicator every 5 years
    if mod(`startyear' - `firstyear', 5) == 0 {
        display as text "  Processing start year: `startyear'..."
    }
}

* ── Combine all heatmap results ──
local jminus1 = `j' - 1
preserve
    clear
    forval i = 1/`jminus1' {
        capture append using `hm`i''
    }
    gen period = endyear - startyear

    * Export
    export delimited using "convergence_heatmap_coefficients.csv", replace
    display as text "Results exported to: convergence_heatmap_coefficients.csv"

    * ── (A) OLS Heatmap ──
    #delimit ;
    tw (scatter startyear endyear if beta_ols > .0035, msize(medsmall)
            mfcolor("5 48 97") mlcolor("20 20 19") mlwidth(vthin))
       (scatter startyear endyear if beta_ols < .0035 & beta_ols > .0025, msize(medsmall)
            mfcolor("33 102 172") mlcolor("20 20 19") mlwidth(vthin))
       (scatter startyear endyear if beta_ols < .0025 & beta_ols > .0015, msize(medsmall)
            mfcolor("67 147 195") mlcolor("20 20 19") mlwidth(vthin))
       (scatter startyear endyear if beta_ols < .0015 & beta_ols > .0005, msize(medsmall)
            mfcolor("146 197 222") mlcolor("20 20 19") mlwidth(vthin))
       (scatter startyear endyear if beta_ols < .0005 & beta_ols > -.0005, msize(medsmall)
            mfcolor("209 229 240") mlcolor("20 20 19") mlwidth(vthin))
       (scatter startyear endyear if beta_ols > -.0015 & beta_ols < -.0005, msize(medsmall)
            mfcolor("247 247 247") mlcolor("20 20 19") mlwidth(vthin))
       (scatter startyear endyear if beta_ols > -.0025 & beta_ols < -.0015, msize(medsmall)
            mfcolor("253 219 199") mlcolor("20 20 19") mlwidth(vthin))
       (scatter startyear endyear if beta_ols > -.0035 & beta_ols < -.0025, msize(medsmall)
            mfcolor("244 165 130") mlcolor("20 20 19") mlwidth(vthin))
       (scatter startyear endyear if beta_ols > -.0045 & beta_ols < -.0035, msize(medsmall)
            mfcolor("214 96 77") mlcolor("20 20 19") mlwidth(vthin))
       (scatter startyear endyear if beta_ols > -.0055 & beta_ols < -.0045, msize(medsmall)
            mfcolor("178 24 43") mlcolor("20 20 19") mlwidth(vthin))
       (scatter startyear endyear if beta_ols < -.0055, msize(medsmall)
            mfcolor("103 0 31") mlcolor("20 20 19") mlwidth(vthin)),
       plotregion(style(none) lcolor(none))
       graphregion(fcolor(white) lcolor(white))
       xtitle("End Year") ytitle("Start Year")
       title("Convergence Heatmap: OLS ({&lambda} {&rarr} {&beta} conversion)", color("20 20 19") size(medium))
       subtitle("Blue = convergence ({&beta}>0), Red = divergence ({&beta}<0)", ///
                color("20 20 19") size(small))
       aspectratio(1)
       xlabel(1960(10)2020) ylabel(1960(10)2010)
       xsize(5) ysize(5)
       legend(order(1 ">.0035" 2 "[.0025,.0035]" 3 "[.0015,.0025]"
                    4 "[.0005,.0015]" 5 "[-.0005,.0005]"
                    6 "[-.0015,-.0005]" 7 "[-.0025,-.0015]"
                    8 "[-.0035,-.0025]" 9 "[-.0045,-.0035]"
                    10 "[-.0055,-.0045]" 11 "<-.0055")
              size(vsmall) pos(10) ring(0) bmargin(medsmall) col(1)
              region(lcolor(none) fcolor(none))
              title("{&beta} coefficient", size(small)))
       note("Data: Penn World Tables 10.0. 84-country balanced panel."
            "OLS {&lambda} converted to structural {&beta} = -ln(1+{&lambda}s)/s.", size(vsmall))
    ;
    #delimit cr
    graph export "stata_convergence_heatmap_ols.png", replace width(2400)
    display as text "Figure saved: stata_convergence_heatmap_ols.png"

    * ── (B) NLS Heatmap ──
    #delimit ;
    tw (scatter startyear endyear if beta_nls > .0035, msize(medsmall)
            mfcolor("5 48 97") mlcolor("20 20 19") mlwidth(vthin))
       (scatter startyear endyear if beta_nls < .0035 & beta_nls > .0025, msize(medsmall)
            mfcolor("33 102 172") mlcolor("20 20 19") mlwidth(vthin))
       (scatter startyear endyear if beta_nls < .0025 & beta_nls > .0015, msize(medsmall)
            mfcolor("67 147 195") mlcolor("20 20 19") mlwidth(vthin))
       (scatter startyear endyear if beta_nls < .0015 & beta_nls > .0005, msize(medsmall)
            mfcolor("146 197 222") mlcolor("20 20 19") mlwidth(vthin))
       (scatter startyear endyear if beta_nls < .0005 & beta_nls > -.0005, msize(medsmall)
            mfcolor("209 229 240") mlcolor("20 20 19") mlwidth(vthin))
       (scatter startyear endyear if beta_nls > -.0015 & beta_nls < -.0005, msize(medsmall)
            mfcolor("247 247 247") mlcolor("20 20 19") mlwidth(vthin))
       (scatter startyear endyear if beta_nls > -.0025 & beta_nls < -.0015, msize(medsmall)
            mfcolor("253 219 199") mlcolor("20 20 19") mlwidth(vthin))
       (scatter startyear endyear if beta_nls > -.0035 & beta_nls < -.0025, msize(medsmall)
            mfcolor("244 165 130") mlcolor("20 20 19") mlwidth(vthin))
       (scatter startyear endyear if beta_nls > -.0045 & beta_nls < -.0035, msize(medsmall)
            mfcolor("214 96 77") mlcolor("20 20 19") mlwidth(vthin))
       (scatter startyear endyear if beta_nls > -.0055 & beta_nls < -.0045, msize(medsmall)
            mfcolor("178 24 43") mlcolor("20 20 19") mlwidth(vthin))
       (scatter startyear endyear if beta_nls < -.0055, msize(medsmall)
            mfcolor("103 0 31") mlcolor("20 20 19") mlwidth(vthin)),
       plotregion(style(none) lcolor(none))
       graphregion(fcolor(white) lcolor(white))
       xtitle("End Year") ytitle("Start Year")
       title("Convergence Heatmap: NLS (Direct Estimation)", color("20 20 19") size(medium))
       subtitle("Blue = convergence ({&beta}>0), Red = divergence ({&beta}<0)", ///
                color("20 20 19") size(small))
       aspectratio(1)
       xlabel(1960(10)2020) ylabel(1960(10)2010)
       xsize(5) ysize(5)
       legend(order(1 ">.0035" 2 "[.0025,.0035]" 3 "[.0015,.0025]"
                    4 "[.0005,.0015]" 5 "[-.0005,.0005]"
                    6 "[-.0015,-.0005]" 7 "[-.0025,-.0015]"
                    8 "[-.0035,-.0025]" 9 "[-.0045,-.0035]"
                    10 "[-.0055,-.0045]" 11 "<-.0055")
              size(vsmall) pos(10) ring(0) bmargin(medsmall) col(1)
              region(lcolor(none) fcolor(none))
              title("{&beta} coefficient", size(small)))
       note("Data: Penn World Tables 10.0. 84-country balanced panel."
            "NLS estimation (Barro & Sala-i-Martin 1992 specification).", size(vsmall))
    ;
    #delimit cr
    graph export "stata_convergence_heatmap_nls.png", replace width(2400)
    display as text "Figure saved: stata_convergence_heatmap_nls.png"

restore


*═══════════════════════════════════════════════════════════════════════════════
* CLEAN UP AND FINAL SUMMARY
*═══════════════════════════════════════════════════════════════════════════════

display as text ""
display as text "╔══════════════════════════════════════════════════════════════╗"
display as text "║  TUTORIAL SUMMARY                                          ║"
display as text "╠══════════════════════════════════════════════════════════════╣"
display as text "║                                                            ║"
display as text "║  Sample: 84-country balanced panel (data since 1960)       ║"
display as text "║                                                            ║"
display as text "║  Beta Convergence (β):                                     ║"
display as text "║    - 1960-2000: NO convergence (era of divergence)         ║"
display as text "║    - 2000-2019: YES convergence (new era)                  ║"
display as text "║    - OLS (λ→β) and NLS give nearly identical estimates     ║"
display as text "║    - Speed ≈ 0.4% per year (much slower than 2% benchmark) ║"
display as text "║    - Half-life ≈ 170 years                                 ║"
display as text "║                                                            ║"
display as text "║  Sigma Convergence (σ):                                    ║"
display as text "║    - 1960-2008: variance rose (σ-DIVERGENCE)               ║"
display as text "║    - Post-2008: variance started declining (σ-convergence) ║"
display as text "║    - β is necessary but NOT sufficient for σ               ║"
display as text "║                                                            ║"
display as text "║  Key Reference:                                            ║"
display as text "║    Patel, Sandefur, Subramanian (2021)                     ║"
display as text "║    'The New Era of Unconditional Convergence'              ║"
display as text "║    Journal of Development Economics                         ║"
display as text "╚══════════════════════════════════════════════════════════════╝"

* ── Clean up temporary files ──
capture erase "convergence_working.dta"

display as text ""
display as text "Script completed successfully."
display as text "Figures generated:"
display as text "  stata_convergence_scatter_1960_2019.png"
display as text "  stata_convergence_scatter_two_eras.png"
display as text "  stata_convergence_speed_ols.png"
display as text "  stata_convergence_speed_nls.png"
display as text "  stata_convergence_rolling_lambda.png"
display as text "  stata_convergence_rolling_beta_ols.png"
display as text "  stata_convergence_rolling_beta_nls.png"
display as text "  stata_convergence_sigma_two_periods.png"
display as text "  stata_convergence_sigma_evolution.png"
display as text "  stata_convergence_heatmap_ols.png"
display as text "  stata_convergence_heatmap_nls.png"
display as text ""

log close
exit