text: /**
   * class ArgumentParser
   * Object for parsing command line strings into js objects.
   * Inherited from [[ActionContainer]]
   **/
  'use strict';
  
  var util    = require('util');
  var format  = require('util').format;
  var Path    = require('path');
  var sprintf = require('sprintf-js').sprintf;
  
  // Constants
  var c  = require('./const');
  var $$ = require('./utils');
  var ActionContainer = require('./action_container');
  
  // Errors
  var argumentErrorHelper = require('./argument/error');
  var HelpFormatter = require('./help/formatter');
  var Namespace = require('./namespace');
  
  /**
   * new ArgumentParser(options)
   *
   * Create a new ArgumentParser object.
   *
   * ##### Options:
   * - `prog`                The name of the program (default: Path.basename(process.argv[1]))
   * - `usage`               A usage message (default: auto-generated from arguments)
   * - `description`         A description of what the program does
   * - `epilog`              Text following the argument descriptions
   * - `parents`             Parsers whose arguments should be copied into this one
   * - `formatterClass`      HelpFormatter class for printing help messages
   * - `prefixChars`         Characters that prefix optional arguments
   * - `fromfilePrefixChars` Characters that prefix files containing additional arguments
   * - `argumentDefault`     The default value for all arguments
   * - `addHelp`             Add a -h/-help option
   * - `conflictHandler`     Specifies how to handle conflicting argument names
   * - `debug`               Enable debug mode. Argument errors throw exception in
   *   debug mode and process.exit in normal. Used for development and
   *   testing (default: false)
   *
   * See also [original guide][1]
   *
   * [1]:http://docs.python.org/dev/library/argparse.html#argumentparser-objects
   **/
  function ArgumentParser(options) {
    if (!(this instanceof ArgumentParser)) {
      return new ArgumentParser(options);
    }
    var self = this;
    options  = options || {};
  
    options.description     = (options.description     || null);
    options.argumentDefault = (options.argumentDefault || null);
    options.prefixChars     = (options.prefixChars     || '-');
    options.conflictHandler = (options.conflictHandler || 'error');
    ActionContainer.call(this, options);
  
    options.addHelp = typeof options.addHelp === 'undefined' || !!options.addHelp;
    options.parents = options.parents || [];
    // default program name
    options.prog = (options.prog || Path.basename(process.argv[1]));
    this.prog    = options.prog;
    this.usage   = options.usage;
    this.epilog  = options.epilog;
    this.version = options.version;
  
    this.debug = (options.debug === true);
  
    this.formatterClass = (options.formatterClass || HelpFormatter);
    this.fromfilePrefixChars = options.fromfilePrefixChars || null;
    this._positionals = this.addArgumentGroup({ title: 'Positional arguments' });
    this._optionals   = this.addArgumentGroup({ title: 'Optional arguments' });
    this._subparsers  = null;
  
    // register types
    function FUNCTION_IDENTITY(o) {
      return o;
    }
    this.register('type', 'auto', FUNCTION_IDENTITY);
    this.register('type', null, FUNCTION_IDENTITY);
    this.register('type', 'int', function (x) {
      var result = parseInt(x, 10);
      if (isNaN(result)) {
        throw new Error(x + ' is not a valid integer.');
      }
      return result;
    });
    this.register('type', 'float', function (x) {
      var result = parseFloat(x);
      if (isNaN(result)) {
        throw new Error(x + ' is not a valid float.');
      }
      return result;
    });
    this.register('type', 'string', function (x) {
      return '' + x;
    });
  
    // add help and version arguments if necessary
    var defaultPrefix = (this.prefixChars.indexOf('-') > -1) ? '-' : this.prefixChars[0];
    if (options.addHelp) {
      this.addArgument(
        [defaultPrefix + 'h', defaultPrefix + defaultPrefix + 'help'],
        {
          action: 'help',
          defaultValue: c.SUPPRESS,
          help: 'Show this help message and exit.'
        }
      );
    }
    if (typeof this.version !== 'undefined') {
      this.addArgument(
        [defaultPrefix + 'v', defaultPrefix + defaultPrefix + 'version'],
        {
          action: 'version',
          version: this.version,
          defaultValue: c.SUPPRESS,
          help: "Show program's version number and exit."
        }
      );
    }
  
    // add parent arguments and defaults
    options.parents.forEach(function (parent) {
      self._addContainerActions(parent);
      if (typeof parent._defaults !== 'undefined') {
        for (var defaultKey in parent._defaults) {
          if (parent._defaults.hasOwnProperty(defaultKey)) {
            self._defaults[defaultKey] = parent._defaults[defaultKey];
          }
        }
      }
    });
  }
  
  util.inherits(ArgumentParser, ActionContainer);
  
  /**
   * ArgumentParser#addSubparsers(options) -> [[ActionSubparsers]]
   * - options (object): hash of options see [[ActionSubparsers.new]]
   *
   * See also [subcommands][1]
   *
   * [1]:http://docs.python.org/dev/library/argparse.html#sub-commands
   **/
  ArgumentParser.prototype.addSubparsers = function (options) {
    if (this._subparsers) {
      this.error('Cannot have multiple subparser arguments.');
    }
  
    options = options || {};
    options.debug = (this.debug === true);
    options.optionStrings = [];
    options.parserClass = (options.parserClass || ArgumentParser);
  
    if (!!options.title || !!options.description) {
  
      this._subparsers = this.addArgumentGroup({
        title: (options.title || 'subcommands'),
        description: options.description
      });
      delete options.title;
      delete options.description;
  
    } else {
      this._subparsers = this._positionals;
    }
  
    // prog defaults to the usage message of this parser, skipping
    // optional arguments and with no "usage:" prefix
    if (!options.prog) {
      var formatter = this._getFormatter();
      var positionals = this._getPositionalActions();
      var groups = this._mutuallyExclusiveGroups;
      formatter.addUsage(this.usage, positionals, groups, '');
      options.prog = formatter.formatHelp().trim();
    }
  
    // create the parsers action and add it to the positionals list
    var ParsersClass = this._popActionClass(options, 'parsers');
    var action = new ParsersClass(options);
    this._subparsers._addAction(action);
  
    // return the created parsers action
    return action;
  };
  
  ArgumentParser.prototype._addAction = function (action) {
    if (action.isOptional()) {
      this._optionals._addAction(action);
    } else {
      this._positionals._addAction(action);
    }
    return action;
  };
  
  ArgumentParser.prototype._getOptionalActions = function () {
    return this._actions.filter(function (action) {
      return action.isOptional();
    });
  };
  
  ArgumentParser.prototype._getPositionalActions = function () {
    return this._actions.filter(function (action) {
      return action.isPositional();
    });
  };
  
  /**
   * ArgumentParser#parseArgs(args, namespace) -> Namespace|Object
   * - args (array): input elements
   * - namespace (Namespace|Object): result object
   *
   * Parsed args and throws error if some arguments are not recognized
   *
   * See also [original guide][1]
   *
   * [1]:http://docs.python.org/dev/library/argparse.html#the-parse-args-method
   **/
  ArgumentParser.prototype.parseArgs = function (args, namespace) {
    var argv;
    var result = this.parseKnownArgs(args, namespace);
  
    args = result[0];
    argv = result[1];
    if (argv && argv.length > 0) {
      this.error(format('Unrecognized arguments: %s.', argv.join(' ')));
    }
    return args;
  };
  
  /**
   * ArgumentParser#parseKnownArgs(args, namespace) -> array
   * - args (array): input options
   * - namespace (Namespace|Object): result object
   *
   * Parse known arguments and return tuple of result object
   * and unknown args
   *
   * See also [original guide][1]
   *
   * [1]:http://docs.python.org/dev/library/argparse.html#partial-parsing
   **/
  ArgumentParser.prototype.parseKnownArgs = function (args, namespace) {
    var self = this;
  
    // args default to the system args
    args = args || process.argv.slice(2);
  
    // default Namespace built from parser defaults
    namespace = namespace || new Namespace();
  
    self._actions.forEach(function (action) {
      if (action.dest !== c.SUPPRESS) {
        if (!$$.has(namespace, action.dest)) {
          if (action.defaultValue !== c.SUPPRESS) {
            var defaultValue = action.defaultValue;
            if (typeof action.defaultValue === 'string') {
              defaultValue = self._getValue(action, defaultValue);
            }
            namespace[action.dest] = defaultValue;
          }
        }
      }
    });
  
    Object.keys(self._defaults).forEach(function (dest) {
      namespace[dest] = self._defaults[dest];
    });
  
    // parse the arguments and exit if there are any errors
    try {
      var res = this._parseKnownArgs(args, namespace);
  
      namespace = res[0];
      args = res[1];
      if ($$.has(namespace, c._UNRECOGNIZED_ARGS_ATTR)) {
        args = $$.arrayUnion(args, namespace[c._UNRECOGNIZED_ARGS_ATTR]);
        delete namespace[c._UNRECOGNIZED_ARGS_ATTR];
      }
      return [namespace, args];
    } catch (e) {
      this.error(e);
    }
  };
  
  ArgumentParser.prototype._parseKnownArgs = function (argStrings, namespace) {
    var self = this;
    var extras = [];
  
    // replace arg strings that are file references
    if (this.fromfilePrefixChars !== null) {
      argStrings = this._readArgsFromFiles(argStrings);
    }
    // map all mutually exclusive arguments to the other arguments
    // they can't occur with
    // Python has 'conflicts = action_conflicts.setdefault(mutex_action, [])'
    // though I can't conceive of a way in which an action could be a member
    // of two different mutually exclusive groups.
  
    function actionHash(action) {
      // some sort of hashable key for this action
      // action itself cannot be a key in actionConflicts
      // I think getName() (join of optionStrings) is unique enough
      return action.getName();
    }
  
    var conflicts, key;
    var actionConflicts = {};
  
    this._mutuallyExclusiveGroups.forEach(function (mutexGroup) {
      mutexGroup._groupActions.forEach(function (mutexAction, i, groupActions) {
        key = actionHash(mutexAction);
        if (!$$.has(actionConflicts, key)) {
          actionConflicts[key] = [];
        }
        conflicts = actionConflicts[key];
        conflicts.push.apply(conflicts, groupActions.slice(0, i));
        conflicts.push.apply(conflicts, groupActions.slice(i + 1));
      });
    });
  
    // find all option indices, and determine the arg_string_pattern
    // which has an 'O' if there is an option at an index,
    // an 'A' if there is an argument, or a '-' if there is a '--'
    var optionStringIndices = {};
  
    var argStringPatternParts = [];
    // var counter = 1;
    argStrings.forEach(function (argString, argStringIndex) {
      if (argString === '--') {
        argStringPatternParts.push('-');
        while (argStringIndex < argStrings.length) {
          argStringPatternParts.push('A');
          argStringIndex++;
        }
      } else {
        // otherwise, add the arg to the arg strings
        // and note the index if it was an option
        var pattern;
        var optionTuple = self._parseOptional(argString);
        if (!optionTuple) {
          pattern = 'A';
        } else {
          optionStringIndices[argStringIndex] = optionTuple;
          pattern = 'O';
        }
        argStringPatternParts.push(pattern);
      }
      /* used for \test\fromfile.js
      for (var property in optionStringIndices) {
        if (optionStringIndices.hasOwnProperty(property)) {
          console.log(
            '    * argument_parser #356 optionStringIndices property: ',
            property,
            ',\n    value:\n    ',
            optionStringIndices[property]
          );
        }
      }
      console.log('-------------------------------------------------------------------- End of #356 ---');
      console.log('    * argument_parser #365 argStringPatternParts: "' +
        argStringPatternParts + '"' + ', counter: ' + counter
      );
      ++counter;
      console.log('-------------------------------------------------------------------- End of #365 ---');
      */
    });
  
    var argStringsPattern = argStringPatternParts.join('');
    var seenActions = [];
    var seenNonDefaultActions = [];
  
    function takeAction(action, argumentStrings, optionString) {
      seenActions.push(action);
      var argumentValues = self._getValues(action, argumentStrings);
  
      // error if this argument is not allowed with other previously
      // seen arguments, assuming that actions that use the default
      // value don't really count as "present"
      if (argumentValues !== action.defaultValue) {
        seenNonDefaultActions.push(action);
        if (actionConflicts[actionHash(action)]) {
          actionConflicts[actionHash(action)].forEach(function (actionConflict) {
            if (seenNonDefaultActions.indexOf(actionConflict) >= 0) {
              throw argumentErrorHelper(
                action,
                format('Not allowed with argument "%s".', actionConflict.getName())
              );
            }
          });
        }
      }
  
      if (argumentValues !== c.SUPPRESS) {
        action.call(self, namespace, argumentValues, optionString);
      }
    }
  
    function consumeOptional(startIndex) {
      // get the optional identified at this index
      var optionTuple  = optionStringIndices[startIndex];
      var action       = optionTuple[0];
      var optionString = optionTuple[1];
      var explicitArg  = optionTuple[2];
  
      // identify additional optionals in the same arg string
      // (e.g. -xyz is the same as -x -y -z if no args are required)
      var actionTuples = [];
  
      var args, argCount, start, stop;
  
      for (;;) {
        if (!action) {
          extras.push(argStrings[startIndex]);
          return startIndex + 1;
        }
        if (explicitArg) {
          argCount = self._matchArgument(action, 'A');
  
          // if the action is a single-dash option and takes no
          // arguments, try to parse more single-dash options out
          // of the tail of the option string
          var chars = self.prefixChars;
          if (argCount === 0 && chars.indexOf(optionString[1]) < 0) {
            actionTuples.push([action, [], optionString]);
            optionString = optionString[0] + explicitArg[0];
            var newExplicitArg = explicitArg.slice(1) || null;
            var optionalsMap = self._optionStringActions;
  
            if (Object.keys(optionalsMap).indexOf(optionString) >= 0) {
              action = optionalsMap[optionString];
              explicitArg = newExplicitArg;
            } else {
              throw argumentErrorHelper(action, sprintf('ignored explicit argument %r', explicitArg));
            }
          } else if (argCount === 1) {
            // if the action expect exactly one argument, we've
            // successfully matched the option; exit the loop
            stop = startIndex + 1;
            args = [explicitArg];
            actionTuples.push([action, args, optionString]);
            break;
          } else {
            // error if a double-dash option did not use the
            // explicit argument
            throw argumentErrorHelper(action, sprintf('ignored explicit argument %r', explicitArg));
          }
        } else {
          // if there is no explicit argument, try to match the
          // optional's string arguments with the following strings
          // if successful, exit the loop
  
          start = startIndex + 1;
          var selectedPatterns = argStringsPattern.substr(start);
  
          argCount = self._matchArgument(action, selectedPatterns);
          stop = start + argCount;
          args = argStrings.slice(start, stop);
  
          actionTuples.push([action, args, optionString]);
          break;
        }
      }
  
      // add the Optional to the list and return the index at which
      // the Optional's string args stopped
      if (actionTuples.length < 1) {
        throw new Error('length should be > 0');
      }
      for (var i = 0; i < actionTuples.length; i++) {
        takeAction.apply(self, actionTuples[i]);
      }
      return stop;
    }
  
    // the list of Positionals left to be parsed; this is modified
    // by consume_positionals()
    var positionals = self._getPositionalActions();
  
    function consumePositionals(startIndex) {
      // match as many Positionals as possible
      var selectedPattern = argStringsPattern.substr(startIndex);
      var argCounts = self._matchArgumentsPartial(positionals, selectedPattern);
  
      // slice off the appropriate arg strings for each Positional
      // and add the Positional and its args to the list
      for (var i = 0; i < positionals.length; i++) {
        var action = positionals[i];
        var argCount = argCounts[i];
        if (typeof argCount === 'undefined') {
          continue;
        }
        var args = argStrings.slice(startIndex, startIndex + argCount);
  
        startIndex += argCount;
        takeAction(action, args);
      }
  
      // slice off the Positionals that we just parsed and return the
      // index at which the Positionals' string args stopped
      positionals = positionals.slice(argCounts.length);
      return startIndex;
    }
  
    // consume Positionals and Optionals alternately, until we have
    // passed the last option string
    var startIndex = 0;
    var position;
  
    var maxOptionStringIndex = -1;
  
    Object.keys(optionStringIndices).forEach(function (position) {
      maxOptionStringIndex = Math.max(maxOptionStringIndex, parseInt(position, 10));
    });
  
    var positionalsEndIndex, nextOptionStringIndex;
  
    while (startIndex <= maxOptionStringIndex) {
      // consume any Positionals preceding the next option
      nextOptionStringIndex = null;
      for (position in optionStringIndices) {
        if (!optionStringIndices.hasOwnProperty(position)) { continue; }
  
        position = parseInt(position, 10);
        if (position >= startIndex) {
          if (nextOptionStringIndex !== null) {
            nextOptionStringIndex = Math.min(nextOptionStringIndex, position);
          } else {
            nextOptionStringIndex = position;
          }
        }
        /*
        if (optionStringIndices.hasOwnProperty(position)) {
          console.log(
            '    * startIndex <= maxOptionStringIndex #538 optionStringIndices property: ',
            position,
            ',\n    value:\n    ',
            optionStringIndices[position]
          );
        }
        console.log('-------------------------------------------------------------------- End of #538 ---'); */
      }
  
      if (startIndex !== nextOptionStringIndex) {
        positionalsEndIndex = consumePositionals(startIndex);
        // only try to parse the next optional if we didn't consume
        // the option string during the positionals parsing
        if (positionalsEndIndex > startIndex) {
          startIndex = positionalsEndIndex;
          continue;
        } else {
          startIndex = positionalsEndIndex;
        }
      }
  
      // if we consumed all the positionals we could and we're not
      // at the index of an option string, there were extra arguments
      if (!optionStringIndices[startIndex]) {
        var strings = argStrings.slice(startIndex, nextOptionStringIndex);
        extras = extras.concat(strings);
        startIndex = nextOptionStringIndex;
      }
      // consume the next optional and any arguments for it
      startIndex = consumeOptional(startIndex);
    }
  
    // consume any positionals following the last Optional
    var stopIndex = consumePositionals(startIndex);
  
    // if we didn't consume all the argument strings, there were extras
    extras = extras.concat(argStrings.slice(stopIndex));
  
    // if we didn't use all the Positional objects, there were too few
    // arg strings supplied.
    if (positionals.length > 0) {
      self.error('too few arguments');
    }
  
    // make sure all required actions were present
    self._actions.forEach(function (action) {
      if (action.required) {
        if (seenActions.indexOf(action) < 0) {
          self.error(format('Argument "%s" is required', action.getName()));
        }
      }
    });
  
    // make sure all required groups have one option present
    var actionUsed = false;
    self._mutuallyExclusiveGroups.forEach(function (group) {
      if (group.required) {
        actionUsed = group._groupActions.some(function (action) {
          return seenNonDefaultActions.indexOf(action) !== -1;
        });
  
        // if no actions were used, report the error
        if (!actionUsed) {
          var names = [];
          group._groupActions.forEach(function (action) {
            if (action.help !== c.SUPPRESS) {
              names.push(action.getName());
            }
          });
          names = names.join(' ');
          var msg = 'one of the arguments ' + names + ' is required';
          self.error(msg);
        }
      }
    });
  
    // return the updated namespace and the extra arguments
    return [namespace, extras];
  };
  
  ArgumentParser.prototype._readArgsFromFiles = function (argStrings) {
    // expand arguments referencing files
    var self = this;
    var fs = require('fs');
    var newArgStrings = [];
    argStrings.forEach(function (argString) {
      if (self.fromfilePrefixChars.indexOf(argString[0]) < 0) {
        // for regular arguments, just add them back into the list
        newArgStrings.push(argString);
      } else {
        // replace arguments referencing files with the file content
        try {
          var argstrs  = [];
          var filename = argString.slice(1);
          var content  = fs.readFileSync(filename, 'utf8');
          content = content.trim().split('\n');
          content.forEach(function (argLine) {
            self.convertArgLineToArgs(argLine).forEach(function (arg) {
              argstrs.push(arg);
            });
            argstrs = self._readArgsFromFiles(argstrs);
          });
          newArgStrings.push.apply(newArgStrings, argstrs);
        } catch (error) {
          return self.error(error.message);
        }
      }
    });
    return newArgStrings;
  };
  
  ArgumentParser.prototype.convertArgLineToArgs = function (argLine) {
    return [argLine];
  };
  
  ArgumentParser.prototype._matchArgument = function (action, regexpArgStrings) {
  
    // match the pattern for this action to the arg strings
    var regexpNargs = new RegExp('^' + this._getNargsPattern(action));
    var matches = regexpArgStrings.match(regexpNargs);
    var message;
  
    // throw an exception if we weren't able to find a match
    if (!matches) {
      switch (action.nargs) {
        /*eslint-disable no-undefined*/
        case undefined:
        case null:
          message = 'Expected one argument.';
          break;
        case c.OPTIONAL:
          message = 'Expected at most one argument.';
          break;
        case c.ONE_OR_MORE:
          message = 'Expected at least one argument.';
          break;
        default:
          message = 'Expected %s argument(s)';
      }
      throw argumentErrorHelper(action, format(message, action.nargs));
    }
    // return the number of arguments matched
    return matches[1].length;
  };
  
  ArgumentParser.prototype._matchArgumentsPartial = function (actions, regexpArgStrings) {
    // progressively shorten the actions list by slicing off the
    // final actions until we find a match
    var self = this;
    var result = [];
    var actionSlice, pattern, matches;
    var i, j;
  
    function getLength(string) {
      return string.length;
    }
  
    for (i = actions.length; i > 0; i--) {
      pattern = '';
      actionSlice = actions.slice(0, i);
      for (j = 0; j < actionSlice.length; j++) {
        pattern += self._getNargsPattern(actionSlice[j]);
      }
  
      pattern = new RegExp('^' + pattern);
      matches = regexpArgStrings.match(pattern);
  
      if (matches && matches.length > 0) {
        // need only groups
        matches = matches.splice(1);
        result = result.concat(matches.map(getLength));
        break;
      }
    }
  
    // return the list of arg string counts
    return result;
  };
  
  ArgumentParser.prototype._parseOptional = function (argString) {
    var action, optionString, argExplicit, optionTuples;
  
    // if it's an empty string, it was meant to be a positional
    if (!argString) {
      return null;
    }
  
    // if it doesn't start with a prefix, it was meant to be positional
    if (this.prefixChars.indexOf(argString[0]) < 0) {
      return null;
    }
  
    // if the option string is present in the parser, return the action
    if (this._optionStringActions[argString]) {
      return [this._optionStringActions[argString], argString, null];
    }
  
    // if it's just a single character, it was meant to be positional
    if (argString.length === 1) {
      return null;
    }
  
    // if the option string before the "=" is present, return the action
    if (argString.indexOf('=') >= 0) {
      optionString = argString.split('=', 1)[0];
      argExplicit = argString.slice(optionString.length + 1);
  
      if (this._optionStringActions[optionString]) {
        action = this._optionStringActions[optionString];
        return [action, optionString, argExplicit];
      }
    }
  
    // search through all possible prefixes of the option string
    // and all actions in the parser for possible interpretations
    optionTuples = this._getOptionTuples(argString);
  
    // if multiple actions match, the option string was ambiguous
    if (optionTuples.length > 1) {
      var optionStrings = optionTuples.map(function (optionTuple) {
        return optionTuple[1];
      });
      this.error(format(
        'Ambiguous option: "%s" could match %s.',
        argString,
        optionStrings.join(', ')
      ));
    // if exactly one action matched, this segmentation is good,
    // so return the parsed action
    } else if (optionTuples.length === 1) {
      return optionTuples[0];
    }
  
    // if it was not found as an option, but it looks like a negative
    // number, it was meant to be positional
    // unless there are negative-number-like options
    if (argString.match(this._regexpNegativeNumber)) {
      if (!this._hasNegativeNumberOptionals.some(Boolean)) {
        return null;
      }
    }
    // if it contains a space, it was meant to be a positional
    if (argString.search(' ') >= 0) {
      return null;
    }
  
    // it was meant to be an optional but there is no such option
    // in this parser (though it might be a valid option in a subparser)
    return [null, argString, null];
  };
  
  ArgumentParser.prototype._getOptionTuples = function (optionString) {
    var result = [];
    var chars = this.prefixChars;
    var optionPrefix;
    var argExplicit;
    var action;
    var actionOptionString;
  
    // option strings starting with two prefix characters are only split at
    // the '='
    if (chars.indexOf(optionString[0]) >= 0 && chars.indexOf(optionString[1]) >= 0) {
      if (optionString.indexOf('=') >= 0) {
        var optionStringSplit = optionString.split('=', 1);
  
        optionPrefix = optionStringSplit[0];
        argExplicit  = optionStringSplit[1];
      } else {
        optionPrefix = optionString;
        argExplicit  = null;
      }
  
      for (actionOptionString in this._optionStringActions) {
        if (actionOptionString.substr(0, optionPrefix.length) === optionPrefix) {
          action = this._optionStringActions[actionOptionString];
          /* console.log('#814 action: ' + action + ', actionOptionString: ' +
            actionOptionString + ', argExplicit: ' + argExplicit
          );
          console.log('-------------------------------------------------------------------- End of #814 ---'); */
          result.push([action, actionOptionString, argExplicit]);
        } /*
        if (this._optionStringActions.hasOwnProperty(actionOptionString)) {
          console.log(
            '    * _getOptionTuples #821 this._optionStringActions property: ',
            actionOptionString,
            ',\n    value:\n    ',
            this._optionStringActions[actionOptionString]
          );
          console.log('-------------------------------------------------------------------- End of #821 ---');
        } */
      }
  
    // single character options can be concatenated with their arguments
    // but multiple character options always have to have their argument
    // separate
    } else if (chars.indexOf(optionString[0]) >= 0 && chars.indexOf(optionString[1]) < 0) {
      optionPrefix = optionString;
      argExplicit = null;
      var optionPrefixShort = optionString.substr(0, 2);
      var argExplicitShort = optionString.substr(2);
  
      for (actionOptionString in this._optionStringActions) {
        if (!$$.has(this._optionStringActions, actionOptionString)) { continue; }
  
        action = this._optionStringActions[actionOptionString];
        if (actionOptionString === optionPrefixShort) {
          /* console.log('#845 action: ' + action + ', actionOptionString: ' +
            actionOptionString + ', argExplicitShort: ' + argExplicitShort
          );
          console.log('-------------------------------------------------------------------- End of #845 ---'); */
          result.push([action, actionOptionString, argExplicitShort]);
        } else if (actionOptionString.substr(0, optionPrefix.length) === optionPrefix) {
          /* console.log('#851 action: ' + action + ', actionOptionString: ' +
            actionOptionString + ', argExplicit: ' + argExplicit
          );
          console.log('-------------------------------------------------------------------- End of #851 ---'); */
          result.push([action, actionOptionString, argExplicit]);
        } /*
        if (this._optionStringActions.hasOwnProperty(actionOptionString)) {
          console.log(
            '    * _getOptionTuples #858 this._optionStringActions property: ',
            actionOptionString,
            ',\n    value:\n    ',
            this._optionStringActions[actionOptionString]
          );
          console.log('-------------------------------------------------------------------- End of #858 ---');
        } */
      }
  
    // shouldn't ever get here
    } else {
      throw new Error(format('Unexpected option string: %s.', optionString));
    }
    // return the collected option tuples
    return result;
  };
  
  ArgumentParser.prototype._getNargsPattern = function (action) {
    // in all examples below, we have to allow for '--' args
    // which are represented as '-' in the pattern
    var regexpNargs;
  
    switch (action.nargs) {
      // the default (null) is assumed to be a single argument
      case undefined:
      case null:
        regexpNargs = '(-*A-*)';
        break;
      // allow zero or more arguments
      case c.OPTIONAL:
        regexpNargs = '(-*A?-*)';
        break;
      // allow zero or more arguments
      case c.ZERO_OR_MORE:
        regexpNargs = '(-*[A-]*)';
        break;
      // allow one or more arguments
      case c.ONE_OR_MORE:
        regexpNargs = '(-*A[A-]*)';
        break;
      // allow any number of options or arguments
      case c.REMAINDER:
        regexpNargs = '([-AO]*)';
        break;
      // allow one argument followed by any number of options or arguments
      case c.PARSER:
        regexpNargs = '(-*A[-AO]*)';
        break;
      // all others should be integers
      default:
        regexpNargs = '(-*' + $$.repeat('-*A', action.nargs) + '-*)';
        // console.log('#910 _getNargsPattern regexpNargs: ' + regexpNargs);
        // console.log('-------------------------------------------------------------------- End of #910 ---');
    }
  
    // if this is an optional action, -- is not allowed
    if (action.isOptional()) {
      regexpNargs = regexpNargs.replace(/-\*/g, '');
      regexpNargs = regexpNargs.replace(/-/g, '');
    }
  
    // return the pattern
    return regexpNargs;
  };
  
  /// Value conversion methods
  ArgumentParser.prototype._getValues = function (action, argStrings) {
    var self = this;
  
    // for everything but PARSER args, strip out '--'
    if (action.nargs !== c.PARSER && action.nargs !== c.REMAINDER) {
      argStrings = argStrings.filter(function (arrayElement) {
        return arrayElement !== '--';
      });
    }
  
    var value, argString;
  
    // optional argument produces a default when not present
    if (argStrings.length === 0 && action.nargs === c.OPTIONAL) {
  
      value = (action.isOptional()) ? action.constant : action.defaultValue;
  
      if (typeof (value) === 'string') {
        value = this._getValue(action, value);
        this._checkValue(action, value);
      }
  
    // when nargs='*' on a positional, if there were no command-line
    // args, use the default if it is anything other than None
    } else if (argStrings.length === 0 && action.nargs === c.ZERO_OR_MORE && action.optionStrings.length === 0) {
  
      value = (action.defaultValue || argStrings);
      this._checkValue(action, value);
  
    // single argument or optional argument produces a single value
    } else if (argStrings.length === 1 && (!action.nargs || action.nargs === c.OPTIONAL)) {
  
      argString = argStrings[0];
      value = this._getValue(action, argString);
      this._checkValue(action, value);
  
    // REMAINDER arguments convert all values, checking none
    } else if (action.nargs === c.REMAINDER) {
      value = argStrings.map(function (v) {
        return self._getValue(action, v);
      });
  
    // PARSER arguments convert all values, but check only the first
    } else if (action.nargs === c.PARSER) {
      value = argStrings.map(function (v) {
        return self._getValue(action, v);
      });
      this._checkValue(action, value[0]);
  
    // all other types of nargs produce a list
    } else {
      value = argStrings.map(function (v) {
        return self._getValue(action, v);
      });
      value.forEach(function (v) {
        self._checkValue(action, v);
      });
    }
  
    // return the converted value
    return value;
  };
  
  ArgumentParser.prototype._getValue = function (action, argString) {
    var result;
  
    var typeFunction = this._registryGet('type', action.type, action.type);
    if (typeof typeFunction !== 'function') {
      var message = format('%s is not callable', typeFunction);
      throw argumentErrorHelper(action, message);
    }
  
    // convert the value to the appropriate type
    try {
      result = typeFunction(argString);
  
      // ArgumentTypeErrors indicate errors
      // If action.type is not a registered string, it is a function
      // Try to deduce its name for inclusion in the error message
      // Failing that, include the error message it raised.
    } catch (e) {
      var name = null;
      if (typeof action.type === 'string') {
        name = action.type;
      } else {
        name = action.type.name || action.type.displayName || '<function>';
      }
      var msg = format('Invalid %s value: %s', name, argString);
      if (name === '<function>') { msg += '\n' + e.message; }
      throw argumentErrorHelper(action, msg);
    }
    // return the converted value
    return result;
  };
  
  ArgumentParser.prototype._checkValue = function (action, value) {
    // converted value must be one of the choices (if specified)
    var choices = action.choices;
    if (choices) {
      // choise for argument can by array or string
      if ((typeof choices === 'string' || Array.isArray(choices)) && choices.indexOf(value) !== -1) {
        return;
      }
      // choise for subparsers can by only hash
      if (typeof choices === 'object' && !Array.isArray(choices) && choices[value]) {
        return;
      }
  
      if (typeof choices === 'string') {
        choices = choices.split('').join(', ');
      } else if (Array.isArray(choices)) {
        choices = choices.join(', ');
      } else {
        choices = Object.keys(choices).join(', ');
      }
      var message = format('Invalid choice: %s (choose from [%s])', value, choices);
      throw argumentErrorHelper(action, message);
    }
  };
  
  /// Help formatting methods
  /**
   * ArgumentParser#formatUsage -> string
   *
   * Return usage string
   *
   * See also [original guide][1]
   *
   * [1]:http://docs.python.org/dev/library/argparse.html#printing-help
   **/
  ArgumentParser.prototype.formatUsage = function () {
    var formatter = this._getFormatter();
    formatter.addUsage(this.usage, this._actions, this._mutuallyExclusiveGroups);
    return formatter.formatHelp();
  };
  
  /**
   * ArgumentParser#formatHelp -> string
   *
   * Return help
   *
   * See also [original guide][1]
   *
   * [1]:http://docs.python.org/dev/library/argparse.html#printing-help
   **/
  ArgumentParser.prototype.formatHelp = function () {
    var formatter = this._getFormatter();
  
    // usage
    formatter.addUsage(this.usage, this._actions, this._mutuallyExclusiveGroups);
  
    // description
    formatter.addText(this.description);
  
    // positionals, optionals and user-defined groups
    this._actionGroups.forEach(function (actionGroup) {
      formatter.startSection(actionGroup.title);
      formatter.addText(actionGroup.description);
      formatter.addArguments(actionGroup._groupActions);
      formatter.endSection();
    });
  
    // epilog
    formatter.addText(this.epilog);
  
    // determine help from format above
    return formatter.formatHelp();
  };
  
  ArgumentParser.prototype._getFormatter = function () {
    var FormatterClass = this.formatterClass;
    var formatter = new FormatterClass({ prog: this.prog });
    return formatter;
  };
  
  ///  Print functions
  /**
   * ArgumentParser#printUsage() -> Void
   *
   * Print usage
   *
   * See also [original guide][1]
   *
   * [1]:http://docs.python.org/dev/library/argparse.html#printing-help
   **/
  ArgumentParser.prototype.printUsage = function () {
    this._printMessage(this.formatUsage());
  };
  
  /**
   * ArgumentParser#printHelp() -> Void
   *
   * Print help
   *
   * See also [original guide][1]
   *
   * [1]:http://docs.python.org/dev/library/argparse.html#printing-help
   **/
  ArgumentParser.prototype.printHelp = function () {
    this._printMessage(this.formatHelp());
  };
  
  ArgumentParser.prototype._printMessage = function (message, stream) {
    if (!stream) {
      stream = process.stdout;
    }
    if (message) {
      stream.write('' + message);
    }
  };
  
  ///  Exit functions
  /**
   * ArgumentParser#exit(status=0, message) -> Void
   * - status (int): exit status
   * - message (string): message
   *
   * Print message in stderr/stdout and exit program
   **/
  ArgumentParser.prototype.exit = function (status, message) {
    if (message) {
      if (status === 0) {
        this._printMessage(message);
      } else {
        this._printMessage(message, process.stderr);
      }
    }
    process.exit(status);
  };
  
  /**
   * ArgumentParser#error(message) -> Void
   * - err (Error|string): message
   *
   * Error method Prints a usage message incorporating the message to stderr and
   * exits. If you override this in a subclass,
   * it should not return -- it should
   * either exit or throw an exception.
   *
   **/
  ArgumentParser.prototype.error = function (err) {
    var message;
    if (err instanceof Error) {
      if (this.debug === true) {
        throw err;
      }
      message = err.message;
    } else {
      message = err;
    }
    var msg = format('%s: error: %s', this.prog, message) + c.EOL;
  
    if (this.debug === true) {
      throw new Error(msg);
    }
  
    this.printUsage(process.stderr);
    return this.exit(2, msg);
  };
  
  module.exports = ArgumentParser;
  