program define tabsort
*! NJC 1.1.0 27 July 1998
    version 5.0
    local varlist "req max(2)"
    local if "opt"
    local in "opt"
    local weight "fweight aweight iweight"
    local options "Missing noRSort noCSort noLAbel Generate(str)"
    local options "`options' Summari(str) SOrt(str) REVerse *"
    parse "`*'"
    parse "`varlist'", parse(" ")

    local so = substr("`sort'",1,1)
    if "`sort'" == "" | "`so'" == "f" | "`so'" == "c" {
        local sort "freq"
        local so "f"
    }

    if "`summari'" != "" {
        unabbrev `summari', max(1)
        local summari "$S_1"
        if "`sort'" != "freq" {
            if "`so'" == "m" { local sort "mean" }
            else if "`so'" == "s" { local sort "sd" }
            else {
                di in r "sort( ) should be freq, mean or sd"
                exit 198
            }
        }
        local summ "s(`summari')"
    }
    else {
        if "`so'" != "f" {
            di in r "sort(`sort') not allowed without summarize( )"
            exit 198
        }
    }

    tempvar touse w W stat group1 group2

    mark `touse' `if' `in'
    if "`missing'" == "" { markout `touse' `varlist', strok }

    qui {
        if "`exp'" == "" { gen `w' = 1 }
        else { /* do this now, just in case expression depends on _n */
            gen `w' `exp'
            markout `touse' `w'
            local exp "= `w'"
        }
        gen `W' = .
        gen double `stat' = .
    }

    * sort on row stats, or carry row variable as is
    if "`rsort'" == "" { local slist "`1'" }
    else local tlist "`1'"

    * sort on col stats, or carry col variable as is
    if "`csort'" == "" { local slist "`slist' `2'" }
    else local ulist "`2'"

    local nsort : word count `slist'
    local k = 1

    qui while `k' <= `nsort' {
        local var`k' : word `k' of `slist'
        local type : type `var`k''
        local numvar = substr("`type'",1,3) != "str"
        local fmt : format `var`k''
        local vallab : value label `var`k''

        sort `touse' `var`k''
        if "`sort'" == "freq" {
            by `touse' `var`k'' : replace `stat' = sum(`w') if `touse'
            by `touse' `var`k'' : replace `stat' = `stat'[_N]
        }
        else { /* mean or sd */
            by `touse' `var`k'' : replace `W' = sum(`w') / sum(1) /*
             */ if `touse'
            by `touse' `var`k'' : replace `W' = `w' / `W'[_N]
            by `touse' `var`k'' : replace `stat' = /*
             */ sum(`W' * `summari') / sum(1) if `touse'
            by `touse' `var`k'' : replace `stat' = `stat'[_N]
        }
        if "`sort'" == "sd" {
    		by `touse' `var`k'' : replace `stat' = /*
        	 */ sqrt(sum(`W' * (`summari' - `stat')^2) / (sum(1) - 1))
    		by `touse' `var`k'' : replace `stat' = `stat'[_N]
        }
        if "`reverse'" == "" { replace `stat' = -`stat' }
        sort `stat' `var`k''
        by `stat' `var`k'' : gen int `group`k'' = _n == 1 if `touse'
        replace `group`k'' = sum(`group`k'')

        local ng = `group`k''[_N]
        local i = 1
        local j = 1

        while `i' <= `ng' {
            local lbl = `var`k''[`j']
            if `numvar' {
                local big = `lbl' >= 1e8 | `lbl' <= -1e7
                local small = `lbl' < 1 & `lbl' > 0
                local small = `small' | (`lbl' > -1 & `lbl' < 0)
                local nonint = `lbl' != int(`lbl')
                if `big' | `small' { local lbl : di %7.0e `lbl' }
                else if `nonint' { local lbl : di `fmt' `lbl' }
                if "`vallab'" != "" { local lbl : label `vallab' `lbl' }
            }
            else { /* see notes at end */
                if "`lbl'" == "" {
                    local len = length(`var`k''[`j'])
                    if `len' == 0 { local lbl "missing" }
                    else local lbl : di "|" _dup(`len') " " "|"
                }
            }
            label define `group`k'' `i' "`lbl'", add
            count if `group`k'' == `i'
            local j = `j' + _result(1)
            local i = `i' + 1
        }

        label val `group`k'' `group`k''
        _crcslbl `group`k'' `var`k''
        local tlist "`tlist' `group`k''"
        local k = `k' + 1
    }

    tabulate `tlist' `ulist' [`weight' `exp'] if `touse', /*
     */ `summ' `label' `options'

    if "`label'" == "nolabel" & "`slist'" != "" {
        local slist = trim("`slist'")
        if `nsort' == 2 { local s "s" }
        di in bl /*
         */ "Note: labels of `slist' are not those of original variable`s'"
    }

    if "`generat'" != "" { qui tab `varlist', gen(`generat') }

    capture label drop `group1'
    capture label drop `group2'
end

/*

    Spaces in string values:

     get lost in copying -- local lbl = `var`k''[`j']
     must put them back again!

    Missing string values:

     label def <lbl> <val> "", add  -- will not work
     label def <lbl> <val> " ", add -- would work, but then missings and
                                       blanks would give the same result


*/