diff --git a/README.md b/README.md
index 6f06afe..7fbbf0a 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,82 @@
# getOptions
-getOptions: clean handling of variable arguments (varargin) into MATLAB functions.
+
+`getOptions` is a simple, user friendly way to handle variable argument inputs (varargin) into MATLAB functions.
+
+Please see my 2014 write-up __[dealing with variable options (varargin) in matlab](https://bahanonu.com/getOptions)__ ([https://bahanonu.com/getOptions](https://bahanonu.com/getOptions)) for details and reasons behind implementing and using this function. Installation and usage instructions below.
+
+Contact: __Biafra Ahanonu, PhD (bahanonu [at] alum [dot] mit [dot] edu)__.
+
+Made in USA.
+
+
+## Installation
+
+Download or clone the `getOptions` repository and place the root folder in the MATLAB path. Follow instructions for use below.
+
+Test by running `unitTestGetOptions` unit test. Will print out the modified structures and give out several warnings to show examples of what happens if user inputs Name-Value pairs that are not associated with any options.
+
+## Instructions on use
+There are two ways to have parent functions pass Name-Value pairs to child functions that `getOptions` will parse to update default options in child function.
+
+### Method #1
+Use the `'options', options` [Name-Value](https://www.mathworks.com/help/matlab/ref/varargin.html) pair to input an `options` structure that will overwrite default options in a function.
+- The name of the `opts` variable does not matter, but have to have the `options` Name input as one of the variable arguments for `getOptions` to recognize using Method #1.
+- If the user inputs a Name-Value pair that is not associated with an option for that function, `getOptions` gives out a warning rather than an error.
+
+```MATLAB
+mutationList = [1 2 3];
+opts.Stargazer = 1;
+opts.SHH = 0;
+exampleFxn(mutationList,'options',opts);
+```
+
+### Method #2
+Alternatively use Name-Value pairs for all variable input arguments. Will produce the same result as above.
+
+```MATLAB
+mutationList = [1 2 3];
+exampleFxn(mutationList,'Stargazer',1,'SHH',0);
+```
+
+### `getOptions` in user functions
+To adapt `getOptions` to your own functions, add the section marked `FUNCTION OPTIONS` at the beginning and add all your options.
+
+```MATLAB
+function [out1] = exampleFxn(in1,varargin)
+
+ % ========================
+ % FUNCTION OPTIONS
+ % Description option #1
+ opts.Stargazer = '';
+ % Description option #2
+ opts.SHH = 1;
+ % get options
+ opts = getOptions(opts,varargin);
+ % disp(opts)
+ % unpack options into current workspace (not recommended)
+ % fn=fieldnames(opts);
+ % for i=1:length(fn)
+ % eval([fn{i} '=opts.' fn{i} ';']);
+ % end
+ % ========================
+
+ try
+ % Do something.
+ catch err
+ disp(repmat('@',1,7))
+ disp(getReport(err,'extended','hyperlinks','on'));
+ disp(repmat('@',1,7))
+ end
+end
+```
+
+## Notes
+- If `getOptions` masks an existing function due to naming conflicts, just rename it (e.g. `getOptionsCustom`) and it will function the same.
+- `getOptions.m` can be placed in a package (e.g. `myPkg` within `+myPkg` folder) to allow it to be called as a function of that package (e.g. `myPkg.getOptions`) to reduce masking or naming conflicts if that is a concern.
+
+## Using `getSettings`
+To provide a central location for all options or allow users a central place to edit settings, can add each function's options to `getSettings` switch statement matching the parent functions name. See `getSettings.m` and call `getOptions` using the `getFunctionDefaults` Name-Value input as `opts = getOptions(opts,varargin,'getFunctionDefaults',1);`.
+
+## License
+
+See `LICENSE`. MIT @ Biafra Ahanonu
\ No newline at end of file
diff --git a/getOptions.m b/getOptions.m
new file mode 100644
index 0000000..c655b2a
--- /dev/null
+++ b/getOptions.m
@@ -0,0 +1,227 @@
+function [options] = getOptions(options,inputArgs,varargin)
+ % Gets default options for a function and replaces them with inputArgs inputs if they are present in Name-Value pair input (e.g. varargin).
+ % Biafra Ahanonu
+ % Started: 2013.11.04.
+ %
+ % inputs
+ % options - structure with options.
+ % inputArgs - an even numbered cell array, with {'option','value'} as the ordering. Normally pass varargin.
+ % Outputs
+ % options - options structure with inputs to function added.
+ % NOTE
+ % use the 'options' name-value pair to input an options structure that will overwrite default options in a function, example below.
+ % options.Stargazer = 1;
+ % options.SHH = 0;
+ % getMutations(mutationList,'options',options);
+
+ % This is in contrast to using name-value pairs, both will produce the same result.
+ % getMutations(mutationList,'Stargazer',1,'SHH',0);
+ %
+ % USAGE
+ % function [input1,input2] = exampleFxn(input1,input2,varargin)
+ % %========================
+ % % DESCRIPTION
+ % options.Stargazer = '';
+ % options.SHH = '';
+ % % get options
+ % options = getOptions(options,varargin);
+ % % display(options)
+ % % unpack options into current workspace
+ % % fn=fieldnames(options);
+ % % for i=1:length(fn)
+ % % eval([fn{i} '=options.' fn{i} ';']);
+ % % end
+ % %========================
+ % try
+ % % Do something.
+ % catch err
+ % disp(repmat('@',1,7))
+ % disp(getReport(err,'extended','hyperlinks','on'));
+ % disp(repmat('@',1,7))
+ % end
+ % end
+
+ % changelog
+ % 2014.02.12 [11:56:00] - added feature to allow input of an options structure that contains the options instead of having to input multiple name-value pairs. - Biafra
+ % 2014.07.10 [05:19:00] - added displayed warning if an option is input that was not present (this usually indicates typo). - Lacey (merged)
+ % 2014.12.10 [19:32:54] - now gets calling function and uses that to get default options - Biafra
+ % 2015.08.24 [23:31:36] - updated comments. - Biafra
+ % 2015.12.03 [13:52:15] - Added recursive aspect to mirrorRightStruct and added support for handling struct name-value inputs. mirrorRightStruct checks that struct options input by the user are struct in the input options. - Biafra
+ % 2016.xx.xx - warnings now show both calling function and it's parent function, improve debug for warnings. Slight refactoring of code to make easier to follow. - Biafra
+
+ % TODO
+ % allow input of an option structure - DONE!
+ % call settings function to have defaults for all functions in a single place - DONE!
+ % allow recursive overwriting of options structure - DONE!
+ % Type checking of all field names input by the user?
+
+ %========================
+ % Options for getOptions. Avoid recursion here, hence don't use getOptions for getOptions's options.
+ % Binary: 1 = whether getOptions should use recursive structures or crawl through a structure's field names or just replace the entire structure. For example, if "1" then options that themselves are a structure or contain sub-structures, the fields will be replaced rather than the entire strucutre.
+ goptions.recursiveStructs = 1;
+ % Binary: 1 = show warning if user inputs Name-Value pair option input that is not in original structure.
+ goptions.showWarnings = 1;
+ % Int: number of parent stacks to show during warning.
+ goptions.nParentStacks = 1;
+ % Binary: 1 = get defaults for a function from getSettings.
+ goptions.getFunctionDefaults = 0;
+ % Filter through options
+ try
+ for i = 1:2:length(varargin)
+ inputField = varargin{i};
+ if isfield(goptions, inputField)
+ inputValue = varargin{i+1};
+ goptions.(inputField) = inputValue;
+ end
+ end
+ catch err
+ localShowErrorReport(err);
+ display(['Incorrect options given to getOptions"'])
+ end
+ % Don't do this! Recursion with no base case waiting to happen...
+ % goptions = getOptions(goptions,varargin);
+ %========================
+
+ % Get default options for a function
+ if goptions.getFunctionDefaults==1
+ [ST,I] = dbstack;
+ % fieldnames(ST)
+ parentFunctionName = {ST.name};
+ parentFunctionName = parentFunctionName{2};
+ [optionsTmp] = getSettings(parentFunctionName);
+ if isempty(optionsTmp)
+ % Do nothing, don't use defaults if not present
+ else
+ options = optionsTmp;
+ % options = mirrorRightStruct(inputOptions,options,goptions,val);
+ end
+ end
+
+ % Get list of available options
+ validOptions = fieldnames(options);
+
+ % Loop over all input arguments, overwrite default/input options
+ for i = 1:2:length(inputArgs)
+ % inputArgs = inputArgs{1};
+ val = inputArgs{i};
+ if ischar(val)
+ %display([inputArgs{i} ': ' num2str(inputArgs{i+1})]);
+ if strcmp('options',val)
+ % Special options struct, only add field names defined by the user. Keep all original field names that are not input by the user.
+ inputOptions = inputArgs{i+1};
+ options = mirrorRightStruct(inputOptions,options,goptions,val);
+ elseif sum(strcmp(val,validOptions))>0&isstruct(options.(val))&goptions.recursiveStructs==1
+ % If struct name-value, add users field name changes only, keep all original field names in the struct intact, struct-recursion ON
+ inputOptions = inputArgs{i+1};
+ options.(val) = mirrorRightStruct(inputOptions,options.(val),goptions,val);
+ elseif sum(strcmp(val,validOptions))>0
+ % Non-options, non-struct value, struct-recursion OFF
+ % elseif ~isempty(strcmp(val,validOptions))
+ % Way more elegant, directly overwrite option
+ options.(val) = inputArgs{i+1};
+ % eval(['options.' val '=' num2str(inputArgs{i+1}) ';']);
+ else
+ if goptions.showWarnings==1
+ localShowWarnings(2,'name-value','','',val,goptions.nParentStacks);
+ end
+ end
+ else
+ if goptions.showWarnings==1
+ localShowWarnings(2,'name-value incorrect','','',val,goptions.nParentStacks);
+ end
+ continue;
+ end
+ end
+ %display(options);
+end
+function [toStruct] = mirrorRightStruct(fromStruct,toStruct,goptions,toStructName)
+ % Overwrites fields in toStruct with those in fromStruct, other toStruct fields remain intact.
+ % More generally, copies fields in fromStruct into toStruct, if there is an overlap in field names, fromStruct overwrites.
+ % Fields present in toStruct but not fromStruct are kept in toStruct output.
+ fromNames = fieldnames(fromStruct);
+ for name = 1:length(fromNames)
+ fromField = fromNames{name};
+ % if a field name is a struct, recursively grab user options from it
+ if isfield(toStruct, fromField)|isprop(toStruct, fromField)
+ if isstruct(fromStruct.(fromField))&goptions.recursiveStructs==1
+ % safety check: field exist in toStruct and is also a structure
+ if isstruct(toStruct.(fromField))
+ toStruct.(fromField) = mirrorRightStruct(fromStruct.(fromField),toStruct.(fromField),goptions,[toStructName '.' fromField]);
+ else
+ localShowWarnings(3,'notstruct',toStructName,fromField,'',goptions.nParentStacks);
+ end
+ else
+ toStruct.(fromField) = fromStruct.(fromField);
+ end
+ else
+ if goptions.showWarnings==1
+ localShowWarnings(3,'struct',toStructName,fromField,'',goptions.nParentStacks);
+ end
+ end
+ end
+end
+function localShowErrorReport(err)
+ % Displays an error report.
+ display(repmat('@',1,7))
+ disp(getReport(err,'extended','hyperlinks','on'));
+ display(repmat('@',1,7))
+end
+function localShowWarnings(stackLevel,displayType,toStructName,fromField,val,nParentStacks)
+ % Sub-function to centralize displaying of warnings within the function
+ try
+ % Calling localShowWarnings adds to the stack, adjust accordingly.
+ stackLevel = stackLevel+1;
+
+ % Get the entire function-call stack.
+ [ST,~] = dbstack;
+ callingFxn = ST(stackLevel).name;
+ callingFxnPath=which(ST(stackLevel).file);
+ callingFxnLine = num2str(ST(stackLevel).line);
+
+ % Add info about parent function of function that called getOptions.
+ callingFxnParentStr = '';
+ % nParentStacks = 2;
+ stackLevelTwo = stackLevel+1;
+ for stackNo = 1:nParentStacks
+ if length(ST)>=(stackLevelTwo)
+ callingFxnParent = ST(stackLevelTwo).name;
+ callingFxnParentPath = which(ST(stackLevelTwo).file);
+ callingFxnParentLine = num2str(ST(stackLevelTwo).line);
+ callingFxnParentStr = [callingFxnParentStr ' | ' callingFxnParent ' line ' callingFxnParentLine];
+ else
+ callingFxnParentStr = '';
+ end
+ stackLevelTwo = stackLevelTwo+1;
+ end
+
+ % Display different information based on what type of warning occurred.
+ switch displayType
+ case 'struct'
+ warning(['WARNING: ' toStructName '.' fromField ' is not a valid option for ' callingFxn ' on line ' callingFxnLine callingFxnParentStr])
+ case 'notstruct'
+ warning(['WARNING: ' toStructName '.' fromField ' is not originally a STRUCT, ignoring. ' callingFxn ' on line ' callingFxnLine callingFxnParentStr])
+ case 'name-value incorrect'
+ warning(['WARNING: enter the parameter name before its associated value in ' callingFxn ' on line ' callingFxnLine callingFxnParentStr])
+ case 'name-value'
+ warning(['WARNING: ' val ' is not a valid option for ' callingFxn ' on line ' callingFxnLine callingFxnParentStr])
+ otherwise
+ % do nothing
+ end
+ catch err
+ localShowErrorReport(err);
+ callingFxn = 'UNKNOWN FUNCTION';
+ % Display different information based on what type of warning occurred.
+ switch displayType
+ case 'struct'
+ warning(['WARNING: ' toStructName '.' fromField ' is not a valid option for "' callingFxn '"'])
+ case 'notstruct'
+ warning('Unknown error.')
+ case 'name-value incorrect'
+ warning(['WARNING: enter the parameter name before its associated value in "' callingFxn '"'])
+ case 'name-value'
+ warning(['WARNING: ' val ' is not a valid option for "' callingFxn '"'])
+ otherwise
+ % do nothing
+ end
+ end
+end
\ No newline at end of file
diff --git a/getSettings.m b/getSettings.m
new file mode 100644
index 0000000..1ede874
--- /dev/null
+++ b/getSettings.m
@@ -0,0 +1,43 @@
+function [options] = getSettings(functionName)
+ % Send back default options to getOptions, users can modify settings here.
+ % Biafra Ahanonu
+ % started: 2014.12.10
+ %
+ % Inputs
+ % functionName - name of function whose option should be loaded
+ % Note
+ % Don't let this function call getOptions! Else you'll potentially get into an infinite loop.
+
+ % changelog
+ %
+
+ try
+ switch functionName
+ case 'exampleFxn'
+ options.example1 = '';
+ options.example1 = 0;
+ case 'exampleFxn2'
+ % Str: DESCRIPTION.
+ options.example1 = '';
+ % Binary: DESCRIPTION.
+ options.example1 = 0;
+ case 'unitTestGetOptions'
+ % Str: DESCRIPTION.
+ options.example1 = '';
+ % Binary: DESCRIPTION.
+ options.example2 = 0;
+ case 'unit_getOptions_testFunction'
+ % Str: DESCRIPTION.
+ options.example1 = '';
+ % Binary: DESCRIPTION.
+ options.example2 = 0;
+ otherwise
+ options.error = 1;
+ end
+ catch err
+ display(repmat('@',1,7))
+ disp(getReport(err,'extended','hyperlinks','on'));
+ display(repmat('@',1,7))
+ options = [];
+ end
+end
\ No newline at end of file
diff --git a/unitTestGetOptions.m b/unitTestGetOptions.m
new file mode 100644
index 0000000..f8c842c
--- /dev/null
+++ b/unitTestGetOptions.m
@@ -0,0 +1,98 @@
+function optionsCheck = unitTestGetOptions(varargin)
+ % Unit test for getOptions.
+ % Will perform 4 tests. Using Name-Value vs. options input and with or without structure crawling (recursion) enabled.
+ % Biafra Ahanonu
+ % started: 2015.12.03
+ % inputs
+ %
+ % outputs
+ %
+
+ % changelog
+ %
+ % TODO
+ %
+ clc
+
+
+ disp(repmat('=',1,7))
+ disp('Default options')
+ opts = unit_getOptions_testFunction(1,0);
+ dispStruct(opts,'options',1);
+
+ recursionStr = {'OFF','ON'};
+ for recursionState = [0 1]
+ disp(repmat('=',1,7))
+ disp(['options test | recursion ' recursionStr{recursionState+1}])
+ % Show that inputing an options stuct with sub-struct returns entire
+ % sub-structs if user doesn't provide all sub-struct field names
+ optsCheck.check1 = 20;
+ optsCheck.check3 = 20;
+ optsCheck.secondaryOpts.check1 = 'USA USA USA';
+ optsCheck.secondaryOpts.check3 = 'AMERICA';
+ optsCheck.secondaryOpts.check2.go1.h1 = 'AMERICA';
+ optsCheck.secondaryOpts.check2.go1.h2 = 1776;
+ optsCheck.secondaryOpts.check2.go2.h1 = 'AMERICA';
+ optsCheck.secondaryOpts.check2.go2.h2 = 1776;
+ optsCheck.secondaryOpts.tertiaryOptions.check1 = 'The rest of the world';
+ opts = unit_getOptions_testFunction(recursionState,0,'options',optsCheck,1);
+ dispStruct(opts,'options',1);
+ end
+
+ for recursionState = [0 1]
+ disp(repmat('=',1,7))
+ disp(['name-value struct test | recursion ' recursionStr{recursionState+1}])
+ % Show that inputing struct as a name-value argument returns all that
+ % sub-structs values
+ secondaryOpts.check3 = 'America';
+ secondaryOpts.tertiaryOpts.check1 = 'USA USA USA';
+ secondaryOpts.tertiaryOpts.check3 = 'America';
+ secondaryOpts.tertiaryOpts.moreOpts.check1 = 'USA USA USA';
+ secondaryOpts.tertiaryOpts.moreOpts.check3 = 'America';
+ opts = unit_getOptions_testFunction(recursionState,0,'secondaryOptions',secondaryOpts);
+ dispStruct(opts,'options',1);
+ end
+
+ for recursionState = [0 1]
+ disp(repmat('=',1,7))
+ disp(['Default options using getSettings, should see warnings | name-value struct test | recursion ' recursionStr{recursionState+1}])
+ % Show that inputing struct as a name-value argument returns all that
+ % sub-structs values
+ secondaryOpts.check3 = 'America';
+ secondaryOpts.tertiaryOpts.check1 = 'USA USA USA';
+ secondaryOpts.tertiaryOpts.check3 = 'America';
+ secondaryOpts.tertiaryOpts.moreOpts.check1 = 'USA USA USA';
+ secondaryOpts.tertiaryOpts.moreOpts.check3 = 'America';
+ opts = unit_getOptions_testFunction(recursionState,1,'options',optsCheck);
+ dispStruct(opts,'options',1);
+ end
+end
+function opts = unit_getOptions_testFunction(arg1,arg2,varargin)
+ % default options
+ opts.check1 = 1;
+ opts.check2 = 1;
+ opts.secondaryOpts.check1 = 1;
+ opts.secondaryOpts.check2.go1 = struct;
+ opts.secondaryOpts.check2.go2 = 'USA USA USA';
+ opts.secondaryOpts.quadOpts.check1 = 1;
+ opts.secondaryOpts.quadOpts.check2 = {1,2};
+ opts.secondaryOpts.tertiaryOpts.check1 = 1;
+ opts.secondaryOpts.tertiaryOpts.check2 = 1;
+ opts.secondaryOpts.tertiaryOpts.moreOpts.check1 = 1;
+ opts.secondaryOpts.tertiaryOpts.moreOpts.check2 = 1;
+ opts = getOptions(opts,varargin,'recursiveStructs',arg1,'getFunctionDefaults',arg2);
+end
+function dispStruct(iStruct,iField,lvlNum)
+ if isstruct(iStruct)
+ disp(['' iField ''])
+ disp(iStruct)
+ fprintf('\b')
+ nameList = fieldnames(iStruct);
+ for i = 1:length(nameList)
+ if isstruct(iStruct.(nameList{i}))
+ dispStruct(iStruct.(nameList{i}),nameList{i},lvlNum+1);
+ end
+ end
+ else
+ end
+end
\ No newline at end of file