*! scenario -- generate a scenario                      STB-24: sts7.6
*! version 1.0     Sean Becketti     January 1995
program define scenario
quietly {
	version 3.1
	local varlist "req ex min(1)"
	local options "ACtion(str) AMount(str) Begin(str) Length(str)"
	parse "`*'"
	if !_N {
		di in re "scenario: no observations"
		exit 98
	}
        if "`begin'"=="" {
		projdate
		local begin "$S_1"
	}
	date2obs `begin'
        local begin $S_1
        if (!`begin') {
        	di in re "scenario: no projection date"
                exit 98
        }
        if (`begin'<1) | (`begin'>_N) {
                di in re "scenario: illegal projection date=`begin'"
                exit 98
        }
/*
        Parse scenario.  A scenario is a program for filling in the
        variable during the projection period.  Each instruction in the
        program is a triple: an action, an amount, and a length.
        The possible actions are:

                flat    -- leave the variable unchanged
                grow    -- change the variable a specified amount over
                           a specified period of time using linear
                           interpolation to fill in intermediate periods.
		incr	-- increment a fixed amount each period.
                jump    -- immediately increment the variable
                pchange -- grow at constant rate
                pgrow   -- like "grow", but use constant percentage growth
                           instead of linear interpolation.
                set     -- set the variable to an explicit value

        A length is a positive integer or a ".", which indicates the
        remainder of the program.  The scenario need not fill out the
        entire range of the variable.
*/
        local flat    0            /* Choose codes for the actions */
        local grow    1
	local incr    2
        local jump    3
	local pchange 4
        local pgrow   5
        local set     6
        tempname mx
	parse "`action'", parse(" ,")
	while "`1'"!="" {
		if "`1'"!="," { local a "`a' `1'" }
		mac shift
	}
	local action "`a'"
/*
        Store the coded actions in a program matrix.
*/
        local plen : word count `action'
        if `plen'==0 {
                di in re "scenario: no actions specified"
                error 98
        }
        tempname p
        mat def `p' = J(`plen',3,-99)
        parse "`action'", parse(" ")
        local i 0
        while `i'<`plen' {
                local i = `i' + 1
                if "``i''"=="flat"         { local code = `flat'    }
                else if "``i''"=="grow"    { local code = `grow'    }
                else if "``i''"=="incr"    { local code = `incr'    }
                else if "``i''"=="jump"    { local code = `jump'    }
                else if "``i''"=="pchange" { local code = `pchange' }
                else if "``i''"=="pgrow"   { local code = `pgrow'   }
                else if "``i''"=="set"     { local code = `set'     }
                else {
                        di in re "scenario: unrecognized action = ``i''"
                        exit 98
                }
                mat def `mx' = J(1,1,`code')
                mat substitute `p'[`i',1] = `mx'
        }
/*
        Store the amounts in the program matrix.
*/
	parse "`amount'", parse(" ,")
        local a
	while "`1'"!="" {
		if "`1'"!="," { local a "`a' `1'" }
		mac shift
	}
	local amount "`a'"
        local j : word count `amount'
        if `j'!=`plen' {
                di in re "scenario: number of amounts (`j') doesn't match number of actions (`plen')"
                exit 98
        }
	parse "`amount'", parse(" ,")
        local i 0
        while `i'<`plen' {
                local i = `i' + 1
		local a = ``i''
                cap conf number `a'
                if _rc {
                        di in re "scenario: amount is not a number = `a'"
                        exit 98
                }
                mat def `mx' = J(1,1,`a')
                mat substitute `p'[`i',2] = `mx'
        }
/*
        Store the lengths of each action in the program matrix.
        The dot (unlimited length) is stored as _N.
*/
        local dot = _N
	parse "`length'", parse(" ,")
        local a
	while "`1'"!="" {
		if "`1'"!="," { local a "`a' `1'" }
		mac shift
	}
	local length "`a'"
        local j : word count `length'
        if `j'!=`plen' {
                di in re "scenario: number of lengths (`j') doesn't match number of actions (`plen')"
                exit 98
        }
	parse "`length'", parse(" ,")
        local i 0
        while `i'<`plen' {
                local i = `i' + 1
		local a = ``i''
                if `a'==. { local a = `dot' }
                cap conf integer number `a'
                if _rc {
                        di in re "scenario: illegal length = `a'"
                        exit 98
                }
                if `a'<=0 {
                        di in re "scenario: illegal length = `a'"
                        exit 98
                }
                mat def `mx' = J(1,1,`a')
                mat substitute `p'[`i',3] = `mx'
        }
/*
        All parsing is finished.  Execute the program.
*/
        tempvar x
        tempname base
        parse "`varlist'", parse(" ")
        while "`1'"!="" {
                local v "`1'"
                mac shift
                local type : type `v'
                gen `type' `x' = `v'
                local i 0               /* instruction number */
                local l = `begin' - 1
                while (`i'<`plen') & (`l'<_N) {
                        local i = `i' + 1
                        local action = `p'[`i',1]
                        local amount = `p'[`i',2]
                        local length = `p'[`i',3]
                        local f = `l' + 1
                        scalar `base' = `x'[`f'-1]
                        local l = min(_N,`f'+`length'-1)
			local len = `l' - `f' + 1
                        if `action'==`flat' {
                                replace `x' = `base' in `f'/`l'
                        }
                        else if `action'==`grow' {
                                replace `x' = (`amount'/`len') + `x'[_n-1] in `f'/`l'
                        }
                        else if `action'==`incr' {
                                replace `x' = `amount' + `x'[_n-1] in `f'/`l'
                        }
                        else if `action'==`jump' {
                                scalar `base' = `base' + `amount'
                                replace `x' = `base' in `f'/`l'
                        }
                        else if `action'==`pchange' {
                                replace `x' = (1+`amount')*`x'[_n-1] in `f'/`l'
                        }
                        else if `action'==`pgrow' {
                                local R = ((`base'+`amount')/`base')^(1/`len')
                                replace `x' = (`R')*`x'[_n-1] in `f'/`l'
                        }
                        else if `action'==`set' {
                                replace `x' = `amount' in `f'/`l'
                        }
                        else {
                                di in re "scenario: program error, action code `action'"
                                exit 999
                        }
                }
                replace `v' = `x' in `begin'/l
                drop `x'

        }
}	/*	end quietly	*/
end