[Pkg-javascript-commits] [node-braces] 01/01: New upstream version 2.3.0

Jérémy Lal kapouer at moszumanska.debian.org
Fri Dec 29 14:35:06 UTC 2017


This is an automated email from the git hooks/post-receive script.

kapouer pushed a commit to annotated tag upstream/2.3.0
in repository node-braces.

commit a3fbcb85e0b1f44c3b5d5ec0c74445bef159e9b1
Author: Jérémy Lal <kapouer at melix.org>
Date:   Fri Dec 29 15:07:48 2017 +0100

    New upstream version 2.3.0
---
 .editorconfig                           |   9 +-
 .eslintrc.json                          |   5 -
 .gitignore                              |   9 +
 .travis.yml                             |   5 +-
 .verb.md                                | 418 ++++++++++++++++++-------
 LICENSE                                 |   2 +-
 README.md                               | 427 ++++++++++++++++---------
 appveyor.yml                            |   1 +
 benchmark/last.md                       |  48 +--
 examples.md                             |  60 ----
 examples/ast.js                         |  22 ++
 examples/braces.js                      |  13 +
 examples/expand.js                      |   8 +
 examples/extglobs.js                    |   4 +
 examples/{nested.js => nested-regex.js} |   0
 examples/nested.js                      |  57 +++-
 examples/options.quantifiers.js         |  15 +
 examples/paths.js                       |   6 +
 examples/readme.js                      |  28 ++
 examples/regex.js                       |   8 +-
 examples/sequences.js                   |   9 +
 index.js                                | 133 ++++----
 lib/braces.js                           |  42 +--
 lib/compilers.js                        |  50 +--
 lib/parsers.js                          | 324 +++++++++++++------
 lib/utils.js                            | 224 +++++++++-----
 package.json                            |  62 ++--
 test/bash.expanded.js                   |  20 +-
 test/bash.optimized.js                  |  13 +-
 test/bash.spec.js                       |   9 +-
 test/brace-expansion.js                 |  58 ++--
 test/braces.create.js                   |  21 ++
 test/braces.js                          | 110 ++++++-
 test/braces.makeRe.js                   |  21 ++
 test/expanded.integration.js            |  16 +-
 test/expanded.ranges.js                 | 222 ++++++-------
 test/expanded.sets.js                   | 238 +++++++-------
 test/multiples.js                       |   5 +-
 test/optimized.js                       | 424 ++++++++++++++-----------
 test/options.js                         |  75 +++--
 test/regression-1.8.5.js                | 530 ++++++++++++++++----------------
 test/support/generate.js                |  89 ++++--
 test/support/utils.js                   |   6 +-
 test/utils.js                           |  78 +++++
 44 files changed, 2455 insertions(+), 1469 deletions(-)

diff --git a/.editorconfig b/.editorconfig
index 818e072..449f0da 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -1,13 +1,14 @@
+# http://editorconfig.org/
 root = true
 
 [*]
-indent_style = space
-end_of_line = lf
 charset = utf-8
+end_of_line = lf
 indent_size = 2
-trim_trailing_whitespace = true
+indent_style = space
 insert_final_newline = true
+trim_trailing_whitespace = true
 
 [{**/{actual,fixtures,expected,templates}/**,*.md}]
 trim_trailing_whitespace = false
-insert_final_newline = false
\ No newline at end of file
+insert_final_newline = false
diff --git a/.eslintrc.json b/.eslintrc.json
index 948dbdb..61e8895 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -1,9 +1,4 @@
 {
-  "ecmaFeatures": {
-    "modules": true,
-    "experimentalObjectRestSpread": true
-  },
-
   "env": {
     "browser": false,
     "es6": true,
diff --git a/.gitignore b/.gitignore
index 13bb9ba..4bf0a60 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,20 +1,29 @@
 # always ignore files
 *.DS_Store
+.idea
 *.sublime-*
 
 # test related, or directories generated by tests
 test/actual
 actual
 coverage
+.nyc*
 
 # npm
 node_modules
 npm-debug.log
 
+# yarn
+yarn.lock
+yarn-error.log
+
 # misc
 _gh_pages
+_draft
+_drafts
 bower_components
 vendor
 temp
 tmp
 TODO.md
+package-lock.json
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
index 98b1311..67decb2 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,11 +1,14 @@
 sudo: false
-language: node_js
 os:
   - linux
   - osx
+language: node_js
 node_js:
   - node
+  - '8'
+  - '7'
   - '6'
   - '5'
+  - '4'
   - '0.12'
   - '0.10'
diff --git a/.verb.md b/.verb.md
index 97af4a3..43ee463 100644
--- a/.verb.md
+++ b/.verb.md
@@ -1,26 +1,31 @@
+## Why use braces?
+
+Brace patterns are great for matching ranges. Users (and implementors) shouldn't have to think about whether or not they will break their application (or yours) from accidentally defining an aggressive brace pattern. _Braces is the only library that offers a [solution to this problem](#performance)_.
+
+- **Safe(r)**: Braces isn't vulnerable to DoS attacks like [brace-expansion][], [minimatch][] and [multimatch][] (a different bug than the [other regex DoS bug][bug]). 
+- **Accurate**: complete support for the [Bash 4.3 Brace Expansion][bash] specification (passes all of the Bash braces tests)
+- **[fast and performant](#benchmarks)**: Starts fast, runs fast and [scales well](#performance) as patterns increase in complexity.
+- **Organized code base**: with parser and compiler that are eas(y|ier) to maintain and update when edge cases crop up.
+- **Well-tested**: thousands of test assertions. Passes 100% of the [minimatch][] and [brace-expansion][] unit tests as well (as of the writing of this). 
+
 ## Usage
 
-The main export is a function that takes a brace `pattern` to expand and an `options` object if necessary.
+The main export is a function that takes one or more brace `patterns` and `options`.
 
 ```js
-var braces = require('{%= name %}');
+var braces = require('braces');
 braces(pattern[, options]);
 ```
 
-## Highlights
+By default, braces returns an optimized regex-source string. To get an array of brace patterns, use `brace.expand()`. 
 
-- **Safer**: Braces isn't [vulnerable to DoS attacks](#braces-is-safe), unlike [brace-expansion][], [minimatch][] and [multimatch][] (this not the same [DoS bug][bug] that was found in minimatch and multimatch recently)
-- **Accurate**: complete support for the [Bash 4.3 Brace Expansion][bash] specification (passes all of the Bash braces tests)
-- **[fast and performant](#benchmarks)**: not only runs fast, but scales well as patterns increase in complexity (other libs don't - see the notes on [performance](#performance)). 
-- **Organized code base**: with parser and compiler that are easy to maintain and update when edge cases crop up.
-- **Well-tested**: 900+ unit tests with thousands of actual patterns tested. Passes 100% of the [minimatch][] and [brace-expansion][] unit tests as well.
-- **Optimized braces**: By default returns an optimized string that can be used for creating regular expressions for matching. 
-- **Expanded braces**: Optionally returns an array (like bash). See a [comparison](#optimized-vs-expanded) between optimized and expanded.
+The following section explains the difference in more detail. _(If you're curious about "why" braces does this by default, see [brace matching pitfalls](#brace-matching-pitfalls)_.
 
+### Optimized vs. expanded braces
 
 **Optimized**
 
-Patterns are optimized for regex and matching by default:
+By default, patterns are optimized for regex and matching:
 
 ```js
 console.log(braces('a/{x,y,z}/b'));
@@ -29,22 +34,27 @@ console.log(braces('a/{x,y,z}/b'));
 
 **Expanded**
 
-To expand patterns the same way as Bash or [minimatch][], use the [.expand](#expand) method:
+To expand patterns the same way as Bash or [minimatch](https://github.com/isaacs/minimatch), use the [.expand](#expand) method:
 
 ```js
 console.log(braces.expand('a/{x,y,z}/b'));
 //=> ['a/x/b', 'a/y/b', 'a/z/b']
 ```
 
+Or use [options.expand](#optionsexpand):
 
-## Features
+```js
+console.log(braces('a/{x,y,z}/b', {expand: true}));
+//=> ['a/x/b', 'a/y/b', 'a/z/b']
+```
 
-- [lists](#lists): Supports "lists": `a/{b,c}/d` => `['a/b/d', 'a/c/d']`
-- [sequences](#sequences): Supports alphabetical or numerical "sequences" (ranges): `{1..3}` => `['1', '2', '3']` 
-- [steps](#steps): Supports "steps" or increments: `{2..10..2}` => `['2', '4', '6', '8', '10']`
-- [escaping](#escaping)
-- [options](#options)
+## Features
 
+* [lists](#lists): Supports "lists": `a/{b,c}/d` => `['a/b/d', 'a/c/d']`
+* [sequences](#sequences): Supports alphabetical or numerical "sequences" (ranges): `{1..3}` => `['1', '2', '3']`
+* [steps](#steps): Supports "steps" or increments: `{2..10..2}` => `['2', '4', '6', '8', '10']`
+* [escaping](#escaping)
+* [options](#options)
 
 ### Lists
 
@@ -63,15 +73,15 @@ console.log(braces.expand('a/{foo,bar,baz}/*.js'));
 Uses [fill-range](https://github.com/jonschlinkert/fill-range) for expanding alphabetical or numeric ranges (bash "sequences"):
 
 ```js
-// padding is not retained when string is optimized
-console.log(braces('a{01..03}b')); // ['a([1-3])b']
-console.log(braces('a{1..3}b'));   // ['a([1-3])b']
-
 console.log(braces.expand('{1..3}'));     // ['1', '2', '3']
 console.log(braces.expand('a{01..03}b')); // ['a01b', 'a02b', 'a03b']
 console.log(braces.expand('a{1..3}b'));   // ['a1b', 'a2b', 'a3b']
 console.log(braces.expand('{a..c}'));     // ['a', 'b', 'c']
 console.log(braces.expand('foo/{a..c}')); // ['foo/a', 'foo/b', 'foo/c']
+
+// supports padded ranges
+console.log(braces('a{01..03}b'));   //=> [ 'a(0[1-3])b' ]
+console.log(braces('a{001..300}b')); //=> [ 'a(0{2}[1-9]|0[1-9][0-9]|[12][0-9]{2}|300)b' ]
 ```
 
 ### Steps
@@ -86,13 +96,13 @@ console.log(braces('{2..10..2}'));
 //=> ['(2|4|6|8|10)']
 ```
 
-When the [.optimize](#optimize) method is used, or [options.optimize](#optionsoptimize) is set to true, sequences are passed to [to-regex-range][] for expansion.
+When the [.optimize](#optimize) method is used, or [options.optimize](#optionsoptimize) is set to true, sequences are passed to [to-regex-range](https://github.com/jonschlinkert/to-regex-range) for expansion.
 
 ### Nesting
 
-Brace patterns may be nested. The results of each expanded string are not sorted, and left to right order is preserved. 
+Brace patterns may be nested. The results of each expanded string are not sorted, and left to right order is preserved.
 
-**"Expanded" examples**
+**"Expanded" braces**
 
 ```js
 console.log(braces.expand('a{b,c,/{x,y}}/e'));
@@ -102,7 +112,7 @@ console.log(braces.expand('a/{x,{1..5},y}/c'));
 //=> ['a/x/c', 'a/1/c', 'a/2/c', 'a/3/c', 'a/4/c', 'a/5/c', 'a/y/c']
 ```
 
-**"Optimized" examples**
+**"Optimized" braces**
 
 ```js
 console.log(braces('a{b,c,/{x,y}}/e'));
@@ -116,7 +126,7 @@ console.log(braces('a/{x,{1..5},y}/c'));
 
 **Escaping braces**
 
-Prevent braces from being expanded or evaluted by escaping either the opening or closing brace:
+A brace pattern will not be expanded or evaluted if _either the opening or closing brace is escaped_:
 
 ```js
 console.log(braces.expand('a\\{d,c,b}e'));
@@ -131,31 +141,31 @@ console.log(braces.expand('a{d,c,b\\}e'));
 Commas inside braces may also be escaped:
 
 ```js
+console.log(braces.expand('a{b\\,c}d'));
+//=> ['a{b,c}d']
+
 console.log(braces.expand('a{d\\,c,b}e'));
 //=> ['ad,ce', 'abe']
 ```
 
 **Single items**
 
-A brace pattern is also considered to be escaped when it contains a single item: 
+Following bash conventions, a brace pattern is also not expanded when it contains a single character:
 
 ```js
 console.log(braces.expand('a{b}c'));
 //=> ['a{b}c']
-
-console.log(braces.expand('a{b\\,c}d'));
-//=> ['a{b,c}d']
 ```
 
 ## Options
 
 ### options.expand
 
-Type: `Boolean`
+**Type**: `Boolean`
 
-Default: `undefined`
+**Default**: `undefined`
 
-Generate an "expanded" brace pattern (this option is unncessary with the `.expand` method, which does the same thing).
+**Description**: Generate an "expanded" brace pattern (this option is unncessary with the `.expand` method, which does the same thing).
 
 ```js
 console.log(braces('a/{b,c}/d', {expand: true}));
@@ -164,11 +174,11 @@ console.log(braces('a/{b,c}/d', {expand: true}));
 
 ### options.optimize
 
-Type: `Boolean`
+**Type**: `Boolean`
 
-Default: `true`
+**Default**: `true`
 
-Enabled by default.
+**Description**: Enabled by default.
 
 ```js
 console.log(braces('a/{b,c}/d'));
@@ -177,20 +187,19 @@ console.log(braces('a/{b,c}/d'));
 
 ### options.nodupes
 
-Type: `Boolean`
-
-Default: `true`
+**Type**: `Boolean`
 
-Duplicates are removed by default. To keep duplicates, pass `{nodupes: false}` on the options
+**Default**: `true`
 
+**Description**: Duplicates are removed by default. To keep duplicates, pass `{nodupes: false}` on the options
 
 ### options.rangeLimit
 
-Type: `Number`
+**Type**: `Number`
 
-Default: `250`
+**Default**: `250`
 
-When `braces.expand()` is used, or `options.expand` is true, brace patterns will automatically be [optimized](#optionsoptimize) when the difference between the range minimum and range maximum exceeds the `rangeLimit`. This is to prevent huge ranges from freezing your application.
+**Description**: When `braces.expand()` is used, or `options.expand` is true, brace patterns will automatically be [optimized](#optionsoptimize) when the difference between the range minimum and range maximum exceeds the `rangeLimit`. This is to prevent huge ranges from freezing your application.
 
 You can set this to any number, or change `options.rangeLimit` to `Inifinity` to disable this altogether.
 
@@ -208,11 +217,11 @@ console.log(braces.expand('{1..100}'));
 
 ### options.transform
 
-Type: `Function`
+**Type**: `Function`
 
-Default: `undefined`
+**Default**: `undefined`
 
-Customize range expansion.
+**Description**: Customize range expansion.
 
 ```js
 var range = braces.expand('x{a..e}y', {
@@ -227,15 +236,15 @@ console.log(range);
 
 ### options.quantifiers
 
-Type: `Boolean`
+**Type**: `Boolean`
 
-Default: `undefined`
+**Default**: `undefined`
 
-In regular expressions, quanitifiers can be used to specify how many times a token can be repeated. For example, `a{1,3}` will match the letter `a` one to three times.
+**Description**: In regular expressions, quanitifiers can be used to specify how many times a token can be repeated. For example, `a{1,3}` will match the letter `a` one to three times.
 
 Unfortunately, regex quantifiers happen to share the same syntax as [Bash lists](#lists)
 
-The `quantifiers` option tells braces to detect when [regex quantifiers][quantifiers] are defined in the given pattern, and not to try to expand them as lists.
+The `quantifiers` option tells braces to detect when [regex quantifiers](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#quantifiers) are defined in the given pattern, and not to try to expand them as lists.
 
 **Examples**
 
@@ -249,104 +258,287 @@ console.log(braces('a/b{1,3}/{x,y,z}', {quantifiers: true, expand: true}));
 //=> [ 'a/b{1,3}/x', 'a/b{1,3}/y', 'a/b{1,3}/z' ]
 ```
 
-## Benchmarks
+### options.unescape
 
-### Running benchmarks
+**Type**: `Boolean`
 
-Install dev dependencies:
+**Default**: `undefined`
 
-```bash
-npm i -d && npm benchmark
+**Description**: Strip backslashes that were used for escaping from the result.
+
+
+## What is "brace expansion"?
+
+Brace expansion is a type of parameter expansion that was made popular by unix shells for generating lists of strings, as well as regex-like matching when used alongside wildcards (globs).
+
+In addition to "expansion", braces are also used for matching. In other words:
+
+* [brace expansion](#brace-expansion) is for generating new lists
+* [brace matching](#brace-matching) is for filtering existing lists
+
+<details>
+  <summary><strong>More about brace expansion</strong> (click to expand)</summary>
+
+There are two main types of brace expansion:
+
+1. **lists**: which are defined using comma-separated values inside curly braces: `{a,b,c}`
+2. **sequences**: which are defined using a starting value and an ending value, separated by two dots: `a{1..3}b`. Optionally, a third argument may be passed to define a "step" or increment to use: `a{1..100..10}b`. These are also sometimes referred to as "ranges".
+
+Here are some example brace patterns to illustrate how they work:
+
+**Sets**
+
+```
+{a,b,c}       => a b c
+{a,b,c}{1,2}  => a1 a2 b1 b2 c1 c2
 ```
 
-### Latest results
+**Sequences**
 
-```bash
-{%= docs("benchmark/last.md") %}
 ```
+{1..9}        => 1 2 3 4 5 6 7 8 9
+{4..-4}       => 4 3 2 1 0 -1 -2 -3 -4
+{1..20..3}    => 1 4 7 10 13 16 19
+{a..j}        => a b c d e f g h i j
+{j..a}        => j i h g f e d c b a
+{a..z..3}     => a d g j m p s v y
+```
+
+**Combination**
+
+Sets and sequences can be mixed together or used along with any other strings.
+
+```
+{a,b,c}{1..3}   => a1 a2 a3 b1 b2 b3 c1 c2 c3
+foo/{a,b,c}/bar => foo/a/bar foo/b/bar foo/c/bar
+```
+
+The fact that braces can be "expanded" from relatively simple patterns makes them ideal for quickly generating test fixtures, file paths, and similar use cases.
+
+## Brace matching 
+
+In addition to _expansion_, brace patterns are also useful for performing regular-expression-like matching.
+
+For example, the pattern `foo/{1..3}/bar` would match any of following strings:
+
+```
+foo/1/bar
+foo/2/bar
+foo/3/bar
+```
+
+But not:
+
+```
+baz/1/qux
+baz/2/qux
+baz/3/qux
+```
+
+Braces can also be combined with [glob patterns][micromatch] to perform more advanced wildcard matching. For example, the pattern `*/{1..3}/*` would match any of following strings:
+
+```
+foo/1/bar
+foo/2/bar
+foo/3/bar
+baz/1/qux
+baz/2/qux
+baz/3/qux
+```
+
+## Brace matching pitfalls
+
+Although brace patterns offer a user-friendly way of matching ranges or sets of strings, there are also some major disadvantages and potential risks you should be aware of. 
+
+### tldr
+
+**"brace bombs"**
+
+- brace expansion can eat up a huge amount of processing resources
+- as brace patterns increase _linearly in size_, the system resources required to expand the pattern increase exponentially
+- users can accidentally (or intentially) exhaust your system's resources resulting in the equivalent of a DoS attack (bonus: no programming knowledge is required!)
+
+For a more detailed explanation with examples, see the [geometric complexity](#geometric-complexity) section.
+
+### The solution
+
+Jump to the [performance section](#performance) to see how Braces solves this problem in comparison to other libraries.
+
+### Geometric complexity
+
+At minimum, brace patterns with sets limited to two elements have quadradic or `O(n^2)` complexity. But the complexity of the algorithm increases exponentially as the number of sets, _and elements per set_, increases, which is `O(n^c)`.
+
+For example, the following sets demonstrate quadratic (`O(n^2)`) complexity:
+
+```
+{1,2}{3,4}      => (2X2)    => 13 14 23 24
+{1,2}{3,4}{5,6} => (2X2X2)  => 135 136 145 146 235 236 245 246
+```
+
+But add an element to a set, and we get a n-fold Cartesian product with `O(n^c)` complexity:
+
+```
+{1,2,3}{4,5,6}{7,8,9} => (3X3X3) => 147 148 149 157 158 159 167 168 169 247 248 
+                                    249 257 258 259 267 268 269 347 348 349 357 
+                                    358 359 367 368 369
+```
+
+Now, imagine how this complexity grows given that each element is a n-tuple:
+
+```
+{1..100}{1..100}         => (100X100)     => 10,000 elements (38.4 kB)
+{1..100}{1..100}{1..100} => (100X100X100) => 1,000,000 elements (5.76 MB)
+```
+
+Although these examples are clearly contrived, they demonstrate how brace patterns can quickly grow out of control. 
+
+**More information**
+
+Interested in learning more about brace expansion?
+
+- [linuxjournal/bash-brace-expansion](http://www.linuxjournal.com/content/bash-brace-expansion)
+- [rosettacode/Brace_expansion](https://rosettacode.org/wiki/Brace_expansion)
+- [cartesian product](https://en.wikipedia.org/wiki/Cartesian_product)
+
+</details>
+
 
 ## Performance
 
-### What's the big deal?
+Braces is not only screaming fast, it's also more accurate the other brace expansion libraries. 
+
+### Better algorithms
+
+Fortunately there is a solution to the ["brace bomb" problem](#brace-matching-pitfalls): _don't expand brace patterns into an array when they're used for matching_. 
+
+Instead, convert the pattern into an optimized regular expression. This is easier said than done, and braces is the only library that does this currently. 
+
+**The proof is in the numbers**
+
+Minimatch gets exponentially slower as patterns increase in complexity, braces does not. The following results were generated using `braces()` and `minimatch.braceExpand()`, respectively.
 
-If you use globbing a lot, whether in your build tool chain or in your application, the speed of the glob matcher not only impacts your experience, but a slow glob matcher can slow everything else down, limitimg what you thought was possible in your application.
+| **Pattern**                 | **braces**         | **[minimatch][]**            |
+| ---                         | ---                | ---                          |
+| `{1..9007199254740991}`[^1] | `298 B` (5ms 459μs)|  N/A (freezes)               |
+| `{1..1000000000000000}`     | `41 B` (1ms 15μs)  |  N/A (freezes)               |
+| `{1..100000000000000}`      | `40 B` (890μs)     |  N/A (freezes)               |
+| `{1..10000000000000}`       | `39 B` (2ms 49μs)  |  N/A (freezes)               |
+| `{1..1000000000000}`        | `38 B` (608μs)     |  N/A (freezes)               |
+| `{1..100000000000}`         | `37 B` (397μs)     |  N/A (freezes)               |
+| `{1..10000000000}`          | `35 B` (983μs)     |  N/A (freezes)               |
+| `{1..1000000000}`           | `34 B` (798μs)     |  N/A (freezes)               |
+| `{1..100000000}`            | `33 B` (733μs)     |  N/A (freezes)               |
+| `{1..10000000}`             | `32 B` (5ms 632μs) | `78.89 MB` (16s 388ms 569μs) |
+| `{1..1000000}`              | `31 B` (1ms 381μs) | `6.89 MB` (1s 496ms 887μs)   |
+| `{1..100000}`               | `30 B` (950μs)     | `588.89 kB` (146ms 921μs)    |
+| `{1..10000}`                | `29 B` (1ms 114μs) | `48.89 kB` (14ms 187μs)      |
+| `{1..1000}`                 | `28 B` (760μs)     | `3.89 kB` (1ms 453μs)        |
+| `{1..100}`                  | `22 B` (345μs)     | `291 B` (196μs)              |
+| `{1..10}`                   | `10 B` (533μs)     | `20 B` (37μs)                |
+| `{1..3}`                    | `7 B` (190μs)      | `5 B` (27μs)                 |
 
-### Braces is fast
 
-> minimatch gets exponentially slower as patterns increase in complexity, braces does not
+### Faster algorithms
 
-Brace patterns are meant to be expanded - at least, if you're using Bash, that is. It's not at all unusual for users to want to use brace patterns for dates or similar ranges. It's also very easy to define a brace pattern that appears to be very simple but in fact is fairly complex. 
+When you need expansion, braces is still much faster.
 
-For example, here is how generated regex size and processing time compare as patterns increase in complexity: 
+_(the following results were generated using `braces.expand()` and `minimatch.braceExpand()`, respectively)_
 
-_(the following results were generated using `braces()` and `minimatch.braceExpand()`)_
+| **Pattern**     | **braces**                  | **[minimatch][]**            |
+| ---             | ---                         | ---                          |
+| `{1..10000000}` | `78.89 MB` (2s 698ms 642μs) | `78.89 MB` (18s 601ms 974μs) |
+| `{1..1000000}`  | `6.89 MB` (458ms 576μs)     | `6.89 MB` (1s 491ms 621μs)   |
+| `{1..100000}`   | `588.89 kB` (20ms 728μs)    | `588.89 kB` (156ms 919μs)    |
+| `{1..10000}`    | `48.89 kB` (2ms 202μs)      | `48.89 kB` (13ms 641μs)      |
+| `{1..1000}`     | `3.89 kB` (1ms 796μs)       | `3.89 kB` (1ms 958μs)        |
+| `{1..100}`      | `291 B` (424μs)             | `291 B` (211μs)              |
+| `{1..10}`       | `20 B` (487μs)              | `20 B` (72μs)                |
+| `{1..3}`        | `5 B` (166μs)               | `5 B` (27μs)                 |
 
-| **Pattern** | **minimatch** | **braces** |
-| ---                     | ---                        | ---                  |
-| `{1..9007199254740991}`[^1] | N/A (freezes)          | `300 B` (12ms 878μs) |
-| `{1..10000000}`         | `98.9 MB` (20s 193ms 13μs) | `34 B` (11ms 204μs)  |
-| `{1..1000000}`          | `8.89 MB` (1s 838ms 718μs) | `33 B` (1ms 761μs)   |
-| `{1..100000}`           | `789 kB` (181ms 518μs)     | `32 B` (1ms 76μs)    |
-| `{1..10000}`            | `68.9 kB` (17ms 436μs)     | `31 B` (1ms 382μs)   |
-| `{1..1000}`             | `5.89 kB` (1ms 773μs)      | `30 B` (1ms 509μs)   |
-| `{1..100}`              | `491 B` (321μs)            | `24 B` (309μs)       |
-| `{1..10}`               | `40 B` (56μs)              | `12 B` (333μs)       |
-| `{1..3}`                | `11 B` (80μs)              | `9 B` (370μs)        |
+If you'd like to run these comparisons yourself, see [test/support/generate.js](test/support/generate.js).
 
-These numbers are actually pretty small as far as numeric ranges are concerned. Regardless, we shouldn't have to consider such things when creating a glob pattern. The tool should get out of your way and let you be as creative as you want.
 
-### Braces is performant
+## Benchmarks
 
-Even when brace patterns are fully **expanded**, `braces` is still much faster. 
+### Running benchmarks
 
-_(the following results were generated using `braces.expand()` and `minimatch.braceExpand()`)_
+Install dev dependencies:
 
-| **Pattern** | **minimatch** | **braces** |
-| ---               | ---                         | ---                        |
-| `a/{1..10000000}` | `98.9 MB` (19s 754ms 376μs) | `98.9 MB` (5s 734ms 419μs) |
-| `a/{1..1000000}`  | `8.89 MB` (1s 866ms 968μs)  | `8.89 MB` (561ms 594μs)    |
-| `a/{1..100000}`   | `789 kB` (178ms 311μs)      | `789 kB` (29ms 823μs)      |
-| `a/{1..10000}`    | `68.9 kB` (17ms 692μs)      | `68.9 kB` (2ms 351μs)      |
-| `a/{1..1000}`     | `5.89 kB` (1ms 823μs)       | `5.89 kB` (706μs)          |
-| `a/{1..100}`      | `491 B` (609μs)             | `491 B` (267μs)            |
-| `a/{1..10}`       | `40 B` (61μs)               | `40 B` (636μs)             |
-| `a/{1..3}`        | `11 B` (206μs)              | `11 B` (207μs)             |
+```bash
+npm i -d && npm benchmark
+```
 
-### Why is braces so fast?
+### Latest results
 
-Braces was built from the ground up to be as performant as possible when matching, using a combination of the following:
+```bash
+Benchmarking: (8 of 8)
+ · combination-nested
+ · combination
+ · escaped
+ · list-basic
+ · list-multiple
+ · no-braces
+ · sequence-basic
+ · sequence-multiple
+
+# benchmark/fixtures/combination-nested.js (52 bytes)
+  brace-expansion x 4,756 ops/sec ±1.09% (86 runs sampled)
+  braces x 11,202,303 ops/sec ±1.06% (88 runs sampled)
+  minimatch x 4,816 ops/sec ±0.99% (87 runs sampled)
+
+  fastest is braces
 
-- **minimizes loops and iterating**: we try to pass over the pattern once to parse it before passing it to the compiler.
-- **generates an optimized string**: sequences/ranges are optimized by [to-regex-range][], which is not only highly accurate, but produces patterns that are a fraction of the size of patterns generated by other brace expansion libraries, such as Bash and [minimatch][] (via [brace-expansion][])
-- **generates results faster**: can handle even the most complex patterns that cause other implementations like [minimatch][] and Bash to fail. 
+# benchmark/fixtures/combination.js (51 bytes)
+  brace-expansion x 625 ops/sec ±0.87% (87 runs sampled)
+  braces x 11,031,884 ops/sec ±0.72% (90 runs sampled)
+  minimatch x 637 ops/sec ±0.84% (88 runs sampled)
 
-### Braces is safe
+  fastest is braces
 
-Last, but perhaps most important of all, unlike [brace-expansion][] and [minimatch][], braces will not freeze your application when a user passes a complex brace pattern.
+# benchmark/fixtures/escaped.js (44 bytes)
+  brace-expansion x 163,325 ops/sec ±1.05% (87 runs sampled)
+  braces x 10,655,071 ops/sec ±1.22% (88 runs sampled)
+  minimatch x 147,495 ops/sec ±0.96% (88 runs sampled)
 
-**The trouble with ~~tribbles~~ brace patterns**
+  fastest is braces
 
-There are unlimited scenarios where brace patterns cause the resulting array to grow geometrically. If you define two brace patterns for example, the result is multiplicative if not exponential. For example, given the pattern `{1..1000}`, we would end up with 1 thousand strings. But if two patterns are given, such as `{1...1000}{1...1000}`, we would end up with 1 million strings. 
+# benchmark/fixtures/list-basic.js (40 bytes)
+  brace-expansion x 99,726 ops/sec ±1.07% (83 runs sampled)
+  braces x 10,596,584 ops/sec ±0.98% (88 runs sampled)
+  minimatch x 100,069 ops/sec ±1.17% (86 runs sampled)
 
-It's easy to think, "I wouldn't do that", but that's not the point. Even though those example patterns are contrived:
+  fastest is braces
 
-1. it's not uncommon for users to define brace patterns that result in similarly huge strings (consider date ranges `1-2016`, for example, and how easy it might be for someone to also add an alphabetical range if searching records of some kind)
-1. we can't control how users define their patterns (and we shouldn't have to care)
-1. even if it's uncommon for user to define unwieldy patterns, there is still potential for intentionally exploiting this feature to [cause a DoS][dos] in your application (and given that this is a not-very-technically-advanced method for causing a DoS - e.g. anyone can do it, you should be concerned!)
+# benchmark/fixtures/list-multiple.js (52 bytes)
+  brace-expansion x 34,348 ops/sec ±1.08% (88 runs sampled)
+  braces x 9,264,131 ops/sec ±1.12% (88 runs sampled)
+  minimatch x 34,893 ops/sec ±0.87% (87 runs sampled)
 
-**Potential for DoS attacks**
+  fastest is braces
 
-Most brace expansion libraries, including Bash, [minimatch][], [multimatch][] and [brace-expansion](https://github.com/juliangruber/brace-expansion) are vulnerable to DoS attacks (similar to the [ReDoS bug][bug] minimatch had recently).
+# benchmark/fixtures/no-braces.js (48 bytes)
+  brace-expansion x 275,368 ops/sec ±1.18% (89 runs sampled)
+  braces x 9,134,677 ops/sec ±0.95% (88 runs sampled)
+  minimatch x 3,755,954 ops/sec ±1.13% (89 runs sampled)
 
-Braces mitigates this risk in three ways: 
+  fastest is braces
 
-1. Braces prevents malicious strings from being used by checking the length of the input strings, as well as generated regex
-1. Braces [optimizes](#optimize) the generated result by default, so that only _one output string is generated for every input string_.
-1. When [.expand](#expand) or [options.expand](#optionsexpand) are used, braces has checks in place to prevent huge ranges from being (accidentally) created. By default, when more than 250 strings will result from the given pattern, braces will [optimize](#optimize) the result instead of expanding it. You can override this limit of 250 by passing a custom number on [options.rangeLimit](#optionsrangelimit).
+# benchmark/fixtures/sequence-basic.js (41 bytes)
+  brace-expansion x 5,492 ops/sec ±1.35% (87 runs sampled)
+  braces x 8,485,034 ops/sec ±1.28% (89 runs sampled)
+  minimatch x 5,341 ops/sec ±1.17% (87 runs sampled)
 
-_(you might also want to consider using [micromatch][] for glob matching, as a safer alternative to minimatch and multimatch)_
+  fastest is braces
+
+# benchmark/fixtures/sequence-multiple.js (51 bytes)
+  brace-expansion x 116 ops/sec ±0.77% (77 runs sampled)
+  braces x 9,445,118 ops/sec ±1.32% (84 runs sampled)
+  minimatch x 109 ops/sec ±1.16% (76 runs sampled)
+
+  fastest is braces
+```
 
-[^1]: this is the largest safe integer allowed in JavaScript. 
+[^1]: this is the largest safe integer allowed in JavaScript.
 
 [bash]: www.gnu.org/software/bash/
 [braces]: https://github.com/jonschlinkert/braces
@@ -357,4 +549,4 @@ _(you might also want to consider using [micromatch][] for glob matching, as a s
 [minimatch]: https://github.com/isaacs/minimatch
 [quantifiers]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#quantifiers
 [dos]: https://en.wikipedia.org/wiki/Denial-of-service_attack
-[bug]: https://medium.com/node-security/minimatch-redos-vulnerability-590da24e6d3c#.jew0b6mpc
\ No newline at end of file
+[bug]: https://medium.com/node-security/minimatch-redos-vulnerability-590da24e6d3c#.jew0b6mpc
diff --git a/LICENSE b/LICENSE
index 39245ac..3f2eca1 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
 The MIT License (MIT)
 
-Copyright (c) 2014-2016, Jon Schlinkert.
+Copyright (c) 2014-2017, Jon Schlinkert.
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/README.md b/README.md
index dd944a2..b6f83df 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,8 @@
-# braces [![NPM version](https://img.shields.io/npm/v/braces.svg?style=flat)](https://www.npmjs.com/package/braces) [![NPM monthly downloads](https://img.shields.io/npm/dm/braces.svg?style=flat)](https://npmjs.org/package/braces)  [![NPM total downloads](https://img.shields.io/npm/dt/braces.svg?style=flat)](https://npmjs.org/package/braces) [![Linux Build Status](https://img.shields.io/travis/jonschlinkert/braces.svg?style=flat&label=Travis)](https://travis-ci.org/jonschlinkert/braces) [ [...]
+# braces [![NPM version](https://img.shields.io/npm/v/braces.svg?style=flat)](https://www.npmjs.com/package/braces) [![NPM monthly downloads](https://img.shields.io/npm/dm/braces.svg?style=flat)](https://npmjs.org/package/braces) [![NPM total downloads](https://img.shields.io/npm/dt/braces.svg?style=flat)](https://npmjs.org/package/braces) [![Linux Build Status](https://img.shields.io/travis/micromatch/braces.svg?style=flat&label=Travis)](https://travis-ci.org/micromatch/braces) [![Windo [...]
 
-> Fast, comprehensive, bash-like brace expansion implemented in JavaScript. Complete support for the Bash 4.3 braces specification, without sacrificing speed.
+> Bash-like brace expansion, implemented in JavaScript. Safer than other brace expansion libs, with complete support for the Bash 4.3 braces specification, without sacrificing speed.
+
+Please consider following this project's author, [Jon Schlinkert](https://github.com/jonschlinkert), and consider starring the project to show your :heart: and support.
 
 ## Install
 
@@ -10,28 +12,34 @@ Install with [npm](https://www.npmjs.com/):
 $ npm install --save braces
 ```
 
+## Why use braces?
+
+Brace patterns are great for matching ranges. Users (and implementors) shouldn't have to think about whether or not they will break their application (or yours) from accidentally defining an aggressive brace pattern. _Braces is the only library that offers a [solution to this problem](#performance)_.
+
+* **Safe(r)**: Braces isn't vulnerable to DoS attacks like [brace-expansion](https://github.com/juliangruber/brace-expansion), [minimatch](https://github.com/isaacs/minimatch) and [multimatch](https://github.com/sindresorhus/multimatch) (a different bug than the [other regex DoS bug](https://medium.com/node-security/minimatch-redos-vulnerability-590da24e6d3c#.jew0b6mpc)).
+* **Accurate**: complete support for the [Bash 4.3 Brace Expansion](www.gnu.org/software/bash/) specification (passes all of the Bash braces tests)
+* **[fast and performant](#benchmarks)**: Starts fast, runs fast and [scales well](#performance) as patterns increase in complexity.
+* **Organized code base**: with parser and compiler that are eas(y|ier) to maintain and update when edge cases crop up.
+* **Well-tested**: thousands of test assertions. Passes 100% of the [minimatch](https://github.com/isaacs/minimatch) and [brace-expansion](https://github.com/juliangruber/brace-expansion) unit tests as well (as of the writing of this).
+
 ## Usage
 
-The main export is a function that takes a brace `pattern` to expand and an `options` object if necessary.
+The main export is a function that takes one or more brace `patterns` and `options`.
 
 ```js
 var braces = require('braces');
 braces(pattern[, options]);
 ```
 
-## Highlights
+By default, braces returns an optimized regex-source string. To get an array of brace patterns, use `brace.expand()`.
 
-* **Safer**: Braces isn't [vulnerable to DoS attacks](#braces-is-safe), unlike [brace-expansion](https://github.com/juliangruber/brace-expansion), [minimatch](https://github.com/isaacs/minimatch) and [multimatch](https://github.com/sindresorhus/multimatch) (this not the same [DoS bug](https://medium.com/node-security/minimatch-redos-vulnerability-590da24e6d3c#.jew0b6mpc) that was found in minimatch and multimatch recently)
-* **Accurate**: complete support for the [Bash 4.3 Brace Expansion](www.gnu.org/software/bash/) specification (passes all of the Bash braces tests)
-* **[fast and performant](#benchmarks)**: not only runs fast, but scales well as patterns increase in complexity (other libs don't - see the notes on [performance](#performance)).
-* **Organized code base**: with parser and compiler that are easy to maintain and update when edge cases crop up.
-* **Well-tested**: 900+ unit tests with thousands of actual patterns tested. Passes 100% of the [minimatch](https://github.com/isaacs/minimatch) and [brace-expansion](https://github.com/juliangruber/brace-expansion) unit tests as well.
-* **Optimized braces**: By default returns an optimized string that can be used for creating regular expressions for matching.
-* **Expanded braces**: Optionally returns an array (like bash). See a [comparison](#optimized-vs-expanded) between optimized and expanded.
+The following section explains the difference in more detail. _(If you're curious about "why" braces does this by default, see [brace matching pitfalls](#brace-matching-pitfalls)_.
+
+### Optimized vs. expanded braces
 
 **Optimized**
 
-Patterns are optimized for regex and matching by default:
+By default, patterns are optimized for regex and matching:
 
 ```js
 console.log(braces('a/{x,y,z}/b'));
@@ -47,6 +55,13 @@ console.log(braces.expand('a/{x,y,z}/b'));
 //=> ['a/x/b', 'a/y/b', 'a/z/b']
 ```
 
+Or use [options.expand](#optionsexpand):
+
+```js
+console.log(braces('a/{x,y,z}/b', {expand: true}));
+//=> ['a/x/b', 'a/y/b', 'a/z/b']
+```
+
 ## Features
 
 * [lists](#lists): Supports "lists": `a/{b,c}/d` => `['a/b/d', 'a/c/d']`
@@ -72,15 +87,15 @@ console.log(braces.expand('a/{foo,bar,baz}/*.js'));
 Uses [fill-range](https://github.com/jonschlinkert/fill-range) for expanding alphabetical or numeric ranges (bash "sequences"):
 
 ```js
-// padding is not retained when string is optimized
-console.log(braces('a{01..03}b')); // ['a([1-3])b']
-console.log(braces('a{1..3}b'));   // ['a([1-3])b']
-
 console.log(braces.expand('{1..3}'));     // ['1', '2', '3']
 console.log(braces.expand('a{01..03}b')); // ['a01b', 'a02b', 'a03b']
 console.log(braces.expand('a{1..3}b'));   // ['a1b', 'a2b', 'a3b']
 console.log(braces.expand('{a..c}'));     // ['a', 'b', 'c']
 console.log(braces.expand('foo/{a..c}')); // ['foo/a', 'foo/b', 'foo/c']
+
+// supports padded ranges
+console.log(braces('a{01..03}b'));   //=> [ 'a(0[1-3])b' ]
+console.log(braces('a{001..300}b')); //=> [ 'a(0{2}[1-9]|0[1-9][0-9]|[12][0-9]{2}|300)b' ]
 ```
 
 ### Steps
@@ -101,7 +116,7 @@ When the [.optimize](#optimize) method is used, or [options.optimize](#optionsop
 
 Brace patterns may be nested. The results of each expanded string are not sorted, and left to right order is preserved.
 
-**"Expanded" examples**
+**"Expanded" braces**
 
 ```js
 console.log(braces.expand('a{b,c,/{x,y}}/e'));
@@ -111,7 +126,7 @@ console.log(braces.expand('a/{x,{1..5},y}/c'));
 //=> ['a/x/c', 'a/1/c', 'a/2/c', 'a/3/c', 'a/4/c', 'a/5/c', 'a/y/c']
 ```
 
-**"Optimized" examples**
+**"Optimized" braces**
 
 ```js
 console.log(braces('a{b,c,/{x,y}}/e'));
@@ -125,7 +140,7 @@ console.log(braces('a/{x,{1..5},y}/c'));
 
 **Escaping braces**
 
-Prevent braces from being expanded or evaluted by escaping either the opening or closing brace:
+A brace pattern will not be expanded or evaluted if _either the opening or closing brace is escaped_:
 
 ```js
 console.log(braces.expand('a\\{d,c,b}e'));
@@ -140,31 +155,31 @@ console.log(braces.expand('a{d,c,b\\}e'));
 Commas inside braces may also be escaped:
 
 ```js
+console.log(braces.expand('a{b\\,c}d'));
+//=> ['a{b,c}d']
+
 console.log(braces.expand('a{d\\,c,b}e'));
 //=> ['ad,ce', 'abe']
 ```
 
 **Single items**
 
-A brace pattern is also considered to be escaped when it contains a single item:
+Following bash conventions, a brace pattern is also not expanded when it contains a single character:
 
 ```js
 console.log(braces.expand('a{b}c'));
 //=> ['a{b}c']
-
-console.log(braces.expand('a{b\\,c}d'));
-//=> ['a{b,c}d']
 ```
 
 ## Options
 
 ### options.expand
 
-Type: `Boolean`
+**Type**: `Boolean`
 
-Default: `undefined`
+**Default**: `undefined`
 
-Generate an "expanded" brace pattern (this option is unncessary with the `.expand` method, which does the same thing).
+**Description**: Generate an "expanded" brace pattern (this option is unncessary with the `.expand` method, which does the same thing).
 
 ```js
 console.log(braces('a/{b,c}/d', {expand: true}));
@@ -173,11 +188,11 @@ console.log(braces('a/{b,c}/d', {expand: true}));
 
 ### options.optimize
 
-Type: `Boolean`
+**Type**: `Boolean`
 
-Default: `true`
+**Default**: `true`
 
-Enabled by default.
+**Description**: Enabled by default.
 
 ```js
 console.log(braces('a/{b,c}/d'));
@@ -186,19 +201,19 @@ console.log(braces('a/{b,c}/d'));
 
 ### options.nodupes
 
-Type: `Boolean`
+**Type**: `Boolean`
 
-Default: `true`
+**Default**: `true`
 
-Duplicates are removed by default. To keep duplicates, pass `{nodupes: false}` on the options
+**Description**: Duplicates are removed by default. To keep duplicates, pass `{nodupes: false}` on the options
 
 ### options.rangeLimit
 
-Type: `Number`
+**Type**: `Number`
 
-Default: `250`
+**Default**: `250`
 
-When `braces.expand()` is used, or `options.expand` is true, brace patterns will automatically be [optimized](#optionsoptimize) when the difference between the range minimum and range maximum exceeds the `rangeLimit`. This is to prevent huge ranges from freezing your application.
+**Description**: When `braces.expand()` is used, or `options.expand` is true, brace patterns will automatically be [optimized](#optionsoptimize) when the difference between the range minimum and range maximum exceeds the `rangeLimit`. This is to prevent huge ranges from freezing your application.
 
 You can set this to any number, or change `options.rangeLimit` to `Inifinity` to disable this altogether.
 
@@ -216,11 +231,11 @@ console.log(braces.expand('{1..100}'));
 
 ### options.transform
 
-Type: `Function`
+**Type**: `Function`
 
-Default: `undefined`
+**Default**: `undefined`
 
-Customize range expansion.
+**Description**: Customize range expansion.
 
 ```js
 var range = braces.expand('x{a..e}y', {
@@ -235,11 +250,11 @@ console.log(range);
 
 ### options.quantifiers
 
-Type: `Boolean`
+**Type**: `Boolean`
 
-Default: `undefined`
+**Default**: `undefined`
 
-In regular expressions, quanitifiers can be used to specify how many times a token can be repeated. For example, `a{1,3}` will match the letter `a` one to three times.
+**Description**: In regular expressions, quanitifiers can be used to specify how many times a token can be repeated. For example, `a{1,3}` will match the letter `a` one to three times.
 
 Unfortunately, regex quantifiers happen to share the same syntax as [Bash lists](#lists)
 
@@ -257,6 +272,202 @@ console.log(braces('a/b{1,3}/{x,y,z}', {quantifiers: true, expand: true}));
 //=> [ 'a/b{1,3}/x', 'a/b{1,3}/y', 'a/b{1,3}/z' ]
 ```
 
+### options.unescape
+
+**Type**: `Boolean`
+
+**Default**: `undefined`
+
+**Description**: Strip backslashes that were used for escaping from the result.
+
+## What is "brace expansion"?
+
+Brace expansion is a type of parameter expansion that was made popular by unix shells for generating lists of strings, as well as regex-like matching when used alongside wildcards (globs).
+
+In addition to "expansion", braces are also used for matching. In other words:
+
+* [brace expansion](#brace-expansion) is for generating new lists
+* [brace matching](#brace-matching) is for filtering existing lists
+
+<details>
+<summary><strong>More about brace expansion</strong> (click to expand)</summary>
+
+There are two main types of brace expansion:
+
+1. **lists**: which are defined using comma-separated values inside curly braces: `{a,b,c}`
+2. **sequences**: which are defined using a starting value and an ending value, separated by two dots: `a{1..3}b`. Optionally, a third argument may be passed to define a "step" or increment to use: `a{1..100..10}b`. These are also sometimes referred to as "ranges".
+
+Here are some example brace patterns to illustrate how they work:
+
+**Sets**
+
+```
+{a,b,c}       => a b c
+{a,b,c}{1,2}  => a1 a2 b1 b2 c1 c2
+```
+
+**Sequences**
+
+```
+{1..9}        => 1 2 3 4 5 6 7 8 9
+{4..-4}       => 4 3 2 1 0 -1 -2 -3 -4
+{1..20..3}    => 1 4 7 10 13 16 19
+{a..j}        => a b c d e f g h i j
+{j..a}        => j i h g f e d c b a
+{a..z..3}     => a d g j m p s v y
+```
+
+**Combination**
+
+Sets and sequences can be mixed together or used along with any other strings.
+
+```
+{a,b,c}{1..3}   => a1 a2 a3 b1 b2 b3 c1 c2 c3
+foo/{a,b,c}/bar => foo/a/bar foo/b/bar foo/c/bar
+```
+
+The fact that braces can be "expanded" from relatively simple patterns makes them ideal for quickly generating test fixtures, file paths, and similar use cases.
+
+## Brace matching
+
+In addition to _expansion_, brace patterns are also useful for performing regular-expression-like matching.
+
+For example, the pattern `foo/{1..3}/bar` would match any of following strings:
+
+```
+foo/1/bar
+foo/2/bar
+foo/3/bar
+```
+
+But not:
+
+```
+baz/1/qux
+baz/2/qux
+baz/3/qux
+```
+
+Braces can also be combined with [glob patterns](https://github.com/jonschlinkert/micromatch) to perform more advanced wildcard matching. For example, the pattern `*/{1..3}/*` would match any of following strings:
+
+```
+foo/1/bar
+foo/2/bar
+foo/3/bar
+baz/1/qux
+baz/2/qux
+baz/3/qux
+```
+
+## Brace matching pitfalls
+
+Although brace patterns offer a user-friendly way of matching ranges or sets of strings, there are also some major disadvantages and potential risks you should be aware of.
+
+### tldr
+
+**"brace bombs"**
+
+* brace expansion can eat up a huge amount of processing resources
+* as brace patterns increase _linearly in size_, the system resources required to expand the pattern increase exponentially
+* users can accidentally (or intentially) exhaust your system's resources resulting in the equivalent of a DoS attack (bonus: no programming knowledge is required!)
+
+For a more detailed explanation with examples, see the [geometric complexity](#geometric-complexity) section.
+
+### The solution
+
+Jump to the [performance section](#performance) to see how Braces solves this problem in comparison to other libraries.
+
+### Geometric complexity
+
+At minimum, brace patterns with sets limited to two elements have quadradic or `O(n^2)` complexity. But the complexity of the algorithm increases exponentially as the number of sets, _and elements per set_, increases, which is `O(n^c)`.
+
+For example, the following sets demonstrate quadratic (`O(n^2)`) complexity:
+
+```
+{1,2}{3,4}      => (2X2)    => 13 14 23 24
+{1,2}{3,4}{5,6} => (2X2X2)  => 135 136 145 146 235 236 245 246
+```
+
+But add an element to a set, and we get a n-fold Cartesian product with `O(n^c)` complexity:
+
+```
+{1,2,3}{4,5,6}{7,8,9} => (3X3X3) => 147 148 149 157 158 159 167 168 169 247 248 
+                                    249 257 258 259 267 268 269 347 348 349 357 
+                                    358 359 367 368 369
+```
+
+Now, imagine how this complexity grows given that each element is a n-tuple:
+
+```
+{1..100}{1..100}         => (100X100)     => 10,000 elements (38.4 kB)
+{1..100}{1..100}{1..100} => (100X100X100) => 1,000,000 elements (5.76 MB)
+```
+
+Although these examples are clearly contrived, they demonstrate how brace patterns can quickly grow out of control.
+
+**More information**
+
+Interested in learning more about brace expansion?
+
+* [linuxjournal/bash-brace-expansion](http://www.linuxjournal.com/content/bash-brace-expansion)
+* [rosettacode/Brace_expansion](https://rosettacode.org/wiki/Brace_expansion)
+* [cartesian product](https://en.wikipedia.org/wiki/Cartesian_product)
+
+</details>
+
+## Performance
+
+Braces is not only screaming fast, it's also more accurate the other brace expansion libraries.
+
+### Better algorithms
+
+Fortunately there is a solution to the ["brace bomb" problem](#brace-matching-pitfalls): _don't expand brace patterns into an array when they're used for matching_.
+
+Instead, convert the pattern into an optimized regular expression. This is easier said than done, and braces is the only library that does this currently.
+
+**The proof is in the numbers**
+
+Minimatch gets exponentially slower as patterns increase in complexity, braces does not. The following results were generated using `braces()` and `minimatch.braceExpand()`, respectively.
+
+| **Pattern** | **braces** | **[minimatch](https://github.com/isaacs/minimatch)** | 
+| --- | --- | --- |
+| `{1..9007199254740991}`<sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup> | `298 B` (5ms 459μs) | N/A (freezes) |
+| `{1..1000000000000000}` | `41 B` (1ms 15μs) | N/A (freezes) |
+| `{1..100000000000000}` | `40 B` (890μs) | N/A (freezes) |
+| `{1..10000000000000}` | `39 B` (2ms 49μs) | N/A (freezes) |
+| `{1..1000000000000}` | `38 B` (608μs) | N/A (freezes) |
+| `{1..100000000000}` | `37 B` (397μs) | N/A (freezes) |
+| `{1..10000000000}` | `35 B` (983μs) | N/A (freezes) |
+| `{1..1000000000}` | `34 B` (798μs) | N/A (freezes) |
+| `{1..100000000}` | `33 B` (733μs) | N/A (freezes) |
+| `{1..10000000}` | `32 B` (5ms 632μs) | `78.89 MB` (16s 388ms 569μs) |
+| `{1..1000000}` | `31 B` (1ms 381μs) | `6.89 MB` (1s 496ms 887μs) |
+| `{1..100000}` | `30 B` (950μs) | `588.89 kB` (146ms 921μs) |
+| `{1..10000}` | `29 B` (1ms 114μs) | `48.89 kB` (14ms 187μs) |
+| `{1..1000}` | `28 B` (760μs) | `3.89 kB` (1ms 453μs) |
+| `{1..100}` | `22 B` (345μs) | `291 B` (196μs) |
+| `{1..10}` | `10 B` (533μs) | `20 B` (37μs) |
+| `{1..3}` | `7 B` (190μs) | `5 B` (27μs) |
+
+### Faster algorithms
+
+When you need expansion, braces is still much faster.
+
+_(the following results were generated using `braces.expand()` and `minimatch.braceExpand()`, respectively)_
+
+| **Pattern** | **braces** | **[minimatch](https://github.com/isaacs/minimatch)** | 
+| --- | --- | --- |
+| `{1..10000000}` | `78.89 MB` (2s 698ms 642μs) | `78.89 MB` (18s 601ms 974μs) |
+| `{1..1000000}` | `6.89 MB` (458ms 576μs) | `6.89 MB` (1s 491ms 621μs) |
+| `{1..100000}` | `588.89 kB` (20ms 728μs) | `588.89 kB` (156ms 919μs) |
+| `{1..10000}` | `48.89 kB` (2ms 202μs) | `48.89 kB` (13ms 641μs) |
+| `{1..1000}` | `3.89 kB` (1ms 796μs) | `3.89 kB` (1ms 958μs) |
+| `{1..100}` | `291 B` (424μs) | `291 B` (211μs) |
+| `{1..10}` | `20 B` (487μs) | `20 B` (72μs) |
+| `{1..3}` | `5 B` (166μs) | `5 B` (27μs) |
+
+If you'd like to run these comparisons yourself, see [test/support/generate.js](test/support/generate.js).
+
 ## Benchmarks
 
 ### Running benchmarks
@@ -337,144 +548,74 @@ Benchmarking: (8 of 8)
   fastest is braces
 ```
 
-## Performance
-
-### What's the big deal?
-
-If you use globbing a lot, whether in your build tool chain or in your application, the speed of the glob matcher not only impacts your experience, but a slow glob matcher can slow everything else down, limitimg what you thought was possible in your application.
-
-### Braces is fast
-
-> minimatch gets exponentially slower as patterns increase in complexity, braces does not
-
-Brace patterns are meant to be expanded - at least, if you're using Bash, that is. It's not at all unusual for users to want to use brace patterns for dates or similar ranges. It's also very easy to define a brace pattern that appears to be very simple but in fact is fairly complex.
-
-For example, here is how generated regex size and processing time compare as patterns increase in complexity:
-
-_(the following results were generated using `braces()` and `minimatch.braceExpand()`)_
-
-| **Pattern** | **minimatch** | **braces** | 
-| --- | --- | --- |
-| `{1..9007199254740991}`<sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup> | N/A (freezes) | `300 B` (12ms 878μs) |
-| `{1..10000000}` | `98.9 MB` (20s 193ms 13μs) | `34 B` (11ms 204μs) |
-| `{1..1000000}` | `8.89 MB` (1s 838ms 718μs) | `33 B` (1ms 761μs) |
-| `{1..100000}` | `789 kB` (181ms 518μs) | `32 B` (1ms 76μs) |
-| `{1..10000}` | `68.9 kB` (17ms 436μs) | `31 B` (1ms 382μs) |
-| `{1..1000}` | `5.89 kB` (1ms 773μs) | `30 B` (1ms 509μs) |
-| `{1..100}` | `491 B` (321μs) | `24 B` (309μs) |
-| `{1..10}` | `40 B` (56μs) | `12 B` (333μs) |
-| `{1..3}` | `11 B` (80μs) | `9 B` (370μs) |
-
-These numbers are actually pretty small as far as numeric ranges are concerned. Regardless, we shouldn't have to consider such things when creating a glob pattern. The tool should get out of your way and let you be as creative as you want.
-
-### Braces is performant
-
-Even when brace patterns are fully **expanded**, `braces` is still much faster.
-
-_(the following results were generated using `braces.expand()` and `minimatch.braceExpand()`)_
-
-| **Pattern** | **minimatch** | **braces** | 
-| --- | --- | --- | --- | --- | --- |
-| `a/{1..10000000}` | `98.9 MB` (19s 754ms 376μs) | `98.9 MB` (5s 734ms 419μs) |
-| `a/{1..1000000}` | `8.89 MB` (1s 866ms 968μs) | `8.89 MB` (561ms 594μs) |
-| `a/{1..100000}` | `789 kB` (178ms 311μs) | `789 kB` (29ms 823μs) |
-| `a/{1..10000}` | `68.9 kB` (17ms 692μs) | `68.9 kB` (2ms 351μs) |
-| `a/{1..1000}` | `5.89 kB` (1ms 823μs) | `5.89 kB` (706μs) |
-| `a/{1..100}` | `491 B` (609μs) | `491 B` (267μs) |
-| `a/{1..10}` | `40 B` (61μs) | `40 B` (636μs) |
-| `a/{1..3}` | `11 B` (206μs) | `11 B` (207μs) |
-
-### Why is braces so fast?
-
-Braces was built from the ground up to be as performant as possible when matching, using a combination of the following:
+## About
 
-* **minimizes loops and iterating**: we try to pass over the pattern once to parse it before passing it to the compiler.
-* **generates an optimized string**: sequences/ranges are optimized by [to-regex-range](https://github.com/jonschlinkert/to-regex-range), which is not only highly accurate, but produces patterns that are a fraction of the size of patterns generated by other brace expansion libraries, such as Bash and [minimatch](https://github.com/isaacs/minimatch) (via [brace-expansion](https://github.com/juliangruber/brace-expansion))
-* **generates results faster**: can handle even the most complex patterns that cause other implementations like [minimatch](https://github.com/isaacs/minimatch) and Bash to fail.
+<details>
+<summary><strong>Contributing</strong></summary>
 
-### Braces is safe
+Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new).
 
-Last, but perhaps most important of all, unlike [brace-expansion](https://github.com/juliangruber/brace-expansion) and [minimatch](https://github.com/isaacs/minimatch), braces will not freeze your application when a user passes a complex brace pattern.
+</details>
 
-**The trouble with ~~tribbles~~ brace patterns**
+<details>
+<summary><strong>Running Tests</strong></summary>
 
-There are unlimited scenarios where brace patterns cause the resulting array to grow geometrically. If you define two brace patterns for example, the result is multiplicative if not exponential. For example, given the pattern `{1..1000}`, we would end up with 1 thousand strings. But if two patterns are given, such as `{1...1000}{1...1000}`, we would end up with 1 million strings.
+Running and reviewing unit tests is a great way to get familiarized with a library and its API. You can install dependencies and run tests with the following command:
 
-It's easy to think, "I wouldn't do that", but that's not the point. Even though those example patterns are contrived:
+```sh
+$ npm install && npm test
+```
 
-1. it's not uncommon for users to define brace patterns that result in similarly huge strings (consider date ranges `1-2016`, for example, and how easy it might be for someone to also add an alphabetical range if searching records of some kind)
-2. we can't control how users define their patterns (and we shouldn't have to care)
-3. even if it's uncommon for user to define unwieldy patterns, there is still potential for intentionally exploiting this feature to [cause a DoS](https://en.wikipedia.org/wiki/Denial-of-service_attack) in your application (and given that this is a not-very-technically-advanced method for causing a DoS - e.g. anyone can do it, you should be concerned!)
+</details>
 
-**Potential for DoS attacks**
+<details>
+<summary><strong>Building docs</strong></summary>
 
-Most brace expansion libraries, including Bash, [minimatch](https://github.com/isaacs/minimatch), [multimatch](https://github.com/sindresorhus/multimatch) and [brace-expansion](https://github.com/juliangruber/brace-expansion) are vulnerable to DoS attacks (similar to the [ReDoS bug](https://medium.com/node-security/minimatch-redos-vulnerability-590da24e6d3c#.jew0b6mpc) minimatch had recently).
+_(This project's readme.md is generated by [verb](https://github.com/verbose/verb-generate-readme), please don't edit the readme directly. Any changes to the readme must be made in the [.verb.md](.verb.md) readme template.)_
 
-Braces mitigates this risk in three ways:
+To generate the readme, run the following command:
 
-1. Braces prevents malicious strings from being used by checking the length of the input strings, as well as generated regex
-2. Braces [optimizes](#optimize) the generated result by default, so that only _one output string is generated for every input string_.
-3. When [.expand](#expand) or [options.expand](#optionsexpand) are used, braces has checks in place to prevent huge ranges from being (accidentally) created. By default, when more than 250 strings will result from the given pattern, braces will [optimize](#optimize) the result instead of expanding it. You can override this limit of 250 by passing a custom number on [options.rangeLimit](#optionsrangelimit).
+```sh
+$ npm install -g verbose/verb#dev verb-generate-readme && verb
+```
 
-_(you might also want to consider using [micromatch](https://github.com/jonschlinkert/micromatch) for glob matching, as a safer alternative to minimatch and multimatch)_
-
-## About
+</details>
 
 ### Related projects
 
+You might also be interested in these projects:
+
 * [expand-brackets](https://www.npmjs.com/package/expand-brackets): Expand POSIX bracket expressions (character classes) in glob patterns. | [homepage](https://github.com/jonschlinkert/expand-brackets "Expand POSIX bracket expressions (character classes) in glob patterns.")
-* [extglob](https://www.npmjs.com/package/extglob): Convert extended globs to regex-compatible strings. Add (almost) the expressive power of regular expressions to… [more](https://github.com/jonschlinkert/extglob) | [homepage](https://github.com/jonschlinkert/extglob "Convert extended globs to regex-compatible strings. Add (almost) the expressive power of regular expressions to glob patterns.")
+* [extglob](https://www.npmjs.com/package/extglob): Extended glob support for JavaScript. Adds (almost) the expressive power of regular expressions to glob… [more](https://github.com/micromatch/extglob) | [homepage](https://github.com/micromatch/extglob "Extended glob support for JavaScript. Adds (almost) the expressive power of regular expressions to glob patterns.")
 * [fill-range](https://www.npmjs.com/package/fill-range): Fill in a range of numbers or letters, optionally passing an increment or `step` to… [more](https://github.com/jonschlinkert/fill-range) | [homepage](https://github.com/jonschlinkert/fill-range "Fill in a range of numbers or letters, optionally passing an increment or `step` to use, or create a regex-compatible range with `options.toRegex`")
-* [micromatch](https://www.npmjs.com/package/micromatch): Glob matching for javascript/node.js. A drop-in replacement and faster alternative to minimatch and multimatch. | [homepage](https://github.com/jonschlinkert/micromatch "Glob matching for javascript/node.js. A drop-in replacement and faster alternative to minimatch and multimatch.")
-* [nanomatch](https://www.npmjs.com/package/nanomatch): Fast, minimal glob matcher for node.js. Similar to micromatch, minimatch and multimatch, but complete Bash… [more](https://github.com/jonschlinkert/nanomatch) | [homepage](https://github.com/jonschlinkert/nanomatch "Fast, minimal glob matcher for node.js. Similar to micromatch, minimatch and multimatch, but complete Bash 4.3 wildcard support only (no support for exglobs, posix brackets or braces)")
-
-### Contributing
-
-Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new).
+* [micromatch](https://www.npmjs.com/package/micromatch): Glob matching for javascript/node.js. A drop-in replacement and faster alternative to minimatch and multimatch. | [homepage](https://github.com/micromatch/micromatch "Glob matching for javascript/node.js. A drop-in replacement and faster alternative to minimatch and multimatch.")
+* [nanomatch](https://www.npmjs.com/package/nanomatch): Fast, minimal glob matcher for node.js. Similar to micromatch, minimatch and multimatch, but complete Bash… [more](https://github.com/micromatch/nanomatch) | [homepage](https://github.com/micromatch/nanomatch "Fast, minimal glob matcher for node.js. Similar to micromatch, minimatch and multimatch, but complete Bash 4.3 wildcard support only (no support for exglobs, posix brackets or braces)")
 
 ### Contributors
 
-| **Commits** | **Contributor**<br/> | 
-| --- | --- | --- | --- | --- | --- | --- | --- |
-| 138 | [jonschlinkert](https://github.com/jonschlinkert) |
+| **Commits** | **Contributor** | 
+| --- | --- |
+| 184 | [jonschlinkert](https://github.com/jonschlinkert) |
 | 4 | [doowb](https://github.com/doowb) |
 | 1 | [es128](https://github.com/es128) |
 | 1 | [eush77](https://github.com/eush77) |
 | 1 | [hemanth](https://github.com/hemanth) |
 
-### Building docs
-
-_(This document was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme) (a [verb](https://github.com/verbose/verb) generator), please don't edit the readme directly. Any changes to the readme must be made in [.verb.md](.verb.md).)_
-
-To generate the readme and API documentation with [verb](https://github.com/verbose/verb):
-
-```sh
-$ npm install -g verb verb-generate-readme && verb
-```
-
-### Running tests
-
-Install dev dependencies:
-
-```sh
-$ npm install -d && npm test
-```
-
 ### Author
 
 **Jon Schlinkert**
 
 * [github/jonschlinkert](https://github.com/jonschlinkert)
-* [twitter/jonschlinkert](http://twitter.com/jonschlinkert)
+* [twitter/jonschlinkert](https://twitter.com/jonschlinkert)
 
 ### License
 
-Copyright © 2016, [Jon Schlinkert](https://github.com/jonschlinkert).
-Released under the [MIT license](https://github.com/jonschlinkert/braces/blob/master/LICENSE).
+Copyright © 2017, [Jon Schlinkert](https://github.com/jonschlinkert).
+Released under the [MIT License](LICENSE).
 
 ***
 
-_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.2.0, on October 21, 2016._
+_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.6.0, on October 19, 2017._
 
 <hr class="footnotes-sep">
 <section class="footnotes">
diff --git a/appveyor.yml b/appveyor.yml
index ce44317..be354d3 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -2,6 +2,7 @@
 environment:
   matrix:
     # node.js
+    - nodejs_version: "7.0"
     - nodejs_version: "6.0"
     - nodejs_version: "5.0"
     - nodejs_version: "0.12"
diff --git a/benchmark/last.md b/benchmark/last.md
index 2297e01..e805f89 100644
--- a/benchmark/last.md
+++ b/benchmark/last.md
@@ -9,57 +9,57 @@ Benchmarking: (8 of 8)
  · sequence-multiple
 
 # benchmark/fixtures/combination-nested.js (52 bytes)
-  brace-expansion x 4,756 ops/sec ±1.09% (86 runs sampled)
-  braces x 11,202,303 ops/sec ±1.06% (88 runs sampled)
-  minimatch x 4,816 ops/sec ±0.99% (87 runs sampled)
+  brace-expansion x 5,605 ops/sec ±1.14% (83 runs sampled)
+  braces x 14,410,490 ops/sec ±1.15% (85 runs sampled)
+  minimatch x 5,977 ops/sec ±1.28% (85 runs sampled)
 
   fastest is braces
 
 # benchmark/fixtures/combination.js (51 bytes)
-  brace-expansion x 625 ops/sec ±0.87% (87 runs sampled)
-  braces x 11,031,884 ops/sec ±0.72% (90 runs sampled)
-  minimatch x 637 ops/sec ±0.84% (88 runs sampled)
+  brace-expansion x 755 ops/sec ±1.18% (83 runs sampled)
+  braces x 10,759,364 ops/sec ±0.94% (85 runs sampled)
+  minimatch x 723 ops/sec ±0.98% (84 runs sampled)
 
   fastest is braces
 
 # benchmark/fixtures/escaped.js (44 bytes)
-  brace-expansion x 163,325 ops/sec ±1.05% (87 runs sampled)
-  braces x 10,655,071 ops/sec ±1.22% (88 runs sampled)
-  minimatch x 147,495 ops/sec ±0.96% (88 runs sampled)
+  brace-expansion x 189,901 ops/sec ±1.23% (86 runs sampled)
+  braces x 10,832,036 ops/sec ±0.89% (85 runs sampled)
+  minimatch x 150,475 ops/sec ±1.29% (88 runs sampled)
 
   fastest is braces
 
 # benchmark/fixtures/list-basic.js (40 bytes)
-  brace-expansion x 99,726 ops/sec ±1.07% (83 runs sampled)
-  braces x 10,596,584 ops/sec ±0.98% (88 runs sampled)
-  minimatch x 100,069 ops/sec ±1.17% (86 runs sampled)
+  brace-expansion x 126,961 ops/sec ±0.70% (85 runs sampled)
+  braces x 11,004,254 ops/sec ±1.29% (84 runs sampled)
+  minimatch x 111,199 ops/sec ±1.26% (85 runs sampled)
 
   fastest is braces
 
 # benchmark/fixtures/list-multiple.js (52 bytes)
-  brace-expansion x 34,348 ops/sec ±1.08% (88 runs sampled)
-  braces x 9,264,131 ops/sec ±1.12% (88 runs sampled)
-  minimatch x 34,893 ops/sec ±0.87% (87 runs sampled)
+  brace-expansion x 36,894 ops/sec ±0.70% (86 runs sampled)
+  braces x 8,609,924 ops/sec ±1.03% (85 runs sampled)
+  minimatch x 41,010 ops/sec ±1.17% (88 runs sampled)
 
   fastest is braces
 
 # benchmark/fixtures/no-braces.js (48 bytes)
-  brace-expansion x 275,368 ops/sec ±1.18% (89 runs sampled)
-  braces x 9,134,677 ops/sec ±0.95% (88 runs sampled)
-  minimatch x 3,755,954 ops/sec ±1.13% (89 runs sampled)
+  brace-expansion x 309,785 ops/sec ±0.82% (88 runs sampled)
+  braces x 8,709,136 ops/sec ±1.23% (88 runs sampled)
+  minimatch x 2,208,995 ops/sec ±1.03% (88 runs sampled)
 
   fastest is braces
 
 # benchmark/fixtures/sequence-basic.js (41 bytes)
-  brace-expansion x 5,492 ops/sec ±1.35% (87 runs sampled)
-  braces x 8,485,034 ops/sec ±1.28% (89 runs sampled)
-  minimatch x 5,341 ops/sec ±1.17% (87 runs sampled)
+  brace-expansion x 6,236 ops/sec ±0.94% (83 runs sampled)
+  braces x 9,241,779 ops/sec ±1.26% (83 runs sampled)
+  minimatch x 7,230 ops/sec ±1.35% (85 runs sampled)
 
   fastest is braces
 
 # benchmark/fixtures/sequence-multiple.js (51 bytes)
-  brace-expansion x 116 ops/sec ±0.77% (77 runs sampled)
-  braces x 9,445,118 ops/sec ±1.32% (84 runs sampled)
-  minimatch x 109 ops/sec ±1.16% (76 runs sampled)
+  brace-expansion x 133 ops/sec ±1.08% (73 runs sampled)
+  braces x 8,859,756 ops/sec ±1.31% (85 runs sampled)
+  minimatch x 135 ops/sec ±0.94% (73 runs sampled)
 
   fastest is braces
diff --git a/examples.md b/examples.md
deleted file mode 100644
index 9a90d3a..0000000
--- a/examples.md
+++ /dev/null
@@ -1,60 +0,0 @@
-# Examples
-
-> Usage examples
-
-TBC...
-
-## Sequences
-
-Letters
-
-```js
-'{a..c}'
-//=> ['a', 'b', 'c']
-```
-
-Numbers
-
-```js
-'{1..3}'
-//=> ['1', '2', '3']
-```
-
-### Sequences with steps
-
-Letters with step:
-
-```js
-'{a..j..2}'
-//=> ['b', 'd', 'f', 'h', 'j']
-```
-
-Numbers with step:
-
-```js
-'{1..10..2}'
-//=> ['2', '4', '6', '8', '10']
-```
-
-## Lists
-
-```js
-'{a,b}'
-//=> ['a', 'b']
-
-'a/{b,c}'
-//=> ['a/b', 'a/c']
-```
-
-## Nesting
-
-```js
-'a/{b,c/{d,e}}/f'
-//=> ['a/b/f', 'a/c/d/f', 'a/c/e/f']
-
-'a/{b,c/{d,e}/f,g}/h'
-//=> ['a/b/h', 'a/c/d/f/h', 'a/c/e/f/h', 'a/g/h']
-
-'a/{b,c/{2..8..2}}/f'
-//=> ['a/b/f', 'a/c/2/f', 'a/c/4/f', 'a/c/6/f', 'a/c/8/f']
-```
diff --git a/examples/ast.js b/examples/ast.js
new file mode 100644
index 0000000..7eb2998
--- /dev/null
+++ b/examples/ast.js
@@ -0,0 +1,22 @@
+var braces = require('..');
+
+var ast = braces.parse('a/{\\{b,c,d},z}/e', {unescape: false});
+var str = '';
+console.log(ast);
+
+visit(ast, function(node) {
+  if (node.val) str += node.val;
+});
+
+console.log(str);
+
+function visit(node, fn) {
+  return node.nodes ? mapVisit(node, fn) : fn(node);
+}
+
+function mapVisit(node, fn) {
+  for (var i = 0; i < node.nodes.length; i++) {
+    visit(node.nodes[i], fn);
+  }
+  return node;
+}
diff --git a/examples/braces.js b/examples/braces.js
new file mode 100644
index 0000000..490b95d
--- /dev/null
+++ b/examples/braces.js
@@ -0,0 +1,13 @@
+'use strict';
+
+var braces = require('..');
+// console.log(braces('some/path/{a,b,c}'));
+// console.log(braces.expand('some/path/{a,b,c}'));
+
+var res = braces('{1..10000000}');
+console.log(res);
+console.log(format(res[0].length));
+
+var res = braces('{1..100000000}');
+console.log(res);
+console.log(format(res[0].length));
diff --git a/examples/expand.js b/examples/expand.js
index e3a9af8..a84ec08 100644
--- a/examples/expand.js
+++ b/examples/expand.js
@@ -5,3 +5,11 @@ var braces = require('..');
 
 console.log(braces.expand('a/{b,c}/d'));
 //=> [ 'a/b/d', 'a/c/d' ]
+
+var ast = braces.parse('a${b{a,b}}c', {sourcemap: true});
+var res = braces.compile(ast);
+console.log(res.ast)
+
+
+console.log(braces.expand('{1..100}{1..100}').length);
+console.log(braces.expand('{1..10}{1..10}{1..10}').length);
diff --git a/examples/extglobs.js b/examples/extglobs.js
new file mode 100644
index 0000000..e52e293
--- /dev/null
+++ b/examples/extglobs.js
@@ -0,0 +1,4 @@
+'use strict';
+
+var braces = require('..');
+console.log(braces('{!(a|b),!(c|d)}'));
diff --git a/examples/nested.js b/examples/nested-regex.js
similarity index 100%
copy from examples/nested.js
copy to examples/nested-regex.js
diff --git a/examples/nested.js b/examples/nested.js
index 6d6d532..3677928 100644
--- a/examples/nested.js
+++ b/examples/nested.js
@@ -1,12 +1,49 @@
-'use strict';
+var util = require('util');
+var braces = require('..').expand;
+var expand = require('brace-expansion');
+var bash = require('../test/support/bash');
 
-var minimatch = require('minimatch');
-var braces = require('..');
-
-console.log('braces');
-console.log(braces.makeRe('a{b,c{1..100}/{foo/bar},h}x/z'));
-console.log();
-console.log();
-console.log('brace-expansion');
-console.log(minimatch.makeRe('a{b,c{1..100}/{foo/bar},h}x/z'));
+function log(pattern) {
+  // console.log('---');
+  console.log('equal(\'' + pattern + '\', ', util.inspect(braces(pattern).sort()).split('\n').join('') + ');');
+  // console.log('equal(\'' + pattern + '\', ', util.inspect(bash(pattern).sort()).split('\n').join('') + ');');
+  // console.log('equal(\'' + pattern + '\', ', util.inspect(expand(pattern).sort()).split('\n').join('') + ');');
+  // console.log(expand(pattern));
+  console.log();
+}
 
+log('{,eno,thro,ro}ugh')
+log('{,{,eno,thro,ro}ugh}{,out}')
+log('{{,eno,thro,ro}ugh,}{,out}')
+log('{,{,a,b}z}{,c}')
+log('{,{,a,b}z}{c,}')
+log('{,{,a,b}z}{,c,}')
+log('{,{,a,b}z}{c,d}')
+log('{{,a,b}z,}{,c}')
+log('{,a{,b}z,}{,c}')
+log('{,a{,b},}{,c}')
+log('{,a{,b}}{,c}')
+log('{,b}{,d}')
+log('{a,b}{,d}')
+log('{,a}{z,c}')
+log('{,{,a},}{z,c}')
+log('{,,a,}{z,c}')
+log('{{,a},}{z,c}')
+log('{,{,a},}{z,c}')
+log('{,{,a}}{z,c}')
+log('{{a,},}{z,c}')
+log('{{,a},}{z,c}')
+log('{,,a}{z,c}')
+log('{,a,}{z,c}')
+log('{,{,}}{z,c}')
+log('{,{a,b}}{,c}')
+log('{,{a,}}{,c}')
+log('{,{,b}}{,c}')
+log('{,{,}}{,c}')
+log('{,a}{,c}')
+log('{,{,a}b}')
+log('{,b}')
+log('{,b{,a}}')
+log('{b,{,a}}')
+log('{,b}{,d}')
+log('{a,b}{,d}')
diff --git a/examples/options.quantifiers.js b/examples/options.quantifiers.js
index 72c55e3..ff79a57 100644
--- a/examples/options.quantifiers.js
+++ b/examples/options.quantifiers.js
@@ -3,9 +3,24 @@
 var mm = require('minimatch');
 var braces = require('..');
 
+/**
+ * True
+ */
+
 console.log(braces('a/b{1,3}/{x,y,z}', {quantifiers: true}));
 //=> [ 'a/b(1|3)/(x|y|z)' ]
 console.log(braces('a/b{1,3}/{x,y,z}', {quantifiers: true}));
 //=> [ 'a/b{1,3}/(x|y|z)' ]
 console.log(braces('a/b{1,3}/{x,y,z}', {quantifiers: true, expand: true}));
 //=> [ 'a/b{1,3}/x', 'a/b{1,3}/y', 'a/b{1,3}/z' ]
+
+/**
+ * False
+ */
+
+console.log(braces('a/b{1,3}/{x,y,z}', {quantifiers: false}));
+//=> [ 'a/b(1|3)/(x|y|z)' ]
+console.log(braces('a/b{1,3}/{x,y,z}', {quantifiers: false}));
+//=> [ 'a/b{1,3}/(x|y|z)' ]
+console.log(braces('a/b{1,3}/{x,y,z}', {quantifiers: false, expand: true}));
+//=> [ 'a/b{1,3}/x', 'a/b{1,3}/y', 'a/b{1,3}/z' ]
diff --git a/examples/paths.js b/examples/paths.js
new file mode 100644
index 0000000..9eb4558
--- /dev/null
+++ b/examples/paths.js
@@ -0,0 +1,6 @@
+'use strict';
+
+var braces = require('..');
+
+var res = braces('foo*{/*,*}');
+console.log(res);
diff --git a/examples/readme.js b/examples/readme.js
new file mode 100644
index 0000000..53131a7
--- /dev/null
+++ b/examples/readme.js
@@ -0,0 +1,28 @@
+var braces = require('..');
+
+console.log(braces.expand('it{,{{em,alic}iz,erat}e{d,}}'));
+console.log(braces.expand('I like {pizza,beer,money}.'));
+// console.log(braces.expand('{bull,shoe}{,horn}'));
+
+console.log(braces.expand('{a,b}{1,2}'));
+//=> [ 'a1', 'a2', 'b1', 'b2' ]
+
+console.log(braces.expand('{a,b}/{1,2}'));
+//=> [ 'a/1', 'a/2', 'b/1', 'b/2' ]
+
+console.log(braces.expand('foo/{a,b,c}/bar').join(' '));
+console.log(braces('{a,b}{1,2}'));
+//=> [ '(a|b)(1|2)' ]
+
+console.log(braces.expand('{a,b,c}{1,2}').join(' '));
+console.log(braces.expand('{4..-4}').join(' '));
+//=> [ '(a|b)/(1|2)' ]
+
+console.log(braces.expand('{a,b,c}{1..3}').join(' '));
+console.log(braces.expand('{a..j}').join(' '));
+console.log(braces.expand('{j..a}').join(' '));
+console.log(braces.expand('{1..20..3}').join(' '));
+console.log(braces.expand('{a..z..3}').join(' '));
+console.log(braces.expand('a{1..3}b').join(' '));
+console.log(braces.expand('{1..3}.{0..9}0'));
+//=> [ 'a1b', 'a2b', 'a3b' ]
diff --git a/examples/regex.js b/examples/regex.js
index 3b3b8fa..711e8e2 100644
--- a/examples/regex.js
+++ b/examples/regex.js
@@ -12,6 +12,8 @@ var braces = require('..');
 // console.log('minimatch.makeRe: "foo/{1..20000}/bar/{a..j}/baz"');
 // console.log(mm.makeRe('foo/{1..20000}/bar/{a..j}/baz'));
 
-var re = braces.makeRe('a/{foo/bar}/z');
-console.log(re);
-console.log(re.test('a/{foo/bar}/z'));
+// var re = braces.makeRe('a/{foo/bar}/z');
+// console.log(re);
+// console.log(re.test('a/{foo/bar}/z'));
+
+console.log(braces.makeRe('{00000001..99999999}'));
diff --git a/examples/sequences.js b/examples/sequences.js
new file mode 100644
index 0000000..88dcf88
--- /dev/null
+++ b/examples/sequences.js
@@ -0,0 +1,9 @@
+'use strict';
+
+var mm = require('minimatch');
+var braces = require('..');
+
+console.log(braces('{1..5}', {expand: true}));
+//=> [ '1', '2', '3', '4', '5' ]
+
+console.log(braces('{"1..5"}', {expand: true}))
diff --git a/index.js b/index.js
index a6f6b4e..4006b72 100644
--- a/index.js
+++ b/index.js
@@ -7,7 +7,7 @@
 var toRegex = require('to-regex');
 var unique = require('array-unique');
 var extend = require('extend-shallow');
-var debug = require('debug')('braces');
+var define = require('define-property');
 
 /**
  * Local dependencies
@@ -18,11 +18,6 @@ var parsers = require('./lib/parsers');
 var Braces = require('./lib/braces');
 var utils = require('./lib/utils');
 var MAX_LENGTH = 1024 * 64;
-
-/**
- * Session cache (disable with `options.cache`)
- */
-
 var cache = {};
 
 /**
@@ -43,29 +38,30 @@ var cache = {};
  */
 
 function braces(pattern, options) {
-  debug('initializing from <%s>', __filename);
-
-  var key = utils.createKey(pattern, options);
-  options = options || {};
+  var key = utils.createKey(String(pattern), options);
+  var arr = [];
 
-  if (options.cache !== false && cache.hasOwnProperty(key)) {
+  var disabled = options && options.cache === false;
+  if (!disabled && cache.hasOwnProperty(key)) {
     return cache[key];
   }
 
-  var results = [];
   if (Array.isArray(pattern)) {
     for (var i = 0; i < pattern.length; i++) {
-      results.push.apply(results, braces.create(pattern[i], options));
-    }
-
-    if (options && options.nodupes === true) {
-      results = unique(results);
+      arr.push.apply(arr, braces.create(pattern[i], options));
     }
   } else {
-    results = braces.create(pattern, options);
+    arr = braces.create(pattern, options);
+  }
+
+  if (options && options.nodupes === true) {
+    arr = unique(arr);
   }
 
-  return (cache[key] = results);
+  if (!disabled) {
+    cache[key] = arr;
+  }
+  return arr;
 }
 
 /**
@@ -119,41 +115,46 @@ braces.optimize = function(pattern, options) {
  */
 
 braces.create = function(pattern, options) {
-  if (pattern instanceof RegExp) {
-    pattern = pattern.source;
-  }
-
   if (typeof pattern !== 'string') {
     throw new TypeError('expected a string');
   }
 
-  if (pattern.length > MAX_LENGTH) {
+  if (pattern.length >= MAX_LENGTH) {
     throw new Error('expected pattern to be less than ' + MAX_LENGTH + ' characters');
   }
 
   function create() {
-    if (pattern === '' || pattern.length <= 2) {
+    if (pattern === '' || pattern.length < 3) {
       return [pattern];
     }
 
-    if (/^(?:{,})+$/.test(pattern)) {
+    if (utils.isEmptySets(pattern)) {
       return [];
     }
 
-    var quoted = /^(['"`])(.*)(\1)$/g.exec(pattern);
-    if (quoted) {
-      return [quoted[2]];
+    if (utils.isQuotedString(pattern)) {
+      return [pattern.slice(1, -1)];
     }
 
     var proto = new Braces(options);
-    var arr = !options || !options.expand
+    var result = !options || options.expand !== true
       ? proto.optimize(pattern, options)
       : proto.expand(pattern, options);
 
+    // get the generated pattern(s)
+    var arr = result.output;
+
+    // filter out empty strings if specified
+    if (options && options.noempty === true) {
+      arr = arr.filter(Boolean);
+    }
+
+    // filter out duplicates if specified
     if (options && options.nodupes === true) {
-      return unique(arr);
+      arr = unique(arr);
     }
 
+    define(arr, 'result', result);
     return arr;
   }
 
@@ -165,8 +166,9 @@ braces.create = function(pattern, options) {
  *
  * ```js
  * var braces = require('braces');
- * console.log(braces.makeRe('user-{200..300}'))
- * //=> /^(?:user-(20[0-9]|2[1-9][0-9]|300))$/
+ *
+ * console.log(braces.makeRe('id-{200..300}'));
+ * //=> /^(?:id-(20[0-9]|2[1-9][0-9]|300))$/
  * ```
  * @param {String} `pattern` The pattern to convert to regex.
  * @param {Object} `options`
@@ -175,23 +177,16 @@ braces.create = function(pattern, options) {
  */
 
 braces.makeRe = function(pattern, options) {
-  if (pattern instanceof RegExp) {
-    return pattern;
-  }
-
   if (typeof pattern !== 'string') {
     throw new TypeError('expected a string');
   }
 
-  if (pattern.length > MAX_LENGTH) {
+  if (pattern.length >= MAX_LENGTH) {
     throw new Error('expected pattern to be less than ' + MAX_LENGTH + ' characters');
   }
 
   function makeRe() {
     var arr = braces(pattern, options);
-    for (var i = 0; i < arr.length; i++) {
-      arr[i] = arr[i].replace(/([{}])/g, '\\$1');
-    }
     var opts = extend({strictErrors: false}, options);
     return toRegex(arr, opts);
   }
@@ -220,15 +215,15 @@ braces.makeRe = function(pattern, options) {
  * //      { type: 'text', val: '/d' },
  * //      { type: 'eos', val: '' } ] }
  * ```
- * @param {String} `str`
+ * @param {String} `pattern` Brace pattern to parse
  * @param {Object} `options`
  * @return {Object} Returns an AST
  * @api public
  */
 
-braces.parse = function(str, options) {
-  var inst = new Braces(options);
-  return inst.parse(str, options);
+braces.parse = function(pattern, options) {
+  var proto = new Braces(options);
+  return proto.parse(pattern, options);
 };
 
 /**
@@ -253,48 +248,66 @@ braces.parse = function(str, options) {
  * //    { ... },
  * //   parsingErrors: [] }
  * ```
- * @param {Object|String} `ast`
+ * @param {Object|String} `ast` AST from [.parse](#parse). If a string is passed it will be parsed first.
  * @param {Object} `options`
  * @return {Object} Returns an object that has an `output` property with the compiled string.
  * @api public
  */
 
 braces.compile = function(ast, options) {
-  var inst = new Braces(options);
-  return inst.compile(ast, options);
+  var proto = new Braces(options);
+  return proto.compile(ast, options);
+};
+
+/**
+ * Clear the regex cache.
+ *
+ * ```js
+ * braces.clearCache();
+ * ```
+ * @api public
+ */
+
+braces.clearCache = function() {
+  cache = braces.cache = {};
 };
 
 /**
- * Memoize if `options.cache` is not false
+ * Memoize a generated regex or function. A unique key is generated
+ * from the method name, pattern, and user-defined options. Set
+ * options.memoize to false to disable.
  */
 
 function memoize(type, pattern, options, fn) {
   var key = utils.createKey(type + ':' + pattern, options);
-  options = options || {};
+  var disabled = options && options.cache === false;
+  if (disabled) {
+    braces.clearCache();
+    return fn(pattern, options);
+  }
 
-  if (options.cache !== false && cache.hasOwnProperty(key)) {
+  if (cache.hasOwnProperty(key)) {
     return cache[key];
   }
 
   var res = fn(pattern, options);
-  if (options.cache !== false) {
-    cache[key] = res;
-  }
+  cache[key] = res;
   return res;
 }
 
 /**
- * Expose `braces`
+ * Expose `Braces` constructor and methods
  * @type {Function}
  */
 
-module.exports = braces;
+braces.Braces = Braces;
+braces.compilers = compilers;
+braces.parsers = parsers;
+braces.cache = cache;
 
 /**
- * Expose `Braces` constructor
+ * Expose `braces`
  * @type {Function}
  */
 
-module.exports.Braces = Braces;
-module.exports.compilers = compilers;
-module.exports.parsers = parsers;
+module.exports = braces;
diff --git a/lib/braces.js b/lib/braces.js
index 0c58873..2deea3a 100644
--- a/lib/braces.js
+++ b/lib/braces.js
@@ -1,6 +1,5 @@
 'use strict';
 
-var debug = require('debug')('braces');
 var Snapdragon = require('snapdragon');
 var compilers = require('./compilers');
 var parsers = require('./parsers');
@@ -11,7 +10,6 @@ var utils = require('./utils');
  */
 
 function Braces(options) {
-  debug('initializing from <%s>', __filename);
   this.options = utils.extend({}, options);
 }
 
@@ -25,8 +23,8 @@ Braces.prototype.init = function(options) {
   this.compiler = this.snapdragon.compiler;
   this.parser = this.snapdragon.parser;
 
-  compilers(this.snapdragon);
-  parsers(this.snapdragon);
+  compilers(this.snapdragon, opts);
+  parsers(this.snapdragon, opts);
 
   /**
    * Call Snapdragon `.parse` method. When AST is returned, we check to
@@ -81,9 +79,13 @@ Braces.prototype.parse = function(ast, options) {
  */
 
 Braces.prototype.compile = function(ast, options) {
-  if (typeof ast === 'string') ast = this.parse(ast, options);
-  this.lazyInit(options);
-  return this.snapdragon.compile(ast, options);
+  if (typeof ast === 'string') {
+    ast = this.parse(ast, options);
+  } else {
+    this.lazyInit(options);
+  }
+  var res = this.snapdragon.compile(ast, options);
+  return res;
 };
 
 /**
@@ -92,8 +94,7 @@ Braces.prototype.compile = function(ast, options) {
 
 Braces.prototype.expand = function(pattern) {
   var ast = this.parse(pattern, {expand: true});
-  var res = this.compile(ast, {expand: true});
-  return res.output;
+  return this.compile(ast, {expand: true});
 };
 
 /**
@@ -102,31 +103,10 @@ Braces.prototype.expand = function(pattern) {
 
 Braces.prototype.optimize = function(pattern) {
   var ast = this.parse(pattern, {optimize: true});
-  var res = this.compile(ast, {optimize: true});
-  return res.output;
+  return this.compile(ast, {optimize: true});
 };
 
 /**
- * Visit `node` with the given `fn`
- */
-
-function visit(node, fn) {
-  return node.nodes ? mapVisit(node.nodes, fn) : fn(node);
-}
-
-/**
- * Map visit over array of `nodes`.
- */
-
-function mapVisit(nodes, fn) {
-  var len = nodes.length;
-  var idx = -1;
-  while (++idx < len) {
-    visit(nodes[idx], fn);
-  }
-}
-
-/**
  * Expose `Braces`
  */
 
diff --git a/lib/compilers.js b/lib/compilers.js
index 46bfdc3..a3b820e 100644
--- a/lib/compilers.js
+++ b/lib/compilers.js
@@ -2,7 +2,7 @@
 
 var utils = require('./utils');
 
-module.exports = function(braces) {
+module.exports = function(braces, options) {
   braces.compiler
 
     /**
@@ -10,6 +10,7 @@ module.exports = function(braces) {
      */
 
     .set('bos', function() {
+      if (this.output) return;
       this.ast.queue = isEscaped(this.ast) ? [this.ast.val] : [];
       this.ast.count = 1;
     })
@@ -68,19 +69,23 @@ module.exports = function(braces) {
 
     .set('text', function(node) {
       var queue = node.parent.queue;
-      var segs = [node.val];
       var escaped = node.escaped;
+      var segs = [node.val];
+
+      if (node.optimize === false) {
+        options = utils.extend({}, options, {optimize: false});
+      }
 
       if (node.multiplier > 1) {
         node.parent.count *= node.multiplier;
       }
 
-      if (this.options.quantifiers === true && /^(\d,\d|,\d|\d,)$/.test(node.val)) {
+      if (options.quantifiers === true && utils.isQuantifier(node.val)) {
         escaped = true;
 
       } else if (node.val.length > 1) {
         if (isType(node.parent, 'brace') && !isEscaped(node)) {
-          var expanded = utils.expand(node.val, this.options);
+          var expanded = utils.expand(node.val, options);
           segs = expanded.segs;
 
           if (expanded.isOptimized) {
@@ -89,11 +94,13 @@ module.exports = function(braces) {
 
           // if nothing was expanded, we probably have a literal brace
           if (!segs.length) {
-            var val = (expanded.val || node.val)
+            var val = (expanded.val || node.val);
+            if (options.unescape !== false) {
               // unescape unexpanded brace sequence/set separators
-              .replace(/\\([,.])/g, '$1')
+              val = val.replace(/\\([,.])/g, '$1');
               // strip quotes
-              .replace(/["']/g, '');
+              val = val.replace(/["'`]/g, '');
+            }
 
             segs = [val];
             escaped = true;
@@ -101,8 +108,8 @@ module.exports = function(braces) {
         }
 
       } else if (node.val === ',') {
-        if (this.options.expand) {
-          node.parent.queue.push([]);
+        if (options.expand) {
+          node.parent.queue.push(['']);
           segs = [''];
         } else {
           segs = ['|'];
@@ -125,13 +132,12 @@ module.exports = function(braces) {
       }
 
       var last = utils.arrayify(queue.pop());
-      if (node.parent.count > 1 && this.options.expand) {
+      if (node.parent.count > 1 && options.expand) {
         last = multiply(last, node.parent.count);
         node.parent.count = 1;
       }
 
-      var temp = utils.join(utils.flatten(last), segs.shift());
-      queue.push(temp);
+      queue.push(utils.join(utils.flatten(last), segs.shift()));
       queue.push.apply(queue, segs);
     })
 
@@ -146,7 +152,7 @@ module.exports = function(braces) {
       var open = node.parent.open;
       var close = node.val;
 
-      if (open && close && isOptimized(node, this.options)) {
+      if (open && close && isOptimized(node, options)) {
         open = '(';
         close = ')';
       }
@@ -154,7 +160,7 @@ module.exports = function(braces) {
       // if a close brace exists, and the previous segment is one character
       // don't wrap the result in braces or parens
       var ele = utils.last(queue);
-      if (node.parent.count > 1 && this.options.expand) {
+      if (node.parent.count > 1 && options.expand) {
         ele = multiply(queue.pop(), node.parent.count);
         node.parent.count = 1;
         queue.push(ele);
@@ -165,13 +171,16 @@ module.exports = function(braces) {
         close = '';
       }
 
-      if (isLiteralBrace(node, this.options) || noInner(node)) {
+      if ((isLiteralBrace(node, options) || noInner(node)) && !node.parent.hasEmpty) {
         queue.push(utils.join(open, queue.pop() || ''));
         queue = utils.flatten(utils.join(queue, close));
       }
 
-      var arr = utils.flatten(utils.join(last, queue));
-      prev.queue.push(arr);
+      if (typeof last === 'undefined') {
+        prev.queue = [queue];
+      } else {
+        prev.queue.push(utils.flatten(utils.join(last, queue)));
+      }
     })
 
     /**
@@ -179,7 +188,9 @@ module.exports = function(braces) {
      */
 
     .set('eos', function(node) {
-      if (this.options.optimize !== false) {
+      if (this.input) return;
+
+      if (options.optimize !== false) {
         this.output = utils.last(utils.flatten(this.ast.queue));
       } else if (Array.isArray(utils.last(this.ast.queue))) {
         this.output = utils.flatten(this.ast.queue.pop());
@@ -187,11 +198,12 @@ module.exports = function(braces) {
         this.output = utils.flatten(this.ast.queue);
       }
 
-      if (node.parent.count > 1 && this.options.expand) {
+      if (node.parent.count > 1 && options.expand) {
         this.output = multiply(this.output, node.parent.count);
       }
 
       this.output = utils.arrayify(this.output);
+      this.ast.queue = [];
     });
 
 };
diff --git a/lib/parsers.js b/lib/parsers.js
index ce756ba..42730fe 100644
--- a/lib/parsers.js
+++ b/lib/parsers.js
@@ -1,39 +1,95 @@
 'use strict';
 
+var Node = require('snapdragon-node');
 var utils = require('./utils');
-var regexNot = require('regex-not');
-var toRegex = require('to-regex');
 
 /**
- * Characters to use in negation regex (we want to "not" match
- * characters that are matched by other parsers)
+ * Braces parsers
  */
 
-var cached;
-var regex = textRegex();
+module.exports = function(braces, options) {
+  braces.parser
+    .set('bos', function() {
+      if (!this.parsed) {
+        this.ast = this.nodes[0] = new Node(this.ast);
+      }
+    })
 
-/**
- * Braces parsers
- */
+    /**
+     * Character parsers
+     */
+
+    .set('escape', function() {
+      var pos = this.position();
+      var m = this.match(/^(?:\\(.)|\$\{)/);
+      if (!m) return;
+
+      var prev = this.prev();
+      var last = utils.last(prev.nodes);
+
+      var node = pos(new Node({
+        type: 'text',
+        multiplier: 1,
+        val: m[0]
+      }));
+
+      if (node.val === '\\\\') {
+        return node;
+      }
+
+      if (node.val === '${') {
+        var str = this.input;
+        var idx = -1;
+        var ch;
+
+        while ((ch = str[++idx])) {
+          this.consume(1);
+          node.val += ch;
+          if (ch === '\\') {
+            node.val += str[++idx];
+            continue;
+          }
+          if (ch === '}') {
+            break;
+          }
+        }
+      }
 
-module.exports = function(brace) {
-  brace.parser.sets.brace = brace.parser.sets.brace || [];
-  brace.parser
+      if (this.options.unescape !== false) {
+        node.val = node.val.replace(/\\([{}])/g, '$1');
+      }
+
+      if (last.val === '"' && this.input.charAt(0) === '"') {
+        last.val = node.val;
+        this.consume(1);
+        return;
+      }
+
+      return concatNodes.call(this, pos, node, prev, options);
+    })
 
     /**
-     * Brackets: "[...]" (basic, this can be overridden by other parsers)
+     * Brackets: "[...]" (basic, this is overridden by
+     * other parsers in more advanced implementations)
      */
 
-    .capture('bracket', function() {
+    .set('bracket', function() {
+      var isInside = this.isInside('brace');
       var pos = this.position();
       var m = this.match(/^(?:\[([!^]?)([^\]]{2,}|\]\-)(\]|[^*+?]+)|\[)/);
       if (!m) return;
 
+      var prev = this.prev();
       var val = m[0];
       var negated = m[1] ? '^' : '';
       var inner = m[2] || '';
       var close = m[3] || '';
 
+      if (isInside && prev.type === 'brace') {
+        prev.text = prev.text || '';
+        prev.text += val;
+      }
+
       var esc = this.input.slice(0, 2);
       if (inner === '' && esc === '\\]') {
         inner += esc;
@@ -53,186 +109,252 @@ module.exports = function(brace) {
         }
       }
 
-      return pos({
+      return pos(new Node({
         type: 'bracket',
         val: val,
         escaped: close !== ']',
         negated: negated,
         inner: inner,
         close: close
-      });
+      }));
     })
 
     /**
-     * Character parsers
+     * Empty braces (we capture these early to
+     * speed up processing in the compiler)
      */
 
-    .capture('escape', function() {
+    .set('multiplier', function() {
+      var isInside = this.isInside('brace');
       var pos = this.position();
-      var m = this.match(/^(?:\\(.)|\$\{)/);
+      var m = this.match(/^\{(,+(?:(\{,+\})*),*|,*(?:(\{,+\})*),+)\}/);
       if (!m) return;
 
-      var node = pos({
-        type: 'text',
-        multiplier: 1,
-        val: m[0]
-      });
+      this.multiplier = true;
+      var prev = this.prev();
+      var val = m[0];
 
-      if (node.val === '\\\\') {
-        return node;
+      if (isInside && prev.type === 'brace') {
+        prev.text = prev.text || '';
+        prev.text += val;
       }
 
-      var chars = {'${': '}', '`': '`', '"': '"'};
-      var val = node.val;
-      if (chars[val]) {
-        var str = this.input;
-        var idx = -1;
-        var ch;
-
-        while ((ch = str[++idx])) {
-          this.consume(1);
-          node.val += ch;
-          if (ch === '\\') {
-            ch += str[++idx];
-            node.val += ch;
-          }
-          if (ch === chars[val]) {
-            break;
-          }
-        }
-      }
-
-      if (this.options.unescape !== false) {
-        node.val = node.val.replace(/\\([{}])/g, '$1');
-      }
+      var node = pos(new Node({
+        type: 'text',
+        multiplier: 1,
+        match: m,
+        val: val
+      }));
 
-      return concatNodes.call(this, pos, node);
+      return concatNodes.call(this, pos, node, prev, options);
     })
 
     /**
      * Open
      */
 
-    .capture('brace.open', function() {
+    .set('brace.open', function() {
       var pos = this.position();
       var m = this.match(/^\{(?!(?:[^\\}]?|,+)\})/);
       if (!m) return;
 
       var prev = this.prev();
-      var val = m[0];
+      var last = utils.last(prev.nodes);
 
-      var open = pos({
+      // if the last parsed character was an extglob character
+      // we need to _not optimize_ the brace pattern because
+      // it might be mistaken for an extglob by a downstream parser
+      if (last && last.val && isExtglobChar(last.val.slice(-1))) {
+        last.optimize = false;
+      }
+
+      var open = pos(new Node({
         type: 'brace.open',
-        val: val
-      });
+        val: m[0]
+      }));
 
-      var node = pos({
+      var node = pos(new Node({
         type: 'brace',
-        nodes: [open]
-      });
+        nodes: []
+      }));
 
-      utils.define(node, 'parent', prev);
-      utils.define(open, 'parent', node);
+      node.push(open);
+      prev.push(node);
       this.push('brace', node);
-      prev.nodes.push(node);
     })
 
     /**
      * Close
      */
 
-    .capture('brace.close', function() {
+    .set('brace.close', function() {
       var pos = this.position();
       var m = this.match(/^\}/);
       if (!m || !m[0]) return;
 
       var brace = this.pop('brace');
-      var node = pos({
+      var node = pos(new Node({
         type: 'brace.close',
         val: m[0]
-      });
+      }));
 
       if (!this.isType(brace, 'brace')) {
         if (this.options.strict) {
           throw new Error('missing opening "{"');
         }
         node.type = 'text';
-        node.multiplier = 1;
+        node.multiplier = 0;
         node.escaped = true;
         return node;
       }
 
-      brace.nodes.push(node);
-      utils.define(node, 'parent', brace);
+      var prev = this.prev();
+      var last = utils.last(prev.nodes);
+      if (last.text) {
+        var lastNode = utils.last(last.nodes);
+        if (lastNode.val === ')' && /[!@*?+]\(/.test(last.text)) {
+          var open = last.nodes[0];
+          var text = last.nodes[1];
+          if (open.type === 'brace.open' && text && text.type === 'text') {
+            text.optimize = false;
+          }
+        }
+      }
+
+      if (brace.nodes.length > 2) {
+        var first = brace.nodes[1];
+        if (first.type === 'text' && first.val === ',') {
+          brace.nodes.splice(1, 1);
+          brace.nodes.push(first);
+        }
+      }
+
+      brace.push(node);
     })
 
     /**
-     * Inner
+     * Capture boundary characters
      */
 
-    .capture('text', function() {
+    .set('boundary', function() {
       var pos = this.position();
-      var m = this.match(regex);
+      var m = this.match(/^[$^](?!\{)/);
       if (!m) return;
+      return pos(new Node({
+        type: 'text',
+        val: m[0]
+      }));
+    })
 
-      var node = pos({
+    /**
+     * One or zero, non-comma characters wrapped in braces
+     */
+
+    .set('nobrace', function() {
+      var isInside = this.isInside('brace');
+      var pos = this.position();
+      var m = this.match(/^\{[^,]?\}/);
+      if (!m) return;
+
+      var prev = this.prev();
+      var val = m[0];
+
+      if (isInside && prev.type === 'brace') {
+        prev.text = prev.text || '';
+        prev.text += val;
+      }
+
+      return pos(new Node({
+        type: 'text',
+        multiplier: 0,
+        val: val
+      }));
+    })
+
+    /**
+     * Text
+     */
+
+    .set('text', function() {
+      var isInside = this.isInside('brace');
+      var pos = this.position();
+      var m = this.match(/^((?!\\)[^${}\[\]])+/);
+      if (!m) return;
+
+      var prev = this.prev();
+      var val = m[0];
+
+      if (isInside && prev.type === 'brace') {
+        prev.text = prev.text || '';
+        prev.text += val;
+      }
+
+      var node = pos(new Node({
         type: 'text',
         multiplier: 1,
-        val: m[0]
-      });
+        val: val
+      }));
 
-      return concatNodes.call(this, pos, node);
+      return concatNodes.call(this, pos, node, prev, options);
     });
-
 };
 
 /**
+ * Returns true if the character is an extglob character.
+ */
+
+function isExtglobChar(ch) {
+  return ch === '!' || ch === '@' || ch === '*' || ch === '?' || ch === '+';
+}
+
+/**
  * Combine text nodes, and calculate empty sets (`{,,}`)
  * @param {Function} `pos` Function to calculate node position
  * @param {Object} `node` AST node
  * @return {Object}
  */
 
-function concatNodes(pos, node) {
+function concatNodes(pos, node, parent, options) {
+  node.orig = node.val;
   var prev = this.prev();
   var last = utils.last(prev.nodes);
-  var re = /^\{(,+)\}/;
-  var multi;
+  var isEscaped = false;
+
+  if (node.val.length > 1) {
+    var a = node.val.charAt(0);
+    var b = node.val.slice(-1);
 
-  var a = node.val.charAt(0);
-  var b = node.val.slice(-1);
+    isEscaped = (a === '"' && b === '"')
+      || (a === "'" && b === "'")
+      || (a === '`' && b === '`');
+  }
 
-  if ((a === '"' && b === '"') || (a === '`' && b === '`') || (a === "'" && b === "'")) {
+  if (isEscaped && options.unescape !== false) {
     node.val = node.val.slice(1, node.val.length - 1);
     node.escaped = true;
   }
 
-  if (re.test(node.val)) {
-    this.input = node.val + this.input;
+  if (node.match) {
+    var match = node.match[1];
+    if (!match || match.indexOf('}') === -1) {
+      match = node.match[0];
+    }
+
+    // replace each set with a single ","
+    var val = match.replace(/\{/g, ',').replace(/\}/g, '');
+    node.multiplier *= val.length;
     node.val = '';
   }
 
-  while ((multi = re.exec(this.input))) {
-    this.consume(multi[0].length);
-    node.multiplier *= multi[1].length + 1;
-  }
+  var simpleText = last.type === 'text'
+    && last.multiplier === 1
+    && node.multiplier === 1
+    && node.val;
 
-  if (last.type === 'text' && node.val && node.multiplier === 1 && last.multiplier === 1) {
+  if (simpleText) {
     last.val += node.val;
     return;
   }
 
-  return node;
-}
-
-/**
- * Create and cache regex to use for text nodes
- */
-
-function textRegex(pattern) {
-  if (cached) return cached;
-  var opts = {contains: true, strictClose: false};
-  var not = regexNot.create('(\\$?\\{|\\\\.|\\}|[\\[\\]])', opts);
-  var re = toRegex('^(?:\\{(,+|.?)\\}|' + not + ')', opts);
-  return (cached = re);
+  prev.push(node);
 }
diff --git a/lib/utils.js b/lib/utils.js
index 36eba65..4d2131a 100644
--- a/lib/utils.js
+++ b/lib/utils.js
@@ -1,5 +1,6 @@
 'use strict';
 
+var splitString = require('split-string');
 var utils = module.exports;
 
 /**
@@ -10,27 +11,47 @@ utils.define = require('define-property');
 utils.extend = require('extend-shallow');
 utils.flatten = require('arr-flatten');
 utils.isObject = require('isobject');
-utils.range = require('fill-range');
+utils.fillRange = require('fill-range');
 utils.repeat = require('repeat-element');
 utils.unique = require('array-unique');
 
 /**
+ * Returns true if the given string contains only empty brace sets.
+ */
+
+utils.isEmptySets = function(str) {
+  return /^(?:\{,\})+$/.test(str);
+};
+
+/**
+ * Returns true if the given string contains only empty brace sets.
+ */
+
+utils.isQuotedString = function(str) {
+  var open = str.charAt(0);
+  if (open === '\'' || open === '"' || open === '`') {
+    return str.slice(-1) === open;
+  }
+  return false;
+};
+
+/**
  * Create the key to use for memoization. The unique key is generated
  * by iterating over the options and concatenating key-value pairs
  * to the pattern string.
  */
 
 utils.createKey = function(pattern, options) {
-  var key = pattern;
+  var id = pattern;
   if (typeof options === 'undefined') {
-    return key;
+    return id;
   }
-  for (var prop in options) {
-    if (options.hasOwnProperty(prop)) {
-      key += ';' + prop + '=' + String(options[prop]);
-    }
+  var keys = Object.keys(options);
+  for (var i = 0; i < keys.length; i++) {
+    var key = keys[i];
+    id += ';' + key + '=' + String(options[key]);
   }
-  return key;
+  return id;
 };
 
 /**
@@ -59,6 +80,7 @@ utils.join = function(a, b, options) {
   options = options || {};
   a = utils.arrayify(a);
   b = utils.arrayify(b);
+
   if (!a.length) return b;
   if (!b.length) return a;
 
@@ -67,20 +89,22 @@ utils.join = function(a, b, options) {
   var arr = [];
 
   while (++idx < len) {
-    var str = a[idx];
-    if (Array.isArray(str)) {
-      str = str.map(function(ele) {
-        return utils.join(ele, b);
-      });
-      arr.push(str);
+    var val = a[idx];
+    if (Array.isArray(val)) {
+      for (var i = 0; i < val.length; i++) {
+        val[i] = utils.join(val[i], b, options);
+      }
+      arr.push(val);
       continue;
     }
 
-    for (var i = 0; i < b.length; i++) {
-      if (Array.isArray(b[i])) {
-        arr.push(utils.join(str, b[i]));
+    for (var j = 0; j < b.length; j++) {
+      var bval = b[j];
+
+      if (Array.isArray(bval)) {
+        arr.push(utils.join(val, bval, options));
       } else {
-        arr.push(str + b[i]);
+        arr.push(val + bval);
       }
     }
   }
@@ -91,46 +115,17 @@ utils.join = function(a, b, options) {
  * Split the given string on `,` if not escaped.
  */
 
-utils.split = function(str) {
-  var segs = str.split(',');
-  var tok = {rest: ''};
-  var res = [];
-
-  while (segs.length) {
-    var key = segs.shift();
-    var next = segs[segs.length - 1];
-    var quoted;
-
-    while (key.slice(-1) === '\\' || (quoted = isQuote(key, next))) {
-      if (quoted) tok.quoted = true;
-      tok.escaped = true;
-      var ch = segs.shift();
-      if (!ch) break;
-      key = key.slice(0, -1) + ',' + (quoted ? ch.slice(1) : ch);
-    }
-    res.push(key);
+utils.split = function(str, options) {
+  var opts = utils.extend({sep: ','}, options);
+  if (typeof opts.keepQuotes !== 'boolean') {
+    opts.keepQuotes = true;
   }
-
-  if (tok.quoted) {
-    tok.val = res[0];
+  if (opts.unescape === false) {
+    opts.keepEscaping = true;
   }
-
-  tok.segs = res;
-  return tok;
+  return splitString(str, opts, utils.escapeBrackets(opts));
 };
 
-function isDoubleQuote(key, next) {
-  return key.slice(-1) === '"' && next.charAt(0) === '"';
-}
-
-function isSingleQuote(key, next) {
-  return key.slice(-1) === "'" && next.charAt(0) === "'";
-}
-
-function isQuote(key, next) {
-  return isDoubleQuote(key, next) || isSingleQuote(key, next);
-}
-
 /**
  * Expand ranges or sets in the given `pattern`.
  *
@@ -140,9 +135,17 @@ function isQuote(key, next) {
  */
 
 utils.expand = function(str, options) {
-  var opts = utils.extend({rangeLimit: 250}, options);
-  var tok = utils.split(str);
-  var segs = tok.segs;
+  var opts = utils.extend({rangeLimit: 10000}, options);
+  var segs = utils.split(str, opts);
+  var tok = { segs: segs };
+
+  if (utils.isQuotedString(str)) {
+    return tok;
+  }
+
+  if (opts.rangeLimit === true) {
+    opts.rangeLimit = 10000;
+  }
 
   if (segs.length > 1) {
     if (opts.optimize === false) {
@@ -150,12 +153,12 @@ utils.expand = function(str, options) {
       return tok;
     }
 
-    tok.segs = utils.stringify(tok.segs);
+    tok.segs = utils.stringifyArray(tok.segs);
   } else if (segs.length === 1) {
     var arr = str.split('..');
 
     if (arr.length === 1) {
-      tok.val = tok.segs.pop() || tok.val || str;
+      tok.val = tok.segs[tok.segs.length - 1] || tok.val || str;
       tok.segs = [];
       return tok;
     }
@@ -177,15 +180,14 @@ utils.expand = function(str, options) {
         var min = Math.min(arr[0], arr[1]);
         var max = Math.max(arr[0], arr[1]);
         var step = arr[2] || 1;
-        if (((max - min) / step) >= opts.rangeLimit) {
-          opts.optimize = true;
-          tok.isOptimized = true;
-          delete opts.expand;
+
+        if (opts.rangeLimit !== false && ((max - min) / step >= opts.rangeLimit)) {
+          throw new RangeError('expanded array length exceeds range limit. Use options.rangeLimit to increase or disable the limit.');
         }
       }
 
       arr.push(opts);
-      tok.segs = utils.range.apply(null, arr);
+      tok.segs = utils.fillRange.apply(null, arr);
 
       if (!tok.segs.length) {
         tok.escaped = true;
@@ -194,7 +196,7 @@ utils.expand = function(str, options) {
       }
 
       if (opts.optimize === true) {
-        tok.segs = utils.stringify(tok.segs);
+        tok.segs = utils.stringifyArray(tok.segs);
       }
 
       if (tok.segs === '') {
@@ -211,11 +213,86 @@ utils.expand = function(str, options) {
 };
 
 /**
+ * Ensure commas inside brackets and parens are not split.
+ * @param {Object} `tok` Token from the `split-string` module
+ * @return {undefined}
+ */
+
+utils.escapeBrackets = function(options) {
+  return function(tok) {
+    if (tok.escaped && tok.val === 'b') {
+      tok.val = '\\b';
+      return;
+    }
+
+    if (tok.val !== '(' && tok.val !== '[') return;
+    var opts = utils.extend({}, options);
+    var brackets = [];
+    var parens = [];
+    var stack = [];
+    var val = tok.val;
+    var str = tok.str;
+    var i = tok.idx - 1;
+
+    while (++i < str.length) {
+      var ch = str[i];
+
+      if (ch === '\\') {
+        val += (opts.keepEscaping === false ? '' : ch) + str[++i];
+        continue;
+      }
+
+      if (ch === '(') {
+        parens.push(ch);
+        stack.push(ch);
+      }
+
+      if (ch === '[') {
+        brackets.push(ch);
+        stack.push(ch);
+      }
+
+      if (ch === ')') {
+        parens.pop();
+        stack.pop();
+        if (!stack.length) {
+          val += ch;
+          break;
+        }
+      }
+
+      if (ch === ']') {
+        brackets.pop();
+        stack.pop();
+        if (!stack.length) {
+          val += ch;
+          break;
+        }
+      }
+      val += ch;
+    }
+
+    tok.split = false;
+    tok.val = val.slice(1);
+    tok.idx = i;
+  };
+};
+
+/**
+ * Returns true if the given string looks like a regex quantifier
+ * @return {Boolean}
+ */
+
+utils.isQuantifier = function(str) {
+  return /^(?:[0-9]?,[0-9]|[0-9],)$/.test(str);
+};
+
+/**
  * Cast `val` to an array.
  * @param {*} `val`
  */
 
-utils.stringify = function(arr) {
+utils.stringifyArray = function(arr) {
   return [utils.arrayify(arr).join('|')];
 };
 
@@ -225,8 +302,13 @@ utils.stringify = function(arr) {
  */
 
 utils.arrayify = function(arr) {
-  if (typeof arr === 'undefined') return [];
-  return Array.isArray(arr) ? arr : [arr];
+  if (typeof arr === 'undefined') {
+    return [];
+  }
+  if (typeof arr === 'string') {
+    return [arr];
+  }
+  return arr;
 };
 
 /**
@@ -244,10 +326,10 @@ utils.isString = function(str) {
  * @return {*}
  */
 
-utils.last = function(arr) {
-  return arr[arr.length - 1];
+utils.last = function(arr, n) {
+  return arr[arr.length - (n || 1)];
 };
 
 utils.escapeRegex = function(str) {
-  return str.replace(/\\?([!$^*?()\[\]{}+?/])/g, '\\$1');
+  return str.replace(/\\?([!^*?()\[\]{}+?/])/g, '\\$1');
 };
diff --git a/package.json b/package.json
index e7decb4..a2f47a6 100644
--- a/package.json
+++ b/package.json
@@ -1,19 +1,19 @@
 {
   "name": "braces",
-  "description": "Fast, comprehensive, bash-like brace expansion implemented in JavaScript. Complete support for the Bash 4.3 braces specification, without sacrificing speed.",
-  "version": "2.0.2",
-  "homepage": "https://github.com/jonschlinkert/braces",
+  "description": "Bash-like brace expansion, implemented in JavaScript. Safer than other brace expansion libs, with complete support for the Bash 4.3 braces specification, without sacrificing speed.",
+  "version": "2.3.0",
+  "homepage": "https://github.com/micromatch/braces",
   "author": "Jon Schlinkert (https://github.com/jonschlinkert)",
   "contributors": [
-    "Brian Woodward <brian.woodward at gmail.com> (https://github.com/doowb)",
+    "Brian Woodward (https://twitter.com/doowb)",
     "Elan Shanker (https://github.com/es128)",
     "Eugene Sharygin (https://github.com/eush77)",
-    "hemanth.hm <hemanth.hm at gmail.com> (http://h3manth.com)",
-    "Jon Schlinkert <jon.schlinkert at sellside.com> (http://twitter.com/jonschlinkert)"
+    "hemanth.hm (http://h3manth.com)",
+    "Jon Schlinkert (http://twitter.com/jonschlinkert)"
   ],
-  "repository": "jonschlinkert/braces",
+  "repository": "micromatch/braces",
   "bugs": {
-    "url": "https://github.com/jonschlinkert/braces/issues"
+    "url": "https://github.com/micromatch/braces/issues"
   },
   "license": "MIT",
   "files": [
@@ -25,41 +25,41 @@
     "node": ">=0.10.0"
   },
   "scripts": {
-    "test": "mocha"
+    "test": "mocha",
+    "benchmark": "node benchmark"
   },
   "dependencies": {
-    "arr-flatten": "^1.0.1",
+    "arr-flatten": "^1.1.0",
     "array-unique": "^0.3.2",
-    "debug": "^2.2.0",
-    "define-property": "^0.2.5",
+    "define-property": "^1.0.0",
     "extend-shallow": "^2.0.1",
-    "fill-range": "^3.0.3",
-    "isobject": "^2.1.0",
-    "regex-not": "^1.0.0",
+    "fill-range": "^4.0.0",
+    "isobject": "^3.0.1",
     "repeat-element": "^1.1.2",
     "snapdragon": "^0.8.1",
+    "snapdragon-node": "^2.0.1",
+    "split-string": "^3.0.2",
     "to-regex": "^3.0.1"
   },
   "devDependencies": {
     "ansi-cyan": "^0.1.1",
-    "benchmarked": "^0.2.5",
-    "brace-expansion": "^1.1.6",
-    "cross-spawn": "^4.0.2",
-    "fs-exists-sync": "^0.1.0",
+    "benchmarked": "^2.0.0",
+    "brace-expansion": "^1.1.8",
+    "cross-spawn": "^5.1.0",
     "gulp": "^3.9.1",
-    "gulp-eslint": "^3.0.1",
-    "gulp-format-md": "^0.1.11",
-    "gulp-istanbul": "^1.1.1",
+    "gulp-eslint": "^4.0.0",
+    "gulp-format-md": "^1.0.0",
+    "gulp-istanbul": "^1.1.2",
     "gulp-mocha": "^3.0.1",
-    "gulp-unused": "^0.2.0",
-    "is-windows": "^0.2.0",
-    "minimatch": "^3.0.3",
-    "mocha": "^3.1.2",
+    "gulp-unused": "^0.2.1",
+    "is-windows": "^1.0.1",
+    "minimatch": "^3.0.4",
+    "mocha": "^3.2.0",
     "noncharacters": "^1.1.0",
     "pretty-bytes": "^4.0.2",
     "text-table": "^0.2.0",
     "time-diff": "^0.3.1",
-    "yargs-parser": "^4.0.2"
+    "yargs-parser": "^8.0.0"
   },
   "keywords": [
     "alpha",
@@ -105,12 +105,6 @@
         "micromatch",
         "nanomatch"
       ]
-    },
-    "reflinks": [
-      "brace-expansion",
-      "to-regex-range",
-      "verb",
-      "verb-generate-readme"
-    ]
+    }
   }
 }
diff --git a/test/bash.expanded.js b/test/bash.expanded.js
index b58b81a..f2a4951 100644
--- a/test/bash.expanded.js
+++ b/test/bash.expanded.js
@@ -4,9 +4,9 @@ var extend = require('extend-shallow');
 var assert = require('assert');
 var braces = require('..');
 
-function match(pattern, expected, options) {
-  var actual = braces.expand(pattern, options).sort();
-  assert.deepEqual(actual, expected.sort(), pattern);
+function equal(pattern, expected, options) {
+  var actual = braces.expand(pattern, options);
+  assert.deepEqual(actual.sort(), expected.sort(), pattern);
 }
 
 /**
@@ -14,6 +14,12 @@ function match(pattern, expected, options) {
  */
 
 describe('bash.expanded', function() {
+  it('should throw an error when range exceeds rangeLimit', function() {
+    assert.throws(function() {
+      braces.expand('{214748364..2147483649}');
+    });
+  });
+
   var fixtures = [
     [ 'a{b,c{1..50}/{foo,bar,baz}/,g}h/i', {}, [ 'abh/i', 'ac1/bar/h/i', 'ac1/baz/h/i', 'ac1/foo/h/i', 'ac10/bar/h/i', 'ac10/baz/h/i', 'ac10/foo/h/i', 'ac11/bar/h/i', 'ac11/baz/h/i', 'ac11/foo/h/i', 'ac12/bar/h/i', 'ac12/baz/h/i', 'ac12/foo/h/i', 'ac13/bar/h/i', 'ac13/baz/h/i', 'ac13/foo/h/i', 'ac14/bar/h/i', 'ac14/baz/h/i', 'ac14/foo/h/i', 'ac15/bar/h/i', 'ac15/baz/h/i', 'ac15/foo/h/i', 'ac16/bar/h/i', 'ac16/baz/h/i', 'ac16/foo/h/i', 'ac17/bar/h/i', 'ac17/baz/h/i', 'ac17/foo/h/i', 'ac18 [...]
     [ '0{1..9} {10..20}', {}, [ '01 10', '01 11', '01 12', '01 13', '01 14', '01 15', '01 16', '01 17', '01 18', '01 19', '01 20', '02 10', '02 11', '02 12', '02 13', '02 14', '02 15', '02 16', '02 17', '02 18', '02 19', '02 20', '03 10', '03 11', '03 12', '03 13', '03 14', '03 15', '03 16', '03 17', '03 18', '03 19', '03 20', '04 10', '04 11', '04 12', '04 13', '04 14', '04 15', '04 16', '04 17', '04 18', '04 19', '04 20', '05 10', '05 11', '05 12', '05 13', '05 14', '05 15', '05 16', ' [...]
@@ -217,7 +223,7 @@ describe('bash.expanded', function() {
     [ 'a{ ,c{d, },h}x', {}, [ 'a x', 'ac x', 'acdx', 'ahx' ] ],
     [ 'a{ ,c{d, },h} ', {}, [ 'a  ', 'ac  ', 'acd ', 'ah ' ] ],
 
-    'see https://github.com/jonschlinkert/micromatch/issues/66',
+    'see https://github.com/jonschlinkert/microequal/issues/66',
 
     [ '/Users/tobiasreich/Sites/aaa/bbb/ccc 2016/src/**/[^_]*.{html,ejs}', {}, [ '/Users/tobiasreich/Sites/aaa/bbb/ccc 2016/src/**/[^_]*.ejs', '/Users/tobiasreich/Sites/aaa/bbb/ccc 2016/src/**/[^_]*.html' ] ],
 
@@ -248,6 +254,7 @@ describe('bash.expanded', function() {
 
     [ '{"klklkl"}{1,2,3}', {}, [ '{klklkl}1', '{klklkl}2', '{klklkl}3' ] ],
     [ '{"x,x"}', {}, [ '{x,x}' ] ],
+    [ '{\'x,x\'}', {}, [ '{x,x}' ] ],
 
     'should escaped outer braces in nested non-sets',
 
@@ -321,8 +328,7 @@ describe('bash.expanded', function() {
 
     'HEADS UP! If you\'re using the `--mm` flag minimatch freezes on these', 'should expand large numbers',
 
-    [ '{2147483645..2147483649}', { minimatch: false, optimize: true }, [ '2147483645', '2147483646', '2147483647', '2147483648', '2147483649' ] ],
-    [ '{214748364..2147483649}', { minimatch: false, optimize: true }, [ '(21474836[4-9]|2147483[7-9][0-9]|214748[4-9][0-9]{2}|214749[0-9]{3}|2147[5-9][0-9]{4}|214[8-9][0-9]{5}|21[5-9][0-9]{6}|2[2-9][0-9]{7}|[3-9][0-9]{8}|1[0-9]{9}|20[0-9]{8}|21[0-3][0-9]{7}|214[0-6][0-9]{6}|2147[0-3][0-9]{5}|21474[0-7][0-9]{4}|214748[0-2][0-9]{3}|2147483[0-5][0-9]{2}|21474836[0-4][0-9])' ] ],
+    [ '{2147483645..2147483649}', { minimatch: false }, [ '2147483645', '2147483646', '2147483647', '2147483648', '2147483649' ] ],
 
     'should expand ranges using steps',
 
@@ -405,7 +411,7 @@ describe('bash.expanded', function() {
     }
 
     it('should compile: ' + pattern, function() {
-      match(pattern, expected, options);
+      equal(pattern, expected, options);
     });
   });
 });
diff --git a/test/bash.optimized.js b/test/bash.optimized.js
index 848d7f2..f0ce837 100644
--- a/test/bash.optimized.js
+++ b/test/bash.optimized.js
@@ -4,7 +4,7 @@ var extend = require('extend-shallow');
 var assert = require('assert');
 var braces = require('..');
 
-function match(pattern, expected, options) {
+function equal(pattern, expected, options) {
   var actual = braces.optimize(pattern, options).sort();
   assert.deepEqual(actual, expected.sort(), pattern);
 }
@@ -13,7 +13,7 @@ function match(pattern, expected, options) {
  * Bash 4.3 unit tests with `braces.optimize()`
  */
 
-describe('bash.expanded', function() {
+describe('bash.optimized', function() {
   var fixtures = [
     ['a{b,c{1..100}/{foo,bar}/,h}x/z', {}, ['a(b|c([1-9]|[1-9][0-9]|100)/(foo|bar)/|h)x/z']],
     ['0{1..9} {10..20}', {}, ['0([1-9]) (1[0-9]|20)']],
@@ -78,7 +78,7 @@ describe('bash.expanded', function() {
     ['{1..ff}', {}, ['{1..ff}']],
     ['{1.20..2}', {}, ['{1.20..2}']],
     ['{10..1}', {}, ['([1-9]|10)']],
-    ['{214748364..2147483649}', {}, ['(21474836[4-9]|2147483[7-9][0-9]|214748[4-9][0-9]{2}|214749[0-9]{3}|2147[5-9][0-9]{4}|214[8-9][0-9]{5}|21[5-9][0-9]{6}|2[2-9][0-9]{7}|[3-9][0-9]{8}|1[0-9]{9}|20[0-9]{8}|21[0-3][0-9]{7}|214[0-6][0-9]{6}|2147[0-3][0-9]{5}|21474[0-7][0-9]{4}|214748[0-2][0-9]{3}|2147483[0-5][0-9]{2}|21474836[0-4][0-9])']],
+    ['{214748364..2147483649}', {}, ['(21474836[4-9]|2147483[7-9][0-9]|214748[4-9][0-9]{2}|214749[0-9]{3}|2147[5-9][0-9]{4}|214[89][0-9]{5}|21[5-9][0-9]{6}|2[2-9][0-9]{7}|[3-9][0-9]{8}|1[0-9]{9}|20[0-9]{8}|21[0-3][0-9]{7}|214[0-6][0-9]{6}|2147[0-3][0-9]{5}|21474[0-7][0-9]{4}|214748[0-2][0-9]{3}|2147483[0-5][0-9]{2}|21474836[0-4][0-9])']],
     ['{2147483645..2147483649}', {}, ['(214748364[5-9])']],
     ['{3..3}', {}, ['3']],
     ['{5..8}', {}, ['([5-8])']],
@@ -165,6 +165,7 @@ describe('bash.expanded', function() {
     // should not expand braces in sets with es6/bash-like variables
     ['abc/${ddd}/xyz', {}, ['abc/${ddd}/xyz']],
     ['a${b}c', {}, ['a${b}c']],
+    ['a${b{a,b}}c', {}, ['a${b{a,b}}c']],
     ['a/{${b},c}/d', {}, ['a/(${b}|c)/d']],
     ['a${b,d}/{foo,bar}c', {}, ['a${b,d}/(foo|bar)c']],
 
@@ -231,7 +232,7 @@ describe('bash.expanded', function() {
     ['a{ ,c{d, },h}x', {}, ['a( |c(d| )|h)x']],
     ['a{ ,c{d, },h} ', {}, ['a( |c(d| )|h) ']],
 
-    // see https://github.com/jonschlinkert/micromatch/issues/66
+    // see https://github.com/jonschlinkert/microequal/issues/66
     ['/Users/tobiasreich/Sites/aaa/bbb/ccc 2016/src/**/[^_]*.{html,ejs}', {}, ['/Users/tobiasreich/Sites/aaa/bbb/ccc 2016/src/**/[^_]*.(html|ejs)']],
 
     /**
@@ -331,7 +332,7 @@ describe('bash.expanded', function() {
     // HEADS UP! If you're using the `--mm` flag minimatch freezes on these
     // should expand large numbers
     ['{2147483645..2147483649}', {}, ['(214748364[5-9])']],
-    ['{214748364..2147483649}', {}, ['(21474836[4-9]|2147483[7-9][0-9]|214748[4-9][0-9]{2}|214749[0-9]{3}|2147[5-9][0-9]{4}|214[8-9][0-9]{5}|21[5-9][0-9]{6}|2[2-9][0-9]{7}|[3-9][0-9]{8}|1[0-9]{9}|20[0-9]{8}|21[0-3][0-9]{7}|214[0-6][0-9]{6}|2147[0-3][0-9]{5}|21474[0-7][0-9]{4}|214748[0-2][0-9]{3}|2147483[0-5][0-9]{2}|21474836[0-4][0-9])']],
+    ['{214748364..2147483649}', {}, ['(21474836[4-9]|2147483[7-9][0-9]|214748[4-9][0-9]{2}|214749[0-9]{3}|2147[5-9][0-9]{4}|214[89][0-9]{5}|21[5-9][0-9]{6}|2[2-9][0-9]{7}|[3-9][0-9]{8}|1[0-9]{9}|20[0-9]{8}|21[0-3][0-9]{7}|214[0-6][0-9]{6}|2147[0-3][0-9]{5}|21474[0-7][0-9]{4}|214748[0-2][0-9]{3}|2147483[0-5][0-9]{2}|21474836[0-4][0-9])']],
 
     // should expand ranges using steps
     ['{1..10..1}', {bash: false}, ['([1-9]|10)']],
@@ -403,7 +404,7 @@ describe('bash.expanded', function() {
     }
 
     it('should compile: ' + pattern, function() {
-      match(pattern, expected, options);
+      equal(pattern, expected, options);
     });
   });
 });
diff --git a/test/bash.spec.js b/test/bash.spec.js
index 862ade0..bf22ab4 100644
--- a/test/bash.spec.js
+++ b/test/bash.spec.js
@@ -4,7 +4,7 @@ var extend = require('extend-shallow');
 var assert = require('assert');
 var braces = require('..');
 
-function match(pattern, expected, options) {
+function equal(pattern, expected, options) {
   var actual = braces.expand(pattern, options).sort();
   assert.deepEqual(actual, expected.sort(), pattern);
 }
@@ -16,10 +16,12 @@ function match(pattern, expected, options) {
 describe('bash', function() {
   var fixtures = [
     [ '{1\\.2}', {}, [ '{1.2}' ] ],
+    [ '{1\\.2}', {unescape: false}, [ '{1\\.2}' ] ],
     [ '{"x,x"}', {}, [ '{x,x}' ] ],
     [ '{x","x}', {}, [ '{x,x}' ] ],
     [ '\'{x,x}\'', {}, [ '{x,x}' ] ],
-    [ '{x`,`x}', {}, [ 'x`', '`x' ] ],
+    [ '{x`,`x}', {}, [ '{x,x}' ] ],
+    [ '{x`,`x}', {unescape: false}, [ '{x`,`x}' ] ],
     [ '\'{a,b}{{a,b},a,b}\'', {}, [ '{a,b}{{a,b},a,b}' ] ],
     [ 'A{b,{d,e},{f,g}}Z', {}, [ 'AbZ', 'AdZ', 'AeZ', 'AfZ', 'AgZ' ] ],
     [ 'PRE-{a,b}{{a,b},a,b}-POST', {}, [ 'PRE-aa-POST', 'PRE-ab-POST', 'PRE-aa-POST', 'PRE-ab-POST', 'PRE-ba-POST', 'PRE-bb-POST', 'PRE-ba-POST', 'PRE-bb-POST' ] ],
@@ -176,13 +178,12 @@ describe('bash', function() {
     var options = extend({}, arr[1]);
     var pattern = arr[0];
     var expected = arr[2];
-
     if (options.skip === true) {
       return;
     }
 
     it('should compile: ' + pattern, function() {
-      match(pattern, expected, options);
+      equal(pattern, expected, options);
     });
   });
 });
diff --git a/test/brace-expansion.js b/test/brace-expansion.js
index a3d8f63..af729d8 100644
--- a/test/brace-expansion.js
+++ b/test/brace-expansion.js
@@ -3,9 +3,9 @@
 var assert = require('assert');
 var braces = require('..');
 
-function match(pattern, expected, options) {
+function equal(pattern, expected, options) {
   var actual = braces.expand(pattern, options).sort();
-  assert.deepEqual(actual, expected.sort(), pattern);
+  assert.deepEqual(actual.filter(Boolean), expected.sort(), pattern);
 }
 
 /**
@@ -16,75 +16,75 @@ function match(pattern, expected, options) {
 describe('unit tests from brace-expand', function() {
   describe('sequences', function() {
     it('numeric sequences', function() {
-      match('a{1..2}b{2..3}c', ['a1b2c', 'a1b3c', 'a2b2c', 'a2b3c']);
-      match('{1..2}{2..3}', ['12', '13', '22', '23']);
+      equal('a{1..2}b{2..3}c', ['a1b2c', 'a1b3c', 'a2b2c', 'a2b3c']);
+      equal('{1..2}{2..3}', ['12', '13', '22', '23']);
     });
 
     it('numeric sequences with step count', function() {
-      match('{0..8..2}', ['0', '2', '4', '6', '8']);
-      match('{1..8..2}', ['1', '3', '5', '7']);
+      equal('{0..8..2}', ['0', '2', '4', '6', '8']);
+      equal('{1..8..2}', ['1', '3', '5', '7']);
     });
 
     it('numeric sequence with negative x / y', function() {
-      match('{3..-2}', ['3', '2', '1', '0', '-1', '-2']);
+      equal('{3..-2}', ['3', '2', '1', '0', '-1', '-2']);
     });
 
     it('alphabetic sequences', function() {
-      match('1{a..b}2{b..c}3', ['1a2b3', '1a2c3', '1b2b3', '1b2c3']);
-      match('{a..b}{b..c}', ['ab', 'ac', 'bb', 'bc']);
+      equal('1{a..b}2{b..c}3', ['1a2b3', '1a2c3', '1b2b3', '1b2c3']);
+      equal('{a..b}{b..c}', ['ab', 'ac', 'bb', 'bc']);
     });
 
     it('alphabetic sequences with step count', function() {
-      match('{a..k..2}', ['a', 'c', 'e', 'g', 'i', 'k']);
-      match('{b..k..2}', ['b', 'd', 'f', 'h', 'j']);
+      equal('{a..k..2}', ['a', 'c', 'e', 'g', 'i', 'k']);
+      equal('{b..k..2}', ['b', 'd', 'f', 'h', 'j']);
     });
   });
 
   describe('dollar', function() {
     it('ignores ${', function() {
-      match('${1..3}', ['${1..3}']);
-      match('${a,b}${c,d}', ['${a,b}${c,d}']);
-      match('x${a,b}x${c,d}x', ['x${a,b}x${c,d}x']);
+      equal('${1..3}', ['${1..3}']);
+      equal('${a,b}${c,d}', ['${a,b}${c,d}']);
+      equal('x${a,b}x${c,d}x', ['x${a,b}x${c,d}x']);
     });
   });
 
   describe('empty option', function() {
     it('should support empty sets', function() {
-      match('-v{,,,,}', ['-v', '-v', '-v', '-v', '-v']);
+      equal('-v{,,,,}', ['-v', '-v', '-v', '-v', '-v']);
     });
   });
 
   describe('negative increments', function() {
     it('should support negative steps', function() {
-      match('{3..1}', ['3', '2', '1']);
-      match('{10..8}', ['10', '9', '8']);
-      match('{10..08}', ['10', '09', '08']);
-      match('{c..a}', ['c', 'b', 'a']);
-
-      match('{4..0..2}', ['4', '2', '0']);
-      match('{4..0..-2}', ['4', '2', '0']);
-      match('{e..a..2}', ['e', 'c', 'a']);
+      equal('{3..1}', ['3', '2', '1']);
+      equal('{10..8}', ['10', '9', '8']);
+      equal('{10..08}', ['10', '09', '08']);
+      equal('{c..a}', ['c', 'b', 'a']);
+
+      equal('{4..0..2}', ['4', '2', '0']);
+      equal('{4..0..-2}', ['4', '2', '0']);
+      equal('{e..a..2}', ['e', 'c', 'a']);
     });
   });
 
   describe('nested', function() {
     it('should support nested sets', function() {
-      match('{a,b{1..3},c}', ['a', 'b1', 'b2', 'b3', 'c']);
-      match('{{A..Z},{a..z}}', [ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' ].sort());
-      match('ppp{,config,oe{,conf}}', ['ppp', 'pppconfig', 'pppoe', 'pppoeconf']);
+      equal('{a,b{1..3},c}', ['a', 'b1', 'b2', 'b3', 'c']);
+      equal('{{A..Z},{a..z}}', ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'].sort());
+      equal('ppp{,config,oe{,conf}}', ['ppp', 'pppconfig', 'pppoe', 'pppoeconf']);
     });
   });
 
   describe('order', function() {
     it('should expand in given order', function() {
-      match('a{d,c,b}e', ['ade', 'ace', 'abe']);
+      equal('a{d,c,b}e', ['ade', 'ace', 'abe']);
     });
   });
 
   describe('pad', function() {
     it('should support padding', function() {
-      match('{9..11}', ['9', '10', '11']);
-      match('{09..11}', ['09', '10', '11']);
+      equal('{9..11}', ['9', '10', '11']);
+      equal('{09..11}', ['09', '10', '11']);
     });
   });
 });
diff --git a/test/braces.create.js b/test/braces.create.js
new file mode 100644
index 0000000..ee733e0
--- /dev/null
+++ b/test/braces.create.js
@@ -0,0 +1,21 @@
+'use strict';
+
+require('mocha');
+var assert = require('assert');
+var braces = require('..');
+
+describe('.makeRe', function() {
+  it('should throw an error when invalid args are passed', function() {
+    assert.throws(function() {
+      braces.makeRe();
+    });
+  });
+
+  it('should throw an error when string exceeds max safe length', function() {
+    var MAX_LENGTH = 1024 * 64;
+
+    assert.throws(function() {
+      braces.makeRe(Array(MAX_LENGTH + 1).join('.'));
+    });
+  });
+});
diff --git a/test/braces.js b/test/braces.js
index 9d4a1c2..19d9868 100644
--- a/test/braces.js
+++ b/test/braces.js
@@ -4,25 +4,30 @@ require('mocha');
 var assert = require('assert');
 var braces = require('..');
 
-function match(pattern, expected, options) {
+function equal(pattern, expected, options) {
   assert.deepEqual(braces(pattern, options), expected);
 }
 
+equal.expand = function(pattern, expected, options) {
+  var actual = braces.expand(pattern, options).sort();
+  assert.deepEqual(actual.filter(Boolean), expected.sort(), pattern);
+};
+
 describe('braces', function() {
   it('should return an array', function() {
     assert(Array.isArray(braces('{a,b}')));
   });
 
   it('should return an optimized string by default', function() {
-    match('a/{b,c}/d', ['a/(b|c)/d']);
+    equal('a/{b,c}/d', ['a/(b|c)/d']);
   });
 
   it('should return an expanded array if defined on options', function() {
-    match('a/{b,c}/d', ['a/b/d', 'a/c/d'], {expand: true});
+    equal('a/{b,c}/d', ['a/b/d', 'a/c/d'], {expand: true});
   });
 
   it('should optimize an array of patterns', function() {
-    match(['a/{b,c}/d', 'x/{foo,bar}/z'], ['a/(b|c)/d', 'x/(foo|bar)/z']);
+    equal(['a/{b,c}/d', 'x/{foo,bar}/z'], ['a/(b|c)/d', 'x/(foo|bar)/z']);
   });
 
   it('should expand an array of patterns', function() {
@@ -41,18 +46,101 @@ describe('braces', function() {
   });
 
   it('should expand ranges', function() {
-    match('a{1..5}b', ['a1b', 'a2b', 'a3b', 'a4b', 'a5b'], {expand: true});
+    equal('a{1..5}b', ['a1b', 'a2b', 'a3b', 'a4b', 'a5b'], {expand: true});
   });
 
   it('should expand ranges that are nested in a set', function() {
-    match('a{b,c,{1..5}}e', ['abe', 'ace', 'a1e', 'a2e', 'a3e', 'a4e', 'a5e'], {expand: true});
+    equal('a{b,c,{1..5}}e', ['abe', 'ace', 'a1e', 'a2e', 'a3e', 'a4e', 'a5e'], {expand: true});
   });
 
   it('should not expand ranges when they are just characters in a set', function() {
-    match('a{b,c,1..5}e', ['abe', 'ace', 'a1..5e'], {expand: true});
-    match('a{/../}e', ['a/e'], {expand: true});
-    match('a{/../,z}e', ['a/../e', 'aze'], {expand: true});
-    match('a{b,c/*/../d}e', ['abe', 'ac/*/../de'], {expand: true});
-    match('a{b,b,c/../b}d', ['abd', 'abd', 'ac/../bd'], {expand: true});
+    equal('a{b,c,1..5}e', ['abe', 'ace', 'a1..5e'], {expand: true});
+    equal('a{/../}e', ['a/e'], {expand: true});
+    equal('a{/../,z}e', ['a/../e', 'aze'], {expand: true});
+    equal('a{b,c/*/../d}e', ['abe', 'ac/*/../de'], {expand: true});
+    equal('a{b,b,c/../b}d', ['abd', 'abd', 'ac/../bd'], {expand: true});
+  });
+
+  it('should support expanded nested empty sets', function() {
+    equal.expand('{\`foo,bar\`}', ['{foo,bar}']);
+    equal.expand('{\\`foo,bar\\`}', ['`foo', 'bar`']);
+    equal.expand('{`foo\,bar`}', ['{foo,bar}']);
+    equal.expand('{`foo\\,bar`}', ['{`foo\\,bar`}']);
+    equal.expand('{a,\\\\{a,b}c}', ['a', '\\\\ac', '\\\\bc']);
+    equal.expand('{a,\\{a,b}c}', ['ac}', '{ac}', 'bc}']);
+    equal.expand('{,eno,thro,ro}ugh', ['ugh', 'enough', 'through', 'rough']);
+    equal.expand('{,{,eno,thro,ro}ugh}{,out}', ['out', 'ugh', 'ughout', 'enough', 'enoughout', 'through', 'throughout', 'rough', 'roughout']);
+    equal.expand('{{,eno,thro,ro}ugh,}{,out}', ['ugh', 'ughout', 'enough', 'enoughout', 'through', 'throughout', 'rough', 'roughout', 'out']);
+    equal.expand('{,{,a,b}z}{,c}', ['c', 'z', 'zc', 'az', 'azc', 'bz', 'bzc']);
+    equal.expand('{,{,a,b}z}{c,}', ['c', 'zc', 'z', 'azc', 'az', 'bzc', 'bz']);
+    equal.expand('{,{,a,b}z}{,c,}', ['c', 'z', 'zc', 'z', 'az', 'azc', 'az', 'bz', 'bzc', 'bz']);
+    equal.expand('{,{,a,b}z}{c,d}', ['c', 'd', 'zc', 'zd', 'azc', 'azd', 'bzc', 'bzd']);
+    equal.expand('{{,a,b}z,}{,c}', ['z', 'zc', 'az', 'azc', 'bz', 'bzc', 'c']);
+    equal.expand('{,a{,b}z,}{,c}', ['c', 'az', 'azc', 'abz', 'abzc', 'c']);
+    equal.expand('{,a{,b},}{,c}', ['c', 'a', 'ac', 'ab', 'abc', 'c']);
+    equal.expand('{,a{,b}}{,c}', ['c', 'a', 'ac', 'ab', 'abc']);
+    equal.expand('{,b}{,d}', ['d', 'b', 'bd']);
+    equal.expand('{a,b}{,d}', ['a', 'ad', 'b', 'bd']);
+    equal.expand('{,a}{z,c}', ['z', 'c', 'az', 'ac']);
+    equal.expand('{,{a,}}{z,c}', ['z', 'c', 'z', 'c', 'az', 'ac']);
+    equal.expand('{,{,a}}{z,c}', ['z', 'c', 'z', 'c', 'az', 'ac']);
+    equal.expand('{,{,a},}{z,c}', ['z', 'c', 'z', 'c', 'az', 'ac', 'z', 'c']);
+    equal.expand('{{,,a}}{z,c}', [ '{}z', '{}c', '{}z', '{}c', '{a}z', '{a}c' ]);
+    equal.expand('{{,a},}{z,c}', ['z', 'c', 'az', 'ac', 'z', 'c']);
+    equal.expand('{,,a}{z,c}', ['z', 'c', 'z', 'c', 'az', 'ac']);
+    equal.expand('{,{,}}{z,c}', ['z', 'c', 'z', 'c', 'z', 'c']);
+    equal.expand('{,{a,b}}{,c}', ['c', 'a', 'ac', 'b', 'bc']);
+    equal.expand('{,{a,}}{,c}', ['c', 'a', 'ac', 'c']);
+    equal.expand('{,{,b}}{,c}', ['c', 'c', 'b', 'bc']);
+    equal.expand('{,{,}}{,c}', ['c', 'c', 'c']);
+    equal.expand('{,a}{,c}', ['c', 'a', 'ac']);
+    equal.expand('{,{,a}b}', ['b', 'ab']);
+    equal.expand('{,b}', ['b']);
+    equal.expand('{,b{,a}}', ['b', 'ba']);
+    equal.expand('{b,{,a}}', ['b', 'a']);
+    equal.expand('{,b}{,d}', ['d', 'b', 'bd']);
+    equal.expand('{a,b}{,d}', ['a', 'ad', 'b', 'bd']);
+  });
+
+  it('should support optimized nested empty sets', function() {
+    equal('{\`foo,bar\`}', [ '{foo,bar}' ]);
+    equal('{\\`foo,bar\\`}', [ '(`foo|bar`)' ]);
+    equal('{`foo\,bar`}', [ '{foo,bar}' ]);
+    equal('{`foo\\,bar`}', [ '(`foo\\,bar`)' ]);
+    equal('{a,\\\\{a,b}c}', [ '(a|\\\\(a|b)c)' ]);
+    equal('{a,\\{a,b}c}', [ '(a|{a|b)c}' ]);
+    equal('a/{\\{b,c,d},z}/e', [ 'a/({b|c|d),z}/e' ]);
+    equal('{,eno,thro,ro}ugh', [ '(|eno|thro|ro)ugh' ]);
+    equal('{,{,eno,thro,ro}ugh}{,out}', [ '((|eno|thro|ro)ugh|)(|out)' ]);
+    equal('{{,eno,thro,ro}ugh,}{,out}', [ '((|eno|thro|ro)ugh|)(|out)' ]);
+    equal('{,{,a,b}z}{,c}', [ '((|a|b)z|)(|c)' ]);
+    equal('{,{,a,b}z}{c,}', [ '((|a|b)z|)(c|)' ]);
+    equal('{,{,a,b}z}{,c,}', [ '((|a|b)z|)(|c|)' ]);
+    equal('{,{,a,b}z}{c,d}', [ '((|a|b)z|)(c|d)' ]);
+    equal('{{,a,b}z,}{,c}', [ '((|a|b)z|)(|c)' ]);
+    equal('{,a{,b}z,}{,c}', [ '(|a(|b)z|)(|c)' ]);
+    equal('{,a{,b},}{,c}', [ '(|a(|b)|)(|c)' ]);
+    equal('{,a{,b}}{,c}', [ '(|a(|b))(|c)' ]);
+    equal('{,b}{,d}', [ '(|b)(|d)' ]);
+    equal('{a,b}{,d}', [ '(a|b)(|d)' ]);
+    equal('{,a}{z,c}', [ '(|a)(z|c)' ]);
+    equal('{,{a,}}{z,c}', [ '((a|)|)(z|c)' ]);
+    equal('{,{,a}}{z,c}', [ '((|a)|)(z|c)' ]);
+    equal('{,{,a},}{z,c}', [ '((|a)||)(z|c)' ]);
+    equal('{{,,a}}{z,c}', [ '((||a))(z|c)' ]);
+    equal('{{,a},}{z,c}', [ '((|a)|)(z|c)' ]);
+    equal('{,,a}{z,c}', [ '(||a)(z|c)' ]);
+    equal('{,{,}}{z,c}', [ '(z|c)' ]);
+    equal('{,{a,b}}{,c}', [ '((a|b)|)(|c)' ]);
+    equal('{,{a,}}{,c}', [ '((a|)|)(|c)' ]);
+    equal('{,{,b}}{,c}', [ '((|b)|)(|c)' ]);
+    equal('{,{,}}{,c}', [ '(|c)' ]);
+    equal('{,a}{,c}', [ '(|a)(|c)' ]);
+    equal('{,{,a}b}', [ '((|a)b|)' ]);
+    equal('{,b}', [ '(|b)' ]);
+    equal('{,b{,a}}', [ '(|b(|a))' ]);
+    equal('{b,{,a}}', [ '(b|(|a))' ]);
+    equal('{,b}{,d}', [ '(|b)(|d)' ]);
+    equal('{a,b}{,d}', [ '(a|b)(|d)' ]);
   });
 });
diff --git a/test/braces.makeRe.js b/test/braces.makeRe.js
new file mode 100644
index 0000000..ae02896
--- /dev/null
+++ b/test/braces.makeRe.js
@@ -0,0 +1,21 @@
+'use strict';
+
+require('mocha');
+var assert = require('assert');
+var braces = require('..');
+
+describe('.create', function() {
+  it('should throw an error when invalid args are passed', function() {
+    assert.throws(function() {
+      braces.create();
+    });
+  });
+
+  it('should throw an error when string exceeds max safe length', function() {
+    var MAX_LENGTH = 1024 * 64;
+
+    assert.throws(function() {
+      braces.create(Array(MAX_LENGTH + 1).join('.'));
+    });
+  });
+});
diff --git a/test/expanded.integration.js b/test/expanded.integration.js
index b855ffe..dc664ae 100644
--- a/test/expanded.integration.js
+++ b/test/expanded.integration.js
@@ -3,25 +3,25 @@
 var assert = require('assert');
 var braces = require('..');
 
-function match(pattern, expected, options) {
+function equal(pattern, expected, options) {
   var actual = braces.expand(pattern, options).sort();
   assert.deepEqual(actual, expected.sort(), pattern);
 }
 
 describe('integration', function() {
   it('should work with dots in file paths', function() {
-    match('../{1..3}/../foo', ['../1/../foo', '../2/../foo', '../3/../foo']);
-    match('../{2..10..2}/../foo', ['../2/../foo', '../4/../foo', '../6/../foo', '../8/../foo', '../10/../foo']);
-    match('../{1..3}/../{a,b,c}/foo', ['../1/../a/foo', '../2/../a/foo', '../3/../a/foo', '../1/../b/foo', '../2/../b/foo', '../3/../b/foo', '../1/../c/foo', '../2/../c/foo', '../3/../c/foo']);
-    match('./{a..z..3}/', ['./a/', './d/', './g/', './j/', './m/', './p/', './s/', './v/', './y/']);
-    match('./{"x,y"}/{a..z..3}/', ['./{x,y}/a/', './{x,y}/d/', './{x,y}/g/', './{x,y}/j/', './{x,y}/m/', './{x,y}/p/', './{x,y}/s/', './{x,y}/v/', './{x,y}/y/']);
+    equal('../{1..3}/../foo', ['../1/../foo', '../2/../foo', '../3/../foo']);
+    equal('../{2..10..2}/../foo', ['../2/../foo', '../4/../foo', '../6/../foo', '../8/../foo', '../10/../foo']);
+    equal('../{1..3}/../{a,b,c}/foo', ['../1/../a/foo', '../2/../a/foo', '../3/../a/foo', '../1/../b/foo', '../2/../b/foo', '../3/../b/foo', '../1/../c/foo', '../2/../c/foo', '../3/../c/foo']);
+    equal('./{a..z..3}/', ['./a/', './d/', './g/', './j/', './m/', './p/', './s/', './v/', './y/']);
+    equal('./{"x,y"}/{a..z..3}/', ['./{x,y}/a/', './{x,y}/d/', './{x,y}/g/', './{x,y}/j/', './{x,y}/m/', './{x,y}/p/', './{x,y}/s/', './{x,y}/v/', './{x,y}/y/']);
   });
 
   it('should expand a complex combination of ranges and sets:', function() {
-    match('a/{x,y}/{1..5}c{d,e}f.{md,txt}', ['a/x/1cdf.md', 'a/y/1cdf.md', 'a/x/2cdf.md', 'a/y/2cdf.md', 'a/x/3cdf.md', 'a/y/3cdf.md', 'a/x/4cdf.md', 'a/y/4cdf.md', 'a/x/5cdf.md', 'a/y/5cdf.md', 'a/x/1cef.md', 'a/y/1cef.md', 'a/x/2cef.md', 'a/y/2cef.md', 'a/x/3cef.md', 'a/y/3cef.md', 'a/x/4cef.md', 'a/y/4cef.md', 'a/x/5cef.md', 'a/y/5cef.md', 'a/x/1cdf.txt', 'a/y/1cdf.txt', 'a/x/2cdf.txt', 'a/y/2cdf.txt', 'a/x/3cdf.txt', 'a/y/3cdf.txt', 'a/x/4cdf.txt', 'a/y/4cdf.txt', 'a/x/5cdf.txt', 'a/ [...]
+    equal('a/{x,y}/{1..5}c{d,e}f.{md,txt}', ['a/x/1cdf.md', 'a/y/1cdf.md', 'a/x/2cdf.md', 'a/y/2cdf.md', 'a/x/3cdf.md', 'a/y/3cdf.md', 'a/x/4cdf.md', 'a/y/4cdf.md', 'a/x/5cdf.md', 'a/y/5cdf.md', 'a/x/1cef.md', 'a/y/1cef.md', 'a/x/2cef.md', 'a/y/2cef.md', 'a/x/3cef.md', 'a/y/3cef.md', 'a/x/4cef.md', 'a/y/4cef.md', 'a/x/5cef.md', 'a/y/5cef.md', 'a/x/1cdf.txt', 'a/y/1cdf.txt', 'a/x/2cdf.txt', 'a/y/2cdf.txt', 'a/x/3cdf.txt', 'a/y/3cdf.txt', 'a/x/4cdf.txt', 'a/y/4cdf.txt', 'a/x/5cdf.txt', 'a/ [...]
   });
 
   it('should expand complex sets and ranges in `bash` mode:', function() {
-    match('a/{x,{1..5},y}/c{d}e', ['a/x/c{d}e', 'a/1/c{d}e', 'a/2/c{d}e', 'a/3/c{d}e', 'a/4/c{d}e', 'a/5/c{d}e', 'a/y/c{d}e']);
+    equal('a/{x,{1..5},y}/c{d}e', ['a/x/c{d}e', 'a/1/c{d}e', 'a/2/c{d}e', 'a/3/c{d}e', 'a/4/c{d}e', 'a/5/c{d}e', 'a/y/c{d}e']);
   });
 });
diff --git a/test/expanded.ranges.js b/test/expanded.ranges.js
index cd32ea0..d118fa8 100644
--- a/test/expanded.ranges.js
+++ b/test/expanded.ranges.js
@@ -3,185 +3,191 @@
 var assert = require('assert');
 var braces = require('..');
 
-function match(pattern, expected) {
+function equal(pattern, expected) {
   var actual = braces.expand(pattern).sort();
   assert.deepEqual(actual, expected.sort());
 }
 
 describe('expanded ranges', function() {
+
+  // HEADS UP! If you're using the `--mm` flag minimatch freezes on these
+  describe('large numbers', function() {
+    it('should expand large numbers', function() {
+      equal('{2147483645..2147483649}', ['2147483645', '2147483646', '2147483647', '2147483648', '2147483649']);
+    });
+
+    it('should throw an error when range exceeds rangeLimit', function() {
+      assert.throws(function() {
+        braces.expand('{214748364..2147483649}');
+      });
+    });
+  });
+
   describe('escaping / invalid ranges', function() {
     it('should not try to expand ranges with decimals', function() {
-      match('{1.1..2.1}', ['{1.1..2.1}']);
-      match('{1.1..~2.1}', ['{1.1..~2.1}']);
+      equal('{1.1..2.1}', ['{1.1..2.1}']);
+      equal('{1.1..~2.1}', ['{1.1..~2.1}']);
     });
 
     it('should escape invalid ranges:', function() {
-      match('{1..0f}', ['{1..0f}']);
-      match('{1..10..ff}', ['{1..10..ff}']);
-      match('{1..10.f}', ['{1..10.f}']);
-      match('{1..10f}', ['{1..10f}']);
-      match('{1..20..2f}', ['{1..20..2f}']);
-      match('{1..20..f2}', ['{1..20..f2}']);
-      match('{1..2f..2}', ['{1..2f..2}']);
-      match('{1..ff..2}', ['{1..ff..2}']);
-      match('{1..ff}', ['{1..ff}']);
-      match('{1.20..2}', ['{1.20..2}']);
+      equal('{1..0f}', ['{1..0f}']);
+      equal('{1..10..ff}', ['{1..10..ff}']);
+      equal('{1..10.f}', ['{1..10.f}']);
+      equal('{1..10f}', ['{1..10f}']);
+      equal('{1..20..2f}', ['{1..20..2f}']);
+      equal('{1..20..f2}', ['{1..20..f2}']);
+      equal('{1..2f..2}', ['{1..2f..2}']);
+      equal('{1..ff..2}', ['{1..ff..2}']);
+      equal('{1..ff}', ['{1..ff}']);
+      equal('{1.20..2}', ['{1.20..2}']);
     });
 
     it('weirdly-formed brace expansions -- fixed in post-bash-3.1', function() {
-      match('a-{b{d,e}}-c', ['a-{bd}-c', 'a-{be}-c']);
-      match('a-{bdef-{g,i}-c', ['a-{bdef-g-c', 'a-{bdef-i-c']);
+      equal('a-{b{d,e}}-c', ['a-{bd}-c', 'a-{be}-c']);
+      equal('a-{bdef-{g,i}-c', ['a-{bdef-g-c', 'a-{bdef-i-c']);
     });
 
     it('should not expand quoted strings.', function() {
-      match('{"klklkl"}{1,2,3}', ['{klklkl}1', '{klklkl}2', '{klklkl}3']);
-      match('{"x,x"}', ['{x,x}']);
+      equal('{"klklkl"}{1,2,3}', ['{klklkl}1', '{klklkl}2', '{klklkl}3']);
+      equal('{"x,x"}', ['{x,x}']);
     });
 
     it('should escaped outer braces in nested non-sets', function() {
-      match('{a-{b,c,d}}', ['{a-b}', '{a-c}', '{a-d}']);
-      match('{a,{a-{b,c,d}}}', ['a', '{a-b}', '{a-c}', '{a-d}']);
+      equal('{a-{b,c,d}}', ['{a-b}', '{a-c}', '{a-d}']);
+      equal('{a,{a-{b,c,d}}}', ['a', '{a-b}', '{a-c}', '{a-d}']);
     });
 
     it('should escape imbalanced braces', function() {
-      match('a-{bdef-{g,i}-c', ['a-{bdef-g-c', 'a-{bdef-i-c']);
-      match('abc{', ['abc{']);
-      match('{abc{', ['{abc{']);
-      match('{abc', ['{abc']);
-      match('}abc', ['}abc']);
-      match('ab{c', ['ab{c']);
-      match('ab{c', ['ab{c']);
-      match('{{a,b}', ['{a', '{b']);
-      match('{a,b}}', ['a}', 'b}']);
-      match('abcd{efgh', ['abcd{efgh']);
-      match('a{b{c{d,e}f}g}h', ['a{b{cdf}g}h', 'a{b{cef}g}h']);
-      match('f{x,y{{g,z}}h}', ['fx', 'fy{g}h', 'fy{z}h']);
-      match('z{a,b},c}d', ['za,c}d', 'zb,c}d']);
-      match('a{b{c{d,e}f{x,y{{g}h', ['a{b{cdf{x,y{{g}h', 'a{b{cef{x,y{{g}h']);
-      match('f{x,y{{g}h', ['f{x,y{{g}h']);
-      match('f{x,y{{g}}h', ['f{x,y{{g}}h']);
-      match('a{b{c{d,e}f{x,y{}g}h', ['a{b{cdfxh', 'a{b{cdfy{}gh', 'a{b{cefxh', 'a{b{cefy{}gh']);
-      match('f{x,y{}g}h', ['fxh', 'fy{}gh']);
-      match('z{a,b{,c}d', ['z{a,bd', 'z{a,bcd']);
+      equal('a-{bdef-{g,i}-c', ['a-{bdef-g-c', 'a-{bdef-i-c']);
+      equal('abc{', ['abc{']);
+      equal('{abc{', ['{abc{']);
+      equal('{abc', ['{abc']);
+      equal('}abc', ['}abc']);
+      equal('ab{c', ['ab{c']);
+      equal('ab{c', ['ab{c']);
+      equal('{{a,b}', ['{a', '{b']);
+      equal('{a,b}}', ['a}', 'b}']);
+      equal('abcd{efgh', ['abcd{efgh']);
+      equal('a{b{c{d,e}f}g}h', ['a{b{cdf}g}h', 'a{b{cef}g}h']);
+      equal('f{x,y{{g,z}}h}', ['fx', 'fy{g}h', 'fy{z}h']);
+      equal('z{a,b},c}d', ['za,c}d', 'zb,c}d']);
+      equal('a{b{c{d,e}f{x,y{{g}h', ['a{b{cdf{x,y{{g}h', 'a{b{cef{x,y{{g}h']);
+      equal('f{x,y{{g}h', ['f{x,y{{g}h']);
+      equal('f{x,y{{g}}h', ['f{x,y{{g}}h']);
+      equal('a{b{c{d,e}f{x,y{}g}h', ['a{b{cdfxh', 'a{b{cdfy{}gh', 'a{b{cefxh', 'a{b{cefy{}gh']);
+      equal('f{x,y{}g}h', ['fxh', 'fy{}gh']);
+      equal('z{a,b{,c}d', ['z{a,bd', 'z{a,bcd']);
     });
   });
 
   describe('positive numeric ranges', function() {
     it('should expand numeric ranges', function() {
-      match('a{0..3}d', ['a0d', 'a1d', 'a2d', 'a3d']);
-      match('x{10..1}y', ['x10y', 'x9y', 'x8y', 'x7y', 'x6y', 'x5y', 'x4y', 'x3y', 'x2y', 'x1y']);
-      match('x{3..3}y', ['x3y']);
-      match('{1..10}', ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']);
-      match('{1..3}', ['1', '2', '3']);
-      match('{1..9}', ['1', '2', '3', '4', '5', '6', '7', '8', '9']);
-      match('{10..1}', ['10', '9', '8', '7', '6', '5', '4', '3', '2', '1']);
-      match('{10..1}y', ['10y', '9y', '8y', '7y', '6y', '5y', '4y', '3y', '2y', '1y']);
-      match('{3..3}', ['3']);
-      match('{5..8}', ['5', '6', '7', '8']);
+      equal('a{0..3}d', ['a0d', 'a1d', 'a2d', 'a3d']);
+      equal('x{10..1}y', ['x10y', 'x9y', 'x8y', 'x7y', 'x6y', 'x5y', 'x4y', 'x3y', 'x2y', 'x1y']);
+      equal('x{3..3}y', ['x3y']);
+      equal('{1..10}', ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']);
+      equal('{1..3}', ['1', '2', '3']);
+      equal('{1..9}', ['1', '2', '3', '4', '5', '6', '7', '8', '9']);
+      equal('{10..1}', ['10', '9', '8', '7', '6', '5', '4', '3', '2', '1']);
+      equal('{10..1}y', ['10y', '9y', '8y', '7y', '6y', '5y', '4y', '3y', '2y', '1y']);
+      equal('{3..3}', ['3']);
+      equal('{5..8}', ['5', '6', '7', '8']);
     });
   });
 
   describe('negative ranges', function() {
     it('should expand ranges with negative numbers', function() {
-      match('{-10..-1}', ['-10', '-9', '-8', '-7', '-6', '-5', '-4', '-3', '-2', '-1']);
-      match('{-20..0}', ['-20', '-19', '-18', '-17', '-16', '-15', '-14', '-13', '-12', '-11', '-10', '-9', '-8', '-7', '-6', '-5', '-4', '-3', '-2', '-1', '0']);
-      match('{0..-5}', ['0', '-1', '-2', '-3', '-4', '-5']);
-      match('{9..-4}', ['9', '8', '7', '6', '5', '4', '3', '2', '1', '0', '-1', '-2', '-3', '-4']);
+      equal('{-10..-1}', ['-10', '-9', '-8', '-7', '-6', '-5', '-4', '-3', '-2', '-1']);
+      equal('{-20..0}', ['-20', '-19', '-18', '-17', '-16', '-15', '-14', '-13', '-12', '-11', '-10', '-9', '-8', '-7', '-6', '-5', '-4', '-3', '-2', '-1', '0']);
+      equal('{0..-5}', ['0', '-1', '-2', '-3', '-4', '-5']);
+      equal('{9..-4}', ['9', '8', '7', '6', '5', '4', '3', '2', '1', '0', '-1', '-2', '-3', '-4']);
     });
   });
 
   describe('alphabetical ranges', function() {
     it('should expand alphabetical ranges', function() {
-      match('{a..F}', ['F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a']);
-      match('0{a..d}0', ['0a0', '0b0', '0c0', '0d0']);
-      match('a/{b..d}/e', ['a/b/e', 'a/c/e', 'a/d/e']);
-      match('{1..f}', ['1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f']);
-      match('{a..A}', ['a', '`', '_', '^', ']', '\\', '[', 'Z', 'Y', 'X', 'W', 'V', 'U', 'T', 'S', 'R', 'Q', 'P', 'O', 'N', 'M', 'L', 'K', 'J', 'I', 'H', 'G', 'F', 'E', 'D', 'C', 'B', 'A']);
-      match('{A..a}', ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a']);
-      match('{a..e}', ['a', 'b', 'c', 'd', 'e']);
-      match('{A..E}', ['A', 'B', 'C', 'D', 'E']);
-      match('{a..f}', ['a', 'b', 'c', 'd', 'e', 'f']);
-      match('{a..z}', ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']);
-      match('{E..A}', ['E', 'D', 'C', 'B', 'A']);
-      match('{f..1}', ['f', 'e', 'd', 'c', 'b', 'a', '`', '_', '^', ']', '\\', '[', 'Z', 'Y', 'X', 'W', 'V', 'U', 'T', 'S', 'R', 'Q', 'P', 'O', 'N', 'M', 'L', 'K', 'J', 'I', 'H', 'G', 'F', 'E', 'D', 'C', 'B', 'A', '@', '?', '>', '=', '<', ';', ':', '9', '8', '7', '6', '5', '4', '3', '2', '1']);
-      match('{f..a}', ['f', 'e', 'd', 'c', 'b', 'a']);
-      match('{f..f}', ['f']);
+      equal('{a..F}', ['F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a']);
+      equal('0{a..d}0', ['0a0', '0b0', '0c0', '0d0']);
+      equal('a/{b..d}/e', ['a/b/e', 'a/c/e', 'a/d/e']);
+      equal('{1..f}', ['1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f']);
+      equal('{a..A}', ['a', '`', '_', '^', ']', '\\', '[', 'Z', 'Y', 'X', 'W', 'V', 'U', 'T', 'S', 'R', 'Q', 'P', 'O', 'N', 'M', 'L', 'K', 'J', 'I', 'H', 'G', 'F', 'E', 'D', 'C', 'B', 'A']);
+      equal('{A..a}', ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a']);
+      equal('{a..e}', ['a', 'b', 'c', 'd', 'e']);
+      equal('{A..E}', ['A', 'B', 'C', 'D', 'E']);
+      equal('{a..f}', ['a', 'b', 'c', 'd', 'e', 'f']);
+      equal('{a..z}', ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']);
+      equal('{E..A}', ['E', 'D', 'C', 'B', 'A']);
+      equal('{f..1}', ['f', 'e', 'd', 'c', 'b', 'a', '`', '_', '^', ']', '\\', '[', 'Z', 'Y', 'X', 'W', 'V', 'U', 'T', 'S', 'R', 'Q', 'P', 'O', 'N', 'M', 'L', 'K', 'J', 'I', 'H', 'G', 'F', 'E', 'D', 'C', 'B', 'A', '@', '?', '>', '=', '<', ';', ':', '9', '8', '7', '6', '5', '4', '3', '2', '1']);
+      equal('{f..a}', ['f', 'e', 'd', 'c', 'b', 'a']);
+      equal('{f..f}', ['f']);
     });
 
     it('should expand multiple ranges:', function() {
-      match('a/{b..d}/e/{f..h}', ['a/b/e/f', 'a/b/e/g', 'a/b/e/h', 'a/c/e/f', 'a/c/e/g', 'a/c/e/h', 'a/d/e/f', 'a/d/e/g', 'a/d/e/h']);
+      equal('a/{b..d}/e/{f..h}', ['a/b/e/f', 'a/b/e/g', 'a/b/e/h', 'a/c/e/f', 'a/c/e/g', 'a/c/e/h', 'a/d/e/f', 'a/d/e/g', 'a/d/e/h']);
     });
   });
 
   describe('combo', function() {
     it('should expand numerical ranges - positive and negative', function() {
-      match('a{01..05}b', ['a01b', 'a02b', 'a03b', 'a04b', 'a05b' ]);
-      match('0{1..9}/{10..20}', ['01/10', '01/11', '01/12', '01/13', '01/14', '01/15', '01/16', '01/17', '01/18', '01/19', '01/20', '02/10', '02/11', '02/12', '02/13', '02/14', '02/15', '02/16', '02/17', '02/18', '02/19', '02/20', '03/10', '03/11', '03/12', '03/13', '03/14', '03/15', '03/16', '03/17', '03/18', '03/19', '03/20', '04/10', '04/11', '04/12', '04/13', '04/14', '04/15', '04/16', '04/17', '04/18', '04/19', '04/20', '05/10', '05/11', '05/12', '05/13', '05/14', '05/15', '05/16',  [...]
-      match('{-10..10}', ['-1', '-10', '-2', '-3', '-4', '-5', '-6', '-7', '-8', '-9', '0', '1', '10', '2', '3', '4', '5', '6', '7', '8', '9' ]);
-    });
-  });
-
-  // HEADS UP! If you're using the `--mm` flag minimatch freezes on these
-  describe('large numbers', function() {
-    it('should expand large numbers', function() {
-      match('{2147483645..2147483649}', ['2147483645', '2147483646', '2147483647', '2147483648', '2147483649']);
-      match('{214748364..2147483649}', ['(21474836[4-9]|2147483[7-9][0-9]|214748[4-9][0-9]{2}|214749[0-9]{3}|2147[5-9][0-9]{4}|214[8-9][0-9]{5}|21[5-9][0-9]{6}|2[2-9][0-9]{7}|[3-9][0-9]{8}|1[0-9]{9}|20[0-9]{8}|21[0-3][0-9]{7}|214[0-6][0-9]{6}|2147[0-3][0-9]{5}|21474[0-7][0-9]{4}|214748[0-2][0-9]{3}|2147483[0-5][0-9]{2}|21474836[0-4][0-9])']);
+      equal('a{01..05}b', ['a01b', 'a02b', 'a03b', 'a04b', 'a05b' ]);
+      equal('0{1..9}/{10..20}', ['01/10', '01/11', '01/12', '01/13', '01/14', '01/15', '01/16', '01/17', '01/18', '01/19', '01/20', '02/10', '02/11', '02/12', '02/13', '02/14', '02/15', '02/16', '02/17', '02/18', '02/19', '02/20', '03/10', '03/11', '03/12', '03/13', '03/14', '03/15', '03/16', '03/17', '03/18', '03/19', '03/20', '04/10', '04/11', '04/12', '04/13', '04/14', '04/15', '04/16', '04/17', '04/18', '04/19', '04/20', '05/10', '05/11', '05/12', '05/13', '05/14', '05/15', '05/16',  [...]
+      equal('{-10..10}', ['-1', '-10', '-2', '-3', '-4', '-5', '-6', '-7', '-8', '-9', '0', '1', '10', '2', '3', '4', '5', '6', '7', '8', '9' ]);
     });
   });
 
   describe('steps > positive ranges', function() {
     it('should expand ranges using steps:', function() {
-      match('{1..10..1}', ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']);
-      match('{1..10..2}', ['1', '3', '5', '7', '9']);
-      match('{1..20..20}', ['1']);
-      match('{1..20..2}', ['1', '3', '5', '7', '9', '11', '13', '15', '17', '19']);
-      match('{10..0..2}', ['10', '8', '6', '4', '2', '0']);
-      match('{10..1..2}', ['10', '8', '6', '4', '2']);
-      match('{100..0..5}', ['100', '95', '90', '85', '80', '75', '70', '65', '60', '55', '50', '45', '40', '35', '30', '25', '20', '15', '10', '5', '0']);
-      match('{2..10..1}', ['2', '3', '4', '5', '6', '7', '8', '9', '10']);
-      match('{2..10..2}', ['2', '4', '6', '8', '10']);
-      match('{2..10..3}', ['2', '5', '8']);
-      match('{a..z..2}', ['a', 'c', 'e', 'g', 'i', 'k', 'm', 'o', 'q', 's', 'u', 'w', 'y']);
+      equal('{1..10..1}', ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']);
+      equal('{1..10..2}', ['1', '3', '5', '7', '9']);
+      equal('{1..20..20}', ['1']);
+      equal('{1..20..2}', ['1', '3', '5', '7', '9', '11', '13', '15', '17', '19']);
+      equal('{10..0..2}', ['10', '8', '6', '4', '2', '0']);
+      equal('{10..1..2}', ['10', '8', '6', '4', '2']);
+      equal('{100..0..5}', ['100', '95', '90', '85', '80', '75', '70', '65', '60', '55', '50', '45', '40', '35', '30', '25', '20', '15', '10', '5', '0']);
+      equal('{2..10..1}', ['2', '3', '4', '5', '6', '7', '8', '9', '10']);
+      equal('{2..10..2}', ['2', '4', '6', '8', '10']);
+      equal('{2..10..3}', ['2', '5', '8']);
+      equal('{a..z..2}', ['a', 'c', 'e', 'g', 'i', 'k', 'm', 'o', 'q', 's', 'u', 'w', 'y']);
     });
 
     it('should expand positive ranges with negative steps:', function() {
-      match('{10..0..-2}', ['10', '8', '6', '4', '2', '0']);
+      equal('{10..0..-2}', ['10', '8', '6', '4', '2', '0']);
     });
   });
 
   describe('steps > negative ranges', function() {
     it('should expand negative ranges using steps:', function() {
-      match('{-1..-10..-2}', ['-1', '-3', '-5', '-7', '-9']);
-      match('{-1..-10..2}', ['-1', '-3', '-5', '-7', '-9']);
-      match('{-10..-2..2}', ['-10', '-8', '-6', '-4', '-2']);
-      match('{-2..-10..1}', ['-2', '-3', '-4', '-5', '-6', '-7', '-8', '-9', '-10']);
-      match('{-2..-10..2}', ['-2', '-4', '-6', '-8', '-10']);
-      match('{-2..-10..3}', ['-2', '-5', '-8']);
-      match('{-50..-0..5}', ['-50', '-45', '-40', '-35', '-30', '-25', '-20', '-15', '-10', '-5', '0']);
-      match('{10..1..-2}', ['2', '4', '6', '8', '10']);
-      match('{100..0..-5}', ['100', '95', '90', '85', '80', '75', '70', '65', '60', '55', '50', '45', '40', '35', '30', '25', '20', '15', '10', '5', '0']);
+      equal('{-1..-10..-2}', ['-1', '-3', '-5', '-7', '-9']);
+      equal('{-1..-10..2}', ['-1', '-3', '-5', '-7', '-9']);
+      equal('{-10..-2..2}', ['-10', '-8', '-6', '-4', '-2']);
+      equal('{-2..-10..1}', ['-2', '-3', '-4', '-5', '-6', '-7', '-8', '-9', '-10']);
+      equal('{-2..-10..2}', ['-2', '-4', '-6', '-8', '-10']);
+      equal('{-2..-10..3}', ['-2', '-5', '-8']);
+      equal('{-50..-0..5}', ['-50', '-45', '-40', '-35', '-30', '-25', '-20', '-15', '-10', '-5', '0']);
+      equal('{10..1..-2}', ['2', '4', '6', '8', '10']);
+      equal('{100..0..-5}', ['100', '95', '90', '85', '80', '75', '70', '65', '60', '55', '50', '45', '40', '35', '30', '25', '20', '15', '10', '5', '0']);
     });
   });
 
   describe('steps > alphabetical ranges', function() {
     it('should expand alpha ranges with steps', function() {
-      match('{a..e..2}', ['a', 'c', 'e']);
-      match('{E..A..2}', ['E', 'C', 'A']);
-      match('{a..z}', ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']);
-      match('{a..z..2}', ['a', 'c', 'e', 'g', 'i', 'k', 'm', 'o', 'q', 's', 'u', 'w', 'y']);
-      match('{z..a..-2}', ['z', 'x', 'v', 't', 'r', 'p', 'n', 'l', 'j', 'h', 'f', 'd', 'b']);
+      equal('{a..e..2}', ['a', 'c', 'e']);
+      equal('{E..A..2}', ['E', 'C', 'A']);
+      equal('{a..z}', ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']);
+      equal('{a..z..2}', ['a', 'c', 'e', 'g', 'i', 'k', 'm', 'o', 'q', 's', 'u', 'w', 'y']);
+      equal('{z..a..-2}', ['z', 'x', 'v', 't', 'r', 'p', 'n', 'l', 'j', 'h', 'f', 'd', 'b']);
     });
 
     it('should expand alpha ranges with negative steps', function() {
-      match('{z..a..-2}', ['z', 'x', 'v', 't', 'r', 'p', 'n', 'l', 'j', 'h', 'f', 'd', 'b']);
+      equal('{z..a..-2}', ['z', 'x', 'v', 't', 'r', 'p', 'n', 'l', 'j', 'h', 'f', 'd', 'b']);
     });
   });
 
   describe('padding', function() {
     it('unwanted zero-padding -- fixed post-bash-4.0', function() {
-      match('{10..0..2}', ['10', '8', '6', '4', '2', '0']);
-      match('{10..0..-2}', ['10', '8', '6', '4', '2', '0']);
-      match('{-50..-0..5}', ['-50', '-45', '-40', '-35', '-30', '-25', '-20', '-15', '-10', '-5', '0']);
+      equal('{10..0..2}', ['10', '8', '6', '4', '2', '0']);
+      equal('{10..0..-2}', ['10', '8', '6', '4', '2', '0']);
+      equal('{-50..-0..5}', ['-50', '-45', '-40', '-35', '-30', '-25', '-20', '-15', '-10', '-5', '0']);
     });
   });
 });
diff --git a/test/expanded.sets.js b/test/expanded.sets.js
index 0d1d6bb..a3a0d0c 100644
--- a/test/expanded.sets.js
+++ b/test/expanded.sets.js
@@ -3,202 +3,214 @@
 var assert = require('assert');
 var braces = require('..');
 
-function match(pattern, expected) {
-  assert.deepEqual(braces.expand(pattern).sort(), expected.sort());
+function equal(pattern, expected) {
+  var actual = braces.expand(pattern);
+  assert.deepEqual(actual.sort(), expected.sort());
 }
 
 describe('expanded sets', function() {
   describe('invalid sets', function() {
     it('should handle invalid sets:', function() {
-      match('{0..10,braces}', ['0..10', 'braces']);
-      match('{1..10,braces}', ['1..10', 'braces']);
+      equal('{0..10,braces}', ['0..10', 'braces']);
+      equal('{1..10,braces}', ['1..10', 'braces']);
+    });
+  });
+
+  describe('extglobs', function() {
+    it('should split on commas when braces are inside extglobs', function() {
+      equal('*(a|{b|c,d})', ['*(a|b|c)', '*(a|d)']);
+    });
+
+    it('should not split on commas in extglobs when inside braces', function() {
+      equal('{a,@(b,c)}', ['a', '@(b,c)']);
+      equal('{a,*(b|c,d)}', ['a', '*(b|c,d)']);
     });
   });
 
   describe('escaping', function() {
     it('should not expand escaped braces', function() {
-      match('y{\\},a}x', ['y}x', 'yax']);
-      match('{a,\\\\{a,b}c}', ['a', '\\\\ac', '\\\\bc']);
-      match('\\{a,b,c,d,e}', ['{a,b,c,d,e}']);
-      match('a/b/c/{x,y\\}', ['a/b/c/{x,y}']);
-      match('a/\\{x,y}/cde', ['a/{x,y}/cde']);
-      match('abcd{efgh', ['abcd{efgh']);
-      match('{abc}', ['{abc}']);
-      match('{x,y,\\{a,b,c\\}}', ['x', 'y', '{a', 'b', 'c}']);
-      match('{x,y,{a,b,c\\}}', ['{x,y,a', '{x,y,b', '{x,y,c}']);
-      match('{x,y,{abc},trie}', ['x', 'y', '{abc}', 'trie']);
-      match('{x\\,y,\\{abc\\},trie}', ['x,y', '{abc}', 'trie']);
+      equal('y{\\},a}x', ['y}x', 'yax']);
+      equal('{a,\\\\{a,b}c}', ['a', '\\\\ac', '\\\\bc']);
+      equal('\\{a,b,c,d,e}', ['{a,b,c,d,e}']);
+      equal('a/b/c/{x,y\\}', ['a/b/c/{x,y}']);
+      equal('a/\\{x,y}/cde', ['a/{x,y}/cde']);
+      equal('abcd{efgh', ['abcd{efgh']);
+      equal('{abc}', ['{abc}']);
+      equal('{x,y,\\{a,b,c\\}}', ['x', 'y', '{a', 'b', 'c}']);
+      equal('{x,y,{a,b,c\\}}', ['{x,y,a', '{x,y,b', '{x,y,c}']);
+      equal('{x,y,{abc},trie}', ['x', 'y', '{abc}', 'trie']);
+      equal('{x\\,y,\\{abc\\},trie}', ['x,y', '{abc}', 'trie']);
     });
 
     it('should handle empty braces', function() {
-      match('{ }', ['{ }']);
-      match('{', ['{']);
-      match('{}', ['{}']);
-      match('}', ['}']);
+      equal('{ }', ['{ }']);
+      equal('{', ['{']);
+      equal('{}', ['{}']);
+      equal('}', ['}']);
     });
 
     it('should handle empty sets', function() {
-      match('{ }', ['{ }']);
-      match('{', ['{']);
-      match('{}', ['{}']);
-      match('}', ['}']);
+      equal('{ }', ['{ }']);
+      equal('{', ['{']);
+      equal('{}', ['{}']);
+      equal('}', ['}']);
     });
 
     it('should escape braces when only one value is defined', function() {
-      match('a{b}c', ['a{b}c']);
-      match('a/b/c{d}e', ['a/b/c{d}e']);
+      equal('a{b}c', ['a{b}c']);
+      equal('a/b/c{d}e', ['a/b/c{d}e']);
     });
 
     it('should escape closing braces when open is not defined', function() {
-      match('{a,b}c,d}', ['ac,d}', 'bc,d}']);
+      equal('{a,b}c,d}', ['ac,d}', 'bc,d}']);
     });
 
     it('should not expand braces in sets with es6/bash-like variables', function() {
-      match('abc/${ddd}/xyz', ['abc/${ddd}/xyz']);
-      match('a${b}c', ['a${b}c']);
-      match('a/{${b},c}/d', ['a/${b}/d', 'a/c/d']);
-      match('a${b,d}/{foo,bar}c', ['a${b,d}/fooc', 'a${b,d}/barc']);
+      equal('abc/${ddd}/xyz', ['abc/${ddd}/xyz']);
+      equal('a${b}c', ['a${b}c']);
+      equal('a/{${b},c}/d', ['a/${b}/d', 'a/c/d']);
+      equal('a${b,d}/{foo,bar}c', ['a${b,d}/fooc', 'a${b,d}/barc']);
     });
 
     it('should not expand escaped commas.', function() {
-      match('a{b\\,c\\,d}e', ['a{b,c,d}e']);
-      match('a{b\\,c}d', ['a{b,c}d']);
-      match('{abc\\,def}', ['{abc,def}']);
-      match('{abc\\,def,ghi}', ['abc,def', 'ghi']);
-      match('a/{b,c}/{x\\,y}/d/e', ['a/b/{x,y}/d/e', 'a/c/{x,y}/d/e']);
+      equal('a{b\\,c\\,d}e', ['a{b,c,d}e']);
+      equal('a{b\\,c}d', ['a{b,c}d']);
+      equal('{abc\\,def}', ['{abc,def}']);
+      equal('{abc\\,def,ghi}', ['abc,def', 'ghi']);
+      equal('a/{b,c}/{x\\,y}/d/e', ['a/b/{x,y}/d/e', 'a/c/{x,y}/d/e']);
     });
 
     it('should return sets with escaped commas', function() {
     });
 
     it('should not expand escaped braces.', function() {
-      match('{a,b\\}c,d}', ['a', 'b}c', 'd']);
-      match('\\{a,b,c,d,e}', ['{a,b,c,d,e}']);
-      match('a/{z,\\{a,b,c,d,e}/d', ['a/{a/d', 'a/b/d', 'a/c/d', 'a/d/d', 'a/e/d', 'a/z/d']);
-      match('a/\\{b,c}/{d,e}/f', ['a/{b,c}/d/f', 'a/{b,c}/e/f']);
-      match('./\\{x,y}/{a..z..3}/', ['./{x,y}/a/', './{x,y}/d/', './{x,y}/g/', './{x,y}/j/', './{x,y}/m/', './{x,y}/p/', './{x,y}/s/', './{x,y}/v/', './{x,y}/y/']);
+      equal('{a,b\\}c,d}', ['a', 'b}c', 'd']);
+      equal('\\{a,b,c,d,e}', ['{a,b,c,d,e}']);
+      equal('a/{z,\\{a,b,c,d,e}/d', ['a/{a/d', 'a/b/d', 'a/c/d', 'a/d/d', 'a/e/d', 'a/z/d']);
+      equal('a/\\{b,c}/{d,e}/f', ['a/{b,c}/d/f', 'a/{b,c}/e/f']);
+      equal('./\\{x,y}/{a..z..3}/', ['./{x,y}/a/', './{x,y}/d/', './{x,y}/g/', './{x,y}/j/', './{x,y}/m/', './{x,y}/p/', './{x,y}/s/', './{x,y}/v/', './{x,y}/y/']);
     });
 
     it('should not expand escaped braces or commas.', function() {
-      match('{x\\,y,\\{abc\\},trie}', ['{abc}', 'trie', 'x,y']);
+      equal('{x\\,y,\\{abc\\},trie}', ['{abc}', 'trie', 'x,y']);
     });
   });
 
   describe('multipliers', function() {
     it('should support multipliers', function() {
-      match('{{d,d},e}{,}', ['d', 'd', 'd', 'd', 'e', 'e']);
-      match('{d{,},e}{,}', ['d', 'd', 'd', 'd', 'e', 'e']);
-      match('a/{,}{c,d}/e', ['a/c/e', 'a/c/e', 'a/d/e', 'a/d/e']);
-      match('a/{c,d}{,}/e', ['a/c/e', 'a/c/e', 'a/d/e', 'a/d/e']);
-      match('a/{b,c{,}}', ['a/b', 'a/c', 'a/c']);
-      match('a{,,}', ['a', 'a', 'a']);
-      match('a{,}', ['a', 'a']);
-      match('a{,}/{c,d}/e', ['a/c/e', 'a/c/e', 'a/d/e', 'a/d/e']);
-      match('a{,}{,}', ['a', 'a', 'a', 'a']);
-      match('a{,}{,}{,}', ['a', 'a', 'a', 'a', 'a', 'a', 'a', 'a']);
-      match('a{,}{,}{,}{,}', ['a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a']);
-      match('{,}', []);
-      match('{a,b{,}{,}{,},c}d', ['ad', 'bd', 'bd', 'bd', 'bd', 'bd', 'bd', 'bd', 'bd', 'cd']);
-      match('{a,b{,}{,}{,}}', ['a', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b']);
-      match('{a{,,}b{,}}', ['{ab}', '{ab}', '{ab}', '{ab}', '{ab}', '{ab}']);
-      match('{d{,},e}{,}', ['d', 'd', 'd', 'd', 'e', 'e']);
-      match('a/{b,c}{,}/{d{,},e}{,}', ['a/b/d', 'a/b/d', 'a/b/d', 'a/b/d', 'a/b/d', 'a/b/d', 'a/b/d', 'a/b/d', 'a/b/e', 'a/b/e', 'a/b/e', 'a/b/e', 'a/c/d', 'a/c/d', 'a/c/d', 'a/c/d', 'a/c/d', 'a/c/d', 'a/c/d', 'a/c/d', 'a/c/e', 'a/c/e', 'a/c/e', 'a/c/e']);
-      match('a/{b,c{,}}', ['a/b', 'a/c', 'a/c']);
-      match('a{,,}', ['a', 'a', 'a']);
-      match('a{,}', ['a', 'a']);
-      match('a{,}/{c,d}/e', ['a/c/e', 'a/c/e', 'a/d/e', 'a/d/e']);
-      match('a/{,}{c,d}/e', ['a/c/e', 'a/c/e', 'a/d/e', 'a/d/e']);
-      match('a/{c,d}{,}/e', ['a/c/e', 'a/c/e', 'a/d/e', 'a/d/e']);
-      match('a{,}{,}', ['a', 'a', 'a', 'a']);
-      match('a{,}{,}{,}', ['a', 'a', 'a', 'a', 'a', 'a', 'a', 'a']);
-      match('a{,}{,}{,}{,}', ['a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a']);
-      match('{,}', []);
-      match('{a,b{,}{,}{,},c}d', ['ad', 'bd', 'bd', 'bd', 'bd', 'bd', 'bd', 'bd', 'bd', 'cd']);
-      match('{a,b{,}{,}{,}}', ['a', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b']);
-      match('{a{,,}b{,}}', ['{ab}', '{ab}', '{ab}', '{ab}', '{ab}', '{ab}']);
+      equal('{{d,d},e}{,}', ['d', 'd', 'd', 'd', 'e', 'e']);
+      equal('{d{,},e}{,}', ['d', 'd', 'd', 'd', 'e', 'e']);
+      equal('a/{,}{c,d}/e', ['a/c/e', 'a/c/e', 'a/d/e', 'a/d/e']);
+      equal('a/{c,d}{,}/e', ['a/c/e', 'a/c/e', 'a/d/e', 'a/d/e']);
+      equal('a/{b,c{,}}', ['a/b', 'a/c', 'a/c']);
+      equal('a{,,}', ['a', 'a', 'a']);
+      equal('a{,}', ['a', 'a']);
+      equal('a{,}/{c,d}/e', ['a/c/e', 'a/c/e', 'a/d/e', 'a/d/e']);
+      equal('a{,}{,}', ['a', 'a', 'a', 'a']);
+      equal('a{,}{,}{,}', ['a', 'a', 'a', 'a', 'a', 'a', 'a', 'a']);
+      equal('a{,}{,}{,}{,}', ['a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a']);
+      equal('{,}', []);
+      equal('{a,b{,}{,}{,},c}d', ['ad', 'bd', 'bd', 'bd', 'bd', 'bd', 'bd', 'bd', 'bd', 'cd']);
+      equal('{a,b{,}{,}{,}}', ['a', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b']);
+      equal('{a{,,}b{,}}', ['{ab}', '{ab}', '{ab}', '{ab}', '{ab}', '{ab}']);
+      equal('{d{,},e}{,}', ['d', 'd', 'd', 'd', 'e', 'e']);
+      equal('a/{b,c}{,}/{d{,},e}{,}', ['a/b/d', 'a/b/d', 'a/b/d', 'a/b/d', 'a/b/d', 'a/b/d', 'a/b/d', 'a/b/d', 'a/b/e', 'a/b/e', 'a/b/e', 'a/b/e', 'a/c/d', 'a/c/d', 'a/c/d', 'a/c/d', 'a/c/d', 'a/c/d', 'a/c/d', 'a/c/d', 'a/c/e', 'a/c/e', 'a/c/e', 'a/c/e']);
+      equal('a/{b,c{,}}', ['a/b', 'a/c', 'a/c']);
+      equal('a{,,}', ['a', 'a', 'a']);
+      equal('a{,}', ['a', 'a']);
+      equal('a{,}/{c,d}/e', ['a/c/e', 'a/c/e', 'a/d/e', 'a/d/e']);
+      equal('a/{,}{c,d}/e', ['a/c/e', 'a/c/e', 'a/d/e', 'a/d/e']);
+      equal('a/{c,d}{,}/e', ['a/c/e', 'a/c/e', 'a/d/e', 'a/d/e']);
+      equal('a{,}{,}', ['a', 'a', 'a', 'a']);
+      equal('a{,}{,}{,}', ['a', 'a', 'a', 'a', 'a', 'a', 'a', 'a']);
+      equal('a{,}{,}{,}{,}', ['a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a']);
+      equal('{,}', []);
+      equal('{a,b{,}{,}{,},c}d', ['ad', 'bd', 'bd', 'bd', 'bd', 'bd', 'bd', 'bd', 'bd', 'cd']);
+      equal('{a,b{,}{,}{,}}', ['a', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b']);
+      equal('{a{,,}b{,}}', ['{ab}', '{ab}', '{ab}', '{ab}', '{ab}', '{ab}']);
     });
   });
 
   describe('set expansion', function() {
     it('should support sequence brace operators', function() {
-      match('{a,b,c}', ['a', 'b', 'c']);
-      match('{1..10}', ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']);
+      equal('{a,b,c}', ['a', 'b', 'c']);
+      equal('{1..10}', ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']);
     });
 
     it('should support sequence braces with leading characters', function() {
-      match('/usr/{ucb/{ex,edit},lib/{ex,how_ex}}', ['/usr/lib/ex', '/usr/lib/how_ex', '/usr/ucb/edit', '/usr/ucb/ex']);
-      match('ff{c,b,a}', ['ffa', 'ffb', 'ffc']);
+      equal('/usr/{ucb/{ex,edit},lib/{ex,how_ex}}', ['/usr/lib/ex', '/usr/lib/how_ex', '/usr/ucb/edit', '/usr/ucb/ex']);
+      equal('ff{c,b,a}', ['ffa', 'ffb', 'ffc']);
     });
 
     it('should support sequence braces with trailing characters', function() {
-      match('f{d,e,f}g', ['fdg', 'feg', 'ffg']);
-      match('{l,n,m}xyz', ['lxyz', 'mxyz', 'nxyz']);
+      equal('f{d,e,f}g', ['fdg', 'feg', 'ffg']);
+      equal('{l,n,m}xyz', ['lxyz', 'mxyz', 'nxyz']);
     });
 
     it('should support sequence braces with trailing characters', function() {
-      match('{braces,{0..10}}', ['braces', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10']);
-      match('{{0..10},braces}', ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'braces']);
-      match('{{1..10..2},braces}', ['1', '3', '5', '7', '9', 'braces']);
-      match('{{1..10},braces}', ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'braces']);
+      equal('{braces,{0..10}}', ['braces', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10']);
+      equal('{{0..10},braces}', ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'braces']);
+      equal('{{1..10..2},braces}', ['1', '3', '5', '7', '9', 'braces']);
+      equal('{{1..10},braces}', ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'braces']);
     });
 
     it('should support nested sequence braces with trailing characters', function() {
-      match('x{{0..10},braces}y', ['xbracesy', 'x0y', 'x1y', 'x2y', 'x3y', 'x4y', 'x5y', 'x6y', 'x7y', 'x8y', 'x9y', 'x10y']);
+      equal('x{{0..10},braces}y', ['xbracesy', 'x0y', 'x1y', 'x2y', 'x3y', 'x4y', 'x5y', 'x6y', 'x7y', 'x8y', 'x9y', 'x10y']);
     });
 
     it('should expand multiple sets', function() {
-      match('a/{a,b}/{c,d}/e', ['a/a/c/e', 'a/a/d/e', 'a/b/c/e', 'a/b/d/e']);
-      match('a{b,c}d{e,f}g', ['abdeg', 'abdfg', 'acdeg', 'acdfg']);
-      match('a/{x,y}/c{d,e}f.{md,txt}', ['a/x/cdf.md', 'a/x/cdf.txt', 'a/x/cef.md', 'a/x/cef.txt', 'a/y/cdf.md', 'a/y/cdf.txt', 'a/y/cef.md', 'a/y/cef.txt']);
+      equal('a/{a,b}/{c,d}/e', ['a/a/c/e', 'a/a/d/e', 'a/b/c/e', 'a/b/d/e']);
+      equal('a{b,c}d{e,f}g', ['abdeg', 'abdfg', 'acdeg', 'acdfg']);
+      equal('a/{x,y}/c{d,e}f.{md,txt}', ['a/x/cdf.md', 'a/x/cdf.txt', 'a/x/cef.md', 'a/x/cef.txt', 'a/y/cdf.md', 'a/y/cdf.txt', 'a/y/cef.md', 'a/y/cef.txt']);
     });
 
     it('should expand nested sets', function() {
-      match('{{d,d},e}{}', ['d{}', 'd{}', 'e{}']);
-      match('{{d,d},e}a', ['da', 'da', 'ea']);
-      match('{{d,d},e}{a,b}', ['da', 'da', 'db', 'db', 'ea', 'eb']);
-      match('{d,d,{d,d},{e,e}}', ['d', 'd', 'd', 'd', 'e', 'e']);
-      match('{{d,d},e}{a,b}', ['da', 'da', 'db', 'db', 'ea', 'eb']);
-      match('{{d,d},e}', ['d', 'd', 'e']);
-      match('{a,b}{{a,b},a,b}', ['aa', 'aa', 'ab', 'ab', 'ba', 'ba', 'bb', 'bb']);
-      match('a{b,c{d,e}f}g', ['abg', 'acdfg', 'acefg']);
-      match('a{{x,y},z}b', ['axb', 'ayb', 'azb']);
-      match('f{x,y{g,z}}h', ['fxh', 'fygh', 'fyzh']);
-      match('a{b,c{d,e},h}x/z', ['abx/z', 'acdx/z', 'acex/z', 'ahx/z']);
-      match('a{b,c{d,e},h}x{y,z}', ['abxy', 'abxz', 'acdxy', 'acdxz', 'acexy', 'acexz', 'ahxy', 'ahxz']);
-      match('a{b,c{d,e},{f,g}h}x{y,z}', ['abxy', 'abxz', 'acdxy', 'acdxz', 'acexy', 'acexz', 'afhxy', 'afhxz', 'aghxy', 'aghxz']);
-      match('a-{b{d,e}}-c', ['a-{bd}-c', 'a-{be}-c']);
+      equal('{{d,d},e}{}', ['d{}', 'd{}', 'e{}']);
+      equal('{{d,d},e}a', ['da', 'da', 'ea']);
+      equal('{{d,d},e}{a,b}', ['da', 'da', 'db', 'db', 'ea', 'eb']);
+      equal('{d,d,{d,d},{e,e}}', ['d', 'd', 'd', 'd', 'e', 'e']);
+      equal('{{d,d},e}{a,b}', ['da', 'da', 'db', 'db', 'ea', 'eb']);
+      equal('{{d,d},e}', ['d', 'd', 'e']);
+      equal('{a,b}{{a,b},a,b}', ['aa', 'aa', 'ab', 'ab', 'ba', 'ba', 'bb', 'bb']);
+      equal('a{b,c{d,e}f}g', ['abg', 'acdfg', 'acefg']);
+      equal('a{{x,y},z}b', ['axb', 'ayb', 'azb']);
+      equal('f{x,y{g,z}}h', ['fxh', 'fygh', 'fyzh']);
+      equal('a{b,c{d,e},h}x/z', ['abx/z', 'acdx/z', 'acex/z', 'ahx/z']);
+      equal('a{b,c{d,e},h}x{y,z}', ['abxy', 'abxz', 'acdxy', 'acdxz', 'acexy', 'acexz', 'ahxy', 'ahxz']);
+      equal('a{b,c{d,e},{f,g}h}x{y,z}', ['abxy', 'abxz', 'acdxy', 'acdxz', 'acexy', 'acexz', 'afhxy', 'afhxz', 'aghxy', 'aghxz']);
+      equal('a-{b{d,e}}-c', ['a-{bd}-c', 'a-{be}-c']);
     });
 
     it('should expand with globs.', function() {
-      match('a/b/{d,e}/*.js', ['a/b/d/*.js', 'a/b/e/*.js']);
-      match('a/**/c/{d,e}/f*.js', ['a/**/c/d/f*.js', 'a/**/c/e/f*.js']);
-      match('a/**/c/{d,e}/f*.{md,txt}', ['a/**/c/d/f*.md', 'a/**/c/d/f*.txt', 'a/**/c/e/f*.md', 'a/**/c/e/f*.txt']);
-      match('a/b/{d,e,[1-5]}/*.js', ['a/b/d/*.js', 'a/b/e/*.js', 'a/b/[1-5]/*.js']);
+      equal('a/b/{d,e}/*.js', ['a/b/d/*.js', 'a/b/e/*.js']);
+      equal('a/**/c/{d,e}/f*.js', ['a/**/c/d/f*.js', 'a/**/c/e/f*.js']);
+      equal('a/**/c/{d,e}/f*.{md,txt}', ['a/**/c/d/f*.md', 'a/**/c/d/f*.txt', 'a/**/c/e/f*.md', 'a/**/c/e/f*.txt']);
+      equal('a/b/{d,e,[1-5]}/*.js', ['a/b/d/*.js', 'a/b/e/*.js', 'a/b/[1-5]/*.js']);
     });
   });
 
   describe('commas', function() {
     it('should work with leading and trailing commas.', function() {
-      match('a{b,}c', ['abc', 'ac']);
-      match('a{,b}c', ['abc', 'ac']);
-      match('{{a,b},a}c', ['ac', 'ac', 'bc']);
-      match('{{a,b},}c', ['ac', 'bc', 'c']);
-      match('a{{a,b},}c', ['aac', 'abc', 'ac']);
+      equal('a{b,}c', ['abc', 'ac']);
+      equal('a{,b}c', ['abc', 'ac']);
+      equal('{{a,b},a}c', ['ac', 'ac', 'bc']);
+      equal('{{a,b},}c', ['ac', 'bc', 'c']);
+      equal('a{{a,b},}c', ['aac', 'abc', 'ac']);
     });
   });
 
   describe('spaces', function() {
     it('should handle spaces', function() {
-      match('0{1..9} {10..20}', ['01 10', '01 11', '01 12', '01 13', '01 14', '01 15', '01 16', '01 17', '01 18', '01 19', '01 20', '02 10', '02 11', '02 12', '02 13', '02 14', '02 15', '02 16', '02 17', '02 18', '02 19', '02 20', '03 10', '03 11', '03 12', '03 13', '03 14', '03 15', '03 16', '03 17', '03 18', '03 19', '03 20', '04 10', '04 11', '04 12', '04 13', '04 14', '04 15', '04 16', '04 17', '04 18', '04 19', '04 20', '05 10', '05 11', '05 12', '05 13', '05 14', '05 15', '05 16',  [...]
-      match('a{ ,c{d, },h}x', ['acdx', 'ac x', 'ahx', 'a x']);
-      match('a{ ,c{d, },h} ', ['a  ', 'ac  ', 'acd ', 'ah ']);
+      equal('0{1..9} {10..20}', ['01 10', '01 11', '01 12', '01 13', '01 14', '01 15', '01 16', '01 17', '01 18', '01 19', '01 20', '02 10', '02 11', '02 12', '02 13', '02 14', '02 15', '02 16', '02 17', '02 18', '02 19', '02 20', '03 10', '03 11', '03 12', '03 13', '03 14', '03 15', '03 16', '03 17', '03 18', '03 19', '03 20', '04 10', '04 11', '04 12', '04 13', '04 14', '04 15', '04 16', '04 17', '04 18', '04 19', '04 20', '05 10', '05 11', '05 12', '05 13', '05 14', '05 15', '05 16',  [...]
+      equal('a{ ,c{d, },h}x', ['acdx', 'ac x', 'ahx', 'a x']);
+      equal('a{ ,c{d, },h} ', ['a  ', 'ac  ', 'acd ', 'ah ']);
 
       // see https://github.com/jonschlinkert/micromatch/issues/66
-      match('/Users/tobiasreich/Sites/aaa/bbb/ccc 2016/src/**/[^_]*.{html,ejs}', ['/Users/tobiasreich/Sites/aaa/bbb/ccc 2016/src/**/[^_]*.ejs', '/Users/tobiasreich/Sites/aaa/bbb/ccc 2016/src/**/[^_]*.html' ]);
+      equal('/Users/tobiasreich/Sites/aaa/bbb/ccc 2016/src/**/[^_]*.{html,ejs}', ['/Users/tobiasreich/Sites/aaa/bbb/ccc 2016/src/**/[^_]*.ejs', '/Users/tobiasreich/Sites/aaa/bbb/ccc 2016/src/**/[^_]*.html' ]);
 
       // Bash 4.3 says the following should be equivalent to `foo|(1|2)|bar`,
       // That makes sense in Bash, since ' ' is a separator, but not here.
-      match('foo {1,2} bar', ['foo 1 bar', 'foo 2 bar']);
+      equal('foo {1,2} bar', ['foo 1 bar', 'foo 2 bar']);
     });
   });
 });
diff --git a/test/multiples.js b/test/multiples.js
index 44869ed..ff8b244 100644
--- a/test/multiples.js
+++ b/test/multiples.js
@@ -3,7 +3,7 @@
 var assert = require('assert');
 var braces = require('..');
 
-function match(pattern, expected, options) {
+function equal(pattern, expected, options) {
   var actual = braces.expand(pattern, options).sort();
   assert.deepEqual(actual, expected.sort(), pattern);
 }
@@ -16,6 +16,7 @@ describe('multiples', function() {
     ['a/{,}/b', ['a//b', 'a//b']],
     ['a/{,}{c,d}/e', ['a/c/e', 'a/c/e', 'a/d/e', 'a/d/e']],
     ['a/{a,b,{,}{,}{,},c}/b', ['a//b', 'a//b', 'a//b', 'a//b', 'a//b', 'a//b', 'a//b', 'a//b', 'a/a/b', 'a/b/b', 'a/c/b']],
+    ['a/{a,b,{,},c}/b', ['a//b', 'a//b', 'a/a/b', 'a/b/b', 'a/c/b']],
     ['a/{a,b,{,}{,}{,}}/b', ['a//b', 'a//b', 'a//b', 'a//b', 'a//b', 'a//b', 'a//b', 'a//b', 'a/a/b', 'a/b/b']],
     ['a/{b,cz{,}}/{d{,},ef}{,}', ['a/b/d', 'a/b/d', 'a/b/d', 'a/b/d', 'a/b/ef', 'a/b/ef', 'a/cz/d', 'a/cz/d', 'a/cz/d', 'a/cz/d', 'a/cz/d', 'a/cz/d', 'a/cz/d', 'a/cz/d', 'a/cz/ef', 'a/cz/ef', 'a/cz/ef', 'a/cz/ef']],
     ['a/{b,cz}{,}/{d{,},ef}{,}', ['a/b/d', 'a/b/d', 'a/b/d', 'a/b/d', 'a/b/d', 'a/b/d', 'a/b/d', 'a/b/d', 'a/b/ef', 'a/b/ef', 'a/b/ef', 'a/b/ef', 'a/cz/d', 'a/cz/d', 'a/cz/d', 'a/cz/d', 'a/cz/d', 'a/cz/d', 'a/cz/d', 'a/cz/d', 'a/cz/ef', 'a/cz/ef', 'a/cz/ef', 'a/cz/ef']],
@@ -54,7 +55,7 @@ describe('multiples', function() {
 
   patterns.forEach(function(pattern) {
     it('should expand: ' + pattern[0], function() {
-      match(pattern[0], pattern[1]);
+      equal(pattern[0], pattern[1]);
     });
   });
 });
diff --git a/test/optimized.js b/test/optimized.js
index 070c905..e11eb84 100644
--- a/test/optimized.js
+++ b/test/optimized.js
@@ -11,7 +11,7 @@ require('mocha');
 var assert = require('assert');
 var braces = require('..');
 
-function match(pattern, expected, options) {
+function equal(pattern, expected, options) {
   assert.deepEqual(braces(pattern, options), expected);
 }
 
@@ -19,129 +19,143 @@ describe('optimized', function() {
   describe('sets', function() {
     describe('invalid sets', function() {
       it('should handle invalid sets:', function() {
-        match('{0..10,braces}', ['(0..10|braces)']);
-        match('{1..10,braces}', ['(1..10|braces)']);
+        equal('{0..10,braces}', ['(0..10|braces)']);
+        equal('{1..10,braces}', ['(1..10|braces)']);
+      });
+    });
+
+    describe('extglobs', function() {
+      it('should not optimize when preceded by an extglob character', function() {
+        equal('a/@{b,c}/d', ['a/@b/d', 'a/@c/d']);
+        equal('a/!{b,c}/d', ['a/!b/d', 'a/!c/d']);
+        equal('a/*{b,c}/d', ['a/*b/d', 'a/*c/d']);
+        equal('a/+{b,c}/d', ['a/+b/d', 'a/+c/d']);
+        equal('a/?{b,c}/d', ['a/?b/d', 'a/?c/d']);
+      });
+
+      it('should not optimize when brace set contains extglobs', function() {
+        equal('{b,[F-Z],!([B-F])}.js', ['b.js', '[F-Z].js', '!([B-F]).js']);
       });
     });
 
     describe('escaping', function() {
       it('should not expand escaped braces', function() {
-        match('\\{a,b,c,d,e}', ['{a,b,c,d,e}']);
-        match('a/b/c/{x,y\\}', ['a/b/c/{x,y}']);
-        match('a/\\{x,y}/cde', ['a/{x,y}/cde']);
-        match('abcd{efgh', ['abcd{efgh']);
-        match('{abc}', ['{abc}']);
-        match('{x,y,\\{a,b,c\\}}', ['(x|y|{a|b|c})']);
-        match('{x,y,{a,b,c\\}}', ['{x,y,(a|b|c})']);
-        match('{x,y,{abc},trie}', ['(x|y|{abc}|trie)']);
-        match('{x\\,y,\\{abc\\},trie}', ['(x,y|{abc}|trie)']);
+        equal('\\{a,b,c,d,e}', ['{a,b,c,d,e}']);
+        equal('a/b/c/{x,y\\}', ['a/b/c/{x,y}']);
+        equal('a/\\{x,y}/cde', ['a/{x,y}/cde']);
+        equal('abcd{efgh', ['abcd{efgh']);
+        equal('{abc}', ['{abc}']);
+        equal('{x,y,\\{a,b,c\\}}', ['(x|y|{a|b|c})']);
+        equal('{x,y,{a,b,c\\}}', ['{x,y,(a|b|c})']);
+        equal('{x,y,{abc},trie}', ['(x|y|{abc}|trie)']);
+        equal('{x\\,y,\\{abc\\},trie}', ['(x,y|{abc}|trie)']);
       });
 
       it('should handle spaces', function() {
         // Bash 4.3 says the following should be equivalent to `foo|(1|2)|bar`,
         // That makes sense in Bash, since ' ' is a separator, but not here.
-        match('foo {1,2} bar', ['foo (1|2) bar']);
+        equal('foo {1,2} bar', ['foo (1|2) bar']);
       });
 
       it('should handle empty braces', function() {
-        match('{ }', ['{ }']);
-        match('{', ['{']);
-        match('{}', ['{}']);
-        match('}', ['}']);
+        equal('{ }', ['{ }']);
+        equal('{', ['{']);
+        equal('{}', ['{}']);
+        equal('}', ['}']);
       });
 
       it('should escape braces when only one value is defined', function() {
-        match('a{b}c', ['a{b}c']);
-        match('a/b/c{d}e', ['a/b/c{d}e']);
+        equal('a{b}c', ['a{b}c']);
+        equal('a/b/c{d}e', ['a/b/c{d}e']);
       });
 
       it('should not expand braces in sets with es6/bash-like variables', function() {
-        match('abc/${ddd}/xyz', ['abc/${ddd}/xyz']);
-        match('a${b}c', ['a${b}c']);
-        match('a/{${b},c}/d', ['a/(${b}|c)/d']);
-        match('a${b,d}/{foo,bar}c', ['a${b,d}/(foo|bar)c']);
+        equal('abc/${ddd}/xyz', ['abc/${ddd}/xyz']);
+        equal('a${b}c', ['a${b}c']);
+        equal('a/{${b},c}/d', ['a/(${b}|c)/d']);
+        equal('a${b,d}/{foo,bar}c', ['a${b,d}/(foo|bar)c']);
       });
 
       it('should not expand escaped commas.', function() {
-        match('a{b\\,c\\,d}e', ['a{b,c,d}e']);
-        match('a{b\\,c}d', ['a{b,c}d']);
-        match('{abc\\,def}', ['{abc,def}']);
-        match('{abc\\,def,ghi}', ['(abc,def|ghi)']);
-        match('a/{b,c}/{x\\,y}/d/e', ['a/(b|c)/{x,y}/d/e']);
+        equal('a{b\\,c\\,d}e', ['a{b,c,d}e']);
+        equal('a{b\\,c}d', ['a{b,c}d']);
+        equal('{abc\\,def}', ['{abc,def}']);
+        equal('{abc\\,def,ghi}', ['(abc,def|ghi)']);
+        equal('a/{b,c}/{x\\,y}/d/e', ['a/(b|c)/{x,y}/d/e']);
       });
 
       it('should return sets with escaped commas', function() {
-        match('a/{b,c}/{x\\,y}/d/e', ['a/(b|c)/{x,y}/d/e']);
+        equal('a/{b,c}/{x\\,y}/d/e', ['a/(b|c)/{x,y}/d/e']);
       });
 
       it('should not expand escaped braces.', function() {
-        match('{a,b\\}c,d}', ['(a|b}c|d)']);
-        match('\\{a,b,c,d,e}', ['{a,b,c,d,e}']);
-        match('a/{z,\\{a,b,c,d,e}/d', ['a/(z|{a|b|c|d|e)/d']);
-        match('a/\\{b,c}/{d,e}/f', ['a/{b,c}/(d|e)/f']);
-        match('./\\{x,y}/{a..z..3}/', ['./{x,y}/(a|d|g|j|m|p|s|v|y)/']);
+        equal('{a,b\\}c,d}', ['(a|b}c|d)']);
+        equal('\\{a,b,c,d,e}', ['{a,b,c,d,e}']);
+        equal('a/{z,\\{a,b,c,d,e}/d', ['a/(z|{a|b|c|d|e)/d']);
+        equal('a/\\{b,c}/{d,e}/f', ['a/{b,c}/(d|e)/f']);
+        equal('./\\{x,y}/{a..z..3}/', ['./{x,y}/(a|d|g|j|m|p|s|v|y)/']);
       });
 
       it('should not expand escaped braces or commas.', function() {
-        match('{x\\,y,\\{abc\\},trie}', ['(x,y|{abc}|trie)']);
+        equal('{x\\,y,\\{abc\\},trie}', ['(x,y|{abc}|trie)']);
       });
     });
 
     describe('set expansion', function() {
       it('should support sequence brace operators', function() {
-        match('/usr/{ucb/{ex,edit},lib/{ex,how_ex}}', ['/usr/(ucb/(ex|edit)|lib/(ex|how_ex))']);
-        match('ff{c,b,a}', ['ff(c|b|a)']);
-        match('f{d,e,f}g', ['f(d|e|f)g']);
-        match('x{{0..10},braces}y', ['x(([0-9]|10)|braces)y']);
-        match('{1..10}', ['([1-9]|10)']);
-        match('{a,b,c}', ['(a|b|c)']);
-        match('{braces,{0..10}}', ['(braces|([0-9]|10))']);
-        match('{l,n,m}xyz', ['(l|n|m)xyz']);
-        match('{{0..10},braces}', ['(([0-9]|10)|braces)']);
-        match('{{1..10..2},braces}', ['((1|3|5|7|9)|braces)']);
-        match('{{1..10},braces}', ['(([1-9]|10)|braces)']);
+        equal('/usr/{ucb/{ex,edit},lib/{ex,how_ex}}', ['/usr/(ucb/(ex|edit)|lib/(ex|how_ex))']);
+        equal('ff{c,b,a}', ['ff(c|b|a)']);
+        equal('f{d,e,f}g', ['f(d|e|f)g']);
+        equal('x{{0..10},braces}y', ['x(([0-9]|10)|braces)y']);
+        equal('{1..10}', ['([1-9]|10)']);
+        equal('{a,b,c}', ['(a|b|c)']);
+        equal('{braces,{0..10}}', ['(braces|([0-9]|10))']);
+        equal('{l,n,m}xyz', ['(l|n|m)xyz']);
+        equal('{{0..10},braces}', ['(([0-9]|10)|braces)']);
+        equal('{{1..10..2},braces}', ['((1|3|5|7|9)|braces)']);
+        equal('{{1..10},braces}', ['(([1-9]|10)|braces)']);
       });
 
       it('should expand multiple sets', function() {
-        match('a/{a,b}/{c,d}/e', ['a/(a|b)/(c|d)/e']);
-        match('a{b,c}d{e,f}g', ['a(b|c)d(e|f)g']);
-        match('a/{x,y}/c{d,e}f.{md,txt}', ['a/(x|y)/c(d|e)f.(md|txt)']);
+        equal('a/{a,b}/{c,d}/e', ['a/(a|b)/(c|d)/e']);
+        equal('a{b,c}d{e,f}g', ['a(b|c)d(e|f)g']);
+        equal('a/{x,y}/c{d,e}f.{md,txt}', ['a/(x|y)/c(d|e)f.(md|txt)']);
       });
 
       it('should expand nested sets', function() {
-        match('{a,b}{{a,b},a,b}', ['(a|b)((a|b)|a|b)']);
-        match('a{b,c{d,e}f}g', ['a(b|c(d|e)f)g']);
-        match('a{{x,y},z}b', ['a((x|y)|z)b']);
-        match('f{x,y{g,z}}h', ['f(x|y(g|z))h']);
-        match('a{b,c}{d,e}/hx/z', ['a(b|c)(d|e)/hx/z']);
-        match('a{b,c{d,e},h}x/z', ['a(b|c(d|e)|h)x/z']);
-        match('a{b,c{d,e},h}x{y,z}', ['a(b|c(d|e)|h)x(y|z)']);
-        match('a{b,c{d,e},{f,g}h}x{y,z}', ['a(b|c(d|e)|(f|g)h)x(y|z)']);
-        match('a-{b{d,e}}-c', ['a-{b(d|e)}-c']);
+        equal('{a,b}{{a,b},a,b}', ['(a|b)((a|b)|a|b)']);
+        equal('a{b,c{d,e}f}g', ['a(b|c(d|e)f)g']);
+        equal('a{{x,y},z}b', ['a((x|y)|z)b']);
+        equal('f{x,y{g,z}}h', ['f(x|y(g|z))h']);
+        equal('a{b,c}{d,e}/hx/z', ['a(b|c)(d|e)/hx/z']);
+        equal('a{b,c{d,e},h}x/z', ['a(b|c(d|e)|h)x/z']);
+        equal('a{b,c{d,e},h}x{y,z}', ['a(b|c(d|e)|h)x(y|z)']);
+        equal('a{b,c{d,e},{f,g}h}x{y,z}', ['a(b|c(d|e)|(f|g)h)x(y|z)']);
+        equal('a-{b{d,e}}-c', ['a-{b(d|e)}-c']);
       });
 
       it('should expand not modify non-brace characters', function() {
-        match('a/b/{d,e}/*.js', ['a/b/(d|e)/*.js']);
-        match('a/**/c/{d,e}/f*.js', ['a/**/c/(d|e)/f*.js']);
-        match('a/**/c/{d,e}/f*.{md,txt}', ['a/**/c/(d|e)/f*.(md|txt)']);
+        equal('a/b/{d,e}/*.js', ['a/b/(d|e)/*.js']);
+        equal('a/**/c/{d,e}/f*.js', ['a/**/c/(d|e)/f*.js']);
+        equal('a/**/c/{d,e}/f*.{md,txt}', ['a/**/c/(d|e)/f*.(md|txt)']);
       });
     });
 
     describe('commas', function() {
       it('should work with leading and trailing commas.', function() {
-        match('a{b,}c', ['a(b|)c']);
-        match('a{,b}c', ['a(|b)c']);
+        equal('a{b,}c', ['a(b|)c']);
+        equal('a{,b}c', ['a(|b)c']);
       });
     });
 
     describe('spaces', function() {
       it('should handle spaces', function() {
-        match('0{1..9} {10..20}', ['0([1-9]) (1[0-9]|20)']);
-        match('a{ ,c{d, },h}x', ['a( |c(d| )|h)x']);
-        match('a{ ,c{d, },h} ', ['a( |c(d| )|h) ']);
+        equal('0{1..9} {10..20}', ['0([1-9]) (1[0-9]|20)']);
+        equal('a{ ,c{d, },h}x', ['a( |c(d| )|h)x']);
+        equal('a{ ,c{d, },h} ', ['a( |c(d| )|h) ']);
 
-        // see https://github.com/jonschlinkert/micromatch/issues/66
-        match('/Users/tobiasreich/Sites/aaa/bbb/ccc 2016/src/**/[^_]*.{html,ejs}', ['/Users/tobiasreich/Sites/aaa/bbb/ccc 2016/src/**/[^_]*.(html|ejs)']);
+        // see https://github.com/jonschlinkert/microequal/issues/66
+        equal('/Users/tobiasreich/Sites/aaa/bbb/ccc 2016/src/**/[^_]*.{html,ejs}', ['/Users/tobiasreich/Sites/aaa/bbb/ccc 2016/src/**/[^_]*.(html|ejs)']);
       });
     });
   });
@@ -153,198 +167,252 @@ describe('optimized', function() {
   describe('ranges', function() {
     describe('escaping / invalid ranges', function() {
       it('should not try to expand ranges with decimals', function() {
-        match('{1.1..2.1}', ['{1.1..2.1}']);
-        match('{1.1..~2.1}', ['{1.1..~2.1}']);
+        equal('{1.1..2.1}', ['{1.1..2.1}']);
+        equal('{1.1..~2.1}', ['{1.1..~2.1}']);
       });
 
       it('should escape invalid ranges:', function() {
-        match('{1..0f}', ['{1..0f}']);
-        match('{1..10..ff}', ['{1..10..ff}']);
-        match('{1..10.f}', ['{1..10.f}']);
-        match('{1..10f}', ['{1..10f}']);
-        match('{1..20..2f}', ['{1..20..2f}']);
-        match('{1..20..f2}', ['{1..20..f2}']);
-        match('{1..2f..2}', ['{1..2f..2}']);
-        match('{1..ff..2}', ['{1..ff..2}']);
-        match('{1..ff}', ['{1..ff}']);
-        match('{1..f}', ['([1-f])']);
-        match('{1.20..2}', ['{1.20..2}']);
+        equal('{1..0f}', ['{1..0f}']);
+        equal('{1..10..ff}', ['{1..10..ff}']);
+        equal('{1..10.f}', ['{1..10.f}']);
+        equal('{1..10f}', ['{1..10f}']);
+        equal('{1..20..2f}', ['{1..20..2f}']);
+        equal('{1..20..f2}', ['{1..20..f2}']);
+        equal('{1..2f..2}', ['{1..2f..2}']);
+        equal('{1..ff..2}', ['{1..ff..2}']);
+        equal('{1..ff}', ['{1..ff}']);
+        equal('{1..f}', ['([1-f])']);
+        equal('{1.20..2}', ['{1.20..2}']);
       });
 
       it('weirdly-formed brace expansions -- fixed in post-bash-3.1', function() {
-        match('a-{b{d,e}}-c', ['a-{b(d|e)}-c']);
-        match('a-{bdef-{g,i}-c', ['a-{bdef-(g|i)-c']);
+        equal('a-{b{d,e}}-c', ['a-{b(d|e)}-c']);
+        equal('a-{bdef-{g,i}-c', ['a-{bdef-(g|i)-c']);
       });
 
       it('should not expand quoted strings.', function() {
-        match('{"klklkl"}{1,2,3}', ['{klklkl}(1|2|3)']);
-        match('{"x,x"}', ['{x,x}']);
+        equal('{"klklkl"}{1,2,3}', ['{klklkl}(1|2|3)']);
+        equal('{"x,x"}', ['{x,x}']);
       });
 
       it('should escaped outer braces in nested non-sets', function() {
-        match('{a-{b,c,d}}', ['{a-(b|c|d)}']);
-        match('{a,{a-{b,c,d}}}', ['(a|{a-(b|c|d)})']);
+        equal('{a-{b,c,d}}', ['{a-(b|c|d)}']);
+        equal('{a,{a-{b,c,d}}}', ['(a|{a-(b|c|d)})']);
       });
 
       it('should escape imbalanced braces', function() {
-        match('a-{bdef-{g,i}-c', ['a-{bdef-(g|i)-c']);
-        match('abc{', ['abc{']);
-        match('{abc{', ['{abc{']);
-        match('{abc', ['{abc']);
-        match('}abc', ['}abc']);
-        match('ab{c', ['ab{c']);
-        match('{{a,b}', ['{(a|b)']);
-        match('{a,b}}', ['(a|b)}']);
-        match('abcd{efgh', ['abcd{efgh']);
-        match('a{b{c{d,e}f}g}h', ['a(b(c(d|e)f)g)h']);
-        match('f{x,y{{g,z}}h}', ['f(x|y((g|z))h)']);
-        match('z{a,b},c}d', ['z(a|b),c}d']);
-        match('a{b{c{d,e}f{x,y{{g}h', ['a{b{c(d|e)f{x,y{{g}h']);
-        match('f{x,y{{g}h', ['f{x,y{{g}h']);
-        match('f{x,y{{g}}h', ['f{x,y{{g}}h']);
-        match('a{b{c{d,e}f{x,y{}g}h', ['a{b{c(d|e)f(x|y{}g)h']);
-        match('f{x,y{}g}h', ['f(x|y{}g)h']);
-        match('z{a,b{,c}d', ['z{a,b(|c)d']);
+        equal('a-{bdef-{g,i}-c', ['a-{bdef-(g|i)-c']);
+        equal('abc{', ['abc{']);
+        equal('{abc{', ['{abc{']);
+        equal('{abc', ['{abc']);
+        equal('}abc', ['}abc']);
+        equal('ab{c', ['ab{c']);
+        equal('{{a,b}', ['{(a|b)']);
+        equal('{a,b}}', ['(a|b)}']);
+        equal('abcd{efgh', ['abcd{efgh']);
+        equal('a{b{c{d,e}f}g}h', ['a(b(c(d|e)f)g)h']);
+        equal('f{x,y{{g,z}}h}', ['f(x|y((g|z))h)']);
+        equal('z{a,b},c}d', ['z(a|b),c}d']);
+        equal('a{b{c{d,e}f{x,y{{g}h', ['a{b{c(d|e)f{x,y{{g}h']);
+        equal('f{x,y{{g}h', ['f{x,y{{g}h']);
+        equal('f{x,y{{g}}h', ['f{x,y{{g}}h']);
+        equal('a{b{c{d,e}f{x,y{}g}h', ['a{b{c(d|e)f(x|y{}g)h']);
+        equal('f{x,y{}g}h', ['f(x|y{}g)h']);
+        equal('z{a,b{,c}d', ['z{a,b(|c)d']);
       });
     });
 
     describe('positive numeric ranges', function() {
       it('should expand numeric ranges', function() {
-        match('a{0..3}d', ['a([0-3])d']);
-        match('x{10..1}y', ['x([1-9]|10)y']);
-        match('x{3..3}y', ['x3y']);
-        match('{1..10}', ['([1-9]|10)']);
-        match('{1..3}', ['([1-3])']);
-        match('{1..9}', ['([1-9])']);
-        match('{10..1}', ['([1-9]|10)']);
-        match('{10..1}y', ['([1-9]|10)y']);
-        match('{3..3}', ['3']);
-        match('{5..8}', ['([5-8])']);
+        equal('a{0..3}d', ['a([0-3])d']);
+        equal('x{10..1}y', ['x([1-9]|10)y']);
+        equal('x{3..3}y', ['x3y']);
+        equal('{1..10}', ['([1-9]|10)']);
+        equal('{1..3}', ['([1-3])']);
+        equal('{1..9}', ['([1-9])']);
+        equal('{10..1}', ['([1-9]|10)']);
+        equal('{10..1}y', ['([1-9]|10)y']);
+        equal('{3..3}', ['3']);
+        equal('{5..8}', ['([5-8])']);
       });
     });
 
     describe('negative ranges', function() {
       it('should expand ranges with negative numbers', function() {
-        match('{-1..-10}', ['(-[1-9]|-10)']);
-        match('{-10..-1}', ['(-[1-9]|-10)']);
-        match('{-20..0}', ['(-[1-9]|-1[0-9]|-20|0)']);
-        match('{0..-5}', ['(-[1-5]|0)']);
-        match('{9..-4}', ['(-[1-4]|[0-9])']);
+        equal('{-1..-10}', ['(-[1-9]|-10)']);
+        equal('{-10..-1}', ['(-[1-9]|-10)']);
+        equal('{-20..0}', ['(-[1-9]|-1[0-9]|-20|0)']);
+        equal('{0..-5}', ['(-[1-5]|0)']);
+        equal('{9..-4}', ['(-[1-4]|[0-9])']);
       });
     });
 
     describe('alphabetical ranges', function() {
       it('should expand alphabetical ranges', function() {
-        match('0{1..9}/{10..20}', ['0([1-9])/(1[0-9]|20)']);
-        match('0{a..d}0', ['0([a-d])0']);
-        match('a/{b..d}/e', ['a/([b-d])/e']);
-        match('{1..f}', ['([1-f])']);
-        match('{a..A}', ['([A-a])']);
-        match('{A..a}', ['([A-a])']);
-        match('{a..e}', ['([a-e])']);
-        match('{A..E}', ['([A-E])']);
-        match('{a..f}', ['([a-f])']);
-        match('{a..z}', ['([a-z])']);
-        match('{E..A}', ['([A-E])']);
-        match('{f..1}', ['([1-f])']);
-        match('{f..a}', ['([a-f])']);
-        match('{f..f}', ['f']);
+        equal('0{1..9}/{10..20}', ['0([1-9])/(1[0-9]|20)']);
+        equal('0{a..d}0', ['0([a-d])0']);
+        equal('a/{b..d}/e', ['a/([b-d])/e']);
+        equal('{1..f}', ['([1-f])']);
+        equal('{a..A}', ['([A-a])']);
+        equal('{A..a}', ['([A-a])']);
+        equal('{a..e}', ['([a-e])']);
+        equal('{A..E}', ['([A-E])']);
+        equal('{a..f}', ['([a-f])']);
+        equal('{a..z}', ['([a-z])']);
+        equal('{E..A}', ['([A-E])']);
+        equal('{f..1}', ['([1-f])']);
+        equal('{f..a}', ['([a-f])']);
+        equal('{f..f}', ['f']);
       });
 
       it('should expand multiple ranges:', function() {
-        match('a/{b..d}/e/{f..h}', ['a/([b-d])/e/([f-h])']);
+        equal('a/{b..d}/e/{f..h}', ['a/([b-d])/e/([f-h])']);
       });
     });
 
     describe('combo', function() {
       it('should expand numerical ranges - positive and negative', function() {
-        match('{-10..10}', ['(-[1-9]|-?10|[0-9])']);
+        equal('{-10..10}', ['(-[1-9]|-?10|[0-9])']);
       });
     });
 
     // HEADS UP! If you're using the `--mm` flag minimatch freezes on these
     describe('large numbers', function() {
       it('should expand large numbers', function() {
-        match('{2147483645..2147483649}', ['(214748364[5-9])']);
-        match('{214748364..2147483649}', ['(21474836[4-9]|2147483[7-9][0-9]|214748[4-9][0-9]{2}|214749[0-9]{3}|2147[5-9][0-9]{4}|214[8-9][0-9]{5}|21[5-9][0-9]{6}|2[2-9][0-9]{7}|[3-9][0-9]{8}|1[0-9]{9}|20[0-9]{8}|21[0-3][0-9]{7}|214[0-6][0-9]{6}|2147[0-3][0-9]{5}|21474[0-7][0-9]{4}|214748[0-2][0-9]{3}|2147483[0-5][0-9]{2}|21474836[0-4][0-9])']);
+        equal('{2147483645..2147483649}', ['(214748364[5-9])']);
+        equal('{214748364..2147483649}', ['(21474836[4-9]|2147483[7-9][0-9]|214748[4-9][0-9]{2}|214749[0-9]{3}|2147[5-9][0-9]{4}|214[89][0-9]{5}|21[5-9][0-9]{6}|2[2-9][0-9]{7}|[3-9][0-9]{8}|1[0-9]{9}|20[0-9]{8}|21[0-3][0-9]{7}|214[0-6][0-9]{6}|2147[0-3][0-9]{5}|21474[0-7][0-9]{4}|214748[0-2][0-9]{3}|2147483[0-5][0-9]{2}|21474836[0-4][0-9])']);
       });
     });
 
     describe('steps > positive ranges', function() {
       it('should expand ranges using steps:', function() {
-        match('{1..10..1}', ['([1-9]|10)']);
-        match('{1..10..2}', ['(1|3|5|7|9)']);
-        match('{1..20..20}', ['1']);
-        match('{1..20..20}', ['1']);
-        match('{1..20..20}', ['1']);
-        match('{1..20..2}', ['(1|3|5|7|9|11|13|15|17|19)']);
-        match('{10..0..2}', ['(10|8|6|4|2|0)']);
-        match('{10..1..2}', ['(10|8|6|4|2)']);
-        match('{100..0..5}', ['(100|95|90|85|80|75|70|65|60|55|50|45|40|35|30|25|20|15|10|5|0)']);
-        match('{2..10..1}', ['([2-9]|10)']);
-        match('{2..10..2}', ['(2|4|6|8|10)']);
-        match('{2..10..3}', ['(2|5|8)']);
-        match('{a..z..2}', ['(a|c|e|g|i|k|m|o|q|s|u|w|y)']);
+        equal('{1..10..1}', ['([1-9]|10)']);
+        equal('{1..10..2}', ['(1|3|5|7|9)']);
+        equal('{1..20..20}', ['1']);
+        equal('{1..20..20}', ['1']);
+        equal('{1..20..20}', ['1']);
+        equal('{1..20..2}', ['(1|3|5|7|9|11|13|15|17|19)']);
+        equal('{10..0..2}', ['(10|8|6|4|2|0)']);
+        equal('{10..1..2}', ['(10|8|6|4|2)']);
+        equal('{100..0..5}', ['(100|95|90|85|80|75|70|65|60|55|50|45|40|35|30|25|20|15|10|5|0)']);
+        equal('{2..10..1}', ['([2-9]|10)']);
+        equal('{2..10..2}', ['(2|4|6|8|10)']);
+        equal('{2..10..3}', ['(2|5|8)']);
+        equal('{a..z..2}', ['(a|c|e|g|i|k|m|o|q|s|u|w|y)']);
       });
 
       it('should expand positive ranges with negative steps:', function() {
-        match('{10..0..-2}', ['(10|8|6|4|2|0)']);
+        equal('{10..0..-2}', ['(10|8|6|4|2|0)']);
       });
     });
 
     describe('steps > negative ranges', function() {
       it('should expand negative ranges using steps:', function() {
-        match('{-1..-10..-2}', ['(-(1|3|5|7|9))']);
-        match('{-1..-10..2}', ['(-(1|3|5|7|9))']);
-        match('{-10..-2..2}', ['(-(10|8|6|4|2))']);
-        match('{-2..-10..1}', ['(-[2-9]|-10)']);
-        match('{-2..-10..2}', ['(-(2|4|6|8|10))']);
-        match('{-2..-10..3}', ['(-(2|5|8))']);
-        match('{-50..-0..5}', ['(0|-(50|45|40|35|30|25|20|15|10|5))']);
-        match('{-9..9..3}', ['(0|3|6|9|-(9|6|3))']);
-        match('{10..1..-2}', ['(10|8|6|4|2)']);
-        match('{100..0..-5}', ['(100|95|90|85|80|75|70|65|60|55|50|45|40|35|30|25|20|15|10|5|0)']);
+        equal('{-1..-10..-2}', ['(-(1|3|5|7|9))']);
+        equal('{-1..-10..2}', ['(-(1|3|5|7|9))']);
+        equal('{-10..-2..2}', ['(-(10|8|6|4|2))']);
+        equal('{-2..-10..1}', ['(-[2-9]|-10)']);
+        equal('{-2..-10..2}', ['(-(2|4|6|8|10))']);
+        equal('{-2..-10..3}', ['(-(2|5|8))']);
+        equal('{-50..-0..5}', ['(0|-(50|45|40|35|30|25|20|15|10|5))']);
+        equal('{-9..9..3}', ['(0|3|6|9|-(9|6|3))']);
+        equal('{10..1..-2}', ['(10|8|6|4|2)']);
+        equal('{100..0..-5}', ['(100|95|90|85|80|75|70|65|60|55|50|45|40|35|30|25|20|15|10|5|0)']);
       });
     });
 
     describe('steps > alphabetical ranges', function() {
       it('should expand alpha ranges with steps', function() {
-        match('{a..e..2}', ['(a|c|e)']);
-        match('{E..A..2}', ['(E|C|A)']);
-        match('{a..z}', ['([a-z])']);
-        match('{a..z..2}', ['(a|c|e|g|i|k|m|o|q|s|u|w|y)']);
-        match('{z..a..-2}', ['(z|x|v|t|r|p|n|l|j|h|f|d|b)']);
+        equal('{a..e..2}', ['(a|c|e)']);
+        equal('{E..A..2}', ['(E|C|A)']);
+        equal('{a..z}', ['([a-z])']);
+        equal('{a..z..2}', ['(a|c|e|g|i|k|m|o|q|s|u|w|y)']);
+        equal('{z..a..-2}', ['(z|x|v|t|r|p|n|l|j|h|f|d|b)']);
       });
 
       it('should expand alpha ranges with negative steps', function() {
-        match('{z..a..-2}', ['(z|x|v|t|r|p|n|l|j|h|f|d|b)']);
+        equal('{z..a..-2}', ['(z|x|v|t|r|p|n|l|j|h|f|d|b)']);
       });
     });
 
     describe('padding', function() {
+      function isMatch(str, pattern) {
+        return braces.makeRe(pattern).test(str);
+      }
+
+      it('should handled padded ranges', function() {
+        // 1..5
+        assert(!isMatch('1', '{001..005}'));
+        assert(!isMatch('2', '{001..005}'));
+        assert(!isMatch('3', '{001..005}'));
+        assert(!isMatch('4', '{001..005}'));
+        assert(!isMatch('5', '{001..005}'));
+
+        assert(isMatch('001', '{001..005}'));
+        assert(isMatch('002', '{001..005}'));
+        assert(isMatch('003', '{001..005}'));
+        assert(isMatch('004', '{001..005}'));
+        assert(isMatch('005', '{001..005}'));
+
+        // 1..100
+        assert(!isMatch('01', '{001..100}'));
+        assert(!isMatch('10', '{001..100}'));
+        assert(!isMatch('99', '{001..100}'));
+        assert(isMatch('001', '{001..100}'));
+        assert(isMatch('010', '{001..100}'));
+        assert(isMatch('099', '{001..100}'));
+        assert(isMatch('100', '{001..100}'));
+
+        // -001..100
+        assert(!isMatch('01', '{-0100..100}'));
+        assert(!isMatch('10', '{-0100..100}'));
+        assert(!isMatch('99', '{-0100..100}'));
+        assert(isMatch('-01', '{-0100..100}'));
+        assert(isMatch('-010', '{-0100..100}'));
+        assert(isMatch('-100', '{-0100..100}'));
+        assert(isMatch('-099', '{-0100..100}'));
+        assert(isMatch('100', '{-0100..100}'));
+        assert(isMatch('001', '{-0100..100}'));
+        assert(isMatch('010', '{-0100..100}'));
+        assert(isMatch('099', '{-0100..100}'));
+        assert(isMatch('100', '{-0100..100}'));
+
+        assert(!isMatch('100', '{-001..-100}'));
+        assert(!isMatch('001', '{-001..-100}'));
+        assert(!isMatch('010', '{-001..-100}'));
+        assert(!isMatch('099', '{-001..-100}'));
+        assert(!isMatch('100', '{-001..-100}'));
+        assert(isMatch('-1', '{-001..-100}'));
+        assert(isMatch('-001', '{-001..-100}'));
+        assert(isMatch('-01', '{-001..-100}'));
+        assert(isMatch('-010', '{-001..-100}'));
+        assert(isMatch('-100', '{-001..-100}'));
+        assert(isMatch('-099', '{-001..-100}'));
+      });
+
       it('unwanted zero-padding -- fixed post-bash-4.0', function() {
-        match('{10..0..2}', ['(10|8|6|4|2|0)']);
-        match('{10..0..-2}', ['(10|8|6|4|2|0)']);
-        match('{-50..-0..5}', ['(0|-(50|45|40|35|30|25|20|15|10|5))']);
+        equal('{10..0..2}', ['(10|8|6|4|2|0)']);
+        equal('{10..0..-2}', ['(10|8|6|4|2|0)']);
+        equal('{-50..-0..5}', ['(0|-(50|45|40|35|30|25|20|15|10|5))']);
       });
     });
   });
 
   describe('integration', function() {
     it('should work with dots in file paths', function() {
-      match('../{1..3}/../foo', ['../([1-3])/../foo']);
-      match('../{2..10..2}/../foo', ['../(2|4|6|8|10)/../foo']);
-      match('../{1..3}/../{a,b,c}/foo', ['../([1-3])/../(a|b|c)/foo']);
-      match('./{a..z..3}/', ['./(a|d|g|j|m|p|s|v|y)/']);
-      match('./{"x,y"}/{a..z..3}/', ['./{x,y}/(a|d|g|j|m|p|s|v|y)/']);
+      equal('../{1..3}/../foo', ['../([1-3])/../foo']);
+      equal('../{2..10..2}/../foo', ['../(2|4|6|8|10)/../foo']);
+      equal('../{1..3}/../{a,b,c}/foo', ['../([1-3])/../(a|b|c)/foo']);
+      equal('./{a..z..3}/', ['./(a|d|g|j|m|p|s|v|y)/']);
+      equal('./{"x,y"}/{a..z..3}/', ['./{x,y}/(a|d|g|j|m|p|s|v|y)/']);
     });
 
     it('should expand a complex combination of ranges and sets:', function() {
-      match('a/{x,y}/{1..5}c{d,e}f.{md,txt}', ['a/(x|y)/([1-5])c(d|e)f.(md|txt)']);
+      equal('a/{x,y}/{1..5}c{d,e}f.{md,txt}', ['a/(x|y)/([1-5])c(d|e)f.(md|txt)']);
     });
 
     it('should expand complex sets and ranges in `bash` mode:', function() {
-      match('a/{x,{1..5},y}/c{d}e', ['a/(x|([1-5])|y)/c{d}e']);
+      equal('a/{x,{1..5},y}/c{d}e', ['a/(x|([1-5])|y)/c{d}e']);
     });
   });
 });
diff --git a/test/options.js b/test/options.js
index e790ce0..b472b9f 100644
--- a/test/options.js
+++ b/test/options.js
@@ -11,63 +11,88 @@ require('mocha');
 var assert = require('assert');
 var braces = require('..');
 
-function match(pattern, expected, options) {
+function equal(pattern, expected, options) {
   assert.deepEqual(braces(pattern, options).sort(), expected.sort());
 }
 
 describe('options', function() {
   describe('options.expand', function() {
     it('should expand braces when `options.expand` is true', function() {
-      match('a/{b,c}/d', ['a/b/d', 'a/c/d'], {expand: true});
+      equal('a/{b,c}/d', ['a/b/d', 'a/c/d'], {expand: true});
     });
   });
 
-  describe('options.unescape', function() {
-    it('should remove backslashes from escaped brace characters', function() {
-      match('{a,b\\}c,d}', ['(a|b}c|d)']);
-      match('\\{a,b,c,d,e}', ['{a,b,c,d,e}']);
-      match('a/{z,\\{a,b,c,d,e}/d', ['a/(z|{a|b|c|d|e)/d']);
-      match('a/\\{b,c}/{d,e}/f', ['a/{b,c}/(d|e)/f']);
-      match('./\\{x,y}/{a..z..3}/', ['./{x,y}/(a|d|g|j|m|p|s|v|y)/']);
+  describe('options.cache', function() {
+    it('should disable caching', function() {
+      braces('a/{b,c}/d');
+      assert(braces.cache.hasOwnProperty('a/{b,c}/d'));
+      braces('a/{b,c}/d');
+      assert(braces.cache.hasOwnProperty('a/{b,c}/d'));
+      braces('a/{b,c}/d');
+      assert(braces.cache.hasOwnProperty('a/{b,c}/d'));
+      braces('a/{b,c}/d', {cache: false});
+      braces('a/{b,c}/d', {cache: false});
+      braces('a/{b,c}/d', {cache: false});
+      assert.deepEqual(braces.cache, {});
     });
+  });
 
-    it('should not remove backslashes when `options.unescape` is false', function() {
-      match('{a,b\\}c,d}', ['(a|b\\}c|d)'], {unescape: false});
-      match('\\{a,b,c,d,e}', ['\\{a,b,c,d,e}'], {unescape: false});
-      match('a/{z,\\{a,b,c,d,e}/d', ['a/(z|\\{a|b|c|d|e)/d'], {unescape: false});
-      match('a/\\{b,c}/{d,e}/f', ['a/\\{b,c}/(d|e)/f'], {unescape: false});
-      match('./\\{x,y}/{a..z..3}/', ['./\\{x,y}/(a|d|g|j|m|p|s|v|y)/'], {unescape: false});
+  describe('options.noempty', function() {
+    it('should not remove empty values by default', function() {
+      equal('{,b{,a}}', ['', 'b', 'ba'], {expand: true});
+    });
+
+    it('should remove empty values when `options.noempty` is false', function() {
+      equal('{,b{,a}}', ['b', 'ba'], {expand: true, noempty: true});
     });
   });
 
   describe('options.nodupes', function() {
     it('should not remove duplicates by default', function() {
-      match('a/{b,b,b}/c', ['a/b/c', 'a/b/c', 'a/b/c'], {expand: true});
+      equal('a/{b,b,b}/c', ['a/b/c', 'a/b/c', 'a/b/c'], {expand: true});
     });
 
     it('should remove duplicates when `options.nodupes` is true', function() {
-      match('a/{b,b,b}/c', ['a/b/c'], {expand: true, nodupes: true});
+      equal('a/{b,b,b}/c', ['a/b/c'], {expand: true, nodupes: true});
     });
   });
 
   describe('options.optimize', function() {
     it('should optimize braces when `options.optimize` is true', function() {
-      match('a/{b,c}/d', ['a/(b|c)/d'], {optimize: true});
+      equal('a/{b,c}/d', ['a/(b|c)/d'], {optimize: true});
     });
   });
 
   describe('options.quantifiers:', function() {
     it('should not expand regex quantifiers when `options.quantifiers` is true', function() {
-      match('a{2}c', ['a{2}c']);
-      match('a{2}c', ['a{2}c'], {quantifiers: true});
-      match('a{2,}c', ['a{2,}c'], {quantifiers: true});
-      match('a{,2}c', ['a{,2}c'], {quantifiers: true});
-      match('a{2,3}c', ['a{2,3}c'], {quantifiers: true});
+      equal('a{2}c', ['a{2}c']);
+      equal('a{2}c', ['a{2}c'], {quantifiers: true});
+      equal('a{2,}c', ['a{2,}c'], {quantifiers: true});
+      equal('a{,2}c', ['a{,2}c'], {quantifiers: true});
+      equal('a{2,3}c', ['a{2,3}c'], {quantifiers: true});
     });
 
     it('should expand non-quantifiers when `options.quantifiers` is true', function() {
-      match('a{2}c/{x,y}/z', ['a{2}c/(x|y)/z'], {quantifiers: true});
-      match('a{2}c/{x,y}/z', ['a{2}c/x/z', 'a{2}c/y/z'], {quantifiers: true, expand: true});
+      equal('a{2}c/{x,y}/z', ['a{2}c/(x|y)/z'], {quantifiers: true});
+      equal('a{2}c/{x,y}/z', ['a{2}c/x/z', 'a{2}c/y/z'], {quantifiers: true, expand: true});
+    });
+  });
+
+  describe('options.unescape', function() {
+    it('should remove backslashes from escaped brace characters', function() {
+      equal('{a,b\\}c,d}', ['(a|b}c|d)']);
+      equal('\\{a,b,c,d,e}', ['{a,b,c,d,e}']);
+      equal('a/{z,\\{a,b,c,d,e}/d', ['a/(z|{a|b|c|d|e)/d']);
+      equal('a/\\{b,c}/{d,e}/f', ['a/{b,c}/(d|e)/f']);
+      equal('./\\{x,y}/{a..z..3}/', ['./{x,y}/(a|d|g|j|m|p|s|v|y)/']);
+    });
+
+    it('should not remove backslashes when `options.unescape` is false', function() {
+      equal('{a,b\\}c,d}', ['(a|b\\}c|d)'], {unescape: false});
+      equal('\\{a,b,c,d,e}', ['\\{a,b,c,d,e}'], {unescape: false});
+      equal('a/{z,\\{a,b,c,d,e}/d', ['a/(z|\\{a|b|c|d|e)/d'], {unescape: false});
+      equal('a/\\{b,c}/{d,e}/f', ['a/\\{b,c}/(d|e)/f'], {unescape: false});
+      equal('./\\{x,y}/{a..z..3}/', ['./\\{x,y}/(a|d|g|j|m|p|s|v|y)/'], {unescape: false});
     });
   });
 });
diff --git a/test/regression-1.8.5.js b/test/regression-1.8.5.js
index a0d4e5f..00f652a 100644
--- a/test/regression-1.8.5.js
+++ b/test/regression-1.8.5.js
@@ -3,7 +3,7 @@
 var assert = require('assert');
 var braces = require('..');
 
-function match(pattern, expected, options) {
+function equal(pattern, expected, options) {
   options = options || {};
   var fn = braces;
   if (options.optimize !== true) {
@@ -15,449 +15,449 @@ function match(pattern, expected, options) {
 
 describe('braces tests from 1.8.5', function() {
   it('braces', function() {
-    match('ff{c,b,a}', ['ffc', 'ffb', 'ffa']);
-    match('f{d,e,f}g', ['fdg', 'feg', 'ffg']);
-    match('{l,n,m}xyz', ['lxyz', 'nxyz', 'mxyz']);
-    match('{abc\\,d,ef}', ['abc,d', 'ef']);
-    match('{abc}', ['{abc}']);
+    equal('ff{c,b,a}', ['ffc', 'ffb', 'ffa']);
+    equal('f{d,e,f}g', ['fdg', 'feg', 'ffg']);
+    equal('{l,n,m}xyz', ['lxyz', 'nxyz', 'mxyz']);
+    equal('{abc\\,d,ef}', ['abc,d', 'ef']);
+    equal('{abc}', ['{abc}']);
 
-    match('\\{a,b,c,d,e}', ['{a,b,c,d,e}']);
-    match('{x,y,\\{a,b,c}}', ['x}', 'y}', '{a}', 'b}', 'c}']);
-    match('{x\\,y,\\{abc\\},trie}', ['x,y', '{abc}', 'trie']);
+    equal('\\{a,b,c,d,e}', ['{a,b,c,d,e}']);
+    equal('{x,y,\\{a,b,c}}', ['x}', 'y}', '{a}', 'b}', 'c}']);
+    equal('{x\\,y,\\{abc\\},trie}', ['x,y', '{abc}', 'trie']);
 
-    match('/usr/{ucb/{ex,edit},lib/{ex,how_ex}}', ['/usr/ucb/ex', '/usr/lib/ex', '/usr/ucb/edit', '/usr/lib/how_ex']);
+    equal('/usr/{ucb/{ex,edit},lib/{ex,how_ex}}', ['/usr/ucb/ex', '/usr/lib/ex', '/usr/ucb/edit', '/usr/lib/how_ex']);
 
-    match('{}', ['{}']);
-    match('{ }', ['{ }']);
-    match('}', ['}']);
-    match('{', ['{']);
-    match('abcd{efgh', ['abcd{efgh']);
+    equal('{}', ['{}']);
+    equal('{ }', ['{ }']);
+    equal('}', ['}']);
+    equal('{', ['{']);
+    equal('abcd{efgh', ['abcd{efgh']);
 
-    match('foo {1,2} bar', ['foo 1 bar', 'foo 2 bar']);
+    equal('foo {1,2} bar', ['foo 1 bar', 'foo 2 bar']);
   });
 
   it('new sequence brace operators', function() {
-    match('{1..10}', ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']);
-    match('{0..10,braces}', ['0..10', 'braces']);
-    match('{braces,{0..10}}', ['braces', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10']);
-    match('{{0..10},braces}', ['0', 'braces', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10']);
-    match('x{{0..10},braces}y', ['x0y', 'xbracesy', 'x1y', 'x2y', 'x3y', 'x4y', 'x5y', 'x6y', 'x7y', 'x8y', 'x9y', 'x10y']);
+    equal('{1..10}', ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']);
+    equal('{0..10,braces}', ['0..10', 'braces']);
+    equal('{braces,{0..10}}', ['braces', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10']);
+    equal('{{0..10},braces}', ['0', 'braces', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10']);
+    equal('x{{0..10},braces}y', ['x0y', 'xbracesy', 'x1y', 'x2y', 'x3y', 'x4y', 'x5y', 'x6y', 'x7y', 'x8y', 'x9y', 'x10y']);
   });
 
   it('ranges', function() {
-    match('{3..3}', ['3']);
-    match('x{3..3}y', ['x3y']);
-    match('{10..1}', ['10', '9', '8', '7', '6', '5', '4', '3', '2', '1']);
-    match('{10..1}y', ['10y', '9y', '8y', '7y', '6y', '5y', '4y', '3y', '2y', '1y']);
-    match('x{10..1}y', ['x10y', 'x9y', 'x8y', 'x7y', 'x6y', 'x5y', 'x4y', 'x3y', 'x2y', 'x1y']);
-    match('{a..f}', ['a', 'b', 'c', 'd', 'e', 'f']);
-    match('{f..a}', ['f', 'e', 'd', 'c', 'b', 'a']);
-
-    match('{a..A}', ['a', '`', '_', '^', ']', '\\', '[', 'Z', 'Y', 'X', 'W', 'V', 'U', 'T', 'S', 'R', 'Q', 'P', 'O', 'N', 'M', 'L', 'K', 'J', 'I', 'H', 'G', 'F', 'E', 'D', 'C', 'B', 'A']);
-    match('{A..a}', ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a']);
-
-    match('{f..f}', ['f']);
-    match('0{1..9} {10..20}', ['01 10', '01 11', '01 12', '01 13', '01 14', '01 15', '01 16', '01 17', '01 18', '01 19', '01 20', '02 10', '02 11', '02 12', '02 13', '02 14', '02 15', '02 16', '02 17', '02 18', '02 19', '02 20', '03 10', '03 11', '03 12', '03 13', '03 14', '03 15', '03 16', '03 17', '03 18', '03 19', '03 20', '04 10', '04 11', '04 12', '04 13', '04 14', '04 15', '04 16', '04 17', '04 18', '04 19', '04 20', '05 10', '05 11', '05 12', '05 13', '05 14', '05 15', '05 16', '0 [...]
+    equal('{3..3}', ['3']);
+    equal('x{3..3}y', ['x3y']);
+    equal('{10..1}', ['10', '9', '8', '7', '6', '5', '4', '3', '2', '1']);
+    equal('{10..1}y', ['10y', '9y', '8y', '7y', '6y', '5y', '4y', '3y', '2y', '1y']);
+    equal('x{10..1}y', ['x10y', 'x9y', 'x8y', 'x7y', 'x6y', 'x5y', 'x4y', 'x3y', 'x2y', 'x1y']);
+    equal('{a..f}', ['a', 'b', 'c', 'd', 'e', 'f']);
+    equal('{f..a}', ['f', 'e', 'd', 'c', 'b', 'a']);
+
+    equal('{a..A}', ['a', '`', '_', '^', ']', '\\', '[', 'Z', 'Y', 'X', 'W', 'V', 'U', 'T', 'S', 'R', 'Q', 'P', 'O', 'N', 'M', 'L', 'K', 'J', 'I', 'H', 'G', 'F', 'E', 'D', 'C', 'B', 'A']);
+    equal('{A..a}', ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a']);
+
+    equal('{f..f}', ['f']);
+    equal('0{1..9} {10..20}', ['01 10', '01 11', '01 12', '01 13', '01 14', '01 15', '01 16', '01 17', '01 18', '01 19', '01 20', '02 10', '02 11', '02 12', '02 13', '02 14', '02 15', '02 16', '02 17', '02 18', '02 19', '02 20', '03 10', '03 11', '03 12', '03 13', '03 14', '03 15', '03 16', '03 17', '03 18', '03 19', '03 20', '04 10', '04 11', '04 12', '04 13', '04 14', '04 15', '04 16', '04 17', '04 18', '04 19', '04 20', '05 10', '05 11', '05 12', '05 13', '05 14', '05 15', '05 16', '0 [...]
   });
 
   it('mixes are incorrectly-formed brace expansions', function() {
     // the first one is valid, but Bash fails on it
-    match('{1..f}', ['1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f']);
-    match('{f..1}', ['1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f']);
+    equal('{1..f}', ['1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f']);
+    equal('{f..1}', ['1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f']);
   });
 
   it('do negative numbers work?', function() {
-    match('{-1..-10}', ['-1', '-2', '-3', '-4', '-5', '-6', '-7', '-8', '-9', '-10']);
-    match('{-20..0}', ['-20', '-19', '-18', '-17', '-16', '-15', '-14', '-13', '-12', '-11', '-10', '-9', '-8', '-7', '-6', '-5', '-4', '-3', '-2', '-1', '0']);
+    equal('{-1..-10}', ['-1', '-2', '-3', '-4', '-5', '-6', '-7', '-8', '-9', '-10']);
+    equal('{-20..0}', ['-20', '-19', '-18', '-17', '-16', '-15', '-14', '-13', '-12', '-11', '-10', '-9', '-8', '-7', '-6', '-5', '-4', '-3', '-2', '-1', '0']);
   });
 
   it('weirdly-formed brace expansions -- fixed in post-bash-3.1', function() {
-    match('{-1..-10}', ['-1', '-2', '-3', '-4', '-5', '-6', '-7', '-8', '-9', '-10']);
-    match('{-20..0}', ['-20', '-19', '-18', '-17', '-16', '-15', '-14', '-13', '-12', '-11', '-10', '-9', '-8', '-7', '-6', '-5', '-4', '-3', '-2', '-1', '0']);
-    match('a-{b{d,e}}-c', ['a-{bd}-c', 'a-{be}-c']);
+    equal('{-1..-10}', ['-1', '-2', '-3', '-4', '-5', '-6', '-7', '-8', '-9', '-10']);
+    equal('{-20..0}', ['-20', '-19', '-18', '-17', '-16', '-15', '-14', '-13', '-12', '-11', '-10', '-9', '-8', '-7', '-6', '-5', '-4', '-3', '-2', '-1', '0']);
+    equal('a-{b{d,e}}-c', ['a-{bd}-c', 'a-{be}-c']);
 
-    match('a-{bdef-{g,i}-c', ['a-{bdef-g-c', 'a-{bdef-i-c']);
+    equal('a-{bdef-{g,i}-c', ['a-{bdef-g-c', 'a-{bdef-i-c']);
 
-    match('{"klklkl"}{1,2,3}', ['{klklkl}1', '{klklkl}2', '{klklkl}3']);
-    match('{"x,x"}', ['{x,x}']);
+    equal('{"klklkl"}{1,2,3}', ['{klklkl}1', '{klklkl}2', '{klklkl}3']);
+    equal('{"x,x"}', ['{x,x}']);
   });
 
   it('numerical ranges with steps', function() {
-    match('{1..10..2}', ['1', '3', '5', '7', '9']);
-    match('{-1..-10..2}', ['-1', '-3', '-5', '-7', '-9']);
-    match('{-1..-10..-2}', ['-1', '-3', '-5', '-7', '-9']);
+    equal('{1..10..2}', ['1', '3', '5', '7', '9']);
+    equal('{-1..-10..2}', ['-1', '-3', '-5', '-7', '-9']);
+    equal('{-1..-10..-2}', ['-1', '-3', '-5', '-7', '-9']);
 
-    match('{10..1..-2}', ['10', '8', '6', '4', '2']);
-    match('{10..1..2}', ['10', '8', '6', '4', '2']);
+    equal('{10..1..-2}', ['10', '8', '6', '4', '2']);
+    equal('{10..1..2}', ['10', '8', '6', '4', '2']);
 
-    match('{1..20..2}', ['1', '3', '5', '7', '9', '11', '13', '15', '17', '19']);
-    match('{1..20..20}', ['1']);
+    equal('{1..20..2}', ['1', '3', '5', '7', '9', '11', '13', '15', '17', '19']);
+    equal('{1..20..20}', ['1']);
 
-    match('{100..0..5}', ['100', '95', '90', '85', '80', '75', '70', '65', '60', '55', '50', '45', '40', '35', '30', '25', '20', '15', '10', '5', '0']);
-    match('{100..0..-5}', ['100', '95', '90', '85', '80', '75', '70', '65', '60', '55', '50', '45', '40', '35', '30', '25', '20', '15', '10', '5', '0']);
+    equal('{100..0..5}', ['100', '95', '90', '85', '80', '75', '70', '65', '60', '55', '50', '45', '40', '35', '30', '25', '20', '15', '10', '5', '0']);
+    equal('{100..0..-5}', ['100', '95', '90', '85', '80', '75', '70', '65', '60', '55', '50', '45', '40', '35', '30', '25', '20', '15', '10', '5', '0']);
   });
 
   it('alpha ranges with steps', function() {
-    match('{a..z}', ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']);
-    match('{a..z..2}', ['a', 'c', 'e', 'g', 'i', 'k', 'm', 'o', 'q', 's', 'u', 'w', 'y']);
-    match('{z..a..-2}', ['z', 'x', 'v', 't', 'r', 'p', 'n', 'l', 'j', 'h', 'f', 'd', 'b']);
+    equal('{a..z}', ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']);
+    equal('{a..z..2}', ['a', 'c', 'e', 'g', 'i', 'k', 'm', 'o', 'q', 's', 'u', 'w', 'y']);
+    equal('{z..a..-2}', ['z', 'x', 'v', 't', 'r', 'p', 'n', 'l', 'j', 'h', 'f', 'd', 'b']);
   });
 
   it('make sure brace expansion handles ints > 2**31 - 1 using intmax_t', function() {
-    match('{2147483645..2147483649}', ['2147483645', '2147483646', '2147483647', '2147483648', '2147483649']);
+    equal('{2147483645..2147483649}', ['2147483645', '2147483646', '2147483647', '2147483648', '2147483649']);
   });
 
   it('unwanted zero-padding -- fixed post-bash-4.0', function() {
-    match('{10..0..2}', ['10', '8', '6', '4', '2', '0']);
-    match('{10..0..-2}', ['10', '8', '6', '4', '2', '0']);
-    match('{-50..-0..5}', ['-50', '-45', '-40', '-35', '-30', '-25', '-20', '-15', '-10', '-5', '0']);
+    equal('{10..0..2}', ['10', '8', '6', '4', '2', '0']);
+    equal('{10..0..-2}', ['10', '8', '6', '4', '2', '0']);
+    equal('{-50..-0..5}', ['-50', '-45', '-40', '-35', '-30', '-25', '-20', '-15', '-10', '-5', '0']);
   });
 
   it('bad', function() {
-    match('{1..10.f}', ['{1..10.f}']);
-    match('{1..ff}', ['{1..ff}']);
-    match('{1..10..ff}', ['{1..10..ff}']);
-    match('{1.20..2}', ['{1.20..2}']);
-    match('{1..20..f2}', ['{1..20..f2}']);
-    match('{1..20..2f}', ['{1..20..2f}']);
-    match('{1..2f..2}', ['{1..2f..2}']);
-    match('{1..ff..2}', ['{1..ff..2}']);
-    match('{1..ff}', ['{1..ff}']);
-    match('{1..0f}', ['{1..0f}']);
-    match('{1..10f}', ['{1..10f}']);
-    match('{1..10.f}', ['{1..10.f}']);
-    match('{1..10.f}', ['{1..10.f}']);
+    equal('{1..10.f}', ['{1..10.f}']);
+    equal('{1..ff}', ['{1..ff}']);
+    equal('{1..10..ff}', ['{1..10..ff}']);
+    equal('{1.20..2}', ['{1.20..2}']);
+    equal('{1..20..f2}', ['{1..20..f2}']);
+    equal('{1..20..2f}', ['{1..20..2f}']);
+    equal('{1..2f..2}', ['{1..2f..2}']);
+    equal('{1..ff..2}', ['{1..ff..2}']);
+    equal('{1..ff}', ['{1..ff}']);
+    equal('{1..0f}', ['{1..0f}']);
+    equal('{1..10f}', ['{1..10f}']);
+    equal('{1..10.f}', ['{1..10.f}']);
+    equal('{1..10.f}', ['{1..10.f}']);
   });
 });
 
 describe('bash tests', function() {
   describe('brace expansion', function() {
     it('should return an empty array when no braces are found', function() {
-      match('', ['']);
+      equal('', ['']);
     });
 
     it('should expand emty sets', function() {
-      match('a{,}', ['a', 'a']);
-      match('{,}b', ['b', 'b']);
-      match('a{,}b', ['ab', 'ab']);
-      match('a{,}', ['a', 'a']);
-      match('a{,}{,}', ['a', 'a', 'a', 'a']);
-      match('a{,}{,}{,}', ['a', 'a', 'a', 'a', 'a', 'a', 'a', 'a']);
-      match('{a,b{,}{,}{,}}', ['a', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b']);
-      match('a{,}/{c,d}/e', ['a/c/e', 'a/c/e', 'a/d/e', 'a/d/e']);
-      match('{a,b{,}{,}{,},c}d', ['ad', 'bd', 'bd', 'bd', 'bd', 'bd', 'bd', 'bd', 'bd', 'cd']);
+      equal('a{,}', ['a', 'a']);
+      equal('{,}b', ['b', 'b']);
+      equal('a{,}b', ['ab', 'ab']);
+      equal('a{,}', ['a', 'a']);
+      equal('a{,}{,}', ['a', 'a', 'a', 'a']);
+      equal('a{,}{,}{,}', ['a', 'a', 'a', 'a', 'a', 'a', 'a', 'a']);
+      equal('{a,b{,}{,}{,}}', ['a', 'b', 'b', 'b', 'b', 'b', 'b', 'b', 'b']);
+      equal('a{,}/{c,d}/e', ['a/c/e', 'a/c/e', 'a/d/e', 'a/d/e']);
+      equal('{a,b{,}{,}{,},c}d', ['ad', 'bd', 'bd', 'bd', 'bd', 'bd', 'bd', 'bd', 'bd', 'cd']);
     });
 
     it('should eliminate dupes in repeated strings', function() {
-      match('a{,}', ['a'], {nodupes: true});
-      match('a{,}{,}', ['a'], {nodupes: true});
-      match('a{,}{,}{,}', ['a'], {nodupes: true});
-      match('{a,b{,}{,}{,}}', ['a', 'b'], {nodupes: true});
-      match('{a,b{,}{,}{,},c}d', ['ad', 'bd', 'cd'], {nodupes: true});
-      match('{a,b{,}{,}{,},c}d', ['ad', 'bd', 'cd'], {nodupes: true});
+      equal('a{,}', ['a'], {nodupes: true});
+      equal('a{,}{,}', ['a'], {nodupes: true});
+      equal('a{,}{,}{,}', ['a'], {nodupes: true});
+      equal('{a,b{,}{,}{,}}', ['a', 'b'], {nodupes: true});
+      equal('{a,b{,}{,}{,},c}d', ['ad', 'bd', 'cd'], {nodupes: true});
+      equal('{a,b{,}{,}{,},c}d', ['ad', 'bd', 'cd'], {nodupes: true});
     });
 
     it('should work with no braces', function() {
-      match('abc', ['abc']);
+      equal('abc', ['abc']);
     });
 
     it('should work with no commas', function() {
-      match('a{b}c', ['a{b}c']);
+      equal('a{b}c', ['a{b}c']);
     });
 
     it('should work with no commas in `bash` mode', function() {
-      match('a{b}c', ['a{b}c']);
+      equal('a{b}c', ['a{b}c']);
     });
 
     it('should handle spaces', function() {
-      match('a{ ,c{d, },h}x', ['a x', 'acdx', 'ac x', 'ahx']);
-      match('a{ ,c{d, },h} ', [ 'a  ', 'acd ', 'ac  ', 'ah ' ]);
+      equal('a{ ,c{d, },h}x', ['a x', 'acdx', 'ac x', 'ahx']);
+      equal('a{ ,c{d, },h} ', [ 'a  ', 'acd ', 'ac  ', 'ah ' ]);
 
-      // see https://github.com/jonschlinkert/micromatch/issues/66
-      match('/Users/tobiasreich/Sites/aaa/bbb/ccc 2016/src/**/[^_]*.{html,ejs}', [
+      // see https://github.com/jonschlinkert/microequal/issues/66
+      equal('/Users/tobiasreich/Sites/aaa/bbb/ccc 2016/src/**/[^_]*.{html,ejs}', [
         '/Users/tobiasreich/Sites/aaa/bbb/ccc 2016/src/**/[^_]*.html',
         '/Users/tobiasreich/Sites/aaa/bbb/ccc 2016/src/**/[^_]*.ejs'
       ]);
     });
 
     it('should handle empty braces', function() {
-      match('{ }', ['{ }']);
-      match('{}', ['{}']);
-      match('}', ['}']);
-      match('{', ['{']);
-      match('{,}', []);
+      equal('{ }', ['{ }']);
+      equal('{}', ['{}']);
+      equal('}', ['}']);
+      equal('{', ['{']);
+      equal('{,}', []);
     });
 
     it('should handle imbalanced braces', function() {
-      match('a-{bdef-{g,i}-c', ['a-{bdef-g-c', 'a-{bdef-i-c']);
-      match('abc{', ['abc{']);
-      match('{abc{', ['{abc{']);
-      match('{abc', ['{abc']);
-      match('}abc', ['}abc']);
-      match('ab{c', ['ab{c']);
-      match('ab{c', ['ab{c']);
-      match('{{a,b}', ['{a', '{b']);
-      match('{a,b}}', ['a}', 'b}']);
-      match('abcd{efgh', ['abcd{efgh']);
-      match('a{b{c{d,e}f}g}h', ['a{b{cdf}g}h', 'a{b{cef}g}h']);
-      match('f{x,y{{g,z}}h}', ['fx', 'fy{g}h', 'fy{z}h']);
-      match('z{a,b},c}d', ['za,c}d', 'zb,c}d']);
-      match('a{b{c{d,e}f{x,y{{g}h', ['a{b{cdf{x,y{{g}h', 'a{b{cef{x,y{{g}h']);
-      match('f{x,y{{g}h', ['f{x,y{{g}h']);
-      match('f{x,y{{g}}h', ['f{x,y{{g}}h']);
-      match('a{b{c{d,e}f{x,y{}g}h', ['a{b{cdfxh', 'a{b{cefxh', 'a{b{cdfy{}gh', 'a{b{cefy{}gh']);
-      match('f{x,y{}g}h', ['fxh', 'fy{}gh']);
-      match('z{a,b{,c}d', ['z{a,bd', 'z{a,bcd']);
+      equal('a-{bdef-{g,i}-c', ['a-{bdef-g-c', 'a-{bdef-i-c']);
+      equal('abc{', ['abc{']);
+      equal('{abc{', ['{abc{']);
+      equal('{abc', ['{abc']);
+      equal('}abc', ['}abc']);
+      equal('ab{c', ['ab{c']);
+      equal('ab{c', ['ab{c']);
+      equal('{{a,b}', ['{a', '{b']);
+      equal('{a,b}}', ['a}', 'b}']);
+      equal('abcd{efgh', ['abcd{efgh']);
+      equal('a{b{c{d,e}f}g}h', ['a{b{cdf}g}h', 'a{b{cef}g}h']);
+      equal('f{x,y{{g,z}}h}', ['fx', 'fy{g}h', 'fy{z}h']);
+      equal('z{a,b},c}d', ['za,c}d', 'zb,c}d']);
+      equal('a{b{c{d,e}f{x,y{{g}h', ['a{b{cdf{x,y{{g}h', 'a{b{cef{x,y{{g}h']);
+      equal('f{x,y{{g}h', ['f{x,y{{g}h']);
+      equal('f{x,y{{g}}h', ['f{x,y{{g}}h']);
+      equal('a{b{c{d,e}f{x,y{}g}h', ['a{b{cdfxh', 'a{b{cefxh', 'a{b{cdfy{}gh', 'a{b{cefy{}gh']);
+      equal('f{x,y{}g}h', ['fxh', 'fy{}gh']);
+      equal('z{a,b{,c}d', ['z{a,bd', 'z{a,bcd']);
     });
 
     it('should handle invalid braces in `bash mode`:', function() {
-      match('a{b{c{d,e}f}g}h', ['a{b{cdf}g}h', 'a{b{cef}g}h']);
-      match('f{x,y{{g,z}}h}', ['fx', 'fy{g}h', 'fy{z}h']);
-      match('z{a,b},c}d', ['za,c}d', 'zb,c}d']);
-      match('a{b{c{d,e}f{x,y{{g}h', ['a{b{cdf{x,y{{g}h', 'a{b{cef{x,y{{g}h']);
-      match('f{x,y{{g}h', ['f{x,y{{g}h']);
-      match('f{x,y{{g}}h', ['f{x,y{{g}}h']);
+      equal('a{b{c{d,e}f}g}h', ['a{b{cdf}g}h', 'a{b{cef}g}h']);
+      equal('f{x,y{{g,z}}h}', ['fx', 'fy{g}h', 'fy{z}h']);
+      equal('z{a,b},c}d', ['za,c}d', 'zb,c}d']);
+      equal('a{b{c{d,e}f{x,y{{g}h', ['a{b{cdf{x,y{{g}h', 'a{b{cef{x,y{{g}h']);
+      equal('f{x,y{{g}h', ['f{x,y{{g}h']);
+      equal('f{x,y{{g}}h', ['f{x,y{{g}}h']);
     });
 
     it('should return invalid braces:', function() {
-      match('{0..10,braces}', ['0..10', 'braces']);
+      equal('{0..10,braces}', ['0..10', 'braces']);
     });
 
     it('should not expand quoted strings.', function() {
-      match('{"x,x"}', ['{x,x}']);
-      match('{"klklkl"}{1,2,3}', ['{klklkl}1', '{klklkl}2', '{klklkl}3']);
+      equal('{"x,x"}', ['{x,x}']);
+      equal('{"klklkl"}{1,2,3}', ['{klklkl}1', '{klklkl}2', '{klklkl}3']);
     });
 
     it('should work with one value', function() {
-      match('a{b}c', ['a{b}c']);
-      match('a/b/c{d}e', ['a/b/c{d}e']);
+      equal('a{b}c', ['a{b}c']);
+      equal('a/b/c{d}e', ['a/b/c{d}e']);
     });
 
     it('should work with one value in `bash` mode', function() {
-      match('a{b}c', ['a{b}c']);
-      match('a/b/c{d}e', ['a/b/c{d}e']);
+      equal('a{b}c', ['a{b}c']);
+      equal('a/b/c{d}e', ['a/b/c{d}e']);
     });
 
     it('should work with nested non-sets', function() {
-      match('foo {1,2} bar', ['foo 1 bar', 'foo 2 bar']);
-      match('{a-{b,c,d}}', ['{a-b}', '{a-c}', '{a-d}']);
-      match('{a,{a-{b,c,d}}}', ['a', '{a-b}', '{a-c}', '{a-d}']);
+      equal('foo {1,2} bar', ['foo 1 bar', 'foo 2 bar']);
+      equal('{a-{b,c,d}}', ['{a-b}', '{a-c}', '{a-d}']);
+      equal('{a,{a-{b,c,d}}}', ['a', '{a-b}', '{a-c}', '{a-d}']);
     });
 
     it('should work with nested non-sets in `bash` mode', function() {
-      match('{a-{b,c,d}}', ['{a-b}', '{a-c}', '{a-d}']);
-      match('{a,{a-{b,c,d}}}', ['a', '{a-b}', '{a-c}', '{a-d}']);
+      equal('{a-{b,c,d}}', ['{a-b}', '{a-c}', '{a-d}']);
+      equal('{a,{a-{b,c,d}}}', ['a', '{a-b}', '{a-c}', '{a-d}']);
     });
 
     it('should not expand dots with leading slashes (escaped or paths).', function() {
-      match('a{b,c/*/../d}e', ['abe', 'ac/*/../de']);
-      match('a{b,b,c/../b}d', ['abd', 'abd', 'ac/../bd']);
+      equal('a{b,c/*/../d}e', ['abe', 'ac/*/../de']);
+      equal('a{b,b,c/../b}d', ['abd', 'abd', 'ac/../bd']);
     });
 
     it('should work with commas.', function() {
-      match('a{b,}c', ['abc', 'ac']);
-      match('a{,b}c', ['ac', 'abc']);
+      equal('a{b,}c', ['abc', 'ac']);
+      equal('a{,b}c', ['ac', 'abc']);
     });
 
     it('should expand sets', function() {
-      match('a/{x,y}/cde', ['a/x/cde', 'a/y/cde']);
-      match('a/b/c/{x,y}', ['a/b/c/x', 'a/b/c/y']);
-      match('ff{c,b,a}', ['ffc', 'ffb', 'ffa']);
-      match('f{d,e,f}g', ['fdg', 'feg', 'ffg']);
-      match('{l,n,m}xyz', ['lxyz', 'nxyz', 'mxyz']);
-      match('{x,y,{abc},trie}', ['x', 'y', '{abc}', 'trie']);
+      equal('a/{x,y}/cde', ['a/x/cde', 'a/y/cde']);
+      equal('a/b/c/{x,y}', ['a/b/c/x', 'a/b/c/y']);
+      equal('ff{c,b,a}', ['ffc', 'ffb', 'ffa']);
+      equal('f{d,e,f}g', ['fdg', 'feg', 'ffg']);
+      equal('{l,n,m}xyz', ['lxyz', 'nxyz', 'mxyz']);
+      equal('{x,y,{abc},trie}', ['x', 'y', '{abc}', 'trie']);
     });
 
     it('should expand multiple sets', function() {
-      match('a/{a,b}/{c,d}/e', ['a/a/c/e', 'a/b/c/e', 'a/a/d/e', 'a/b/d/e']);
-      match('a{b,c}d{e,f}g', ['abdeg', 'acdeg', 'abdfg', 'acdfg']);
-      match('a/{x,y}/c{d,e}f.{md,txt}', ['a/x/cdf.md', 'a/y/cdf.md', 'a/x/cef.md', 'a/y/cef.md', 'a/x/cdf.txt', 'a/y/cdf.txt', 'a/x/cef.txt', 'a/y/cef.txt']);
+      equal('a/{a,b}/{c,d}/e', ['a/a/c/e', 'a/b/c/e', 'a/a/d/e', 'a/b/d/e']);
+      equal('a{b,c}d{e,f}g', ['abdeg', 'acdeg', 'abdfg', 'acdfg']);
+      equal('a/{x,y}/c{d,e}f.{md,txt}', ['a/x/cdf.md', 'a/y/cdf.md', 'a/x/cef.md', 'a/y/cef.md', 'a/x/cdf.txt', 'a/y/cdf.txt', 'a/x/cef.txt', 'a/y/cef.txt']);
     });
 
     it('should expand nested sets', function() {
-      match('a/{b,c,{d,e}}/g', ['a/b/g', 'a/c/g', 'a/d/g', 'a/e/g']);
-      match('a/{a,b}/{c,d}/e', ['a/a/c/e', 'a/a/d/e', 'a/b/c/e', 'a/b/d/e']);
-      match('{a,b}{{a,b},a,b}', ['aa', 'aa', 'ab', 'ab', 'ba', 'ba', 'bb', 'bb']);
-      match('/usr/{ucb/{ex,edit},lib/{ex,how_ex}}', ['/usr/ucb/ex', '/usr/lib/ex', '/usr/ucb/edit', '/usr/lib/how_ex']);
-      match('a{b,c{d,e}f}g', ['abg', 'acdfg', 'acefg']);
-      match('a{{x,y},z}b', ['axb', 'azb', 'ayb']);
-      match('f{x,y{g,z}}h', ['fxh', 'fygh', 'fyzh']);
-      match('a{b,c{d,e},h}x/z', ['abx/z', 'acdx/z', 'ahx/z', 'acex/z']);
-      match('a{b,c{d,e},h}x{y,z}', ['abxy', 'acdxy', 'ahxy', 'acexy', 'abxz', 'acdxz', 'ahxz', 'acexz']);
-      match('a{b,c{d,e},{f,g}h}x{y,z}', ['abxy', 'acdxy', 'afhxy', 'acexy', 'aghxy', 'abxz', 'acdxz', 'afhxz', 'acexz', 'aghxz']);
-      match('a-{b{d,e}}-c', ['a-{bd}-c', 'a-{be}-c']);
+      equal('a/{b,c,{d,e}}/g', ['a/b/g', 'a/c/g', 'a/d/g', 'a/e/g']);
+      equal('a/{a,b}/{c,d}/e', ['a/a/c/e', 'a/a/d/e', 'a/b/c/e', 'a/b/d/e']);
+      equal('{a,b}{{a,b},a,b}', ['aa', 'aa', 'ab', 'ab', 'ba', 'ba', 'bb', 'bb']);
+      equal('/usr/{ucb/{ex,edit},lib/{ex,how_ex}}', ['/usr/ucb/ex', '/usr/lib/ex', '/usr/ucb/edit', '/usr/lib/how_ex']);
+      equal('a{b,c{d,e}f}g', ['abg', 'acdfg', 'acefg']);
+      equal('a{{x,y},z}b', ['axb', 'azb', 'ayb']);
+      equal('f{x,y{g,z}}h', ['fxh', 'fygh', 'fyzh']);
+      equal('a{b,c{d,e},h}x/z', ['abx/z', 'acdx/z', 'ahx/z', 'acex/z']);
+      equal('a{b,c{d,e},h}x{y,z}', ['abxy', 'acdxy', 'ahxy', 'acexy', 'abxz', 'acdxz', 'ahxz', 'acexz']);
+      equal('a{b,c{d,e},{f,g}h}x{y,z}', ['abxy', 'acdxy', 'afhxy', 'acexy', 'aghxy', 'abxz', 'acdxz', 'afhxz', 'acexz', 'aghxz']);
+      equal('a-{b{d,e}}-c', ['a-{bd}-c', 'a-{be}-c']);
     });
 
     it('should expand with globs.', function() {
-      match('a/b/{d,e}/*.js', ['a/b/d/*.js', 'a/b/e/*.js']);
-      match('a/**/c/{d,e}/f*.js', ['a/**/c/d/f*.js', 'a/**/c/e/f*.js']);
-      match('a/**/c/{d,e}/f*.{md,txt}', ['a/**/c/d/f*.md', 'a/**/c/e/f*.md', 'a/**/c/d/f*.txt', 'a/**/c/e/f*.txt']);
+      equal('a/b/{d,e}/*.js', ['a/b/d/*.js', 'a/b/e/*.js']);
+      equal('a/**/c/{d,e}/f*.js', ['a/**/c/d/f*.js', 'a/**/c/e/f*.js']);
+      equal('a/**/c/{d,e}/f*.{md,txt}', ['a/**/c/d/f*.md', 'a/**/c/e/f*.md', 'a/**/c/d/f*.txt', 'a/**/c/e/f*.txt']);
     });
 
     it('should expand with extglobs.', function() {
-      match('a/b/{d,e,[1-5]}/*.js', ['a/b/d/*.js', 'a/b/e/*.js', 'a/b/[1-5]/*.js']);
+      equal('a/b/{d,e,[1-5]}/*.js', ['a/b/d/*.js', 'a/b/e/*.js', 'a/b/[1-5]/*.js']);
     });
   });
 
   describe('escaping:', function() {
     it('should not expand strings with es6/bash-like variables.', function() {
-      match('abc/${ddd}/xyz', ['abc/${ddd}/xyz']);
-      match('a${b}c', ['a${b}c']);
-      match('a/{${b},c}/d', ['a/${b}/d', 'a/c/d']);
-      match('a${b,d}/{foo,bar}c', ['a${b,d}/fooc', 'a${b,d}/barc']);
+      equal('abc/${ddd}/xyz', ['abc/${ddd}/xyz']);
+      equal('a${b}c', ['a${b}c']);
+      equal('a/{${b},c}/d', ['a/${b}/d', 'a/c/d']);
+      equal('a${b,d}/{foo,bar}c', ['a${b,d}/fooc', 'a${b,d}/barc']);
     });
 
     it('should not expand escaped commas.', function() {
-      match('a{b\\,c}d', ['a{b,c}d']);
-      match('a{b\\,c\\,d}e', ['a{b,c,d}e']);
-      match('{abc\\,def}', ['{abc,def}']);
-      match('{abc\\,def,ghi}', ['abc,def', 'ghi']);
-      match('a/{b,c}/{x\\,y}/d/e', ['a/b/{x,y}/d/e', 'a/c/{x,y}/d/e']);
+      equal('a{b\\,c}d', ['a{b,c}d']);
+      equal('a{b\\,c\\,d}e', ['a{b,c,d}e']);
+      equal('{abc\\,def}', ['{abc,def}']);
+      equal('{abc\\,def,ghi}', ['abc,def', 'ghi']);
+      equal('a/{b,c}/{x\\,y}/d/e', ['a/b/{x,y}/d/e', 'a/c/{x,y}/d/e']);
     });
 
     it('should return sets with escaped commas in `bash` mode.', function() {
-      match('a/{b,c}/{x\\,y}/d/e', ['a/b/{x,y}/d/e', 'a/c/{x,y}/d/e']);
+      equal('a/{b,c}/{x\\,y}/d/e', ['a/b/{x,y}/d/e', 'a/c/{x,y}/d/e']);
     });
 
     it('should not expand escaped braces.', function() {
-      match('{a,b\\}c,d}', ['a', 'b}c', 'd']);
-      match('\\{a,b,c,d,e}', ['{a,b,c,d,e}']);
-      match('a/{b,\\{a,b,c,d,e}/d', ['a/b/d', 'a/b/d', 'a/{a/d', 'a/c/d', 'a/d/d', 'a/e/d']);
-      match('a/\\{b,c}/{d,e}/f', ['a/{b,c}/d/f', 'a/{b,c}/e/f']);
-      match('./\\{x,y}/{a..z..3}/', ['./{x,y}/(a|d|g|j|m|p|s|v|y)/'], {optimize: true});
+      equal('{a,b\\}c,d}', ['a', 'b}c', 'd']);
+      equal('\\{a,b,c,d,e}', ['{a,b,c,d,e}']);
+      equal('a/{b,\\{a,b,c,d,e}/d', ['a/b/d', 'a/b/d', 'a/{a/d', 'a/c/d', 'a/d/d', 'a/e/d']);
+      equal('a/\\{b,c}/{d,e}/f', ['a/{b,c}/d/f', 'a/{b,c}/e/f']);
+      equal('./\\{x,y}/{a..z..3}/', ['./{x,y}/(a|d|g|j|m|p|s|v|y)/'], {optimize: true});
     });
 
     it('should not expand escaped braces or commas.', function() {
-      match('{x\\,y,\\{abc\\},trie}', ['x,y', '{abc}', 'trie']);
+      equal('{x\\,y,\\{abc\\},trie}', ['x,y', '{abc}', 'trie']);
     });
   });
 
   describe('complex', function() {
     it('should expand a complex combination of ranges and sets:', function() {
-      match('a/{x,y}/{1..5}c{d,e}f.{md,txt}', ['a/x/1cdf.md', 'a/y/1cdf.md', 'a/x/2cdf.md', 'a/y/2cdf.md', 'a/x/3cdf.md', 'a/y/3cdf.md', 'a/x/4cdf.md', 'a/y/4cdf.md', 'a/x/5cdf.md', 'a/y/5cdf.md', 'a/x/1cef.md', 'a/y/1cef.md', 'a/x/2cef.md', 'a/y/2cef.md', 'a/x/3cef.md', 'a/y/3cef.md', 'a/x/4cef.md', 'a/y/4cef.md', 'a/x/5cef.md', 'a/y/5cef.md', 'a/x/1cdf.txt', 'a/y/1cdf.txt', 'a/x/2cdf.txt', 'a/y/2cdf.txt', 'a/x/3cdf.txt', 'a/y/3cdf.txt', 'a/x/4cdf.txt', 'a/y/4cdf.txt', 'a/x/5cdf.txt', ' [...]
+      equal('a/{x,y}/{1..5}c{d,e}f.{md,txt}', ['a/x/1cdf.md', 'a/y/1cdf.md', 'a/x/2cdf.md', 'a/y/2cdf.md', 'a/x/3cdf.md', 'a/y/3cdf.md', 'a/x/4cdf.md', 'a/y/4cdf.md', 'a/x/5cdf.md', 'a/y/5cdf.md', 'a/x/1cef.md', 'a/y/1cef.md', 'a/x/2cef.md', 'a/y/2cef.md', 'a/x/3cef.md', 'a/y/3cef.md', 'a/x/4cef.md', 'a/y/4cef.md', 'a/x/5cef.md', 'a/y/5cef.md', 'a/x/1cdf.txt', 'a/y/1cdf.txt', 'a/x/2cdf.txt', 'a/y/2cdf.txt', 'a/x/3cdf.txt', 'a/y/3cdf.txt', 'a/x/4cdf.txt', 'a/y/4cdf.txt', 'a/x/5cdf.txt', ' [...]
     });
 
     it('should expand complex sets and ranges in `bash` mode:', function() {
-      match('a/{x,{1..5},y}/c{d}e', ['a/x/c{d}e', 'a/1/c{d}e', 'a/y/c{d}e', 'a/2/c{d}e', 'a/3/c{d}e', 'a/4/c{d}e', 'a/5/c{d}e']);
+      equal('a/{x,{1..5},y}/c{d}e', ['a/x/c{d}e', 'a/1/c{d}e', 'a/y/c{d}e', 'a/2/c{d}e', 'a/3/c{d}e', 'a/4/c{d}e', 'a/5/c{d}e']);
     });
   });
 });
 
 describe('range expansion', function() {
   it('should expand numerical ranges', function() {
-    match('a{0..3}d', ['a0d', 'a1d', 'a2d', 'a3d']);
-    match('x{10..1}y', ['x10y', 'x9y', 'x8y', 'x7y', 'x6y', 'x5y', 'x4y', 'x3y', 'x2y', 'x1y']);
-    match('x{3..3}y', ['x3y']);
-    match('{-1..-10}', ['-1', '-2', '-3', '-4', '-5', '-6', '-7', '-8', '-9', '-10']);
-    match('{-20..0}', ['-20', '-19', '-18', '-17', '-16', '-15', '-14', '-13', '-12', '-11', '-10', '-9', '-8', '-7', '-6', '-5', '-4', '-3', '-2', '-1', '0']);
-    match('{1..10}', ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']);
-    match('{1..3}', ['1', '2', '3']);
-    match('{1..9}', ['1', '2', '3', '4', '5', '6', '7', '8', '9']);
-    match('{3..3}', ['3']);
-    match('{5..8}', ['5', '6', '7', '8']);
+    equal('a{0..3}d', ['a0d', 'a1d', 'a2d', 'a3d']);
+    equal('x{10..1}y', ['x10y', 'x9y', 'x8y', 'x7y', 'x6y', 'x5y', 'x4y', 'x3y', 'x2y', 'x1y']);
+    equal('x{3..3}y', ['x3y']);
+    equal('{-1..-10}', ['-1', '-2', '-3', '-4', '-5', '-6', '-7', '-8', '-9', '-10']);
+    equal('{-20..0}', ['-20', '-19', '-18', '-17', '-16', '-15', '-14', '-13', '-12', '-11', '-10', '-9', '-8', '-7', '-6', '-5', '-4', '-3', '-2', '-1', '0']);
+    equal('{1..10}', ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']);
+    equal('{1..3}', ['1', '2', '3']);
+    equal('{1..9}', ['1', '2', '3', '4', '5', '6', '7', '8', '9']);
+    equal('{3..3}', ['3']);
+    equal('{5..8}', ['5', '6', '7', '8']);
   });
 
   it('should expand alphabetical ranges', function() {
-    match('0{a..d}0', ['0a0', '0b0', '0c0', '0d0']);
-    match('a/{b..d}/e', ['a/b/e', 'a/c/e', 'a/d/e']);
-    match('{a..A}', ['a', '`', '_', '^', ']', '\\', '[', 'Z', 'Y', 'X', 'W', 'V', 'U', 'T', 'S', 'R', 'Q', 'P', 'O', 'N', 'M', 'L', 'K', 'J', 'I', 'H', 'G', 'F', 'E', 'D', 'C', 'B', 'A']);
-    match('{A..a}', ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a']);
-    match('{a..e}', ['a', 'b', 'c', 'd', 'e']);
-    match('{A..E}', ['A', 'B', 'C', 'D', 'E']);
-    match('{a..f}', ['a', 'b', 'c', 'd', 'e', 'f']);
-    match('{a..z}', ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']);
-    match('{E..A}', ['E', 'D', 'C', 'B', 'A']);
-    match('{f..a}', ['f', 'e', 'd', 'c', 'b', 'a']);
-    match('{f..f}', ['f']);
+    equal('0{a..d}0', ['0a0', '0b0', '0c0', '0d0']);
+    equal('a/{b..d}/e', ['a/b/e', 'a/c/e', 'a/d/e']);
+    equal('{a..A}', ['a', '`', '_', '^', ']', '\\', '[', 'Z', 'Y', 'X', 'W', 'V', 'U', 'T', 'S', 'R', 'Q', 'P', 'O', 'N', 'M', 'L', 'K', 'J', 'I', 'H', 'G', 'F', 'E', 'D', 'C', 'B', 'A']);
+    equal('{A..a}', ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a']);
+    equal('{a..e}', ['a', 'b', 'c', 'd', 'e']);
+    equal('{A..E}', ['A', 'B', 'C', 'D', 'E']);
+    equal('{a..f}', ['a', 'b', 'c', 'd', 'e', 'f']);
+    equal('{a..z}', ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']);
+    equal('{E..A}', ['E', 'D', 'C', 'B', 'A']);
+    equal('{f..a}', ['f', 'e', 'd', 'c', 'b', 'a']);
+    equal('{f..f}', ['f']);
   });
 
   it('should use steps with alphabetical ranges', function() {
-    match('{a..e..2}', ['a', 'c', 'e']);
-    match('{E..A..2}', ['E', 'C', 'A']);
+    equal('{a..e..2}', ['a', 'c', 'e']);
+    equal('{E..A..2}', ['E', 'C', 'A']);
   });
 
   it('should not try to expand ranges with decimals', function() {
-    match('{1.1..2.1}', ['{1.1..2.1}']);
-    match('{1.1..2.1}', ['{1.1..2.1}'], {optimize: true});
-    match('{1.1..~2.1}', ['{1.1..~2.1}'], {optimize: true});
+    equal('{1.1..2.1}', ['{1.1..2.1}']);
+    equal('{1.1..2.1}', ['{1.1..2.1}'], {optimize: true});
+    equal('{1.1..~2.1}', ['{1.1..~2.1}'], {optimize: true});
   });
 
   it('should expand negative ranges', function() {
-    match('{z..a..-2}', ['z', 'x', 'v', 't', 'r', 'p', 'n', 'l', 'j', 'h', 'f', 'd', 'b']);
-    match('{-10..-1}', ['-10', '-9', '-8', '-7', '-6', '-5', '-4', '-3', '-2', '-1']);
-    match('{0..-5}', ['0', '-1', '-2', '-3', '-4', '-5']);
-    match('{9..-4}', ['9', '8', '7', '6', '5', '4', '3', '2', '1', '0', '-1', '-2', '-3', '-4']);
+    equal('{z..a..-2}', ['z', 'x', 'v', 't', 'r', 'p', 'n', 'l', 'j', 'h', 'f', 'd', 'b']);
+    equal('{-10..-1}', ['-10', '-9', '-8', '-7', '-6', '-5', '-4', '-3', '-2', '-1']);
+    equal('{0..-5}', ['0', '-1', '-2', '-3', '-4', '-5']);
+    equal('{9..-4}', ['9', '8', '7', '6', '5', '4', '3', '2', '1', '0', '-1', '-2', '-3', '-4']);
   });
 
   it('should expand multiple ranges:', function() {
-    match('a/{b..d}/e/{f..h}', ['a/b/e/f', 'a/c/e/f', 'a/d/e/f', 'a/b/e/g', 'a/c/e/g', 'a/d/e/g', 'a/b/e/h', 'a/c/e/h', 'a/d/e/h']);
+    equal('a/{b..d}/e/{f..h}', ['a/b/e/f', 'a/c/e/f', 'a/d/e/f', 'a/b/e/g', 'a/c/e/g', 'a/d/e/g', 'a/b/e/h', 'a/c/e/h', 'a/d/e/h']);
   });
 
   it('should work with dots in file paths', function() {
-    match('../{1..3}/../foo', ['../1/../foo', '../2/../foo', '../3/../foo']);
+    equal('../{1..3}/../foo', ['../1/../foo', '../2/../foo', '../3/../foo']);
   });
 
   it('should make a regex-string when `options.optimize` is defined:', function() {
-    match('../{1..3}/../foo', ['../([1-3])/../foo'], {optimize: true});
-    match('../{2..10..2}/../foo', ['../(2|4|6|8|10)/../foo'], {optimize: true});
-    match('../{1..3}/../{a,b,c}/foo', ['../([1-3])/../(a|b|c)/foo'], {optimize: true});
-    match('./{a..z..3}/', ['./(a|d|g|j|m|p|s|v|y)/'], {optimize: true});
-    match('./{"x,y"}/{a..z..3}/', ['./{x,y}/(a|d|g|j|m|p|s|v|y)/'], {optimize: true});
+    equal('../{1..3}/../foo', ['../([1-3])/../foo'], {optimize: true});
+    equal('../{2..10..2}/../foo', ['../(2|4|6|8|10)/../foo'], {optimize: true});
+    equal('../{1..3}/../{a,b,c}/foo', ['../([1-3])/../(a|b|c)/foo'], {optimize: true});
+    equal('./{a..z..3}/', ['./(a|d|g|j|m|p|s|v|y)/'], {optimize: true});
+    equal('./{"x,y"}/{a..z..3}/', ['./{x,y}/(a|d|g|j|m|p|s|v|y)/'], {optimize: true});
   });
 
   it('should expand ranges using steps:', function() {
-    match('{-1..-10..-2}', ['-1', '-3', '-5', '-7', '-9']);
-    match('{-1..-10..2}', ['-1', '-3', '-5', '-7', '-9']);
-    match('{-50..-0..5}', ['-50', '-45', '-40', '-35', '-30', '-25', '-20', '-15', '-10', '-5', '0']);
-    match('{1..10..2}', ['1', '3', '5', '7', '9']);
-    match('{1..20..20}', ['1']);
-    match('{1..20..2}', ['1', '3', '5', '7', '9', '11', '13', '15', '17', '19']);
-    match('{10..0..-2}', ['10', '8', '6', '4', '2', '0']);
-    match('{10..0..2}', ['10', '8', '6', '4', '2', '0']);
-    match('{10..1..-2}', ['10', '8', '6', '4', '2']);
-    match('{10..1..2}', ['10', '8', '6', '4', '2']);
-    match('{10..1}', ['10', '9', '8', '7', '6', '5', '4', '3', '2', '1']);
-    match('{10..1}y', ['10y', '9y', '8y', '7y', '6y', '5y', '4y', '3y', '2y', '1y']);
-    match('{100..0..-5}', ['100', '95', '90', '85', '80', '75', '70', '65', '60', '55', '50', '45', '40', '35', '30', '25', '20', '15', '10', '5', '0']);
-    match('{100..0..5}', ['100', '95', '90', '85', '80', '75', '70', '65', '60', '55', '50', '45', '40', '35', '30', '25', '20', '15', '10', '5', '0']);
-    match('{a..z..2}', ['a', 'c', 'e', 'g', 'i', 'k', 'm', 'o', 'q', 's', 'u', 'w', 'y']);
-    match('{1..10..1}', ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']);
-    match('{1..10..2}', ['1', '3', '5', '7', '9']);
-    match('{1..20..20}', ['1']);
-    match('{1..20..2}', ['1', '3', '5', '7', '9', '11', '13', '15', '17', '19']);
-    match('{10..1..-2}', ['10', '8', '6', '4', '2']);
-    match('{10..1..2}', ['10', '8', '6', '4', '2']);
-    match('{2..10..1}', ['2', '3', '4', '5', '6', '7', '8', '9', '10']);
-    match('{2..10..2}', ['2', '4', '6', '8', '10']);
-    match('{2..10..3}', ['2', '5', '8']);
+    equal('{-1..-10..-2}', ['-1', '-3', '-5', '-7', '-9']);
+    equal('{-1..-10..2}', ['-1', '-3', '-5', '-7', '-9']);
+    equal('{-50..-0..5}', ['-50', '-45', '-40', '-35', '-30', '-25', '-20', '-15', '-10', '-5', '0']);
+    equal('{1..10..2}', ['1', '3', '5', '7', '9']);
+    equal('{1..20..20}', ['1']);
+    equal('{1..20..2}', ['1', '3', '5', '7', '9', '11', '13', '15', '17', '19']);
+    equal('{10..0..-2}', ['10', '8', '6', '4', '2', '0']);
+    equal('{10..0..2}', ['10', '8', '6', '4', '2', '0']);
+    equal('{10..1..-2}', ['10', '8', '6', '4', '2']);
+    equal('{10..1..2}', ['10', '8', '6', '4', '2']);
+    equal('{10..1}', ['10', '9', '8', '7', '6', '5', '4', '3', '2', '1']);
+    equal('{10..1}y', ['10y', '9y', '8y', '7y', '6y', '5y', '4y', '3y', '2y', '1y']);
+    equal('{100..0..-5}', ['100', '95', '90', '85', '80', '75', '70', '65', '60', '55', '50', '45', '40', '35', '30', '25', '20', '15', '10', '5', '0']);
+    equal('{100..0..5}', ['100', '95', '90', '85', '80', '75', '70', '65', '60', '55', '50', '45', '40', '35', '30', '25', '20', '15', '10', '5', '0']);
+    equal('{a..z..2}', ['a', 'c', 'e', 'g', 'i', 'k', 'm', 'o', 'q', 's', 'u', 'w', 'y']);
+    equal('{1..10..1}', ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']);
+    equal('{1..10..2}', ['1', '3', '5', '7', '9']);
+    equal('{1..20..20}', ['1']);
+    equal('{1..20..2}', ['1', '3', '5', '7', '9', '11', '13', '15', '17', '19']);
+    equal('{10..1..-2}', ['10', '8', '6', '4', '2']);
+    equal('{10..1..2}', ['10', '8', '6', '4', '2']);
+    equal('{2..10..1}', ['2', '3', '4', '5', '6', '7', '8', '9', '10']);
+    equal('{2..10..2}', ['2', '4', '6', '8', '10']);
+    equal('{2..10..3}', ['2', '5', '8']);
   });
 
   it('should expand negative ranges using steps:', function() {
-    match('{-1..-10..-2}', ['-1', '-3', '-5', '-7', '-9']);
-    match('{-1..-10..2}', ['-1', '-3', '-5', '-7', '-9']);
-    match('{-10..-2..2}', ['-10', '-8', '-6', '-4', '-2']);
-    match('{-2..-10..1}', ['-2', '-3', '-4', '-5', '-6', '-7', '-8', '-9', '-10']);
-    match('{-2..-10..2}', ['-2', '-4', '-6', '-8', '-10']);
-    match('{-2..-10..3}', ['-2', '-5', '-8']);
-    match('{-9..9..3}', ['-9', '-6', '-3', '0', '3', '6', '9']);
+    equal('{-1..-10..-2}', ['-1', '-3', '-5', '-7', '-9']);
+    equal('{-1..-10..2}', ['-1', '-3', '-5', '-7', '-9']);
+    equal('{-10..-2..2}', ['-10', '-8', '-6', '-4', '-2']);
+    equal('{-2..-10..1}', ['-2', '-3', '-4', '-5', '-6', '-7', '-8', '-9', '-10']);
+    equal('{-2..-10..2}', ['-2', '-4', '-6', '-8', '-10']);
+    equal('{-2..-10..3}', ['-2', '-5', '-8']);
+    equal('{-9..9..3}', ['-9', '-6', '-3', '0', '3', '6', '9']);
   });
 
   it('should expand mixed ranges and sets:', function() {
-    match('x{{0..10},braces}y', ['x0y', 'xbracesy', 'x1y', 'x2y', 'x3y', 'x4y', 'x5y', 'x6y', 'x7y', 'x8y', 'x9y', 'x10y']);
-    match('{{0..10},braces}', ['0', 'braces', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10']);
-    match('{2147483645..2147483649}', ['2147483645', '2147483646', '2147483647', '2147483648', '2147483649']);
+    equal('x{{0..10},braces}y', ['x0y', 'xbracesy', 'x1y', 'x2y', 'x3y', 'x4y', 'x5y', 'x6y', 'x7y', 'x8y', 'x9y', 'x10y']);
+    equal('{{0..10},braces}', ['0', 'braces', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10']);
+    equal('{2147483645..2147483649}', ['2147483645', '2147483646', '2147483647', '2147483648', '2147483649']);
   });
 
   it('should return invalid ranges:', function() {
-    match('{1.20..2}', ['{1.20..2}']);
-    match('{1..0f}', ['{1..0f}']);
-    match('{1..10..ff}', ['{1..10..ff}']);
-    match('{1..10.f}', ['{1..10.f}']);
-    match('{1..10f}', ['{1..10f}']);
-    match('{1..20..2f}', ['{1..20..2f}']);
-    match('{1..20..f2}', ['{1..20..f2}']);
-    match('{1..2f..2}', ['{1..2f..2}']);
-    match('{1..ff..2}', ['{1..ff..2}']);
-    match('{1..ff}', ['{1..ff}']);
+    equal('{1.20..2}', ['{1.20..2}']);
+    equal('{1..0f}', ['{1..0f}']);
+    equal('{1..10..ff}', ['{1..10..ff}']);
+    equal('{1..10.f}', ['{1..10.f}']);
+    equal('{1..10f}', ['{1..10f}']);
+    equal('{1..20..2f}', ['{1..20..2f}']);
+    equal('{1..20..f2}', ['{1..20..f2}']);
+    equal('{1..2f..2}', ['{1..2f..2}']);
+    equal('{1..ff..2}', ['{1..ff..2}']);
+    equal('{1..ff}', ['{1..ff}']);
   });
 });
diff --git a/test/support/generate.js b/test/support/generate.js
index 5ab1a77..f892749 100644
--- a/test/support/generate.js
+++ b/test/support/generate.js
@@ -2,27 +2,29 @@
 
 var braces = require('../..');
 var mm = require('minimatch');
-var size = require('pretty-bytes');
 var text = require('text-table');
 var Time = require('time-diff');
 var time = new Time();
 
 var table = [
-  ['**Pattern**', '**minimatch**', '**braces**'],
-  ['---', '---', '---'],
+  ['**Pattern**', '**braces**', '**minimatch**'],
+  ['---', '---', '---']
 ];
 
-function generate(pattern) {
-  // time.start('minimatch');
-
-  // var mval = mm.braceExpand(pattern).join('|');
-  // var m = [wrap(size(mval.length)), '(' + time.end('minimatch', 'μs') + ')'].join(' ');
+// warm up both libs
+mm.braceExpand('{a,b}');
+braces('{a,b}');
 
+function generate(pattern) {
   time.start('braces');
-  var bval = braces.expand(pattern).join('|');
-  var b = [wrap(size(bval.length)), '(' + time.end('braces', 'μs') + ')'].join(' ');
+  var bval = braces(pattern, {rangeLimit: false}).join('|');
+  var b = [wrap(format(bval.length)), '(' + time.end('braces', 'μs') + ')'].join(' ');
 
-  table.push([wrap(pattern), 'm', b]);
+  time.start('minimatch');
+  var mval = mm.braceExpand(pattern).join('|');
+  var m = [wrap(format(mval.length)), '(' + time.end('minimatch', 'μs') + ')'].join(' ');
+
+  table.push([wrap(pattern), b, m]);
   return table;
 }
 
@@ -31,27 +33,54 @@ function wrap(str) {
 }
 
 var patterns = [
-  '{1..9007199254740991}',
-  // 'a/{1..10000000}',
-  // 'a/{1..1000000}',
-  // 'a/{1..100000}',
-  // 'a/{1..10000}',
-  // 'a/{1..1000}',
-  // 'a/{1..100}',
-  // 'a/{1..10}',
-  // 'a/{1..3}',
-  // 'a/{2000..2016}/bar/{a..j}/baz',
-  // 'a/{1900..2016}/bar/{a..j}/baz',
-  // 'a/{1000..2016}/bar/{a..j}/baz',
-  // 'a/{100..2016}/bar/{a..j}/baz',
+  // '{1..9007199254740991}',
+  // '{1..1000000000000000}',
+  // '{1..100000000000000}',
+  // '{1..10000000000000}',
+  // '{1..1000000000000}',
+  // '{1..100000000000}',
+  // '{1..10000000000}',
+  // '{1..1000000000}',
+  // '{1..100000000}',
+  '{1..10000000}',
+  '{1..1000000}',
+  '{1..100000}',
+  '{1..10000}',
+  '{1..1000}',
+  '{1..100}',
+  '{1..10}',
+  '{1..3}',
+  // '/some/file/path/id-{0001..2017}',
+  // '/some/file/path/id-{0100..2017}',
+  // '/some/file/path/id-{1000..2017}',
+  // '/some/file/path/id-{1900..2017}',
+  // '/some/file/path/id-{2000..2017}',
 ];
 
-var len = patterns.length;
-var idx = -1;
-var res = [];
-
-while (++idx < len) {
-  generate(patterns[idx]);
+for (var i = 0; i < patterns.length; i++) {
+  generate(patterns[i]);
 }
 
 console.log(text(table, {hsep: ' | '}));
+
+function format(number, precision) {
+  if (typeof precision !== 'number') {
+    precision = 2;
+  }
+
+  var abbr = ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
+  precision = Math.pow(10, precision);
+  number = Number(number);
+
+  var len = abbr.length - 1;
+  while (len-- >= 0) {
+    var size = Math.pow(10, len * 3);
+    if (size <= (number + 1)) {
+      number = Math.round(number * precision / size) / precision;
+      number += ' ' + abbr[len];
+      break;
+    }
+  }
+  return number;
+}
+
diff --git a/test/support/utils.js b/test/support/utils.js
index 6e214ad..21be11a 100644
--- a/test/support/utils.js
+++ b/test/support/utils.js
@@ -1,5 +1,6 @@
 'use strict';
 
+var fs = require('fs');
 var util = require('util');
 var bashPath = '';
 
@@ -7,15 +8,14 @@ var bashPath = '';
  * Utils
  */
 
-exports.exists = require('fs-exists-sync');
 exports.extend = require('extend-shallow');
 exports.nc = require('noncharacters');
 
 exports.getBashPath = function() {
   if (bashPath) return bashPath;
-  if (exports.exists('/usr/local/bin/bash')) {
+  if (fs.existsSync('/usr/local/bin/bash')) {
     bashPath = '/usr/local/bin/bash';
-  } else if (exports.exists('/bin/bash')) {
+  } else if (fs.existsSync('/bin/bash')) {
     bashPath = '/bin/bash';
   } else {
     bashPath = 'bash';
diff --git a/test/utils.js b/test/utils.js
new file mode 100644
index 0000000..c2afd72
--- /dev/null
+++ b/test/utils.js
@@ -0,0 +1,78 @@
+/*!
+ * braces <https://github.com/jonschlinkert/braces>
+ *
+ * Copyright (c) 2014-2016, Jon Schlinkert.
+ * Licensed under the MIT License
+ */
+
+'use strict';
+
+require('mocha');
+var assert = require('assert');
+var utils = require('../lib/utils');
+
+describe('utils', function() {
+  describe('.isEmptySets', function() {
+    it('should return true if string contains only empty stems', function() {
+      assert(utils.isEmptySets('{,}'));
+      assert(utils.isEmptySets('{,}{,}'));
+      assert(utils.isEmptySets('{,}{,}{,}{,}{,}'));
+    });
+
+    it('should return false if string contains more than empty stems', function() {
+      assert(!utils.isEmptySets('{,}foo'));
+    });
+
+    it('should return false if string contains other than empty stems', function() {
+      assert(!utils.isEmptySets('foo'));
+    });
+  });
+
+  describe('.split', function() {
+    it('should split on commas by default', function() {
+      assert.deepEqual(utils.split('a,b,c'), ['a', 'b', 'c']);
+      assert.deepEqual(utils.split('{a,b,c}'), ['{a', 'b', 'c}']);
+    });
+
+    it('should not split inside parens', function() {
+      assert.deepEqual(utils.split('*(a|{b|c,d})'), ['*(a|{b|c,d})']);
+      assert.deepEqual(utils.split('a,@(b,c)'), ['a', '@(b,c)']);
+      assert.deepEqual(utils.split('a,*(b|c,d),z'), ['a', '*(b|c,d)', 'z']);
+    });
+
+    it('should work with unclosed parens', function() {
+      assert.deepEqual(utils.split('*(a|{b|c,d}'), ['*(a|{b|c,d}']);
+    });
+
+    it('should not split inside nested parens', function() {
+      assert.deepEqual(utils.split('a,*(b|(c,d)),z'), ['a', '*(b|(c,d))', 'z']);
+      assert.deepEqual(utils.split('a,*(b,(c,d)),z'), ['a', '*(b,(c,d))', 'z']);
+    });
+
+    it('should not split inside brackets', function() {
+      assert.deepEqual(utils.split('[a-z,"]*'), ['[a-z,"]*']);
+    });
+
+    it('should work with unclosed brackets', function() {
+      assert.deepEqual(utils.split('[a-z,"*'), ['[a-z,"*']);
+    });
+
+    it('should not split parens nested inside brackets', function() {
+      assert.deepEqual(utils.split('[-a(z,")]*'), ['[-a(z,")]*']);
+    });
+
+    it('should not split brackets nested inside parens', function() {
+      assert.deepEqual(utils.split('x,(a,[-a,])*'), ['x', '(a,[-a,])*']);
+      assert.deepEqual(utils.split('a,(1,[^(x,y)],3),z'), ['a', '(1,[^(x,y)],3)', 'z']);
+    });
+
+    it('should support escaped parens', function() {
+      assert.deepEqual(utils.split('a,@(b,c\\),z)'), ['a', '@(b,c\\),z)']);
+    });
+
+    it('should support escaped brackets', function() {
+      assert.deepEqual(utils.split('a,@([b,c\\],x]|b),z'), ['a', '@([b,c\\],x]|b)', 'z']);
+      assert.deepEqual(utils.split('a,@([b,c\\],x],b),z'), ['a', '@([b,c\\],x],b)', 'z']);
+    });
+  });
+});

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/node-braces.git



More information about the Pkg-javascript-commits mailing list