*! autofmt 1.0.0 01oct2009 roywada@hotmail.com
*! automatic formating of a significant number of digits

program define autofmt, rclass
version 7.0

syntax, input(string) [dec(integer 3) less(integer 0) parse(string) strict]
* parse( ) takes only one character; " " is always included as a parse

if `"`parse'"'=="" {
        local parse " "
}

local rest `"`input'"'
local count 0

if "`rest'"~="" {
        * handles the possibility the first token is empty
        gettoken first rest: rest, parse("`parse'")
        local first=trim(`"`first'"')
        if `"`first'"'==`"`parse'"' {
                local count=`count'+1
                local input`count' ""
        }
        else {
                local count=`count'+1
                local input`count' `"`first'"'
        }
}
while "`rest'"~="" {
        gettoken first rest: rest, parse("`parse'")
        local first=trim(`"`first'"')
        if `"`first'"'~=`"`parse'"' {
                local count=`count'+1
                local input`count' `"`first'"'
        }
}

if `count'==0 {
        * input( ) was left empty
        exit
}

if "`strict'"=="strict" {
	local one 0
}
else {
	local one 1
}


*** run as many times

forval num=1/`count' {

        * confirm a number
        capture confirm number `input`num''
        local rc=_rc

        * run if not missing and is a number
        if "`input`num''"~="." & "`input`num''"~="" & `rc'==0 {
                local times=0
                local left=0

                * integer checked by modified mod function
                if round((`input`num'' - int(`input`num'')),0.0000000001)==0 {
                        local whole=1
                }
                else {
                        local whole=0
                        * non-interger
                         if `input`num''<. {

                                * digits that need to be moved if it were only decimals: take the ceiling of log 10 of absolute value of decimals
                                local times=abs(int(ln(abs(`input`num''-int(`input`num'')))/ln(10)-1))

                                * the whole number: take the ceiling of log 10 of absolute value
                                local left=int(ln(abs(`input`num''))/ln(10)+1)
                        }
                }


                * assign the fixed decimal values into aadec
                if `whole'==1 {
                        local aadec=0
                }
                else if .>`left' & `left'>0 {
                        * reduce the left by one if more than zero to accept one extra digit
                        if `left'<=`dec' {
                                local aadec=`dec'-`left'+`one'
                        }
                        else {
                                local aadec=0
                        }
                }
                else {
                        local aadec=`times'+`dec'-1
                }

                if "`less'"=="" {
                        * needs to between 0 and 11
                        if `aadec'<0 {
                                local aadec=0
                        }
                        *if `aadec'<11 {
                        if `aadec'<7 {
                                * use fixed
                                local fmt "`aadec'f"
                        }
                        else {
                                * use exponential
                                local fmt "`=`dec'-1'e"
                        }
                }
                else {
                        * needs to between 0 and 11
                        local aadec=`aadec'-`less'
                        if `aadec'<0 {
                                local aadec=0
                        }
                        *if `aadec'<10 {
                        if `aadec'<7 {
                                * use fixed
                                local fmt "`aadec'f"
                        }
                        else {
                                * use exponential
                                local fmt "`=`dec'-1'e"
                        }
                }

                * make it exponential if too big
                if `input`num''>1000000 & `input`num''<. {
                        local fmt "`=`dec'-0'e"
                }

                * make it exponential if too negative (small)
                if `input`num''<-1000000 & `input`num''<. {
                        local fmt "`=`dec'-0'e"
                }

                local fmt`num' `fmt'
                local aadec`num' `aadec'

                local output`num'=string(`input`num'',"%12.`fmt'")

                return scalar deci`num'=`aadec'
                return local fmt`num'="`fmt'"
                return local input`num'="`input`num''"

                return local output`num'=`"`output`num''"'

        }
        else {
                * it is a missing value, empty, or non-number
                local output`num'=trim(`"`input`num''"')

                return scalar deci`num'=.
                return local fmt`num'="."
                if "`input`num''"=="" {
                        * return a dot when empty
                        return local input`num'="."
                }
                else {
                        return local input`num'="`input`num''"
                }

                return local output`num'=`"`output`num''"'

        }
}
end
exit