index.js
function (expected, args, minimum) {
   var shiftedArgs = [];
   var curArg = args.length - 1;
   var remainingOptionalArgs = expected.length - minimum;
   var optionalIndiceSegments = [];
   var optionalIndiceSegment = [];
   var availableArgsSegments = [];
   var availableArgsSegment = [];

   // Fill the return array with nulls first.
   for (var i = 0; i < expected.length; i++) shiftedArgs[i] = null;

   // Capture groups of available arguments separated by ones that have been used.
   var advanceArg = function () {
      availableArgsSegment.unshift(curArg);
      curArg--;
      remainingOptionalArgs--;
      if (curArg < 0 || remainingOptionalArgs < 0) {
         throw Error(getExpectedVsRecieved_(expected, args));
      }
   };

   // Fill in all of the required types, starting from the last expected argument and working
   // towards the first.
   for (i = expected.length - 1; i >= 0; i--) {
      var type = expected[i];
      if (isOptionalType(type)) {
         optionalIndiceSegment.unshift(i);
         continue;
      }
      // Keep moving down the line of arguments until one matches.
      while (!isOfType(args[curArg], type)) {
         advanceArg();
      }

      // Check if this argument should be left for a trailing optional argument.
      if (checkIfShouldLeaveArgument_(expected, i, args, curArg)) {
         // Found enough matches to let this be an optional argument. Advance the argument and
         // then restart on this same function.
         advanceArg();
         i++;
         continue;
      }

      // Capture groups of optional arguments separated by required arguments.
      optionalIndiceSegments.unshift(optionalIndiceSegment);
      optionalIndiceSegment = [];
      availableArgsSegments.unshift(availableArgsSegment);
      availableArgsSegment = []
      shiftedArgs[i] = args[curArg--];
   }
   // Now that we have found all the required arguments, group the rest for processing with optional
   // arguments.
   while (curArg >= 0) availableArgsSegment.unshift(curArg--);
   availableArgsSegments.unshift(availableArgsSegment);
   optionalIndiceSegments.unshift(optionalIndiceSegment);

   // Make sure that the optional argument count matches up correctly.
   if (availableArgsSegments.length != optionalIndiceSegments.length) {
      throw Error(getExpectedVsRecieved_(expected, args));
   }

   // Go through all the optional segments and argument segments to match up the optional arguments.
   optionalIndiceSegments.forEach(function (optionalIndices, index) {
      availableArgsSegment = availableArgsSegments[index];
      i = 0;
      availableArgsSegment.forEach(function (argIndex) {
         arg = args[argIndex]
         // Skip forward until we find an optional expected argument that matches.
         while (!isOfType(arg, expected[optionalIndices[i]]) && i < optionalIndices.length) {
            i++;
         }
         // If none match then the arguments are invalid.
         if (i >= optionalIndices.length) {
            throw Error(getExpectedVsRecieved_(expected, args));
         }
         // Success! This is an optional expected argument.
         shiftedArgs[optionalIndices[i++]] = arg;
      });
   });

   return shiftedArgs;
}
function (expected, args, minimum) {
   var shiftedArgs = [];
   var curArg = args.length - 1;
   var remainingOptionalArgs = expected.length - minimum;
   var optionalIndiceSegments = [];
   var optionalIndiceSegment = [];
   var availableArgsSegments = [];
   var availableArgsSegment = [];

   // Fill the return array with nulls first.
   for (var i = 0; i < expected.length; i++) shiftedArgs[i] = null;

   // Capture groups of available arguments separated by ones that have been used.
   var advanceArg = function () {
      availableArgsSegment.unshift(curArg);
      curArg--;
      remainingOptionalArgs--;
      if (curArg < 0 || remainingOptionalArgs < 0) {
         throw Error(getExpectedVsRecieved_(expected, args));
      }
   };

   // Fill in all of the required types, starting from the last expected argument and working
   // towards the first.
   for (i = expected.length - 1; i >= 0; i--) {
      var type = expected[i];
      if (isOptionalType(type)) {
         optionalIndiceSegment.unshift(i);
         continue;
      }
      // Keep moving down the line of arguments until one matches.
      while (!isOfType(args[curArg], type)) {
         advanceArg();
      }

      // Check if this argument should be left for a trailing optional argument.
      if (checkIfShouldLeaveArgument_(expected, i, args, curArg)) {
         // Found enough matches to let this be an optional argument. Advance the argument and
         // then restart on this same function.
         advanceArg();
         i++;
         continue;
      }

      // Capture groups of optional arguments separated by required arguments.
      optionalIndiceSegments.unshift(optionalIndiceSegment);
      optionalIndiceSegment = [];
      availableArgsSegments.unshift(availableArgsSegment);
      availableArgsSegment = []
      shiftedArgs[i] = args[curArg--];
   }
   // Now that we have found all the required arguments, group the rest for processing with optional
   // arguments.
   while (curArg >= 0) availableArgsSegment.unshift(curArg--);
   availableArgsSegments.unshift(availableArgsSegment);
   optionalIndiceSegments.unshift(optionalIndiceSegment);

   // Make sure that the optional argument count matches up correctly.
   if (availableArgsSegments.length != optionalIndiceSegments.length) {
      throw Error(getExpectedVsRecieved_(expected, args));
   }

   // Go through all the optional segments and argument segments to match up the optional arguments.
   optionalIndiceSegments.forEach(function (optionalIndices, index) {
      availableArgsSegment = availableArgsSegments[index];
      i = 0;
      availableArgsSegment.forEach(function (argIndex) {
         arg = args[argIndex]
         // Skip forward until we find an optional expected argument that matches.
         while (!isOfType(arg, expected[optionalIndices[i]]) && i < optionalIndices.length) {
            i++;
         }
         // If none match then the arguments are invalid.
         if (i >= optionalIndices.length) {
            throw Error(getExpectedVsRecieved_(expected, args));
         }
         // Success! This is an optional expected argument.
         shiftedArgs[optionalIndices[i++]] = arg;
      });
   });

   return shiftedArgs;
}
Returns an array of all of the arguments shifted into the correct place.
@param {Array} expected
@param {Array} actual
@returns {Array}
@private