*! jnsni.ado Version 1.2 2007-01-17 JRC
program define jnsni
    version 9.2
    syntax , mean(real) sd(real) [SKEWness(real 0) kurtosis(real 3) called(integer 0) ///
      TOLerance(real 0.01) SBITERate(integer 50) ///
      MOITERate(integer 50) MOTOLerance(real 0.00001) MIITERate(integer 50) MITOLerance(real 0.00000001)]

    tempname fault skewness_squared r_w r_x r_kurtosis

    if (`sd' < 0) {
        jnsn_NULL , why("sd")
        display in smcl as error "`r(fault)'"
        exit = 99
    }
    scalar define `skewness_squared' = `skewness' * `skewness'
    if (`kurtosis' < (`skewness_squared' + 1)) {
        jnsn_NULL , why("ps")
        display in smcl as error "`r(fault)'"
        exit = 99
    }

    scalar define `fault' = "Program terminated normally"

// All values are the same?
    if (`sd' == 0) {
        jnsn_STabbreviated , mean(`mean') // If so, declare Johnson ST distribution (binomial)
        jnsn_reporter, called(`called')
        scalar drop `fault' `skewness_squared'
        exit
    }

// Johnson ST distribution (binomial)?
    if (`kurtosis' <= (`skewness_squared' + `tolerance' + 1)) {
        jnsn_ST , mean(`mean') sd(`sd') skewness(`skewness') fault(`fault')
        jnsn_reporter, called(`called')
        scalar drop `fault' `skewness_squared'
        exit
    }

// Johnson SN distribution (normal)?
    if ( (abs(`skewness') <= `tolerance') & (abs(`kurtosis' - 3) <= `tolerance') ) {
        jnsn_SN , mean(`mean') sd(`sd') fault(`fault')
        jnsn_reporter, called(`called')
        scalar drop `fault' `skewness_squared'
        exit
    }

// Testing for position relative to lognormal line
    jnsn_SLline , skewness(`skewness') kurtosis(`kurtosis') fault(`fault')
    scalar define `r_x' = r(x)  // In order to maintain precision
    scalar define `r_w' = r(w)
    scalar define `r_kurtosis' = r(kurtosis)

// Johnson SL distribution family (lognormal)?
    if (abs(`r_x') <= `tolerance') {
        jnsn_SL , mean(`mean') sd(`sd') skewness(`skewness') w(`r_w') fault(`fault')
        jnsn_reporter, called(`called')
        scalar drop `fault' `skewness_squared' `r_x' `r_w' `r_kurtosis'
        exit
    }

// Johnson SU distribution family (unbounded)?
    if (`r_x' <= 0) {
        jnsn_SU , mean(`mean') sd(`sd') skewness(`skewness') kurtosis(`r_kurtosis') tolerance(`tolerance')
        jnsn_reporter, called(`called')
        scalar drop `fault' `skewness_squared' `r_x' `r_w' `r_kurtosis'
        exit
    }

// Else it must be the Johnson SB distribution family (bounded)
    jnsn_SB ,  mean(`mean') sd(`sd') skewness(`skewness') kurtosis(`kurtosis') ///
      tolerance(`tolerance') iterate(`sbiterate') fault(`fault') ///
      moiterate(`moiterate') motolerance(`motolerance') miiterate(`miiterate') mitolerance(`mitolerance')
    if `fault' != "Program terminated normally" { // Fail.  And so must approximate the distribution as well as possible
        if (`kurtosis' > (`skewness_squared' + 2)) {
            if (abs(`skewness') < `tolerance') { // Declare it normal
                jnsn_SN , mean(`mean') sd(`sd') fault(`fault')
            }
            else { // Declare it log-normal; get w for jnsn_SL to use
                jnsn_SLline , skewness(`skewness') kurtosis(`kurtosis') fault(`fault')
                assert r(x) == 0 // Consequence of fault != "Program terminated normally" from jnsn_SB
                scalar define `r_w' = r(w)
                jnsn_SL , mean(`mean') sd(`sd') skewness(`skewness') w(`r_w') fault(`fault')
            }
        }
        else { // Declare it Johnson ST
            jnsn_ST , mean(`mean') sd(`sd') skewness(`skewness') fault(`fault')
        }
    }
    jnsn_reporter, called(`called')
    scalar drop `fault' `skewness_squared' `r_x' `r_w' `r_kurtosis'
end
*
program define jnsn_NULL, rclass
    version 9.2
    syntax , why(string)
    return local johnson_type = ""
    return scalar gamma = .
    return scalar delta = .
    return scalar xi = .
    return scalar lambda = .
    assert inlist("`why'", "sd", "ps")
    if "`why'" == "sd" {
        return local fault = "SD is negative"
    }
    else {
        return local fault = "Kurtosis is less than skewness squared + 1"
    }
end
*
program define jnsn_reporter
    version 9.2
    syntax , called(integer)
    if (!`called') {
        display in smcl as text "Johnson's system of transformations"
    }
    display in smcl _newline(1)
    display in smcl as text "Johnson distribution type: " as result "`r(johnson_type)'"
    display in smcl as text " gamma = " as result %05.3f r(gamma)
    display in smcl as text " delta = " as result %05.3f r(delta)
    display in smcl as text "    xi = " as result %05.3f r(xi)
    display in smcl as text "lambda = " as result %05.3f r(lambda)
    display in smcl _newline(1)
    if "`r(fault)'" != "Program terminated normally" {
        display in smcl as text "Note:  `r(fault)'"
        display in smcl as text "       `r(johnson_type)' is a fall-back approximation to SB type"
    }
    else {
        display in smcl as text "Note: `r(fault)'"
    }
end
*
program define jnsn_STabbreviated, rclass
    version 9.2
    syntax , mean(real)
    return local johnson_type ST
    return scalar gamma = 0
    return scalar delta = 0
    return scalar xi = `mean'
    return scalar lambda = 0
    return local fault = "Program terminated normally"
end
*
program define jnsn_ST, rclass
    version 9.2
    syntax , mean(real) sd(real) skewness(real) fault(name)
    tempname x
    return local johnson_type ST
    return scalar gamma = 0
    return scalar delta = 0.5 + 0.5 * sqrt(1 - 4 / (`skewness' * `skewness' + 4))
    if (`skewness' > 0) return scalar delta = 1 - return(delta)
    scalar define `x' = `sd' / sqrt(return(delta) * (1 - return(delta)))
    return scalar xi = `mean' - return(delta) * scalar(`x')
    return scalar lambda = return(xi) + scalar(`x')
    return local fault = scalar(`fault')
    scalar drop `x'
end
*
program define jnsn_SN, rclass
    version 9.2
    syntax , mean(real) sd(real) fault(name)
    return local johnson_type SN
    return scalar gamma = -`mean' / `sd'
    return scalar delta = 1 / `sd'
    return scalar xi = 0
    return scalar lambda = 1
    return local fault = scalar(`fault')
end
*
program define jnsn_SL, rclass
    version 9.2
    syntax , mean(real) sd(real) skewness(real) w(name) fault(name)
    tempname u x y
    return local johnson_type SL
    local s = sign(`skewness')
    scalar define `u' = `s' * `mean'
    scalar define `x' = 1 / sqrt(ln(`w'))
    scalar define `y' = 0.5 * `x' * ln(`w' * (`w' - 1) / (`sd' * `sd'))
    return scalar gamma = `y'
    return scalar delta = `x'
    return scalar xi = `s' * (`u' - exp((0.5 / `x' - `y') / `x'))
    return scalar lambda = `s'
    return local fault = scalar(`fault')
    macro drop _s
    scalar drop `u' `x' `y'
end
*
program define jnsn_SLline, rclass
    version 9.2
    syntax , skewness(real) kurtosis(real) fault(name)
    tempname x y u
    scalar define `x' = 0.5 * `skewness' * `skewness' + 1
    scalar define `y' = abs(`skewness') * sqrt(0.25 * `skewness' * `skewness' + 1)
    scalar define `u' = (`x' + `y') ^ (1 / 3)
    return scalar w = `u' + 1 / `u' - 1
    scalar define `u' = return(w) * return(w) * (3 + return(w) * (2 + return(w))) - 3
    return scalar kurtosis = cond(((`kurtosis' < 0) | (`fault' != "Program terminated normally")), `u', `kurtosis')
    return scalar x = `u' - return(kurtosis)
    scalar drop `x' `y' `u'
end
*
program define jnsn_SU, rclass
    version 9.2
    syntax , mean(real) sd(real) skewness(real) kurtosis(name) tolerance(real)

    tempname skewness_squared kurtosis_minus_3 w y z w1 wm1 v a b x

    scalar define `skewness_squared' = `skewness' * `skewness'
    scalar define `kurtosis_minus_3' = `kurtosis' - 3
    scalar define `w' = sqrt(sqrt(2 * `kurtosis' - 2.8 * `skewness_squared' - 2) - 1)

    scalar define `w1' = .  // In order to avoid exception with symmetric distribution
    scalar define `wm1' = . // when dropping scalars at end of this subroutine
    scalar define `v' = .
    scalar define `a' = .
    scalar define `b' = .

    if (abs(`skewness') <= `tolerance') scalar define `y' = 0 // Symmetrical distribution
    else { // Johnson iteration (using y for his m)
        local flag 1
        scalar define `z' = 0
        while ((abs(`skewness_squared' - `z') > `tolerance') | (`flag' == 1)) {
            local flag 0
            scalar define `w1' = `w' + 1
            scalar define `wm1' = `w' - 1
            scalar define `z' = `w1' * `kurtosis_minus_3'
            scalar define `v' = `w' * (6 + `w' * (3 + `w'))
            scalar define `a' = 8 * (`wm1' * (3 + `w' * (7 + `v')) - `z')
            scalar define `b' = 16 * (`wm1' * (6 + `v') - `kurtosis_minus_3')
            scalar define `y' = ///
              (sqrt(`a' * `a' - 2 * `b' * (`wm1' * (3 + `w' * (9 + `w' * (10 + `v'))) - 2 * `w1' * `z')) - `a') / `b'
            scalar define `z' = `y' * `wm1' * (4 * (`w' + 2) * `y' + 3 * `w1' * `w1') ^ 2 / (2 * (2 * `y' + `w1') ^ 3)
            scalar define `v' = `w' * `w'
            scalar define `w' = ///
              sqrt(1 - 2 * (1.5 - `kurtosis' + (`skewness_squared' * (`kurtosis' - 1.5 - `v' * (1 + 0.5 * `v'))) / `z'))
            scalar define `w' = sqrt(`w' - 1)
        }
        scalar define `y' = `y' / `w'
        scalar define `y' = ln(sqrt(`y') + sqrt(`y' + 1))
        if (`skewness' > 0) scalar define `y' = -(`y')
    }
    scalar define `x' = sqrt(1 / ln(`w'))
    return local johnson_type SU
    return scalar gamma = `y' * `x'
    return scalar delta = `x'
    scalar define `y' = exp(`y')
    scalar define `z' = `y' * `y'
    scalar define `x' = `sd' / sqrt(0.5 * (`w' - 1) * (0.5 * `w' * (`z' + 1 / `z') + 1))
    return scalar xi = (0.5 * sqrt(`w') * (`y' - 1 / `y')) * `x' + `mean'
    return scalar lambda = `x'
    return local fault = "Program terminated normally"
    macro drop _flag
    scalar drop `skewness_squared' `kurtosis_minus_3' `w' `y' `z' `w1' `wm1' `v' `a' `b'
