% originally written by Oleg Melnikov in Gauss, 1998
function pmgshell()
pause on

% Build Par struct with general program data
Par.batchjob        = 0;
Par.configfile      = ['config_' num2str(Par.batchjob) '.mat'];
Par.nocheck         = -1e50; 
Par.uu_competition  = 1;
Par.uu_monopoly     = 2;
Par.uu_planner      = 3;
Par.uu_quality      = 1;
Par.uu_cost         = 2;
Par.uu_capacity     = 3;
Par.uu_ran_entry    = 1;
Par.uu_det_entry    = 2;


% Load Params struct from file (if it exists) or use the default Params
if exist(Par.configfile, 'file')
    disp('loading config file...');
    load(Par.configfile, 'Params');
else
    disp('starting with default config file...');
    Params = default(Par);
    save(Par.configfile, 'Params');
end


% Display model type and various data tests
% RP - This entire section is not fully clear to me
[~, eqltype, invtype] = acronym(Par, Params.eql_type, Params.ind_type);
disp(['current model:' eqltype ', investment in' invtype '']);
disp(' ');

if (Params.rlg_y>0 && Params.wstar>0)
    disp(' '); 
    disp ('hey:should not use both rlg_y & wstar'); 
    disp(' '); 
end
if (Params.rlg_y>0 && Params.rlg_y<Params.mc)
    disp(' '); 
    disp ('hey:must have rlg_y>mc else profits = zero'); 
    disp(' '); 
end

