*! version 1.1.0  19jul2026
program define recode12, rclass
    version 19.5
    syntax [varlist(default=none)] [, YESValue(string) SUFfix(name) REPlace DISPlay]

    if `"`yesvalue'"' == "" {
        di as err "yesvalue() is required; specify yesvalue(1) or yesvalue(2)"
        exit 198
    }
    if !inlist(`"`yesvalue'"', "1", "2") {
        di as err "yesvalue() must be 1 or 2"
        exit 198
    }

    local suffix_given = (`"`suffix'"' != "")
    if `"`replace'"' != "" & `suffix_given' {
        di as err "suffix() may not be combined with replace"
        exit 198
    }
    if `"`suffix'"' == "" local suffix "_01"

    if `"`varlist'"' == "" {
        quietly ds
        local varlist `r(varlist)'
    }
    if `"`varlist'"' == "" {
        di as txt "no variables found"
        return local skipped ""
        return local source ""
        return local numeric_source ""
        return local string_source ""
        return local numeric_recoded ""
        return local string_recoded ""
        return local recoded ""
        return local value_label ""
        return local status_variable ""
        return scalar yesvalue = `yesvalue'
        return scalar verified = 0
        return scalar n_numeric_recoded = 0
        return scalar n_string_recoded = 0
        return scalar n_recoded = 0
        exit
    }

    local eligible
    local numeric_eligible
    local string_eligible
    local skipped
    quietly foreach v of local varlist {
        capture confirm numeric variable `v'
        if !_rc {
            count if !inlist(`v', 1, 2, .)
            local bad = r(N)
            count if `v' == 1
            local n1 = r(N)
            count if `v' == 2
            local n2 = r(N)
            if (`bad' == 0 & `n1' > 0 & `n2' > 0) {
                local eligible `eligible' `v'
                local numeric_eligible `numeric_eligible' `v'
            }
            else local skipped `skipped' `v'
        }
        else {
            tempvar normalized sourcecode obsno
            generate strL `normalized' = ustrtrim(`v')
            generate long `obsno' = _n
            summarize `obsno' if !inlist(`normalized', "", "."), meanonly
            if r(N) == 0 {
                local skipped `skipped' `v'
                continue
            }
            local first1 = r(min)
            generate byte `sourcecode' = .
            replace `sourcecode' = 1 if `normalized' == `normalized'[`first1'] & !inlist(`normalized', "", ".")
            summarize `obsno' if !inlist(`normalized', "", ".") & missing(`sourcecode'), meanonly
            if r(N) == 0 {
                local skipped `skipped' `v'
                continue
            }
            local first2 = r(min)
            replace `sourcecode' = 2 if `normalized' == `normalized'[`first2'] & !inlist(`normalized', "", ".")
            count if !inlist(`normalized', "", ".") & missing(`sourcecode')
            if r(N) == 0 {
                local eligible `eligible' `v'
                local string_eligible `string_eligible' `v'
            }
            else local skipped `skipped' `v'
        }
    }

    if `"`eligible'"' == "" {
        di as txt "no variables met the two-category coding rule"
        return local skipped `"`skipped'"'
        return local source ""
        return local numeric_source ""
        return local string_source ""
        return local numeric_recoded ""
        return local string_recoded ""
        return local recoded ""
        return local value_label ""
        return local status_variable ""
        return scalar yesvalue = `yesvalue'
        return scalar verified = 0
        return scalar n_numeric_recoded = 0
        return scalar n_string_recoded = 0
        return scalar n_recoded = 0
        exit
    }

    local statusvar "recode12_status"
    capture confirm variable `statusvar'
    if !_rc {
        local statuslabel : variable label `statusvar'
        local statustype : type `statusvar'
        if substr("`statustype'", 1, 3) != "str" | ///
            !inlist(`"`statuslabel'"', "recode12 verification status", ///
                "recode12 Verification Status") {
            di as err "variable `statusvar' already exists and was not created by recode12"
            exit 110
        }
    }

    if `"`replace'"' == "" {
        foreach v of local eligible {
            local new `v'`suffix'
            confirm new variable `new'
        }
    }

    local vallab "recode12_NoYes"
    capture quietly label list `vallab'
    if _rc label define `vallab' 0 "No" 1 "Yes"
    else {
        local lab0 : label `vallab' 0
        local lab1 : label `vallab' 1
        if `"`lab0'"' != "No" | `"`lab1'"' != "Yes" {
            di as err "value label `vallab' already exists with incompatible definitions"
            exit 110
        }
    }

    if `yesvalue' == 1 {
        di as txt "mapping rule: source category 1 -> 1 (Yes); source category 2 -> 0 (No)"
    }
    else {
        di as txt "mapping rule: source category 1 -> 0 (No); source category 2 -> 1 (Yes)"
    }

    local recoded
    local numeric_recoded
    local string_recoded
    foreach v of local eligible {
        capture confirm numeric variable `v'
        if !_rc {
            local source_vallab : value label `v'
            local cat1
            local cat2
            if `"`source_vallab'"' != "" {
                local cat1 : label `source_vallab' 1
                local cat2 : label `source_vallab' 2
            }
            local target
            if `"`source_vallab'"' != "" {
                if `yesvalue' == 1 local target `"`cat1'"'
                else local target `"`cat2'"'
            }
            if `"`target'"' == "" local target "`v' == `yesvalue'"
            local target : subinstr local target `"' "'", all
            local newvl `"Recoded `target' (0=No; 1=Yes)"'
            local newvl = ustrleft(`"`newvl'"', 80)

            if `"`replace'"' != "" {
                tempvar original
                quietly clonevar `original' = `v'
                quietly replace `v' = (`original' == `yesvalue') if !missing(`original')
                label values `v' `vallab'
                label variable `v' `"`newvl'"'
                assert `v' == (`original' == `yesvalue') if !missing(`original')
                assert missing(`v') if missing(`original')
                assert inlist(`v', 0, 1) | missing(`v')
                local recoded `recoded' `v'
                local numeric_recoded `numeric_recoded' `v'
            }
            else {
                local new `v'`suffix'
                quietly generate byte `new' = (`v' == `yesvalue') if !missing(`v')
                label variable `new' `"`newvl'"'
                label values `new' `vallab'
                assert `new' == (`v' == `yesvalue') if !missing(`v')
                assert missing(`new') if missing(`v')
                assert inlist(`new', 0, 1) | missing(`new')
                local recoded `recoded' `new'
                local numeric_recoded `numeric_recoded' `new'
            }
        }
        else {
            tempvar normalized sourcecode obsno
            quietly generate strL `normalized' = ustrtrim(`v')
            quietly generate long `obsno' = _n
            quietly summarize `obsno' if !inlist(`normalized', "", "."), meanonly
            local first1 = r(min)
            quietly generate byte `sourcecode' = .
            quietly replace `sourcecode' = 1 if `normalized' == `normalized'[`first1'] & !inlist(`normalized', "", ".")
            quietly summarize `obsno' if !inlist(`normalized', "", ".") & missing(`sourcecode'), meanonly
            local first2 = r(min)
            quietly replace `sourcecode' = 2 if `normalized' == `normalized'[`first2'] & !inlist(`normalized', "", ".")

            local cat1 = `normalized'[`first1']
            local cat2 = `normalized'[`first2']
            local target `"`cat`yesvalue''"'
            local target : subinstr local target `"' "'", all
            local newvl `"Recoded `target' (0=No; 1=Yes)"'
            local newvl = ustrleft(`"`newvl'"', 80)
            if `"`replace'"' != "" {
                tempvar original newvalue
                quietly clonevar `original' = `v'
                quietly generate byte `newvalue' = (`sourcecode' == `yesvalue') if !missing(`sourcecode')
                quietly order `newvalue', before(`v')
                quietly drop `v'
                quietly rename `newvalue' `v'
                label variable `v' `"`newvl'"'
                label values `v' `vallab'
                assert `v' == (`sourcecode' == `yesvalue') if !missing(`sourcecode')
                assert missing(`v') if missing(`sourcecode')
                assert inlist(`v', 0, 1) | missing(`v')
                local recoded `recoded' `v'
                local string_recoded `string_recoded' `v'
            }
            else {
                local new `v'`suffix'
                quietly generate byte `new' = (`sourcecode' == `yesvalue') if !missing(`sourcecode')
                label variable `new' `"`newvl'"'
                label values `new' `vallab'
                assert `new' == (`sourcecode' == `yesvalue') if !missing(`sourcecode')
                assert missing(`new') if missing(`sourcecode')
                assert inlist(`new', 0, 1) | missing(`new')
                local recoded `recoded' `new'
                local string_recoded `string_recoded' `new'
            }
        }
    }

    capture confirm variable `statusvar'
    if _rc generate str9 `statusvar' = "confirmed"
    else quietly replace `statusvar' = "confirmed"
    label variable `statusvar' "recode12 Verification Status"

    local n_recoded : word count `recoded'
    local n_numeric_recoded : word count `numeric_recoded'
    local n_string_recoded : word count `string_recoded'
    if `"`display'"' != "" {
        di as txt "number of numeric variables standardized: " ///
            as result `n_numeric_recoded'
        if `n_numeric_recoded' > 0 {
            di as txt "names of numeric variables standardized:"
            local detail_line
            local detail_count = 0
            local detail_width = max(20, c(linesize) - 4)
            foreach name of local numeric_recoded {
                local candidate = strtrim("`detail_line' `name'")
                local candidate_length = strlen("`candidate'")
                if `detail_count' > 0 & ///
                    (`detail_count' >= 7 | `candidate_length' > `detail_width') {
                    di as result "    `detail_line'"
                    local detail_line "`name'"
                    local detail_count = 1
                }
                else {
                    local detail_line "`candidate'"
                    local ++detail_count
                }
            }
            if `"`detail_line'"' != "" di as result "    `detail_line'"
        }
        else di as txt "names of numeric variables standardized: " as result "none"

        di as txt "number of string variables standardized: " ///
            as result `n_string_recoded'
        if `n_string_recoded' > 0 {
            di as txt "names of string variables standardized:"
            local detail_line
            local detail_count = 0
            local detail_width = max(20, c(linesize) - 4)
            foreach name of local string_recoded {
                local candidate = strtrim("`detail_line' `name'")
                local candidate_length = strlen("`candidate'")
                if `detail_count' > 0 & ///
                    (`detail_count' >= 7 | `candidate_length' > `detail_width') {
                    di as result "    `detail_line'"
                    local detail_line "`name'"
                    local detail_count = 1
                }
                else {
                    local detail_line "`candidate'"
                    local ++detail_count
                }
            }
            if `"`detail_line'"' != "" di as result "    `detail_line'"
        }
        else di as txt "names of string variables standardized: " as result "none"
    }
    di as txt "verification passed: all recoded values match the selected mapping rule"
    return local value_label "`vallab'"
    return local status_variable "`statusvar'"
    return scalar yesvalue = `yesvalue'
    return scalar verified = 1
    return local skipped `"`skipped'"'
    return local source `"`eligible'"'
    return local numeric_source `"`numeric_eligible'"'
    return local string_source `"`string_eligible'"'
    return local numeric_recoded `"`numeric_recoded'"'
    return local string_recoded `"`string_recoded'"'
    return local recoded `"`recoded'"'
    return scalar n_numeric_recoded = `n_numeric_recoded'
    return scalar n_string_recoded = `n_string_recoded'
    return scalar n_recoded = `n_recoded'
end