end
*
program define jnsn_SB, rclass
    version 9.2
    syntax , mean(real) sd(real) skewness(real) kurtosis(real) tolerance(real) iterate(integer) ///
      moiterate(integer) motolerance(real) miiterate(integer) mitolerance(real) fault(name)
*
    tempname hmu dd deriv tt skewness_squared e x y u w f d g s t h2 h2a h2b h3 rbet h4 bet2 xlam

    matrix define `hmu' = J(1, 6, 0)
    matrix define `dd' = J(1, 4, 0)
    matrix define `deriv' = J(1, 4, 0)

    scalar define `tt' = `tolerance' * `tolerance'
*

    local  a1 = 0.0124
    local  a2 = 0.0623
    local  a3 = 0.4043
    local  a4 = 0.408
    local  a5 = 0.479
    local  a6 = 0.485
    local  a7 = 0.5291
    local  a8 = 0.5955
    local  a9 = 0.626
    local a10 = 0.64
    local a11 = 0.7077
    local a12 = 0.7466
    local a13 = 0.8
    local a14 = 0.9281
    local a15 = 1.0614
    local a16 = 1.25
    local a17 = 1.7973
    local a18 = 1.8
    local a19 = 2.163
    local a20 = 2.5
    local a21 = 8.5245
    local a22 = 11.346

*
    scalar define `skewness_squared' = `skewness' * `skewness'
    local negative = (`skewness' < 0)