if Params.rlg_inv>0
    disp(' '); 
    if (0)
        aeff = Params.inv_mult*(1+Params.rlg_inv*rev((0:1:Params.kmax+1)));
        disp(['using linear spillover with coeff of ' num2str(Params.rlg_inv) ', yielding aeff = ' num2str(aeff')]);
    else
        aeff = [ones(Params.kmax, 1)*Params.inv_mult*(1+Params.rlg_inv); Params.inv_mult];
        disp(['using common spillover for all laggards, yielding aeff = ' num2str(aeff')]);
        if (Params.delta>0 || Params.wstar>0)
            disp('hey:probably should not use this form of spillover (set in pmgshell.m), since leader often not at frontier'); 
        end
    end
    disp(' ');
else
    aeff = Params.inv_mult*ones(Params.kmax+1, 1);
end

% Setup menu
mc1 = 'set model primitives';
mc2 = 'set static parameters';
mc3 = 'set dynamic parameters';
mc4 = 'save parameters';
mc5 = 'load parameters';
mc6 = 'compute profit function';
mc7 = 'compute dynamic equilibrium';
mc8 = 'simulate model';
mc9 = 'exit';
Par.pmg_menu = {mc1, mc2, mc3, mc4, mc5, mc6, mc7, mc8, mc9};
Par.nchoices = size(Par.pmg_menu, 2);


% Main menu loop
while true
    Par = setstatus(Par);
    clc;  disp(' ');
    showparms(Par, Params);
    action = getaction(Par);
    if action == 1
        Params = askprim(Par);
        save(Par.configfile, 'Params');
    elseif action == 2
        Params = askparms(Par);
        save(Par.configfile, 'Params');
    elseif action == 3
        Params = askeqparms(Par);
        save(Par.configfile, 'Params');
    elseif action == 4
        saveparms(Par, Params);
    elseif action == 5
        Params = loadparms(Par);
    elseif action == 6
        profit_main(0);
        disp('press any key to return to main menu');
        pause();
    elseif action == 7
        if (Params.eql_type == Par.uu_competition)
            eql_ma_foc(0);
            disp('press any key to return to main menu');
            pause();
        else
            disp('Only competition mode is implemented!');
            disp('press any key to return to main menu');
            pause();
            % eql_sa();
        end
    elseif action == 8
        welf_ma(0);
        disp('press any key to return to main menu');
        pause();
    elseif action == 9
        break;
    else
        disp(['invalid action ' int2str(action)]);
        disp('; press any key to return to main menu');
        pause();
    end
end
end % End of function pmgshell



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Function default - load default values into Param struct
% see description of each of these parameters in  batchruns.m
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function p = default(Par)
p.max_firms = 3;
p.eql_type = Par.uu_competition;
p.ind_type = Par.uu_quality;
p.entry_type = Par.uu_ran_entry;
p.entry_low = 22; % 0.15;
p.entry_high = 24; % 0.25;
p.entry_sunk = 0.2;
p.entry_at = 4;
p.beta = 0.925;
p.delta = 0.5; % 0.7;
p.scrap_val = 21; % 0.1;
p.inv_mult = .5; % 3;
p.mc = 5;
p.mkt_size = 5;
p.kmax = 7; % 19;
p.wstar = -1; % 0;
p.intercept = 3;
p.fixed_cost = 0.0;
p.gamma = 1;
p.tau = 0.1;

p.rlg_wscale = .2; % 1;         % 3 in Rand94?
p.rlg_wshift = 0; % 15;         % -7 in Rand94?
p.rlg_sh_cap = 1;               % .55, .65 in Rand94
p.rlg_inv = 0; % .6;          
p.rlg_y = 15; % 0;
p.rlg_scrap_hi = 22; % .2;      % upper bound of scrap value
p.rlg_alpha = .4; % 1;          % price coefficient
p.rlg_outgood = 1; % 0;         % outside good present
p.rlg_leap = 0.0;               % Prob entrant goes to KMAX instead of _ENTRY_AT
p.rlg_maxp = 9999;              % ignored unless NO Outside Good  &  Y = 0, in which case monopolist price = rlg_maxp * price of leader in max-differentiated duopoly
p.rlg_foc = 1;                  % if 1, use FOC method.  If 0, use Iterative Best Response method of Pakes-McGuire
p.rlg_min_innov1 = 0;           % minimum innovation rate by frontier firms.  Should generally be 0.  No justification for >0.

p.profit_done = 0;
p.eql_done = 0;
p.active_cfg = 0;
end % End of function default



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Function acronym - get textual description of types
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [s,  etype,  itype] = acronym(Par, et, it)
if et == Par.uu_competition
    s = '';
    etype = 'markov perfect nash equilibrium';
elseif et == Par.uu_monopoly
    s = 'm';
    etype = 'monopoly';
elseif et == Par.uu_planner
    s = 's';
    etype = 'social planner';
end

if it == Par.uu_quality
    s = [s 'b'];
    itype = 'quality (differentiated products)';
elseif it == Par.uu_cost
    s = [s 'c'];
    itype = 'marginal cost (homogenous products)';
elseif it == Par.uu_capacity
    s = [s 'p'];
    itype = 'capacity (homogenous products)';
end

if Par.batchjob > 0 
    s = ['bj' num2str(Par.batchjob) '_' s]; 
end
end % End of function acronym



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Function setstatus - Which options are active in menu?
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function Par = setstatus(Par)
    Par.menu_status = ones(Par.nchoices, 1); % All options active
end % End of function setstatus



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Function showparms - Display current parameters in menu
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function showparms(Par, Params)

[~,et,it] = acronym(Par,Params.eql_type, Params.ind_type);

disp('********                          Dynamic Oligopoly Algorithm                          ********');
disp('******** based on Goettler and Gordon (2013) modification of Pakes and McGuire (1994)  ********');
cfg = ['Current configuration: ' Params.active_cfg];
disp( center(cfg, ''));

disp( outline('Model Primitives'));
fprintf(['INVESTMENT IN:\t' it '\n']);
fprintf(['EQUILIBRIUM:\t' et '\n']);
fprintf(['MAX NUMBER OF FIRMS:\t' int2str(Params.max_firms) '\tMAX EFFICIENCY LEVEL (KMAX):\t' int2str(Params.kmax) '\n']);
disp( outline('Static Parameters'));
if (Params.ind_type == Par.uu_quality)
    fprintf(['MARGINAL, FIXED COST:\t' num2str(Params.mc) '\t' num2str(Params.fixed_cost) '\t']);
    fprintf(['MKT SIZE:\t' num2str(Params.mkt_size) '\n']);
    fprintf(['OUTSIDE GOOD:\t' int2str(Params.rlg_outgood) '\t']);
    fprintf(['Y:\t' num2str(Params.rlg_y) '\t']);
    fprintf(['MAXP FACTOR: ' num2str(Params.rlg_maxp) '\n']);
    fprintf(['W SCALE,  SHIFT:\t' num2str(Params.rlg_wscale) '\t' num2str(Params.rlg_wshift) '\t']);
    fprintf(['PRICE COEFF:\t' num2str(Params.rlg_alpha) '\t']);
    fprintf(['SHARE CAP:  ' num2str(Params.rlg_sh_cap) '\n']);
    fprintf(['w*:\t' int2str(Params.wstar) '  (-1 for linear GG, >0 for PM w*)\n']);
    if Params.rlg_outgood==0;
      if Params.rlg_y>0;  disp('Note: with no outside good & Y>0 , monopoly price is Y');  
      else;               disp('Note: with no outside good & Y=0 , monopoly price is MAXP * leader price in max-differentiated duopoly');
      end;
    end;
    % if Params.wstar>0 ;
    %  disp(['Note: w*= ' num2str(Params.wstar) ' yields log(eg(kmax)) = ' num2str(log(exp(Params.wstar)*(2.0-exp(-(Params.kmax*Params.rlg_wscale+Params.rlg_wshift-Params.wstar)))))]);
    % end;
elseif (Params.ind_type == Par.uu_cost)
    disp(['demand intercept: ' num2str(Params.intercept)]);
    disp('');
    disp(['fixed cost: ' num2str(Params.fixed_cost)]);
    disp('');
    disp(['maximum marginal cost: ' num2str(Params.gamma)]);
elseif (Params.ind_type == Par.uu_capacity)
    disp(['demand intercept: ' num2str(Params.intercept)]);
    disp('');
    disp(['marginal cost: ' num2str(Params.mc)]);
    disp('');
    disp(['tau: ' num2str(Params.tau)]);
end

disp( outline('Dynamic Parameters'));
fprintf(['DISCOUNT FACTOR:\t' num2str(Params.beta) '\tPROB OUTSIDE GOOD IMPROVES (delta): ' num2str(Params.delta) '\n']);
fprintf(['SCRAP VALUE: ' num2str(Params.scrap_val) '-' num2str(Params.rlg_scrap_hi) '\t']);
fprintf('SUNK COST: ');
if Params.entry_type == Par.uu_det_entry;
  fprintf(['fixed at ' num2str(Params.entry_sunk) '\t']);
else;
  fprintf([num2str(Params.entry_low) '-' num2str(Params.entry_high) '\t']);
end;
fprintf(['ENTRY AT:' int2str(Params.entry_at) ' (if <0, relative to leader) \n']);
fprintf(['INNOVATION EFFICIENCY: a = ' num2str(Params.inv_mult) '  spill = ' num2str(Params.rlg_inv) '\tPROB ENTER AT KMAX: ' num2str(Params.rlg_leap) '\n']);
disp( repmat('-', 1,79));
end % End of function showparms



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Function getaction - Display possible actions and get choice from user
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function action = getaction(Par)
for i = 1:Par.nchoices
    if Par.menu_status(i,1)==1
        disp([repmat(' ',  1, 23) int2str(i) ' - ' Par.pmg_menu{i}]);
    end
end
action = getnum('your choice ', 9);
end % End of function getaction



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Function getint - Get integer between nlow and nhigh with default value
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function n = getint(Par, prompt,  nlow,  nhigh,  default)
n = Par.nocheck-1;
while 1
    n = getnum(prompt, default);
    if (n ~= round(n))
        disp('please re-enter. This parameter must be integer-valued ');
        continue;
    end
    if((n<nlow && nlow ~= Par.nocheck) || (n>nhigh && nhigh ~= Par.nocheck))
        disp('please re-enter. Valid range for this parameter is ');
        if(nhigh == Par.nocheck)
            disp(['>' num2str(nlow)]);
        elseif(nlow == Par.nocheck)
            disp(['<' num2str(nhigh)]);
        else
            disp([num2str(nlow) '-' num2str(nhigh)]);
        end
    else
        break;
    end
end
end % End of function getint



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Function getfloat - Get float between nlow and nhigh with default value
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function n = getfloat(Par, prompt,  nlow,  nhigh,  default)
n = Par.nocheck-1;
while 1
    n = getnum(prompt, default);
    if((n<nlow && nlow ~= Par.nocheck) || (n>nhigh && nhigh ~= Par.nocheck));
        disp('please re-enter.  Valid range for this parameter is '); ;
        if(nhigh == Par.nocheck)
            disp(['>' num2str(nlow)]);
        elseif(nlow == Par.nocheck)
            disp(['<' num2str(nhigh)]);
        else
            disp([num2str(nlow) '-' num2str(nhigh)]);
        end
    else
        break;
    end
end
end % End of function getfloat



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Function askprim - Set model primitives
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function Params = askprim(Par)
load(Par.configfile, 'Params');
clc;  disp(' ');
disp('***enter model primitives***');
Params.ind_type  = getint(Par, 'investment effect (1:quality (default), 2:marginal cost, 3:capacity) ', 1, 3, 1);
Params.eql_type  = getint(Par, 'equilibrium type (1:Nash (default), 2:monopoly, 3:social planner) ', 1, 3, 1);
Params.max_firms = getint(Par, 'maximum number of active firms (1-12, default 3) ', 1, 12, 3);
Params.kmax      = getint(Par, 'kmax: highest quality rung attainable (1-99, default 14) ', 1, 99, 14);
end % End of function askprim



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Function askparms - Set model static parameters
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function Params = askparms(Par)
load(Par.configfile, 'Params');
clc;  disp(' ');
disp('***enter static parameters***');
if Params.ind_type == Par.uu_quality;
    Params.mc           = getfloat(Par, 'marginal cost (default 5) ', 0, Par.nocheck, 5);
    Params.fixed_cost   = getfloat(Par, 'fixed cost (default 0) ', 0, Par.nocheck, 0);
    Params.mkt_size     = getfloat(Par, 'market size (default 5) ', 0, Par.nocheck, 5);
    Params.rlg_sh_cap   = getfloat(Par, 'market share cap (default 1, meaning no cap) ', 0.5, 1, 1);
    Params.rlg_outgood  = getfloat(Par, 'include outside good 1 = yes, 0 = no (default 1) ', 0, 1, 1);
    Params.rlg_alpha    = getfloat(Par, 'price coefficient (default 1) ', 0, Par.nocheck, 1);
    Params.rlg_wscale   = getfloat(Par, 'w scale (default 1, quality grid = [1:kmax]*w_scale + w_shift) ', 0, Par.nocheck, 1);
    Params.rlg_wshift   = getfloat(Par, 'w shift (default 0) ', Par.nocheck, Par.nocheck, 0);
    Params.rlg_y        = getfloat(Par, 'income y: (0 [default] for linear utility.  >0 for Cobb-Douglas utility = w+log(y-p)-log(y) which bounds p<y) ', 0, Par.nocheck, 0);
    Params.wstar        = getint(Par, 'bound method: >0 for PM w*, -1 [default] for linear utility of GG', -1, Params.rlg_wscale*Params.kmax+Params.rlg_wshift, -1);
    if (Params.rlg_y == 0)
        Params.rlg_maxp = getfloat(Par, 'max monopolist price factor (price <= MAXP * leader price in max-differentiated duopoly, ONLY IF no outside good & Y=0, default 9999) ', 0, Par.nocheck, 9999);
    else
        Params.rlg_maxp = 9999;
    end
elseif Params.ind_type == Par.uu_cost
    Params.intercept    = getfloat(Par, 'demand intercept (>1, default 3) ', 1, Par.nocheck, 3);
    Params.fixed_cost   = getfloat(Par, 'fixed cost (default 0.2) ', 0, Par.nocheck, 0.2);
    Params.gamma        = getfloat(Par, 'maximum marginal cost (gamma) (default 1) ', 0, Par.nocheck, 1);
elseif Params.ind_type == Par.uu_capacity
    Params.intercept    = getfloat(Par, 'demand intercept (>1, default 2) ', 1, Par.nocheck, 2);
    Params.mc           = getfloat(Par, 'marginal cost (default 2) ', 0, Par.nocheck, 2);
    Params.tau          = getfloat(Par, 'state number/capacity constraint ratio (tau) (default 0.1)', 0, Par.nocheck, 0.1);
end
end % End of function askparms



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Function askeqparms - Set model dynamic parameters
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function Params = askeqparms(Par)
load(Par.configfile, 'Params');
clc;  disp(' ');
disp('***enter dynamic parameters***');
nfirms = Params.max_firms;
Params.beta = getfloat(Par, 'discount factor (0-1, default 0.925)', 0, 1, 0.925);

disp('entry & exit parameters:');

Params.scrap_val    = getfloat(Par, 'scrap value lower bound (min = default = 0.001)', 0.001, Par.nocheck, 0.001);
Params.rlg_scrap_hi = getfloat(Par, 'scrap value upper bound (default=lower bound+1)', Params.scrap_val+.01, Par.nocheck, Params.scrap_val+1);
%% No longer allowing fixed scrap
% while (Params.rlg_scrap_hi>0 && Params.rlg_scrap_hi <= Params.scrap_val)
%     Params.rlg_scrap_hi = getfloat('Hey: enter 0 (for fixed scrap) or bigger than other scrap number (default 0-->not random)', 0, Par.nocheck, 0);
% end
% if (Params.rlg_scrap_hi == 0)
%     Params.rlg_scrap_hi = Params.scrap_val; 
% end

Params.entry_type = getint(Par, 'entry sunk cost type (1-stochastic, 2-deterministic, default 1)', 1, 2, 1);
if (Params.entry_type == Par.uu_ran_entry)
    Params.entry_low = getfloat(Par, 'sunk cost lower bound (default 0.15)', 0, Par.nocheck, 0.15);
    Params.entry_high = getfloat(Par, 'sunk cost upper bound (default 0.25)', Params.entry_low, Par.nocheck, 0.25);
else
    Params.entry_sunk = getfloat(Par, 'sunk cost of entry (default 0.2)', 0, Par.nocheck, 0.2);
end

Params.entry_at = getint(Par, ['ENTRY_AT: efficiency at which firms enter (1 to ' int2str(Params.kmax) ', default 4)'], 1, Params.kmax, 4);
Params.rlg_leap = getfloat(Par, 'probability entrant starts at frontier (KMAX) instead of at efficiency given by ENTRY_AT (default 0)', 0, 1, 0);

disp('investment efficiency probability distribution parameters:');

Params.inv_mult = getfloat(Par, 'investment multiplier (a from p(x) = ax/(1+ax), default 1)', 0, Par.nocheck, 1);
Params.rlg_inv  = getfloat(Par, 'spillover: if >0, all laggards have investment multiplier of  a*(1+spillover).  If <0, then  a*(1+|spillover|*Steps_Behind), default 0)', 0, Par.nocheck, 0);
Params.delta    = getfloat(Par, 'probability of outside good improving (default 0)', 0, 1, 0);
end % End of function askeqparms



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Function saveparms - Save current configuration to different file
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function saveparms(Par, Params)
fid = getint(Par,'File id (integer number 0-9999, default 0):',0,999999,0);
configfile = ['config_' num2str(fid) '.mat'];
overwrite = 'y';
if exist(configfile, 'file')
    disp(['file ' configfile ' already exist; overwrite(y/n)?']);
    overwrite = input('', 's');
end
overwrite = lower(overwrite);
if(overwrite == 'y')
    Params.active_cfg = fid;
    save(configfile, 'Params');
    Params.active_cfg = 0;
    save(Par.configfile, 'Params');
else
    disp('Did not write file!');
    disp('Press any key to return to main menu');
    pause
end
end % End of function saveparms



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Function loadparms - Load configuration from different file
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function Params = loadparms(Par)
fid = getint(Par,'File id (integer number 0-9999, default 0):',0,999999,0);
configfile = ['config_' num2str(fid) '.mat'];
if exist(configfile, 'file')
    load(configfile, 'Params');
    Params.active_cfg = 0;
    save(Par.configfile, 'Params');
    disp('press any key to return to main menu');
else
    disp(['file ' configfile ' not found; press any key to return to main menu']);
end
pause
end % End of function loadparms



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Helper functions
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% centered formatting - for headings
function sfmt = center(s,  fill)
spad = repmat(fill, 1, floor( (80-(length(s)+2)) /2) );
sfmt = [ spad  ' ' s ' ' spad];
end

% outlined formatting - for minor headings
function sfmt = outline(title)
sfmt = ['---( ' title ' )'];
sfmt = [sfmt repmat('-', 1, 79-length(sfmt))];
end % End of function outline


% get a number from the user
function ret = getnum(prompt,  default)
ret = input(prompt);
if (length(ret) == 0)
    ret = default;
end
end % End of function getnum