*
// Get d as the first estimate of delta
    scalar define `e' = `skewness_squared' + 1
    scalar define `x' = 0.5 * `skewness_squared' + 1
    scalar define `y' = abs(`skewness') * sqrt(0.25 * `skewness_squared' + 1)
/*    scalar define `u' = (`x' + `y') ^ (1/3)
    scalar define `w' = `u' + 1 / `u' - 1
These two lines are replaced with slightly more efficient code in the next two lines. */
    scalar define `u' = (1/3)
    scalar define `w' = (`x' + `y') ^ (`u') + (`x' - `y') ^ (`u') - 1
/* End revised */
    scalar define `f' = `w' * `w' * (3 + `w' * (2 + `w')) - 3
    scalar define `e' = (`kurtosis' - `e') / (`f' - `e')
    if (abs(`skewness') > `tolerance') {
        scalar define `d' = 1 / sqrt(ln(`w'))
        if (`d' < `a10') {
            scalar define `f' = `a16' * `d'
        }
        else {
            scalar define `f' = 2 - `a21' / (`d' * (`d' * (`d' - `a19') + `a22'))
        }
    }
    else {
        scalar define `f' = 2
    }
    scalar define `f' = `e' * `f' + 1
    if (`f' < `a18') {
        scalar define `d' = `a13' * (`f' - 1)
    }
    else {
        scalar define `d' = (`a9' * `f' - `a4') * (3 - `f') ^ (-(`a5'))
    }
// Get g as the first estimate of gamma
    scalar define `g' = 0
    if (`skewness_squared' >= `tt') {
        if (`d' <= 1) {
            scalar define `g' = (`a12' * (`d') ^ (`a17') + `a8') * (`skewness_squared') ^ (`a6')
        }
        else {
            if (`d' >= `a20') {
                scalar define `u' = `a1'
                scalar define `y' = `a7'
            }
            else {
                scalar define `u' = `a2'
                scalar define `y' = `a3'
            }
            scalar define `g' = (`skewness_squared') ^ (`u' * `d' + `y') * (`a14' + `d' * (`a15' * `d' - `a11'))
        }
    }
    local m = 0
*
// Main iteration starts here
    local flag 1
    while ((abs(`u') > `tt') | (abs(`y') > `tt') | (`flag' == 1)) {
        local flag 0
        local m = `m' + 1
        if (`m' > `iterate') {
            scalar define `fault' = "Convergence not achieved in jnsn_SB"
            exit
        }
// Get first six moments for latest g and d values
        jnsn_mom , g(`g') d(`d') a(`hmu') outeriterate(`moiterate') zz(`motolerance') ///
          inneriterate(`miiterate') vv(`mitolerance') fault(`fault')
        if `fault' != "Program terminated normally" {
            exit
        }
        scalar define `s' = `hmu'[1,1] * `hmu'[1,1]
        scalar define `h2' = `hmu'[1,2] - `s'
        if (`h2' < 0) {
            scalar define `fault' = "H2 is less than zero in jnsn_SB"
            exit
        }

        scalar define `t' = sqrt(`h2')
        scalar define `h2a' = `t' * `h2'
        scalar define `h2b' = `h2' * `h2'
        scalar define `h3' = `hmu'[1,3] - `hmu'[1,1] * (3 * `hmu'[1,2] - 2 * `s')
        scalar define `rbet' = `h3' / `h2a'
        scalar define `h4' = `hmu'[1,4] - `hmu'[1,1] * (4 * `hmu'[1,3] - `hmu'[1,1] * (6 * `hmu'[1,2] - 3 * `s'))
        scalar define `bet2' = `h4' / `h2b'
        scalar define `w' = `g' * `d'
        scalar define `u' = `d' * `d'
// Get derivatives
        forvalues j = 1/2 {
            forvalues k = 1/4 {
                scalar define `t' = `k'
                if (`j' != 1) {
                    scalar define `s' = ((`w' - `t') * (`hmu'[1,`k'] - `hmu'[1,`k'+1]) + (`t' + 1) * ///
                      (`hmu'[1,`k'+1] - `hmu'[1,`k'+2])) / `u'
                }
                else {
                    scalar define `s' = `hmu'[1,`k'+1] - `hmu'[1,`k']
                }
                matrix define `dd'[1,`k'] = `t' * `s' / `d'
            }
            scalar define `t' = 2 * `hmu'[1,1] * `dd'[1,1]
            scalar define `s' = `hmu'[1,1] * `dd'[1,2]
            scalar define `y' = `dd'[1,2] - `t'
            matrix define `deriv'[1,`j'] = (`dd'[1,3] - 3 * (`s' + `hmu'[1,2] * `dd'[1,1] - `t' * `hmu'[1,1]) - ///
              1.5 * `h3' * `y' / `h2') / `h2a'
            matrix define `deriv'[1,`j'+2] = (`dd'[1,4] - 4 * (`dd'[1,3] * `hmu'[1,1] + `dd'[1,1] * `hmu'[1,3]) + ///
              6 * (`hmu'[1,2] * `t' + `hmu'[1,1] * (`s' - `t' * `hmu'[1,1])) - 2 * `h4' * `y' / `h2') / `h2b'
        }
        scalar define `t' = 1 / (`deriv'[1,1] * `deriv'[1,4] - `deriv'[1,2] * `deriv'[1,3])
        scalar define `u' = (`deriv'[1,4] * (`rbet' - abs(`skewness')) - `deriv'[1,2] * (`bet2' - `kurtosis')) * `t'
        scalar define `y' = (`deriv'[1,1] * (`bet2' - `kurtosis') - `deriv'[1,3] * (`rbet' - abs(`skewness'))) * `t'
// Form new estimates of g and d
        scalar define `g' = `g' - `u'
        scalar define `g' = cond(((`skewness_squared' == 0) | (`g' < 0)), 0, `g')
        scalar define `d' = `d' - `y'
    }
    macro drop _flag
*
    return local johnson_type SB
    if (`negative' == 0) {
        return scalar gamma = `g'
    }
    else {
        return scalar gamma = -(`g')
        matrix define `hmu'[1,1] = 1 - `hmu'[1,1]
    }
    return scalar delta = `d'
    scalar define `xlam' = `sd' / sqrt(`h2')
    return scalar xi = `mean' - `xlam' * `hmu'[1,1]
    return scalar lambda = `xlam'
    return local fault = scalar(`fault')
    matrix drop `hmu' `dd' `deriv'
    scalar drop `tt' `skewness_squared' `e' `x' `y' `u' `w' `f' `d' `g' `s' `t' `h2a' `h2b' `h3' `rbet' `h4' `bet2' `xlam'
    macro drop _a* _negative _m
end
*
program define jnsn_mom
    version 9.2
    syntax , g(name) d(name) a(name) outeriterate(integer) zz(real) inneriterate(integer) vv(real) fault(name)
*
    tempname b c e r h t u y x v f z s p q aa ab
    matrix define `b' = J(1, 6, 0)
    matrix define `c' = J(1, 6, 0)

// Trial value of h
    if missing(exp(`g' / `d')) {
        scalar define `fault' = "Overflow in jnsn_mom"
        exit
    }
    scalar define `e' = exp(`g' / `d') + 1
    scalar define `r' = sqrt(2) / `d'
    scalar define `h' = 0.75
// Start of outer loop
    local first_flag = 1
    local outer_continue_flag = 0
    while ((`outer_continue_flag' == 1) | (`first_flag' == 1)) {
        if (`first_flag' == 1) {
            if (`d' < 3) scalar define `h' = 0.25 * `d'
            local k = 1
            local first_flag = 0
        }
        else {
            local k = `k' + 1
            if (`k' > `outeriterate') {
                scalar define `fault' = "Convergence not achieved (outer loop) in jnsn_mom"
                exit
            }
            matrix define `c' = `a'
            scalar define `h' = 0.5 * `h' // No convergence yet.  Try smaller h
        }
        scalar define `t' = `g' / `d'
        scalar define `u' = `t'
        scalar define `y' = `h' * `h'
        scalar define `x' = 2 * `y'
        matrix define `a'[1,1] = 1 / `e'
        forvalues i = 2/6 {
            matrix define `a'[1,`i'] = `a'[1,`i'-1] / `e'
        }
        scalar define `v' = `y'
        scalar define `f' = `r' * `h'
        local m = 0
        local inner_continue_flag = 0
// Start of inner loop to evaluate infinite series
        while ((`inner_continue_flag' == 1) | (`m' == 0)) {
            local m = `m' + 1
            if (`m' > `inneriterate') {
                scalar define `fault' = "Convergence not achieved (inner loop) in jnsn_mom"
                exit
            }
            matrix define `b' = `a'
            scalar define `u' = `u' - `f'
            scalar define `z' = cond(((1 + exp(`u')) > 1), 1 + exp(`u'), 1)
            scalar define `t' = `t' + `f'
            local l = ((1 + exp(-`t')) == 1)
            if (!`l') scalar define `s' = exp(`t') + 1
            scalar define `p' = exp(-`v')
            scalar define `q' = `p'
            forvalues i = 1/6 {
                scalar define `aa' = `a'[1,`i']
                scalar define `p' = `p' / `z'
                scalar define `ab' = `aa'
                scalar define `aa' = `aa' + `p'
                if (`aa' == `ab') {
                    continue, break
                }
                if (!`l') {
                    scalar define `q' = `q' / `s'
                    scalar define `ab' = `aa'
                    scalar define `aa' = `aa' + `q'
                    local l = (`aa' == `ab')
                }
                matrix define `a'[1,`i'] = `aa'
            }
            scalar define `y' = `y' + `x'
            scalar define `v' = `v' + `y'
            local inner_continue_flag = 0
            forvalues i = 1/6 {
                if (`a'[1,`i'] == 0) {
                    scalar define `fault' = "A[1,`i'] == 0 in inner loop of jnsn_mom"
                    continue, break
                }
                if (abs((`a'[1,`i'] - `b'[1,`i']) / `a'[1,`i']) > `vv') {
                    local inner_continue_flag = 1
                }
            }
            if `fault' != "Program terminated normally" {
                exit
            }
        }
// End of inner loop
        scalar define `v' = 1 / sqrt(_pi) * `h'
        matrix define `a' = `a' * `v'
        local outer_continue_flag = 0
        forvalues i = 1/6 {
            if (`a'[1,`i'] == 0) {
                scalar define `fault' = "A[1,`i'] == 0 in outer loop of jnsn_mom"
                continue, break
            }
            if (abs((`a'[1,`i'] - `c'[1,`i']) / `a'[1,`i']) > `zz') {
                local outer_continue_flag = 1
            }
        }
        if `fault' != "Program terminated normally" {
            exit
        }

    }
// End of outer loop
    matrix drop `b' `c'
    scalar drop `e' `r' `h' `t' `u' `y' `x' `v' `f' `z' `s' `p' `q' `aa' `ab'
    macro drop _first_flag _outer_continue_flag _inner_continue_flag _k _m _l
end