wanghuidong 5 жил өмнө
parent
commit
8f940409b0
69 өөрчлөгдсөн 12781 нэмэгдсэн , 219 устгасан
  1. 1 0
      .gitignore
  2. 1 0
      src/github.com/PuerkitoBio/goquery/.gitattributes
  3. 16 0
      src/github.com/PuerkitoBio/goquery/.gitignore
  4. 17 0
      src/github.com/PuerkitoBio/goquery/.travis.yml
  5. 12 0
      src/github.com/PuerkitoBio/goquery/LICENSE
  6. 184 0
      src/github.com/PuerkitoBio/goquery/README.md
  7. 23 2
      src/github.com/PuerkitoBio/goquery/array.go
  8. 54 0
      src/github.com/PuerkitoBio/goquery/array_test.go
  9. 436 0
      src/github.com/PuerkitoBio/goquery/bench/v0.1.0
  10. 438 0
      src/github.com/PuerkitoBio/goquery/bench/v0.1.1
  11. 405 0
      src/github.com/PuerkitoBio/goquery/bench/v0.1.1-v0.2.1-go1.1rc1.svg
  12. 459 0
      src/github.com/PuerkitoBio/goquery/bench/v0.2.0
  13. 420 0
      src/github.com/PuerkitoBio/goquery/bench/v0.2.0-v0.2.1-go1.1rc1.svg
  14. 470 0
      src/github.com/PuerkitoBio/goquery/bench/v0.2.1-go1.1rc1
  15. 476 0
      src/github.com/PuerkitoBio/goquery/bench/v0.3.0
  16. 478 0
      src/github.com/PuerkitoBio/goquery/bench/v0.3.2-go1.2
  17. 477 0
      src/github.com/PuerkitoBio/goquery/bench/v0.3.2-go1.2-take2
  18. 477 0
      src/github.com/PuerkitoBio/goquery/bench/v0.3.2-go1.2rc1
  19. 85 0
      src/github.com/PuerkitoBio/goquery/bench/v1.0.0-go1.7
  20. 85 0
      src/github.com/PuerkitoBio/goquery/bench/v1.0.1a-go1.7
  21. 85 0
      src/github.com/PuerkitoBio/goquery/bench/v1.0.1b-go1.7
  22. 86 0
      src/github.com/PuerkitoBio/goquery/bench/v1.0.1c-go1.7
  23. 12 4
      src/github.com/PuerkitoBio/goquery/bench_array_test.go
  24. 0 2
      src/github.com/PuerkitoBio/goquery/bench_example_test.go
  25. 36 4
      src/github.com/PuerkitoBio/goquery/bench_expand_test.go
  26. 36 12
      src/github.com/PuerkitoBio/goquery/bench_filter_test.go
  27. 11 4
      src/github.com/PuerkitoBio/goquery/bench_iteration_test.go
  28. 6 2
      src/github.com/PuerkitoBio/goquery/bench_property_test.go
  29. 21 7
      src/github.com/PuerkitoBio/goquery/bench_query_test.go
  30. 129 43
      src/github.com/PuerkitoBio/goquery/bench_traversal_test.go
  31. 7 2
      src/github.com/PuerkitoBio/goquery/doc.go
  32. 68 0
      src/github.com/PuerkitoBio/goquery/doc/tips.md
  33. 60 10
      src/github.com/PuerkitoBio/goquery/example_test.go
  34. 27 6
      src/github.com/PuerkitoBio/goquery/expand.go
  35. 50 0
      src/github.com/PuerkitoBio/goquery/expand_test.go
  36. 16 9
      src/github.com/PuerkitoBio/goquery/filter.go
  37. 15 0
      src/github.com/PuerkitoBio/goquery/filter_test.go
  38. 8 0
      src/github.com/PuerkitoBio/goquery/go.mod
  39. 8 0
      src/github.com/PuerkitoBio/goquery/go.sum
  40. 8 2
      src/github.com/PuerkitoBio/goquery/iteration.go
  41. 42 14
      src/github.com/PuerkitoBio/goquery/manipulation.go
  42. 60 0
      src/github.com/PuerkitoBio/goquery/manipulation_test.go
  43. 37 0
      src/github.com/PuerkitoBio/goquery/misc/git/pre-commit
  44. 17 34
      src/github.com/PuerkitoBio/goquery/property.go
  45. 2 9
      src/github.com/PuerkitoBio/goquery/query.go
  46. 7 0
      src/github.com/PuerkitoBio/goquery/query_test.go
  47. 855 0
      src/github.com/PuerkitoBio/goquery/testdata/gotesting.html
  48. 26 0
      src/github.com/PuerkitoBio/goquery/testdata/gowiki.html
  49. 413 0
      src/github.com/PuerkitoBio/goquery/testdata/metalreview.html
  50. 102 0
      src/github.com/PuerkitoBio/goquery/testdata/page.html
  51. 24 0
      src/github.com/PuerkitoBio/goquery/testdata/page2.html
  52. 24 0
      src/github.com/PuerkitoBio/goquery/testdata/page3.html
  53. 31 29
      src/github.com/PuerkitoBio/goquery/traversal.go
  54. 99 3
      src/github.com/PuerkitoBio/goquery/traversal_test.go
  55. 33 5
      src/github.com/PuerkitoBio/goquery/type.go
  56. 19 9
      src/github.com/PuerkitoBio/goquery/type_test.go
  57. 84 7
      src/github.com/PuerkitoBio/goquery/utilities.go
  58. 128 0
      src/github.com/PuerkitoBio/goquery/utilities_test.go
  59. 6 0
      src/github.com/shopspring/decimal/.gitignore
  60. 13 0
      src/github.com/shopspring/decimal/.travis.yml
  61. 19 0
      src/github.com/shopspring/decimal/CHANGELOG.md
  62. 45 0
      src/github.com/shopspring/decimal/LICENSE
  63. 130 0
      src/github.com/shopspring/decimal/README.md
  64. 415 0
      src/github.com/shopspring/decimal/decimal-go.go
  65. 1504 0
      src/github.com/shopspring/decimal/decimal.go
  66. 185 0
      src/github.com/shopspring/decimal/decimal_bench_test.go
  67. 2595 0
      src/github.com/shopspring/decimal/decimal_test.go
  68. 3 0
      src/github.com/shopspring/decimal/go.mod
  69. 160 0
      src/github.com/shopspring/decimal/rounding.go

+ 1 - 0
.gitignore

@@ -0,0 +1 @@
+pkg/

+ 1 - 0
src/github.com/PuerkitoBio/goquery/.gitattributes

@@ -0,0 +1 @@
+testdata/* linguist-vendored

+ 16 - 0
src/github.com/PuerkitoBio/goquery/.gitignore

@@ -0,0 +1,16 @@
+# editor temporary files
+*.sublime-*
+.DS_Store
+*.swp
+#*.*#
+tags
+
+# direnv config
+.env*
+
+# test binaries
+*.test
+
+# coverage and profilte outputs
+*.out
+

+ 17 - 0
src/github.com/PuerkitoBio/goquery/.travis.yml

@@ -0,0 +1,17 @@
+language: go
+
+go:
+    - 1.2.x
+    - 1.3.x
+    - 1.4.x
+    - 1.5.x
+    - 1.6.x
+    - 1.7.x
+    - 1.8.x
+    - 1.9.x
+    - 1.10.x
+    - 1.11.x
+    - 1.12.x
+    - 1.13.x
+    - tip
+

+ 12 - 0
src/github.com/PuerkitoBio/goquery/LICENSE

@@ -0,0 +1,12 @@
+Copyright (c) 2012-2016, Martin Angers & Contributors
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+* Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ 184 - 0
src/github.com/PuerkitoBio/goquery/README.md

@@ -0,0 +1,184 @@
+# goquery - a little like that j-thing, only in Go
+[![build status](https://secure.travis-ci.org/PuerkitoBio/goquery.svg?branch=master)](http://travis-ci.org/PuerkitoBio/goquery) [![GoDoc](https://godoc.org/github.com/PuerkitoBio/goquery?status.png)](http://godoc.org/github.com/PuerkitoBio/goquery) [![Sourcegraph Badge](https://sourcegraph.com/github.com/PuerkitoBio/goquery/-/badge.svg)](https://sourcegraph.com/github.com/PuerkitoBio/goquery?badge)
+
+goquery brings a syntax and a set of features similar to [jQuery][] to the [Go language][go]. It is based on Go's [net/html package][html] and the CSS Selector library [cascadia][]. Since the net/html parser returns nodes, and not a full-featured DOM tree, jQuery's stateful manipulation functions (like height(), css(), detach()) have been left off.
+
+Also, because the net/html parser requires UTF-8 encoding, so does goquery: it is the caller's responsibility to ensure that the source document provides UTF-8 encoded HTML. See the [wiki][] for various options to do this.
+
+Syntax-wise, it is as close as possible to jQuery, with the same function names when possible, and that warm and fuzzy chainable interface. jQuery being the ultra-popular library that it is, I felt that writing a similar HTML-manipulating library was better to follow its API than to start anew (in the same spirit as Go's `fmt` package), even though some of its methods are less than intuitive (looking at you, [index()][index]...).
+
+## Table of Contents
+
+* [Installation](#installation)
+* [Changelog](#changelog)
+* [API](#api)
+* [Examples](#examples)
+* [Related Projects](#related-projects)
+* [Support](#support)
+* [License](#license)
+
+## Installation
+
+Please note that because of the net/html dependency, goquery requires Go1.1+.
+
+    $ go get github.com/PuerkitoBio/goquery
+
+(optional) To run unit tests:
+
+    $ cd $GOPATH/src/github.com/PuerkitoBio/goquery
+    $ go test
+
+(optional) To run benchmarks (warning: it runs for a few minutes):
+
+    $ cd $GOPATH/src/github.com/PuerkitoBio/goquery
+    $ go test -bench=".*"
+
+## Changelog
+
+**Note that goquery's API is now stable, and will not break.**
+
+*    **2020-02-04 (v1.5.1)** : Update module dependencies.
+*    **2018-11-15 (v1.5.0)** : Go module support (thanks @Zaba505).
+*    **2018-06-07 (v1.4.1)** : Add `NewDocumentFromReader` examples.
+*    **2018-03-24 (v1.4.0)** : Deprecate `NewDocument(url)` and `NewDocumentFromResponse(response)`.
+*    **2018-01-28 (v1.3.0)** : Add `ToEnd` constant to `Slice` until the end of the selection (thanks to @davidjwilkins for raising the issue).
+*    **2018-01-11 (v1.2.0)** : Add `AddBack*` and deprecate `AndSelf` (thanks to @davidjwilkins).
+*    **2017-02-12 (v1.1.0)** : Add `SetHtml` and `SetText` (thanks to @glebtv).
+*    **2016-12-29 (v1.0.2)** : Optimize allocations for `Selection.Text` (thanks to @radovskyb).
+*    **2016-08-28 (v1.0.1)** : Optimize performance for large documents.
+*    **2016-07-27 (v1.0.0)** : Tag version 1.0.0.
+*    **2016-06-15** : Invalid selector strings internally compile to a `Matcher` implementation that never matches any node (instead of a panic). So for example, `doc.Find("~")` returns an empty `*Selection` object.
+*    **2016-02-02** : Add `NodeName` utility function similar to the DOM's `nodeName` property. It returns the tag name of the first element in a selection, and other relevant values of non-element nodes (see godoc for details). Add `OuterHtml` utility function similar to the DOM's `outerHTML` property (named `OuterHtml` in small caps for consistency with the existing `Html` method on the `Selection`).
+*    **2015-04-20** : Add `AttrOr` helper method to return the attribute's value or a default value if absent. Thanks to [piotrkowalczuk][piotr].
+*    **2015-02-04** : Add more manipulation functions - Prepend* - thanks again to [Andrew Stone][thatguystone].
+*    **2014-11-28** : Add more manipulation functions - ReplaceWith*, Wrap* and Unwrap - thanks again to [Andrew Stone][thatguystone].
+*    **2014-11-07** : Add manipulation functions (thanks to [Andrew Stone][thatguystone]) and `*Matcher` functions, that receive compiled cascadia selectors instead of selector strings, thus avoiding potential panics thrown by goquery via `cascadia.MustCompile` calls. This results in better performance (selectors can be compiled once and reused) and more idiomatic error handling (you can handle cascadia's compilation errors, instead of recovering from panics, which had been bugging me for a long time). Note that the actual type expected is a `Matcher` interface, that `cascadia.Selector` implements. Other matcher implementations could be used.
+*    **2014-11-06** : Change import paths of net/html to golang.org/x/net/html (see https://groups.google.com/forum/#!topic/golang-nuts/eD8dh3T9yyA). Make sure to update your code to use the new import path too when you call goquery with `html.Node`s.
+*    **v0.3.2** : Add `NewDocumentFromReader()` (thanks jweir) which allows creating a goquery document from an io.Reader.
+*    **v0.3.1** : Add `NewDocumentFromResponse()` (thanks assassingj) which allows creating a goquery document from an http response.
+*    **v0.3.0** : Add `EachWithBreak()` which allows to break out of an `Each()` loop by returning false. This function was added instead of changing the existing `Each()` to avoid breaking compatibility.
+*    **v0.2.1** : Make go-getable, now that [go.net/html is Go1.0-compatible][gonet] (thanks to @matrixik for pointing this out).
+*    **v0.2.0** : Add support for negative indices in Slice(). **BREAKING CHANGE** `Document.Root` is removed, `Document` is now a `Selection` itself (a selection of one, the root element, just like `Document.Root` was before). Add jQuery's Closest() method.
+*    **v0.1.1** : Add benchmarks to use as baseline for refactorings, refactor Next...() and Prev...() methods to use the new html package's linked list features (Next/PrevSibling, FirstChild). Good performance boost (40+% in some cases).
+*    **v0.1.0** : Initial release.
+
+## API
+
+goquery exposes two structs, `Document` and `Selection`, and the `Matcher` interface. Unlike jQuery, which is loaded as part of a DOM document, and thus acts on its containing document, goquery doesn't know which HTML document to act upon. So it needs to be told, and that's what the `Document` type is for. It holds the root document node as the initial Selection value to manipulate.
+
+jQuery often has many variants for the same function (no argument, a selector string argument, a jQuery object argument, a DOM element argument, ...). Instead of exposing the same features in goquery as a single method with variadic empty interface arguments, statically-typed signatures are used following this naming convention:
+
+*    When the jQuery equivalent can be called with no argument, it has the same name as jQuery for the no argument signature (e.g.: `Prev()`), and the version with a selector string argument is called `XxxFiltered()` (e.g.: `PrevFiltered()`)
+*    When the jQuery equivalent **requires** one argument, the same name as jQuery is used for the selector string version (e.g.: `Is()`)
+*    The signatures accepting a jQuery object as argument are defined in goquery as `XxxSelection()` and take a `*Selection` object as argument (e.g.: `FilterSelection()`)
+*    The signatures accepting a DOM element as argument in jQuery are defined in goquery as `XxxNodes()` and take a variadic argument of type `*html.Node` (e.g.: `FilterNodes()`)
+*    The signatures accepting a function as argument in jQuery are defined in goquery as `XxxFunction()` and take a function as argument (e.g.: `FilterFunction()`)
+*    The goquery methods that can be called with a selector string have a corresponding version that take a `Matcher` interface and are defined as `XxxMatcher()` (e.g.: `IsMatcher()`)
+
+Utility functions that are not in jQuery but are useful in Go are implemented as functions (that take a `*Selection` as parameter), to avoid a potential naming clash on the `*Selection`'s methods (reserved for jQuery-equivalent behaviour).
+
+The complete [godoc reference documentation can be found here][doc].
+
+Please note that Cascadia's selectors do not necessarily match all supported selectors of jQuery (Sizzle). See the [cascadia project][cascadia] for details. Invalid selector strings compile to a `Matcher` that fails to match any node. Behaviour of the various functions that take a selector string as argument follows from that fact, e.g. (where `~` is an invalid selector string):
+
+* `Find("~")` returns an empty selection because the selector string doesn't match anything.
+* `Add("~")` returns a new selection that holds the same nodes as the original selection, because it didn't add any node (selector string didn't match anything).
+* `ParentsFiltered("~")` returns an empty selection because the selector string doesn't match anything.
+* `ParentsUntil("~")` returns all parents of the selection because the selector string didn't match any element to stop before the top element.
+
+## Examples
+
+See some tips and tricks in the [wiki][].
+
+Adapted from example_test.go:
+
+```Go
+package main
+
+import (
+  "fmt"
+  "log"
+  "net/http"
+
+  "github.com/PuerkitoBio/goquery"
+)
+
+func ExampleScrape() {
+  // Request the HTML page.
+  res, err := http.Get("http://metalsucks.net")
+  if err != nil {
+    log.Fatal(err)
+  }
+  defer res.Body.Close()
+  if res.StatusCode != 200 {
+    log.Fatalf("status code error: %d %s", res.StatusCode, res.Status)
+  }
+
+  // Load the HTML document
+  doc, err := goquery.NewDocumentFromReader(res.Body)
+  if err != nil {
+    log.Fatal(err)
+  }
+
+  // Find the review items
+  doc.Find(".sidebar-reviews article .content-block").Each(func(i int, s *goquery.Selection) {
+    // For each item found, get the band and title
+    band := s.Find("a").Text()
+    title := s.Find("i").Text()
+    fmt.Printf("Review %d: %s - %s\n", i, band, title)
+  })
+}
+
+func main() {
+  ExampleScrape()
+}
+```
+
+## Related Projects
+
+- [Goq][goq], an HTML deserialization and scraping library based on goquery and struct tags.
+- [andybalholm/cascadia][cascadia], the CSS selector library used by goquery.
+- [suntong/cascadia][cascadiacli], a command-line interface to the cascadia CSS selector library, useful to test selectors.
+- [gocolly/colly](https://github.com/gocolly/colly), a lightning fast and elegant Scraping Framework
+- [gnulnx/goperf](https://github.com/gnulnx/goperf), a website performance test tool that also fetches static assets.
+- [MontFerret/ferret](https://github.com/MontFerret/ferret), declarative web scraping.
+- [tacusci/berrycms](https://github.com/tacusci/berrycms), a modern simple to use CMS with easy to write plugins
+- [Dataflow kit](https://github.com/slotix/dataflowkit), Web Scraping framework for Gophers.  
+- [Geziyor](https://github.com/geziyor/geziyor), a fast web crawling & scraping framework for Go. Supports JS rendering.
+- [Pagser](https://github.com/foolin/pagser), a simple, easy, extensible, configurable HTML parser to struct based on goquery and struct tags.
+
+## Support
+
+There are a number of ways you can support the project:
+
+* Use it, star it, build something with it, spread the word!
+  - If you do build something open-source or otherwise publicly-visible, let me know so I can add it to the [Related Projects](#related-projects) section!
+* Raise issues to improve the project (note: doc typos and clarifications are issues too!)
+  - Please search existing issues before opening a new one - it may have already been adressed.
+* Pull requests: please discuss new code in an issue first, unless the fix is really trivial.
+  - Make sure new code is tested.
+  - Be mindful of existing code - PRs that break existing code have a high probability of being declined, unless it fixes a serious issue.
+
+If you desperately want to send money my way, I have a BuyMeACoffee.com page:
+
+<a href="https://www.buymeacoffee.com/mna" target="_blank"><img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Buy Me A Coffee" style="height: 41px !important;width: 174px !important;box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;-webkit-box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;" ></a>
+
+## License
+
+The [BSD 3-Clause license][bsd], the same as the [Go language][golic]. Cascadia's license is [here][caslic].
+
+[jquery]: http://jquery.com/
+[go]: http://golang.org/
+[cascadia]: https://github.com/andybalholm/cascadia
+[cascadiacli]: https://github.com/suntong/cascadia
+[bsd]: http://opensource.org/licenses/BSD-3-Clause
+[golic]: http://golang.org/LICENSE
+[caslic]: https://github.com/andybalholm/cascadia/blob/master/LICENSE
+[doc]: http://godoc.org/github.com/PuerkitoBio/goquery
+[index]: http://api.jquery.com/index/
+[gonet]: https://github.com/golang/net/
+[html]: http://godoc.org/golang.org/x/net/html
+[wiki]: https://github.com/PuerkitoBio/goquery/wiki/Tips-and-tricks
+[thatguystone]: https://github.com/thatguystone
+[piotr]: https://github.com/piotrkowalczuk
+[goq]: https://github.com/andrewstuart/goq

+ 23 - 2
src/github.com/PuerkitoBio/goquery/array.go

@@ -4,6 +4,16 @@ import (
 	"golang.org/x/net/html"
 )
 
+const (
+	maxUint = ^uint(0)
+	maxInt  = int(maxUint >> 1)
+
+	// ToEnd is a special index value that can be used as end index in a call
+	// to Slice so that all elements are selected until the end of the Selection.
+	// It is equivalent to passing (*Selection).Length().
+	ToEnd = maxInt
+)
+
 // First reduces the set of matched elements to the first in the set.
 // It returns a new Selection object, and an empty Selection object if the
 // the selection is empty.
@@ -35,12 +45,23 @@ func (s *Selection) Eq(index int) *Selection {
 }
 
 // Slice reduces the set of matched elements to a subset specified by a range
-// of indices.
+// of indices. The start index is 0-based and indicates the index of the first
+// element to select. The end index is 0-based and indicates the index at which
+// the elements stop being selected (the end index is not selected).
+//
+// The indices may be negative, in which case they represent an offset from the
+// end of the selection.
+//
+// The special value ToEnd may be specified as end index, in which case all elements
+// until the end are selected. This works both for a positive and negative start
+// index.
 func (s *Selection) Slice(start, end int) *Selection {
 	if start < 0 {
 		start += len(s.Nodes)
 	}
-	if end < 0 {
+	if end == ToEnd {
+		end = len(s.Nodes)
+	} else if end < 0 {
 		end += len(s.Nodes)
 	}
 	return pushStack(s, s.Nodes[start:end])

+ 54 - 0
src/github.com/PuerkitoBio/goquery/array_test.go

@@ -14,6 +14,11 @@ func TestFirstEmpty(t *testing.T) {
 	assertLength(t, sel.Nodes, 0)
 }
 
+func TestFirstInvalid(t *testing.T) {
+	sel := Doc().Find("").First()
+	assertLength(t, sel.Nodes, 0)
+}
+
 func TestFirstRollback(t *testing.T) {
 	sel := Doc().Find(".pvk-content")
 	sel2 := sel.First().End()
@@ -36,6 +41,11 @@ func TestLastEmpty(t *testing.T) {
 	assertLength(t, sel.Nodes, 0)
 }
 
+func TestLastInvalid(t *testing.T) {
+	sel := Doc().Find("").Last()
+	assertLength(t, sel.Nodes, 0)
+}
+
 func TestLastRollback(t *testing.T) {
 	sel := Doc().Find(".pvk-content")
 	sel2 := sel.Last().End()
@@ -63,6 +73,11 @@ func TestEqEmpty(t *testing.T) {
 	assertLength(t, sel.Nodes, 0)
 }
 
+func TestEqInvalid(t *testing.T) {
+	sel := Doc().Find("").Eq(0)
+	assertLength(t, sel.Nodes, 0)
+}
+
 func TestEqInvalidPositive(t *testing.T) {
 	sel := Doc().Find(".pvk-content").Eq(3)
 	assertLength(t, sel.Nodes, 0)
@@ -83,6 +98,32 @@ func TestSlice(t *testing.T) {
 	sel := Doc().Find(".pvk-content").Slice(0, 2)
 
 	assertLength(t, sel.Nodes, 2)
+	assertSelectionIs(t, sel, "#pc1", "#pc2")
+}
+
+func TestSliceToEnd(t *testing.T) {
+	sel := Doc().Find(".pvk-content").Slice(1, ToEnd)
+
+	assertLength(t, sel.Nodes, 2)
+	assertSelectionIs(t, sel.Eq(0), "#pc2")
+	if _, ok := sel.Eq(1).Attr("id"); ok {
+		t.Error("Want no attribute ID, got one")
+	}
+}
+
+func TestSliceEmpty(t *testing.T) {
+	defer assertPanic(t)
+	Doc().Find("x").Slice(0, 2)
+}
+
+func TestSliceInvalid(t *testing.T) {
+	defer assertPanic(t)
+	Doc().Find("").Slice(0, 2)
+}
+
+func TestSliceInvalidToEnd(t *testing.T) {
+	defer assertPanic(t)
+	Doc().Find("").Slice(2, ToEnd)
 }
 
 func TestSliceOutOfBounds(t *testing.T) {
@@ -110,6 +151,12 @@ func TestNegativeSliceBoth(t *testing.T) {
 	assertSelectionIs(t, sel.Eq(1), "#cf3")
 }
 
+func TestNegativeSliceToEnd(t *testing.T) {
+	sel := Doc().Find(".container-fluid").Slice(-3, ToEnd)
+	assertLength(t, sel.Nodes, 3)
+	assertSelectionIs(t, sel, "#cf2", "#cf3", "#cf4")
+}
+
 func TestNegativeSliceOutOfBounds(t *testing.T) {
 	defer assertPanic(t)
 	Doc().Find(".container-fluid").Slice(-12, -7)
@@ -157,6 +204,13 @@ func TestIndexSelector(t *testing.T) {
 	}
 }
 
+func TestIndexSelectorInvalid(t *testing.T) {
+	sel := Doc().Find(".hero-unit")
+	if i := sel.IndexSelector(""); i != -1 {
+		t.Errorf("Expected index of -1, got %v.", i)
+	}
+}
+
 func TestIndexOfNode(t *testing.T) {
 	sel := Doc().Find("div.pvk-gutter")
 	if i := sel.IndexOfNode(sel.Nodes[1]); i != 1 {

+ 436 - 0
src/github.com/PuerkitoBio/goquery/bench/v0.1.0

@@ -0,0 +1,436 @@
+PASS
+BenchmarkFirst	20000000	        92.9 ns/op
+BenchmarkLast	20000000	        91.6 ns/op
+BenchmarkEq	20000000	        90.6 ns/op
+BenchmarkSlice	20000000	        86.7 ns/op
+BenchmarkGet	1000000000	         2.14 ns/op
+BenchmarkIndex	  500000	      5308 ns/op
+--- BENCH: BenchmarkIndex
+	bench_array_test.go:73: Index=3
+	bench_array_test.go:73: Index=3
+	bench_array_test.go:73: Index=3
+	bench_array_test.go:73: Index=3
+BenchmarkIndexSelector	   50000	     54962 ns/op
+--- BENCH: BenchmarkIndexSelector
+	bench_array_test.go:85: IndexSelector=4
+	bench_array_test.go:85: IndexSelector=4
+	bench_array_test.go:85: IndexSelector=4
+	bench_array_test.go:85: IndexSelector=4
+BenchmarkIndexOfNode	100000000	        11.4 ns/op
+--- BENCH: BenchmarkIndexOfNode
+	bench_array_test.go:99: IndexOfNode=2
+	bench_array_test.go:99: IndexOfNode=2
+	bench_array_test.go:99: IndexOfNode=2
+	bench_array_test.go:99: IndexOfNode=2
+	bench_array_test.go:99: IndexOfNode=2
+BenchmarkIndexOfSelection	100000000	        12.1 ns/op
+--- BENCH: BenchmarkIndexOfSelection
+	bench_array_test.go:111: IndexOfSelection=2
+	bench_array_test.go:111: IndexOfSelection=2
+	bench_array_test.go:111: IndexOfSelection=2
+	bench_array_test.go:111: IndexOfSelection=2
+	bench_array_test.go:111: IndexOfSelection=2
+BenchmarkMetalReviewExample	    5000	    327144 ns/op
+--- BENCH: BenchmarkMetalReviewExample
+	bench_example_test.go:40: Review 0: Midnight - Complete and Total Hell (8.5).
+		Review 1: Over Your Threshold - Facticity (6.0).
+		Review 2: Nuclear Death Terror - Chaos Reigns (7.5).
+		Review 3: Evoken - Atra Mors (9.5).
+		
+	bench_example_test.go:41: MetalReviewExample=10
+	bench_example_test.go:40: Review 0: Midnight - Complete and Total Hell (8.5).
+		Review 1: Over Your Threshold - Facticity (6.0).
+		Review 2: Nuclear Death Terror - Chaos Reigns (7.5).
+		Review 3: Evoken - Atra Mors (9.5).
+	... [output truncated]
+BenchmarkAdd	   50000	     52945 ns/op
+--- BENCH: BenchmarkAdd
+	bench_expand_test.go:20: Add=43
+	bench_expand_test.go:20: Add=43
+	bench_expand_test.go:20: Add=43
+	bench_expand_test.go:20: Add=43
+BenchmarkAddSelection	10000000	       205 ns/op
+--- BENCH: BenchmarkAddSelection
+	bench_expand_test.go:37: AddSelection=43
+	bench_expand_test.go:37: AddSelection=43
+	bench_expand_test.go:37: AddSelection=43
+	bench_expand_test.go:37: AddSelection=43
+	bench_expand_test.go:37: AddSelection=43
+BenchmarkAddNodes	10000000	       203 ns/op
+--- BENCH: BenchmarkAddNodes
+	bench_expand_test.go:55: AddNodes=43
+	bench_expand_test.go:55: AddNodes=43
+	bench_expand_test.go:55: AddNodes=43
+	bench_expand_test.go:55: AddNodes=43
+	bench_expand_test.go:55: AddNodes=43
+BenchmarkAndSelf	 1000000	      2639 ns/op
+--- BENCH: BenchmarkAndSelf
+	bench_expand_test.go:71: AndSelf=44
+	bench_expand_test.go:71: AndSelf=44
+	bench_expand_test.go:71: AndSelf=44
+	bench_expand_test.go:71: AndSelf=44
+BenchmarkFilter	   50000	     30182 ns/op
+--- BENCH: BenchmarkFilter
+	bench_filter_test.go:20: Filter=13
+	bench_filter_test.go:20: Filter=13
+	bench_filter_test.go:20: Filter=13
+	bench_filter_test.go:20: Filter=13
+BenchmarkNot	   50000	     34855 ns/op
+--- BENCH: BenchmarkNot
+	bench_filter_test.go:36: Not=371
+	bench_filter_test.go:36: Not=371
+	bench_filter_test.go:36: Not=371
+	bench_filter_test.go:36: Not=371
+BenchmarkFilterFunction	   50000	     66052 ns/op
+--- BENCH: BenchmarkFilterFunction
+	bench_filter_test.go:55: FilterFunction=112
+	bench_filter_test.go:55: FilterFunction=112
+	bench_filter_test.go:55: FilterFunction=112
+	bench_filter_test.go:55: FilterFunction=112
+BenchmarkNotFunction	   50000	     69721 ns/op
+--- BENCH: BenchmarkNotFunction
+	bench_filter_test.go:74: NotFunction=261
+	bench_filter_test.go:74: NotFunction=261
+	bench_filter_test.go:74: NotFunction=261
+	bench_filter_test.go:74: NotFunction=261
+BenchmarkFilterNodes	   50000	     66077 ns/op
+--- BENCH: BenchmarkFilterNodes
+	bench_filter_test.go:92: FilterNodes=2
+	bench_filter_test.go:92: FilterNodes=2
+	bench_filter_test.go:92: FilterNodes=2
+	bench_filter_test.go:92: FilterNodes=2
+BenchmarkNotNodes	   20000	     80021 ns/op
+--- BENCH: BenchmarkNotNodes
+	bench_filter_test.go:110: NotNodes=360
+	bench_filter_test.go:110: NotNodes=360
+	bench_filter_test.go:110: NotNodes=360
+	bench_filter_test.go:110: NotNodes=360
+BenchmarkFilterSelection	   50000	     66256 ns/op
+--- BENCH: BenchmarkFilterSelection
+	bench_filter_test.go:127: FilterSelection=2
+	bench_filter_test.go:127: FilterSelection=2
+	bench_filter_test.go:127: FilterSelection=2
+	bench_filter_test.go:127: FilterSelection=2
+BenchmarkNotSelection	   20000	     79568 ns/op
+--- BENCH: BenchmarkNotSelection
+	bench_filter_test.go:144: NotSelection=360
+	bench_filter_test.go:144: NotSelection=360
+	bench_filter_test.go:144: NotSelection=360
+	bench_filter_test.go:144: NotSelection=360
+BenchmarkHas	    5000	    569441 ns/op
+--- BENCH: BenchmarkHas
+	bench_filter_test.go:160: Has=13
+	bench_filter_test.go:160: Has=13
+	bench_filter_test.go:160: Has=13
+BenchmarkHasNodes	   10000	    230585 ns/op
+--- BENCH: BenchmarkHasNodes
+	bench_filter_test.go:178: HasNodes=15
+	bench_filter_test.go:178: HasNodes=15
+	bench_filter_test.go:178: HasNodes=15
+BenchmarkHasSelection	   10000	    231470 ns/op
+--- BENCH: BenchmarkHasSelection
+	bench_filter_test.go:195: HasSelection=15
+	bench_filter_test.go:195: HasSelection=15
+	bench_filter_test.go:195: HasSelection=15
+BenchmarkEnd	500000000	         4.65 ns/op
+--- BENCH: BenchmarkEnd
+	bench_filter_test.go:211: End=373
+	bench_filter_test.go:211: End=373
+	bench_filter_test.go:211: End=373
+	bench_filter_test.go:211: End=373
+	bench_filter_test.go:211: End=373
+	bench_filter_test.go:211: End=373
+BenchmarkEach	  200000	      9558 ns/op
+--- BENCH: BenchmarkEach
+	bench_iteration_test.go:22: Each=59
+	bench_iteration_test.go:22: Each=59
+	bench_iteration_test.go:22: Each=59
+	bench_iteration_test.go:22: Each=59
+BenchmarkMap	  100000	     16809 ns/op
+--- BENCH: BenchmarkMap
+	bench_iteration_test.go:41: Map=59
+	bench_iteration_test.go:41: Map=59
+	bench_iteration_test.go:41: Map=59
+	bench_iteration_test.go:41: Map=59
+BenchmarkAttr	50000000	        37.5 ns/op
+--- BENCH: BenchmarkAttr
+	bench_property_test.go:16: Attr=firstHeading
+	bench_property_test.go:16: Attr=firstHeading
+	bench_property_test.go:16: Attr=firstHeading
+	bench_property_test.go:16: Attr=firstHeading
+	bench_property_test.go:16: Attr=firstHeading
+BenchmarkText	  100000	     18583 ns/op
+BenchmarkLength	2000000000	         0.80 ns/op
+--- BENCH: BenchmarkLength
+	bench_property_test.go:37: Length=14
+	bench_property_test.go:37: Length=14
+	bench_property_test.go:37: Length=14
+	bench_property_test.go:37: Length=14
+	bench_property_test.go:37: Length=14
+	bench_property_test.go:37: Length=14
+BenchmarkHtml	 5000000	       666 ns/op
+BenchmarkIs	   50000	     34328 ns/op
+--- BENCH: BenchmarkIs
+	bench_query_test.go:16: Is=true
+	bench_query_test.go:16: Is=true
+	bench_query_test.go:16: Is=true
+	bench_query_test.go:16: Is=true
+BenchmarkIsPositional	   50000	     32423 ns/op
+--- BENCH: BenchmarkIsPositional
+	bench_query_test.go:28: IsPositional=true
+	bench_query_test.go:28: IsPositional=true
+	bench_query_test.go:28: IsPositional=true
+	bench_query_test.go:28: IsPositional=true
+BenchmarkIsFunction	 1000000	      2707 ns/op
+--- BENCH: BenchmarkIsFunction
+	bench_query_test.go:43: IsFunction=true
+	bench_query_test.go:43: IsFunction=true
+	bench_query_test.go:43: IsFunction=true
+	bench_query_test.go:43: IsFunction=true
+BenchmarkIsSelection	   50000	     66976 ns/op
+--- BENCH: BenchmarkIsSelection
+	bench_query_test.go:56: IsSelection=true
+	bench_query_test.go:56: IsSelection=true
+	bench_query_test.go:56: IsSelection=true
+	bench_query_test.go:56: IsSelection=true
+BenchmarkIsNodes	   50000	     66740 ns/op
+--- BENCH: BenchmarkIsNodes
+	bench_query_test.go:70: IsNodes=true
+	bench_query_test.go:70: IsNodes=true
+	bench_query_test.go:70: IsNodes=true
+	bench_query_test.go:70: IsNodes=true
+BenchmarkHasClass	    5000	    701722 ns/op
+--- BENCH: BenchmarkHasClass
+	bench_query_test.go:82: HasClass=true
+	bench_query_test.go:82: HasClass=true
+	bench_query_test.go:82: HasClass=true
+BenchmarkContains	100000000	        11.9 ns/op
+--- BENCH: BenchmarkContains
+	bench_query_test.go:96: Contains=true
+	bench_query_test.go:96: Contains=true
+	bench_query_test.go:96: Contains=true
+	bench_query_test.go:96: Contains=true
+	bench_query_test.go:96: Contains=true
+BenchmarkFind	   50000	     55444 ns/op
+--- BENCH: BenchmarkFind
+	bench_traversal_test.go:18: Find=41
+	bench_traversal_test.go:18: Find=41
+	bench_traversal_test.go:18: Find=41
+	bench_traversal_test.go:18: Find=41
+BenchmarkFindWithinSelection	   10000	    127984 ns/op
+--- BENCH: BenchmarkFindWithinSelection
+	bench_traversal_test.go:34: FindWithinSelection=39
+	bench_traversal_test.go:34: FindWithinSelection=39
+	bench_traversal_test.go:34: FindWithinSelection=39
+BenchmarkFindSelection	    5000	    355944 ns/op
+--- BENCH: BenchmarkFindSelection
+	bench_traversal_test.go:51: FindSelection=73
+	bench_traversal_test.go:51: FindSelection=73
+	bench_traversal_test.go:51: FindSelection=73
+BenchmarkFindNodes	    5000	    355596 ns/op
+--- BENCH: BenchmarkFindNodes
+	bench_traversal_test.go:69: FindNodes=73
+	bench_traversal_test.go:69: FindNodes=73
+	bench_traversal_test.go:69: FindNodes=73
+BenchmarkContents	  500000	      5656 ns/op
+--- BENCH: BenchmarkContents
+	bench_traversal_test.go:85: Contents=16
+	bench_traversal_test.go:85: Contents=16
+	bench_traversal_test.go:85: Contents=16
+	bench_traversal_test.go:85: Contents=16
+BenchmarkContentsFiltered	  200000	      9007 ns/op
+--- BENCH: BenchmarkContentsFiltered
+	bench_traversal_test.go:101: ContentsFiltered=1
+	bench_traversal_test.go:101: ContentsFiltered=1
+	bench_traversal_test.go:101: ContentsFiltered=1
+	bench_traversal_test.go:101: ContentsFiltered=1
+BenchmarkChildren	 1000000	      1237 ns/op
+--- BENCH: BenchmarkChildren
+	bench_traversal_test.go:117: Children=2
+	bench_traversal_test.go:117: Children=2
+	bench_traversal_test.go:117: Children=2
+	bench_traversal_test.go:117: Children=2
+BenchmarkChildrenFiltered	  500000	      5613 ns/op
+--- BENCH: BenchmarkChildrenFiltered
+	bench_traversal_test.go:133: ChildrenFiltered=2
+	bench_traversal_test.go:133: ChildrenFiltered=2
+	bench_traversal_test.go:133: ChildrenFiltered=2
+	bench_traversal_test.go:133: ChildrenFiltered=2
+BenchmarkParent	   50000	     47026 ns/op
+--- BENCH: BenchmarkParent
+	bench_traversal_test.go:149: Parent=55
+	bench_traversal_test.go:149: Parent=55
+	bench_traversal_test.go:149: Parent=55
+	bench_traversal_test.go:149: Parent=55
+BenchmarkParentFiltered	   50000	     51438 ns/op
+--- BENCH: BenchmarkParentFiltered
+	bench_traversal_test.go:165: ParentFiltered=4
+	bench_traversal_test.go:165: ParentFiltered=4
+	bench_traversal_test.go:165: ParentFiltered=4
+	bench_traversal_test.go:165: ParentFiltered=4
+BenchmarkParents	   20000	     91820 ns/op
+--- BENCH: BenchmarkParents
+	bench_traversal_test.go:181: Parents=73
+	bench_traversal_test.go:181: Parents=73
+	bench_traversal_test.go:181: Parents=73
+	bench_traversal_test.go:181: Parents=73
+BenchmarkParentsFiltered	   20000	     95156 ns/op
+--- BENCH: BenchmarkParentsFiltered
+	bench_traversal_test.go:197: ParentsFiltered=18
+	bench_traversal_test.go:197: ParentsFiltered=18
+	bench_traversal_test.go:197: ParentsFiltered=18
+	bench_traversal_test.go:197: ParentsFiltered=18
+BenchmarkParentsUntil	   10000	    134383 ns/op
+--- BENCH: BenchmarkParentsUntil
+	bench_traversal_test.go:213: ParentsUntil=52
+	bench_traversal_test.go:213: ParentsUntil=52
+	bench_traversal_test.go:213: ParentsUntil=52
+BenchmarkParentsUntilSelection	   10000	    235456 ns/op
+--- BENCH: BenchmarkParentsUntilSelection
+	bench_traversal_test.go:230: ParentsUntilSelection=70
+	bench_traversal_test.go:230: ParentsUntilSelection=70
+	bench_traversal_test.go:230: ParentsUntilSelection=70
+BenchmarkParentsUntilNodes	   10000	    235936 ns/op
+--- BENCH: BenchmarkParentsUntilNodes
+	bench_traversal_test.go:248: ParentsUntilNodes=70
+	bench_traversal_test.go:248: ParentsUntilNodes=70
+	bench_traversal_test.go:248: ParentsUntilNodes=70
+BenchmarkParentsFilteredUntil	   50000	     32451 ns/op
+--- BENCH: BenchmarkParentsFilteredUntil
+	bench_traversal_test.go:264: ParentsFilteredUntil=2
+	bench_traversal_test.go:264: ParentsFilteredUntil=2
+	bench_traversal_test.go:264: ParentsFilteredUntil=2
+	bench_traversal_test.go:264: ParentsFilteredUntil=2
+BenchmarkParentsFilteredUntilSelection	   50000	     30570 ns/op
+--- BENCH: BenchmarkParentsFilteredUntilSelection
+	bench_traversal_test.go:281: ParentsFilteredUntilSelection=2
+	bench_traversal_test.go:281: ParentsFilteredUntilSelection=2
+	bench_traversal_test.go:281: ParentsFilteredUntilSelection=2
+	bench_traversal_test.go:281: ParentsFilteredUntilSelection=2
+BenchmarkParentsFilteredUntilNodes	   50000	     30729 ns/op
+--- BENCH: BenchmarkParentsFilteredUntilNodes
+	bench_traversal_test.go:299: ParentsFilteredUntilNodes=2
+	bench_traversal_test.go:299: ParentsFilteredUntilNodes=2
+	bench_traversal_test.go:299: ParentsFilteredUntilNodes=2
+	bench_traversal_test.go:299: ParentsFilteredUntilNodes=2
+BenchmarkSiblings	   10000	    106704 ns/op
+--- BENCH: BenchmarkSiblings
+	bench_traversal_test.go:315: Siblings=293
+	bench_traversal_test.go:315: Siblings=293
+	bench_traversal_test.go:315: Siblings=293
+BenchmarkSiblingsFiltered	   10000	    115592 ns/op
+--- BENCH: BenchmarkSiblingsFiltered
+	bench_traversal_test.go:331: SiblingsFiltered=46
+	bench_traversal_test.go:331: SiblingsFiltered=46
+	bench_traversal_test.go:331: SiblingsFiltered=46
+BenchmarkNext	   50000	     54449 ns/op
+--- BENCH: BenchmarkNext
+	bench_traversal_test.go:347: Next=49
+	bench_traversal_test.go:347: Next=49
+	bench_traversal_test.go:347: Next=49
+	bench_traversal_test.go:347: Next=49
+BenchmarkNextFiltered	   50000	     58503 ns/op
+--- BENCH: BenchmarkNextFiltered
+	bench_traversal_test.go:363: NextFiltered=6
+	bench_traversal_test.go:363: NextFiltered=6
+	bench_traversal_test.go:363: NextFiltered=6
+	bench_traversal_test.go:363: NextFiltered=6
+BenchmarkNextAll	   20000	     77698 ns/op
+--- BENCH: BenchmarkNextAll
+	bench_traversal_test.go:379: NextAll=234
+	bench_traversal_test.go:379: NextAll=234
+	bench_traversal_test.go:379: NextAll=234
+	bench_traversal_test.go:379: NextAll=234
+BenchmarkNextAllFiltered	   20000	     85034 ns/op
+--- BENCH: BenchmarkNextAllFiltered
+	bench_traversal_test.go:395: NextAllFiltered=33
+	bench_traversal_test.go:395: NextAllFiltered=33
+	bench_traversal_test.go:395: NextAllFiltered=33
+	bench_traversal_test.go:395: NextAllFiltered=33
+BenchmarkPrev	   50000	     56458 ns/op
+--- BENCH: BenchmarkPrev
+	bench_traversal_test.go:411: Prev=49
+	bench_traversal_test.go:411: Prev=49
+	bench_traversal_test.go:411: Prev=49
+	bench_traversal_test.go:411: Prev=49
+BenchmarkPrevFiltered	   50000	     60163 ns/op
+--- BENCH: BenchmarkPrevFiltered
+	bench_traversal_test.go:429: PrevFiltered=7
+	bench_traversal_test.go:429: PrevFiltered=7
+	bench_traversal_test.go:429: PrevFiltered=7
+	bench_traversal_test.go:429: PrevFiltered=7
+BenchmarkPrevAll	   50000	     47679 ns/op
+--- BENCH: BenchmarkPrevAll
+	bench_traversal_test.go:445: PrevAll=78
+	bench_traversal_test.go:445: PrevAll=78
+	bench_traversal_test.go:445: PrevAll=78
+	bench_traversal_test.go:445: PrevAll=78
+BenchmarkPrevAllFiltered	   50000	     51563 ns/op
+--- BENCH: BenchmarkPrevAllFiltered
+	bench_traversal_test.go:461: PrevAllFiltered=6
+	bench_traversal_test.go:461: PrevAllFiltered=6
+	bench_traversal_test.go:461: PrevAllFiltered=6
+	bench_traversal_test.go:461: PrevAllFiltered=6
+BenchmarkNextUntil	   10000	    213998 ns/op
+--- BENCH: BenchmarkNextUntil
+	bench_traversal_test.go:477: NextUntil=84
+	bench_traversal_test.go:477: NextUntil=84
+	bench_traversal_test.go:477: NextUntil=84
+BenchmarkNextUntilSelection	   10000	    140720 ns/op
+--- BENCH: BenchmarkNextUntilSelection
+	bench_traversal_test.go:494: NextUntilSelection=42
+	bench_traversal_test.go:494: NextUntilSelection=42
+	bench_traversal_test.go:494: NextUntilSelection=42
+BenchmarkNextUntilNodes	   20000	     90702 ns/op
+--- BENCH: BenchmarkNextUntilNodes
+	bench_traversal_test.go:512: NextUntilNodes=12
+	bench_traversal_test.go:512: NextUntilNodes=12
+	bench_traversal_test.go:512: NextUntilNodes=12
+	bench_traversal_test.go:512: NextUntilNodes=12
+BenchmarkPrevUntil	    5000	    456039 ns/op
+--- BENCH: BenchmarkPrevUntil
+	bench_traversal_test.go:528: PrevUntil=238
+	bench_traversal_test.go:528: PrevUntil=238
+	bench_traversal_test.go:528: PrevUntil=238
+BenchmarkPrevUntilSelection	   10000	    167944 ns/op
+--- BENCH: BenchmarkPrevUntilSelection
+	bench_traversal_test.go:545: PrevUntilSelection=49
+	bench_traversal_test.go:545: PrevUntilSelection=49
+	bench_traversal_test.go:545: PrevUntilSelection=49
+BenchmarkPrevUntilNodes	   20000	     82059 ns/op
+--- BENCH: BenchmarkPrevUntilNodes
+	bench_traversal_test.go:563: PrevUntilNodes=11
+	bench_traversal_test.go:563: PrevUntilNodes=11
+	bench_traversal_test.go:563: PrevUntilNodes=11
+	bench_traversal_test.go:563: PrevUntilNodes=11
+BenchmarkNextFilteredUntil	   10000	    150883 ns/op
+--- BENCH: BenchmarkNextFilteredUntil
+	bench_traversal_test.go:579: NextFilteredUntil=22
+	bench_traversal_test.go:579: NextFilteredUntil=22
+	bench_traversal_test.go:579: NextFilteredUntil=22
+BenchmarkNextFilteredUntilSelection	   10000	    146578 ns/op
+--- BENCH: BenchmarkNextFilteredUntilSelection
+	bench_traversal_test.go:596: NextFilteredUntilSelection=22
+	bench_traversal_test.go:596: NextFilteredUntilSelection=22
+	bench_traversal_test.go:596: NextFilteredUntilSelection=22
+BenchmarkNextFilteredUntilNodes	   10000	    148284 ns/op
+--- BENCH: BenchmarkNextFilteredUntilNodes
+	bench_traversal_test.go:614: NextFilteredUntilNodes=22
+	bench_traversal_test.go:614: NextFilteredUntilNodes=22
+	bench_traversal_test.go:614: NextFilteredUntilNodes=22
+BenchmarkPrevFilteredUntil	   10000	    154303 ns/op
+--- BENCH: BenchmarkPrevFilteredUntil
+	bench_traversal_test.go:630: PrevFilteredUntil=20
+	bench_traversal_test.go:630: PrevFilteredUntil=20
+	bench_traversal_test.go:630: PrevFilteredUntil=20
+BenchmarkPrevFilteredUntilSelection	   10000	    149062 ns/op
+--- BENCH: BenchmarkPrevFilteredUntilSelection
+	bench_traversal_test.go:647: PrevFilteredUntilSelection=20
+	bench_traversal_test.go:647: PrevFilteredUntilSelection=20
+	bench_traversal_test.go:647: PrevFilteredUntilSelection=20
+BenchmarkPrevFilteredUntilNodes	   10000	    150584 ns/op
+--- BENCH: BenchmarkPrevFilteredUntilNodes
+	bench_traversal_test.go:665: PrevFilteredUntilNodes=20
+	bench_traversal_test.go:665: PrevFilteredUntilNodes=20
+	bench_traversal_test.go:665: PrevFilteredUntilNodes=20
+ok  	github.com/PuerkitoBio/goquery	188.326s

+ 438 - 0
src/github.com/PuerkitoBio/goquery/bench/v0.1.1

@@ -0,0 +1,438 @@
+PASS
+BenchmarkFirst	20000000	        96.2 ns/op
+BenchmarkLast	20000000	        95.8 ns/op
+BenchmarkEq	20000000	        94.4 ns/op
+BenchmarkSlice	20000000	        89.9 ns/op
+BenchmarkGet	1000000000	         2.31 ns/op
+BenchmarkIndex	 1000000	      1911 ns/op
+--- BENCH: BenchmarkIndex
+	bench_array_test.go:73: Index=3
+	bench_array_test.go:73: Index=3
+	bench_array_test.go:73: Index=3
+	bench_array_test.go:73: Index=3
+BenchmarkIndexSelector	   50000	     56034 ns/op
+--- BENCH: BenchmarkIndexSelector
+	bench_array_test.go:85: IndexSelector=4
+	bench_array_test.go:85: IndexSelector=4
+	bench_array_test.go:85: IndexSelector=4
+	bench_array_test.go:85: IndexSelector=4
+BenchmarkIndexOfNode	100000000	        11.8 ns/op
+--- BENCH: BenchmarkIndexOfNode
+	bench_array_test.go:99: IndexOfNode=2
+	bench_array_test.go:99: IndexOfNode=2
+	bench_array_test.go:99: IndexOfNode=2
+	bench_array_test.go:99: IndexOfNode=2
+	bench_array_test.go:99: IndexOfNode=2
+BenchmarkIndexOfSelection	100000000	        12.1 ns/op
+--- BENCH: BenchmarkIndexOfSelection
+	bench_array_test.go:111: IndexOfSelection=2
+	bench_array_test.go:111: IndexOfSelection=2
+	bench_array_test.go:111: IndexOfSelection=2
+	bench_array_test.go:111: IndexOfSelection=2
+	bench_array_test.go:111: IndexOfSelection=2
+BenchmarkMetalReviewExample	    5000	    336823 ns/op
+--- BENCH: BenchmarkMetalReviewExample
+	bench_example_test.go:40: Review 0: Midnight - Complete and Total Hell (8.5).
+		Review 1: Over Your Threshold - Facticity (6.0).
+		Review 2: Nuclear Death Terror - Chaos Reigns (7.5).
+		Review 3: Evoken - Atra Mors (9.5).
+		
+	bench_example_test.go:41: MetalReviewExample=10
+	bench_example_test.go:40: Review 0: Midnight - Complete and Total Hell (8.5).
+		Review 1: Over Your Threshold - Facticity (6.0).
+		Review 2: Nuclear Death Terror - Chaos Reigns (7.5).
+		Review 3: Evoken - Atra Mors (9.5).
+	... [output truncated]
+BenchmarkAdd	   50000	     54709 ns/op
+--- BENCH: BenchmarkAdd
+	bench_expand_test.go:20: Add=43
+	bench_expand_test.go:20: Add=43
+	bench_expand_test.go:20: Add=43
+	bench_expand_test.go:20: Add=43
+BenchmarkAddSelection	10000000	       209 ns/op
+--- BENCH: BenchmarkAddSelection
+	bench_expand_test.go:37: AddSelection=43
+	bench_expand_test.go:37: AddSelection=43
+	bench_expand_test.go:37: AddSelection=43
+	bench_expand_test.go:37: AddSelection=43
+	bench_expand_test.go:37: AddSelection=43
+BenchmarkAddNodes	10000000	       202 ns/op
+--- BENCH: BenchmarkAddNodes
+	bench_expand_test.go:55: AddNodes=43
+	bench_expand_test.go:55: AddNodes=43
+	bench_expand_test.go:55: AddNodes=43
+	bench_expand_test.go:55: AddNodes=43
+	bench_expand_test.go:55: AddNodes=43
+BenchmarkAndSelf	 1000000	      2634 ns/op
+--- BENCH: BenchmarkAndSelf
+	bench_expand_test.go:71: AndSelf=44
+	bench_expand_test.go:71: AndSelf=44
+	bench_expand_test.go:71: AndSelf=44
+	bench_expand_test.go:71: AndSelf=44
+BenchmarkFilter	   50000	     31049 ns/op
+--- BENCH: BenchmarkFilter
+	bench_filter_test.go:20: Filter=13
+	bench_filter_test.go:20: Filter=13
+	bench_filter_test.go:20: Filter=13
+	bench_filter_test.go:20: Filter=13
+BenchmarkNot	   50000	     35167 ns/op
+--- BENCH: BenchmarkNot
+	bench_filter_test.go:36: Not=371
+	bench_filter_test.go:36: Not=371
+	bench_filter_test.go:36: Not=371
+	bench_filter_test.go:36: Not=371
+BenchmarkFilterFunction	   50000	     68974 ns/op
+--- BENCH: BenchmarkFilterFunction
+	bench_filter_test.go:55: FilterFunction=112
+	bench_filter_test.go:55: FilterFunction=112
+	bench_filter_test.go:55: FilterFunction=112
+	bench_filter_test.go:55: FilterFunction=112
+BenchmarkNotFunction	   50000	     74760 ns/op
+--- BENCH: BenchmarkNotFunction
+	bench_filter_test.go:74: NotFunction=261
+	bench_filter_test.go:74: NotFunction=261
+	bench_filter_test.go:74: NotFunction=261
+	bench_filter_test.go:74: NotFunction=261
+BenchmarkFilterNodes	   50000	     68670 ns/op
+--- BENCH: BenchmarkFilterNodes
+	bench_filter_test.go:92: FilterNodes=2
+	bench_filter_test.go:92: FilterNodes=2
+	bench_filter_test.go:92: FilterNodes=2
+	bench_filter_test.go:92: FilterNodes=2
+BenchmarkNotNodes	   20000	     81357 ns/op
+--- BENCH: BenchmarkNotNodes
+	bench_filter_test.go:110: NotNodes=360
+	bench_filter_test.go:110: NotNodes=360
+	bench_filter_test.go:110: NotNodes=360
+	bench_filter_test.go:110: NotNodes=360
+BenchmarkFilterSelection	   50000	     68388 ns/op
+--- BENCH: BenchmarkFilterSelection
+	bench_filter_test.go:127: FilterSelection=2
+	bench_filter_test.go:127: FilterSelection=2
+	bench_filter_test.go:127: FilterSelection=2
+	bench_filter_test.go:127: FilterSelection=2
+BenchmarkNotSelection	   20000	     82108 ns/op
+--- BENCH: BenchmarkNotSelection
+	bench_filter_test.go:144: NotSelection=360
+	bench_filter_test.go:144: NotSelection=360
+	bench_filter_test.go:144: NotSelection=360
+	bench_filter_test.go:144: NotSelection=360
+BenchmarkHas	    5000	    582934 ns/op
+--- BENCH: BenchmarkHas
+	bench_filter_test.go:160: Has=13
+	bench_filter_test.go:160: Has=13
+	bench_filter_test.go:160: Has=13
+BenchmarkHasNodes	   10000	    241602 ns/op
+--- BENCH: BenchmarkHasNodes
+	bench_filter_test.go:178: HasNodes=15
+	bench_filter_test.go:178: HasNodes=15
+	bench_filter_test.go:178: HasNodes=15
+BenchmarkHasSelection	   10000	    243612 ns/op
+--- BENCH: BenchmarkHasSelection
+	bench_filter_test.go:195: HasSelection=15
+	bench_filter_test.go:195: HasSelection=15
+	bench_filter_test.go:195: HasSelection=15
+BenchmarkEnd	500000000	         4.14 ns/op
+--- BENCH: BenchmarkEnd
+	bench_filter_test.go:211: End=373
+	bench_filter_test.go:211: End=373
+	bench_filter_test.go:211: End=373
+	bench_filter_test.go:211: End=373
+	bench_filter_test.go:211: End=373
+	bench_filter_test.go:211: End=373
+BenchmarkEach	  200000	      9848 ns/op
+--- BENCH: BenchmarkEach
+	bench_iteration_test.go:22: Each=59
+	bench_iteration_test.go:22: Each=59
+	bench_iteration_test.go:22: Each=59
+	bench_iteration_test.go:22: Each=59
+BenchmarkMap	  100000	     17569 ns/op
+--- BENCH: BenchmarkMap
+	bench_iteration_test.go:41: Map=59
+	bench_iteration_test.go:41: Map=59
+	bench_iteration_test.go:41: Map=59
+	bench_iteration_test.go:41: Map=59
+BenchmarkAttr	50000000	        37.6 ns/op
+--- BENCH: BenchmarkAttr
+	bench_property_test.go:16: Attr=firstHeading
+	bench_property_test.go:16: Attr=firstHeading
+	bench_property_test.go:16: Attr=firstHeading
+	bench_property_test.go:16: Attr=firstHeading
+	bench_property_test.go:16: Attr=firstHeading
+BenchmarkText	  100000	     19345 ns/op
+BenchmarkLength	2000000000	         0.80 ns/op
+--- BENCH: BenchmarkLength
+	bench_property_test.go:37: Length=14
+	bench_property_test.go:37: Length=14
+	bench_property_test.go:37: Length=14
+	bench_property_test.go:37: Length=14
+	bench_property_test.go:37: Length=14
+	bench_property_test.go:37: Length=14
+BenchmarkHtml	 5000000	       688 ns/op
+BenchmarkIs	   50000	     35061 ns/op
+--- BENCH: BenchmarkIs
+	bench_query_test.go:16: Is=true
+	bench_query_test.go:16: Is=true
+	bench_query_test.go:16: Is=true
+	bench_query_test.go:16: Is=true
+BenchmarkIsPositional	   50000	     32789 ns/op
+--- BENCH: BenchmarkIsPositional
+	bench_query_test.go:28: IsPositional=true
+	bench_query_test.go:28: IsPositional=true
+	bench_query_test.go:28: IsPositional=true
+	bench_query_test.go:28: IsPositional=true
+BenchmarkIsFunction	 1000000	      2816 ns/op
+--- BENCH: BenchmarkIsFunction
+	bench_query_test.go:43: IsFunction=true
+	bench_query_test.go:43: IsFunction=true
+	bench_query_test.go:43: IsFunction=true
+	bench_query_test.go:43: IsFunction=true
+BenchmarkIsSelection	   50000	     68272 ns/op
+--- BENCH: BenchmarkIsSelection
+	bench_query_test.go:56: IsSelection=true
+	bench_query_test.go:56: IsSelection=true
+	bench_query_test.go:56: IsSelection=true
+	bench_query_test.go:56: IsSelection=true
+BenchmarkIsNodes	   50000	     68107 ns/op
+--- BENCH: BenchmarkIsNodes
+	bench_query_test.go:70: IsNodes=true
+	bench_query_test.go:70: IsNodes=true
+	bench_query_test.go:70: IsNodes=true
+	bench_query_test.go:70: IsNodes=true
+BenchmarkHasClass	    5000	    709386 ns/op
+--- BENCH: BenchmarkHasClass
+	bench_query_test.go:82: HasClass=true
+	bench_query_test.go:82: HasClass=true
+	bench_query_test.go:82: HasClass=true
+BenchmarkContains	100000000	        12.4 ns/op
+--- BENCH: BenchmarkContains
+	bench_query_test.go:96: Contains=true
+	bench_query_test.go:96: Contains=true
+	bench_query_test.go:96: Contains=true
+	bench_query_test.go:96: Contains=true
+	bench_query_test.go:96: Contains=true
+BenchmarkFind	   50000	     56342 ns/op
+--- BENCH: BenchmarkFind
+	bench_traversal_test.go:18: Find=41
+	bench_traversal_test.go:18: Find=41
+	bench_traversal_test.go:18: Find=41
+	bench_traversal_test.go:18: Find=41
+BenchmarkFindWithinSelection	   10000	    131878 ns/op
+--- BENCH: BenchmarkFindWithinSelection
+	bench_traversal_test.go:34: FindWithinSelection=39
+	bench_traversal_test.go:34: FindWithinSelection=39
+	bench_traversal_test.go:34: FindWithinSelection=39
+BenchmarkFindSelection	    5000	    374240 ns/op
+--- BENCH: BenchmarkFindSelection
+	bench_traversal_test.go:51: FindSelection=73
+	bench_traversal_test.go:51: FindSelection=73
+	bench_traversal_test.go:51: FindSelection=73
+BenchmarkFindNodes	    5000	    374447 ns/op
+--- BENCH: BenchmarkFindNodes
+	bench_traversal_test.go:69: FindNodes=73
+	bench_traversal_test.go:69: FindNodes=73
+	bench_traversal_test.go:69: FindNodes=73
+BenchmarkContents	  200000	      9721 ns/op
+--- BENCH: BenchmarkContents
+	bench_traversal_test.go:85: Contents=16
+	bench_traversal_test.go:85: Contents=16
+	bench_traversal_test.go:85: Contents=16
+	bench_traversal_test.go:85: Contents=16
+BenchmarkContentsFiltered	  200000	     12909 ns/op
+--- BENCH: BenchmarkContentsFiltered
+	bench_traversal_test.go:101: ContentsFiltered=1
+	bench_traversal_test.go:101: ContentsFiltered=1
+	bench_traversal_test.go:101: ContentsFiltered=1
+	bench_traversal_test.go:101: ContentsFiltered=1
+BenchmarkChildren	 1000000	      1869 ns/op
+--- BENCH: BenchmarkChildren
+	bench_traversal_test.go:117: Children=2
+	bench_traversal_test.go:117: Children=2
+	bench_traversal_test.go:117: Children=2
+	bench_traversal_test.go:117: Children=2
+BenchmarkChildrenFiltered	  500000	      5941 ns/op
+--- BENCH: BenchmarkChildrenFiltered
+	bench_traversal_test.go:133: ChildrenFiltered=2
+	bench_traversal_test.go:133: ChildrenFiltered=2
+	bench_traversal_test.go:133: ChildrenFiltered=2
+	bench_traversal_test.go:133: ChildrenFiltered=2
+BenchmarkParent	   50000	     46223 ns/op
+--- BENCH: BenchmarkParent
+	bench_traversal_test.go:149: Parent=55
+	bench_traversal_test.go:149: Parent=55
+	bench_traversal_test.go:149: Parent=55
+	bench_traversal_test.go:149: Parent=55
+BenchmarkParentFiltered	   50000	     51452 ns/op
+--- BENCH: BenchmarkParentFiltered
+	bench_traversal_test.go:165: ParentFiltered=4
+	bench_traversal_test.go:165: ParentFiltered=4
+	bench_traversal_test.go:165: ParentFiltered=4
+	bench_traversal_test.go:165: ParentFiltered=4
+BenchmarkParents	   20000	     93967 ns/op
+--- BENCH: BenchmarkParents
+	bench_traversal_test.go:181: Parents=73
+	bench_traversal_test.go:181: Parents=73
+	bench_traversal_test.go:181: Parents=73
+	bench_traversal_test.go:181: Parents=73
+BenchmarkParentsFiltered	   20000	     97617 ns/op
+--- BENCH: BenchmarkParentsFiltered
+	bench_traversal_test.go:197: ParentsFiltered=18
+	bench_traversal_test.go:197: ParentsFiltered=18
+	bench_traversal_test.go:197: ParentsFiltered=18
+	bench_traversal_test.go:197: ParentsFiltered=18
+BenchmarkParentsUntil	   10000	    138898 ns/op
+--- BENCH: BenchmarkParentsUntil
+	bench_traversal_test.go:213: ParentsUntil=52
+	bench_traversal_test.go:213: ParentsUntil=52
+	bench_traversal_test.go:213: ParentsUntil=52
+BenchmarkParentsUntilSelection	   10000	    247817 ns/op
+--- BENCH: BenchmarkParentsUntilSelection
+	bench_traversal_test.go:230: ParentsUntilSelection=70
+	bench_traversal_test.go:230: ParentsUntilSelection=70
+	bench_traversal_test.go:230: ParentsUntilSelection=70
+BenchmarkParentsUntilNodes	   10000	    246055 ns/op
+--- BENCH: BenchmarkParentsUntilNodes
+	bench_traversal_test.go:248: ParentsUntilNodes=70
+	bench_traversal_test.go:248: ParentsUntilNodes=70
+	bench_traversal_test.go:248: ParentsUntilNodes=70
+BenchmarkParentsFilteredUntil	   50000	     33201 ns/op
+--- BENCH: BenchmarkParentsFilteredUntil
+	bench_traversal_test.go:264: ParentsFilteredUntil=2
+	bench_traversal_test.go:264: ParentsFilteredUntil=2
+	bench_traversal_test.go:264: ParentsFilteredUntil=2
+	bench_traversal_test.go:264: ParentsFilteredUntil=2
+BenchmarkParentsFilteredUntilSelection	   50000	     31486 ns/op
+--- BENCH: BenchmarkParentsFilteredUntilSelection
+	bench_traversal_test.go:281: ParentsFilteredUntilSelection=2
+	bench_traversal_test.go:281: ParentsFilteredUntilSelection=2
+	bench_traversal_test.go:281: ParentsFilteredUntilSelection=2
+	bench_traversal_test.go:281: ParentsFilteredUntilSelection=2
+BenchmarkParentsFilteredUntilNodes	   50000	     31754 ns/op
+--- BENCH: BenchmarkParentsFilteredUntilNodes
+	bench_traversal_test.go:299: ParentsFilteredUntilNodes=2
+	bench_traversal_test.go:299: ParentsFilteredUntilNodes=2
+	bench_traversal_test.go:299: ParentsFilteredUntilNodes=2
+	bench_traversal_test.go:299: ParentsFilteredUntilNodes=2
+BenchmarkSiblings	   20000	     94749 ns/op
+--- BENCH: BenchmarkSiblings
+	bench_traversal_test.go:315: Siblings=293
+	bench_traversal_test.go:315: Siblings=293
+	bench_traversal_test.go:315: Siblings=293
+	bench_traversal_test.go:315: Siblings=293
+BenchmarkSiblingsFiltered	   10000	    103926 ns/op
+--- BENCH: BenchmarkSiblingsFiltered
+	bench_traversal_test.go:331: SiblingsFiltered=46
+	bench_traversal_test.go:331: SiblingsFiltered=46
+	bench_traversal_test.go:331: SiblingsFiltered=46
+BenchmarkNext	   50000	     33782 ns/op
+--- BENCH: BenchmarkNext
+	bench_traversal_test.go:347: Next=49
+	bench_traversal_test.go:347: Next=49
+	bench_traversal_test.go:347: Next=49
+	bench_traversal_test.go:347: Next=49
+BenchmarkNextFiltered	   50000	     37108 ns/op
+--- BENCH: BenchmarkNextFiltered
+	bench_traversal_test.go:363: NextFiltered=6
+	bench_traversal_test.go:363: NextFiltered=6
+	bench_traversal_test.go:363: NextFiltered=6
+	bench_traversal_test.go:363: NextFiltered=6
+BenchmarkNextAll	   50000	     64769 ns/op
+--- BENCH: BenchmarkNextAll
+	bench_traversal_test.go:379: NextAll=234
+	bench_traversal_test.go:379: NextAll=234
+	bench_traversal_test.go:379: NextAll=234
+	bench_traversal_test.go:379: NextAll=234
+BenchmarkNextAllFiltered	   50000	     71050 ns/op
+--- BENCH: BenchmarkNextAllFiltered
+	bench_traversal_test.go:395: NextAllFiltered=33
+	bench_traversal_test.go:395: NextAllFiltered=33
+	bench_traversal_test.go:395: NextAllFiltered=33
+	bench_traversal_test.go:395: NextAllFiltered=33
+BenchmarkPrev	   50000	     33908 ns/op
+--- BENCH: BenchmarkPrev
+	bench_traversal_test.go:411: Prev=49
+	bench_traversal_test.go:411: Prev=49
+	bench_traversal_test.go:411: Prev=49
+	bench_traversal_test.go:411: Prev=49
+BenchmarkPrevFiltered	   50000	     37353 ns/op
+--- BENCH: BenchmarkPrevFiltered
+	bench_traversal_test.go:429: PrevFiltered=7
+	bench_traversal_test.go:429: PrevFiltered=7
+	bench_traversal_test.go:429: PrevFiltered=7
+	bench_traversal_test.go:429: PrevFiltered=7
+BenchmarkPrevAll	   50000	     31056 ns/op
+--- BENCH: BenchmarkPrevAll
+	bench_traversal_test.go:445: PrevAll=78
+	bench_traversal_test.go:445: PrevAll=78
+	bench_traversal_test.go:445: PrevAll=78
+	bench_traversal_test.go:445: PrevAll=78
+BenchmarkPrevAllFiltered	   50000	     34286 ns/op
+--- BENCH: BenchmarkPrevAllFiltered
+	bench_traversal_test.go:461: PrevAllFiltered=6
+	bench_traversal_test.go:461: PrevAllFiltered=6
+	bench_traversal_test.go:461: PrevAllFiltered=6
+	bench_traversal_test.go:461: PrevAllFiltered=6
+BenchmarkNextUntil	   10000	    202553 ns/op
+--- BENCH: BenchmarkNextUntil
+	bench_traversal_test.go:477: NextUntil=84
+	bench_traversal_test.go:477: NextUntil=84
+	bench_traversal_test.go:477: NextUntil=84
+BenchmarkNextUntilSelection	   20000	     98693 ns/op
+--- BENCH: BenchmarkNextUntilSelection
+	bench_traversal_test.go:494: NextUntilSelection=42
+	bench_traversal_test.go:494: NextUntilSelection=42
+	bench_traversal_test.go:494: NextUntilSelection=42
+	bench_traversal_test.go:494: NextUntilSelection=42
+BenchmarkNextUntilNodes	   50000	     45532 ns/op
+--- BENCH: BenchmarkNextUntilNodes
+	bench_traversal_test.go:512: NextUntilNodes=12
+	bench_traversal_test.go:512: NextUntilNodes=12
+	bench_traversal_test.go:512: NextUntilNodes=12
+	bench_traversal_test.go:512: NextUntilNodes=12
+BenchmarkPrevUntil	    5000	    454378 ns/op
+--- BENCH: BenchmarkPrevUntil
+	bench_traversal_test.go:528: PrevUntil=238
+	bench_traversal_test.go:528: PrevUntil=238
+	bench_traversal_test.go:528: PrevUntil=238
+BenchmarkPrevUntilSelection	   10000	    123594 ns/op
+--- BENCH: BenchmarkPrevUntilSelection
+	bench_traversal_test.go:545: PrevUntilSelection=49
+	bench_traversal_test.go:545: PrevUntilSelection=49
+	bench_traversal_test.go:545: PrevUntilSelection=49
+BenchmarkPrevUntilNodes	   50000	     37509 ns/op
+--- BENCH: BenchmarkPrevUntilNodes
+	bench_traversal_test.go:563: PrevUntilNodes=11
+	bench_traversal_test.go:563: PrevUntilNodes=11
+	bench_traversal_test.go:563: PrevUntilNodes=11
+	bench_traversal_test.go:563: PrevUntilNodes=11
+BenchmarkNextFilteredUntil	   10000	    109317 ns/op
+--- BENCH: BenchmarkNextFilteredUntil
+	bench_traversal_test.go:579: NextFilteredUntil=22
+	bench_traversal_test.go:579: NextFilteredUntil=22
+	bench_traversal_test.go:579: NextFilteredUntil=22
+BenchmarkNextFilteredUntilSelection	   10000	    105959 ns/op
+--- BENCH: BenchmarkNextFilteredUntilSelection
+	bench_traversal_test.go:596: NextFilteredUntilSelection=22
+	bench_traversal_test.go:596: NextFilteredUntilSelection=22
+	bench_traversal_test.go:596: NextFilteredUntilSelection=22
+BenchmarkNextFilteredUntilNodes	   10000	    107132 ns/op
+--- BENCH: BenchmarkNextFilteredUntilNodes
+	bench_traversal_test.go:614: NextFilteredUntilNodes=22
+	bench_traversal_test.go:614: NextFilteredUntilNodes=22
+	bench_traversal_test.go:614: NextFilteredUntilNodes=22
+BenchmarkPrevFilteredUntil	   10000	    114474 ns/op
+--- BENCH: BenchmarkPrevFilteredUntil
+	bench_traversal_test.go:630: PrevFilteredUntil=20
+	bench_traversal_test.go:630: PrevFilteredUntil=20
+	bench_traversal_test.go:630: PrevFilteredUntil=20
+BenchmarkPrevFilteredUntilSelection	   10000	    107592 ns/op
+--- BENCH: BenchmarkPrevFilteredUntilSelection
+	bench_traversal_test.go:647: PrevFilteredUntilSelection=20
+	bench_traversal_test.go:647: PrevFilteredUntilSelection=20
+	bench_traversal_test.go:647: PrevFilteredUntilSelection=20
+BenchmarkPrevFilteredUntilNodes	   10000	    107495 ns/op
+--- BENCH: BenchmarkPrevFilteredUntilNodes
+	bench_traversal_test.go:665: PrevFilteredUntilNodes=20
+	bench_traversal_test.go:665: PrevFilteredUntilNodes=20
+	bench_traversal_test.go:665: PrevFilteredUntilNodes=20
+ok  	github.com/PuerkitoBio/goquery	187.652s

+ 405 - 0
src/github.com/PuerkitoBio/goquery/bench/v0.1.1-v0.2.1-go1.1rc1.svg

@@ -0,0 +1,405 @@
+<?xml version="1.0"?>
+<!-- Generated by SVGo -->
+<svg width="1024" height="768"
+     xmlns="http://www.w3.org/2000/svg" 
+     xmlns:xlink="http://www.w3.org/1999/xlink">
+<g style="font-size:20px;font-family:sans-serif">
+<rect x="0" y="0" width="1024" height="768" style="fill:white"/>
+<text x="100" y="50" style="font-size:150%"></text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="118" width="0" height="20" style="fill-opacity:0.3;fill:red"/>
+<text x="607" y="133" style="text-anchor:end">+0.10%</text>
+</g>
+<text x="100" y="138" style="text-anchor:start">BenchmarkFirst</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="144" width="0" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="617" y="159" style="text-anchor:start">-0.10%</text>
+</g>
+<text x="100" y="164" style="text-anchor:start">BenchmarkLast</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="604" y="170" width="8" height="20" style="fill-opacity:0.3;fill:red"/>
+<text x="599" y="185" style="text-anchor:end">+2.86%</text>
+</g>
+<text x="100" y="190" style="text-anchor:start">BenchmarkEq</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="604" y="196" width="8" height="20" style="fill-opacity:0.3;fill:red"/>
+<text x="599" y="211" style="text-anchor:end">+2.67%</text>
+</g>
+<text x="100" y="216" style="text-anchor:start">BenchmarkSlice</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="222" width="32" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="649" y="237" style="text-anchor:start">-10.82%</text>
+</g>
+<text x="100" y="242" style="text-anchor:start">BenchmarkGet</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="248" width="132" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="749" y="263" style="text-anchor:start">-44.06%</text>
+</g>
+<text x="100" y="268" style="text-anchor:start">BenchmarkIndex</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="274" width="156" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="773" y="289" style="text-anchor:start">-52.26%</text>
+</g>
+<text x="100" y="294" style="text-anchor:start">BenchmarkIndexSelector</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="300" width="27" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="644" y="315" style="text-anchor:start">-9.32%</text>
+</g>
+<text x="100" y="320" style="text-anchor:start">BenchmarkIndexOfNode</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="326" width="7" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="624" y="341" style="text-anchor:start">-2.48%</text>
+</g>
+<text x="100" y="346" style="text-anchor:start">BenchmarkIndexOfSelection</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="352" width="104" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="721" y="367" style="text-anchor:start">-34.96%</text>
+</g>
+<text x="100" y="372" style="text-anchor:start">BenchmarkMetalReviewExample</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="378" width="177" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="794" y="393" style="text-anchor:start">-59.16%</text>
+</g>
+<text x="100" y="398" style="text-anchor:start">BenchmarkAdd</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="404" width="0" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="617" y="419" style="text-anchor:start">+0.00%</text>
+</g>
+<text x="100" y="424" style="text-anchor:start">BenchmarkAddSelection</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="602" y="430" width="10" height="20" style="fill-opacity:0.3;fill:red"/>
+<text x="597" y="445" style="text-anchor:end">+3.47%</text>
+</g>
+<text x="100" y="450" style="text-anchor:start">BenchmarkAddNodes</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="456" width="7" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="624" y="471" style="text-anchor:start">-2.51%</text>
+</g>
+<text x="100" y="476" style="text-anchor:start">BenchmarkAndSelf</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="482" width="41" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="658" y="497" style="text-anchor:start">-13.96%</text>
+</g>
+<text x="100" y="502" style="text-anchor:start">BenchmarkFilter</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="508" width="43" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="660" y="523" style="text-anchor:start">-14.53%</text>
+</g>
+<text x="100" y="528" style="text-anchor:start">BenchmarkNot</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="534" width="11" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="628" y="549" style="text-anchor:start">-3.81%</text>
+</g>
+<text x="100" y="554" style="text-anchor:start">BenchmarkFilterFunction</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="560" width="8" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="625" y="575" style="text-anchor:start">-2.83%</text>
+</g>
+<text x="100" y="580" style="text-anchor:start">BenchmarkNotFunction</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="586" width="13" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="630" y="601" style="text-anchor:start">-4.63%</text>
+</g>
+<text x="100" y="606" style="text-anchor:start">BenchmarkFilterNodes</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="612" width="6" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="623" y="627" style="text-anchor:start">-2.32%</text>
+</g>
+<text x="100" y="632" style="text-anchor:start">BenchmarkNotNodes</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="638" width="12" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="629" y="653" style="text-anchor:start">-4.23%</text>
+</g>
+<text x="100" y="658" style="text-anchor:start">BenchmarkFilterSelection</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="664" width="9" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="626" y="679" style="text-anchor:start">-3.31%</text>
+</g>
+<text x="100" y="684" style="text-anchor:start">BenchmarkNotSelection</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="690" width="101" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="718" y="705" style="text-anchor:start">-33.69%</text>
+</g>
+<text x="100" y="710" style="text-anchor:start">BenchmarkHas</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="716" width="13" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="630" y="731" style="text-anchor:start">-4.53%</text>
+</g>
+<text x="100" y="736" style="text-anchor:start">BenchmarkHasNodes</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="742" width="15" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="632" y="757" style="text-anchor:start">-5.30%</text>
+</g>
+<text x="100" y="762" style="text-anchor:start">BenchmarkHasSelection</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="578" y="768" width="34" height="20" style="fill-opacity:0.3;fill:red"/>
+<text x="573" y="783" style="text-anchor:end">+11.35%</text>
+</g>
+<text x="100" y="788" style="text-anchor:start">BenchmarkEnd</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="794" width="9" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="626" y="809" style="text-anchor:start">-3.33%</text>
+</g>
+<text x="100" y="814" style="text-anchor:start">BenchmarkEach</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="820" width="27" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="644" y="835" style="text-anchor:start">-9.16%</text>
+</g>
+<text x="100" y="840" style="text-anchor:start">BenchmarkMap</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="846" width="57" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="674" y="861" style="text-anchor:start">-19.15%</text>
+</g>
+<text x="100" y="866" style="text-anchor:start">BenchmarkAttr</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="872" width="9" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="626" y="887" style="text-anchor:start">-3.24%</text>
+</g>
+<text x="100" y="892" style="text-anchor:start">BenchmarkText</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="898" width="183" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="800" y="913" style="text-anchor:start">-61.25%</text>
+</g>
+<text x="100" y="918" style="text-anchor:start">BenchmarkLength</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="924" width="35" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="652" y="939" style="text-anchor:start">-11.92%</text>
+</g>
+<text x="100" y="944" style="text-anchor:start">BenchmarkHtml</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="950" width="49" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="666" y="965" style="text-anchor:start">-16.46%</text>
+</g>
+<text x="100" y="970" style="text-anchor:start">BenchmarkIs</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="976" width="81" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="698" y="991" style="text-anchor:start">-27.31%</text>
+</g>
+<text x="100" y="996" style="text-anchor:start">BenchmarkIsPositional</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1002" width="40" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="657" y="1017" style="text-anchor:start">-13.49%</text>
+</g>
+<text x="100" y="1022" style="text-anchor:start">BenchmarkIsFunction</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1028" width="5" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="622" y="1043" style="text-anchor:start">-1.71%</text>
+</g>
+<text x="100" y="1048" style="text-anchor:start">BenchmarkIsSelection</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1054" width="9" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="626" y="1069" style="text-anchor:start">-3.03%</text>
+</g>
+<text x="100" y="1074" style="text-anchor:start">BenchmarkIsNodes</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1080" width="60" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="677" y="1095" style="text-anchor:start">-20.30%</text>
+</g>
+<text x="100" y="1100" style="text-anchor:start">BenchmarkHasClass</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1106" width="33" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="650" y="1121" style="text-anchor:start">-11.29%</text>
+</g>
+<text x="100" y="1126" style="text-anchor:start">BenchmarkContains</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1132" width="152" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="769" y="1147" style="text-anchor:start">-50.88%</text>
+</g>
+<text x="100" y="1152" style="text-anchor:start">BenchmarkFind</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1158" width="135" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="752" y="1173" style="text-anchor:start">-45.28%</text>
+</g>
+<text x="100" y="1178" style="text-anchor:start">BenchmarkFindWithinSelection</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="544" y="1184" width="68" height="20" style="fill-opacity:0.3;fill:red"/>
+<text x="539" y="1199" style="text-anchor:end">+22.95%</text>
+</g>
+<text x="100" y="1204" style="text-anchor:start">BenchmarkFindSelection</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="544" y="1210" width="68" height="20" style="fill-opacity:0.3;fill:red"/>
+<text x="539" y="1225" style="text-anchor:end">+22.68%</text>
+</g>
+<text x="100" y="1230" style="text-anchor:start">BenchmarkFindNodes</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1236" width="195" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="812" y="1251" style="text-anchor:start">-65.08%</text>
+</g>
+<text x="100" y="1256" style="text-anchor:start">BenchmarkContents</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1262" width="179" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="796" y="1277" style="text-anchor:start">-59.77%</text>
+</g>
+<text x="100" y="1282" style="text-anchor:start">BenchmarkContentsFiltered</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1288" width="192" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="809" y="1303" style="text-anchor:start">-64.31%</text>
+</g>
+<text x="100" y="1308" style="text-anchor:start">BenchmarkChildren</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1314" width="118" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="735" y="1329" style="text-anchor:start">-39.56%</text>
+</g>
+<text x="100" y="1334" style="text-anchor:start">BenchmarkChildrenFiltered</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1340" width="19" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="636" y="1355" style="text-anchor:start">-6.61%</text>
+</g>
+<text x="100" y="1360" style="text-anchor:start">BenchmarkParent</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1366" width="32" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="649" y="1381" style="text-anchor:start">-10.92%</text>
+</g>
+<text x="100" y="1386" style="text-anchor:start">BenchmarkParentFiltered</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1392" width="5" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="622" y="1407" style="text-anchor:start">-1.72%</text>
+</g>
+<text x="100" y="1412" style="text-anchor:start">BenchmarkParents</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1418" width="7" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="624" y="1433" style="text-anchor:start">-2.37%</text>
+</g>
+<text x="100" y="1438" style="text-anchor:start">BenchmarkParentsFiltered</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1444" width="142" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="759" y="1459" style="text-anchor:start">-47.60%</text>
+</g>
+<text x="100" y="1464" style="text-anchor:start">BenchmarkParentsUntil</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1470" width="109" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="726" y="1485" style="text-anchor:start">-36.39%</text>
+</g>
+<text x="100" y="1490" style="text-anchor:start">BenchmarkParentsUntilSelection</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1496" width="107" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="724" y="1511" style="text-anchor:start">-35.99%</text>
+</g>
+<text x="100" y="1516" style="text-anchor:start">BenchmarkParentsUntilNodes</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1522" width="146" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="763" y="1537" style="text-anchor:start">-48.84%</text>
+</g>
+<text x="100" y="1542" style="text-anchor:start">BenchmarkParentsFilteredUntil</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1548" width="98" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="715" y="1563" style="text-anchor:start">-32.75%</text>
+</g>
+<text x="100" y="1568" style="text-anchor:start">BenchmarkParentsFilteredUntilSelection</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1574" width="99" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="716" y="1589" style="text-anchor:start">-33.12%</text>
+</g>
+<text x="100" y="1594" style="text-anchor:start">BenchmarkParentsFilteredUntilNodes</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1600" width="89" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="706" y="1615" style="text-anchor:start">-29.78%</text>
+</g>
+<text x="100" y="1620" style="text-anchor:start">BenchmarkSiblings</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1626" width="92" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="709" y="1641" style="text-anchor:start">-30.89%</text>
+</g>
+<text x="100" y="1646" style="text-anchor:start">BenchmarkSiblingsFiltered</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1652" width="204" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="821" y="1667" style="text-anchor:start">-68.19%</text>
+</g>
+<text x="100" y="1672" style="text-anchor:start">BenchmarkNext</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1678" width="199" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="816" y="1693" style="text-anchor:start">-66.59%</text>
+</g>
+<text x="100" y="1698" style="text-anchor:start">BenchmarkNextFiltered</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1704" width="93" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="710" y="1719" style="text-anchor:start">-31.08%</text>
+</g>
+<text x="100" y="1724" style="text-anchor:start">BenchmarkNextAll</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1730" width="90" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="707" y="1745" style="text-anchor:start">-30.03%</text>
+</g>
+<text x="100" y="1750" style="text-anchor:start">BenchmarkNextAllFiltered</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1756" width="204" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="821" y="1771" style="text-anchor:start">-68.32%</text>
+</g>
+<text x="100" y="1776" style="text-anchor:start">BenchmarkPrev</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1782" width="199" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="816" y="1797" style="text-anchor:start">-66.65%</text>
+</g>
+<text x="100" y="1802" style="text-anchor:start">BenchmarkPrevFiltered</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1808" width="129" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="746" y="1823" style="text-anchor:start">-43.33%</text>
+</g>
+<text x="100" y="1828" style="text-anchor:start">BenchmarkPrevAll</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1834" width="129" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="746" y="1849" style="text-anchor:start">-43.24%</text>
+</g>
+<text x="100" y="1854" style="text-anchor:start">BenchmarkPrevAllFiltered</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1860" width="162" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="779" y="1875" style="text-anchor:start">-54.27%</text>
+</g>
+<text x="100" y="1880" style="text-anchor:start">BenchmarkNextUntil</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1886" width="113" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="730" y="1901" style="text-anchor:start">-37.90%</text>
+</g>
+<text x="100" y="1906" style="text-anchor:start">BenchmarkNextUntilSelection</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1912" width="129" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="746" y="1927" style="text-anchor:start">-43.21%</text>
+</g>
+<text x="100" y="1932" style="text-anchor:start">BenchmarkNextUntilNodes</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1938" width="147" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="764" y="1953" style="text-anchor:start">-49.33%</text>
+</g>
+<text x="100" y="1958" style="text-anchor:start">BenchmarkPrevUntil</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1964" width="111" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="728" y="1979" style="text-anchor:start">-37.02%</text>
+</g>
+<text x="100" y="1984" style="text-anchor:start">BenchmarkPrevUntilSelection</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1990" width="133" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="750" y="2005" style="text-anchor:start">-44.59%</text>
+</g>
+<text x="100" y="2010" style="text-anchor:start">BenchmarkPrevUntilNodes</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="2016" width="173" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="790" y="2031" style="text-anchor:start">-57.79%</text>
+</g>
+<text x="100" y="2036" style="text-anchor:start">BenchmarkNextFilteredUntil</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="2042" width="109" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="726" y="2057" style="text-anchor:start">-36.61%</text>
+</g>
+<text x="100" y="2062" style="text-anchor:start">BenchmarkNextFilteredUntilSelection</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="2068" width="113" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="730" y="2083" style="text-anchor:start">-37.81%</text>
+</g>
+<text x="100" y="2088" style="text-anchor:start">BenchmarkNextFilteredUntilNodes</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="2094" width="177" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="794" y="2109" style="text-anchor:start">-59.21%</text>
+</g>
+<text x="100" y="2114" style="text-anchor:start">BenchmarkPrevFilteredUntil</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="2120" width="108" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="725" y="2135" style="text-anchor:start">-36.20%</text>
+</g>
+<text x="100" y="2140" style="text-anchor:start">BenchmarkPrevFilteredUntilSelection</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="2146" width="108" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="725" y="2161" style="text-anchor:start">-36.05%</text>
+</g>
+<text x="100" y="2166" style="text-anchor:start">BenchmarkPrevFilteredUntilNodes</text>
+</g>
+</svg>

+ 459 - 0
src/github.com/PuerkitoBio/goquery/bench/v0.2.0

@@ -0,0 +1,459 @@
+PASS
+BenchmarkFirst	20000000	        94.3 ns/op
+BenchmarkLast	20000000	        94.7 ns/op
+BenchmarkEq	20000000	        93.7 ns/op
+BenchmarkSlice	20000000	        89.9 ns/op
+BenchmarkGet	1000000000	         2.72 ns/op
+BenchmarkIndex	 1000000	      1834 ns/op
+--- BENCH: BenchmarkIndex
+bench_array_test.go:73: 	Index=3
+bench_array_test.go:73: 	Index=3
+bench_array_test.go:73: 	Index=3
+bench_array_test.go:73: 	Index=3
+BenchmarkIndexSelector	   50000	     53958 ns/op
+--- BENCH: BenchmarkIndexSelector
+bench_array_test.go:85: 	IndexSelector=4
+bench_array_test.go:85: 	IndexSelector=4
+bench_array_test.go:85: 	IndexSelector=4
+bench_array_test.go:85: 	IndexSelector=4
+BenchmarkIndexOfNode	100000000	        10.1 ns/op
+--- BENCH: BenchmarkIndexOfNode
+bench_array_test.go:99: 	IndexOfNode=2
+bench_array_test.go:99: 	IndexOfNode=2
+bench_array_test.go:99: 	IndexOfNode=2
+bench_array_test.go:99: 	IndexOfNode=2
+bench_array_test.go:99: 	IndexOfNode=2
+BenchmarkIndexOfSelection	100000000	        10.9 ns/op
+--- BENCH: BenchmarkIndexOfSelection
+bench_array_test.go:111: 	IndexOfSelection=2
+bench_array_test.go:111: 	IndexOfSelection=2
+bench_array_test.go:111: 	IndexOfSelection=2
+bench_array_test.go:111: 	IndexOfSelection=2
+bench_array_test.go:111: 	IndexOfSelection=2
+BenchmarkMetalReviewExample	    5000	    326712 ns/op
+--- BENCH: BenchmarkMetalReviewExample
+bench_example_test.go:40: 	Review 0: Midnight - Complete and Total Hell (8.5).
+		Review 1: Over Your Threshold - Facticity (6.0).
+		Review 2: Nuclear Death Terror - Chaos Reigns (7.5).
+		Review 3: Evoken - Atra Mors (9.5).
+		
+		bench_example_test.go:41: 	MetalReviewExample=10
+bench_example_test.go:40: 	Review 0: Midnight - Complete and Total Hell (8.5).
+		Review 1: Over Your Threshold - Facticity (6.0).
+		Review 2: Nuclear Death Terror - Chaos Reigns (7.5).
+		Review 3: Evoken - Atra Mors (9.5).
+	... [output truncated]
+BenchmarkAdd	   50000	     51776 ns/op
+--- BENCH: BenchmarkAdd
+bench_expand_test.go:20: 	Add=43
+bench_expand_test.go:20: 	Add=43
+bench_expand_test.go:20: 	Add=43
+bench_expand_test.go:20: 	Add=43
+BenchmarkAddSelection	10000000	       196 ns/op
+--- BENCH: BenchmarkAddSelection
+bench_expand_test.go:37: 	AddSelection=43
+bench_expand_test.go:37: 	AddSelection=43
+bench_expand_test.go:37: 	AddSelection=43
+bench_expand_test.go:37: 	AddSelection=43
+bench_expand_test.go:37: 	AddSelection=43
+BenchmarkAddNodes	10000000	       191 ns/op
+--- BENCH: BenchmarkAddNodes
+bench_expand_test.go:55: 	AddNodes=43
+bench_expand_test.go:55: 	AddNodes=43
+bench_expand_test.go:55: 	AddNodes=43
+bench_expand_test.go:55: 	AddNodes=43
+bench_expand_test.go:55: 	AddNodes=43
+BenchmarkAndSelf	 1000000	      2495 ns/op
+--- BENCH: BenchmarkAndSelf
+bench_expand_test.go:71: 	AndSelf=44
+bench_expand_test.go:71: 	AndSelf=44
+bench_expand_test.go:71: 	AndSelf=44
+bench_expand_test.go:71: 	AndSelf=44
+BenchmarkFilter	   50000	     30974 ns/op
+--- BENCH: BenchmarkFilter
+bench_filter_test.go:20: 	Filter=13
+bench_filter_test.go:20: 	Filter=13
+bench_filter_test.go:20: 	Filter=13
+bench_filter_test.go:20: 	Filter=13
+BenchmarkNot	   50000	     35322 ns/op
+--- BENCH: BenchmarkNot
+bench_filter_test.go:36: 	Not=371
+bench_filter_test.go:36: 	Not=371
+bench_filter_test.go:36: 	Not=371
+bench_filter_test.go:36: 	Not=371
+BenchmarkFilterFunction	   50000	     65644 ns/op
+--- BENCH: BenchmarkFilterFunction
+bench_filter_test.go:55: 	FilterFunction=112
+bench_filter_test.go:55: 	FilterFunction=112
+bench_filter_test.go:55: 	FilterFunction=112
+bench_filter_test.go:55: 	FilterFunction=112
+BenchmarkNotFunction	   50000	     69245 ns/op
+--- BENCH: BenchmarkNotFunction
+bench_filter_test.go:74: 	NotFunction=261
+bench_filter_test.go:74: 	NotFunction=261
+bench_filter_test.go:74: 	NotFunction=261
+bench_filter_test.go:74: 	NotFunction=261
+BenchmarkFilterNodes	   50000	     64824 ns/op
+--- BENCH: BenchmarkFilterNodes
+bench_filter_test.go:92: 	FilterNodes=2
+bench_filter_test.go:92: 	FilterNodes=2
+bench_filter_test.go:92: 	FilterNodes=2
+bench_filter_test.go:92: 	FilterNodes=2
+BenchmarkNotNodes	   20000	     76247 ns/op
+--- BENCH: BenchmarkNotNodes
+bench_filter_test.go:110: 	NotNodes=360
+bench_filter_test.go:110: 	NotNodes=360
+bench_filter_test.go:110: 	NotNodes=360
+bench_filter_test.go:110: 	NotNodes=360
+BenchmarkFilterSelection	   50000	     66154 ns/op
+--- BENCH: BenchmarkFilterSelection
+bench_filter_test.go:127: 	FilterSelection=2
+bench_filter_test.go:127: 	FilterSelection=2
+bench_filter_test.go:127: 	FilterSelection=2
+bench_filter_test.go:127: 	FilterSelection=2
+BenchmarkNotSelection	   20000	     76336 ns/op
+--- BENCH: BenchmarkNotSelection
+bench_filter_test.go:144: 	NotSelection=360
+bench_filter_test.go:144: 	NotSelection=360
+bench_filter_test.go:144: 	NotSelection=360
+bench_filter_test.go:144: 	NotSelection=360
+BenchmarkHas	    5000	    569495 ns/op
+--- BENCH: BenchmarkHas
+bench_filter_test.go:160: 	Has=13
+bench_filter_test.go:160: 	Has=13
+bench_filter_test.go:160: 	Has=13
+BenchmarkHasNodes	   10000	    227059 ns/op
+--- BENCH: BenchmarkHasNodes
+bench_filter_test.go:178: 	HasNodes=15
+bench_filter_test.go:178: 	HasNodes=15
+bench_filter_test.go:178: 	HasNodes=15
+BenchmarkHasSelection	   10000	    227167 ns/op
+--- BENCH: BenchmarkHasSelection
+bench_filter_test.go:195: 	HasSelection=15
+bench_filter_test.go:195: 	HasSelection=15
+bench_filter_test.go:195: 	HasSelection=15
+BenchmarkEnd	500000000	         3.99 ns/op
+--- BENCH: BenchmarkEnd
+bench_filter_test.go:211: 	End=373
+bench_filter_test.go:211: 	End=373
+bench_filter_test.go:211: 	End=373
+bench_filter_test.go:211: 	End=373
+bench_filter_test.go:211: 	End=373
+bench_filter_test.go:211: 	End=373
+BenchmarkEach	  200000	      9354 ns/op
+--- BENCH: BenchmarkEach
+bench_iteration_test.go:22: 	Each=59
+bench_iteration_test.go:22: 	Each=59
+bench_iteration_test.go:22: 	Each=59
+bench_iteration_test.go:22: 	Each=59
+BenchmarkMap	  100000	     16557 ns/op
+--- BENCH: BenchmarkMap
+bench_iteration_test.go:41: 	Map=59
+bench_iteration_test.go:41: 	Map=59
+bench_iteration_test.go:41: 	Map=59
+bench_iteration_test.go:41: 	Map=59
+BenchmarkAttr	50000000	        36.4 ns/op
+--- BENCH: BenchmarkAttr
+bench_property_test.go:16: 	Attr=firstHeading
+bench_property_test.go:16: 	Attr=firstHeading
+bench_property_test.go:16: 	Attr=firstHeading
+bench_property_test.go:16: 	Attr=firstHeading
+bench_property_test.go:16: 	Attr=firstHeading
+BenchmarkText	  100000	     18473 ns/op
+BenchmarkLength	2000000000	         0.76 ns/op
+--- BENCH: BenchmarkLength
+bench_property_test.go:37: 	Length=14
+bench_property_test.go:37: 	Length=14
+bench_property_test.go:37: 	Length=14
+bench_property_test.go:37: 	Length=14
+bench_property_test.go:37: 	Length=14
+bench_property_test.go:37: 	Length=14
+BenchmarkHtml	 5000000	       666 ns/op
+BenchmarkIs	   50000	     35174 ns/op
+--- BENCH: BenchmarkIs
+bench_query_test.go:16: 	Is=true
+bench_query_test.go:16: 	Is=true
+bench_query_test.go:16: 	Is=true
+bench_query_test.go:16: 	Is=true
+BenchmarkIsPositional	   50000	     31814 ns/op
+--- BENCH: BenchmarkIsPositional
+bench_query_test.go:28: 	IsPositional=true
+bench_query_test.go:28: 	IsPositional=true
+bench_query_test.go:28: 	IsPositional=true
+bench_query_test.go:28: 	IsPositional=true
+BenchmarkIsFunction	 1000000	      2754 ns/op
+--- BENCH: BenchmarkIsFunction
+bench_query_test.go:43: 	IsFunction=true
+bench_query_test.go:43: 	IsFunction=true
+bench_query_test.go:43: 	IsFunction=true
+bench_query_test.go:43: 	IsFunction=true
+BenchmarkIsSelection	   50000	     66260 ns/op
+--- BENCH: BenchmarkIsSelection
+bench_query_test.go:56: 	IsSelection=true
+bench_query_test.go:56: 	IsSelection=true
+bench_query_test.go:56: 	IsSelection=true
+bench_query_test.go:56: 	IsSelection=true
+BenchmarkIsNodes	   50000	     64682 ns/op
+--- BENCH: BenchmarkIsNodes
+bench_query_test.go:70: 	IsNodes=true
+bench_query_test.go:70: 	IsNodes=true
+bench_query_test.go:70: 	IsNodes=true
+bench_query_test.go:70: 	IsNodes=true
+BenchmarkHasClass	    5000	    672953 ns/op
+--- BENCH: BenchmarkHasClass
+bench_query_test.go:82: 	HasClass=true
+bench_query_test.go:82: 	HasClass=true
+bench_query_test.go:82: 	HasClass=true
+BenchmarkContains	100000000	        11.3 ns/op
+--- BENCH: BenchmarkContains
+bench_query_test.go:96: 	Contains=true
+bench_query_test.go:96: 	Contains=true
+bench_query_test.go:96: 	Contains=true
+bench_query_test.go:96: 	Contains=true
+bench_query_test.go:96: 	Contains=true
+BenchmarkFind	   50000	     53780 ns/op
+--- BENCH: BenchmarkFind
+bench_traversal_test.go:18: 	Find=41
+bench_traversal_test.go:18: 	Find=41
+bench_traversal_test.go:18: 	Find=41
+bench_traversal_test.go:18: 	Find=41
+BenchmarkFindWithinSelection	   10000	    125963 ns/op
+--- BENCH: BenchmarkFindWithinSelection
+bench_traversal_test.go:34: 	FindWithinSelection=39
+bench_traversal_test.go:34: 	FindWithinSelection=39
+bench_traversal_test.go:34: 	FindWithinSelection=39
+BenchmarkFindSelection	    5000	    357318 ns/op
+--- BENCH: BenchmarkFindSelection
+bench_traversal_test.go:51: 	FindSelection=73
+bench_traversal_test.go:51: 	FindSelection=73
+bench_traversal_test.go:51: 	FindSelection=73
+BenchmarkFindNodes	    5000	    357587 ns/op
+--- BENCH: BenchmarkFindNodes
+bench_traversal_test.go:69: 	FindNodes=73
+bench_traversal_test.go:69: 	FindNodes=73
+bench_traversal_test.go:69: 	FindNodes=73
+BenchmarkContents	  200000	      9135 ns/op
+--- BENCH: BenchmarkContents
+bench_traversal_test.go:85: 	Contents=16
+bench_traversal_test.go:85: 	Contents=16
+bench_traversal_test.go:85: 	Contents=16
+bench_traversal_test.go:85: 	Contents=16
+BenchmarkContentsFiltered	  200000	     12383 ns/op
+--- BENCH: BenchmarkContentsFiltered
+bench_traversal_test.go:101: 	ContentsFiltered=1
+bench_traversal_test.go:101: 	ContentsFiltered=1
+bench_traversal_test.go:101: 	ContentsFiltered=1
+bench_traversal_test.go:101: 	ContentsFiltered=1
+BenchmarkChildren	 1000000	      1809 ns/op
+--- BENCH: BenchmarkChildren
+bench_traversal_test.go:117: 	Children=2
+bench_traversal_test.go:117: 	Children=2
+bench_traversal_test.go:117: 	Children=2
+bench_traversal_test.go:117: 	Children=2
+BenchmarkChildrenFiltered	  500000	      5814 ns/op
+--- BENCH: BenchmarkChildrenFiltered
+bench_traversal_test.go:133: 	ChildrenFiltered=2
+bench_traversal_test.go:133: 	ChildrenFiltered=2
+bench_traversal_test.go:133: 	ChildrenFiltered=2
+bench_traversal_test.go:133: 	ChildrenFiltered=2
+BenchmarkParent	   50000	     44810 ns/op
+--- BENCH: BenchmarkParent
+bench_traversal_test.go:149: 	Parent=55
+bench_traversal_test.go:149: 	Parent=55
+bench_traversal_test.go:149: 	Parent=55
+bench_traversal_test.go:149: 	Parent=55
+BenchmarkParentFiltered	   50000	     48795 ns/op
+--- BENCH: BenchmarkParentFiltered
+bench_traversal_test.go:165: 	ParentFiltered=4
+bench_traversal_test.go:165: 	ParentFiltered=4
+bench_traversal_test.go:165: 	ParentFiltered=4
+bench_traversal_test.go:165: 	ParentFiltered=4
+BenchmarkParents	   20000	     89102 ns/op
+--- BENCH: BenchmarkParents
+bench_traversal_test.go:181: 	Parents=73
+bench_traversal_test.go:181: 	Parents=73
+bench_traversal_test.go:181: 	Parents=73
+bench_traversal_test.go:181: 	Parents=73
+BenchmarkParentsFiltered	   20000	     93953 ns/op
+--- BENCH: BenchmarkParentsFiltered
+bench_traversal_test.go:197: 	ParentsFiltered=18
+bench_traversal_test.go:197: 	ParentsFiltered=18
+bench_traversal_test.go:197: 	ParentsFiltered=18
+bench_traversal_test.go:197: 	ParentsFiltered=18
+BenchmarkParentsUntil	   10000	    130783 ns/op
+--- BENCH: BenchmarkParentsUntil
+bench_traversal_test.go:213: 	ParentsUntil=52
+bench_traversal_test.go:213: 	ParentsUntil=52
+bench_traversal_test.go:213: 	ParentsUntil=52
+BenchmarkParentsUntilSelection	   10000	    231797 ns/op
+--- BENCH: BenchmarkParentsUntilSelection
+bench_traversal_test.go:230: 	ParentsUntilSelection=70
+bench_traversal_test.go:230: 	ParentsUntilSelection=70
+bench_traversal_test.go:230: 	ParentsUntilSelection=70
+BenchmarkParentsUntilNodes	   10000	    233761 ns/op
+--- BENCH: BenchmarkParentsUntilNodes
+bench_traversal_test.go:248: 	ParentsUntilNodes=70
+bench_traversal_test.go:248: 	ParentsUntilNodes=70
+bench_traversal_test.go:248: 	ParentsUntilNodes=70
+BenchmarkParentsFilteredUntil	   50000	     31360 ns/op
+--- BENCH: BenchmarkParentsFilteredUntil
+bench_traversal_test.go:264: 	ParentsFilteredUntil=2
+bench_traversal_test.go:264: 	ParentsFilteredUntil=2
+bench_traversal_test.go:264: 	ParentsFilteredUntil=2
+bench_traversal_test.go:264: 	ParentsFilteredUntil=2
+BenchmarkParentsFilteredUntilSelection	   50000	     30272 ns/op
+--- BENCH: BenchmarkParentsFilteredUntilSelection
+bench_traversal_test.go:281: 	ParentsFilteredUntilSelection=2
+bench_traversal_test.go:281: 	ParentsFilteredUntilSelection=2
+bench_traversal_test.go:281: 	ParentsFilteredUntilSelection=2
+bench_traversal_test.go:281: 	ParentsFilteredUntilSelection=2
+BenchmarkParentsFilteredUntilNodes	   50000	     30327 ns/op
+--- BENCH: BenchmarkParentsFilteredUntilNodes
+bench_traversal_test.go:299: 	ParentsFilteredUntilNodes=2
+bench_traversal_test.go:299: 	ParentsFilteredUntilNodes=2
+bench_traversal_test.go:299: 	ParentsFilteredUntilNodes=2
+bench_traversal_test.go:299: 	ParentsFilteredUntilNodes=2
+BenchmarkSiblings	   20000	     89862 ns/op
+--- BENCH: BenchmarkSiblings
+bench_traversal_test.go:315: 	Siblings=293
+bench_traversal_test.go:315: 	Siblings=293
+bench_traversal_test.go:315: 	Siblings=293
+bench_traversal_test.go:315: 	Siblings=293
+BenchmarkSiblingsFiltered	   20000	     97948 ns/op
+--- BENCH: BenchmarkSiblingsFiltered
+bench_traversal_test.go:331: 	SiblingsFiltered=46
+bench_traversal_test.go:331: 	SiblingsFiltered=46
+bench_traversal_test.go:331: 	SiblingsFiltered=46
+bench_traversal_test.go:331: 	SiblingsFiltered=46
+BenchmarkNext	   50000	     31975 ns/op
+--- BENCH: BenchmarkNext
+bench_traversal_test.go:347: 	Next=49
+bench_traversal_test.go:347: 	Next=49
+bench_traversal_test.go:347: 	Next=49
+bench_traversal_test.go:347: 	Next=49
+BenchmarkNextFiltered	   50000	     34887 ns/op
+--- BENCH: BenchmarkNextFiltered
+bench_traversal_test.go:363: 	NextFiltered=6
+bench_traversal_test.go:363: 	NextFiltered=6
+bench_traversal_test.go:363: 	NextFiltered=6
+bench_traversal_test.go:363: 	NextFiltered=6
+BenchmarkNextAll	   50000	     60734 ns/op
+--- BENCH: BenchmarkNextAll
+bench_traversal_test.go:379: 	NextAll=234
+bench_traversal_test.go:379: 	NextAll=234
+bench_traversal_test.go:379: 	NextAll=234
+bench_traversal_test.go:379: 	NextAll=234
+BenchmarkNextAllFiltered	   50000	     67428 ns/op
+--- BENCH: BenchmarkNextAllFiltered
+bench_traversal_test.go:395: 	NextAllFiltered=33
+bench_traversal_test.go:395: 	NextAllFiltered=33
+bench_traversal_test.go:395: 	NextAllFiltered=33
+bench_traversal_test.go:395: 	NextAllFiltered=33
+BenchmarkPrev	   50000	     32399 ns/op
+--- BENCH: BenchmarkPrev
+bench_traversal_test.go:411: 	Prev=49
+bench_traversal_test.go:411: 	Prev=49
+bench_traversal_test.go:411: 	Prev=49
+bench_traversal_test.go:411: 	Prev=49
+BenchmarkPrevFiltered	   50000	     34944 ns/op
+--- BENCH: BenchmarkPrevFiltered
+bench_traversal_test.go:429: 	PrevFiltered=7
+bench_traversal_test.go:429: 	PrevFiltered=7
+bench_traversal_test.go:429: 	PrevFiltered=7
+bench_traversal_test.go:429: 	PrevFiltered=7
+BenchmarkPrevAll	  100000	     29360 ns/op
+--- BENCH: BenchmarkPrevAll
+bench_traversal_test.go:445: 	PrevAll=78
+bench_traversal_test.go:445: 	PrevAll=78
+bench_traversal_test.go:445: 	PrevAll=78
+bench_traversal_test.go:445: 	PrevAll=78
+BenchmarkPrevAllFiltered	   50000	     32291 ns/op
+--- BENCH: BenchmarkPrevAllFiltered
+bench_traversal_test.go:461: 	PrevAllFiltered=6
+bench_traversal_test.go:461: 	PrevAllFiltered=6
+bench_traversal_test.go:461: 	PrevAllFiltered=6
+bench_traversal_test.go:461: 	PrevAllFiltered=6
+BenchmarkNextUntil	   10000	    191890 ns/op
+--- BENCH: BenchmarkNextUntil
+bench_traversal_test.go:477: 	NextUntil=84
+bench_traversal_test.go:477: 	NextUntil=84
+bench_traversal_test.go:477: 	NextUntil=84
+BenchmarkNextUntilSelection	   20000	     92054 ns/op
+--- BENCH: BenchmarkNextUntilSelection
+bench_traversal_test.go:494: 	NextUntilSelection=42
+bench_traversal_test.go:494: 	NextUntilSelection=42
+bench_traversal_test.go:494: 	NextUntilSelection=42
+bench_traversal_test.go:494: 	NextUntilSelection=42
+BenchmarkNextUntilNodes	   50000	     43401 ns/op
+--- BENCH: BenchmarkNextUntilNodes
+bench_traversal_test.go:512: 	NextUntilNodes=12
+bench_traversal_test.go:512: 	NextUntilNodes=12
+bench_traversal_test.go:512: 	NextUntilNodes=12
+bench_traversal_test.go:512: 	NextUntilNodes=12
+BenchmarkPrevUntil	    5000	    433383 ns/op
+--- BENCH: BenchmarkPrevUntil
+bench_traversal_test.go:528: 	PrevUntil=238
+bench_traversal_test.go:528: 	PrevUntil=238
+bench_traversal_test.go:528: 	PrevUntil=238
+BenchmarkPrevUntilSelection	   10000	    116423 ns/op
+--- BENCH: BenchmarkPrevUntilSelection
+bench_traversal_test.go:545: 	PrevUntilSelection=49
+bench_traversal_test.go:545: 	PrevUntilSelection=49
+bench_traversal_test.go:545: 	PrevUntilSelection=49
+BenchmarkPrevUntilNodes	   50000	     35338 ns/op
+--- BENCH: BenchmarkPrevUntilNodes
+bench_traversal_test.go:563: 	PrevUntilNodes=11
+bench_traversal_test.go:563: 	PrevUntilNodes=11
+bench_traversal_test.go:563: 	PrevUntilNodes=11
+bench_traversal_test.go:563: 	PrevUntilNodes=11
+BenchmarkNextFilteredUntil	   10000	    104686 ns/op
+--- BENCH: BenchmarkNextFilteredUntil
+bench_traversal_test.go:579: 	NextFilteredUntil=22
+bench_traversal_test.go:579: 	NextFilteredUntil=22
+bench_traversal_test.go:579: 	NextFilteredUntil=22
+BenchmarkNextFilteredUntilSelection	   20000	     99485 ns/op
+--- BENCH: BenchmarkNextFilteredUntilSelection
+bench_traversal_test.go:596: 	NextFilteredUntilSelection=22
+bench_traversal_test.go:596: 	NextFilteredUntilSelection=22
+bench_traversal_test.go:596: 	NextFilteredUntilSelection=22
+bench_traversal_test.go:596: 	NextFilteredUntilSelection=22
+BenchmarkNextFilteredUntilNodes	   20000	     99452 ns/op
+--- BENCH: BenchmarkNextFilteredUntilNodes
+bench_traversal_test.go:614: 	NextFilteredUntilNodes=22
+bench_traversal_test.go:614: 	NextFilteredUntilNodes=22
+bench_traversal_test.go:614: 	NextFilteredUntilNodes=22
+bench_traversal_test.go:614: 	NextFilteredUntilNodes=22
+BenchmarkPrevFilteredUntil	   10000	    112640 ns/op
+--- BENCH: BenchmarkPrevFilteredUntil
+bench_traversal_test.go:630: 	PrevFilteredUntil=20
+bench_traversal_test.go:630: 	PrevFilteredUntil=20
+bench_traversal_test.go:630: 	PrevFilteredUntil=20
+BenchmarkPrevFilteredUntilSelection	   10000	    103702 ns/op
+--- BENCH: BenchmarkPrevFilteredUntilSelection
+bench_traversal_test.go:647: 	PrevFilteredUntilSelection=20
+bench_traversal_test.go:647: 	PrevFilteredUntilSelection=20
+bench_traversal_test.go:647: 	PrevFilteredUntilSelection=20
+BenchmarkPrevFilteredUntilNodes	   10000	    103277 ns/op
+--- BENCH: BenchmarkPrevFilteredUntilNodes
+bench_traversal_test.go:665: 	PrevFilteredUntilNodes=20
+bench_traversal_test.go:665: 	PrevFilteredUntilNodes=20
+bench_traversal_test.go:665: 	PrevFilteredUntilNodes=20
+BenchmarkClosest	  500000	      6530 ns/op
+--- BENCH: BenchmarkClosest
+bench_traversal_test.go:681: 	Closest=2
+bench_traversal_test.go:681: 	Closest=2
+bench_traversal_test.go:681: 	Closest=2
+bench_traversal_test.go:681: 	Closest=2
+BenchmarkClosestSelection	 1000000	      1135 ns/op
+--- BENCH: BenchmarkClosestSelection
+bench_traversal_test.go:698: 	ClosestSelection=2
+bench_traversal_test.go:698: 	ClosestSelection=2
+bench_traversal_test.go:698: 	ClosestSelection=2
+bench_traversal_test.go:698: 	ClosestSelection=2
+BenchmarkClosestNodes	 1000000	      1133 ns/op
+--- BENCH: BenchmarkClosestNodes
+bench_traversal_test.go:715: 	ClosestNodes=2
+bench_traversal_test.go:715: 	ClosestNodes=2
+bench_traversal_test.go:715: 	ClosestNodes=2
+bench_traversal_test.go:715: 	ClosestNodes=2
+ok  	github.com/PuerkitoBio/goquery	192.541s

+ 420 - 0
src/github.com/PuerkitoBio/goquery/bench/v0.2.0-v0.2.1-go1.1rc1.svg

@@ -0,0 +1,420 @@
+<?xml version="1.0"?>
+<!-- Generated by SVGo -->
+<svg width="1024" height="768"
+     xmlns="http://www.w3.org/2000/svg" 
+     xmlns:xlink="http://www.w3.org/1999/xlink">
+<g style="font-size:20px;font-family:sans-serif">
+<rect x="0" y="0" width="1024" height="768" style="fill:white"/>
+<text x="100" y="50" style="font-size:150%"></text>
+<g style="font-style:italic;font-size:75%">
+<rect x="606" y="118" width="6" height="20" style="fill-opacity:0.3;fill:red"/>
+<text x="601" y="133" style="text-anchor:end">+2.12%</text>
+</g>
+<text x="100" y="138" style="text-anchor:start">BenchmarkFirst</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="609" y="144" width="3" height="20" style="fill-opacity:0.3;fill:red"/>
+<text x="604" y="159" style="text-anchor:end">+1.06%</text>
+</g>
+<text x="100" y="164" style="text-anchor:start">BenchmarkLast</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="602" y="170" width="10" height="20" style="fill-opacity:0.3;fill:red"/>
+<text x="597" y="185" style="text-anchor:end">+3.63%</text>
+</g>
+<text x="100" y="190" style="text-anchor:start">BenchmarkEq</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="604" y="196" width="8" height="20" style="fill-opacity:0.3;fill:red"/>
+<text x="599" y="211" style="text-anchor:end">+2.67%</text>
+</g>
+<text x="100" y="216" style="text-anchor:start">BenchmarkSlice</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="222" width="72" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="689" y="237" style="text-anchor:start">-24.26%</text>
+</g>
+<text x="100" y="242" style="text-anchor:start">BenchmarkGet</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="248" width="125" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="742" y="263" style="text-anchor:start">-41.71%</text>
+</g>
+<text x="100" y="268" style="text-anchor:start">BenchmarkIndex</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="274" width="151" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="768" y="289" style="text-anchor:start">-50.42%</text>
+</g>
+<text x="100" y="294" style="text-anchor:start">BenchmarkIndexSelector</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="595" y="300" width="17" height="20" style="fill-opacity:0.3;fill:red"/>
+<text x="590" y="315" style="text-anchor:end">+5.94%</text>
+</g>
+<text x="100" y="320" style="text-anchor:start">BenchmarkIndexOfNode</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="588" y="326" width="24" height="20" style="fill-opacity:0.3;fill:red"/>
+<text x="583" y="341" style="text-anchor:end">+8.26%</text>
+</g>
+<text x="100" y="346" style="text-anchor:start">BenchmarkIndexOfSelection</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="352" width="98" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="715" y="367" style="text-anchor:start">-32.94%</text>
+</g>
+<text x="100" y="372" style="text-anchor:start">BenchmarkMetalReviewExample</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="378" width="170" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="787" y="393" style="text-anchor:start">-56.84%</text>
+</g>
+<text x="100" y="398" style="text-anchor:start">BenchmarkAdd</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="593" y="404" width="19" height="20" style="fill-opacity:0.3;fill:red"/>
+<text x="588" y="419" style="text-anchor:end">+6.63%</text>
+</g>
+<text x="100" y="424" style="text-anchor:start">BenchmarkAddSelection</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="584" y="430" width="28" height="20" style="fill-opacity:0.3;fill:red"/>
+<text x="579" y="445" style="text-anchor:end">+9.42%</text>
+</g>
+<text x="100" y="450" style="text-anchor:start">BenchmarkAddNodes</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="604" y="456" width="8" height="20" style="fill-opacity:0.3;fill:red"/>
+<text x="599" y="471" style="text-anchor:end">+2.93%</text>
+</g>
+<text x="100" y="476" style="text-anchor:start">BenchmarkAndSelf</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="482" width="41" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="658" y="497" style="text-anchor:start">-13.75%</text>
+</g>
+<text x="100" y="502" style="text-anchor:start">BenchmarkFilter</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="508" width="44" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="661" y="523" style="text-anchor:start">-14.90%</text>
+</g>
+<text x="100" y="528" style="text-anchor:start">BenchmarkNot</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="609" y="534" width="3" height="20" style="fill-opacity:0.3;fill:red"/>
+<text x="604" y="549" style="text-anchor:end">+1.07%</text>
+</g>
+<text x="100" y="554" style="text-anchor:start">BenchmarkFilterFunction</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="598" y="560" width="14" height="20" style="fill-opacity:0.3;fill:red"/>
+<text x="593" y="575" style="text-anchor:end">+4.91%</text>
+</g>
+<text x="100" y="580" style="text-anchor:start">BenchmarkNotFunction</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="609" y="586" width="3" height="20" style="fill-opacity:0.3;fill:red"/>
+<text x="604" y="601" style="text-anchor:end">+1.03%</text>
+</g>
+<text x="100" y="606" style="text-anchor:start">BenchmarkFilterNodes</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="600" y="612" width="12" height="20" style="fill-opacity:0.3;fill:red"/>
+<text x="595" y="627" style="text-anchor:end">+4.22%</text>
+</g>
+<text x="100" y="632" style="text-anchor:start">BenchmarkNotNodes</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="638" width="3" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="620" y="653" style="text-anchor:start">-1.00%</text>
+</g>
+<text x="100" y="658" style="text-anchor:start">BenchmarkFilterSelection</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="600" y="664" width="12" height="20" style="fill-opacity:0.3;fill:red"/>
+<text x="595" y="679" style="text-anchor:end">+4.00%</text>
+</g>
+<text x="100" y="684" style="text-anchor:start">BenchmarkNotSelection</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="690" width="96" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="713" y="705" style="text-anchor:start">-32.12%</text>
+</g>
+<text x="100" y="710" style="text-anchor:start">BenchmarkHas</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="608" y="716" width="4" height="20" style="fill-opacity:0.3;fill:red"/>
+<text x="603" y="731" style="text-anchor:end">+1.59%</text>
+</g>
+<text x="100" y="736" style="text-anchor:start">BenchmarkHasNodes</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="608" y="742" width="4" height="20" style="fill-opacity:0.3;fill:red"/>
+<text x="603" y="757" style="text-anchor:end">+1.56%</text>
+</g>
+<text x="100" y="762" style="text-anchor:start">BenchmarkHasSelection</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="566" y="768" width="46" height="20" style="fill-opacity:0.3;fill:red"/>
+<text x="561" y="783" style="text-anchor:end">+15.54%</text>
+</g>
+<text x="100" y="788" style="text-anchor:start">BenchmarkEnd</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="607" y="794" width="5" height="20" style="fill-opacity:0.3;fill:red"/>
+<text x="602" y="809" style="text-anchor:end">+1.77%</text>
+</g>
+<text x="100" y="814" style="text-anchor:start">BenchmarkEach</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="820" width="10" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="627" y="835" style="text-anchor:start">-3.61%</text>
+</g>
+<text x="100" y="840" style="text-anchor:start">BenchmarkMap</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="846" width="49" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="666" y="861" style="text-anchor:start">-16.48%</text>
+</g>
+<text x="100" y="866" style="text-anchor:start">BenchmarkAttr</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="609" y="872" width="3" height="20" style="fill-opacity:0.3;fill:red"/>
+<text x="604" y="887" style="text-anchor:end">+1.33%</text>
+</g>
+<text x="100" y="892" style="text-anchor:start">BenchmarkText</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="898" width="177" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="794" y="913" style="text-anchor:start">-59.21%</text>
+</g>
+<text x="100" y="918" style="text-anchor:start">BenchmarkLength</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="924" width="27" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="644" y="939" style="text-anchor:start">-9.01%</text>
+</g>
+<text x="100" y="944" style="text-anchor:start">BenchmarkHtml</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="950" width="50" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="667" y="965" style="text-anchor:start">-16.73%</text>
+</g>
+<text x="100" y="970" style="text-anchor:start">BenchmarkIs</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="976" width="75" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="692" y="991" style="text-anchor:start">-25.08%</text>
+</g>
+<text x="100" y="996" style="text-anchor:start">BenchmarkIsPositional</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1002" width="34" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="651" y="1017" style="text-anchor:start">-11.55%</text>
+</g>
+<text x="100" y="1022" style="text-anchor:start">BenchmarkIsFunction</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="609" y="1028" width="3" height="20" style="fill-opacity:0.3;fill:red"/>
+<text x="604" y="1043" style="text-anchor:end">+1.28%</text>
+</g>
+<text x="100" y="1048" style="text-anchor:start">BenchmarkIsSelection</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="606" y="1054" width="6" height="20" style="fill-opacity:0.3;fill:red"/>
+<text x="601" y="1069" style="text-anchor:end">+2.10%</text>
+</g>
+<text x="100" y="1074" style="text-anchor:start">BenchmarkIsNodes</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1080" width="47" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="664" y="1095" style="text-anchor:start">-15.99%</text>
+</g>
+<text x="100" y="1100" style="text-anchor:start">BenchmarkHasClass</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1106" width="7" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="624" y="1121" style="text-anchor:start">-2.65%</text>
+</g>
+<text x="100" y="1126" style="text-anchor:start">BenchmarkContains</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1132" width="145" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="762" y="1147" style="text-anchor:start">-48.54%</text>
+</g>
+<text x="100" y="1152" style="text-anchor:start">BenchmarkFind</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1158" width="128" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="745" y="1173" style="text-anchor:start">-42.71%</text>
+</g>
+<text x="100" y="1178" style="text-anchor:start">BenchmarkFindWithinSelection</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="526" y="1184" width="86" height="20" style="fill-opacity:0.3;fill:red"/>
+<text x="521" y="1199" style="text-anchor:end">+28.77%</text>
+</g>
+<text x="100" y="1204" style="text-anchor:start">BenchmarkFindSelection</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="527" y="1210" width="85" height="20" style="fill-opacity:0.3;fill:red"/>
+<text x="522" y="1225" style="text-anchor:end">+28.47%</text>
+</g>
+<text x="100" y="1230" style="text-anchor:start">BenchmarkFindNodes</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1236" width="188" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="805" y="1251" style="text-anchor:start">-62.84%</text>
+</g>
+<text x="100" y="1256" style="text-anchor:start">BenchmarkContents</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1262" width="174" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="791" y="1277" style="text-anchor:start">-58.06%</text>
+</g>
+<text x="100" y="1282" style="text-anchor:start">BenchmarkContentsFiltered</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1288" width="189" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="806" y="1303" style="text-anchor:start">-63.13%</text>
+</g>
+<text x="100" y="1308" style="text-anchor:start">BenchmarkChildren</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1314" width="114" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="731" y="1329" style="text-anchor:start">-38.24%</text>
+</g>
+<text x="100" y="1334" style="text-anchor:start">BenchmarkChildrenFiltered</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1340" width="10" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="627" y="1355" style="text-anchor:start">-3.66%</text>
+</g>
+<text x="100" y="1360" style="text-anchor:start">BenchmarkParent</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1366" width="18" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="635" y="1381" style="text-anchor:start">-6.06%</text>
+</g>
+<text x="100" y="1386" style="text-anchor:start">BenchmarkParentFiltered</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="602" y="1392" width="10" height="20" style="fill-opacity:0.3;fill:red"/>
+<text x="597" y="1407" style="text-anchor:end">+3.64%</text>
+</g>
+<text x="100" y="1412" style="text-anchor:start">BenchmarkParents</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="608" y="1418" width="4" height="20" style="fill-opacity:0.3;fill:red"/>
+<text x="603" y="1433" style="text-anchor:end">+1.44%</text>
+</g>
+<text x="100" y="1438" style="text-anchor:start">BenchmarkParentsFiltered</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1444" width="133" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="750" y="1459" style="text-anchor:start">-44.35%</text>
+</g>
+<text x="100" y="1464" style="text-anchor:start">BenchmarkParentsUntil</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1470" width="95" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="712" y="1485" style="text-anchor:start">-31.99%</text>
+</g>
+<text x="100" y="1490" style="text-anchor:start">BenchmarkParentsUntilSelection</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1496" width="97" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="714" y="1511" style="text-anchor:start">-32.62%</text>
+</g>
+<text x="100" y="1516" style="text-anchor:start">BenchmarkParentsUntilNodes</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1522" width="137" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="754" y="1537" style="text-anchor:start">-45.83%</text>
+</g>
+<text x="100" y="1542" style="text-anchor:start">BenchmarkParentsFilteredUntil</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1548" width="90" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="707" y="1563" style="text-anchor:start">-30.05%</text>
+</g>
+<text x="100" y="1568" style="text-anchor:start">BenchmarkParentsFilteredUntilSelection</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1574" width="89" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="706" y="1589" style="text-anchor:start">-29.97%</text>
+</g>
+<text x="100" y="1594" style="text-anchor:start">BenchmarkParentsFilteredUntilNodes</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1600" width="77" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="694" y="1615" style="text-anchor:start">-25.96%</text>
+</g>
+<text x="100" y="1620" style="text-anchor:start">BenchmarkSiblings</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1626" width="80" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="697" y="1641" style="text-anchor:start">-26.67%</text>
+</g>
+<text x="100" y="1646" style="text-anchor:start">BenchmarkSiblingsFiltered</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1652" width="199" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="816" y="1667" style="text-anchor:start">-66.40%</text>
+</g>
+<text x="100" y="1672" style="text-anchor:start">BenchmarkNext</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1678" width="193" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="810" y="1693" style="text-anchor:start">-64.46%</text>
+</g>
+<text x="100" y="1698" style="text-anchor:start">BenchmarkNextFiltered</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1704" width="79" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="696" y="1719" style="text-anchor:start">-26.50%</text>
+</g>
+<text x="100" y="1724" style="text-anchor:start">BenchmarkNextAll</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1730" width="78" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="695" y="1745" style="text-anchor:start">-26.27%</text>
+</g>
+<text x="100" y="1750" style="text-anchor:start">BenchmarkNextAllFiltered</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1756" width="200" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="817" y="1771" style="text-anchor:start">-66.84%</text>
+</g>
+<text x="100" y="1776" style="text-anchor:start">BenchmarkPrev</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1782" width="193" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="810" y="1797" style="text-anchor:start">-64.35%</text>
+</g>
+<text x="100" y="1802" style="text-anchor:start">BenchmarkPrevFiltered</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1808" width="120" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="737" y="1823" style="text-anchor:start">-40.05%</text>
+</g>
+<text x="100" y="1828" style="text-anchor:start">BenchmarkPrevAll</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1834" width="119" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="736" y="1849" style="text-anchor:start">-39.74%</text>
+</g>
+<text x="100" y="1854" style="text-anchor:start">BenchmarkPrevAllFiltered</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1860" width="155" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="772" y="1875" style="text-anchor:start">-51.73%</text>
+</g>
+<text x="100" y="1880" style="text-anchor:start">BenchmarkNextUntil</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1886" width="100" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="717" y="1901" style="text-anchor:start">-33.42%</text>
+</g>
+<text x="100" y="1906" style="text-anchor:start">BenchmarkNextUntilSelection</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1912" width="121" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="738" y="1927" style="text-anchor:start">-40.42%</text>
+</g>
+<text x="100" y="1932" style="text-anchor:start">BenchmarkNextUntilNodes</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1938" width="140" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="757" y="1953" style="text-anchor:start">-46.87%</text>
+</g>
+<text x="100" y="1958" style="text-anchor:start">BenchmarkPrevUntil</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1964" width="99" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="716" y="1979" style="text-anchor:start">-33.14%</text>
+</g>
+<text x="100" y="1984" style="text-anchor:start">BenchmarkPrevUntilSelection</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="1990" width="123" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="740" y="2005" style="text-anchor:start">-41.19%</text>
+</g>
+<text x="100" y="2010" style="text-anchor:start">BenchmarkPrevUntilNodes</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="2016" width="167" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="784" y="2031" style="text-anchor:start">-55.92%</text>
+</g>
+<text x="100" y="2036" style="text-anchor:start">BenchmarkNextFilteredUntil</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="2042" width="97" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="714" y="2057" style="text-anchor:start">-32.49%</text>
+</g>
+<text x="100" y="2062" style="text-anchor:start">BenchmarkNextFilteredUntilSelection</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="2068" width="99" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="716" y="2083" style="text-anchor:start">-33.00%</text>
+</g>
+<text x="100" y="2088" style="text-anchor:start">BenchmarkNextFilteredUntilNodes</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="2094" width="175" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="792" y="2109" style="text-anchor:start">-58.54%</text>
+</g>
+<text x="100" y="2114" style="text-anchor:start">BenchmarkPrevFilteredUntil</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="2120" width="101" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="718" y="2135" style="text-anchor:start">-33.80%</text>
+</g>
+<text x="100" y="2140" style="text-anchor:start">BenchmarkPrevFilteredUntilSelection</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="2146" width="100" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="717" y="2161" style="text-anchor:start">-33.44%</text>
+</g>
+<text x="100" y="2166" style="text-anchor:start">BenchmarkPrevFilteredUntilNodes</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="2172" width="74" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="691" y="2187" style="text-anchor:start">-24.82%</text>
+</g>
+<text x="100" y="2192" style="text-anchor:start">BenchmarkClosest</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="2198" width="103" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="720" y="2213" style="text-anchor:start">-34.45%</text>
+</g>
+<text x="100" y="2218" style="text-anchor:start">BenchmarkClosestSelection</text>
+<g style="font-style:italic;font-size:75%">
+<rect x="612" y="2224" width="105" height="20" style="fill-opacity:0.3;fill:green"/>
+<text x="722" y="2239" style="text-anchor:start">-35.30%</text>
+</g>
+<text x="100" y="2244" style="text-anchor:start">BenchmarkClosestNodes</text>
+</g>
+</svg>

+ 470 - 0
src/github.com/PuerkitoBio/goquery/bench/v0.2.1-go1.1rc1

@@ -0,0 +1,470 @@
+PASS
+BenchmarkFirst	20000000	        96.3 ns/op
+BenchmarkLast	20000000	        95.7 ns/op
+BenchmarkEq	20000000	        97.1 ns/op
+BenchmarkSlice	20000000	        92.3 ns/op
+BenchmarkGet	1000000000	         2.06 ns/op
+BenchmarkIndex	 1000000	      1069 ns/op
+--- BENCH: BenchmarkIndex
+	bench_array_test.go:73: Index=3
+	bench_array_test.go:73: Index=3
+	bench_array_test.go:73: Index=3
+	bench_array_test.go:73: Index=3
+BenchmarkIndexSelector	  100000	     26750 ns/op
+--- BENCH: BenchmarkIndexSelector
+	bench_array_test.go:85: IndexSelector=4
+	bench_array_test.go:85: IndexSelector=4
+	bench_array_test.go:85: IndexSelector=4
+	bench_array_test.go:85: IndexSelector=4
+BenchmarkIndexOfNode	100000000	        10.7 ns/op
+--- BENCH: BenchmarkIndexOfNode
+	bench_array_test.go:99: IndexOfNode=2
+	bench_array_test.go:99: IndexOfNode=2
+	bench_array_test.go:99: IndexOfNode=2
+	bench_array_test.go:99: IndexOfNode=2
+	bench_array_test.go:99: IndexOfNode=2
+BenchmarkIndexOfSelection	100000000	        11.8 ns/op
+--- BENCH: BenchmarkIndexOfSelection
+	bench_array_test.go:111: IndexOfSelection=2
+	bench_array_test.go:111: IndexOfSelection=2
+	bench_array_test.go:111: IndexOfSelection=2
+	bench_array_test.go:111: IndexOfSelection=2
+	bench_array_test.go:111: IndexOfSelection=2
+BenchmarkMetalReviewExample	   10000	    219078 ns/op
+--- BENCH: BenchmarkMetalReviewExample
+	bench_example_test.go:40: Review 0: Midnight - Complete and Total Hell (8.5).
+		Review 1: Over Your Threshold - Facticity (6.0).
+		Review 2: Nuclear Death Terror - Chaos Reigns (7.5).
+		Review 3: Evoken - Atra Mors (9.5).
+		
+	bench_example_test.go:41: MetalReviewExample=10
+	bench_example_test.go:40: Review 0: Midnight - Complete and Total Hell (8.5).
+		Review 1: Over Your Threshold - Facticity (6.0).
+		Review 2: Nuclear Death Terror - Chaos Reigns (7.5).
+		Review 3: Evoken - Atra Mors (9.5).
+	... [output truncated]
+BenchmarkAdd	  100000	     22345 ns/op
+--- BENCH: BenchmarkAdd
+	bench_expand_test.go:20: Add=43
+	bench_expand_test.go:20: Add=43
+	bench_expand_test.go:20: Add=43
+	bench_expand_test.go:20: Add=43
+BenchmarkAddSelection	10000000	       209 ns/op
+--- BENCH: BenchmarkAddSelection
+	bench_expand_test.go:37: AddSelection=43
+	bench_expand_test.go:37: AddSelection=43
+	bench_expand_test.go:37: AddSelection=43
+	bench_expand_test.go:37: AddSelection=43
+	bench_expand_test.go:37: AddSelection=43
+BenchmarkAddNodes	10000000	       209 ns/op
+--- BENCH: BenchmarkAddNodes
+	bench_expand_test.go:55: AddNodes=43
+	bench_expand_test.go:55: AddNodes=43
+	bench_expand_test.go:55: AddNodes=43
+	bench_expand_test.go:55: AddNodes=43
+	bench_expand_test.go:55: AddNodes=43
+BenchmarkAndSelf	 1000000	      2568 ns/op
+--- BENCH: BenchmarkAndSelf
+	bench_expand_test.go:71: AndSelf=44
+	bench_expand_test.go:71: AndSelf=44
+	bench_expand_test.go:71: AndSelf=44
+	bench_expand_test.go:71: AndSelf=44
+BenchmarkFilter	  100000	     26715 ns/op
+--- BENCH: BenchmarkFilter
+	bench_filter_test.go:20: Filter=13
+	bench_filter_test.go:20: Filter=13
+	bench_filter_test.go:20: Filter=13
+	bench_filter_test.go:20: Filter=13
+BenchmarkNot	   50000	     30058 ns/op
+--- BENCH: BenchmarkNot
+	bench_filter_test.go:36: Not=371
+	bench_filter_test.go:36: Not=371
+	bench_filter_test.go:36: Not=371
+	bench_filter_test.go:36: Not=371
+BenchmarkFilterFunction	   50000	     66346 ns/op
+--- BENCH: BenchmarkFilterFunction
+	bench_filter_test.go:55: FilterFunction=112
+	bench_filter_test.go:55: FilterFunction=112
+	bench_filter_test.go:55: FilterFunction=112
+	bench_filter_test.go:55: FilterFunction=112
+BenchmarkNotFunction	   50000	     72646 ns/op
+--- BENCH: BenchmarkNotFunction
+	bench_filter_test.go:74: NotFunction=261
+	bench_filter_test.go:74: NotFunction=261
+	bench_filter_test.go:74: NotFunction=261
+	bench_filter_test.go:74: NotFunction=261
+BenchmarkFilterNodes	   50000	     65493 ns/op
+--- BENCH: BenchmarkFilterNodes
+	bench_filter_test.go:92: FilterNodes=2
+	bench_filter_test.go:92: FilterNodes=2
+	bench_filter_test.go:92: FilterNodes=2
+	bench_filter_test.go:92: FilterNodes=2
+BenchmarkNotNodes	   20000	     79466 ns/op
+--- BENCH: BenchmarkNotNodes
+	bench_filter_test.go:110: NotNodes=360
+	bench_filter_test.go:110: NotNodes=360
+	bench_filter_test.go:110: NotNodes=360
+	bench_filter_test.go:110: NotNodes=360
+BenchmarkFilterSelection	   50000	     65494 ns/op
+--- BENCH: BenchmarkFilterSelection
+	bench_filter_test.go:127: FilterSelection=2
+	bench_filter_test.go:127: FilterSelection=2
+	bench_filter_test.go:127: FilterSelection=2
+	bench_filter_test.go:127: FilterSelection=2
+BenchmarkNotSelection	   20000	     79387 ns/op
+--- BENCH: BenchmarkNotSelection
+	bench_filter_test.go:144: NotSelection=360
+	bench_filter_test.go:144: NotSelection=360
+	bench_filter_test.go:144: NotSelection=360
+	bench_filter_test.go:144: NotSelection=360
+BenchmarkHas	    5000	    386571 ns/op
+--- BENCH: BenchmarkHas
+	bench_filter_test.go:160: Has=13
+	bench_filter_test.go:160: Has=13
+	bench_filter_test.go:160: Has=13
+BenchmarkHasNodes	   10000	    230664 ns/op
+--- BENCH: BenchmarkHasNodes
+	bench_filter_test.go:178: HasNodes=15
+	bench_filter_test.go:178: HasNodes=15
+	bench_filter_test.go:178: HasNodes=15
+BenchmarkHasSelection	   10000	    230705 ns/op
+--- BENCH: BenchmarkHasSelection
+	bench_filter_test.go:195: HasSelection=15
+	bench_filter_test.go:195: HasSelection=15
+	bench_filter_test.go:195: HasSelection=15
+BenchmarkEnd	500000000	         4.61 ns/op
+--- BENCH: BenchmarkEnd
+	bench_filter_test.go:211: End=373
+	bench_filter_test.go:211: End=373
+	bench_filter_test.go:211: End=373
+	bench_filter_test.go:211: End=373
+	bench_filter_test.go:211: End=373
+	bench_filter_test.go:211: End=373
+BenchmarkEach	  200000	      9520 ns/op
+--- BENCH: BenchmarkEach
+	bench_iteration_test.go:22: Each=59
+	bench_iteration_test.go:22: Each=59
+	bench_iteration_test.go:22: Each=59
+	bench_iteration_test.go:22: Each=59
+BenchmarkMap	  100000	     15960 ns/op
+--- BENCH: BenchmarkMap
+	bench_iteration_test.go:41: Map=59
+	bench_iteration_test.go:41: Map=59
+	bench_iteration_test.go:41: Map=59
+	bench_iteration_test.go:41: Map=59
+BenchmarkAttr	50000000	        30.4 ns/op
+--- BENCH: BenchmarkAttr
+	bench_property_test.go:16: Attr=firstHeading
+	bench_property_test.go:16: Attr=firstHeading
+	bench_property_test.go:16: Attr=firstHeading
+	bench_property_test.go:16: Attr=firstHeading
+	bench_property_test.go:16: Attr=firstHeading
+BenchmarkText	  100000	     18718 ns/op
+BenchmarkLength	2000000000	         0.31 ns/op
+--- BENCH: BenchmarkLength
+	bench_property_test.go:37: Length=14
+	bench_property_test.go:37: Length=14
+	bench_property_test.go:37: Length=14
+	bench_property_test.go:37: Length=14
+	bench_property_test.go:37: Length=14
+	bench_property_test.go:37: Length=14
+BenchmarkHtml	 5000000	       606 ns/op
+BenchmarkIs	  100000	     29289 ns/op
+--- BENCH: BenchmarkIs
+	bench_query_test.go:16: Is=true
+	bench_query_test.go:16: Is=true
+	bench_query_test.go:16: Is=true
+	bench_query_test.go:16: Is=true
+BenchmarkIsPositional	  100000	     23834 ns/op
+--- BENCH: BenchmarkIsPositional
+	bench_query_test.go:28: IsPositional=true
+	bench_query_test.go:28: IsPositional=true
+	bench_query_test.go:28: IsPositional=true
+	bench_query_test.go:28: IsPositional=true
+BenchmarkIsFunction	 1000000	      2436 ns/op
+--- BENCH: BenchmarkIsFunction
+	bench_query_test.go:43: IsFunction=true
+	bench_query_test.go:43: IsFunction=true
+	bench_query_test.go:43: IsFunction=true
+	bench_query_test.go:43: IsFunction=true
+BenchmarkIsSelection	   50000	     67106 ns/op
+--- BENCH: BenchmarkIsSelection
+	bench_query_test.go:56: IsSelection=true
+	bench_query_test.go:56: IsSelection=true
+	bench_query_test.go:56: IsSelection=true
+	bench_query_test.go:56: IsSelection=true
+BenchmarkIsNodes	   50000	     66042 ns/op
+--- BENCH: BenchmarkIsNodes
+	bench_query_test.go:70: IsNodes=true
+	bench_query_test.go:70: IsNodes=true
+	bench_query_test.go:70: IsNodes=true
+	bench_query_test.go:70: IsNodes=true
+BenchmarkHasClass	    5000	    565347 ns/op
+--- BENCH: BenchmarkHasClass
+	bench_query_test.go:82: HasClass=true
+	bench_query_test.go:82: HasClass=true
+	bench_query_test.go:82: HasClass=true
+BenchmarkContains	100000000	        11.0 ns/op
+--- BENCH: BenchmarkContains
+	bench_query_test.go:96: Contains=true
+	bench_query_test.go:96: Contains=true
+	bench_query_test.go:96: Contains=true
+	bench_query_test.go:96: Contains=true
+	bench_query_test.go:96: Contains=true
+BenchmarkFind	  100000	     27677 ns/op
+--- BENCH: BenchmarkFind
+	bench_traversal_test.go:18: Find=41
+	bench_traversal_test.go:18: Find=41
+	bench_traversal_test.go:18: Find=41
+	bench_traversal_test.go:18: Find=41
+BenchmarkFindWithinSelection	   50000	     72162 ns/op
+--- BENCH: BenchmarkFindWithinSelection
+	bench_traversal_test.go:34: FindWithinSelection=39
+	bench_traversal_test.go:34: FindWithinSelection=39
+	bench_traversal_test.go:34: FindWithinSelection=39
+	bench_traversal_test.go:34: FindWithinSelection=39
+BenchmarkFindSelection	    5000	    460124 ns/op
+--- BENCH: BenchmarkFindSelection
+	bench_traversal_test.go:51: FindSelection=73
+	bench_traversal_test.go:51: FindSelection=73
+	bench_traversal_test.go:51: FindSelection=73
+BenchmarkFindNodes	    5000	    459390 ns/op
+--- BENCH: BenchmarkFindNodes
+	bench_traversal_test.go:69: FindNodes=73
+	bench_traversal_test.go:69: FindNodes=73
+	bench_traversal_test.go:69: FindNodes=73
+BenchmarkContents	  500000	      3395 ns/op
+--- BENCH: BenchmarkContents
+	bench_traversal_test.go:85: Contents=16
+	bench_traversal_test.go:85: Contents=16
+	bench_traversal_test.go:85: Contents=16
+	bench_traversal_test.go:85: Contents=16
+BenchmarkContentsFiltered	  500000	      5193 ns/op
+--- BENCH: BenchmarkContentsFiltered
+	bench_traversal_test.go:101: ContentsFiltered=1
+	bench_traversal_test.go:101: ContentsFiltered=1
+	bench_traversal_test.go:101: ContentsFiltered=1
+	bench_traversal_test.go:101: ContentsFiltered=1
+BenchmarkChildren	 5000000	       667 ns/op
+--- BENCH: BenchmarkChildren
+	bench_traversal_test.go:117: Children=2
+	bench_traversal_test.go:117: Children=2
+	bench_traversal_test.go:117: Children=2
+	bench_traversal_test.go:117: Children=2
+	bench_traversal_test.go:117: Children=2
+BenchmarkChildrenFiltered	  500000	      3591 ns/op
+--- BENCH: BenchmarkChildrenFiltered
+	bench_traversal_test.go:133: ChildrenFiltered=2
+	bench_traversal_test.go:133: ChildrenFiltered=2
+	bench_traversal_test.go:133: ChildrenFiltered=2
+	bench_traversal_test.go:133: ChildrenFiltered=2
+BenchmarkParent	   50000	     43168 ns/op
+--- BENCH: BenchmarkParent
+	bench_traversal_test.go:149: Parent=55
+	bench_traversal_test.go:149: Parent=55
+	bench_traversal_test.go:149: Parent=55
+	bench_traversal_test.go:149: Parent=55
+BenchmarkParentFiltered	   50000	     45836 ns/op
+--- BENCH: BenchmarkParentFiltered
+	bench_traversal_test.go:165: ParentFiltered=4
+	bench_traversal_test.go:165: ParentFiltered=4
+	bench_traversal_test.go:165: ParentFiltered=4
+	bench_traversal_test.go:165: ParentFiltered=4
+BenchmarkParents	   20000	     92348 ns/op
+--- BENCH: BenchmarkParents
+	bench_traversal_test.go:181: Parents=73
+	bench_traversal_test.go:181: Parents=73
+	bench_traversal_test.go:181: Parents=73
+	bench_traversal_test.go:181: Parents=73
+BenchmarkParentsFiltered	   20000	     95306 ns/op
+--- BENCH: BenchmarkParentsFiltered
+	bench_traversal_test.go:197: ParentsFiltered=18
+	bench_traversal_test.go:197: ParentsFiltered=18
+	bench_traversal_test.go:197: ParentsFiltered=18
+	bench_traversal_test.go:197: ParentsFiltered=18
+BenchmarkParentsUntil	   50000	     72782 ns/op
+--- BENCH: BenchmarkParentsUntil
+	bench_traversal_test.go:213: ParentsUntil=52
+	bench_traversal_test.go:213: ParentsUntil=52
+	bench_traversal_test.go:213: ParentsUntil=52
+	bench_traversal_test.go:213: ParentsUntil=52
+BenchmarkParentsUntilSelection	   10000	    157639 ns/op
+--- BENCH: BenchmarkParentsUntilSelection
+	bench_traversal_test.go:230: ParentsUntilSelection=70
+	bench_traversal_test.go:230: ParentsUntilSelection=70
+	bench_traversal_test.go:230: ParentsUntilSelection=70
+BenchmarkParentsUntilNodes	   10000	    157510 ns/op
+--- BENCH: BenchmarkParentsUntilNodes
+	bench_traversal_test.go:248: ParentsUntilNodes=70
+	bench_traversal_test.go:248: ParentsUntilNodes=70
+	bench_traversal_test.go:248: ParentsUntilNodes=70
+BenchmarkParentsFilteredUntil	  100000	     16987 ns/op
+--- BENCH: BenchmarkParentsFilteredUntil
+	bench_traversal_test.go:264: ParentsFilteredUntil=2
+	bench_traversal_test.go:264: ParentsFilteredUntil=2
+	bench_traversal_test.go:264: ParentsFilteredUntil=2
+	bench_traversal_test.go:264: ParentsFilteredUntil=2
+BenchmarkParentsFilteredUntilSelection	  100000	     21174 ns/op
+--- BENCH: BenchmarkParentsFilteredUntilSelection
+	bench_traversal_test.go:281: ParentsFilteredUntilSelection=2
+	bench_traversal_test.go:281: ParentsFilteredUntilSelection=2
+	bench_traversal_test.go:281: ParentsFilteredUntilSelection=2
+	bench_traversal_test.go:281: ParentsFilteredUntilSelection=2
+BenchmarkParentsFilteredUntilNodes	  100000	     21238 ns/op
+--- BENCH: BenchmarkParentsFilteredUntilNodes
+	bench_traversal_test.go:299: ParentsFilteredUntilNodes=2
+	bench_traversal_test.go:299: ParentsFilteredUntilNodes=2
+	bench_traversal_test.go:299: ParentsFilteredUntilNodes=2
+	bench_traversal_test.go:299: ParentsFilteredUntilNodes=2
+BenchmarkSiblings	   50000	     66536 ns/op
+--- BENCH: BenchmarkSiblings
+	bench_traversal_test.go:315: Siblings=293
+	bench_traversal_test.go:315: Siblings=293
+	bench_traversal_test.go:315: Siblings=293
+	bench_traversal_test.go:315: Siblings=293
+BenchmarkSiblingsFiltered	   50000	     71822 ns/op
+--- BENCH: BenchmarkSiblingsFiltered
+	bench_traversal_test.go:331: SiblingsFiltered=46
+	bench_traversal_test.go:331: SiblingsFiltered=46
+	bench_traversal_test.go:331: SiblingsFiltered=46
+	bench_traversal_test.go:331: SiblingsFiltered=46
+BenchmarkNext	  200000	     10745 ns/op
+--- BENCH: BenchmarkNext
+	bench_traversal_test.go:347: Next=49
+	bench_traversal_test.go:347: Next=49
+	bench_traversal_test.go:347: Next=49
+	bench_traversal_test.go:347: Next=49
+BenchmarkNextFiltered	  200000	     12399 ns/op
+--- BENCH: BenchmarkNextFiltered
+	bench_traversal_test.go:363: NextFiltered=6
+	bench_traversal_test.go:363: NextFiltered=6
+	bench_traversal_test.go:363: NextFiltered=6
+	bench_traversal_test.go:363: NextFiltered=6
+BenchmarkNextAll	   50000	     44640 ns/op
+--- BENCH: BenchmarkNextAll
+	bench_traversal_test.go:379: NextAll=234
+	bench_traversal_test.go:379: NextAll=234
+	bench_traversal_test.go:379: NextAll=234
+	bench_traversal_test.go:379: NextAll=234
+BenchmarkNextAllFiltered	   50000	     49713 ns/op
+--- BENCH: BenchmarkNextAllFiltered
+	bench_traversal_test.go:395: NextAllFiltered=33
+	bench_traversal_test.go:395: NextAllFiltered=33
+	bench_traversal_test.go:395: NextAllFiltered=33
+	bench_traversal_test.go:395: NextAllFiltered=33
+BenchmarkPrev	  200000	     10743 ns/op
+--- BENCH: BenchmarkPrev
+	bench_traversal_test.go:411: Prev=49
+	bench_traversal_test.go:411: Prev=49
+	bench_traversal_test.go:411: Prev=49
+	bench_traversal_test.go:411: Prev=49
+BenchmarkPrevFiltered	  200000	     12456 ns/op
+--- BENCH: BenchmarkPrevFiltered
+	bench_traversal_test.go:429: PrevFiltered=7
+	bench_traversal_test.go:429: PrevFiltered=7
+	bench_traversal_test.go:429: PrevFiltered=7
+	bench_traversal_test.go:429: PrevFiltered=7
+BenchmarkPrevAll	  100000	     17600 ns/op
+--- BENCH: BenchmarkPrevAll
+	bench_traversal_test.go:445: PrevAll=78
+	bench_traversal_test.go:445: PrevAll=78
+	bench_traversal_test.go:445: PrevAll=78
+	bench_traversal_test.go:445: PrevAll=78
+BenchmarkPrevAllFiltered	  100000	     19460 ns/op
+--- BENCH: BenchmarkPrevAllFiltered
+	bench_traversal_test.go:461: PrevAllFiltered=6
+	bench_traversal_test.go:461: PrevAllFiltered=6
+	bench_traversal_test.go:461: PrevAllFiltered=6
+	bench_traversal_test.go:461: PrevAllFiltered=6
+BenchmarkNextUntil	   20000	     92630 ns/op
+--- BENCH: BenchmarkNextUntil
+	bench_traversal_test.go:477: NextUntil=84
+	bench_traversal_test.go:477: NextUntil=84
+	bench_traversal_test.go:477: NextUntil=84
+	bench_traversal_test.go:477: NextUntil=84
+BenchmarkNextUntilSelection	   50000	     61285 ns/op
+--- BENCH: BenchmarkNextUntilSelection
+	bench_traversal_test.go:494: NextUntilSelection=42
+	bench_traversal_test.go:494: NextUntilSelection=42
+	bench_traversal_test.go:494: NextUntilSelection=42
+	bench_traversal_test.go:494: NextUntilSelection=42
+BenchmarkNextUntilNodes	  100000	     25859 ns/op
+--- BENCH: BenchmarkNextUntilNodes
+	bench_traversal_test.go:512: NextUntilNodes=12
+	bench_traversal_test.go:512: NextUntilNodes=12
+	bench_traversal_test.go:512: NextUntilNodes=12
+	bench_traversal_test.go:512: NextUntilNodes=12
+BenchmarkPrevUntil	   10000	    230236 ns/op
+--- BENCH: BenchmarkPrevUntil
+	bench_traversal_test.go:528: PrevUntil=238
+	bench_traversal_test.go:528: PrevUntil=238
+	bench_traversal_test.go:528: PrevUntil=238
+BenchmarkPrevUntilSelection	   20000	     77837 ns/op
+--- BENCH: BenchmarkPrevUntilSelection
+	bench_traversal_test.go:545: PrevUntilSelection=49
+	bench_traversal_test.go:545: PrevUntilSelection=49
+	bench_traversal_test.go:545: PrevUntilSelection=49
+	bench_traversal_test.go:545: PrevUntilSelection=49
+BenchmarkPrevUntilNodes	  100000	     20784 ns/op
+--- BENCH: BenchmarkPrevUntilNodes
+	bench_traversal_test.go:563: PrevUntilNodes=11
+	bench_traversal_test.go:563: PrevUntilNodes=11
+	bench_traversal_test.go:563: PrevUntilNodes=11
+	bench_traversal_test.go:563: PrevUntilNodes=11
+BenchmarkNextFilteredUntil	   50000	     46147 ns/op
+--- BENCH: BenchmarkNextFilteredUntil
+	bench_traversal_test.go:579: NextFilteredUntil=22
+	bench_traversal_test.go:579: NextFilteredUntil=22
+	bench_traversal_test.go:579: NextFilteredUntil=22
+	bench_traversal_test.go:579: NextFilteredUntil=22
+BenchmarkNextFilteredUntilSelection	   50000	     67164 ns/op
+--- BENCH: BenchmarkNextFilteredUntilSelection
+	bench_traversal_test.go:596: NextFilteredUntilSelection=22
+	bench_traversal_test.go:596: NextFilteredUntilSelection=22
+	bench_traversal_test.go:596: NextFilteredUntilSelection=22
+	bench_traversal_test.go:596: NextFilteredUntilSelection=22
+BenchmarkNextFilteredUntilNodes	   50000	     66628 ns/op
+--- BENCH: BenchmarkNextFilteredUntilNodes
+	bench_traversal_test.go:614: NextFilteredUntilNodes=22
+	bench_traversal_test.go:614: NextFilteredUntilNodes=22
+	bench_traversal_test.go:614: NextFilteredUntilNodes=22
+	bench_traversal_test.go:614: NextFilteredUntilNodes=22
+BenchmarkPrevFilteredUntil	   50000	     46697 ns/op
+--- BENCH: BenchmarkPrevFilteredUntil
+	bench_traversal_test.go:630: PrevFilteredUntil=20
+	bench_traversal_test.go:630: PrevFilteredUntil=20
+	bench_traversal_test.go:630: PrevFilteredUntil=20
+	bench_traversal_test.go:630: PrevFilteredUntil=20
+BenchmarkPrevFilteredUntilSelection	   50000	     68646 ns/op
+--- BENCH: BenchmarkPrevFilteredUntilSelection
+	bench_traversal_test.go:647: PrevFilteredUntilSelection=20
+	bench_traversal_test.go:647: PrevFilteredUntilSelection=20
+	bench_traversal_test.go:647: PrevFilteredUntilSelection=20
+	bench_traversal_test.go:647: PrevFilteredUntilSelection=20
+BenchmarkPrevFilteredUntilNodes	   50000	     68745 ns/op
+--- BENCH: BenchmarkPrevFilteredUntilNodes
+	bench_traversal_test.go:665: PrevFilteredUntilNodes=20
+	bench_traversal_test.go:665: PrevFilteredUntilNodes=20
+	bench_traversal_test.go:665: PrevFilteredUntilNodes=20
+	bench_traversal_test.go:665: PrevFilteredUntilNodes=20
+BenchmarkClosest	  500000	      4909 ns/op
+--- BENCH: BenchmarkClosest
+	bench_traversal_test.go:681: Closest=2
+	bench_traversal_test.go:681: Closest=2
+	bench_traversal_test.go:681: Closest=2
+	bench_traversal_test.go:681: Closest=2
+BenchmarkClosestSelection	 5000000	       744 ns/op
+--- BENCH: BenchmarkClosestSelection
+	bench_traversal_test.go:698: ClosestSelection=2
+	bench_traversal_test.go:698: ClosestSelection=2
+	bench_traversal_test.go:698: ClosestSelection=2
+	bench_traversal_test.go:698: ClosestSelection=2
+	bench_traversal_test.go:698: ClosestSelection=2
+BenchmarkClosestNodes	 5000000	       733 ns/op
+--- BENCH: BenchmarkClosestNodes
+	bench_traversal_test.go:715: ClosestNodes=2
+	bench_traversal_test.go:715: ClosestNodes=2
+	bench_traversal_test.go:715: ClosestNodes=2
+	bench_traversal_test.go:715: ClosestNodes=2
+	bench_traversal_test.go:715: ClosestNodes=2
+ok  	github.com/PuerkitoBio/goquery	220.793s

+ 476 - 0
src/github.com/PuerkitoBio/goquery/bench/v0.3.0

@@ -0,0 +1,476 @@
+PASS
+BenchmarkFirst	20000000	        95.5 ns/op
+BenchmarkLast	20000000	        94.9 ns/op
+BenchmarkEq	20000000	        95.7 ns/op
+BenchmarkSlice	20000000	        91.7 ns/op
+BenchmarkGet	1000000000	         2.05 ns/op
+BenchmarkIndex	 1000000	      1079 ns/op
+--- BENCH: BenchmarkIndex
+	bench_array_test.go:73: Index=3
+	bench_array_test.go:73: Index=3
+	bench_array_test.go:73: Index=3
+	bench_array_test.go:73: Index=3
+BenchmarkIndexSelector	  100000	     26972 ns/op
+--- BENCH: BenchmarkIndexSelector
+	bench_array_test.go:85: IndexSelector=4
+	bench_array_test.go:85: IndexSelector=4
+	bench_array_test.go:85: IndexSelector=4
+	bench_array_test.go:85: IndexSelector=4
+BenchmarkIndexOfNode	100000000	        10.8 ns/op
+--- BENCH: BenchmarkIndexOfNode
+	bench_array_test.go:99: IndexOfNode=2
+	bench_array_test.go:99: IndexOfNode=2
+	bench_array_test.go:99: IndexOfNode=2
+	bench_array_test.go:99: IndexOfNode=2
+	bench_array_test.go:99: IndexOfNode=2
+BenchmarkIndexOfSelection	100000000	        11.7 ns/op
+--- BENCH: BenchmarkIndexOfSelection
+	bench_array_test.go:111: IndexOfSelection=2
+	bench_array_test.go:111: IndexOfSelection=2
+	bench_array_test.go:111: IndexOfSelection=2
+	bench_array_test.go:111: IndexOfSelection=2
+	bench_array_test.go:111: IndexOfSelection=2
+BenchmarkMetalReviewExample	   10000	    213800 ns/op
+--- BENCH: BenchmarkMetalReviewExample
+	bench_example_test.go:40: Review 0: Midnight - Complete and Total Hell (8.5).
+		Review 1: Over Your Threshold - Facticity (6.0).
+		Review 2: Nuclear Death Terror - Chaos Reigns (7.5).
+		Review 3: Evoken - Atra Mors (9.5).
+		
+	bench_example_test.go:41: MetalReviewExample=10
+	bench_example_test.go:40: Review 0: Midnight - Complete and Total Hell (8.5).
+		Review 1: Over Your Threshold - Facticity (6.0).
+		Review 2: Nuclear Death Terror - Chaos Reigns (7.5).
+		Review 3: Evoken - Atra Mors (9.5).
+	... [output truncated]
+BenchmarkAdd	  100000	     21811 ns/op
+--- BENCH: BenchmarkAdd
+	bench_expand_test.go:20: Add=43
+	bench_expand_test.go:20: Add=43
+	bench_expand_test.go:20: Add=43
+	bench_expand_test.go:20: Add=43
+BenchmarkAddSelection	10000000	       205 ns/op
+--- BENCH: BenchmarkAddSelection
+	bench_expand_test.go:37: AddSelection=43
+	bench_expand_test.go:37: AddSelection=43
+	bench_expand_test.go:37: AddSelection=43
+	bench_expand_test.go:37: AddSelection=43
+	bench_expand_test.go:37: AddSelection=43
+BenchmarkAddNodes	10000000	       202 ns/op
+--- BENCH: BenchmarkAddNodes
+	bench_expand_test.go:55: AddNodes=43
+	bench_expand_test.go:55: AddNodes=43
+	bench_expand_test.go:55: AddNodes=43
+	bench_expand_test.go:55: AddNodes=43
+	bench_expand_test.go:55: AddNodes=43
+BenchmarkAndSelf	 1000000	      2467 ns/op
+--- BENCH: BenchmarkAndSelf
+	bench_expand_test.go:71: AndSelf=44
+	bench_expand_test.go:71: AndSelf=44
+	bench_expand_test.go:71: AndSelf=44
+	bench_expand_test.go:71: AndSelf=44
+BenchmarkFilter	  100000	     25643 ns/op
+--- BENCH: BenchmarkFilter
+	bench_filter_test.go:20: Filter=13
+	bench_filter_test.go:20: Filter=13
+	bench_filter_test.go:20: Filter=13
+	bench_filter_test.go:20: Filter=13
+BenchmarkNot	  100000	     29566 ns/op
+--- BENCH: BenchmarkNot
+	bench_filter_test.go:36: Not=371
+	bench_filter_test.go:36: Not=371
+	bench_filter_test.go:36: Not=371
+	bench_filter_test.go:36: Not=371
+BenchmarkFilterFunction	   50000	     66894 ns/op
+--- BENCH: BenchmarkFilterFunction
+	bench_filter_test.go:55: FilterFunction=112
+	bench_filter_test.go:55: FilterFunction=112
+	bench_filter_test.go:55: FilterFunction=112
+	bench_filter_test.go:55: FilterFunction=112
+BenchmarkNotFunction	   50000	     72183 ns/op
+--- BENCH: BenchmarkNotFunction
+	bench_filter_test.go:74: NotFunction=261
+	bench_filter_test.go:74: NotFunction=261
+	bench_filter_test.go:74: NotFunction=261
+	bench_filter_test.go:74: NotFunction=261
+BenchmarkFilterNodes	   50000	     65516 ns/op
+--- BENCH: BenchmarkFilterNodes
+	bench_filter_test.go:92: FilterNodes=2
+	bench_filter_test.go:92: FilterNodes=2
+	bench_filter_test.go:92: FilterNodes=2
+	bench_filter_test.go:92: FilterNodes=2
+BenchmarkNotNodes	   20000	     78880 ns/op
+--- BENCH: BenchmarkNotNodes
+	bench_filter_test.go:110: NotNodes=360
+	bench_filter_test.go:110: NotNodes=360
+	bench_filter_test.go:110: NotNodes=360
+	bench_filter_test.go:110: NotNodes=360
+BenchmarkFilterSelection	   50000	     65232 ns/op
+--- BENCH: BenchmarkFilterSelection
+	bench_filter_test.go:127: FilterSelection=2
+	bench_filter_test.go:127: FilterSelection=2
+	bench_filter_test.go:127: FilterSelection=2
+	bench_filter_test.go:127: FilterSelection=2
+BenchmarkNotSelection	   20000	     78813 ns/op
+--- BENCH: BenchmarkNotSelection
+	bench_filter_test.go:144: NotSelection=360
+	bench_filter_test.go:144: NotSelection=360
+	bench_filter_test.go:144: NotSelection=360
+	bench_filter_test.go:144: NotSelection=360
+BenchmarkHas	    5000	    388834 ns/op
+--- BENCH: BenchmarkHas
+	bench_filter_test.go:160: Has=13
+	bench_filter_test.go:160: Has=13
+	bench_filter_test.go:160: Has=13
+BenchmarkHasNodes	   10000	    228552 ns/op
+--- BENCH: BenchmarkHasNodes
+	bench_filter_test.go:178: HasNodes=15
+	bench_filter_test.go:178: HasNodes=15
+	bench_filter_test.go:178: HasNodes=15
+BenchmarkHasSelection	   10000	    228365 ns/op
+--- BENCH: BenchmarkHasSelection
+	bench_filter_test.go:195: HasSelection=15
+	bench_filter_test.go:195: HasSelection=15
+	bench_filter_test.go:195: HasSelection=15
+BenchmarkEnd	500000000	         4.62 ns/op
+--- BENCH: BenchmarkEnd
+	bench_filter_test.go:211: End=373
+	bench_filter_test.go:211: End=373
+	bench_filter_test.go:211: End=373
+	bench_filter_test.go:211: End=373
+	bench_filter_test.go:211: End=373
+	bench_filter_test.go:211: End=373
+BenchmarkEach	  200000	      9548 ns/op
+--- BENCH: BenchmarkEach
+	bench_iteration_test.go:22: Each=59
+	bench_iteration_test.go:22: Each=59
+	bench_iteration_test.go:22: Each=59
+	bench_iteration_test.go:22: Each=59
+BenchmarkMap	  100000	     15900 ns/op
+--- BENCH: BenchmarkMap
+	bench_iteration_test.go:41: Map=59
+	bench_iteration_test.go:41: Map=59
+	bench_iteration_test.go:41: Map=59
+	bench_iteration_test.go:41: Map=59
+BenchmarkEachWithBreak	 1000000	      1650 ns/op
+--- BENCH: BenchmarkEachWithBreak
+	bench_iteration_test.go:61: Each=10
+	bench_iteration_test.go:61: Each=10
+	bench_iteration_test.go:61: Each=10
+	bench_iteration_test.go:61: Each=10
+BenchmarkAttr	50000000	        30.5 ns/op
+--- BENCH: BenchmarkAttr
+	bench_property_test.go:16: Attr=firstHeading
+	bench_property_test.go:16: Attr=firstHeading
+	bench_property_test.go:16: Attr=firstHeading
+	bench_property_test.go:16: Attr=firstHeading
+	bench_property_test.go:16: Attr=firstHeading
+BenchmarkText	  100000	     18873 ns/op
+BenchmarkLength	2000000000	         0.31 ns/op
+--- BENCH: BenchmarkLength
+	bench_property_test.go:37: Length=14
+	bench_property_test.go:37: Length=14
+	bench_property_test.go:37: Length=14
+	bench_property_test.go:37: Length=14
+	bench_property_test.go:37: Length=14
+	bench_property_test.go:37: Length=14
+BenchmarkHtml	 5000000	       616 ns/op
+BenchmarkIs	  100000	     29499 ns/op
+--- BENCH: BenchmarkIs
+	bench_query_test.go:16: Is=true
+	bench_query_test.go:16: Is=true
+	bench_query_test.go:16: Is=true
+	bench_query_test.go:16: Is=true
+BenchmarkIsPositional	  100000	     23733 ns/op
+--- BENCH: BenchmarkIsPositional
+	bench_query_test.go:28: IsPositional=true
+	bench_query_test.go:28: IsPositional=true
+	bench_query_test.go:28: IsPositional=true
+	bench_query_test.go:28: IsPositional=true
+BenchmarkIsFunction	 1000000	      2404 ns/op
+--- BENCH: BenchmarkIsFunction
+	bench_query_test.go:43: IsFunction=true
+	bench_query_test.go:43: IsFunction=true
+	bench_query_test.go:43: IsFunction=true
+	bench_query_test.go:43: IsFunction=true
+BenchmarkIsSelection	   50000	     65376 ns/op
+--- BENCH: BenchmarkIsSelection
+	bench_query_test.go:56: IsSelection=true
+	bench_query_test.go:56: IsSelection=true
+	bench_query_test.go:56: IsSelection=true
+	bench_query_test.go:56: IsSelection=true
+BenchmarkIsNodes	   50000	     65322 ns/op
+--- BENCH: BenchmarkIsNodes
+	bench_query_test.go:70: IsNodes=true
+	bench_query_test.go:70: IsNodes=true
+	bench_query_test.go:70: IsNodes=true
+	bench_query_test.go:70: IsNodes=true
+BenchmarkHasClass	    5000	    558933 ns/op
+--- BENCH: BenchmarkHasClass
+	bench_query_test.go:82: HasClass=true
+	bench_query_test.go:82: HasClass=true
+	bench_query_test.go:82: HasClass=true
+BenchmarkContains	100000000	        11.1 ns/op
+--- BENCH: BenchmarkContains
+	bench_query_test.go:96: Contains=true
+	bench_query_test.go:96: Contains=true
+	bench_query_test.go:96: Contains=true
+	bench_query_test.go:96: Contains=true
+	bench_query_test.go:96: Contains=true
+BenchmarkFind	  100000	     27841 ns/op
+--- BENCH: BenchmarkFind
+	bench_traversal_test.go:18: Find=41
+	bench_traversal_test.go:18: Find=41
+	bench_traversal_test.go:18: Find=41
+	bench_traversal_test.go:18: Find=41
+BenchmarkFindWithinSelection	   50000	     72096 ns/op
+--- BENCH: BenchmarkFindWithinSelection
+	bench_traversal_test.go:34: FindWithinSelection=39
+	bench_traversal_test.go:34: FindWithinSelection=39
+	bench_traversal_test.go:34: FindWithinSelection=39
+	bench_traversal_test.go:34: FindWithinSelection=39
+BenchmarkFindSelection	    5000	    457349 ns/op
+--- BENCH: BenchmarkFindSelection
+	bench_traversal_test.go:51: FindSelection=73
+	bench_traversal_test.go:51: FindSelection=73
+	bench_traversal_test.go:51: FindSelection=73
+BenchmarkFindNodes	    5000	    459324 ns/op
+--- BENCH: BenchmarkFindNodes
+	bench_traversal_test.go:69: FindNodes=73
+	bench_traversal_test.go:69: FindNodes=73
+	bench_traversal_test.go:69: FindNodes=73
+BenchmarkContents	  500000	      3435 ns/op
+--- BENCH: BenchmarkContents
+	bench_traversal_test.go:85: Contents=16
+	bench_traversal_test.go:85: Contents=16
+	bench_traversal_test.go:85: Contents=16
+	bench_traversal_test.go:85: Contents=16
+BenchmarkContentsFiltered	  500000	      5241 ns/op
+--- BENCH: BenchmarkContentsFiltered
+	bench_traversal_test.go:101: ContentsFiltered=1
+	bench_traversal_test.go:101: ContentsFiltered=1
+	bench_traversal_test.go:101: ContentsFiltered=1
+	bench_traversal_test.go:101: ContentsFiltered=1
+BenchmarkChildren	 5000000	       667 ns/op
+--- BENCH: BenchmarkChildren
+	bench_traversal_test.go:117: Children=2
+	bench_traversal_test.go:117: Children=2
+	bench_traversal_test.go:117: Children=2
+	bench_traversal_test.go:117: Children=2
+	bench_traversal_test.go:117: Children=2
+BenchmarkChildrenFiltered	  500000	      3639 ns/op
+--- BENCH: BenchmarkChildrenFiltered
+	bench_traversal_test.go:133: ChildrenFiltered=2
+	bench_traversal_test.go:133: ChildrenFiltered=2
+	bench_traversal_test.go:133: ChildrenFiltered=2
+	bench_traversal_test.go:133: ChildrenFiltered=2
+BenchmarkParent	   50000	     44867 ns/op
+--- BENCH: BenchmarkParent
+	bench_traversal_test.go:149: Parent=55
+	bench_traversal_test.go:149: Parent=55
+	bench_traversal_test.go:149: Parent=55
+	bench_traversal_test.go:149: Parent=55
+BenchmarkParentFiltered	   50000	     46476 ns/op
+--- BENCH: BenchmarkParentFiltered
+	bench_traversal_test.go:165: ParentFiltered=4
+	bench_traversal_test.go:165: ParentFiltered=4
+	bench_traversal_test.go:165: ParentFiltered=4
+	bench_traversal_test.go:165: ParentFiltered=4
+BenchmarkParents	   20000	     92559 ns/op
+--- BENCH: BenchmarkParents
+	bench_traversal_test.go:181: Parents=73
+	bench_traversal_test.go:181: Parents=73
+	bench_traversal_test.go:181: Parents=73
+	bench_traversal_test.go:181: Parents=73
+BenchmarkParentsFiltered	   20000	     96142 ns/op
+--- BENCH: BenchmarkParentsFiltered
+	bench_traversal_test.go:197: ParentsFiltered=18
+	bench_traversal_test.go:197: ParentsFiltered=18
+	bench_traversal_test.go:197: ParentsFiltered=18
+	bench_traversal_test.go:197: ParentsFiltered=18
+BenchmarkParentsUntil	   50000	     73931 ns/op
+--- BENCH: BenchmarkParentsUntil
+	bench_traversal_test.go:213: ParentsUntil=52
+	bench_traversal_test.go:213: ParentsUntil=52
+	bench_traversal_test.go:213: ParentsUntil=52
+	bench_traversal_test.go:213: ParentsUntil=52
+BenchmarkParentsUntilSelection	   10000	    159820 ns/op
+--- BENCH: BenchmarkParentsUntilSelection
+	bench_traversal_test.go:230: ParentsUntilSelection=70
+	bench_traversal_test.go:230: ParentsUntilSelection=70
+	bench_traversal_test.go:230: ParentsUntilSelection=70
+BenchmarkParentsUntilNodes	   10000	    158811 ns/op
+--- BENCH: BenchmarkParentsUntilNodes
+	bench_traversal_test.go:248: ParentsUntilNodes=70
+	bench_traversal_test.go:248: ParentsUntilNodes=70
+	bench_traversal_test.go:248: ParentsUntilNodes=70
+BenchmarkParentsFilteredUntil	  100000	     17203 ns/op
+--- BENCH: BenchmarkParentsFilteredUntil
+	bench_traversal_test.go:264: ParentsFilteredUntil=2
+	bench_traversal_test.go:264: ParentsFilteredUntil=2
+	bench_traversal_test.go:264: ParentsFilteredUntil=2
+	bench_traversal_test.go:264: ParentsFilteredUntil=2
+BenchmarkParentsFilteredUntilSelection	  100000	     21358 ns/op
+--- BENCH: BenchmarkParentsFilteredUntilSelection
+	bench_traversal_test.go:281: ParentsFilteredUntilSelection=2
+	bench_traversal_test.go:281: ParentsFilteredUntilSelection=2
+	bench_traversal_test.go:281: ParentsFilteredUntilSelection=2
+	bench_traversal_test.go:281: ParentsFilteredUntilSelection=2
+BenchmarkParentsFilteredUntilNodes	  100000	     21338 ns/op
+--- BENCH: BenchmarkParentsFilteredUntilNodes
+	bench_traversal_test.go:299: ParentsFilteredUntilNodes=2
+	bench_traversal_test.go:299: ParentsFilteredUntilNodes=2
+	bench_traversal_test.go:299: ParentsFilteredUntilNodes=2
+	bench_traversal_test.go:299: ParentsFilteredUntilNodes=2
+BenchmarkSiblings	   50000	     66463 ns/op
+--- BENCH: BenchmarkSiblings
+	bench_traversal_test.go:315: Siblings=293
+	bench_traversal_test.go:315: Siblings=293
+	bench_traversal_test.go:315: Siblings=293
+	bench_traversal_test.go:315: Siblings=293
+BenchmarkSiblingsFiltered	   50000	     72503 ns/op
+--- BENCH: BenchmarkSiblingsFiltered
+	bench_traversal_test.go:331: SiblingsFiltered=46
+	bench_traversal_test.go:331: SiblingsFiltered=46
+	bench_traversal_test.go:331: SiblingsFiltered=46
+	bench_traversal_test.go:331: SiblingsFiltered=46
+BenchmarkNext	  200000	     10881 ns/op
+--- BENCH: BenchmarkNext
+	bench_traversal_test.go:347: Next=49
+	bench_traversal_test.go:347: Next=49
+	bench_traversal_test.go:347: Next=49
+	bench_traversal_test.go:347: Next=49
+BenchmarkNextFiltered	  200000	     12588 ns/op
+--- BENCH: BenchmarkNextFiltered
+	bench_traversal_test.go:363: NextFiltered=6
+	bench_traversal_test.go:363: NextFiltered=6
+	bench_traversal_test.go:363: NextFiltered=6
+	bench_traversal_test.go:363: NextFiltered=6
+BenchmarkNextAll	   50000	     45075 ns/op
+--- BENCH: BenchmarkNextAll
+	bench_traversal_test.go:379: NextAll=234
+	bench_traversal_test.go:379: NextAll=234
+	bench_traversal_test.go:379: NextAll=234
+	bench_traversal_test.go:379: NextAll=234
+BenchmarkNextAllFiltered	   50000	     50455 ns/op
+--- BENCH: BenchmarkNextAllFiltered
+	bench_traversal_test.go:395: NextAllFiltered=33
+	bench_traversal_test.go:395: NextAllFiltered=33
+	bench_traversal_test.go:395: NextAllFiltered=33
+	bench_traversal_test.go:395: NextAllFiltered=33
+BenchmarkPrev	  200000	     10933 ns/op
+--- BENCH: BenchmarkPrev
+	bench_traversal_test.go:411: Prev=49
+	bench_traversal_test.go:411: Prev=49
+	bench_traversal_test.go:411: Prev=49
+	bench_traversal_test.go:411: Prev=49
+BenchmarkPrevFiltered	  200000	     12579 ns/op
+--- BENCH: BenchmarkPrevFiltered
+	bench_traversal_test.go:429: PrevFiltered=7
+	bench_traversal_test.go:429: PrevFiltered=7
+	bench_traversal_test.go:429: PrevFiltered=7
+	bench_traversal_test.go:429: PrevFiltered=7
+BenchmarkPrevAll	  100000	     17751 ns/op
+--- BENCH: BenchmarkPrevAll
+	bench_traversal_test.go:445: PrevAll=78
+	bench_traversal_test.go:445: PrevAll=78
+	bench_traversal_test.go:445: PrevAll=78
+	bench_traversal_test.go:445: PrevAll=78
+BenchmarkPrevAllFiltered	  100000	     19702 ns/op
+--- BENCH: BenchmarkPrevAllFiltered
+	bench_traversal_test.go:461: PrevAllFiltered=6
+	bench_traversal_test.go:461: PrevAllFiltered=6
+	bench_traversal_test.go:461: PrevAllFiltered=6
+	bench_traversal_test.go:461: PrevAllFiltered=6
+BenchmarkNextUntil	   20000	     93586 ns/op
+--- BENCH: BenchmarkNextUntil
+	bench_traversal_test.go:477: NextUntil=84
+	bench_traversal_test.go:477: NextUntil=84
+	bench_traversal_test.go:477: NextUntil=84
+	bench_traversal_test.go:477: NextUntil=84
+BenchmarkNextUntilSelection	   50000	     61155 ns/op
+--- BENCH: BenchmarkNextUntilSelection
+	bench_traversal_test.go:494: NextUntilSelection=42
+	bench_traversal_test.go:494: NextUntilSelection=42
+	bench_traversal_test.go:494: NextUntilSelection=42
+	bench_traversal_test.go:494: NextUntilSelection=42
+BenchmarkNextUntilNodes	  100000	     25805 ns/op
+--- BENCH: BenchmarkNextUntilNodes
+	bench_traversal_test.go:512: NextUntilNodes=12
+	bench_traversal_test.go:512: NextUntilNodes=12
+	bench_traversal_test.go:512: NextUntilNodes=12
+	bench_traversal_test.go:512: NextUntilNodes=12
+BenchmarkPrevUntil	   10000	    232225 ns/op
+--- BENCH: BenchmarkPrevUntil
+	bench_traversal_test.go:528: PrevUntil=238
+	bench_traversal_test.go:528: PrevUntil=238
+	bench_traversal_test.go:528: PrevUntil=238
+BenchmarkPrevUntilSelection	   20000	     78316 ns/op
+--- BENCH: BenchmarkPrevUntilSelection
+	bench_traversal_test.go:545: PrevUntilSelection=49
+	bench_traversal_test.go:545: PrevUntilSelection=49
+	bench_traversal_test.go:545: PrevUntilSelection=49
+	bench_traversal_test.go:545: PrevUntilSelection=49
+BenchmarkPrevUntilNodes	  100000	     20657 ns/op
+--- BENCH: BenchmarkPrevUntilNodes
+	bench_traversal_test.go:563: PrevUntilNodes=11
+	bench_traversal_test.go:563: PrevUntilNodes=11
+	bench_traversal_test.go:563: PrevUntilNodes=11
+	bench_traversal_test.go:563: PrevUntilNodes=11
+BenchmarkNextFilteredUntil	   50000	     46567 ns/op
+--- BENCH: BenchmarkNextFilteredUntil
+	bench_traversal_test.go:579: NextFilteredUntil=22
+	bench_traversal_test.go:579: NextFilteredUntil=22
+	bench_traversal_test.go:579: NextFilteredUntil=22
+	bench_traversal_test.go:579: NextFilteredUntil=22
+BenchmarkNextFilteredUntilSelection	   50000	     67227 ns/op
+--- BENCH: BenchmarkNextFilteredUntilSelection
+	bench_traversal_test.go:596: NextFilteredUntilSelection=22
+	bench_traversal_test.go:596: NextFilteredUntilSelection=22
+	bench_traversal_test.go:596: NextFilteredUntilSelection=22
+	bench_traversal_test.go:596: NextFilteredUntilSelection=22
+BenchmarkNextFilteredUntilNodes	   50000	     66995 ns/op
+--- BENCH: BenchmarkNextFilteredUntilNodes
+	bench_traversal_test.go:614: NextFilteredUntilNodes=22
+	bench_traversal_test.go:614: NextFilteredUntilNodes=22
+	bench_traversal_test.go:614: NextFilteredUntilNodes=22
+	bench_traversal_test.go:614: NextFilteredUntilNodes=22
+BenchmarkPrevFilteredUntil	   50000	     47361 ns/op
+--- BENCH: BenchmarkPrevFilteredUntil
+	bench_traversal_test.go:630: PrevFilteredUntil=20
+	bench_traversal_test.go:630: PrevFilteredUntil=20
+	bench_traversal_test.go:630: PrevFilteredUntil=20
+	bench_traversal_test.go:630: PrevFilteredUntil=20
+BenchmarkPrevFilteredUntilSelection	   50000	     68802 ns/op
+--- BENCH: BenchmarkPrevFilteredUntilSelection
+	bench_traversal_test.go:647: PrevFilteredUntilSelection=20
+	bench_traversal_test.go:647: PrevFilteredUntilSelection=20
+	bench_traversal_test.go:647: PrevFilteredUntilSelection=20
+	bench_traversal_test.go:647: PrevFilteredUntilSelection=20
+BenchmarkPrevFilteredUntilNodes	   50000	     68928 ns/op
+--- BENCH: BenchmarkPrevFilteredUntilNodes
+	bench_traversal_test.go:665: PrevFilteredUntilNodes=20
+	bench_traversal_test.go:665: PrevFilteredUntilNodes=20
+	bench_traversal_test.go:665: PrevFilteredUntilNodes=20
+	bench_traversal_test.go:665: PrevFilteredUntilNodes=20
+BenchmarkClosest	  500000	      4922 ns/op
+--- BENCH: BenchmarkClosest
+	bench_traversal_test.go:681: Closest=2
+	bench_traversal_test.go:681: Closest=2
+	bench_traversal_test.go:681: Closest=2
+	bench_traversal_test.go:681: Closest=2
+BenchmarkClosestSelection	 5000000	       738 ns/op
+--- BENCH: BenchmarkClosestSelection
+	bench_traversal_test.go:698: ClosestSelection=2
+	bench_traversal_test.go:698: ClosestSelection=2
+	bench_traversal_test.go:698: ClosestSelection=2
+	bench_traversal_test.go:698: ClosestSelection=2
+	bench_traversal_test.go:698: ClosestSelection=2
+BenchmarkClosestNodes	 5000000	       737 ns/op
+--- BENCH: BenchmarkClosestNodes
+	bench_traversal_test.go:715: ClosestNodes=2
+	bench_traversal_test.go:715: ClosestNodes=2
+	bench_traversal_test.go:715: ClosestNodes=2
+	bench_traversal_test.go:715: ClosestNodes=2
+	bench_traversal_test.go:715: ClosestNodes=2
+ok  	github.com/PuerkitoBio/goquery	224.003s

+ 478 - 0
src/github.com/PuerkitoBio/goquery/bench/v0.3.2-go1.2

@@ -0,0 +1,478 @@
+PASS
+BenchmarkFirst	20000000	        88.4 ns/op
+BenchmarkLast	20000000	        88.2 ns/op
+BenchmarkEq	20000000	        87.4 ns/op
+BenchmarkSlice	20000000	        84.9 ns/op
+BenchmarkGet	2000000000	         1.99 ns/op
+BenchmarkIndex	 2000000	       906 ns/op
+--- BENCH: BenchmarkIndex
+	bench_array_test.go:73: Index=3
+	bench_array_test.go:73: Index=3
+	bench_array_test.go:73: Index=3
+	bench_array_test.go:73: Index=3
+	bench_array_test.go:73: Index=3
+BenchmarkIndexSelector	  100000	     22276 ns/op
+--- BENCH: BenchmarkIndexSelector
+	bench_array_test.go:85: IndexSelector=4
+	bench_array_test.go:85: IndexSelector=4
+	bench_array_test.go:85: IndexSelector=4
+	bench_array_test.go:85: IndexSelector=4
+BenchmarkIndexOfNode	200000000	         9.72 ns/op
+--- BENCH: BenchmarkIndexOfNode
+	bench_array_test.go:99: IndexOfNode=2
+	bench_array_test.go:99: IndexOfNode=2
+	bench_array_test.go:99: IndexOfNode=2
+	bench_array_test.go:99: IndexOfNode=2
+	bench_array_test.go:99: IndexOfNode=2
+	bench_array_test.go:99: IndexOfNode=2
+BenchmarkIndexOfSelection	100000000	        10.4 ns/op
+--- BENCH: BenchmarkIndexOfSelection
+	bench_array_test.go:111: IndexOfSelection=2
+	bench_array_test.go:111: IndexOfSelection=2
+	bench_array_test.go:111: IndexOfSelection=2
+	bench_array_test.go:111: IndexOfSelection=2
+	bench_array_test.go:111: IndexOfSelection=2
+BenchmarkMetalReviewExample	   10000	    199277 ns/op
+--- BENCH: BenchmarkMetalReviewExample
+	bench_example_test.go:40: Review 0: Midnight - Complete and Total Hell (8.5).
+		Review 1: Over Your Threshold - Facticity (6.0).
+		Review 2: Nuclear Death Terror - Chaos Reigns (7.5).
+		Review 3: Evoken - Atra Mors (9.5).
+		
+	bench_example_test.go:41: MetalReviewExample=10
+	bench_example_test.go:40: Review 0: Midnight - Complete and Total Hell (8.5).
+		Review 1: Over Your Threshold - Facticity (6.0).
+		Review 2: Nuclear Death Terror - Chaos Reigns (7.5).
+		Review 3: Evoken - Atra Mors (9.5).
+	... [output truncated]
+BenchmarkAdd	  100000	     18277 ns/op
+--- BENCH: BenchmarkAdd
+	bench_expand_test.go:20: Add=43
+	bench_expand_test.go:20: Add=43
+	bench_expand_test.go:20: Add=43
+	bench_expand_test.go:20: Add=43
+BenchmarkAddSelection	10000000	       200 ns/op
+--- BENCH: BenchmarkAddSelection
+	bench_expand_test.go:37: AddSelection=43
+	bench_expand_test.go:37: AddSelection=43
+	bench_expand_test.go:37: AddSelection=43
+	bench_expand_test.go:37: AddSelection=43
+	bench_expand_test.go:37: AddSelection=43
+BenchmarkAddNodes	10000000	       189 ns/op
+--- BENCH: BenchmarkAddNodes
+	bench_expand_test.go:55: AddNodes=43
+	bench_expand_test.go:55: AddNodes=43
+	bench_expand_test.go:55: AddNodes=43
+	bench_expand_test.go:55: AddNodes=43
+	bench_expand_test.go:55: AddNodes=43
+BenchmarkAndSelf	 1000000	      2569 ns/op
+--- BENCH: BenchmarkAndSelf
+	bench_expand_test.go:71: AndSelf=44
+	bench_expand_test.go:71: AndSelf=44
+	bench_expand_test.go:71: AndSelf=44
+	bench_expand_test.go:71: AndSelf=44
+BenchmarkFilter	  100000	     25195 ns/op
+--- BENCH: BenchmarkFilter
+	bench_filter_test.go:20: Filter=13
+	bench_filter_test.go:20: Filter=13
+	bench_filter_test.go:20: Filter=13
+	bench_filter_test.go:20: Filter=13
+BenchmarkNot	  100000	     29003 ns/op
+--- BENCH: BenchmarkNot
+	bench_filter_test.go:36: Not=371
+	bench_filter_test.go:36: Not=371
+	bench_filter_test.go:36: Not=371
+	bench_filter_test.go:36: Not=371
+BenchmarkFilterFunction	   50000	     60690 ns/op
+--- BENCH: BenchmarkFilterFunction
+	bench_filter_test.go:55: FilterFunction=112
+	bench_filter_test.go:55: FilterFunction=112
+	bench_filter_test.go:55: FilterFunction=112
+	bench_filter_test.go:55: FilterFunction=112
+BenchmarkNotFunction	   50000	     66008 ns/op
+--- BENCH: BenchmarkNotFunction
+	bench_filter_test.go:74: NotFunction=261
+	bench_filter_test.go:74: NotFunction=261
+	bench_filter_test.go:74: NotFunction=261
+	bench_filter_test.go:74: NotFunction=261
+BenchmarkFilterNodes	   50000	     59723 ns/op
+--- BENCH: BenchmarkFilterNodes
+	bench_filter_test.go:92: FilterNodes=2
+	bench_filter_test.go:92: FilterNodes=2
+	bench_filter_test.go:92: FilterNodes=2
+	bench_filter_test.go:92: FilterNodes=2
+BenchmarkNotNodes	   50000	     72698 ns/op
+--- BENCH: BenchmarkNotNodes
+	bench_filter_test.go:110: NotNodes=360
+	bench_filter_test.go:110: NotNodes=360
+	bench_filter_test.go:110: NotNodes=360
+	bench_filter_test.go:110: NotNodes=360
+BenchmarkFilterSelection	   50000	     59598 ns/op
+--- BENCH: BenchmarkFilterSelection
+	bench_filter_test.go:127: FilterSelection=2
+	bench_filter_test.go:127: FilterSelection=2
+	bench_filter_test.go:127: FilterSelection=2
+	bench_filter_test.go:127: FilterSelection=2
+BenchmarkNotSelection	   50000	     72526 ns/op
+--- BENCH: BenchmarkNotSelection
+	bench_filter_test.go:144: NotSelection=360
+	bench_filter_test.go:144: NotSelection=360
+	bench_filter_test.go:144: NotSelection=360
+	bench_filter_test.go:144: NotSelection=360
+BenchmarkHas	    5000	    367076 ns/op
+--- BENCH: BenchmarkHas
+	bench_filter_test.go:160: Has=13
+	bench_filter_test.go:160: Has=13
+	bench_filter_test.go:160: Has=13
+BenchmarkHasNodes	   10000	    219710 ns/op
+--- BENCH: BenchmarkHasNodes
+	bench_filter_test.go:178: HasNodes=15
+	bench_filter_test.go:178: HasNodes=15
+	bench_filter_test.go:178: HasNodes=15
+BenchmarkHasSelection	   10000	    219105 ns/op
+--- BENCH: BenchmarkHasSelection
+	bench_filter_test.go:195: HasSelection=15
+	bench_filter_test.go:195: HasSelection=15
+	bench_filter_test.go:195: HasSelection=15
+BenchmarkEnd	500000000	         4.58 ns/op
+--- BENCH: BenchmarkEnd
+	bench_filter_test.go:211: End=373
+	bench_filter_test.go:211: End=373
+	bench_filter_test.go:211: End=373
+	bench_filter_test.go:211: End=373
+	bench_filter_test.go:211: End=373
+	bench_filter_test.go:211: End=373
+BenchmarkEach	  200000	      8615 ns/op
+--- BENCH: BenchmarkEach
+	bench_iteration_test.go:22: Each=59
+	bench_iteration_test.go:22: Each=59
+	bench_iteration_test.go:22: Each=59
+	bench_iteration_test.go:22: Each=59
+BenchmarkMap	  200000	     14271 ns/op
+--- BENCH: BenchmarkMap
+	bench_iteration_test.go:41: Map=59
+	bench_iteration_test.go:41: Map=59
+	bench_iteration_test.go:41: Map=59
+	bench_iteration_test.go:41: Map=59
+BenchmarkEachWithBreak	 1000000	      1497 ns/op
+--- BENCH: BenchmarkEachWithBreak
+	bench_iteration_test.go:61: Each=10
+	bench_iteration_test.go:61: Each=10
+	bench_iteration_test.go:61: Each=10
+	bench_iteration_test.go:61: Each=10
+BenchmarkAttr	50000000	        30.9 ns/op
+--- BENCH: BenchmarkAttr
+	bench_property_test.go:16: Attr=firstHeading
+	bench_property_test.go:16: Attr=firstHeading
+	bench_property_test.go:16: Attr=firstHeading
+	bench_property_test.go:16: Attr=firstHeading
+	bench_property_test.go:16: Attr=firstHeading
+BenchmarkText	  200000	     13729 ns/op
+BenchmarkLength	2000000000	         0.31 ns/op
+--- BENCH: BenchmarkLength
+	bench_property_test.go:37: Length=14
+	bench_property_test.go:37: Length=14
+	bench_property_test.go:37: Length=14
+	bench_property_test.go:37: Length=14
+	bench_property_test.go:37: Length=14
+	bench_property_test.go:37: Length=14
+BenchmarkHtml	 5000000	       537 ns/op
+BenchmarkIs	  100000	     28904 ns/op
+--- BENCH: BenchmarkIs
+	bench_query_test.go:16: Is=true
+	bench_query_test.go:16: Is=true
+	bench_query_test.go:16: Is=true
+	bench_query_test.go:16: Is=true
+BenchmarkIsPositional	  100000	     23556 ns/op
+--- BENCH: BenchmarkIsPositional
+	bench_query_test.go:28: IsPositional=true
+	bench_query_test.go:28: IsPositional=true
+	bench_query_test.go:28: IsPositional=true
+	bench_query_test.go:28: IsPositional=true
+BenchmarkIsFunction	 1000000	      2195 ns/op
+--- BENCH: BenchmarkIsFunction
+	bench_query_test.go:43: IsFunction=true
+	bench_query_test.go:43: IsFunction=true
+	bench_query_test.go:43: IsFunction=true
+	bench_query_test.go:43: IsFunction=true
+BenchmarkIsSelection	   50000	     60100 ns/op
+--- BENCH: BenchmarkIsSelection
+	bench_query_test.go:56: IsSelection=true
+	bench_query_test.go:56: IsSelection=true
+	bench_query_test.go:56: IsSelection=true
+	bench_query_test.go:56: IsSelection=true
+BenchmarkIsNodes	   50000	     59962 ns/op
+--- BENCH: BenchmarkIsNodes
+	bench_query_test.go:70: IsNodes=true
+	bench_query_test.go:70: IsNodes=true
+	bench_query_test.go:70: IsNodes=true
+	bench_query_test.go:70: IsNodes=true
+BenchmarkHasClass	    5000	    388679 ns/op
+--- BENCH: BenchmarkHasClass
+	bench_query_test.go:82: HasClass=true
+	bench_query_test.go:82: HasClass=true
+	bench_query_test.go:82: HasClass=true
+BenchmarkContains	100000000	        11.0 ns/op
+--- BENCH: BenchmarkContains
+	bench_query_test.go:96: Contains=true
+	bench_query_test.go:96: Contains=true
+	bench_query_test.go:96: Contains=true
+	bench_query_test.go:96: Contains=true
+	bench_query_test.go:96: Contains=true
+BenchmarkFind	  100000	     22779 ns/op
+--- BENCH: BenchmarkFind
+	bench_traversal_test.go:18: Find=41
+	bench_traversal_test.go:18: Find=41
+	bench_traversal_test.go:18: Find=41
+	bench_traversal_test.go:18: Find=41
+BenchmarkFindWithinSelection	   50000	     62033 ns/op
+--- BENCH: BenchmarkFindWithinSelection
+	bench_traversal_test.go:34: FindWithinSelection=39
+	bench_traversal_test.go:34: FindWithinSelection=39
+	bench_traversal_test.go:34: FindWithinSelection=39
+	bench_traversal_test.go:34: FindWithinSelection=39
+BenchmarkFindSelection	    5000	    446918 ns/op
+--- BENCH: BenchmarkFindSelection
+	bench_traversal_test.go:51: FindSelection=73
+	bench_traversal_test.go:51: FindSelection=73
+	bench_traversal_test.go:51: FindSelection=73
+BenchmarkFindNodes	    5000	    441753 ns/op
+--- BENCH: BenchmarkFindNodes
+	bench_traversal_test.go:69: FindNodes=73
+	bench_traversal_test.go:69: FindNodes=73
+	bench_traversal_test.go:69: FindNodes=73
+BenchmarkContents	 1000000	      2807 ns/op
+--- BENCH: BenchmarkContents
+	bench_traversal_test.go:85: Contents=16
+	bench_traversal_test.go:85: Contents=16
+	bench_traversal_test.go:85: Contents=16
+	bench_traversal_test.go:85: Contents=16
+BenchmarkContentsFiltered	  500000	      4477 ns/op
+--- BENCH: BenchmarkContentsFiltered
+	bench_traversal_test.go:101: ContentsFiltered=1
+	bench_traversal_test.go:101: ContentsFiltered=1
+	bench_traversal_test.go:101: ContentsFiltered=1
+	bench_traversal_test.go:101: ContentsFiltered=1
+BenchmarkChildren	 5000000	       548 ns/op
+--- BENCH: BenchmarkChildren
+	bench_traversal_test.go:117: Children=2
+	bench_traversal_test.go:117: Children=2
+	bench_traversal_test.go:117: Children=2
+	bench_traversal_test.go:117: Children=2
+	bench_traversal_test.go:117: Children=2
+BenchmarkChildrenFiltered	  500000	      3304 ns/op
+--- BENCH: BenchmarkChildrenFiltered
+	bench_traversal_test.go:133: ChildrenFiltered=2
+	bench_traversal_test.go:133: ChildrenFiltered=2
+	bench_traversal_test.go:133: ChildrenFiltered=2
+	bench_traversal_test.go:133: ChildrenFiltered=2
+BenchmarkParent	   50000	     38248 ns/op
+--- BENCH: BenchmarkParent
+	bench_traversal_test.go:149: Parent=55
+	bench_traversal_test.go:149: Parent=55
+	bench_traversal_test.go:149: Parent=55
+	bench_traversal_test.go:149: Parent=55
+BenchmarkParentFiltered	   50000	     40677 ns/op
+--- BENCH: BenchmarkParentFiltered
+	bench_traversal_test.go:165: ParentFiltered=4
+	bench_traversal_test.go:165: ParentFiltered=4
+	bench_traversal_test.go:165: ParentFiltered=4
+	bench_traversal_test.go:165: ParentFiltered=4
+BenchmarkParents	   20000	     83043 ns/op
+--- BENCH: BenchmarkParents
+	bench_traversal_test.go:181: Parents=73
+	bench_traversal_test.go:181: Parents=73
+	bench_traversal_test.go:181: Parents=73
+	bench_traversal_test.go:181: Parents=73
+BenchmarkParentsFiltered	   20000	     85391 ns/op
+--- BENCH: BenchmarkParentsFiltered
+	bench_traversal_test.go:197: ParentsFiltered=18
+	bench_traversal_test.go:197: ParentsFiltered=18
+	bench_traversal_test.go:197: ParentsFiltered=18
+	bench_traversal_test.go:197: ParentsFiltered=18
+BenchmarkParentsUntil	   50000	     65118 ns/op
+--- BENCH: BenchmarkParentsUntil
+	bench_traversal_test.go:213: ParentsUntil=52
+	bench_traversal_test.go:213: ParentsUntil=52
+	bench_traversal_test.go:213: ParentsUntil=52
+	bench_traversal_test.go:213: ParentsUntil=52
+BenchmarkParentsUntilSelection	   10000	    144028 ns/op
+--- BENCH: BenchmarkParentsUntilSelection
+	bench_traversal_test.go:230: ParentsUntilSelection=70
+	bench_traversal_test.go:230: ParentsUntilSelection=70
+	bench_traversal_test.go:230: ParentsUntilSelection=70
+BenchmarkParentsUntilNodes	   10000	    146713 ns/op
+--- BENCH: BenchmarkParentsUntilNodes
+	bench_traversal_test.go:248: ParentsUntilNodes=70
+	bench_traversal_test.go:248: ParentsUntilNodes=70
+	bench_traversal_test.go:248: ParentsUntilNodes=70
+BenchmarkParentsFilteredUntil	  100000	     15113 ns/op
+--- BENCH: BenchmarkParentsFilteredUntil
+	bench_traversal_test.go:264: ParentsFilteredUntil=2
+	bench_traversal_test.go:264: ParentsFilteredUntil=2
+	bench_traversal_test.go:264: ParentsFilteredUntil=2
+	bench_traversal_test.go:264: ParentsFilteredUntil=2
+BenchmarkParentsFilteredUntilSelection	  100000	     18881 ns/op
+--- BENCH: BenchmarkParentsFilteredUntilSelection
+	bench_traversal_test.go:281: ParentsFilteredUntilSelection=2
+	bench_traversal_test.go:281: ParentsFilteredUntilSelection=2
+	bench_traversal_test.go:281: ParentsFilteredUntilSelection=2
+	bench_traversal_test.go:281: ParentsFilteredUntilSelection=2
+BenchmarkParentsFilteredUntilNodes	  100000	     18926 ns/op
+--- BENCH: BenchmarkParentsFilteredUntilNodes
+	bench_traversal_test.go:299: ParentsFilteredUntilNodes=2
+	bench_traversal_test.go:299: ParentsFilteredUntilNodes=2
+	bench_traversal_test.go:299: ParentsFilteredUntilNodes=2
+	bench_traversal_test.go:299: ParentsFilteredUntilNodes=2
+BenchmarkSiblings	   50000	     63221 ns/op
+--- BENCH: BenchmarkSiblings
+	bench_traversal_test.go:315: Siblings=293
+	bench_traversal_test.go:315: Siblings=293
+	bench_traversal_test.go:315: Siblings=293
+	bench_traversal_test.go:315: Siblings=293
+BenchmarkSiblingsFiltered	   50000	     69028 ns/op
+--- BENCH: BenchmarkSiblingsFiltered
+	bench_traversal_test.go:331: SiblingsFiltered=46
+	bench_traversal_test.go:331: SiblingsFiltered=46
+	bench_traversal_test.go:331: SiblingsFiltered=46
+	bench_traversal_test.go:331: SiblingsFiltered=46
+BenchmarkNext	  200000	      9133 ns/op
+--- BENCH: BenchmarkNext
+	bench_traversal_test.go:347: Next=49
+	bench_traversal_test.go:347: Next=49
+	bench_traversal_test.go:347: Next=49
+	bench_traversal_test.go:347: Next=49
+BenchmarkNextFiltered	  200000	     10601 ns/op
+--- BENCH: BenchmarkNextFiltered
+	bench_traversal_test.go:363: NextFiltered=6
+	bench_traversal_test.go:363: NextFiltered=6
+	bench_traversal_test.go:363: NextFiltered=6
+	bench_traversal_test.go:363: NextFiltered=6
+BenchmarkNextAll	   50000	     43089 ns/op
+--- BENCH: BenchmarkNextAll
+	bench_traversal_test.go:379: NextAll=234
+	bench_traversal_test.go:379: NextAll=234
+	bench_traversal_test.go:379: NextAll=234
+	bench_traversal_test.go:379: NextAll=234
+BenchmarkNextAllFiltered	   50000	     47867 ns/op
+--- BENCH: BenchmarkNextAllFiltered
+	bench_traversal_test.go:395: NextAllFiltered=33
+	bench_traversal_test.go:395: NextAllFiltered=33
+	bench_traversal_test.go:395: NextAllFiltered=33
+	bench_traversal_test.go:395: NextAllFiltered=33
+BenchmarkPrev	  200000	      9104 ns/op
+--- BENCH: BenchmarkPrev
+	bench_traversal_test.go:411: Prev=49
+	bench_traversal_test.go:411: Prev=49
+	bench_traversal_test.go:411: Prev=49
+	bench_traversal_test.go:411: Prev=49
+BenchmarkPrevFiltered	  200000	     10579 ns/op
+--- BENCH: BenchmarkPrevFiltered
+	bench_traversal_test.go:429: PrevFiltered=7
+	bench_traversal_test.go:429: PrevFiltered=7
+	bench_traversal_test.go:429: PrevFiltered=7
+	bench_traversal_test.go:429: PrevFiltered=7
+BenchmarkPrevAll	  100000	     15185 ns/op
+--- BENCH: BenchmarkPrevAll
+	bench_traversal_test.go:445: PrevAll=78
+	bench_traversal_test.go:445: PrevAll=78
+	bench_traversal_test.go:445: PrevAll=78
+	bench_traversal_test.go:445: PrevAll=78
+BenchmarkPrevAllFiltered	  100000	     17108 ns/op
+--- BENCH: BenchmarkPrevAllFiltered
+	bench_traversal_test.go:461: PrevAllFiltered=6
+	bench_traversal_test.go:461: PrevAllFiltered=6
+	bench_traversal_test.go:461: PrevAllFiltered=6
+	bench_traversal_test.go:461: PrevAllFiltered=6
+BenchmarkNextUntil	   20000	     81087 ns/op
+--- BENCH: BenchmarkNextUntil
+	bench_traversal_test.go:477: NextUntil=84
+	bench_traversal_test.go:477: NextUntil=84
+	bench_traversal_test.go:477: NextUntil=84
+	bench_traversal_test.go:477: NextUntil=84
+BenchmarkNextUntilSelection	   50000	     55831 ns/op
+--- BENCH: BenchmarkNextUntilSelection
+	bench_traversal_test.go:494: NextUntilSelection=42
+	bench_traversal_test.go:494: NextUntilSelection=42
+	bench_traversal_test.go:494: NextUntilSelection=42
+	bench_traversal_test.go:494: NextUntilSelection=42
+BenchmarkNextUntilNodes	  100000	     23130 ns/op
+--- BENCH: BenchmarkNextUntilNodes
+	bench_traversal_test.go:512: NextUntilNodes=12
+	bench_traversal_test.go:512: NextUntilNodes=12
+	bench_traversal_test.go:512: NextUntilNodes=12
+	bench_traversal_test.go:512: NextUntilNodes=12
+BenchmarkPrevUntil	   10000	    204673 ns/op
+--- BENCH: BenchmarkPrevUntil
+	bench_traversal_test.go:528: PrevUntil=238
+	bench_traversal_test.go:528: PrevUntil=238
+	bench_traversal_test.go:528: PrevUntil=238
+BenchmarkPrevUntilSelection	   50000	     70965 ns/op
+--- BENCH: BenchmarkPrevUntilSelection
+	bench_traversal_test.go:545: PrevUntilSelection=49
+	bench_traversal_test.go:545: PrevUntilSelection=49
+	bench_traversal_test.go:545: PrevUntilSelection=49
+	bench_traversal_test.go:545: PrevUntilSelection=49
+BenchmarkPrevUntilNodes	  100000	     18591 ns/op
+--- BENCH: BenchmarkPrevUntilNodes
+	bench_traversal_test.go:563: PrevUntilNodes=11
+	bench_traversal_test.go:563: PrevUntilNodes=11
+	bench_traversal_test.go:563: PrevUntilNodes=11
+	bench_traversal_test.go:563: PrevUntilNodes=11
+BenchmarkNextFilteredUntil	   50000	     42004 ns/op
+--- BENCH: BenchmarkNextFilteredUntil
+	bench_traversal_test.go:579: NextFilteredUntil=22
+	bench_traversal_test.go:579: NextFilteredUntil=22
+	bench_traversal_test.go:579: NextFilteredUntil=22
+	bench_traversal_test.go:579: NextFilteredUntil=22
+BenchmarkNextFilteredUntilSelection	   50000	     61953 ns/op
+--- BENCH: BenchmarkNextFilteredUntilSelection
+	bench_traversal_test.go:596: NextFilteredUntilSelection=22
+	bench_traversal_test.go:596: NextFilteredUntilSelection=22
+	bench_traversal_test.go:596: NextFilteredUntilSelection=22
+	bench_traversal_test.go:596: NextFilteredUntilSelection=22
+BenchmarkNextFilteredUntilNodes	   50000	     62124 ns/op
+--- BENCH: BenchmarkNextFilteredUntilNodes
+	bench_traversal_test.go:614: NextFilteredUntilNodes=22
+	bench_traversal_test.go:614: NextFilteredUntilNodes=22
+	bench_traversal_test.go:614: NextFilteredUntilNodes=22
+	bench_traversal_test.go:614: NextFilteredUntilNodes=22
+BenchmarkPrevFilteredUntil	   50000	     42861 ns/op
+--- BENCH: BenchmarkPrevFilteredUntil
+	bench_traversal_test.go:630: PrevFilteredUntil=20
+	bench_traversal_test.go:630: PrevFilteredUntil=20
+	bench_traversal_test.go:630: PrevFilteredUntil=20
+	bench_traversal_test.go:630: PrevFilteredUntil=20
+BenchmarkPrevFilteredUntilSelection	   50000	     62451 ns/op
+--- BENCH: BenchmarkPrevFilteredUntilSelection
+	bench_traversal_test.go:647: PrevFilteredUntilSelection=20
+	bench_traversal_test.go:647: PrevFilteredUntilSelection=20
+	bench_traversal_test.go:647: PrevFilteredUntilSelection=20
+	bench_traversal_test.go:647: PrevFilteredUntilSelection=20
+BenchmarkPrevFilteredUntilNodes	   50000	     62631 ns/op
+--- BENCH: BenchmarkPrevFilteredUntilNodes
+	bench_traversal_test.go:665: PrevFilteredUntilNodes=20
+	bench_traversal_test.go:665: PrevFilteredUntilNodes=20
+	bench_traversal_test.go:665: PrevFilteredUntilNodes=20
+	bench_traversal_test.go:665: PrevFilteredUntilNodes=20
+BenchmarkClosest	  500000	      4684 ns/op
+--- BENCH: BenchmarkClosest
+	bench_traversal_test.go:681: Closest=2
+	bench_traversal_test.go:681: Closest=2
+	bench_traversal_test.go:681: Closest=2
+	bench_traversal_test.go:681: Closest=2
+BenchmarkClosestSelection	 5000000	       622 ns/op
+--- BENCH: BenchmarkClosestSelection
+	bench_traversal_test.go:698: ClosestSelection=2
+	bench_traversal_test.go:698: ClosestSelection=2
+	bench_traversal_test.go:698: ClosestSelection=2
+	bench_traversal_test.go:698: ClosestSelection=2
+	bench_traversal_test.go:698: ClosestSelection=2
+BenchmarkClosestNodes	 5000000	       617 ns/op
+--- BENCH: BenchmarkClosestNodes
+	bench_traversal_test.go:715: ClosestNodes=2
+	bench_traversal_test.go:715: ClosestNodes=2
+	bench_traversal_test.go:715: ClosestNodes=2
+	bench_traversal_test.go:715: ClosestNodes=2
+	bench_traversal_test.go:715: ClosestNodes=2
+ok  	github.com/PuerkitoBio/goquery	218.724s

+ 477 - 0
src/github.com/PuerkitoBio/goquery/bench/v0.3.2-go1.2-take2

@@ -0,0 +1,477 @@
+PASS
+BenchmarkFirst	20000000	        88.3 ns/op
+BenchmarkLast	20000000	        88.9 ns/op
+BenchmarkEq	20000000	        86.7 ns/op
+BenchmarkSlice	20000000	        84.1 ns/op
+BenchmarkGet	2000000000	         1.99 ns/op
+BenchmarkIndex	 2000000	       907 ns/op
+--- BENCH: BenchmarkIndex
+	bench_array_test.go:73: Index=3
+	bench_array_test.go:73: Index=3
+	bench_array_test.go:73: Index=3
+	bench_array_test.go:73: Index=3
+	bench_array_test.go:73: Index=3
+BenchmarkIndexSelector	  200000	     13052 ns/op
+--- BENCH: BenchmarkIndexSelector
+	bench_array_test.go:85: IndexSelector=4
+	bench_array_test.go:85: IndexSelector=4
+	bench_array_test.go:85: IndexSelector=4
+	bench_array_test.go:85: IndexSelector=4
+BenchmarkIndexOfNode	100000000	        10.5 ns/op
+--- BENCH: BenchmarkIndexOfNode
+	bench_array_test.go:99: IndexOfNode=2
+	bench_array_test.go:99: IndexOfNode=2
+	bench_array_test.go:99: IndexOfNode=2
+	bench_array_test.go:99: IndexOfNode=2
+	bench_array_test.go:99: IndexOfNode=2
+BenchmarkIndexOfSelection	100000000	        11.6 ns/op
+--- BENCH: BenchmarkIndexOfSelection
+	bench_array_test.go:111: IndexOfSelection=2
+	bench_array_test.go:111: IndexOfSelection=2
+	bench_array_test.go:111: IndexOfSelection=2
+	bench_array_test.go:111: IndexOfSelection=2
+	bench_array_test.go:111: IndexOfSelection=2
+BenchmarkMetalReviewExample	   10000	    189556 ns/op
+--- BENCH: BenchmarkMetalReviewExample
+	bench_example_test.go:40: Review 0: Midnight - Complete and Total Hell (8.5).
+		Review 1: Over Your Threshold - Facticity (6.0).
+		Review 2: Nuclear Death Terror - Chaos Reigns (7.5).
+		Review 3: Evoken - Atra Mors (9.5).
+		
+	bench_example_test.go:41: MetalReviewExample=10
+	bench_example_test.go:40: Review 0: Midnight - Complete and Total Hell (8.5).
+		Review 1: Over Your Threshold - Facticity (6.0).
+		Review 2: Nuclear Death Terror - Chaos Reigns (7.5).
+		Review 3: Evoken - Atra Mors (9.5).
+	... [output truncated]
+BenchmarkAdd	  200000	     13714 ns/op
+--- BENCH: BenchmarkAdd
+	bench_expand_test.go:20: Add=43
+	bench_expand_test.go:20: Add=43
+	bench_expand_test.go:20: Add=43
+	bench_expand_test.go:20: Add=43
+BenchmarkAddSelection	10000000	       200 ns/op
+--- BENCH: BenchmarkAddSelection
+	bench_expand_test.go:37: AddSelection=43
+	bench_expand_test.go:37: AddSelection=43
+	bench_expand_test.go:37: AddSelection=43
+	bench_expand_test.go:37: AddSelection=43
+	bench_expand_test.go:37: AddSelection=43
+BenchmarkAddNodes	10000000	       186 ns/op
+--- BENCH: BenchmarkAddNodes
+	bench_expand_test.go:55: AddNodes=43
+	bench_expand_test.go:55: AddNodes=43
+	bench_expand_test.go:55: AddNodes=43
+	bench_expand_test.go:55: AddNodes=43
+	bench_expand_test.go:55: AddNodes=43
+BenchmarkAndSelf	 1000000	      2532 ns/op
+--- BENCH: BenchmarkAndSelf
+	bench_expand_test.go:71: AndSelf=44
+	bench_expand_test.go:71: AndSelf=44
+	bench_expand_test.go:71: AndSelf=44
+	bench_expand_test.go:71: AndSelf=44
+BenchmarkFilter	  100000	     25199 ns/op
+--- BENCH: BenchmarkFilter
+	bench_filter_test.go:20: Filter=13
+	bench_filter_test.go:20: Filter=13
+	bench_filter_test.go:20: Filter=13
+	bench_filter_test.go:20: Filter=13
+BenchmarkNot	  100000	     29162 ns/op
+--- BENCH: BenchmarkNot
+	bench_filter_test.go:36: Not=371
+	bench_filter_test.go:36: Not=371
+	bench_filter_test.go:36: Not=371
+	bench_filter_test.go:36: Not=371
+BenchmarkFilterFunction	   50000	     60733 ns/op
+--- BENCH: BenchmarkFilterFunction
+	bench_filter_test.go:55: FilterFunction=112
+	bench_filter_test.go:55: FilterFunction=112
+	bench_filter_test.go:55: FilterFunction=112
+	bench_filter_test.go:55: FilterFunction=112
+BenchmarkNotFunction	   50000	     66124 ns/op
+--- BENCH: BenchmarkNotFunction
+	bench_filter_test.go:74: NotFunction=261
+	bench_filter_test.go:74: NotFunction=261
+	bench_filter_test.go:74: NotFunction=261
+	bench_filter_test.go:74: NotFunction=261
+BenchmarkFilterNodes	   50000	     59489 ns/op
+--- BENCH: BenchmarkFilterNodes
+	bench_filter_test.go:92: FilterNodes=2
+	bench_filter_test.go:92: FilterNodes=2
+	bench_filter_test.go:92: FilterNodes=2
+	bench_filter_test.go:92: FilterNodes=2
+BenchmarkNotNodes	   50000	     73623 ns/op
+--- BENCH: BenchmarkNotNodes
+	bench_filter_test.go:110: NotNodes=360
+	bench_filter_test.go:110: NotNodes=360
+	bench_filter_test.go:110: NotNodes=360
+	bench_filter_test.go:110: NotNodes=360
+BenchmarkFilterSelection	   50000	     60053 ns/op
+--- BENCH: BenchmarkFilterSelection
+	bench_filter_test.go:127: FilterSelection=2
+	bench_filter_test.go:127: FilterSelection=2
+	bench_filter_test.go:127: FilterSelection=2
+	bench_filter_test.go:127: FilterSelection=2
+BenchmarkNotSelection	   50000	     73477 ns/op
+--- BENCH: BenchmarkNotSelection
+	bench_filter_test.go:144: NotSelection=360
+	bench_filter_test.go:144: NotSelection=360
+	bench_filter_test.go:144: NotSelection=360
+	bench_filter_test.go:144: NotSelection=360
+BenchmarkHas	    5000	    364859 ns/op
+--- BENCH: BenchmarkHas
+	bench_filter_test.go:160: Has=13
+	bench_filter_test.go:160: Has=13
+	bench_filter_test.go:160: Has=13
+BenchmarkHasNodes	   10000	    226980 ns/op
+--- BENCH: BenchmarkHasNodes
+	bench_filter_test.go:178: HasNodes=15
+	bench_filter_test.go:178: HasNodes=15
+	bench_filter_test.go:178: HasNodes=15
+BenchmarkHasSelection	   10000	    220471 ns/op
+--- BENCH: BenchmarkHasSelection
+	bench_filter_test.go:195: HasSelection=15
+	bench_filter_test.go:195: HasSelection=15
+	bench_filter_test.go:195: HasSelection=15
+BenchmarkEnd	500000000	         4.64 ns/op
+--- BENCH: BenchmarkEnd
+	bench_filter_test.go:211: End=373
+	bench_filter_test.go:211: End=373
+	bench_filter_test.go:211: End=373
+	bench_filter_test.go:211: End=373
+	bench_filter_test.go:211: End=373
+	bench_filter_test.go:211: End=373
+BenchmarkEach	  200000	      8811 ns/op
+--- BENCH: BenchmarkEach
+	bench_iteration_test.go:22: Each=59
+	bench_iteration_test.go:22: Each=59
+	bench_iteration_test.go:22: Each=59
+	bench_iteration_test.go:22: Each=59
+BenchmarkMap	  100000	     15365 ns/op
+--- BENCH: BenchmarkMap
+	bench_iteration_test.go:41: Map=59
+	bench_iteration_test.go:41: Map=59
+	bench_iteration_test.go:41: Map=59
+	bench_iteration_test.go:41: Map=59
+BenchmarkEachWithBreak	 1000000	      1559 ns/op
+--- BENCH: BenchmarkEachWithBreak
+	bench_iteration_test.go:61: Each=10
+	bench_iteration_test.go:61: Each=10
+	bench_iteration_test.go:61: Each=10
+	bench_iteration_test.go:61: Each=10
+BenchmarkAttr	50000000	        31.7 ns/op
+--- BENCH: BenchmarkAttr
+	bench_property_test.go:16: Attr=firstHeading
+	bench_property_test.go:16: Attr=firstHeading
+	bench_property_test.go:16: Attr=firstHeading
+	bench_property_test.go:16: Attr=firstHeading
+	bench_property_test.go:16: Attr=firstHeading
+BenchmarkText	  200000	     13901 ns/op
+BenchmarkLength	2000000000	         0.31 ns/op
+--- BENCH: BenchmarkLength
+	bench_property_test.go:37: Length=14
+	bench_property_test.go:37: Length=14
+	bench_property_test.go:37: Length=14
+	bench_property_test.go:37: Length=14
+	bench_property_test.go:37: Length=14
+	bench_property_test.go:37: Length=14
+BenchmarkHtml	 5000000	       541 ns/op
+BenchmarkIs	  100000	     29435 ns/op
+--- BENCH: BenchmarkIs
+	bench_query_test.go:16: Is=true
+	bench_query_test.go:16: Is=true
+	bench_query_test.go:16: Is=true
+	bench_query_test.go:16: Is=true
+BenchmarkIsPositional	  100000	     22938 ns/op
+--- BENCH: BenchmarkIsPositional
+	bench_query_test.go:28: IsPositional=true
+	bench_query_test.go:28: IsPositional=true
+	bench_query_test.go:28: IsPositional=true
+	bench_query_test.go:28: IsPositional=true
+BenchmarkIsFunction	 1000000	      2185 ns/op
+--- BENCH: BenchmarkIsFunction
+	bench_query_test.go:43: IsFunction=true
+	bench_query_test.go:43: IsFunction=true
+	bench_query_test.go:43: IsFunction=true
+	bench_query_test.go:43: IsFunction=true
+BenchmarkIsSelection	   50000	     60607 ns/op
+--- BENCH: BenchmarkIsSelection
+	bench_query_test.go:56: IsSelection=true
+	bench_query_test.go:56: IsSelection=true
+	bench_query_test.go:56: IsSelection=true
+	bench_query_test.go:56: IsSelection=true
+BenchmarkIsNodes	   50000	     61599 ns/op
+--- BENCH: BenchmarkIsNodes
+	bench_query_test.go:70: IsNodes=true
+	bench_query_test.go:70: IsNodes=true
+	bench_query_test.go:70: IsNodes=true
+	bench_query_test.go:70: IsNodes=true
+BenchmarkHasClass	    5000	    395436 ns/op
+--- BENCH: BenchmarkHasClass
+	bench_query_test.go:82: HasClass=true
+	bench_query_test.go:82: HasClass=true
+	bench_query_test.go:82: HasClass=true
+BenchmarkContains	100000000	        11.0 ns/op
+--- BENCH: BenchmarkContains
+	bench_query_test.go:96: Contains=true
+	bench_query_test.go:96: Contains=true
+	bench_query_test.go:96: Contains=true
+	bench_query_test.go:96: Contains=true
+	bench_query_test.go:96: Contains=true
+BenchmarkFind	  200000	     13788 ns/op
+--- BENCH: BenchmarkFind
+	bench_traversal_test.go:18: Find=41
+	bench_traversal_test.go:18: Find=41
+	bench_traversal_test.go:18: Find=41
+	bench_traversal_test.go:18: Find=41
+BenchmarkFindWithinSelection	   50000	     54253 ns/op
+--- BENCH: BenchmarkFindWithinSelection
+	bench_traversal_test.go:34: FindWithinSelection=39
+	bench_traversal_test.go:34: FindWithinSelection=39
+	bench_traversal_test.go:34: FindWithinSelection=39
+	bench_traversal_test.go:34: FindWithinSelection=39
+BenchmarkFindSelection	    5000	    438879 ns/op
+--- BENCH: BenchmarkFindSelection
+	bench_traversal_test.go:51: FindSelection=73
+	bench_traversal_test.go:51: FindSelection=73
+	bench_traversal_test.go:51: FindSelection=73
+BenchmarkFindNodes	    5000	    437225 ns/op
+--- BENCH: BenchmarkFindNodes
+	bench_traversal_test.go:69: FindNodes=73
+	bench_traversal_test.go:69: FindNodes=73
+	bench_traversal_test.go:69: FindNodes=73
+BenchmarkContents	 1000000	      2844 ns/op
+--- BENCH: BenchmarkContents
+	bench_traversal_test.go:85: Contents=16
+	bench_traversal_test.go:85: Contents=16
+	bench_traversal_test.go:85: Contents=16
+	bench_traversal_test.go:85: Contents=16
+BenchmarkContentsFiltered	  500000	      4528 ns/op
+--- BENCH: BenchmarkContentsFiltered
+	bench_traversal_test.go:101: ContentsFiltered=1
+	bench_traversal_test.go:101: ContentsFiltered=1
+	bench_traversal_test.go:101: ContentsFiltered=1
+	bench_traversal_test.go:101: ContentsFiltered=1
+BenchmarkChildren	 5000000	       552 ns/op
+--- BENCH: BenchmarkChildren
+	bench_traversal_test.go:117: Children=2
+	bench_traversal_test.go:117: Children=2
+	bench_traversal_test.go:117: Children=2
+	bench_traversal_test.go:117: Children=2
+	bench_traversal_test.go:117: Children=2
+BenchmarkChildrenFiltered	  500000	      3345 ns/op
+--- BENCH: BenchmarkChildrenFiltered
+	bench_traversal_test.go:133: ChildrenFiltered=2
+	bench_traversal_test.go:133: ChildrenFiltered=2
+	bench_traversal_test.go:133: ChildrenFiltered=2
+	bench_traversal_test.go:133: ChildrenFiltered=2
+BenchmarkParent	   50000	     39482 ns/op
+--- BENCH: BenchmarkParent
+	bench_traversal_test.go:149: Parent=55
+	bench_traversal_test.go:149: Parent=55
+	bench_traversal_test.go:149: Parent=55
+	bench_traversal_test.go:149: Parent=55
+BenchmarkParentFiltered	   50000	     42113 ns/op
+--- BENCH: BenchmarkParentFiltered
+	bench_traversal_test.go:165: ParentFiltered=4
+	bench_traversal_test.go:165: ParentFiltered=4
+	bench_traversal_test.go:165: ParentFiltered=4
+	bench_traversal_test.go:165: ParentFiltered=4
+BenchmarkParents	   20000	     84136 ns/op
+--- BENCH: BenchmarkParents
+	bench_traversal_test.go:181: Parents=73
+	bench_traversal_test.go:181: Parents=73
+	bench_traversal_test.go:181: Parents=73
+	bench_traversal_test.go:181: Parents=73
+BenchmarkParentsFiltered	   20000	     86041 ns/op
+--- BENCH: BenchmarkParentsFiltered
+	bench_traversal_test.go:197: ParentsFiltered=18
+	bench_traversal_test.go:197: ParentsFiltered=18
+	bench_traversal_test.go:197: ParentsFiltered=18
+	bench_traversal_test.go:197: ParentsFiltered=18
+BenchmarkParentsUntil	   50000	     65844 ns/op
+--- BENCH: BenchmarkParentsUntil
+	bench_traversal_test.go:213: ParentsUntil=52
+	bench_traversal_test.go:213: ParentsUntil=52
+	bench_traversal_test.go:213: ParentsUntil=52
+	bench_traversal_test.go:213: ParentsUntil=52
+BenchmarkParentsUntilSelection	   10000	    146903 ns/op
+--- BENCH: BenchmarkParentsUntilSelection
+	bench_traversal_test.go:230: ParentsUntilSelection=70
+	bench_traversal_test.go:230: ParentsUntilSelection=70
+	bench_traversal_test.go:230: ParentsUntilSelection=70
+BenchmarkParentsUntilNodes	   10000	    146638 ns/op
+--- BENCH: BenchmarkParentsUntilNodes
+	bench_traversal_test.go:248: ParentsUntilNodes=70
+	bench_traversal_test.go:248: ParentsUntilNodes=70
+	bench_traversal_test.go:248: ParentsUntilNodes=70
+BenchmarkParentsFilteredUntil	  100000	     16413 ns/op
+--- BENCH: BenchmarkParentsFilteredUntil
+	bench_traversal_test.go:264: ParentsFilteredUntil=2
+	bench_traversal_test.go:264: ParentsFilteredUntil=2
+	bench_traversal_test.go:264: ParentsFilteredUntil=2
+	bench_traversal_test.go:264: ParentsFilteredUntil=2
+BenchmarkParentsFilteredUntilSelection	  100000	     20366 ns/op
+--- BENCH: BenchmarkParentsFilteredUntilSelection
+	bench_traversal_test.go:281: ParentsFilteredUntilSelection=2
+	bench_traversal_test.go:281: ParentsFilteredUntilSelection=2
+	bench_traversal_test.go:281: ParentsFilteredUntilSelection=2
+	bench_traversal_test.go:281: ParentsFilteredUntilSelection=2
+BenchmarkParentsFilteredUntilNodes	  100000	     18800 ns/op
+--- BENCH: BenchmarkParentsFilteredUntilNodes
+	bench_traversal_test.go:299: ParentsFilteredUntilNodes=2
+	bench_traversal_test.go:299: ParentsFilteredUntilNodes=2
+	bench_traversal_test.go:299: ParentsFilteredUntilNodes=2
+	bench_traversal_test.go:299: ParentsFilteredUntilNodes=2
+BenchmarkSiblings	   50000	     63443 ns/op
+--- BENCH: BenchmarkSiblings
+	bench_traversal_test.go:315: Siblings=293
+	bench_traversal_test.go:315: Siblings=293
+	bench_traversal_test.go:315: Siblings=293
+	bench_traversal_test.go:315: Siblings=293
+BenchmarkSiblingsFiltered	   50000	     69250 ns/op
+--- BENCH: BenchmarkSiblingsFiltered
+	bench_traversal_test.go:331: SiblingsFiltered=46
+	bench_traversal_test.go:331: SiblingsFiltered=46
+	bench_traversal_test.go:331: SiblingsFiltered=46
+	bench_traversal_test.go:331: SiblingsFiltered=46
+BenchmarkNext	  200000	      9193 ns/op
+--- BENCH: BenchmarkNext
+	bench_traversal_test.go:347: Next=49
+	bench_traversal_test.go:347: Next=49
+	bench_traversal_test.go:347: Next=49
+	bench_traversal_test.go:347: Next=49
+BenchmarkNextFiltered	  200000	     10767 ns/op
+--- BENCH: BenchmarkNextFiltered
+	bench_traversal_test.go:363: NextFiltered=6
+	bench_traversal_test.go:363: NextFiltered=6
+	bench_traversal_test.go:363: NextFiltered=6
+	bench_traversal_test.go:363: NextFiltered=6
+BenchmarkNextAll	   50000	     42829 ns/op
+--- BENCH: BenchmarkNextAll
+	bench_traversal_test.go:379: NextAll=234
+	bench_traversal_test.go:379: NextAll=234
+	bench_traversal_test.go:379: NextAll=234
+	bench_traversal_test.go:379: NextAll=234
+BenchmarkNextAllFiltered	   50000	     48174 ns/op
+--- BENCH: BenchmarkNextAllFiltered
+	bench_traversal_test.go:395: NextAllFiltered=33
+	bench_traversal_test.go:395: NextAllFiltered=33
+	bench_traversal_test.go:395: NextAllFiltered=33
+	bench_traversal_test.go:395: NextAllFiltered=33
+BenchmarkPrev	  200000	      9114 ns/op
+--- BENCH: BenchmarkPrev
+	bench_traversal_test.go:411: Prev=49
+	bench_traversal_test.go:411: Prev=49
+	bench_traversal_test.go:411: Prev=49
+	bench_traversal_test.go:411: Prev=49
+BenchmarkPrevFiltered	  200000	     11114 ns/op
+--- BENCH: BenchmarkPrevFiltered
+	bench_traversal_test.go:429: PrevFiltered=7
+	bench_traversal_test.go:429: PrevFiltered=7
+	bench_traversal_test.go:429: PrevFiltered=7
+	bench_traversal_test.go:429: PrevFiltered=7
+BenchmarkPrevAll	  100000	     16387 ns/op
+--- BENCH: BenchmarkPrevAll
+	bench_traversal_test.go:445: PrevAll=78
+	bench_traversal_test.go:445: PrevAll=78
+	bench_traversal_test.go:445: PrevAll=78
+	bench_traversal_test.go:445: PrevAll=78
+BenchmarkPrevAllFiltered	  100000	     18322 ns/op
+--- BENCH: BenchmarkPrevAllFiltered
+	bench_traversal_test.go:461: PrevAllFiltered=6
+	bench_traversal_test.go:461: PrevAllFiltered=6
+	bench_traversal_test.go:461: PrevAllFiltered=6
+	bench_traversal_test.go:461: PrevAllFiltered=6
+BenchmarkNextUntil	   20000	     83828 ns/op
+--- BENCH: BenchmarkNextUntil
+	bench_traversal_test.go:477: NextUntil=84
+	bench_traversal_test.go:477: NextUntil=84
+	bench_traversal_test.go:477: NextUntil=84
+	bench_traversal_test.go:477: NextUntil=84
+BenchmarkNextUntilSelection	   50000	     58822 ns/op
+--- BENCH: BenchmarkNextUntilSelection
+	bench_traversal_test.go:494: NextUntilSelection=42
+	bench_traversal_test.go:494: NextUntilSelection=42
+	bench_traversal_test.go:494: NextUntilSelection=42
+	bench_traversal_test.go:494: NextUntilSelection=42
+BenchmarkNextUntilNodes	  100000	     23173 ns/op
+--- BENCH: BenchmarkNextUntilNodes
+	bench_traversal_test.go:512: NextUntilNodes=12
+	bench_traversal_test.go:512: NextUntilNodes=12
+	bench_traversal_test.go:512: NextUntilNodes=12
+	bench_traversal_test.go:512: NextUntilNodes=12
+BenchmarkPrevUntil	   10000	    219407 ns/op
+--- BENCH: BenchmarkPrevUntil
+	bench_traversal_test.go:528: PrevUntil=238
+	bench_traversal_test.go:528: PrevUntil=238
+	bench_traversal_test.go:528: PrevUntil=238
+BenchmarkPrevUntilSelection	   20000	     76033 ns/op
+--- BENCH: BenchmarkPrevUntilSelection
+	bench_traversal_test.go:545: PrevUntilSelection=49
+	bench_traversal_test.go:545: PrevUntilSelection=49
+	bench_traversal_test.go:545: PrevUntilSelection=49
+	bench_traversal_test.go:545: PrevUntilSelection=49
+BenchmarkPrevUntilNodes	  100000	     19417 ns/op
+--- BENCH: BenchmarkPrevUntilNodes
+	bench_traversal_test.go:563: PrevUntilNodes=11
+	bench_traversal_test.go:563: PrevUntilNodes=11
+	bench_traversal_test.go:563: PrevUntilNodes=11
+	bench_traversal_test.go:563: PrevUntilNodes=11
+BenchmarkNextFilteredUntil	   50000	     44648 ns/op
+--- BENCH: BenchmarkNextFilteredUntil
+	bench_traversal_test.go:579: NextFilteredUntil=22
+	bench_traversal_test.go:579: NextFilteredUntil=22
+	bench_traversal_test.go:579: NextFilteredUntil=22
+	bench_traversal_test.go:579: NextFilteredUntil=22
+BenchmarkNextFilteredUntilSelection	   50000	     62751 ns/op
+--- BENCH: BenchmarkNextFilteredUntilSelection
+	bench_traversal_test.go:596: NextFilteredUntilSelection=22
+	bench_traversal_test.go:596: NextFilteredUntilSelection=22
+	bench_traversal_test.go:596: NextFilteredUntilSelection=22
+	bench_traversal_test.go:596: NextFilteredUntilSelection=22
+BenchmarkNextFilteredUntilNodes	   50000	     62035 ns/op
+--- BENCH: BenchmarkNextFilteredUntilNodes
+	bench_traversal_test.go:614: NextFilteredUntilNodes=22
+	bench_traversal_test.go:614: NextFilteredUntilNodes=22
+	bench_traversal_test.go:614: NextFilteredUntilNodes=22
+	bench_traversal_test.go:614: NextFilteredUntilNodes=22
+BenchmarkPrevFilteredUntil	   50000	     43331 ns/op
+--- BENCH: BenchmarkPrevFilteredUntil
+	bench_traversal_test.go:630: PrevFilteredUntil=20
+	bench_traversal_test.go:630: PrevFilteredUntil=20
+	bench_traversal_test.go:630: PrevFilteredUntil=20
+	bench_traversal_test.go:630: PrevFilteredUntil=20
+BenchmarkPrevFilteredUntilSelection	   50000	     64767 ns/op
+--- BENCH: BenchmarkPrevFilteredUntilSelection
+	bench_traversal_test.go:647: PrevFilteredUntilSelection=20
+	bench_traversal_test.go:647: PrevFilteredUntilSelection=20
+	bench_traversal_test.go:647: PrevFilteredUntilSelection=20
+	bench_traversal_test.go:647: PrevFilteredUntilSelection=20
+BenchmarkPrevFilteredUntilNodes	   50000	     67808 ns/op
+--- BENCH: BenchmarkPrevFilteredUntilNodes
+	bench_traversal_test.go:665: PrevFilteredUntilNodes=20
+	bench_traversal_test.go:665: PrevFilteredUntilNodes=20
+	bench_traversal_test.go:665: PrevFilteredUntilNodes=20
+	bench_traversal_test.go:665: PrevFilteredUntilNodes=20
+BenchmarkClosest	  500000	      4870 ns/op
+--- BENCH: BenchmarkClosest
+	bench_traversal_test.go:681: Closest=2
+	bench_traversal_test.go:681: Closest=2
+	bench_traversal_test.go:681: Closest=2
+	bench_traversal_test.go:681: Closest=2
+BenchmarkClosestSelection	 5000000	       656 ns/op
+--- BENCH: BenchmarkClosestSelection
+	bench_traversal_test.go:698: ClosestSelection=2
+	bench_traversal_test.go:698: ClosestSelection=2
+	bench_traversal_test.go:698: ClosestSelection=2
+	bench_traversal_test.go:698: ClosestSelection=2
+	bench_traversal_test.go:698: ClosestSelection=2
+BenchmarkClosestNodes	 5000000	       663 ns/op
+--- BENCH: BenchmarkClosestNodes
+	bench_traversal_test.go:715: ClosestNodes=2
+	bench_traversal_test.go:715: ClosestNodes=2
+	bench_traversal_test.go:715: ClosestNodes=2
+	bench_traversal_test.go:715: ClosestNodes=2
+	bench_traversal_test.go:715: ClosestNodes=2
+ok  	github.com/PuerkitoBio/goquery	218.007s

+ 477 - 0
src/github.com/PuerkitoBio/goquery/bench/v0.3.2-go1.2rc1

@@ -0,0 +1,477 @@
+PASS
+BenchmarkFirst	20000000	        91.0 ns/op
+BenchmarkLast	20000000	        90.5 ns/op
+BenchmarkEq	20000000	        90.2 ns/op
+BenchmarkSlice	20000000	        88.0 ns/op
+BenchmarkGet	1000000000	         2.04 ns/op
+BenchmarkIndex	 2000000	       935 ns/op
+--- BENCH: BenchmarkIndex
+	bench_array_test.go:73: Index=3
+	bench_array_test.go:73: Index=3
+	bench_array_test.go:73: Index=3
+	bench_array_test.go:73: Index=3
+	bench_array_test.go:73: Index=3
+BenchmarkIndexSelector	  100000	     23613 ns/op
+--- BENCH: BenchmarkIndexSelector
+	bench_array_test.go:85: IndexSelector=4
+	bench_array_test.go:85: IndexSelector=4
+	bench_array_test.go:85: IndexSelector=4
+	bench_array_test.go:85: IndexSelector=4
+BenchmarkIndexOfNode	100000000	        10.2 ns/op
+--- BENCH: BenchmarkIndexOfNode
+	bench_array_test.go:99: IndexOfNode=2
+	bench_array_test.go:99: IndexOfNode=2
+	bench_array_test.go:99: IndexOfNode=2
+	bench_array_test.go:99: IndexOfNode=2
+	bench_array_test.go:99: IndexOfNode=2
+BenchmarkIndexOfSelection	100000000	        11.0 ns/op
+--- BENCH: BenchmarkIndexOfSelection
+	bench_array_test.go:111: IndexOfSelection=2
+	bench_array_test.go:111: IndexOfSelection=2
+	bench_array_test.go:111: IndexOfSelection=2
+	bench_array_test.go:111: IndexOfSelection=2
+	bench_array_test.go:111: IndexOfSelection=2
+BenchmarkMetalReviewExample	   10000	    213843 ns/op
+--- BENCH: BenchmarkMetalReviewExample
+	bench_example_test.go:40: Review 0: Midnight - Complete and Total Hell (8.5).
+		Review 1: Over Your Threshold - Facticity (6.0).
+		Review 2: Nuclear Death Terror - Chaos Reigns (7.5).
+		Review 3: Evoken - Atra Mors (9.5).
+		
+	bench_example_test.go:41: MetalReviewExample=10
+	bench_example_test.go:40: Review 0: Midnight - Complete and Total Hell (8.5).
+		Review 1: Over Your Threshold - Facticity (6.0).
+		Review 2: Nuclear Death Terror - Chaos Reigns (7.5).
+		Review 3: Evoken - Atra Mors (9.5).
+	... [output truncated]
+BenchmarkAdd	  100000	     18671 ns/op
+--- BENCH: BenchmarkAdd
+	bench_expand_test.go:20: Add=43
+	bench_expand_test.go:20: Add=43
+	bench_expand_test.go:20: Add=43
+	bench_expand_test.go:20: Add=43
+BenchmarkAddSelection	10000000	       204 ns/op
+--- BENCH: BenchmarkAddSelection
+	bench_expand_test.go:37: AddSelection=43
+	bench_expand_test.go:37: AddSelection=43
+	bench_expand_test.go:37: AddSelection=43
+	bench_expand_test.go:37: AddSelection=43
+	bench_expand_test.go:37: AddSelection=43
+BenchmarkAddNodes	10000000	       195 ns/op
+--- BENCH: BenchmarkAddNodes
+	bench_expand_test.go:55: AddNodes=43
+	bench_expand_test.go:55: AddNodes=43
+	bench_expand_test.go:55: AddNodes=43
+	bench_expand_test.go:55: AddNodes=43
+	bench_expand_test.go:55: AddNodes=43
+BenchmarkAndSelf	 1000000	      2611 ns/op
+--- BENCH: BenchmarkAndSelf
+	bench_expand_test.go:71: AndSelf=44
+	bench_expand_test.go:71: AndSelf=44
+	bench_expand_test.go:71: AndSelf=44
+	bench_expand_test.go:71: AndSelf=44
+BenchmarkFilter	  100000	     27571 ns/op
+--- BENCH: BenchmarkFilter
+	bench_filter_test.go:20: Filter=13
+	bench_filter_test.go:20: Filter=13
+	bench_filter_test.go:20: Filter=13
+	bench_filter_test.go:20: Filter=13
+BenchmarkNot	   50000	     32006 ns/op
+--- BENCH: BenchmarkNot
+	bench_filter_test.go:36: Not=371
+	bench_filter_test.go:36: Not=371
+	bench_filter_test.go:36: Not=371
+	bench_filter_test.go:36: Not=371
+BenchmarkFilterFunction	   50000	     61388 ns/op
+--- BENCH: BenchmarkFilterFunction
+	bench_filter_test.go:55: FilterFunction=112
+	bench_filter_test.go:55: FilterFunction=112
+	bench_filter_test.go:55: FilterFunction=112
+	bench_filter_test.go:55: FilterFunction=112
+BenchmarkNotFunction	   50000	     66702 ns/op
+--- BENCH: BenchmarkNotFunction
+	bench_filter_test.go:74: NotFunction=261
+	bench_filter_test.go:74: NotFunction=261
+	bench_filter_test.go:74: NotFunction=261
+	bench_filter_test.go:74: NotFunction=261
+BenchmarkFilterNodes	   50000	     59699 ns/op
+--- BENCH: BenchmarkFilterNodes
+	bench_filter_test.go:92: FilterNodes=2
+	bench_filter_test.go:92: FilterNodes=2
+	bench_filter_test.go:92: FilterNodes=2
+	bench_filter_test.go:92: FilterNodes=2
+BenchmarkNotNodes	   50000	     73248 ns/op
+--- BENCH: BenchmarkNotNodes
+	bench_filter_test.go:110: NotNodes=360
+	bench_filter_test.go:110: NotNodes=360
+	bench_filter_test.go:110: NotNodes=360
+	bench_filter_test.go:110: NotNodes=360
+BenchmarkFilterSelection	   50000	     59242 ns/op
+--- BENCH: BenchmarkFilterSelection
+	bench_filter_test.go:127: FilterSelection=2
+	bench_filter_test.go:127: FilterSelection=2
+	bench_filter_test.go:127: FilterSelection=2
+	bench_filter_test.go:127: FilterSelection=2
+BenchmarkNotSelection	   50000	     73211 ns/op
+--- BENCH: BenchmarkNotSelection
+	bench_filter_test.go:144: NotSelection=360
+	bench_filter_test.go:144: NotSelection=360
+	bench_filter_test.go:144: NotSelection=360
+	bench_filter_test.go:144: NotSelection=360
+BenchmarkHas	    5000	    395087 ns/op
+--- BENCH: BenchmarkHas
+	bench_filter_test.go:160: Has=13
+	bench_filter_test.go:160: Has=13
+	bench_filter_test.go:160: Has=13
+BenchmarkHasNodes	   10000	    215849 ns/op
+--- BENCH: BenchmarkHasNodes
+	bench_filter_test.go:178: HasNodes=15
+	bench_filter_test.go:178: HasNodes=15
+	bench_filter_test.go:178: HasNodes=15
+BenchmarkHasSelection	   10000	    215612 ns/op
+--- BENCH: BenchmarkHasSelection
+	bench_filter_test.go:195: HasSelection=15
+	bench_filter_test.go:195: HasSelection=15
+	bench_filter_test.go:195: HasSelection=15
+BenchmarkEnd	500000000	         4.59 ns/op
+--- BENCH: BenchmarkEnd
+	bench_filter_test.go:211: End=373
+	bench_filter_test.go:211: End=373
+	bench_filter_test.go:211: End=373
+	bench_filter_test.go:211: End=373
+	bench_filter_test.go:211: End=373
+	bench_filter_test.go:211: End=373
+BenchmarkEach	  200000	      8588 ns/op
+--- BENCH: BenchmarkEach
+	bench_iteration_test.go:22: Each=59
+	bench_iteration_test.go:22: Each=59
+	bench_iteration_test.go:22: Each=59
+	bench_iteration_test.go:22: Each=59
+BenchmarkMap	  200000	     14444 ns/op
+--- BENCH: BenchmarkMap
+	bench_iteration_test.go:41: Map=59
+	bench_iteration_test.go:41: Map=59
+	bench_iteration_test.go:41: Map=59
+	bench_iteration_test.go:41: Map=59
+BenchmarkEachWithBreak	 1000000	      1490 ns/op
+--- BENCH: BenchmarkEachWithBreak
+	bench_iteration_test.go:61: Each=10
+	bench_iteration_test.go:61: Each=10
+	bench_iteration_test.go:61: Each=10
+	bench_iteration_test.go:61: Each=10
+BenchmarkAttr	50000000	        30.9 ns/op
+--- BENCH: BenchmarkAttr
+	bench_property_test.go:16: Attr=firstHeading
+	bench_property_test.go:16: Attr=firstHeading
+	bench_property_test.go:16: Attr=firstHeading
+	bench_property_test.go:16: Attr=firstHeading
+	bench_property_test.go:16: Attr=firstHeading
+BenchmarkText	  200000	     14017 ns/op
+BenchmarkLength	2000000000	         0.31 ns/op
+--- BENCH: BenchmarkLength
+	bench_property_test.go:37: Length=14
+	bench_property_test.go:37: Length=14
+	bench_property_test.go:37: Length=14
+	bench_property_test.go:37: Length=14
+	bench_property_test.go:37: Length=14
+	bench_property_test.go:37: Length=14
+BenchmarkHtml	 5000000	       577 ns/op
+BenchmarkIs	   50000	     31936 ns/op
+--- BENCH: BenchmarkIs
+	bench_query_test.go:16: Is=true
+	bench_query_test.go:16: Is=true
+	bench_query_test.go:16: Is=true
+	bench_query_test.go:16: Is=true
+BenchmarkIsPositional	  100000	     23372 ns/op
+--- BENCH: BenchmarkIsPositional
+	bench_query_test.go:28: IsPositional=true
+	bench_query_test.go:28: IsPositional=true
+	bench_query_test.go:28: IsPositional=true
+	bench_query_test.go:28: IsPositional=true
+BenchmarkIsFunction	 1000000	      2170 ns/op
+--- BENCH: BenchmarkIsFunction
+	bench_query_test.go:43: IsFunction=true
+	bench_query_test.go:43: IsFunction=true
+	bench_query_test.go:43: IsFunction=true
+	bench_query_test.go:43: IsFunction=true
+BenchmarkIsSelection	   50000	     59814 ns/op
+--- BENCH: BenchmarkIsSelection
+	bench_query_test.go:56: IsSelection=true
+	bench_query_test.go:56: IsSelection=true
+	bench_query_test.go:56: IsSelection=true
+	bench_query_test.go:56: IsSelection=true
+BenchmarkIsNodes	   50000	     59629 ns/op
+--- BENCH: BenchmarkIsNodes
+	bench_query_test.go:70: IsNodes=true
+	bench_query_test.go:70: IsNodes=true
+	bench_query_test.go:70: IsNodes=true
+	bench_query_test.go:70: IsNodes=true
+BenchmarkHasClass	    5000	    384894 ns/op
+--- BENCH: BenchmarkHasClass
+	bench_query_test.go:82: HasClass=true
+	bench_query_test.go:82: HasClass=true
+	bench_query_test.go:82: HasClass=true
+BenchmarkContains	100000000	        11.4 ns/op
+--- BENCH: BenchmarkContains
+	bench_query_test.go:96: Contains=true
+	bench_query_test.go:96: Contains=true
+	bench_query_test.go:96: Contains=true
+	bench_query_test.go:96: Contains=true
+	bench_query_test.go:96: Contains=true
+BenchmarkFind	  100000	     23545 ns/op
+--- BENCH: BenchmarkFind
+	bench_traversal_test.go:18: Find=41
+	bench_traversal_test.go:18: Find=41
+	bench_traversal_test.go:18: Find=41
+	bench_traversal_test.go:18: Find=41
+BenchmarkFindWithinSelection	   50000	     63775 ns/op
+--- BENCH: BenchmarkFindWithinSelection
+	bench_traversal_test.go:34: FindWithinSelection=39
+	bench_traversal_test.go:34: FindWithinSelection=39
+	bench_traversal_test.go:34: FindWithinSelection=39
+	bench_traversal_test.go:34: FindWithinSelection=39
+BenchmarkFindSelection	    5000	    441958 ns/op
+--- BENCH: BenchmarkFindSelection
+	bench_traversal_test.go:51: FindSelection=73
+	bench_traversal_test.go:51: FindSelection=73
+	bench_traversal_test.go:51: FindSelection=73
+BenchmarkFindNodes	    5000	    437717 ns/op
+--- BENCH: BenchmarkFindNodes
+	bench_traversal_test.go:69: FindNodes=73
+	bench_traversal_test.go:69: FindNodes=73
+	bench_traversal_test.go:69: FindNodes=73
+BenchmarkContents	 1000000	      2799 ns/op
+--- BENCH: BenchmarkContents
+	bench_traversal_test.go:85: Contents=16
+	bench_traversal_test.go:85: Contents=16
+	bench_traversal_test.go:85: Contents=16
+	bench_traversal_test.go:85: Contents=16
+BenchmarkContentsFiltered	  500000	      4489 ns/op
+--- BENCH: BenchmarkContentsFiltered
+	bench_traversal_test.go:101: ContentsFiltered=1
+	bench_traversal_test.go:101: ContentsFiltered=1
+	bench_traversal_test.go:101: ContentsFiltered=1
+	bench_traversal_test.go:101: ContentsFiltered=1
+BenchmarkChildren	 5000000	       546 ns/op
+--- BENCH: BenchmarkChildren
+	bench_traversal_test.go:117: Children=2
+	bench_traversal_test.go:117: Children=2
+	bench_traversal_test.go:117: Children=2
+	bench_traversal_test.go:117: Children=2
+	bench_traversal_test.go:117: Children=2
+BenchmarkChildrenFiltered	  500000	      3472 ns/op
+--- BENCH: BenchmarkChildrenFiltered
+	bench_traversal_test.go:133: ChildrenFiltered=2
+	bench_traversal_test.go:133: ChildrenFiltered=2
+	bench_traversal_test.go:133: ChildrenFiltered=2
+	bench_traversal_test.go:133: ChildrenFiltered=2
+BenchmarkParent	   50000	     39067 ns/op
+--- BENCH: BenchmarkParent
+	bench_traversal_test.go:149: Parent=55
+	bench_traversal_test.go:149: Parent=55
+	bench_traversal_test.go:149: Parent=55
+	bench_traversal_test.go:149: Parent=55
+BenchmarkParentFiltered	   50000	     41450 ns/op
+--- BENCH: BenchmarkParentFiltered
+	bench_traversal_test.go:165: ParentFiltered=4
+	bench_traversal_test.go:165: ParentFiltered=4
+	bench_traversal_test.go:165: ParentFiltered=4
+	bench_traversal_test.go:165: ParentFiltered=4
+BenchmarkParents	   20000	     84864 ns/op
+--- BENCH: BenchmarkParents
+	bench_traversal_test.go:181: Parents=73
+	bench_traversal_test.go:181: Parents=73
+	bench_traversal_test.go:181: Parents=73
+	bench_traversal_test.go:181: Parents=73
+BenchmarkParentsFiltered	   20000	     87823 ns/op
+--- BENCH: BenchmarkParentsFiltered
+	bench_traversal_test.go:197: ParentsFiltered=18
+	bench_traversal_test.go:197: ParentsFiltered=18
+	bench_traversal_test.go:197: ParentsFiltered=18
+	bench_traversal_test.go:197: ParentsFiltered=18
+BenchmarkParentsUntil	   50000	     65986 ns/op
+--- BENCH: BenchmarkParentsUntil
+	bench_traversal_test.go:213: ParentsUntil=52
+	bench_traversal_test.go:213: ParentsUntil=52
+	bench_traversal_test.go:213: ParentsUntil=52
+	bench_traversal_test.go:213: ParentsUntil=52
+BenchmarkParentsUntilSelection	   10000	    149798 ns/op
+--- BENCH: BenchmarkParentsUntilSelection
+	bench_traversal_test.go:230: ParentsUntilSelection=70
+	bench_traversal_test.go:230: ParentsUntilSelection=70
+	bench_traversal_test.go:230: ParentsUntilSelection=70
+BenchmarkParentsUntilNodes	   10000	    148144 ns/op
+--- BENCH: BenchmarkParentsUntilNodes
+	bench_traversal_test.go:248: ParentsUntilNodes=70
+	bench_traversal_test.go:248: ParentsUntilNodes=70
+	bench_traversal_test.go:248: ParentsUntilNodes=70
+BenchmarkParentsFilteredUntil	  100000	     15579 ns/op
+--- BENCH: BenchmarkParentsFilteredUntil
+	bench_traversal_test.go:264: ParentsFilteredUntil=2
+	bench_traversal_test.go:264: ParentsFilteredUntil=2
+	bench_traversal_test.go:264: ParentsFilteredUntil=2
+	bench_traversal_test.go:264: ParentsFilteredUntil=2
+BenchmarkParentsFilteredUntilSelection	  100000	     19094 ns/op
+--- BENCH: BenchmarkParentsFilteredUntilSelection
+	bench_traversal_test.go:281: ParentsFilteredUntilSelection=2
+	bench_traversal_test.go:281: ParentsFilteredUntilSelection=2
+	bench_traversal_test.go:281: ParentsFilteredUntilSelection=2
+	bench_traversal_test.go:281: ParentsFilteredUntilSelection=2
+BenchmarkParentsFilteredUntilNodes	  100000	     19037 ns/op
+--- BENCH: BenchmarkParentsFilteredUntilNodes
+	bench_traversal_test.go:299: ParentsFilteredUntilNodes=2
+	bench_traversal_test.go:299: ParentsFilteredUntilNodes=2
+	bench_traversal_test.go:299: ParentsFilteredUntilNodes=2
+	bench_traversal_test.go:299: ParentsFilteredUntilNodes=2
+BenchmarkSiblings	   50000	     63891 ns/op
+--- BENCH: BenchmarkSiblings
+	bench_traversal_test.go:315: Siblings=293
+	bench_traversal_test.go:315: Siblings=293
+	bench_traversal_test.go:315: Siblings=293
+	bench_traversal_test.go:315: Siblings=293
+BenchmarkSiblingsFiltered	   50000	     70424 ns/op
+--- BENCH: BenchmarkSiblingsFiltered
+	bench_traversal_test.go:331: SiblingsFiltered=46
+	bench_traversal_test.go:331: SiblingsFiltered=46
+	bench_traversal_test.go:331: SiblingsFiltered=46
+	bench_traversal_test.go:331: SiblingsFiltered=46
+BenchmarkNext	  200000	      9350 ns/op
+--- BENCH: BenchmarkNext
+	bench_traversal_test.go:347: Next=49
+	bench_traversal_test.go:347: Next=49
+	bench_traversal_test.go:347: Next=49
+	bench_traversal_test.go:347: Next=49
+BenchmarkNextFiltered	  200000	     10929 ns/op
+--- BENCH: BenchmarkNextFiltered
+	bench_traversal_test.go:363: NextFiltered=6
+	bench_traversal_test.go:363: NextFiltered=6
+	bench_traversal_test.go:363: NextFiltered=6
+	bench_traversal_test.go:363: NextFiltered=6
+BenchmarkNextAll	   50000	     43398 ns/op
+--- BENCH: BenchmarkNextAll
+	bench_traversal_test.go:379: NextAll=234
+	bench_traversal_test.go:379: NextAll=234
+	bench_traversal_test.go:379: NextAll=234
+	bench_traversal_test.go:379: NextAll=234
+BenchmarkNextAllFiltered	   50000	     48519 ns/op
+--- BENCH: BenchmarkNextAllFiltered
+	bench_traversal_test.go:395: NextAllFiltered=33
+	bench_traversal_test.go:395: NextAllFiltered=33
+	bench_traversal_test.go:395: NextAllFiltered=33
+	bench_traversal_test.go:395: NextAllFiltered=33
+BenchmarkPrev	  200000	      9181 ns/op
+--- BENCH: BenchmarkPrev
+	bench_traversal_test.go:411: Prev=49
+	bench_traversal_test.go:411: Prev=49
+	bench_traversal_test.go:411: Prev=49
+	bench_traversal_test.go:411: Prev=49
+BenchmarkPrevFiltered	  200000	     10811 ns/op
+--- BENCH: BenchmarkPrevFiltered
+	bench_traversal_test.go:429: PrevFiltered=7
+	bench_traversal_test.go:429: PrevFiltered=7
+	bench_traversal_test.go:429: PrevFiltered=7
+	bench_traversal_test.go:429: PrevFiltered=7
+BenchmarkPrevAll	  100000	     15589 ns/op
+--- BENCH: BenchmarkPrevAll
+	bench_traversal_test.go:445: PrevAll=78
+	bench_traversal_test.go:445: PrevAll=78
+	bench_traversal_test.go:445: PrevAll=78
+	bench_traversal_test.go:445: PrevAll=78
+BenchmarkPrevAllFiltered	  100000	     17341 ns/op
+--- BENCH: BenchmarkPrevAllFiltered
+	bench_traversal_test.go:461: PrevAllFiltered=6
+	bench_traversal_test.go:461: PrevAllFiltered=6
+	bench_traversal_test.go:461: PrevAllFiltered=6
+	bench_traversal_test.go:461: PrevAllFiltered=6
+BenchmarkNextUntil	   20000	     80663 ns/op
+--- BENCH: BenchmarkNextUntil
+	bench_traversal_test.go:477: NextUntil=84
+	bench_traversal_test.go:477: NextUntil=84
+	bench_traversal_test.go:477: NextUntil=84
+	bench_traversal_test.go:477: NextUntil=84
+BenchmarkNextUntilSelection	   50000	     56496 ns/op
+--- BENCH: BenchmarkNextUntilSelection
+	bench_traversal_test.go:494: NextUntilSelection=42
+	bench_traversal_test.go:494: NextUntilSelection=42
+	bench_traversal_test.go:494: NextUntilSelection=42
+	bench_traversal_test.go:494: NextUntilSelection=42
+BenchmarkNextUntilNodes	  100000	     23729 ns/op
+--- BENCH: BenchmarkNextUntilNodes
+	bench_traversal_test.go:512: NextUntilNodes=12
+	bench_traversal_test.go:512: NextUntilNodes=12
+	bench_traversal_test.go:512: NextUntilNodes=12
+	bench_traversal_test.go:512: NextUntilNodes=12
+BenchmarkPrevUntil	   10000	    208267 ns/op
+--- BENCH: BenchmarkPrevUntil
+	bench_traversal_test.go:528: PrevUntil=238
+	bench_traversal_test.go:528: PrevUntil=238
+	bench_traversal_test.go:528: PrevUntil=238
+BenchmarkPrevUntilSelection	   50000	     72119 ns/op
+--- BENCH: BenchmarkPrevUntilSelection
+	bench_traversal_test.go:545: PrevUntilSelection=49
+	bench_traversal_test.go:545: PrevUntilSelection=49
+	bench_traversal_test.go:545: PrevUntilSelection=49
+	bench_traversal_test.go:545: PrevUntilSelection=49
+BenchmarkPrevUntilNodes	  100000	     18549 ns/op
+--- BENCH: BenchmarkPrevUntilNodes
+	bench_traversal_test.go:563: PrevUntilNodes=11
+	bench_traversal_test.go:563: PrevUntilNodes=11
+	bench_traversal_test.go:563: PrevUntilNodes=11
+	bench_traversal_test.go:563: PrevUntilNodes=11
+BenchmarkNextFilteredUntil	   50000	     42339 ns/op
+--- BENCH: BenchmarkNextFilteredUntil
+	bench_traversal_test.go:579: NextFilteredUntil=22
+	bench_traversal_test.go:579: NextFilteredUntil=22
+	bench_traversal_test.go:579: NextFilteredUntil=22
+	bench_traversal_test.go:579: NextFilteredUntil=22
+BenchmarkNextFilteredUntilSelection	   50000	     61916 ns/op
+--- BENCH: BenchmarkNextFilteredUntilSelection
+	bench_traversal_test.go:596: NextFilteredUntilSelection=22
+	bench_traversal_test.go:596: NextFilteredUntilSelection=22
+	bench_traversal_test.go:596: NextFilteredUntilSelection=22
+	bench_traversal_test.go:596: NextFilteredUntilSelection=22
+BenchmarkNextFilteredUntilNodes	   50000	     62139 ns/op
+--- BENCH: BenchmarkNextFilteredUntilNodes
+	bench_traversal_test.go:614: NextFilteredUntilNodes=22
+	bench_traversal_test.go:614: NextFilteredUntilNodes=22
+	bench_traversal_test.go:614: NextFilteredUntilNodes=22
+	bench_traversal_test.go:614: NextFilteredUntilNodes=22
+BenchmarkPrevFilteredUntil	   50000	     43409 ns/op
+--- BENCH: BenchmarkPrevFilteredUntil
+	bench_traversal_test.go:630: PrevFilteredUntil=20
+	bench_traversal_test.go:630: PrevFilteredUntil=20
+	bench_traversal_test.go:630: PrevFilteredUntil=20
+	bench_traversal_test.go:630: PrevFilteredUntil=20
+BenchmarkPrevFilteredUntilSelection	   50000	     63768 ns/op
+--- BENCH: BenchmarkPrevFilteredUntilSelection
+	bench_traversal_test.go:647: PrevFilteredUntilSelection=20
+	bench_traversal_test.go:647: PrevFilteredUntilSelection=20
+	bench_traversal_test.go:647: PrevFilteredUntilSelection=20
+	bench_traversal_test.go:647: PrevFilteredUntilSelection=20
+BenchmarkPrevFilteredUntilNodes	   50000	     63543 ns/op
+--- BENCH: BenchmarkPrevFilteredUntilNodes
+	bench_traversal_test.go:665: PrevFilteredUntilNodes=20
+	bench_traversal_test.go:665: PrevFilteredUntilNodes=20
+	bench_traversal_test.go:665: PrevFilteredUntilNodes=20
+	bench_traversal_test.go:665: PrevFilteredUntilNodes=20
+BenchmarkClosest	  500000	      5110 ns/op
+--- BENCH: BenchmarkClosest
+	bench_traversal_test.go:681: Closest=2
+	bench_traversal_test.go:681: Closest=2
+	bench_traversal_test.go:681: Closest=2
+	bench_traversal_test.go:681: Closest=2
+BenchmarkClosestSelection	 5000000	       629 ns/op
+--- BENCH: BenchmarkClosestSelection
+	bench_traversal_test.go:698: ClosestSelection=2
+	bench_traversal_test.go:698: ClosestSelection=2
+	bench_traversal_test.go:698: ClosestSelection=2
+	bench_traversal_test.go:698: ClosestSelection=2
+	bench_traversal_test.go:698: ClosestSelection=2
+BenchmarkClosestNodes	 5000000	       627 ns/op
+--- BENCH: BenchmarkClosestNodes
+	bench_traversal_test.go:715: ClosestNodes=2
+	bench_traversal_test.go:715: ClosestNodes=2
+	bench_traversal_test.go:715: ClosestNodes=2
+	bench_traversal_test.go:715: ClosestNodes=2
+	bench_traversal_test.go:715: ClosestNodes=2
+ok  	github.com/PuerkitoBio/goquery	215.785s

+ 85 - 0
src/github.com/PuerkitoBio/goquery/bench/v1.0.0-go1.7

@@ -0,0 +1,85 @@
+BenchmarkFirst-4                           	30000000	        50.7 ns/op	      48 B/op	       1 allocs/op
+BenchmarkLast-4                            	30000000	        50.9 ns/op	      48 B/op	       1 allocs/op
+BenchmarkEq-4                              	30000000	        55.7 ns/op	      48 B/op	       1 allocs/op
+BenchmarkSlice-4                           	500000000	         3.45 ns/op	       0 B/op	       0 allocs/op
+BenchmarkGet-4                             	2000000000	         1.68 ns/op	       0 B/op	       0 allocs/op
+BenchmarkIndex-4                           	 3000000	       541 ns/op	     248 B/op	      10 allocs/op
+BenchmarkIndexSelector-4                   	  200000	     10749 ns/op	    2464 B/op	      17 allocs/op
+BenchmarkIndexOfNode-4                     	200000000	         6.47 ns/op	       0 B/op	       0 allocs/op
+BenchmarkIndexOfSelection-4                	200000000	         7.27 ns/op	       0 B/op	       0 allocs/op
+BenchmarkMetalReviewExample-4              	   10000	    138426 ns/op	   12240 B/op	     319 allocs/op
+BenchmarkAdd-4                             	  200000	     10192 ns/op	     208 B/op	       9 allocs/op
+BenchmarkAddSelection-4                    	10000000	       158 ns/op	      48 B/op	       1 allocs/op
+BenchmarkAddNodes-4                        	10000000	       156 ns/op	      48 B/op	       1 allocs/op
+BenchmarkAndSelf-4                         	 1000000	      1588 ns/op	    1008 B/op	       5 allocs/op
+BenchmarkFilter-4                          	  100000	     20427 ns/op	     360 B/op	       8 allocs/op
+BenchmarkNot-4                             	  100000	     23508 ns/op	     136 B/op	       5 allocs/op
+BenchmarkFilterFunction-4                  	   50000	     34178 ns/op	   22976 B/op	     755 allocs/op
+BenchmarkNotFunction-4                     	   50000	     38173 ns/op	   29120 B/op	     757 allocs/op
+BenchmarkFilterNodes-4                     	   50000	     34001 ns/op	   20960 B/op	     749 allocs/op
+BenchmarkNotNodes-4                        	   30000	     40344 ns/op	   29120 B/op	     757 allocs/op
+BenchmarkFilterSelection-4                 	   50000	     33308 ns/op	   20960 B/op	     749 allocs/op
+BenchmarkNotSelection-4                    	   30000	     40748 ns/op	   29120 B/op	     757 allocs/op
+BenchmarkHas-4                             	    5000	    263346 ns/op	    1816 B/op	      48 allocs/op
+BenchmarkHasNodes-4                        	   10000	    160840 ns/op	   21184 B/op	     752 allocs/op
+BenchmarkHasSelection-4                    	   10000	    165410 ns/op	   21184 B/op	     752 allocs/op
+BenchmarkEnd-4                             	2000000000	         1.01 ns/op	       0 B/op	       0 allocs/op
+BenchmarkEach-4                            	  300000	      4664 ns/op	    3304 B/op	     118 allocs/op
+BenchmarkMap-4                             	  200000	      8286 ns/op	    5572 B/op	     184 allocs/op
+BenchmarkEachWithBreak-4                   	 2000000	       806 ns/op	     560 B/op	      20 allocs/op
+BenchmarkAttr-4                            	100000000	        21.6 ns/op	       0 B/op	       0 allocs/op
+BenchmarkText-4                            	  200000	      8909 ns/op	    7536 B/op	     110 allocs/op
+BenchmarkLength-4                          	2000000000	         0.34 ns/op	       0 B/op	       0 allocs/op
+BenchmarkHtml-4                            	 3000000	       422 ns/op	     120 B/op	       2 allocs/op
+BenchmarkIs-4                              	  100000	     22615 ns/op	      88 B/op	       4 allocs/op
+BenchmarkIsPositional-4                    	   50000	     26655 ns/op	    1112 B/op	      10 allocs/op
+BenchmarkIsFunction-4                      	 1000000	      1208 ns/op	     784 B/op	      28 allocs/op
+BenchmarkIsSelection-4                     	   50000	     33497 ns/op	   20960 B/op	     749 allocs/op
+BenchmarkIsNodes-4                         	   50000	     33572 ns/op	   20960 B/op	     749 allocs/op
+BenchmarkHasClass-4                        	   10000	    232802 ns/op	   14944 B/op	     976 allocs/op
+BenchmarkContains-4                        	200000000	         7.33 ns/op	       0 B/op	       0 allocs/op
+BenchmarkFind-4                            	  200000	     10715 ns/op	    2464 B/op	      17 allocs/op
+BenchmarkFindWithinSelection-4             	   50000	     35878 ns/op	    2176 B/op	      78 allocs/op
+BenchmarkFindSelection-4                   	   10000	    194356 ns/op	    2672 B/op	      82 allocs/op
+BenchmarkFindNodes-4                       	   10000	    195510 ns/op	    2672 B/op	      82 allocs/op
+BenchmarkContents-4                        	 1000000	      2252 ns/op	     864 B/op	      34 allocs/op
+BenchmarkContentsFiltered-4                	  500000	      3015 ns/op	    1016 B/op	      39 allocs/op
+BenchmarkChildren-4                        	 5000000	       364 ns/op	     152 B/op	       7 allocs/op
+BenchmarkChildrenFiltered-4                	 1000000	      2212 ns/op	     352 B/op	      15 allocs/op
+BenchmarkParent-4                          	   50000	     24643 ns/op	    4048 B/op	     381 allocs/op
+BenchmarkParentFiltered-4                  	   50000	     25967 ns/op	    4248 B/op	     388 allocs/op
+BenchmarkParents-4                         	   30000	     50000 ns/op	   27776 B/op	     830 allocs/op
+BenchmarkParentsFiltered-4                 	   30000	     53107 ns/op	   28360 B/op	     838 allocs/op
+BenchmarkParentsUntil-4                    	  100000	     22423 ns/op	   10352 B/op	     353 allocs/op
+BenchmarkParentsUntilSelection-4           	   20000	     86925 ns/op	   51144 B/op	    1516 allocs/op
+BenchmarkParentsUntilNodes-4               	   20000	     87597 ns/op	   51144 B/op	    1516 allocs/op
+BenchmarkParentsFilteredUntil-4            	  300000	      5568 ns/op	    2232 B/op	      86 allocs/op
+BenchmarkParentsFilteredUntilSelection-4   	  200000	     10966 ns/op	    5440 B/op	     190 allocs/op
+BenchmarkParentsFilteredUntilNodes-4       	  200000	     10919 ns/op	    5440 B/op	     190 allocs/op
+BenchmarkSiblings-4                        	   30000	     46018 ns/op	   15400 B/op	     204 allocs/op
+BenchmarkSiblingsFiltered-4                	   30000	     50566 ns/op	   16496 B/op	     213 allocs/op
+BenchmarkNext-4                            	  200000	      7921 ns/op	    3216 B/op	     112 allocs/op
+BenchmarkNextFiltered-4                    	  200000	      8804 ns/op	    3416 B/op	     118 allocs/op
+BenchmarkNextAll-4                         	   50000	     31098 ns/op	    9912 B/op	     138 allocs/op
+BenchmarkNextAllFiltered-4                 	   50000	     34677 ns/op	   11008 B/op	     147 allocs/op
+BenchmarkPrev-4                            	  200000	      7920 ns/op	    3216 B/op	     112 allocs/op
+BenchmarkPrevFiltered-4                    	  200000	      8913 ns/op	    3416 B/op	     118 allocs/op
+BenchmarkPrevAll-4                         	  200000	     10845 ns/op	    4376 B/op	     113 allocs/op
+BenchmarkPrevAllFiltered-4                 	  100000	     12030 ns/op	    4576 B/op	     119 allocs/op
+BenchmarkNextUntil-4                       	  100000	     19193 ns/op	    5760 B/op	     260 allocs/op
+BenchmarkNextUntilSelection-4              	   50000	     34829 ns/op	   18480 B/op	     542 allocs/op
+BenchmarkNextUntilNodes-4                  	  100000	     14459 ns/op	    7944 B/op	     248 allocs/op
+BenchmarkPrevUntil-4                       	   20000	     66296 ns/op	   12856 B/op	     448 allocs/op
+BenchmarkPrevUntilSelection-4              	   30000	     45037 ns/op	   23432 B/op	     689 allocs/op
+BenchmarkPrevUntilNodes-4                  	  200000	     11525 ns/op	    6152 B/op	     203 allocs/op
+BenchmarkNextFilteredUntil-4               	  100000	     12940 ns/op	    4512 B/op	     173 allocs/op
+BenchmarkNextFilteredUntilSelection-4      	   50000	     38924 ns/op	   19160 B/op	     567 allocs/op
+BenchmarkNextFilteredUntilNodes-4          	   50000	     38528 ns/op	   19160 B/op	     567 allocs/op
+BenchmarkPrevFilteredUntil-4               	  100000	     12980 ns/op	    4664 B/op	     175 allocs/op
+BenchmarkPrevFilteredUntilSelection-4      	   50000	     39671 ns/op	   19936 B/op	     587 allocs/op
+BenchmarkPrevFilteredUntilNodes-4          	   50000	     39484 ns/op	   19936 B/op	     587 allocs/op
+BenchmarkClosest-4                         	  500000	      3310 ns/op	     160 B/op	       8 allocs/op
+BenchmarkClosestSelection-4                	 5000000	       361 ns/op	      96 B/op	       6 allocs/op
+BenchmarkClosestNodes-4                    	 5000000	       359 ns/op	      96 B/op	       6 allocs/op
+PASS
+ok  	github.com/PuerkitoBio/goquery	163.718s

+ 85 - 0
src/github.com/PuerkitoBio/goquery/bench/v1.0.1a-go1.7

@@ -0,0 +1,85 @@
+BenchmarkFirst-4                           	30000000	        50.9 ns/op	      48 B/op	       1 allocs/op
+BenchmarkLast-4                            	30000000	        50.0 ns/op	      48 B/op	       1 allocs/op
+BenchmarkEq-4                              	30000000	        50.5 ns/op	      48 B/op	       1 allocs/op
+BenchmarkSlice-4                           	500000000	         3.53 ns/op	       0 B/op	       0 allocs/op
+BenchmarkGet-4                             	2000000000	         1.66 ns/op	       0 B/op	       0 allocs/op
+BenchmarkIndex-4                           	 2000000	       832 ns/op	     248 B/op	      10 allocs/op
+BenchmarkIndexSelector-4                   	  100000	     16073 ns/op	    3839 B/op	      21 allocs/op
+BenchmarkIndexOfNode-4                     	200000000	         6.38 ns/op	       0 B/op	       0 allocs/op
+BenchmarkIndexOfSelection-4                	200000000	         7.14 ns/op	       0 B/op	       0 allocs/op
+BenchmarkMetalReviewExample-4              	   10000	    140737 ns/op	   12418 B/op	     320 allocs/op
+BenchmarkAdd-4                             	  100000	     13162 ns/op	     974 B/op	      10 allocs/op
+BenchmarkAddSelection-4                    	  500000	      3160 ns/op	     814 B/op	       2 allocs/op
+BenchmarkAddNodes-4                        	  500000	      3159 ns/op	     814 B/op	       2 allocs/op
+BenchmarkAndSelf-4                         	  200000	      7423 ns/op	    2404 B/op	       9 allocs/op
+BenchmarkFilter-4                          	  100000	     19671 ns/op	     360 B/op	       8 allocs/op
+BenchmarkNot-4                             	  100000	     22577 ns/op	     136 B/op	       5 allocs/op
+BenchmarkFilterFunction-4                  	   50000	     33960 ns/op	   22976 B/op	     755 allocs/op
+BenchmarkNotFunction-4                     	   50000	     37909 ns/op	   29120 B/op	     757 allocs/op
+BenchmarkFilterNodes-4                     	   50000	     34196 ns/op	   20960 B/op	     749 allocs/op
+BenchmarkNotNodes-4                        	   30000	     40446 ns/op	   29120 B/op	     757 allocs/op
+BenchmarkFilterSelection-4                 	   50000	     33091 ns/op	   20960 B/op	     749 allocs/op
+BenchmarkNotSelection-4                    	   30000	     40609 ns/op	   29120 B/op	     757 allocs/op
+BenchmarkHas-4                             	    5000	    262936 ns/op	    2371 B/op	      50 allocs/op
+BenchmarkHasNodes-4                        	   10000	    148631 ns/op	   21184 B/op	     752 allocs/op
+BenchmarkHasSelection-4                    	   10000	    153117 ns/op	   21184 B/op	     752 allocs/op
+BenchmarkEnd-4                             	2000000000	         1.02 ns/op	       0 B/op	       0 allocs/op
+BenchmarkEach-4                            	  300000	      4653 ns/op	    3304 B/op	     118 allocs/op
+BenchmarkMap-4                             	  200000	      8257 ns/op	    5572 B/op	     184 allocs/op
+BenchmarkEachWithBreak-4                   	 2000000	       806 ns/op	     560 B/op	      20 allocs/op
+BenchmarkAttr-4                            	100000000	        22.0 ns/op	       0 B/op	       0 allocs/op
+BenchmarkText-4                            	  200000	      8913 ns/op	    7536 B/op	     110 allocs/op
+BenchmarkLength-4                          	2000000000	         0.35 ns/op	       0 B/op	       0 allocs/op
+BenchmarkHtml-4                            	 5000000	       398 ns/op	     120 B/op	       2 allocs/op
+BenchmarkIs-4                              	  100000	     22392 ns/op	      88 B/op	       4 allocs/op
+BenchmarkIsPositional-4                    	   50000	     26259 ns/op	    1112 B/op	      10 allocs/op
+BenchmarkIsFunction-4                      	 1000000	      1212 ns/op	     784 B/op	      28 allocs/op
+BenchmarkIsSelection-4                     	   50000	     33222 ns/op	   20960 B/op	     749 allocs/op
+BenchmarkIsNodes-4                         	   50000	     33408 ns/op	   20960 B/op	     749 allocs/op
+BenchmarkHasClass-4                        	   10000	    233208 ns/op	   14944 B/op	     976 allocs/op
+BenchmarkContains-4                        	200000000	         7.57 ns/op	       0 B/op	       0 allocs/op
+BenchmarkFind-4                            	  100000	     16121 ns/op	    3839 B/op	      21 allocs/op
+BenchmarkFindWithinSelection-4             	   20000	     68019 ns/op	   11521 B/op	      97 allocs/op
+BenchmarkFindSelection-4                   	    5000	    387582 ns/op	   59787 B/op	     176 allocs/op
+BenchmarkFindNodes-4                       	    5000	    389246 ns/op	   59797 B/op	     176 allocs/op
+BenchmarkContents-4                        	  200000	     11475 ns/op	    2878 B/op	      42 allocs/op
+BenchmarkContentsFiltered-4                	  200000	     11222 ns/op	    2498 B/op	      46 allocs/op
+BenchmarkChildren-4                        	 2000000	       650 ns/op	     152 B/op	       7 allocs/op
+BenchmarkChildrenFiltered-4                	  500000	      2568 ns/op	     352 B/op	      15 allocs/op
+BenchmarkParent-4                          	    2000	    702513 ns/op	  194478 B/op	     828 allocs/op
+BenchmarkParentFiltered-4                  	    2000	    690778 ns/op	  194658 B/op	     835 allocs/op
+BenchmarkParents-4                         	   10000	    124855 ns/op	   49869 B/op	     868 allocs/op
+BenchmarkParentsFiltered-4                 	   10000	    128535 ns/op	   50456 B/op	     876 allocs/op
+BenchmarkParentsUntil-4                    	   20000	     72982 ns/op	   23802 B/op	     388 allocs/op
+BenchmarkParentsUntilSelection-4           	   10000	    156099 ns/op	   72453 B/op	    1549 allocs/op
+BenchmarkParentsUntilNodes-4               	   10000	    156610 ns/op	   72455 B/op	    1549 allocs/op
+BenchmarkParentsFilteredUntil-4            	  100000	     15549 ns/op	    4068 B/op	      94 allocs/op
+BenchmarkParentsFilteredUntilSelection-4   	  100000	     20564 ns/op	    7276 B/op	     198 allocs/op
+BenchmarkParentsFilteredUntilNodes-4       	  100000	     20635 ns/op	    7276 B/op	     198 allocs/op
+BenchmarkSiblings-4                        	    3000	    565114 ns/op	  205910 B/op	     336 allocs/op
+BenchmarkSiblingsFiltered-4                	    3000	    580264 ns/op	  206993 B/op	     345 allocs/op
+BenchmarkNext-4                            	   20000	     93177 ns/op	   26810 B/op	     169 allocs/op
+BenchmarkNextFiltered-4                    	   20000	     94171 ns/op	   27013 B/op	     175 allocs/op
+BenchmarkNextAll-4                         	    5000	    270320 ns/op	   89289 B/op	     237 allocs/op
+BenchmarkNextAllFiltered-4                 	    5000	    275283 ns/op	   90375 B/op	     246 allocs/op
+BenchmarkPrev-4                            	   20000	     92777 ns/op	   26810 B/op	     169 allocs/op
+BenchmarkPrevFiltered-4                    	   20000	     95577 ns/op	   27007 B/op	     175 allocs/op
+BenchmarkPrevAll-4                         	   20000	     86339 ns/op	   27515 B/op	     151 allocs/op
+BenchmarkPrevAllFiltered-4                 	   20000	     87759 ns/op	   27715 B/op	     157 allocs/op
+BenchmarkNextUntil-4                       	   10000	    163930 ns/op	   48541 B/op	     330 allocs/op
+BenchmarkNextUntilSelection-4              	   30000	     56382 ns/op	   23880 B/op	     556 allocs/op
+BenchmarkNextUntilNodes-4                  	  100000	     18883 ns/op	    8703 B/op	     252 allocs/op
+BenchmarkPrevUntil-4                       	    3000	    484668 ns/op	  145402 B/op	     611 allocs/op
+BenchmarkPrevUntilSelection-4              	   20000	     72125 ns/op	   28865 B/op	     705 allocs/op
+BenchmarkPrevUntilNodes-4                  	  100000	     14722 ns/op	    6510 B/op	     205 allocs/op
+BenchmarkNextFilteredUntil-4               	   50000	     39006 ns/op	   10990 B/op	     192 allocs/op
+BenchmarkNextFilteredUntilSelection-4      	   20000	     66048 ns/op	   25641 B/op	     586 allocs/op
+BenchmarkNextFilteredUntilNodes-4          	   20000	     65314 ns/op	   25640 B/op	     586 allocs/op
+BenchmarkPrevFilteredUntil-4               	   50000	     33312 ns/op	    9709 B/op	     189 allocs/op
+BenchmarkPrevFilteredUntilSelection-4      	   20000	     64197 ns/op	   24981 B/op	     601 allocs/op
+BenchmarkPrevFilteredUntilNodes-4          	   20000	     64505 ns/op	   24982 B/op	     601 allocs/op
+BenchmarkClosest-4                         	  500000	      4065 ns/op	     160 B/op	       8 allocs/op
+BenchmarkClosestSelection-4                	 2000000	       756 ns/op	      96 B/op	       6 allocs/op
+BenchmarkClosestNodes-4                    	 2000000	       753 ns/op	      96 B/op	       6 allocs/op
+PASS
+ok  	github.com/PuerkitoBio/goquery	162.053s

+ 85 - 0
src/github.com/PuerkitoBio/goquery/bench/v1.0.1b-go1.7

@@ -0,0 +1,85 @@
+BenchmarkFirst-4                           	30000000	        51.8 ns/op	      48 B/op	       1 allocs/op
+BenchmarkLast-4                            	30000000	        50.1 ns/op	      48 B/op	       1 allocs/op
+BenchmarkEq-4                              	30000000	        51.4 ns/op	      48 B/op	       1 allocs/op
+BenchmarkSlice-4                           	500000000	         3.52 ns/op	       0 B/op	       0 allocs/op
+BenchmarkGet-4                             	2000000000	         1.65 ns/op	       0 B/op	       0 allocs/op
+BenchmarkIndex-4                           	 2000000	       787 ns/op	     248 B/op	      10 allocs/op
+BenchmarkIndexSelector-4                   	  100000	     16952 ns/op	    3839 B/op	      21 allocs/op
+BenchmarkIndexOfNode-4                     	200000000	         6.42 ns/op	       0 B/op	       0 allocs/op
+BenchmarkIndexOfSelection-4                	200000000	         7.12 ns/op	       0 B/op	       0 allocs/op
+BenchmarkMetalReviewExample-4              	   10000	    141994 ns/op	   12418 B/op	     320 allocs/op
+BenchmarkAdd-4                             	  200000	     10367 ns/op	     208 B/op	       9 allocs/op
+BenchmarkAddSelection-4                    	10000000	       152 ns/op	      48 B/op	       1 allocs/op
+BenchmarkAddNodes-4                        	10000000	       147 ns/op	      48 B/op	       1 allocs/op
+BenchmarkAndSelf-4                         	 1000000	      1647 ns/op	    1008 B/op	       5 allocs/op
+BenchmarkFilter-4                          	  100000	     19522 ns/op	     360 B/op	       8 allocs/op
+BenchmarkNot-4                             	  100000	     22546 ns/op	     136 B/op	       5 allocs/op
+BenchmarkFilterFunction-4                  	   50000	     35087 ns/op	   22976 B/op	     755 allocs/op
+BenchmarkNotFunction-4                     	   50000	     39123 ns/op	   29120 B/op	     757 allocs/op
+BenchmarkFilterNodes-4                     	   50000	     34890 ns/op	   20960 B/op	     749 allocs/op
+BenchmarkNotNodes-4                        	   30000	     41145 ns/op	   29120 B/op	     757 allocs/op
+BenchmarkFilterSelection-4                 	   50000	     33735 ns/op	   20960 B/op	     749 allocs/op
+BenchmarkNotSelection-4                    	   30000	     41334 ns/op	   29120 B/op	     757 allocs/op
+BenchmarkHas-4                             	    5000	    264058 ns/op	    2370 B/op	      50 allocs/op
+BenchmarkHasNodes-4                        	   10000	    151718 ns/op	   21184 B/op	     752 allocs/op
+BenchmarkHasSelection-4                    	   10000	    156955 ns/op	   21184 B/op	     752 allocs/op
+BenchmarkEnd-4                             	2000000000	         1.01 ns/op	       0 B/op	       0 allocs/op
+BenchmarkEach-4                            	  300000	      4660 ns/op	    3304 B/op	     118 allocs/op
+BenchmarkMap-4                             	  200000	      8404 ns/op	    5572 B/op	     184 allocs/op
+BenchmarkEachWithBreak-4                   	 2000000	       806 ns/op	     560 B/op	      20 allocs/op
+BenchmarkAttr-4                            	100000000	        21.6 ns/op	       0 B/op	       0 allocs/op
+BenchmarkText-4                            	  200000	      8911 ns/op	    7536 B/op	     110 allocs/op
+BenchmarkLength-4                          	2000000000	         0.34 ns/op	       0 B/op	       0 allocs/op
+BenchmarkHtml-4                            	 3000000	       405 ns/op	     120 B/op	       2 allocs/op
+BenchmarkIs-4                              	  100000	     22228 ns/op	      88 B/op	       4 allocs/op
+BenchmarkIsPositional-4                    	   50000	     26469 ns/op	    1112 B/op	      10 allocs/op
+BenchmarkIsFunction-4                      	 1000000	      1240 ns/op	     784 B/op	      28 allocs/op
+BenchmarkIsSelection-4                     	   50000	     33709 ns/op	   20960 B/op	     749 allocs/op
+BenchmarkIsNodes-4                         	   50000	     33711 ns/op	   20960 B/op	     749 allocs/op
+BenchmarkHasClass-4                        	   10000	    236005 ns/op	   14944 B/op	     976 allocs/op
+BenchmarkContains-4                        	200000000	         7.47 ns/op	       0 B/op	       0 allocs/op
+BenchmarkFind-4                            	  100000	     16075 ns/op	    3839 B/op	      21 allocs/op
+BenchmarkFindWithinSelection-4             	   30000	     41418 ns/op	    3539 B/op	      82 allocs/op
+BenchmarkFindSelection-4                   	   10000	    209490 ns/op	    5616 B/op	      89 allocs/op
+BenchmarkFindNodes-4                       	   10000	    208206 ns/op	    5614 B/op	      89 allocs/op
+BenchmarkContents-4                        	  300000	      4751 ns/op	    1420 B/op	      36 allocs/op
+BenchmarkContentsFiltered-4                	  300000	      5454 ns/op	    1570 B/op	      41 allocs/op
+BenchmarkChildren-4                        	 3000000	       527 ns/op	     152 B/op	       7 allocs/op
+BenchmarkChildrenFiltered-4                	 1000000	      2484 ns/op	     352 B/op	      15 allocs/op
+BenchmarkParent-4                          	   50000	     34724 ns/op	    6940 B/op	     387 allocs/op
+BenchmarkParentFiltered-4                  	   50000	     35596 ns/op	    7141 B/op	     394 allocs/op
+BenchmarkParents-4                         	   20000	     62094 ns/op	   30720 B/op	     837 allocs/op
+BenchmarkParentsFiltered-4                 	   20000	     63223 ns/op	   31304 B/op	     845 allocs/op
+BenchmarkParentsUntil-4                    	   50000	     30391 ns/op	   11828 B/op	     358 allocs/op
+BenchmarkParentsUntilSelection-4           	   20000	     99962 ns/op	   54075 B/op	    1523 allocs/op
+BenchmarkParentsUntilNodes-4               	   20000	     98763 ns/op	   54073 B/op	    1523 allocs/op
+BenchmarkParentsFilteredUntil-4            	  200000	      7982 ns/op	    2787 B/op	      88 allocs/op
+BenchmarkParentsFilteredUntilSelection-4   	  100000	     13618 ns/op	    5995 B/op	     192 allocs/op
+BenchmarkParentsFilteredUntilNodes-4       	  100000	     13639 ns/op	    5994 B/op	     192 allocs/op
+BenchmarkSiblings-4                        	   20000	     75287 ns/op	   28453 B/op	     225 allocs/op
+BenchmarkSiblingsFiltered-4                	   20000	     80139 ns/op	   29543 B/op	     234 allocs/op
+BenchmarkNext-4                            	  100000	     14270 ns/op	    4659 B/op	     117 allocs/op
+BenchmarkNextFiltered-4                    	  100000	     15352 ns/op	    4860 B/op	     123 allocs/op
+BenchmarkNextAll-4                         	   20000	     60811 ns/op	   22771 B/op	     157 allocs/op
+BenchmarkNextAllFiltered-4                 	   20000	     69079 ns/op	   23871 B/op	     166 allocs/op
+BenchmarkPrev-4                            	  100000	     14417 ns/op	    4659 B/op	     117 allocs/op
+BenchmarkPrevFiltered-4                    	  100000	     15443 ns/op	    4859 B/op	     123 allocs/op
+BenchmarkPrevAll-4                         	  100000	     22008 ns/op	    7346 B/op	     120 allocs/op
+BenchmarkPrevAllFiltered-4                 	  100000	     23212 ns/op	    7544 B/op	     126 allocs/op
+BenchmarkNextUntil-4                       	   50000	     30589 ns/op	    8767 B/op	     267 allocs/op
+BenchmarkNextUntilSelection-4              	   30000	     40875 ns/op	   19862 B/op	     546 allocs/op
+BenchmarkNextUntilNodes-4                  	  100000	     15987 ns/op	    8134 B/op	     249 allocs/op
+BenchmarkPrevUntil-4                       	   20000	     98799 ns/op	   25727 B/op	     467 allocs/op
+BenchmarkPrevUntilSelection-4              	   30000	     51874 ns/op	   24875 B/op	     694 allocs/op
+BenchmarkPrevUntilNodes-4                  	  100000	     12901 ns/op	    6334 B/op	     204 allocs/op
+BenchmarkNextFilteredUntil-4               	  100000	     19869 ns/op	    5909 B/op	     177 allocs/op
+BenchmarkNextFilteredUntilSelection-4      	   30000	     45412 ns/op	   20557 B/op	     571 allocs/op
+BenchmarkNextFilteredUntilNodes-4          	   30000	     45363 ns/op	   20557 B/op	     571 allocs/op
+BenchmarkPrevFilteredUntil-4               	  100000	     19357 ns/op	    6033 B/op	     179 allocs/op
+BenchmarkPrevFilteredUntilSelection-4      	   30000	     46396 ns/op	   21305 B/op	     591 allocs/op
+BenchmarkPrevFilteredUntilNodes-4          	   30000	     46133 ns/op	   21305 B/op	     591 allocs/op
+BenchmarkClosest-4                         	  500000	      3448 ns/op	     160 B/op	       8 allocs/op
+BenchmarkClosestSelection-4                	 3000000	       528 ns/op	      96 B/op	       6 allocs/op
+BenchmarkClosestNodes-4                    	 3000000	       523 ns/op	      96 B/op	       6 allocs/op
+PASS
+ok  	github.com/PuerkitoBio/goquery	162.012s

+ 86 - 0
src/github.com/PuerkitoBio/goquery/bench/v1.0.1c-go1.7

@@ -0,0 +1,86 @@
+BenchmarkFirst-4                           	30000000	        51.7 ns/op	      48 B/op	       1 allocs/op
+BenchmarkLast-4                            	30000000	        51.9 ns/op	      48 B/op	       1 allocs/op
+BenchmarkEq-4                              	30000000	        50.0 ns/op	      48 B/op	       1 allocs/op
+BenchmarkSlice-4                           	500000000	         3.47 ns/op	       0 B/op	       0 allocs/op
+BenchmarkGet-4                             	2000000000	         1.68 ns/op	       0 B/op	       0 allocs/op
+BenchmarkIndex-4                           	 2000000	       804 ns/op	     248 B/op	      10 allocs/op
+BenchmarkIndexSelector-4                   	  100000	     16285 ns/op	    3839 B/op	      21 allocs/op
+BenchmarkIndexOfNode-4                     	200000000	         6.50 ns/op	       0 B/op	       0 allocs/op
+BenchmarkIndexOfSelection-4                	200000000	         7.02 ns/op	       0 B/op	       0 allocs/op
+BenchmarkMetalReviewExample-4              	   10000	    143160 ns/op	   12417 B/op	     320 allocs/op
+BenchmarkAdd-4                             	  200000	     10326 ns/op	     208 B/op	       9 allocs/op
+BenchmarkAddSelection-4                    	10000000	       155 ns/op	      48 B/op	       1 allocs/op
+BenchmarkAddNodes-4                        	10000000	       156 ns/op	      48 B/op	       1 allocs/op
+BenchmarkAddNodesBig-4                     	   20000	     94439 ns/op	   21847 B/op	      37 allocs/op
+BenchmarkAndSelf-4                         	 1000000	      1791 ns/op	    1008 B/op	       5 allocs/op
+BenchmarkFilter-4                          	  100000	     19470 ns/op	     360 B/op	       8 allocs/op
+BenchmarkNot-4                             	  100000	     22500 ns/op	     136 B/op	       5 allocs/op
+BenchmarkFilterFunction-4                  	   50000	     34578 ns/op	   22976 B/op	     755 allocs/op
+BenchmarkNotFunction-4                     	   50000	     38703 ns/op	   29120 B/op	     757 allocs/op
+BenchmarkFilterNodes-4                     	   50000	     34486 ns/op	   20960 B/op	     749 allocs/op
+BenchmarkNotNodes-4                        	   30000	     41094 ns/op	   29120 B/op	     757 allocs/op
+BenchmarkFilterSelection-4                 	   50000	     33623 ns/op	   20960 B/op	     749 allocs/op
+BenchmarkNotSelection-4                    	   30000	     41483 ns/op	   29120 B/op	     757 allocs/op
+BenchmarkHas-4                             	    5000	    266628 ns/op	    2371 B/op	      50 allocs/op
+BenchmarkHasNodes-4                        	   10000	    152617 ns/op	   21184 B/op	     752 allocs/op
+BenchmarkHasSelection-4                    	   10000	    156682 ns/op	   21184 B/op	     752 allocs/op
+BenchmarkEnd-4                             	2000000000	         1.00 ns/op	       0 B/op	       0 allocs/op
+BenchmarkEach-4                            	  300000	      4712 ns/op	    3304 B/op	     118 allocs/op
+BenchmarkMap-4                             	  200000	      8434 ns/op	    5572 B/op	     184 allocs/op
+BenchmarkEachWithBreak-4                   	 2000000	       819 ns/op	     560 B/op	      20 allocs/op
+BenchmarkAttr-4                            	100000000	        21.7 ns/op	       0 B/op	       0 allocs/op
+BenchmarkText-4                            	  200000	      9376 ns/op	    7536 B/op	     110 allocs/op
+BenchmarkLength-4                          	2000000000	         0.35 ns/op	       0 B/op	       0 allocs/op
+BenchmarkHtml-4                            	 5000000	       401 ns/op	     120 B/op	       2 allocs/op
+BenchmarkIs-4                              	  100000	     22214 ns/op	      88 B/op	       4 allocs/op
+BenchmarkIsPositional-4                    	   50000	     26559 ns/op	    1112 B/op	      10 allocs/op
+BenchmarkIsFunction-4                      	 1000000	      1228 ns/op	     784 B/op	      28 allocs/op
+BenchmarkIsSelection-4                     	   50000	     33471 ns/op	   20960 B/op	     749 allocs/op
+BenchmarkIsNodes-4                         	   50000	     34461 ns/op	   20960 B/op	     749 allocs/op
+BenchmarkHasClass-4                        	   10000	    232429 ns/op	   14944 B/op	     976 allocs/op
+BenchmarkContains-4                        	200000000	         7.62 ns/op	       0 B/op	       0 allocs/op
+BenchmarkFind-4                            	  100000	     16114 ns/op	    3839 B/op	      21 allocs/op
+BenchmarkFindWithinSelection-4             	   30000	     42520 ns/op	    3540 B/op	      82 allocs/op
+BenchmarkFindSelection-4                   	   10000	    209801 ns/op	    5615 B/op	      89 allocs/op
+BenchmarkFindNodes-4                       	   10000	    209082 ns/op	    5614 B/op	      89 allocs/op
+BenchmarkContents-4                        	  300000	      4836 ns/op	    1420 B/op	      36 allocs/op
+BenchmarkContentsFiltered-4                	  200000	      5495 ns/op	    1570 B/op	      41 allocs/op
+BenchmarkChildren-4                        	 3000000	       527 ns/op	     152 B/op	       7 allocs/op
+BenchmarkChildrenFiltered-4                	  500000	      2499 ns/op	     352 B/op	      15 allocs/op
+BenchmarkParent-4                          	   50000	     34072 ns/op	    6942 B/op	     387 allocs/op
+BenchmarkParentFiltered-4                  	   50000	     36077 ns/op	    7141 B/op	     394 allocs/op
+BenchmarkParents-4                         	   20000	     64118 ns/op	   30719 B/op	     837 allocs/op
+BenchmarkParentsFiltered-4                 	   20000	     63432 ns/op	   31303 B/op	     845 allocs/op
+BenchmarkParentsUntil-4                    	   50000	     29589 ns/op	   11829 B/op	     358 allocs/op
+BenchmarkParentsUntilSelection-4           	   10000	    101033 ns/op	   54076 B/op	    1523 allocs/op
+BenchmarkParentsUntilNodes-4               	   10000	    100584 ns/op	   54076 B/op	    1523 allocs/op
+BenchmarkParentsFilteredUntil-4            	  200000	      8061 ns/op	    2787 B/op	      88 allocs/op
+BenchmarkParentsFilteredUntilSelection-4   	  100000	     13848 ns/op	    5995 B/op	     192 allocs/op
+BenchmarkParentsFilteredUntilNodes-4       	  100000	     13766 ns/op	    5995 B/op	     192 allocs/op
+BenchmarkSiblings-4                        	   20000	     75135 ns/op	   28453 B/op	     225 allocs/op
+BenchmarkSiblingsFiltered-4                	   20000	     80532 ns/op	   29544 B/op	     234 allocs/op
+BenchmarkNext-4                            	  100000	     14200 ns/op	    4660 B/op	     117 allocs/op
+BenchmarkNextFiltered-4                    	  100000	     15284 ns/op	    4859 B/op	     123 allocs/op
+BenchmarkNextAll-4                         	   20000	     60889 ns/op	   22774 B/op	     157 allocs/op
+BenchmarkNextAllFiltered-4                 	   20000	     65125 ns/op	   23869 B/op	     166 allocs/op
+BenchmarkPrev-4                            	  100000	     14448 ns/op	    4659 B/op	     117 allocs/op
+BenchmarkPrevFiltered-4                    	  100000	     15444 ns/op	    4859 B/op	     123 allocs/op
+BenchmarkPrevAll-4                         	  100000	     22019 ns/op	    7344 B/op	     120 allocs/op
+BenchmarkPrevAllFiltered-4                 	  100000	     23307 ns/op	    7545 B/op	     126 allocs/op
+BenchmarkNextUntil-4                       	   50000	     30287 ns/op	    8766 B/op	     267 allocs/op
+BenchmarkNextUntilSelection-4              	   30000	     41476 ns/op	   19862 B/op	     546 allocs/op
+BenchmarkNextUntilNodes-4                  	  100000	     16106 ns/op	    8133 B/op	     249 allocs/op
+BenchmarkPrevUntil-4                       	   20000	     98951 ns/op	   25728 B/op	     467 allocs/op
+BenchmarkPrevUntilSelection-4              	   30000	     52390 ns/op	   24875 B/op	     694 allocs/op
+BenchmarkPrevUntilNodes-4                  	  100000	     12986 ns/op	    6334 B/op	     204 allocs/op
+BenchmarkNextFilteredUntil-4               	  100000	     19365 ns/op	    5908 B/op	     177 allocs/op
+BenchmarkNextFilteredUntilSelection-4      	   30000	     45334 ns/op	   20555 B/op	     571 allocs/op
+BenchmarkNextFilteredUntilNodes-4          	   30000	     45292 ns/op	   20556 B/op	     571 allocs/op
+BenchmarkPrevFilteredUntil-4               	  100000	     19412 ns/op	    6032 B/op	     179 allocs/op
+BenchmarkPrevFilteredUntilSelection-4      	   30000	     46286 ns/op	   21304 B/op	     591 allocs/op
+BenchmarkPrevFilteredUntilNodes-4          	   30000	     46554 ns/op	   21305 B/op	     591 allocs/op
+BenchmarkClosest-4                         	  500000	      3480 ns/op	     160 B/op	       8 allocs/op
+BenchmarkClosestSelection-4                	 2000000	       722 ns/op	      96 B/op	       6 allocs/op
+BenchmarkClosestNodes-4                    	 2000000	       719 ns/op	      96 B/op	       6 allocs/op
+PASS
+ok  	github.com/PuerkitoBio/goquery	160.565s

+ 12 - 4
src/github.com/PuerkitoBio/goquery/bench_array_test.go

@@ -70,7 +70,9 @@ func BenchmarkIndex(b *testing.B) {
 	for i := 0; i < b.N; i++ {
 		j = sel.Index()
 	}
-	b.Logf("Index=%d", j)
+	if j != 3 {
+		b.Fatalf("want 3, got %d", j)
+	}
 }
 
 func BenchmarkIndexSelector(b *testing.B) {
@@ -82,7 +84,9 @@ func BenchmarkIndexSelector(b *testing.B) {
 	for i := 0; i < b.N; i++ {
 		j = sel.IndexSelector("dd")
 	}
-	b.Logf("IndexSelector=%d", j)
+	if j != 4 {
+		b.Fatalf("want 4, got %d", j)
+	}
 }
 
 func BenchmarkIndexOfNode(b *testing.B) {
@@ -96,7 +100,9 @@ func BenchmarkIndexOfNode(b *testing.B) {
 	for i := 0; i < b.N; i++ {
 		j = sel.IndexOfNode(n)
 	}
-	b.Logf("IndexOfNode=%d", j)
+	if j != 2 {
+		b.Fatalf("want 2, got %d", j)
+	}
 }
 
 func BenchmarkIndexOfSelection(b *testing.B) {
@@ -108,5 +114,7 @@ func BenchmarkIndexOfSelection(b *testing.B) {
 	for i := 0; i < b.N; i++ {
 		j = sel.IndexOfSelection(sel2)
 	}
-	b.Logf("IndexOfSelection=%d", j)
+	if j != 2 {
+		b.Fatalf("want 2, got %d", j)
+	}
 }

+ 0 - 2
src/github.com/PuerkitoBio/goquery/bench_example_test.go

@@ -37,6 +37,4 @@ func BenchmarkMetalReviewExample(b *testing.B) {
 			}
 		})
 	}
-	b.Log(buf.String())
-	b.Logf("MetalReviewExample=%d", n)
 }

+ 36 - 4
src/github.com/PuerkitoBio/goquery/bench_expand_test.go

@@ -17,7 +17,9 @@ func BenchmarkAdd(b *testing.B) {
 			sel.Add("h2[title]")
 		}
 	}
-	b.Logf("Add=%d", n)
+	if n != 43 {
+		b.Fatalf("want 43, got %d", n)
+	}
 }
 
 func BenchmarkAddSelection(b *testing.B) {
@@ -34,7 +36,9 @@ func BenchmarkAddSelection(b *testing.B) {
 			sel.AddSelection(sel2)
 		}
 	}
-	b.Logf("AddSelection=%d", n)
+	if n != 43 {
+		b.Fatalf("want 43, got %d", n)
+	}
 }
 
 func BenchmarkAddNodes(b *testing.B) {
@@ -52,7 +56,33 @@ func BenchmarkAddNodes(b *testing.B) {
 			sel.AddNodes(nodes...)
 		}
 	}
-	b.Logf("AddNodes=%d", n)
+	if n != 43 {
+		b.Fatalf("want 43, got %d", n)
+	}
+}
+
+func BenchmarkAddNodesBig(b *testing.B) {
+	var n int
+
+	doc := DocW()
+	sel := doc.Find("li")
+	// make nodes > 1000
+	nodes := sel.Nodes
+	nodes = append(nodes, nodes...)
+	nodes = append(nodes, nodes...)
+	sel = doc.Find("xyz")
+	b.ResetTimer()
+
+	for i := 0; i < b.N; i++ {
+		if n == 0 {
+			n = sel.AddNodes(nodes...).Length()
+		} else {
+			sel.AddNodes(nodes...)
+		}
+	}
+	if n != 373 {
+		b.Fatalf("want 373, got %d", n)
+	}
 }
 
 func BenchmarkAndSelf(b *testing.B) {
@@ -68,5 +98,7 @@ func BenchmarkAndSelf(b *testing.B) {
 			sel.AndSelf()
 		}
 	}
-	b.Logf("AndSelf=%d", n)
+	if n != 44 {
+		b.Fatalf("want 44, got %d", n)
+	}
 }

+ 36 - 12
src/github.com/PuerkitoBio/goquery/bench_filter_test.go

@@ -17,7 +17,9 @@ func BenchmarkFilter(b *testing.B) {
 			sel.Filter(".toclevel-1")
 		}
 	}
-	b.Logf("Filter=%d", n)
+	if n != 13 {
+		b.Fatalf("want 13, got %d", n)
+	}
 }
 
 func BenchmarkNot(b *testing.B) {
@@ -33,7 +35,9 @@ func BenchmarkNot(b *testing.B) {
 			sel.Filter(".toclevel-2")
 		}
 	}
-	b.Logf("Not=%d", n)
+	if n != 371 {
+		b.Fatalf("want 371, got %d", n)
+	}
 }
 
 func BenchmarkFilterFunction(b *testing.B) {
@@ -52,7 +56,9 @@ func BenchmarkFilterFunction(b *testing.B) {
 			sel.FilterFunction(f)
 		}
 	}
-	b.Logf("FilterFunction=%d", n)
+	if n != 112 {
+		b.Fatalf("want 112, got %d", n)
+	}
 }
 
 func BenchmarkNotFunction(b *testing.B) {
@@ -71,7 +77,9 @@ func BenchmarkNotFunction(b *testing.B) {
 			sel.NotFunction(f)
 		}
 	}
-	b.Logf("NotFunction=%d", n)
+	if n != 261 {
+		b.Fatalf("want 261, got %d", n)
+	}
 }
 
 func BenchmarkFilterNodes(b *testing.B) {
@@ -89,7 +97,9 @@ func BenchmarkFilterNodes(b *testing.B) {
 			sel.FilterNodes(nodes...)
 		}
 	}
-	b.Logf("FilterNodes=%d", n)
+	if n != 2 {
+		b.Fatalf("want 2, got %d", n)
+	}
 }
 
 func BenchmarkNotNodes(b *testing.B) {
@@ -107,7 +117,9 @@ func BenchmarkNotNodes(b *testing.B) {
 			sel.NotNodes(nodes...)
 		}
 	}
-	b.Logf("NotNodes=%d", n)
+	if n != 360 {
+		b.Fatalf("want 360, got %d", n)
+	}
 }
 
 func BenchmarkFilterSelection(b *testing.B) {
@@ -124,7 +136,9 @@ func BenchmarkFilterSelection(b *testing.B) {
 			sel.FilterSelection(sel2)
 		}
 	}
-	b.Logf("FilterSelection=%d", n)
+	if n != 2 {
+		b.Fatalf("want 2, got %d", n)
+	}
 }
 
 func BenchmarkNotSelection(b *testing.B) {
@@ -141,7 +155,9 @@ func BenchmarkNotSelection(b *testing.B) {
 			sel.NotSelection(sel2)
 		}
 	}
-	b.Logf("NotSelection=%d", n)
+	if n != 360 {
+		b.Fatalf("want 360, got %d", n)
+	}
 }
 
 func BenchmarkHas(b *testing.B) {
@@ -157,7 +173,9 @@ func BenchmarkHas(b *testing.B) {
 			sel.Has(".editsection")
 		}
 	}
-	b.Logf("Has=%d", n)
+	if n != 13 {
+		b.Fatalf("want 13, got %d", n)
+	}
 }
 
 func BenchmarkHasNodes(b *testing.B) {
@@ -175,7 +193,9 @@ func BenchmarkHasNodes(b *testing.B) {
 			sel.HasNodes(nodes...)
 		}
 	}
-	b.Logf("HasNodes=%d", n)
+	if n != 15 {
+		b.Fatalf("want 15, got %d", n)
+	}
 }
 
 func BenchmarkHasSelection(b *testing.B) {
@@ -192,7 +212,9 @@ func BenchmarkHasSelection(b *testing.B) {
 			sel.HasSelection(sel2)
 		}
 	}
-	b.Logf("HasSelection=%d", n)
+	if n != 15 {
+		b.Fatalf("want 15, got %d", n)
+	}
 }
 
 func BenchmarkEnd(b *testing.B) {
@@ -208,5 +230,7 @@ func BenchmarkEnd(b *testing.B) {
 			sel.End()
 		}
 	}
-	b.Logf("End=%d", n)
+	if n != 373 {
+		b.Fatalf("want 373, got %d", n)
+	}
 }

+ 11 - 4
src/github.com/PuerkitoBio/goquery/bench_iteration_test.go

@@ -1,6 +1,7 @@
 package goquery
 
 import (
+	"strconv"
 	"testing"
 )
 
@@ -19,7 +20,9 @@ func BenchmarkEach(b *testing.B) {
 			n = tmp
 		}
 	}
-	b.Logf("Each=%d", n)
+	if n != 59 {
+		b.Fatalf("want 59, got %d", n)
+	}
 }
 
 func BenchmarkMap(b *testing.B) {
@@ -29,7 +32,7 @@ func BenchmarkMap(b *testing.B) {
 	sel := DocW().Find("td")
 	f := func(i int, s *Selection) string {
 		tmp++
-		return string(tmp)
+		return strconv.Itoa(tmp)
 	}
 	b.StartTimer()
 	for i := 0; i < b.N; i++ {
@@ -38,7 +41,9 @@ func BenchmarkMap(b *testing.B) {
 			n = tmp
 		}
 	}
-	b.Logf("Map=%d", n)
+	if n != 59 {
+		b.Fatalf("want 59, got %d", n)
+	}
 }
 
 func BenchmarkEachWithBreak(b *testing.B) {
@@ -58,5 +63,7 @@ func BenchmarkEachWithBreak(b *testing.B) {
 			n = tmp
 		}
 	}
-	b.Logf("Each=%d", n)
+	if n != 10 {
+		b.Fatalf("want 10, got %d", n)
+	}
 }

+ 6 - 2
src/github.com/PuerkitoBio/goquery/bench_property_test.go

@@ -13,7 +13,9 @@ func BenchmarkAttr(b *testing.B) {
 	for i := 0; i < b.N; i++ {
 		s, _ = sel.Attr("id")
 	}
-	b.Logf("Attr=%s", s)
+	if s != "firstHeading" {
+		b.Fatalf("want firstHeading, got %q", s)
+	}
 }
 
 func BenchmarkText(b *testing.B) {
@@ -34,7 +36,9 @@ func BenchmarkLength(b *testing.B) {
 	for i := 0; i < b.N; i++ {
 		n = sel.Length()
 	}
-	b.Logf("Length=%d", n)
+	if n != 14 {
+		b.Fatalf("want 14, got %d", n)
+	}
 }
 
 func BenchmarkHtml(b *testing.B) {

+ 21 - 7
src/github.com/PuerkitoBio/goquery/bench_query_test.go

@@ -13,7 +13,9 @@ func BenchmarkIs(b *testing.B) {
 	for i := 0; i < b.N; i++ {
 		y = sel.Is(".toclevel-2")
 	}
-	b.Logf("Is=%v", y)
+	if !y {
+		b.Fatal("want true")
+	}
 }
 
 func BenchmarkIsPositional(b *testing.B) {
@@ -25,7 +27,9 @@ func BenchmarkIsPositional(b *testing.B) {
 	for i := 0; i < b.N; i++ {
 		y = sel.Is("li:nth-child(2)")
 	}
-	b.Logf("IsPositional=%v", y)
+	if !y {
+		b.Fatal("want true")
+	}
 }
 
 func BenchmarkIsFunction(b *testing.B) {
@@ -40,7 +44,9 @@ func BenchmarkIsFunction(b *testing.B) {
 	for i := 0; i < b.N; i++ {
 		y = sel.IsFunction(f)
 	}
-	b.Logf("IsFunction=%v", y)
+	if !y {
+		b.Fatal("want true")
+	}
 }
 
 func BenchmarkIsSelection(b *testing.B) {
@@ -53,7 +59,9 @@ func BenchmarkIsSelection(b *testing.B) {
 	for i := 0; i < b.N; i++ {
 		y = sel.IsSelection(sel2)
 	}
-	b.Logf("IsSelection=%v", y)
+	if !y {
+		b.Fatal("want true")
+	}
 }
 
 func BenchmarkIsNodes(b *testing.B) {
@@ -67,7 +75,9 @@ func BenchmarkIsNodes(b *testing.B) {
 	for i := 0; i < b.N; i++ {
 		y = sel.IsNodes(nodes...)
 	}
-	b.Logf("IsNodes=%v", y)
+	if !y {
+		b.Fatal("want true")
+	}
 }
 
 func BenchmarkHasClass(b *testing.B) {
@@ -79,7 +89,9 @@ func BenchmarkHasClass(b *testing.B) {
 	for i := 0; i < b.N; i++ {
 		y = sel.HasClass("official")
 	}
-	b.Logf("HasClass=%v", y)
+	if !y {
+		b.Fatal("want true")
+	}
 }
 
 func BenchmarkContains(b *testing.B) {
@@ -93,5 +105,7 @@ func BenchmarkContains(b *testing.B) {
 	for i := 0; i < b.N; i++ {
 		y = sel.Contains(node)
 	}
-	b.Logf("Contains=%v", y)
+	if !y {
+		b.Fatal("want true")
+	}
 }

+ 129 - 43
src/github.com/PuerkitoBio/goquery/bench_traversal_test.go

@@ -15,7 +15,9 @@ func BenchmarkFind(b *testing.B) {
 			DocB().Find("dd")
 		}
 	}
-	b.Logf("Find=%d", n)
+	if n != 41 {
+		b.Fatalf("want 41, got %d", n)
+	}
 }
 
 func BenchmarkFindWithinSelection(b *testing.B) {
@@ -31,7 +33,9 @@ func BenchmarkFindWithinSelection(b *testing.B) {
 			sel.Find("a[class]")
 		}
 	}
-	b.Logf("FindWithinSelection=%d", n)
+	if n != 39 {
+		b.Fatalf("want 39, got %d", n)
+	}
 }
 
 func BenchmarkFindSelection(b *testing.B) {
@@ -48,7 +52,9 @@ func BenchmarkFindSelection(b *testing.B) {
 			sel.FindSelection(sel2)
 		}
 	}
-	b.Logf("FindSelection=%d", n)
+	if n != 73 {
+		b.Fatalf("want 73, got %d", n)
+	}
 }
 
 func BenchmarkFindNodes(b *testing.B) {
@@ -66,7 +72,9 @@ func BenchmarkFindNodes(b *testing.B) {
 			sel.FindNodes(nodes...)
 		}
 	}
-	b.Logf("FindNodes=%d", n)
+	if n != 73 {
+		b.Fatalf("want 73, got %d", n)
+	}
 }
 
 func BenchmarkContents(b *testing.B) {
@@ -82,7 +90,9 @@ func BenchmarkContents(b *testing.B) {
 			sel.Contents()
 		}
 	}
-	b.Logf("Contents=%d", n)
+	if n != 16 {
+		b.Fatalf("want 16, got %d", n)
+	}
 }
 
 func BenchmarkContentsFiltered(b *testing.B) {
@@ -98,7 +108,9 @@ func BenchmarkContentsFiltered(b *testing.B) {
 			sel.ContentsFiltered("a[href=\"#Examples\"]")
 		}
 	}
-	b.Logf("ContentsFiltered=%d", n)
+	if n != 1 {
+		b.Fatalf("want 1, got %d", n)
+	}
 }
 
 func BenchmarkChildren(b *testing.B) {
@@ -114,7 +126,9 @@ func BenchmarkChildren(b *testing.B) {
 			sel.Children()
 		}
 	}
-	b.Logf("Children=%d", n)
+	if n != 2 {
+		b.Fatalf("want 2, got %d", n)
+	}
 }
 
 func BenchmarkChildrenFiltered(b *testing.B) {
@@ -130,7 +144,9 @@ func BenchmarkChildrenFiltered(b *testing.B) {
 			sel.ChildrenFiltered(".editsection")
 		}
 	}
-	b.Logf("ChildrenFiltered=%d", n)
+	if n != 2 {
+		b.Fatalf("want 2, got %d", n)
+	}
 }
 
 func BenchmarkParent(b *testing.B) {
@@ -146,7 +162,9 @@ func BenchmarkParent(b *testing.B) {
 			sel.Parent()
 		}
 	}
-	b.Logf("Parent=%d", n)
+	if n != 55 {
+		b.Fatalf("want 55, got %d", n)
+	}
 }
 
 func BenchmarkParentFiltered(b *testing.B) {
@@ -162,7 +180,9 @@ func BenchmarkParentFiltered(b *testing.B) {
 			sel.ParentFiltered("ul[id]")
 		}
 	}
-	b.Logf("ParentFiltered=%d", n)
+	if n != 4 {
+		b.Fatalf("want 4, got %d", n)
+	}
 }
 
 func BenchmarkParents(b *testing.B) {
@@ -178,7 +198,9 @@ func BenchmarkParents(b *testing.B) {
 			sel.Parents()
 		}
 	}
-	b.Logf("Parents=%d", n)
+	if n != 73 {
+		b.Fatalf("want 73, got %d", n)
+	}
 }
 
 func BenchmarkParentsFiltered(b *testing.B) {
@@ -194,7 +216,9 @@ func BenchmarkParentsFiltered(b *testing.B) {
 			sel.ParentsFiltered("tr")
 		}
 	}
-	b.Logf("ParentsFiltered=%d", n)
+	if n != 18 {
+		b.Fatalf("want 18, got %d", n)
+	}
 }
 
 func BenchmarkParentsUntil(b *testing.B) {
@@ -210,7 +234,9 @@ func BenchmarkParentsUntil(b *testing.B) {
 			sel.ParentsUntil("table")
 		}
 	}
-	b.Logf("ParentsUntil=%d", n)
+	if n != 52 {
+		b.Fatalf("want 52, got %d", n)
+	}
 }
 
 func BenchmarkParentsUntilSelection(b *testing.B) {
@@ -227,7 +253,9 @@ func BenchmarkParentsUntilSelection(b *testing.B) {
 			sel.ParentsUntilSelection(sel2)
 		}
 	}
-	b.Logf("ParentsUntilSelection=%d", n)
+	if n != 70 {
+		b.Fatalf("want 70, got %d", n)
+	}
 }
 
 func BenchmarkParentsUntilNodes(b *testing.B) {
@@ -245,7 +273,9 @@ func BenchmarkParentsUntilNodes(b *testing.B) {
 			sel.ParentsUntilNodes(nodes...)
 		}
 	}
-	b.Logf("ParentsUntilNodes=%d", n)
+	if n != 70 {
+		b.Fatalf("want 70, got %d", n)
+	}
 }
 
 func BenchmarkParentsFilteredUntil(b *testing.B) {
@@ -261,7 +291,9 @@ func BenchmarkParentsFilteredUntil(b *testing.B) {
 			sel.ParentsFilteredUntil(":nth-child(1)", "ul")
 		}
 	}
-	b.Logf("ParentsFilteredUntil=%d", n)
+	if n != 2 {
+		b.Fatalf("want 2, got %d", n)
+	}
 }
 
 func BenchmarkParentsFilteredUntilSelection(b *testing.B) {
@@ -278,7 +310,9 @@ func BenchmarkParentsFilteredUntilSelection(b *testing.B) {
 			sel.ParentsFilteredUntilSelection(":nth-child(1)", sel2)
 		}
 	}
-	b.Logf("ParentsFilteredUntilSelection=%d", n)
+	if n != 2 {
+		b.Fatalf("want 2, got %d", n)
+	}
 }
 
 func BenchmarkParentsFilteredUntilNodes(b *testing.B) {
@@ -296,7 +330,9 @@ func BenchmarkParentsFilteredUntilNodes(b *testing.B) {
 			sel.ParentsFilteredUntilNodes(":nth-child(1)", nodes...)
 		}
 	}
-	b.Logf("ParentsFilteredUntilNodes=%d", n)
+	if n != 2 {
+		b.Fatalf("want 2, got %d", n)
+	}
 }
 
 func BenchmarkSiblings(b *testing.B) {
@@ -312,7 +348,9 @@ func BenchmarkSiblings(b *testing.B) {
 			sel.Siblings()
 		}
 	}
-	b.Logf("Siblings=%d", n)
+	if n != 293 {
+		b.Fatalf("want 293, got %d", n)
+	}
 }
 
 func BenchmarkSiblingsFiltered(b *testing.B) {
@@ -328,7 +366,9 @@ func BenchmarkSiblingsFiltered(b *testing.B) {
 			sel.SiblingsFiltered("[class]")
 		}
 	}
-	b.Logf("SiblingsFiltered=%d", n)
+	if n != 46 {
+		b.Fatalf("want 46, got %d", n)
+	}
 }
 
 func BenchmarkNext(b *testing.B) {
@@ -344,7 +384,9 @@ func BenchmarkNext(b *testing.B) {
 			sel.Next()
 		}
 	}
-	b.Logf("Next=%d", n)
+	if n != 49 {
+		b.Fatalf("want 49, got %d", n)
+	}
 }
 
 func BenchmarkNextFiltered(b *testing.B) {
@@ -360,7 +402,9 @@ func BenchmarkNextFiltered(b *testing.B) {
 			sel.NextFiltered("[class]")
 		}
 	}
-	b.Logf("NextFiltered=%d", n)
+	if n != 6 {
+		b.Fatalf("want 6, got %d", n)
+	}
 }
 
 func BenchmarkNextAll(b *testing.B) {
@@ -376,7 +420,9 @@ func BenchmarkNextAll(b *testing.B) {
 			sel.NextAll()
 		}
 	}
-	b.Logf("NextAll=%d", n)
+	if n != 234 {
+		b.Fatalf("want 234, got %d", n)
+	}
 }
 
 func BenchmarkNextAllFiltered(b *testing.B) {
@@ -392,7 +438,9 @@ func BenchmarkNextAllFiltered(b *testing.B) {
 			sel.NextAllFiltered("[class]")
 		}
 	}
-	b.Logf("NextAllFiltered=%d", n)
+	if n != 33 {
+		b.Fatalf("want 33, got %d", n)
+	}
 }
 
 func BenchmarkPrev(b *testing.B) {
@@ -408,7 +456,9 @@ func BenchmarkPrev(b *testing.B) {
 			sel.Prev()
 		}
 	}
-	b.Logf("Prev=%d", n)
+	if n != 49 {
+		b.Fatalf("want 49, got %d", n)
+	}
 }
 
 func BenchmarkPrevFiltered(b *testing.B) {
@@ -426,7 +476,9 @@ func BenchmarkPrevFiltered(b *testing.B) {
 	}
 	// There is one more Prev li with a class, compared to Next li with a class
 	// (confirmed by looking at the HTML, this is ok)
-	b.Logf("PrevFiltered=%d", n)
+	if n != 7 {
+		b.Fatalf("want 7, got %d", n)
+	}
 }
 
 func BenchmarkPrevAll(b *testing.B) {
@@ -442,7 +494,9 @@ func BenchmarkPrevAll(b *testing.B) {
 			sel.PrevAll()
 		}
 	}
-	b.Logf("PrevAll=%d", n)
+	if n != 78 {
+		b.Fatalf("want 78, got %d", n)
+	}
 }
 
 func BenchmarkPrevAllFiltered(b *testing.B) {
@@ -458,7 +512,9 @@ func BenchmarkPrevAllFiltered(b *testing.B) {
 			sel.PrevAllFiltered("[class]")
 		}
 	}
-	b.Logf("PrevAllFiltered=%d", n)
+	if n != 6 {
+		b.Fatalf("want 6, got %d", n)
+	}
 }
 
 func BenchmarkNextUntil(b *testing.B) {
@@ -474,7 +530,9 @@ func BenchmarkNextUntil(b *testing.B) {
 			sel.NextUntil(":nth-child(4)")
 		}
 	}
-	b.Logf("NextUntil=%d", n)
+	if n != 84 {
+		b.Fatalf("want 84, got %d", n)
+	}
 }
 
 func BenchmarkNextUntilSelection(b *testing.B) {
@@ -491,7 +549,9 @@ func BenchmarkNextUntilSelection(b *testing.B) {
 			sel.NextUntilSelection(sel2)
 		}
 	}
-	b.Logf("NextUntilSelection=%d", n)
+	if n != 42 {
+		b.Fatalf("want 42, got %d", n)
+	}
 }
 
 func BenchmarkNextUntilNodes(b *testing.B) {
@@ -509,7 +569,9 @@ func BenchmarkNextUntilNodes(b *testing.B) {
 			sel.NextUntilNodes(nodes...)
 		}
 	}
-	b.Logf("NextUntilNodes=%d", n)
+	if n != 12 {
+		b.Fatalf("want 12, got %d", n)
+	}
 }
 
 func BenchmarkPrevUntil(b *testing.B) {
@@ -525,7 +587,9 @@ func BenchmarkPrevUntil(b *testing.B) {
 			sel.PrevUntil(":nth-child(4)")
 		}
 	}
-	b.Logf("PrevUntil=%d", n)
+	if n != 238 {
+		b.Fatalf("want 238, got %d", n)
+	}
 }
 
 func BenchmarkPrevUntilSelection(b *testing.B) {
@@ -542,7 +606,9 @@ func BenchmarkPrevUntilSelection(b *testing.B) {
 			sel.PrevUntilSelection(sel2)
 		}
 	}
-	b.Logf("PrevUntilSelection=%d", n)
+	if n != 49 {
+		b.Fatalf("want 49, got %d", n)
+	}
 }
 
 func BenchmarkPrevUntilNodes(b *testing.B) {
@@ -560,7 +626,9 @@ func BenchmarkPrevUntilNodes(b *testing.B) {
 			sel.PrevUntilNodes(nodes...)
 		}
 	}
-	b.Logf("PrevUntilNodes=%d", n)
+	if n != 11 {
+		b.Fatalf("want 11, got %d", n)
+	}
 }
 
 func BenchmarkNextFilteredUntil(b *testing.B) {
@@ -576,7 +644,9 @@ func BenchmarkNextFilteredUntil(b *testing.B) {
 			sel.NextFilteredUntil("p", "div")
 		}
 	}
-	b.Logf("NextFilteredUntil=%d", n)
+	if n != 22 {
+		b.Fatalf("want 22, got %d", n)
+	}
 }
 
 func BenchmarkNextFilteredUntilSelection(b *testing.B) {
@@ -593,7 +663,9 @@ func BenchmarkNextFilteredUntilSelection(b *testing.B) {
 			sel.NextFilteredUntilSelection("p", sel2)
 		}
 	}
-	b.Logf("NextFilteredUntilSelection=%d", n)
+	if n != 22 {
+		b.Fatalf("want 22, got %d", n)
+	}
 }
 
 func BenchmarkNextFilteredUntilNodes(b *testing.B) {
@@ -611,7 +683,9 @@ func BenchmarkNextFilteredUntilNodes(b *testing.B) {
 			sel.NextFilteredUntilNodes("p", nodes...)
 		}
 	}
-	b.Logf("NextFilteredUntilNodes=%d", n)
+	if n != 22 {
+		b.Fatalf("want 22, got %d", n)
+	}
 }
 
 func BenchmarkPrevFilteredUntil(b *testing.B) {
@@ -627,7 +701,9 @@ func BenchmarkPrevFilteredUntil(b *testing.B) {
 			sel.PrevFilteredUntil("p", "div")
 		}
 	}
-	b.Logf("PrevFilteredUntil=%d", n)
+	if n != 20 {
+		b.Fatalf("want 20, got %d", n)
+	}
 }
 
 func BenchmarkPrevFilteredUntilSelection(b *testing.B) {
@@ -644,7 +720,9 @@ func BenchmarkPrevFilteredUntilSelection(b *testing.B) {
 			sel.PrevFilteredUntilSelection("p", sel2)
 		}
 	}
-	b.Logf("PrevFilteredUntilSelection=%d", n)
+	if n != 20 {
+		b.Fatalf("want 20, got %d", n)
+	}
 }
 
 func BenchmarkPrevFilteredUntilNodes(b *testing.B) {
@@ -662,7 +740,9 @@ func BenchmarkPrevFilteredUntilNodes(b *testing.B) {
 			sel.PrevFilteredUntilNodes("p", nodes...)
 		}
 	}
-	b.Logf("PrevFilteredUntilNodes=%d", n)
+	if n != 20 {
+		b.Fatalf("want 20, got %d", n)
+	}
 }
 
 func BenchmarkClosest(b *testing.B) {
@@ -678,7 +758,9 @@ func BenchmarkClosest(b *testing.B) {
 			sel.Closest(".pvk-content")
 		}
 	}
-	b.Logf("Closest=%d", n)
+	if n != 2 {
+		b.Fatalf("want 2, got %d", n)
+	}
 }
 
 func BenchmarkClosestSelection(b *testing.B) {
@@ -695,7 +777,9 @@ func BenchmarkClosestSelection(b *testing.B) {
 			sel.ClosestSelection(sel2)
 		}
 	}
-	b.Logf("ClosestSelection=%d", n)
+	if n != 2 {
+		b.Fatalf("want 2, got %d", n)
+	}
 }
 
 func BenchmarkClosestNodes(b *testing.B) {
@@ -712,5 +796,7 @@ func BenchmarkClosestNodes(b *testing.B) {
 			sel.ClosestNodes(nodes...)
 		}
 	}
-	b.Logf("ClosestNodes=%d", n)
+	if n != 2 {
+		b.Fatalf("want 2, got %d", n)
+	}
 }

+ 7 - 2
src/github.com/PuerkitoBio/goquery/doc.go

@@ -1,4 +1,4 @@
-// Copyright (c) 2012-2014, Martin Angers & Contributors
+// Copyright (c) 2012-2016, Martin Angers & Contributors
 // All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without modification,
@@ -35,7 +35,7 @@ Also, because the net/html parser requires UTF-8 encoding, so does goquery: it i
 the caller's responsibility to ensure that the source document provides UTF-8 encoded HTML.
 See the repository's wiki for various options on how to do this.
 
-Syntax-wise, it is as close as possible to jQuery, with the same function names when
+Syntax-wise, it is as close as possible to jQuery, with the same method names when
 possible, and that warm and fuzzy chainable interface. jQuery being the
 ultra-popular library that it is, writing a similar HTML-manipulating
 library was better to follow its API than to start anew (in the same spirit as
@@ -114,5 +114,10 @@ The three dots (...) indicate that various "overloads" are available.
     - Document
     - Selection
     - Matcher
+
+* utilities.go : definition of helper functions (and not methods on a *Selection)
+that are not part of jQuery, but are useful to goquery.
+    - NodeName
+    - OuterHtml
 */
 package goquery

+ 68 - 0
src/github.com/PuerkitoBio/goquery/doc/tips.md

@@ -0,0 +1,68 @@
+# Tips and tricks
+
+## Handle Non-UTF8 html Pages
+
+The `go.net/html` package used by `goquery` requires that the html document is UTF-8 encoded. When you know the encoding of the html page is not UTF-8, you can use the `iconv` package to convert it to UTF-8 (there are various implementation of the `iconv` API, see [godoc.org][iconv] for other options):
+
+```
+$ go get -u github.com/djimenez/iconv-go
+```
+
+and then:
+
+```
+// Load the URL
+res, err := http.Get(url)
+if err != nil {
+    // handle error
+}
+defer res.Body.Close()
+
+// Convert the designated charset HTML to utf-8 encoded HTML.
+// `charset` being one of the charsets known by the iconv package.
+utfBody, err := iconv.NewReader(res.Body, charset, "utf-8")
+if err != nil {
+    // handler error
+}
+
+// use utfBody using goquery
+doc, err := goquery.NewDocumentFromReader(utfBody)
+if err != nil {
+    // handler error
+}
+// use doc...
+```
+
+Thanks to github user @YuheiNakasaka.
+
+Actually, the official go.text repository covers this use case too, see its [godoc page][text] for the details.
+
+
+## Handle Javascript-based Pages
+
+`goquery` is great to handle normal html pages, but when most of the page is build dynamically using javascript, there's not much it can do. There are various options when faced with this problem:
+
+* Use a headless browser such as [webloop][].
+* Use a Go javascript parser package, such as [otto][].
+
+You can find a code example using `otto` [in this gist][exotto]. Thanks to github user @cryptix.
+
+## For Loop
+
+If all you need is a normal `for` loop over all nodes in the current selection, where `Map/Each`-style iteration is not necessary, you can use the following:
+
+```
+sel := Doc().Find(".selector")
+for i := range sel.Nodes {
+	single := sel.Eq(i)
+    // use `single` as a selection of 1 node
+}
+```
+
+Thanks to github user @jmoiron.
+
+[webloop]: https://github.com/sourcegraph/webloop
+[otto]: https://github.com/robertkrimen/otto
+[exotto]: https://gist.github.com/cryptix/87127f76a94183747b53
+[iconv]: http://godoc.org/?q=iconv
+[text]: https://godoc.org/golang.org/x/text/encoding

+ 60 - 10
src/github.com/PuerkitoBio/goquery/example_test.go

@@ -1,26 +1,37 @@
-package goquery
+package goquery_test
 
 import (
 	"fmt"
 	"log"
+	"net/http"
+	"os"
+	"strings"
 
-	// In real use, this import would be required (not in this example, since it
-	// is part of the goquery package)
-	//"github.com/PuerkitoBio/goquery"
+	"github.com/PuerkitoBio/goquery"
 )
 
 // This example scrapes the reviews shown on the home page of metalsucks.net.
-func ExampleScrape_MetalSucks() {
-	// Load the HTML document (in real use, the type would be *goquery.Document)
-	doc, err := NewDocument("http://metalsucks.net")
+func Example() {
+	// Request the HTML page.
+	res, err := http.Get("http://metalsucks.net")
+	if err != nil {
+		log.Fatal(err)
+	}
+	defer res.Body.Close()
+	if res.StatusCode != 200 {
+		log.Fatalf("status code error: %d %s", res.StatusCode, res.Status)
+	}
+
+	// Load the HTML document
+	doc, err := goquery.NewDocumentFromReader(res.Body)
 	if err != nil {
 		log.Fatal(err)
 	}
 
-	// Find the review items (the type of the Selection would be *goquery.Selection)
-	doc.Find(".reviews-wrap article .review-rhs").Each(func(i int, s *Selection) {
+	// Find the review items
+	doc.Find(".sidebar-reviews article .content-block").Each(func(i int, s *goquery.Selection) {
 		// For each item found, get the band and title
-		band := s.Find("h3").Text()
+		band := s.Find("a").Text()
 		title := s.Find("i").Text()
 		fmt.Printf("Review %d: %s - %s\n", i, band, title)
 	})
@@ -30,3 +41,42 @@ func ExampleScrape_MetalSucks() {
 
 	// xOutput: voluntarily fail the Example output.
 }
+
+// This example shows how to use NewDocumentFromReader from a file.
+func ExampleNewDocumentFromReader_file() {
+	// create from a file
+	f, err := os.Open("some/file.html")
+	if err != nil {
+		log.Fatal(err)
+	}
+	defer f.Close()
+	doc, err := goquery.NewDocumentFromReader(f)
+	if err != nil {
+		log.Fatal(err)
+	}
+	// use the goquery document...
+	_ = doc.Find("h1")
+}
+
+// This example shows how to use NewDocumentFromReader from a string.
+func ExampleNewDocumentFromReader_string() {
+	// create from a string
+	data := `
+<html>
+	<head>
+		<title>My document</title>
+	</head>
+	<body>
+		<h1>Header</h1>
+	</body>
+</html>`
+
+	doc, err := goquery.NewDocumentFromReader(strings.NewReader(data))
+	if err != nil {
+		log.Fatal(err)
+	}
+	header := doc.Find("h1").Text()
+	fmt.Println(header)
+
+	// Output: Header
+}

+ 27 - 6
src/github.com/PuerkitoBio/goquery/expand.go

@@ -1,16 +1,13 @@
 package goquery
 
-import (
-	"github.com/andybalholm/cascadia"
-	"golang.org/x/net/html"
-)
+import "golang.org/x/net/html"
 
 // Add adds the selector string's matching nodes to those in the current
 // selection and returns a new Selection object.
 // The selector string is run in the context of the document of the current
 // Selection object.
 func (s *Selection) Add(selector string) *Selection {
-	return s.AddNodes(findWithMatcher([]*html.Node{s.document.rootNode}, cascadia.MustCompile(selector))...)
+	return s.AddNodes(findWithMatcher([]*html.Node{s.document.rootNode}, compileMatcher(selector))...)
 }
 
 // AddMatcher adds the matcher's matching nodes to those in the current
@@ -38,12 +35,36 @@ func (s *Selection) Union(sel *Selection) *Selection {
 // AddNodes adds the specified nodes to those in the
 // current selection and returns a new Selection object.
 func (s *Selection) AddNodes(nodes ...*html.Node) *Selection {
-	return pushStack(s, appendWithoutDuplicates(s.Nodes, nodes))
+	return pushStack(s, appendWithoutDuplicates(s.Nodes, nodes, nil))
 }
 
 // AndSelf adds the previous set of elements on the stack to the current set.
 // It returns a new Selection object containing the current Selection combined
 // with the previous one.
+// Deprecated: This function has been deprecated and is now an alias for AddBack().
 func (s *Selection) AndSelf() *Selection {
+	return s.AddBack()
+}
+
+// AddBack adds the previous set of elements on the stack to the current set.
+// It returns a new Selection object containing the current Selection combined
+// with the previous one.
+func (s *Selection) AddBack() *Selection {
 	return s.AddSelection(s.prevSel)
 }
+
+// AddBackFiltered reduces the previous set of elements on the stack to those that
+// match the selector string, and adds them to the current set.
+// It returns a new Selection object containing the current Selection combined
+// with the filtered previous one
+func (s *Selection) AddBackFiltered(selector string) *Selection {
+	return s.AddSelection(s.prevSel.Filter(selector))
+}
+
+// AddBackMatcher reduces the previous set of elements on the stack to those that match
+// the mateher, and adds them to the curernt set.
+// It returns a new Selection object containing the current Selection combined
+// with the filtered previous one
+func (s *Selection) AddBackMatcher(m Matcher) *Selection {
+	return s.AddSelection(s.prevSel.FilterMatcher(m))
+}

+ 50 - 0
src/github.com/PuerkitoBio/goquery/expand_test.go

@@ -9,6 +9,16 @@ func TestAdd(t *testing.T) {
 	assertLength(t, sel.Nodes, 19)
 }
 
+func TestAddInvalid(t *testing.T) {
+	sel1 := Doc().Find("div.row-fluid")
+	sel2 := sel1.Add("")
+	assertLength(t, sel1.Nodes, 9)
+	assertLength(t, sel2.Nodes, 9)
+	if sel1 == sel2 {
+		t.Errorf("selections should not be the same")
+	}
+}
+
 func TestAddRollback(t *testing.T) {
 	sel := Doc().Find(".pvk-content")
 	sel2 := sel.Add("a").End()
@@ -56,6 +66,24 @@ func TestAddNodesRollback(t *testing.T) {
 	assertEqual(t, sel, sel2)
 }
 
+func TestAddNodesBig(t *testing.T) {
+	doc := DocW()
+	sel := doc.Find("li")
+	assertLength(t, sel.Nodes, 373)
+	sel2 := doc.Find("xyz")
+	assertLength(t, sel2.Nodes, 0)
+
+	nodes := sel.Nodes
+	sel2 = sel2.AddNodes(nodes...)
+	assertLength(t, sel2.Nodes, 373)
+	nodes2 := append(nodes, nodes...)
+	sel2 = sel2.End().AddNodes(nodes2...)
+	assertLength(t, sel2.Nodes, 373)
+	nodes3 := append(nodes2, nodes...)
+	sel2 = sel2.End().AddNodes(nodes3...)
+	assertLength(t, sel2.Nodes, 373)
+}
+
 func TestAndSelf(t *testing.T) {
 	sel := Doc().Find(".span12").Last().AndSelf()
 	assertLength(t, sel.Nodes, 2)
@@ -66,3 +94,25 @@ func TestAndSelfRollback(t *testing.T) {
 	sel2 := sel.Find("a").AndSelf().End().End()
 	assertEqual(t, sel, sel2)
 }
+
+func TestAddBack(t *testing.T) {
+	sel := Doc().Find(".span12").Last().AddBack()
+	assertLength(t, sel.Nodes, 2)
+}
+
+func TestAddBackRollback(t *testing.T) {
+	sel := Doc().Find(".pvk-content")
+	sel2 := sel.Find("a").AddBack().End().End()
+	assertEqual(t, sel, sel2)
+}
+
+func TestAddBackFiltered(t *testing.T) {
+	sel := Doc().Find(".span12, .footer").Find("h1").AddBackFiltered(".footer")
+	assertLength(t, sel.Nodes, 2)
+}
+
+func TestAddBackFilteredRollback(t *testing.T) {
+	sel := Doc().Find(".span12, .footer")
+	sel2 := sel.Find("h1").AddBackFiltered(".footer").End().End()
+	assertEqual(t, sel, sel2)
+}

+ 16 - 9
src/github.com/PuerkitoBio/goquery/filter.go

@@ -1,19 +1,16 @@
 package goquery
 
-import (
-	"github.com/andybalholm/cascadia"
-	"golang.org/x/net/html"
-)
+import "golang.org/x/net/html"
 
 // Filter reduces the set of matched elements to those that match the selector string.
 // It returns a new Selection object for this subset of matching elements.
 func (s *Selection) Filter(selector string) *Selection {
-	return s.FilterMatcher(cascadia.MustCompile(selector))
+	return s.FilterMatcher(compileMatcher(selector))
 }
 
 // FilterMatcher reduces the set of matched elements to those that match
-// the given matcher.
-// It returns a new Selection object for this subset of matching elements.
+// the given matcher. It returns a new Selection object for this subset
+// of matching elements.
 func (s *Selection) FilterMatcher(m Matcher) *Selection {
 	return pushStack(s, winnow(s, m, true))
 }
@@ -21,7 +18,7 @@ func (s *Selection) FilterMatcher(m Matcher) *Selection {
 // Not removes elements from the Selection that match the selector string.
 // It returns a new Selection object with the matching elements removed.
 func (s *Selection) Not(selector string) *Selection {
-	return s.NotMatcher(cascadia.MustCompile(selector))
+	return s.NotMatcher(compileMatcher(selector))
 }
 
 // NotMatcher removes elements from the Selection that match the given matcher.
@@ -142,8 +139,18 @@ func winnow(sel *Selection, m Matcher, keep bool) []*html.Node {
 // Filter based on an array of nodes, and the indicator to keep (Filter) or
 // to get rid of (Not) the matching elements.
 func winnowNodes(sel *Selection, nodes []*html.Node, keep bool) []*html.Node {
+	if len(nodes)+len(sel.Nodes) < minNodesForSet {
+		return grep(sel, func(i int, s *Selection) bool {
+			return isInSlice(nodes, s.Get(0)) == keep
+		})
+	}
+
+	set := make(map[*html.Node]bool)
+	for _, n := range nodes {
+		set[n] = true
+	}
 	return grep(sel, func(i int, s *Selection) bool {
-		return isInSlice(nodes, s.Get(0)) == keep
+		return set[s.Get(0)] == keep
 	})
 }
 

+ 15 - 0
src/github.com/PuerkitoBio/goquery/filter_test.go

@@ -14,6 +14,11 @@ func TestFilterNone(t *testing.T) {
 	assertLength(t, sel.Nodes, 0)
 }
 
+func TestFilterInvalid(t *testing.T) {
+	sel := Doc().Find(".span12").Filter("")
+	assertLength(t, sel.Nodes, 0)
+}
+
 func TestFilterRollback(t *testing.T) {
 	sel := Doc().Find(".pvk-content")
 	sel2 := sel.Filter(".alert").End()
@@ -74,6 +79,11 @@ func TestNot(t *testing.T) {
 	assertLength(t, sel.Nodes, 1)
 }
 
+func TestNotInvalid(t *testing.T) {
+	sel := Doc().Find(".span12").Not("")
+	assertLength(t, sel.Nodes, 2)
+}
+
 func TestNotRollback(t *testing.T) {
 	sel := Doc().Find(".span12")
 	sel2 := sel.Not(".alert").End()
@@ -145,6 +155,11 @@ func TestHas(t *testing.T) {
 	// Has() returns the high-level .container-fluid div, and the one that is the immediate parent of center-content
 }
 
+func TestHasInvalid(t *testing.T) {
+	sel := Doc().Find(".container-fluid").Has("")
+	assertLength(t, sel.Nodes, 0)
+}
+
 func TestHasRollback(t *testing.T) {
 	sel := Doc().Find(".container-fluid")
 	sel2 := sel.Has(".center-content").End()

+ 8 - 0
src/github.com/PuerkitoBio/goquery/go.mod

@@ -0,0 +1,8 @@
+module github.com/PuerkitoBio/goquery
+
+require (
+	github.com/andybalholm/cascadia v1.1.0
+	golang.org/x/net v0.0.0-20200202094626-16171245cfb2
+)
+
+go 1.13

+ 8 - 0
src/github.com/PuerkitoBio/goquery/go.sum

@@ -0,0 +1,8 @@
+github.com/andybalholm/cascadia v1.1.0 h1:BuuO6sSfQNFRu1LppgbD25Hr2vLYW25JvxHs5zzsLTo=
+github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI=
+golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

+ 8 - 2
src/github.com/PuerkitoBio/goquery/iteration.go

@@ -1,7 +1,10 @@
 package goquery
 
 // Each iterates over a Selection object, executing a function for each
-// matched element. It returns the current Selection object.
+// matched element. It returns the current Selection object. The function
+// f is called for each element in the selection with the index of the
+// element in that selection starting at 0, and a *Selection that contains
+// only that element.
 func (s *Selection) Each(f func(int, *Selection)) *Selection {
 	for i, n := range s.Nodes {
 		f(i, newSingleSelection(n, s.document))
@@ -23,7 +26,10 @@ func (s *Selection) EachWithBreak(f func(int, *Selection) bool) *Selection {
 }
 
 // Map passes each element in the current matched set through a function,
-// producing a slice of string holding the returned values.
+// producing a slice of string holding the returned values. The function
+// f is called for each element in the selection with the index of the
+// element in that selection starting at 0, and a *Selection that contains
+// only that element.
 func (s *Selection) Map(f func(int, *Selection) string) (result []string) {
 	for i, n := range s.Nodes {
 		result = append(result, f(i, newSingleSelection(n, s.document)))

+ 42 - 14
src/github.com/PuerkitoBio/goquery/manipulation.go

@@ -3,7 +3,6 @@ package goquery
 import (
 	"strings"
 
-	"github.com/andybalholm/cascadia"
 	"golang.org/x/net/html"
 )
 
@@ -15,7 +14,7 @@ import (
 //
 // This follows the same rules as Selection.Append.
 func (s *Selection) After(selector string) *Selection {
-	return s.AfterMatcher(cascadia.MustCompile(selector))
+	return s.AfterMatcher(compileMatcher(selector))
 }
 
 // AfterMatcher applies the matcher from the root document and inserts the matched elements
@@ -66,7 +65,7 @@ func (s *Selection) AfterNodes(ns ...*html.Node) *Selection {
 // appended to all target locations except the last one, which will be moved
 // as noted in (2).
 func (s *Selection) Append(selector string) *Selection {
-	return s.AppendMatcher(cascadia.MustCompile(selector))
+	return s.AppendMatcher(compileMatcher(selector))
 }
 
 // AppendMatcher appends the elements specified by the matcher to the end of each element
@@ -103,7 +102,7 @@ func (s *Selection) AppendNodes(ns ...*html.Node) *Selection {
 //
 // This follows the same rules as Selection.Append.
 func (s *Selection) Before(selector string) *Selection {
-	return s.BeforeMatcher(cascadia.MustCompile(selector))
+	return s.BeforeMatcher(compileMatcher(selector))
 }
 
 // BeforeMatcher inserts the matched elements before each element in the set of matched elements.
@@ -165,7 +164,7 @@ func (s *Selection) Empty() *Selection {
 // Prepend prepends the elements specified by the selector to each element in
 // the set of matched elements, following the same rules as Append.
 func (s *Selection) Prepend(selector string) *Selection {
-	return s.PrependMatcher(cascadia.MustCompile(selector))
+	return s.PrependMatcher(compileMatcher(selector))
 }
 
 // PrependMatcher prepends the elements specified by the matcher to each
@@ -213,14 +212,19 @@ func (s *Selection) Remove() *Selection {
 	return s
 }
 
-// RemoveFiltered removes the set of matched elements by selector.
-// It returns the Selection of removed nodes.
+// RemoveFiltered removes from the current set of matched elements those that
+// match the selector filter. It returns the Selection of removed nodes.
+//
+// For example if the selection s contains "<h1>", "<h2>" and "<h3>"
+// and s.RemoveFiltered("h2") is called, only the "<h2>" node is removed
+// (and returned), while "<h1>" and "<h3>" are kept in the document.
 func (s *Selection) RemoveFiltered(selector string) *Selection {
-	return s.RemoveMatcher(cascadia.MustCompile(selector))
+	return s.RemoveMatcher(compileMatcher(selector))
 }
 
-// RemoveMatcher removes the set of matched elements.
-// It returns the Selection of removed nodes.
+// RemoveMatcher removes from the current set of matched elements those that
+// match the Matcher filter. It returns the Selection of removed nodes.
+// See RemoveFiltered for additional information.
 func (s *Selection) RemoveMatcher(m Matcher) *Selection {
 	return s.FilterMatcher(m).Remove()
 }
@@ -231,7 +235,7 @@ func (s *Selection) RemoveMatcher(m Matcher) *Selection {
 //
 // This follows the same rules as Selection.Append.
 func (s *Selection) ReplaceWith(selector string) *Selection {
-	return s.ReplaceWithMatcher(cascadia.MustCompile(selector))
+	return s.ReplaceWithMatcher(compileMatcher(selector))
 }
 
 // ReplaceWithMatcher replaces each element in the set of matched elements with
@@ -271,6 +275,18 @@ func (s *Selection) ReplaceWithNodes(ns ...*html.Node) *Selection {
 	return s.Remove()
 }
 
+// SetHtml sets the html content of each element in the selection to
+// specified html string.
+func (s *Selection) SetHtml(html string) *Selection {
+	return setHtmlNodes(s, parseHtml(html)...)
+}
+
+// SetText sets the content of each element in the selection to specified content.
+// The provided text string is escaped.
+func (s *Selection) SetText(text string) *Selection {
+	return s.SetHtml(html.EscapeString(text))
+}
+
 // Unwrap removes the parents of the set of matched elements, leaving the matched
 // elements (and their siblings, if any) in their place.
 // It returns the original selection.
@@ -293,7 +309,7 @@ func (s *Selection) Unwrap() *Selection {
 //
 // It returns the original set of elements.
 func (s *Selection) Wrap(selector string) *Selection {
-	return s.WrapMatcher(cascadia.MustCompile(selector))
+	return s.WrapMatcher(compileMatcher(selector))
 }
 
 // WrapMatcher wraps each element in the set of matched elements inside the
@@ -345,7 +361,7 @@ func (s *Selection) wrapNodes(ns ...*html.Node) *Selection {
 //
 // It returns the original set of elements.
 func (s *Selection) WrapAll(selector string) *Selection {
-	return s.WrapAllMatcher(cascadia.MustCompile(selector))
+	return s.WrapAllMatcher(compileMatcher(selector))
 }
 
 // WrapAllMatcher wraps a single HTML structure, matched by the given Matcher,
@@ -415,7 +431,7 @@ func (s *Selection) WrapAllNode(n *html.Node) *Selection {
 //
 // It returns the original set of elements.
 func (s *Selection) WrapInner(selector string) *Selection {
-	return s.WrapInnerMatcher(cascadia.MustCompile(selector))
+	return s.WrapInnerMatcher(compileMatcher(selector))
 }
 
 // WrapInnerMatcher wraps an HTML structure, matched by the given selector,
@@ -482,6 +498,18 @@ func parseHtml(h string) []*html.Node {
 	return nodes
 }
 
+func setHtmlNodes(s *Selection, ns ...*html.Node) *Selection {
+	for _, n := range s.Nodes {
+		for c := n.FirstChild; c != nil; c = n.FirstChild {
+			n.RemoveChild(c)
+		}
+		for _, c := range ns {
+			n.AppendChild(cloneNode(c))
+		}
+	}
+	return s
+}
+
 // Get the first child that is an ElementNode
 func getFirstChildEl(n *html.Node) *html.Node {
 	c := n.FirstChild

+ 60 - 0
src/github.com/PuerkitoBio/goquery/manipulation_test.go

@@ -278,6 +278,66 @@ func TestReplaceWithHtml(t *testing.T) {
 	printSel(t, doc.Selection)
 }
 
+func TestSetHtml(t *testing.T) {
+	doc := Doc2Clone()
+	q := doc.Find("#main, #foot")
+	q.SetHtml(`<div id="replace">test</div>`)
+
+	assertLength(t, doc.Find("#replace").Nodes, 2)
+	assertLength(t, doc.Find("#main, #foot").Nodes, 2)
+
+	if q.Text() != "testtest" {
+		t.Errorf("Expected text to be %v, found %v", "testtest", q.Text())
+	}
+
+	printSel(t, doc.Selection)
+}
+
+func TestSetHtmlNoMatch(t *testing.T) {
+	doc := Doc2Clone()
+	q := doc.Find("#notthere")
+	q.SetHtml(`<div id="replace">test</div>`)
+
+	assertLength(t, doc.Find("#replace").Nodes, 0)
+
+	printSel(t, doc.Selection)
+}
+
+func TestSetHtmlEmpty(t *testing.T) {
+	doc := Doc2Clone()
+	q := doc.Find("#main")
+	q.SetHtml(``)
+
+	assertLength(t, doc.Find("#main").Nodes, 1)
+	assertLength(t, doc.Find("#main").Children().Nodes, 0)
+	printSel(t, doc.Selection)
+}
+
+func TestSetText(t *testing.T) {
+	doc := Doc2Clone()
+	q := doc.Find("#main, #foot")
+	repl := "<div id=\"replace\">test</div>"
+	q.SetText(repl)
+
+	assertLength(t, doc.Find("#replace").Nodes, 0)
+	assertLength(t, doc.Find("#main, #foot").Nodes, 2)
+
+	if q.Text() != (repl + repl) {
+		t.Errorf("Expected text to be %v, found %v", (repl + repl), q.Text())
+	}
+
+	h, err := q.Html()
+	if err != nil {
+		t.Errorf("Error: %v", err)
+	}
+	esc := "&lt;div id=&#34;replace&#34;&gt;test&lt;/div&gt;"
+	if h != esc {
+		t.Errorf("Expected html to be %v, found %v", esc, h)
+	}
+
+	printSel(t, doc.Selection)
+}
+
 func TestReplaceWithSelection(t *testing.T) {
 	doc := Doc2Clone()
 	sel := doc.Find("#nf6").ReplaceWithSelection(doc.Find("#nf5"))

+ 37 - 0
src/github.com/PuerkitoBio/goquery/misc/git/pre-commit

@@ -0,0 +1,37 @@
+#!/bin/sh
+
+echo ">>> golint"
+for dir in $(go list ./... | grep -v /vendor/)
+do
+    golint "${dir}"
+done
+echo "<<< golint"
+echo
+
+echo ">>> go vet"
+go vet $(go list ./... | grep -v /vendor/)
+echo "<<< go vet"
+echo
+
+echo ">>> gosimple"
+gosimple $(go list ./... | grep -v /vendor/)
+echo "<<< gosimple"
+echo
+
+# Check for gofmt problems and report if any.
+gofiles=$(git diff --cached --name-only --diff-filter=ACM | grep '.go$' | grep -v /vendor/)
+[ -z "$gofiles" ] && echo "EXIT $vetres" && exit $vetres
+
+if [ -n "$gofiles" ]; then
+    unformatted=$(gofmt -l $gofiles)
+
+    if [ -n "$unformatted" ]; then
+        # Some files are not gofmt'd.
+        echo >&2 "Go files must be formatted with gofmt. Please run:"
+        for fn in $unformatted; do
+            echo >&2 "  gofmt -w $PWD/$fn"
+        done
+    fi
+fi
+echo
+

+ 17 - 34
src/github.com/PuerkitoBio/goquery/property.go

@@ -63,9 +63,22 @@ func (s *Selection) Text() string {
 	var buf bytes.Buffer
 
 	// Slightly optimized vs calling Each: no single selection object created
+	var f func(*html.Node)
+	f = func(n *html.Node) {
+		if n.Type == html.TextNode {
+			// Keep newlines and spaces, like jQuery
+			buf.WriteString(n.Data)
+		}
+		if n.FirstChild != nil {
+			for c := n.FirstChild; c != nil; c = c.NextSibling {
+				f(c)
+			}
+		}
+	}
 	for _, n := range s.Nodes {
-		buf.WriteString(getNodeText(n))
+		f(n)
 	}
+
 	return buf.String()
 }
 
@@ -99,20 +112,6 @@ func (s *Selection) Html() (ret string, e error) {
 	return
 }
 
-func (s *Selection) OuterHtml() (ret string, e error) {
-	// Since there is no .innerHtml, the HTML content must be re-created from
-	// the nodes using html.Render.
-	var buf bytes.Buffer
-	if len(s.Nodes) > 0 {
-		e = html.Render(&buf, s.Nodes[0])
-		if e != nil {
-			return
-		}
-		ret = buf.String()
-	}
-	return
-}
-
 // AddClass adds the given class(es) to each element in the set of matched elements.
 // Multiple class names can be specified, separated by a space or via multiple arguments.
 func (s *Selection) AddClass(class ...string) *Selection {
@@ -126,7 +125,7 @@ func (s *Selection) AddClass(class ...string) *Selection {
 	for _, n := range s.Nodes {
 		curClasses, attr := getClassesAndAttr(n, true)
 		for _, newClass := range tcls {
-			if strings.Index(curClasses, " "+newClass+" ") == -1 {
+			if !strings.Contains(curClasses, " "+newClass+" ") {
 				curClasses += newClass + " "
 			}
 		}
@@ -143,7 +142,7 @@ func (s *Selection) HasClass(class string) bool {
 	class = " " + class + " "
 	for _, n := range s.Nodes {
 		classes, _ := getClassesAndAttr(n, false)
-		if strings.Index(classes, class) > -1 {
+		if strings.Contains(classes, class) {
 			return true
 		}
 	}
@@ -193,7 +192,7 @@ func (s *Selection) ToggleClass(class ...string) *Selection {
 	for _, n := range s.Nodes {
 		classes, attr := getClassesAndAttr(n, true)
 		for _, tcl := range tcls {
-			if strings.Index(classes, " "+tcl+" ") != -1 {
+			if strings.Contains(classes, " "+tcl+" ") {
 				classes = strings.Replace(classes, " "+tcl+" ", " ", -1)
 			} else {
 				classes += tcl + " "
@@ -206,22 +205,6 @@ func (s *Selection) ToggleClass(class ...string) *Selection {
 	return s
 }
 
-// Get the specified node's text content.
-func getNodeText(node *html.Node) string {
-	if node.Type == html.TextNode {
-		// Keep newlines and spaces, like jQuery
-		return node.Data
-	} else if node.FirstChild != nil {
-		var buf bytes.Buffer
-		for c := node.FirstChild; c != nil; c = c.NextSibling {
-			buf.WriteString(getNodeText(c))
-		}
-		return buf.String()
-	}
-
-	return ""
-}
-
 func getAttributePtr(attrName string, n *html.Node) *html.Attribute {
 	if n == nil {
 		return nil

+ 2 - 9
src/github.com/PuerkitoBio/goquery/query.go

@@ -1,18 +1,11 @@
 package goquery
 
-import (
-	"github.com/andybalholm/cascadia"
-	"golang.org/x/net/html"
-)
+import "golang.org/x/net/html"
 
 // Is checks the current matched set of elements against a selector and
 // returns true if at least one of these elements matches.
 func (s *Selection) Is(selector string) bool {
-	if len(s.Nodes) > 0 {
-		return s.IsMatcher(cascadia.MustCompile(selector))
-	}
-
-	return false
+	return s.IsMatcher(compileMatcher(selector))
 }
 
 // IsMatcher checks the current matched set of elements against a matcher and

+ 7 - 0
src/github.com/PuerkitoBio/goquery/query_test.go

@@ -11,6 +11,13 @@ func TestIs(t *testing.T) {
 	}
 }
 
+func TestIsInvalid(t *testing.T) {
+	sel := Doc().Find(".footer p:nth-child(1)")
+	if sel.Is("") {
+		t.Error("Is should not succeed with invalid selector string")
+	}
+}
+
 func TestIsPositional(t *testing.T) {
 	sel := Doc().Find(".footer p:nth-child(2)")
 	if !sel.Is("p:nth-child(2)") {

+ 855 - 0
src/github.com/PuerkitoBio/goquery/testdata/gotesting.html

@@ -0,0 +1,855 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+
+  <title>testing - The Go Programming Language</title>
+
+<link type="text/css" rel="stylesheet" href="/doc/style.css">
+<script type="text/javascript" src="/doc/godocs.js"></script>
+
+<link rel="search" type="application/opensearchdescription+xml" title="godoc" href="/opensearch.xml" />
+
+<script type="text/javascript">
+var _gaq = _gaq || [];
+_gaq.push(["_setAccount", "UA-11222381-2"]);
+_gaq.push(["_trackPageview"]);
+</script>
+</head>
+<body>
+
+<div id="topbar"><div class="container wide">
+
+<form method="GET" action="/search">
+<div id="menu">
+<a href="/doc/">Documents</a>
+<a href="/ref/">References</a>
+<a href="/pkg/">Packages</a>
+<a href="/project/">The Project</a>
+<a href="/help/">Help</a>
+<input type="text" id="search" name="q" class="inactive" value="Search">
+</div>
+<div id="heading"><a href="/">The Go Programming Language</a></div>
+</form>
+
+</div></div>
+
+<div id="page" class="wide">
+
+
+  <div id="plusone"><g:plusone size="small" annotation="none"></g:plusone></div>
+  <h1>Package testing</h1>
+
+
+
+
+<div id="nav"></div>
+
+
+<!--
+	Copyright 2009 The Go Authors. All rights reserved.
+	Use of this source code is governed by a BSD-style
+	license that can be found in the LICENSE file.
+-->
+
+	
+		<div id="short-nav">
+			<dl>
+			<dd><code>import "testing"</code></dd>
+			</dl>
+			<dl>
+			<dd><a href="#overview" class="overviewLink">Overview</a></dd>
+			<dd><a href="#index">Index</a></dd>
+			
+			
+				<dd><a href="#subdirectories">Subdirectories</a></dd>
+			
+			</dl>
+		</div>
+		<!-- The package's Name is printed as title by the top-level template -->
+		<div id="overview" class="toggleVisible">
+			<div class="collapsed">
+				<h2 class="toggleButton" title="Click to show Overview section">Overview ▹</h2>
+			</div>
+			<div class="expanded">
+				<h2 class="toggleButton" title="Click to hide Overview section">Overview ▾</h2>
+				<p>
+Package testing provides support for automated testing of Go packages.
+It is intended to be used in concert with the &ldquo;go test&rdquo; command, which automates
+execution of any function of the form
+</p>
+<pre>func TestXxx(*testing.T)
+</pre>
+<p>
+where Xxx can be any alphanumeric string (but the first letter must not be in
+[a-z]) and serves to identify the test routine.
+These TestXxx routines should be declared within the package they are testing.
+</p>
+<p>
+Functions of the form
+</p>
+<pre>func BenchmarkXxx(*testing.B)
+</pre>
+<p>
+are considered benchmarks, and are executed by the &#34;go test&#34; command when
+the -test.bench flag is provided.
+</p>
+<p>
+A sample benchmark function looks like this:
+</p>
+<pre>func BenchmarkHello(b *testing.B) {
+    for i := 0; i &lt; b.N; i++ {
+        fmt.Sprintf(&#34;hello&#34;)
+    }
+}
+</pre>
+<p>
+The benchmark package will vary b.N until the benchmark function lasts
+long enough to be timed reliably.  The output
+</p>
+<pre>testing.BenchmarkHello    10000000    282 ns/op
+</pre>
+<p>
+means that the loop ran 10000000 times at a speed of 282 ns per loop.
+</p>
+<p>
+If a benchmark needs some expensive setup before running, the timer
+may be stopped:
+</p>
+<pre>func BenchmarkBigLen(b *testing.B) {
+    b.StopTimer()
+    big := NewBig()
+    b.StartTimer()
+    for i := 0; i &lt; b.N; i++ {
+        big.Len()
+    }
+}
+</pre>
+<p>
+The package also runs and verifies example code. Example functions may
+include a concluding comment that begins with &#34;Output:&#34; and is compared with
+the standard output of the function when the tests are run, as in these
+examples of an example:
+</p>
+<pre>func ExampleHello() {
+        fmt.Println(&#34;hello&#34;)
+        // Output: hello
+}
+
+func ExampleSalutations() {
+        fmt.Println(&#34;hello, and&#34;)
+        fmt.Println(&#34;goodbye&#34;)
+        // Output:
+        // hello, and
+        // goodbye
+}
+</pre>
+<p>
+Example functions without output comments are compiled but not executed.
+</p>
+<p>
+The naming convention to declare examples for a function F, a type T and
+method M on type T are:
+</p>
+<pre>func ExampleF() { ... }
+func ExampleT() { ... }
+func ExampleT_M() { ... }
+</pre>
+<p>
+Multiple example functions for a type/function/method may be provided by
+appending a distinct suffix to the name. The suffix must start with a
+lower-case letter.
+</p>
+<pre>func ExampleF_suffix() { ... }
+func ExampleT_suffix() { ... }
+func ExampleT_M_suffix() { ... }
+</pre>
+<p>
+The entire test file is presented as the example when it contains a single
+example function, at least one other function, type, variable, or constant
+declaration, and no test or benchmark functions.
+</p>
+
+			</div>
+		</div>
+		
+	
+		<h2 id="index">Index</h2>
+		<!-- Table of contents for API; must be named manual-nav to turn off auto nav. -->
+		<div id="manual-nav">
+			<dl>
+			
+			
+			
+				
+				<dd><a href="#Main">func Main(matchString func(pat, str string) (bool, error), tests []InternalTest, benchmarks []InternalBenchmark, examples []InternalExample)</a></dd>
+			
+				
+				<dd><a href="#RunBenchmarks">func RunBenchmarks(matchString func(pat, str string) (bool, error), benchmarks []InternalBenchmark)</a></dd>
+			
+				
+				<dd><a href="#RunExamples">func RunExamples(matchString func(pat, str string) (bool, error), examples []InternalExample) (ok bool)</a></dd>
+			
+				
+				<dd><a href="#RunTests">func RunTests(matchString func(pat, str string) (bool, error), tests []InternalTest) (ok bool)</a></dd>
+			
+				
+				<dd><a href="#Short">func Short() bool</a></dd>
+			
+			
+				
+				<dd><a href="#B">type B</a></dd>
+				
+				
+					
+					<dd>&nbsp; &nbsp; <a href="#B.Error">func (c *B) Error(args ...interface{})</a></dd>
+				
+					
+					<dd>&nbsp; &nbsp; <a href="#B.Errorf">func (c *B) Errorf(format string, args ...interface{})</a></dd>
+				
+					
+					<dd>&nbsp; &nbsp; <a href="#B.Fail">func (c *B) Fail()</a></dd>
+				
+					
+					<dd>&nbsp; &nbsp; <a href="#B.FailNow">func (c *B) FailNow()</a></dd>
+				
+					
+					<dd>&nbsp; &nbsp; <a href="#B.Failed">func (c *B) Failed() bool</a></dd>
+				
+					
+					<dd>&nbsp; &nbsp; <a href="#B.Fatal">func (c *B) Fatal(args ...interface{})</a></dd>
+				
+					
+					<dd>&nbsp; &nbsp; <a href="#B.Fatalf">func (c *B) Fatalf(format string, args ...interface{})</a></dd>
+				
+					
+					<dd>&nbsp; &nbsp; <a href="#B.Log">func (c *B) Log(args ...interface{})</a></dd>
+				
+					
+					<dd>&nbsp; &nbsp; <a href="#B.Logf">func (c *B) Logf(format string, args ...interface{})</a></dd>
+				
+					
+					<dd>&nbsp; &nbsp; <a href="#B.ResetTimer">func (b *B) ResetTimer()</a></dd>
+				
+					
+					<dd>&nbsp; &nbsp; <a href="#B.SetBytes">func (b *B) SetBytes(n int64)</a></dd>
+				
+					
+					<dd>&nbsp; &nbsp; <a href="#B.StartTimer">func (b *B) StartTimer()</a></dd>
+				
+					
+					<dd>&nbsp; &nbsp; <a href="#B.StopTimer">func (b *B) StopTimer()</a></dd>
+				
+			
+				
+				<dd><a href="#BenchmarkResult">type BenchmarkResult</a></dd>
+				
+					
+					<dd>&nbsp; &nbsp; <a href="#Benchmark">func Benchmark(f func(b *B)) BenchmarkResult</a></dd>
+				
+				
+					
+					<dd>&nbsp; &nbsp; <a href="#BenchmarkResult.NsPerOp">func (r BenchmarkResult) NsPerOp() int64</a></dd>
+				
+					
+					<dd>&nbsp; &nbsp; <a href="#BenchmarkResult.String">func (r BenchmarkResult) String() string</a></dd>
+				
+			
+				
+				<dd><a href="#InternalBenchmark">type InternalBenchmark</a></dd>
+				
+				
+			
+				
+				<dd><a href="#InternalExample">type InternalExample</a></dd>
+				
+				
+			
+				
+				<dd><a href="#InternalTest">type InternalTest</a></dd>
+				
+				
+			
+				
+				<dd><a href="#T">type T</a></dd>
+				
+				
+					
+					<dd>&nbsp; &nbsp; <a href="#T.Error">func (c *T) Error(args ...interface{})</a></dd>
+				
+					
+					<dd>&nbsp; &nbsp; <a href="#T.Errorf">func (c *T) Errorf(format string, args ...interface{})</a></dd>
+				
+					
+					<dd>&nbsp; &nbsp; <a href="#T.Fail">func (c *T) Fail()</a></dd>
+				
+					
+					<dd>&nbsp; &nbsp; <a href="#T.FailNow">func (c *T) FailNow()</a></dd>
+				
+					
+					<dd>&nbsp; &nbsp; <a href="#T.Failed">func (c *T) Failed() bool</a></dd>
+				
+					
+					<dd>&nbsp; &nbsp; <a href="#T.Fatal">func (c *T) Fatal(args ...interface{})</a></dd>
+				
+					
+					<dd>&nbsp; &nbsp; <a href="#T.Fatalf">func (c *T) Fatalf(format string, args ...interface{})</a></dd>
+				
+					
+					<dd>&nbsp; &nbsp; <a href="#T.Log">func (c *T) Log(args ...interface{})</a></dd>
+				
+					
+					<dd>&nbsp; &nbsp; <a href="#T.Logf">func (c *T) Logf(format string, args ...interface{})</a></dd>
+				
+					
+					<dd>&nbsp; &nbsp; <a href="#T.Parallel">func (t *T) Parallel()</a></dd>
+				
+			
+			
+		</dl>
+
+		
+
+		
+			<h4>Package files</h4>
+			<p>
+			<span style="font-size:90%">
+			
+				<a href="/src/pkg/testing/benchmark.go">benchmark.go</a>
+			
+				<a href="/src/pkg/testing/example.go">example.go</a>
+			
+				<a href="/src/pkg/testing/testing.go">testing.go</a>
+			
+			</span>
+			</p>
+		
+	
+		
+		
+		
+			
+			
+			<h2 id="Main">func <a href="/src/pkg/testing/testing.go?s=9750:9890#L268">Main</a></h2>
+			<pre>func Main(matchString func(pat, str string) (bool, error), tests []InternalTest, benchmarks []InternalBenchmark, examples []InternalExample)</pre>
+			<p>
+An internal function but exported because it is cross-package; part of the implementation
+of the &#34;go test&#34; command.
+</p>
+
+			
+		
+			
+			
+			<h2 id="RunBenchmarks">func <a href="/src/pkg/testing/benchmark.go?s=5365:5464#L207">RunBenchmarks</a></h2>
+			<pre>func RunBenchmarks(matchString func(pat, str string) (bool, error), benchmarks []InternalBenchmark)</pre>
+			<p>
+An internal function but exported because it is cross-package; part of the implementation
+of the &#34;go test&#34; command.
+</p>
+
+			
+		
+			
+			
+			<h2 id="RunExamples">func <a href="/src/pkg/testing/example.go?s=314:417#L12">RunExamples</a></h2>
+			<pre>func RunExamples(matchString func(pat, str string) (bool, error), examples []InternalExample) (ok bool)</pre>
+			
+			
+		
+			
+			
+			<h2 id="RunTests">func <a href="/src/pkg/testing/testing.go?s=10486:10580#L297">RunTests</a></h2>
+			<pre>func RunTests(matchString func(pat, str string) (bool, error), tests []InternalTest) (ok bool)</pre>
+			
+			
+		
+			
+			
+			<h2 id="Short">func <a href="/src/pkg/testing/testing.go?s=4859:4876#L117">Short</a></h2>
+			<pre>func Short() bool</pre>
+			<p>
+Short reports whether the -test.short flag is set.
+</p>
+
+			
+		
+		
+			
+			
+			<h2 id="B">type <a href="/src/pkg/testing/benchmark.go?s=743:872#L17">B</a></h2>
+			<pre>type B struct {
+    N int
+    <span class="comment">// contains filtered or unexported fields</span>
+}</pre>
+			<p>
+B is a type passed to Benchmark functions to manage benchmark
+timing and to specify the number of iterations to run.
+</p>
+
+
+			
+
+			
+
+			
+
+			
+
+			
+				
+				<h3 id="B.Error">func (*B) <a href="/src/pkg/testing/testing.go?s=8110:8153#L209">Error</a></h3>
+				<pre>func (c *B) Error(args ...interface{})</pre>
+				<p>
+Error is equivalent to Log() followed by Fail().
+</p>
+
+				
+				
+			
+				
+				<h3 id="B.Errorf">func (*B) <a href="/src/pkg/testing/testing.go?s=8253:8312#L215">Errorf</a></h3>
+				<pre>func (c *B) Errorf(format string, args ...interface{})</pre>
+				<p>
+Errorf is equivalent to Logf() followed by Fail().
+</p>
+
+				
+				
+			
+				
+				<h3 id="B.Fail">func (*B) <a href="/src/pkg/testing/testing.go?s=6270:6293#L163">Fail</a></h3>
+				<pre>func (c *B) Fail()</pre>
+				<p>
+Fail marks the function as having failed but continues execution.
+</p>
+
+				
+				
+			
+				
+				<h3 id="B.FailNow">func (*B) <a href="/src/pkg/testing/testing.go?s=6548:6574#L170">FailNow</a></h3>
+				<pre>func (c *B) FailNow()</pre>
+				<p>
+FailNow marks the function as having failed and stops its execution.
+Execution will continue at the next test or benchmark.
+</p>
+
+				
+				
+			
+				
+				<h3 id="B.Failed">func (*B) <a href="/src/pkg/testing/testing.go?s=6366:6396#L166">Failed</a></h3>
+				<pre>func (c *B) Failed() bool</pre>
+				<p>
+Failed returns whether the function has failed.
+</p>
+
+				
+				
+			
+				
+				<h3 id="B.Fatal">func (*B) <a href="/src/pkg/testing/testing.go?s=8420:8463#L221">Fatal</a></h3>
+				<pre>func (c *B) Fatal(args ...interface{})</pre>
+				<p>
+Fatal is equivalent to Log() followed by FailNow().
+</p>
+
+				
+				
+			
+				
+				<h3 id="B.Fatalf">func (*B) <a href="/src/pkg/testing/testing.go?s=8569:8628#L227">Fatalf</a></h3>
+				<pre>func (c *B) Fatalf(format string, args ...interface{})</pre>
+				<p>
+Fatalf is equivalent to Logf() followed by FailNow().
+</p>
+
+				
+				
+			
+				
+				<h3 id="B.Log">func (*B) <a href="/src/pkg/testing/testing.go?s=7763:7804#L202">Log</a></h3>
+				<pre>func (c *B) Log(args ...interface{})</pre>
+				<p>
+Log formats its arguments using default formatting, analogous to Println(),
+and records the text in the error log.
+</p>
+
+				
+				
+			
+				
+				<h3 id="B.Logf">func (*B) <a href="/src/pkg/testing/testing.go?s=7959:8016#L206">Logf</a></h3>
+				<pre>func (c *B) Logf(format string, args ...interface{})</pre>
+				<p>
+Logf formats its arguments according to the format, analogous to Printf(),
+and records the text in the error log.
+</p>
+
+				
+				
+			
+				
+				<h3 id="B.ResetTimer">func (*B) <a href="/src/pkg/testing/benchmark.go?s=1503:1527#L48">ResetTimer</a></h3>
+				<pre>func (b *B) ResetTimer()</pre>
+				<p>
+ResetTimer sets the elapsed benchmark time to zero.
+It does not affect whether the timer is running.
+</p>
+
+				
+				
+			
+				
+				<h3 id="B.SetBytes">func (*B) <a href="/src/pkg/testing/benchmark.go?s=1728:1757#L57">SetBytes</a></h3>
+				<pre>func (b *B) SetBytes(n int64)</pre>
+				<p>
+SetBytes records the number of bytes processed in a single operation.
+If this is called, the benchmark will report ns/op and MB/s.
+</p>
+
+				
+				
+			
+				
+				<h3 id="B.StartTimer">func (*B) <a href="/src/pkg/testing/benchmark.go?s=1047:1071#L29">StartTimer</a></h3>
+				<pre>func (b *B) StartTimer()</pre>
+				<p>
+StartTimer starts timing a test.  This function is called automatically
+before a benchmark starts, but it can also used to resume timing after
+a call to StopTimer.
+</p>
+
+				
+				
+			
+				
+				<h3 id="B.StopTimer">func (*B) <a href="/src/pkg/testing/benchmark.go?s=1288:1311#L39">StopTimer</a></h3>
+				<pre>func (b *B) StopTimer()</pre>
+				<p>
+StopTimer stops timing a test.  This can be used to pause the timer
+while performing complex initialization that you don&#39;t
+want to measure.
+</p>
+
+				
+				
+			
+		
+			
+			
+			<h2 id="BenchmarkResult">type <a href="/src/pkg/testing/benchmark.go?s=4206:4391#L165">BenchmarkResult</a></h2>
+			<pre>type BenchmarkResult struct {
+    N     int           <span class="comment">// The number of iterations.</span>
+    T     time.Duration <span class="comment">// The total time taken.</span>
+    Bytes int64         <span class="comment">// Bytes processed in one iteration.</span>
+}</pre>
+			<p>
+The results of a benchmark run.
+</p>
+
+
+			
+
+			
+
+			
+
+			
+				
+				<h3 id="Benchmark">func <a href="/src/pkg/testing/benchmark.go?s=7545:7589#L275">Benchmark</a></h3>
+				<pre>func Benchmark(f func(b *B)) BenchmarkResult</pre>
+				<p>
+Benchmark benchmarks a single function. Useful for creating
+custom benchmarks that do not use the &#34;go test&#34; command.
+</p>
+
+				
+			
+
+			
+				
+				<h3 id="BenchmarkResult.NsPerOp">func (BenchmarkResult) <a href="/src/pkg/testing/benchmark.go?s=4393:4433#L171">NsPerOp</a></h3>
+				<pre>func (r BenchmarkResult) NsPerOp() int64</pre>
+				
+				
+				
+			
+				
+				<h3 id="BenchmarkResult.String">func (BenchmarkResult) <a href="/src/pkg/testing/benchmark.go?s=4677:4717#L185">String</a></h3>
+				<pre>func (r BenchmarkResult) String() string</pre>
+				
+				
+				
+			
+		
+			
+			
+			<h2 id="InternalBenchmark">type <a href="/src/pkg/testing/benchmark.go?s=555:618#L10">InternalBenchmark</a></h2>
+			<pre>type InternalBenchmark struct {
+    Name string
+    F    func(b *B)
+}</pre>
+			<p>
+An internal type but exported because it is cross-package; part of the implementation
+of the &#34;go test&#34; command.
+</p>
+
+
+			
+
+			
+
+			
+
+			
+
+			
+		
+			
+			
+			<h2 id="InternalExample">type <a href="/src/pkg/testing/example.go?s=236:312#L6">InternalExample</a></h2>
+			<pre>type InternalExample struct {
+    Name   string
+    F      func()
+    Output string
+}</pre>
+			
+
+			
+
+			
+
+			
+
+			
+
+			
+		
+			
+			
+			<h2 id="InternalTest">type <a href="/src/pkg/testing/testing.go?s=9065:9121#L241">InternalTest</a></h2>
+			<pre>type InternalTest struct {
+    Name string
+    F    func(*T)
+}</pre>
+			<p>
+An internal type but exported because it is cross-package; part of the implementation
+of the &#34;go test&#34; command.
+</p>
+
+
+			
+
+			
+
+			
+
+			
+
+			
+		
+			
+			
+			<h2 id="T">type <a href="/src/pkg/testing/testing.go?s=6070:6199#L156">T</a></h2>
+			<pre>type T struct {
+    <span class="comment">// contains filtered or unexported fields</span>
+}</pre>
+			<p>
+T is a type passed to Test functions to manage test state and support formatted test logs.
+Logs are accumulated during execution and dumped to standard error when done.
+</p>
+
+
+			
+
+			
+
+			
+
+			
+
+			
+				
+				<h3 id="T.Error">func (*T) <a href="/src/pkg/testing/testing.go?s=8110:8153#L209">Error</a></h3>
+				<pre>func (c *T) Error(args ...interface{})</pre>
+				<p>
+Error is equivalent to Log() followed by Fail().
+</p>
+
+				
+				
+			
+				
+				<h3 id="T.Errorf">func (*T) <a href="/src/pkg/testing/testing.go?s=8253:8312#L215">Errorf</a></h3>
+				<pre>func (c *T) Errorf(format string, args ...interface{})</pre>
+				<p>
+Errorf is equivalent to Logf() followed by Fail().
+</p>
+
+				
+				
+			
+				
+				<h3 id="T.Fail">func (*T) <a href="/src/pkg/testing/testing.go?s=6270:6293#L163">Fail</a></h3>
+				<pre>func (c *T) Fail()</pre>
+				<p>
+Fail marks the function as having failed but continues execution.
+</p>
+
+				
+				
+			
+				
+				<h3 id="T.FailNow">func (*T) <a href="/src/pkg/testing/testing.go?s=6548:6574#L170">FailNow</a></h3>
+				<pre>func (c *T) FailNow()</pre>
+				<p>
+FailNow marks the function as having failed and stops its execution.
+Execution will continue at the next test or benchmark.
+</p>
+
+				
+				
+			
+				
+				<h3 id="T.Failed">func (*T) <a href="/src/pkg/testing/testing.go?s=6366:6396#L166">Failed</a></h3>
+				<pre>func (c *T) Failed() bool</pre>
+				<p>
+Failed returns whether the function has failed.
+</p>
+
+				
+				
+			
+				
+				<h3 id="T.Fatal">func (*T) <a href="/src/pkg/testing/testing.go?s=8420:8463#L221">Fatal</a></h3>
+				<pre>func (c *T) Fatal(args ...interface{})</pre>
+				<p>
+Fatal is equivalent to Log() followed by FailNow().
+</p>
+
+				
+				
+			
+				
+				<h3 id="T.Fatalf">func (*T) <a href="/src/pkg/testing/testing.go?s=8569:8628#L227">Fatalf</a></h3>
+				<pre>func (c *T) Fatalf(format string, args ...interface{})</pre>
+				<p>
+Fatalf is equivalent to Logf() followed by FailNow().
+</p>
+
+				
+				
+			
+				
+				<h3 id="T.Log">func (*T) <a href="/src/pkg/testing/testing.go?s=7763:7804#L202">Log</a></h3>
+				<pre>func (c *T) Log(args ...interface{})</pre>
+				<p>
+Log formats its arguments using default formatting, analogous to Println(),
+and records the text in the error log.
+</p>
+
+				
+				
+			
+				
+				<h3 id="T.Logf">func (*T) <a href="/src/pkg/testing/testing.go?s=7959:8016#L206">Logf</a></h3>
+				<pre>func (c *T) Logf(format string, args ...interface{})</pre>
+				<p>
+Logf formats its arguments according to the format, analogous to Printf(),
+and records the text in the error log.
+</p>
+
+				
+				
+			
+				
+				<h3 id="T.Parallel">func (*T) <a href="/src/pkg/testing/testing.go?s=8809:8831#L234">Parallel</a></h3>
+				<pre>func (t *T) Parallel()</pre>
+				<p>
+Parallel signals that this test is to be run in parallel with (and only with)
+other parallel tests in this CPU group.
+</p>
+
+				
+				
+			
+		
+		</div>
+	
+
+	
+
+
+
+
+
+
+
+	
+	
+		<h2 id="subdirectories">Subdirectories</h2>
+	
+	<table class="dir">
+	<tr>
+	<th>Name</th>
+	<th>&nbsp;&nbsp;&nbsp;&nbsp;</th>
+	<th style="text-align: left; width: auto">Synopsis</th>
+	</tr>
+	
+		<tr>
+		<td><a href="..">..</a></td>
+		</tr>
+	
+	
+		
+			<tr>
+			<td class="name"><a href="iotest">iotest</a></td>
+			<td>&nbsp;&nbsp;&nbsp;&nbsp;</td>
+			<td style="width: auto">Package iotest implements Readers and Writers useful mainly for testing.</td>
+			</tr>
+		
+	
+		
+			<tr>
+			<td class="name"><a href="quick">quick</a></td>
+			<td>&nbsp;&nbsp;&nbsp;&nbsp;</td>
+			<td style="width: auto">Package quick implements utility functions to help with black box testing.</td>
+			</tr>
+		
+	
+	</table>
+	
+
+
+
+</div>
+
+<div id="footer">
+Build version go1.0.2.<br>
+Except as <a href="http://code.google.com/policies.html#restrictions">noted</a>,
+the content of this page is licensed under the
+Creative Commons Attribution 3.0 License,
+and code is licensed under a <a href="/LICENSE">BSD license</a>.<br>
+<a href="/doc/tos.html">Terms of Service</a> | 
+<a href="http://www.google.com/intl/en/privacy/privacy-policy.html">Privacy Policy</a>
+</div>
+
+<script type="text/javascript">
+(function() {
+  var ga = document.createElement("script"); ga.type = "text/javascript"; ga.async = true;
+  ga.src = ("https:" == document.location.protocol ? "https://ssl" : "http://www") + ".google-analytics.com/ga.js";
+  var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(ga, s);
+})();
+</script>
+</body>
+<script type="text/javascript">
+  (function() {
+    var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true;
+    po.src = 'https://apis.google.com/js/plusone.js';
+    var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s);
+  })();
+</script>
+</html>
+

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 26 - 0
src/github.com/PuerkitoBio/goquery/testdata/gowiki.html


+ 413 - 0
src/github.com/PuerkitoBio/goquery/testdata/metalreview.html

@@ -0,0 +1,413 @@
+
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" >
+<head><meta http-equiv="X-UA-Compatible" content="IE=8" />
+    
+<meta name="keywords" content="metal, reviews, metalreview, metalreviews, heavy, rock, review, music, blogs, forums, community" />
+<meta name="description" content="Critical heavy metal album and dvd reviews, written by professional writers. Large community with forums, blogs, photos and commenting system." />
+
+<title>
+	
+    
+	Metal Reviews, News, Blogs, Interviews and Community | Metal Review
+
+
+</title><link rel="stylesheet" type="text/css" href="/Content/Css/reset-fonts-grids.css" /><link rel="stylesheet" type="text/css" href="/Content/Css/base.css" /><link rel="stylesheet" type="text/css" href="/Content/Css/core.css" /><link rel="stylesheet" type="text/css" href="/Content/Css/wt-rotator.css" />
+    <script src="/Scripts/jquery-1.4.2.min.js" type="text/javascript"></script>
+    </head>
+<body>
+    <script type="text/javascript">
+        var _comscore = _comscore || [];
+        _comscore.push({ c1: "2", c2: "9290245" });
+        (function () {
+            var s = document.createElement("script"), el = document.getElementsByTagName("script")[0]; s.async = true;
+            s.src = (document.location.protocol == "https:" ? "https://sb" : "http://b") + ".scorecardresearch.com/beacon.js";
+            el.parentNode.insertBefore(s, el);
+        })();
+    </script>
+    <noscript>
+    <img src="http://b.scorecardresearch.com/p?c1=2&c2=9290245&cv=2.0&cj=1" />
+    </noscript>
+
+    
+<div id="doc2" class="yui-t7">
+	<div id="hd">
+		
+
+<div id="main-logo"><a href="/" title="Home"><img src="/Content/Images/metal-review-logo.png" alt="Metal Review Home" border="0" /></a></div>
+<div id="leaderboard-banner">
+
+<script language="javascript" type="text/javascript"><!--
+    document.write('<scr' + 'ipt language="javascript1.1" src="http://adserver.adtechus.com/addyn/3.0/5110/73085/0/225/ADTECH;loc=100;target=_blank;key=key1+key2+key3+key4;grp=[group];misc=' + new Date().getTime() + '"></scri' + 'pt>');
+    //-->
+</script>
+
+<noscript>
+    <a href="http://adserver.adtechus.com/adlink/3.0/5110/73085/0/225/ADTECH;loc=300;key=key1+key2+key3+key4;grp=[group]" target="_blank">
+        <img src="http://adserver.adtechus.com/adserv/3.0/5110/73085/0/225/ADTECH;loc=300;key=key1+key2+key3+key4;grp=[group]" border="0" width="728" height="90" />
+    </a>
+</noscript>
+</div>
+<div id="header-menu-container">
+    <div id="header-menu">
+        <a href="/reviews/browse">REVIEWS</a>
+        <a href="http://community2.metalreview.com/blogs/editorials/default.aspx">FEATURES</a>
+        <a href="/artists/browse">ARTISTS</a>
+        <a href="/reviews/pipeline">PIPELINE</a>
+        <a href="http://community2.metalreview.com/forums">FORUMS</a>
+        <a href="http://community2.metalreview.com/blogs/">BLOGS</a>
+        <a href="/aboutus">ABOUT US</a>
+    </div>
+    
+        <div id="sign-in"><a href="https://metalreview.com/account/signin">SIGN IN</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href="/account/register">JOIN US</a></div>
+    
+</div>
+	</div>
+	<div id="bd">
+        
+		<div id="yui-main">
+			<div class="yui-b">
+				<div class="yui-g">
+					<div class="yui-u first">
+						
+     
+
+<script src="/Scripts/jquery.wt-rotator.min.js" type="text/javascript"></script>
+<script src="/Scripts/jquery.wt-rotator-initialize.js" type="text/javascript"></script>
+<div id="review-showcase-wrapper">
+    <h2 id="showcase-heading">Reviews</h2>
+    <div id="review-showcase">
+        <div class="container">
+            <div class="wt-rotator">
+                <a href="#"></a>
+                <div class="desc">
+                </div>
+                <div class="preloader">
+                </div>
+                <div class="c-panel">
+                    <div class="buttons">
+                        <div class="prev-btn">
+                        </div>
+                        <div class="play-btn">
+                        </div>
+                        <div class="next-btn">
+                        </div>
+                    </div>
+                    <div class="thumbnails">
+                        <ul>
+                              
+                             <li><a href="artist.photo?mrx=4641" title="Serpentine Path - Serpentine Path"></a><a href="/reviews/6844/serpentine-path-serpentine-path"></a>
+                                 <p style="top: 130px; left: 22px; width: 305px; height:60px;">
+                                    <img class="rotator-cover-art" src="album.cover?art=6844" alt='Serpentine Path - Serpentine Path' title='Serpentine Path - Serpentine Path' />
+                                    <span class="title"><strong>Serpentine Path</strong></span><br />
+                                    Serpentine Path<br />
+                                    
+                                </p>
+                            </li>
+                             
+                             <li><a href="artist.photo?mrx=4635" title="Hunter's Ground - No God But the Wild"></a><a href="/reviews/6830/hunters-ground-no-god-but-the-wild"></a>
+                                 <p style="top: 130px; left: 22px; width: 305px; height:60px;">
+                                    <img class="rotator-cover-art" src="album.cover?art=6830" alt='Hunter's Ground - No God But the Wild' title='Hunter's Ground - No God But the Wild' />
+                                    <span class="title"><strong>Hunter's Ground</strong></span><br />
+                                    No God But the Wild<br />
+                                    
+                                </p>
+                            </li>
+                             
+                             <li><a href="artist.photo?mrx=1035" title="Blut Aus Nord - 777 - Cosmosophy"></a><a href="/reviews/6829/blut-aus-nord-777---cosmosophy"></a>
+                                 <p style="top: 130px; left: 22px; width: 305px; height:60px;">
+                                    <img class="rotator-cover-art" src="album.cover?art=6829" alt='Blut Aus Nord - 777 - Cosmosophy' title='Blut Aus Nord - 777 - Cosmosophy' />
+                                    <span class="title"><strong>Blut Aus Nord</strong></span><br />
+                                    777 - Cosmosophy<br />
+                                    <a href="/tags/10/black"><span class="tag">Black</span></a>
+                                </p>
+                            </li>
+                             
+                             <li><a href="artist.photo?mrx=1217" title="Ufomammut - Oro: Opus Alter"></a><a href="/reviews/6835/ufomammut-oro--opus-alter"></a>
+                                 <p style="top: 130px; left: 22px; width: 305px; height:60px;">
+                                    <img class="rotator-cover-art" src="album.cover?art=6835" alt='Ufomammut - Oro: Opus Alter' title='Ufomammut - Oro: Opus Alter' />
+                                    <span class="title"><strong>Ufomammut</strong></span><br />
+                                    Oro: Opus Alter<br />
+                                    <a href="/tags/2/doom"><span class="tag">Doom</span></a>
+                                </p>
+                            </li>
+                             
+                             <li><a href="artist.photo?mrx=4590" title="Resurgency - False Enlightenment"></a><a href="/reviews/6746/resurgency-false-enlightenment"></a>
+                                 <p style="top: 130px; left: 22px; width: 305px; height:60px;">
+                                    <img class="rotator-cover-art" src="album.cover?art=6746" alt='Resurgency - False Enlightenment' title='Resurgency - False Enlightenment' />
+                                    <span class="title"><strong>Resurgency</strong></span><br />
+                                    False Enlightenment<br />
+                                    <a href="/tags/1/death"><span class="tag">Death</span></a>
+                                </p>
+                            </li>
+                             
+                             <li><a href="artist.photo?mrx=1360" title="Morgoth - Cursed to Live"></a><a href="/reviews/6800/morgoth-cursed-to-live"></a>
+                                 <p style="top: 130px; left: 22px; width: 305px; height:60px;">
+                                    <img class="rotator-cover-art" src="album.cover?art=6800" alt='Morgoth - Cursed to Live' title='Morgoth - Cursed to Live' />
+                                    <span class="title"><strong>Morgoth</strong></span><br />
+                                    Cursed to Live<br />
+                                    <a href="/tags/1/death"><span class="tag">Death</span></a><a href="/tags/31/live"><span class="tag">Live</span></a>
+                                </p>
+                            </li>
+                             
+                             <li><a href="artist.photo?mrx=3879" title="Krallice - Years Past Matter"></a><a href="/reviews/6853/krallice-years-past-matter"></a>
+                                 <p style="top: 130px; left: 22px; width: 305px; height:60px;">
+                                    <img class="rotator-cover-art" src="album.cover?art=6853" alt='Krallice - Years Past Matter' title='Krallice - Years Past Matter' />
+                                    <span class="title"><strong>Krallice</strong></span><br />
+                                    Years Past Matter<br />
+                                    <a href="/tags/10/black"><span class="tag">Black</span></a>
+                                </p>
+                            </li>
+                             
+                             <li><a href="artist.photo?mrx=4243" title="Murder Construct - Results"></a><a href="/reviews/6782/murder-construct-results"></a>
+                                 <p style="top: 130px; left: 22px; width: 305px; height:60px;">
+                                    <img class="rotator-cover-art" src="album.cover?art=6782" alt='Murder Construct - Results' title='Murder Construct - Results' />
+                                    <span class="title"><strong>Murder Construct</strong></span><br />
+                                    Results<br />
+                                    <a href="/tags/13/grindcore"><span class="tag">Grindcore</span></a>
+                                </p>
+                            </li>
+                             
+                             <li><a href="artist.photo?mrx=251" title="Grave - Endless Procession of Souls"></a><a href="/reviews/6834/grave-endless-procession-of-souls"></a>
+                                 <p style="top: 130px; left: 22px; width: 305px; height:60px;">
+                                    <img class="rotator-cover-art" src="album.cover?art=6834" alt='Grave - Endless Procession of Souls' title='Grave - Endless Procession of Souls' />
+                                    <span class="title"><strong>Grave</strong></span><br />
+                                    Endless Procession of Souls<br />
+                                    <a href="/tags/1/death"><span class="tag">Death</span></a>
+                                </p>
+                            </li>
+                             
+                             <li><a href="artist.photo?mrx=3508" title="Master - The New Elite"></a><a href="/reviews/6774/master-the-new-elite"></a>
+                                 <p style="top: 130px; left: 22px; width: 305px; height:60px;">
+                                    <img class="rotator-cover-art" src="album.cover?art=6774" alt='Master - The New Elite' title='Master - The New Elite' />
+                                    <span class="title"><strong>Master</strong></span><br />
+                                    The New Elite<br />
+                                    <a href="/tags/1/death"><span class="tag">Death</span></a>
+                                </p>
+                            </li>
+                            
+                        </ul>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+    <div id="showcase-all-artist-albums">
+        <a href="/reviews/6844/serpentine-path-serpentine-path"><img src="album.cover?art=6844" alt="Serpentine Path - Serpentine Path" /></a><a href="/reviews/6830/hunters-ground-no-god-but-the-wild"><img src="album.cover?art=6830" alt="Hunter's Ground - No God But the Wild" /></a><a href="/reviews/6829/blut-aus-nord-777---cosmosophy"><img src="album.cover?art=6829" alt="Blut Aus Nord - 777 - Cosmosophy" /></a><a href="/reviews/6835/ufomammut-oro--opus-alter"><img src="album.cover?art=6835" alt="Ufomammut - Oro: Opus Alter" /></a><a href="/reviews/6746/resurgency-false-enlightenment"><img src="album.cover?art=6746" alt="Resurgency - False Enlightenment" /></a><a href="/reviews/6800/morgoth-cursed-to-live"><img src="album.cover?art=6800" alt="Morgoth - Cursed to Live" /></a><a href="/reviews/6853/krallice-years-past-matter"><img src="album.cover?art=6853" alt="Krallice - Years Past Matter" /></a><a href="/reviews/6782/murder-construct-results"><img src="album.cover?art=6782" alt="Murder Construct - Results" /></a><a href="/reviews/6834/grave-endless-procession-of-souls"><img src="album.cover?art=6834" alt="Grave - Endless Procession of Souls" /></a><a href="/reviews/6774/master-the-new-elite"><img src="album.cover?art=6774" alt="Master - The New Elite" /></a>
+    </div>
+</div>
+					</div>
+					<div class="yui-u">
+						
+    
+
+<div id="feature-feed">
+<h2>Features</h2>
+<div class="feature-feed-line"><a href="http://community2.metalreview.com/blogs/editorials/archive/2012/08/15/corsair-interview.aspx"><span class="feature-link"><strong>Release The SkyKrakken: Corsair Interview</strong></span></a><br /><span class="publish-date">8/15/2012 by JW</span><img align="left" src="http://community2.metalreview.com/cfs-file.ashx/__key/CommunityServer.Components.Avatars/00.00.00.21.25/4TMR3E1CWERK.jpg" alt="JW's Avatar" width="36px" height="40px" border="0" /></div>
+<div class="feature-feed-line"><a href="http://community2.metalreview.com/blogs/editorials/archive/2012/08/09/riffology-kreative-evolution-part-iii.aspx"><span class="feature-link"><strong>Riffology: Kreative Evolution, Part III</strong></span></a><br /><span class="publish-date">8/9/2012 by Achilles</span><img align="left" src="http://community2.metalreview.com/cfs-file.ashx/__key/CommunityServer.Components.Avatars/00.00.00.21.44/4THUGH622I68.jpg" alt="Achilles's Avatar" width="40px" height="39px" border="0" /></div>
+<div class="feature-feed-line"><a href="http://community2.metalreview.com/blogs/editorials/archive/2012/08/02/reverend-s-bazaar-don-t-trend-on-me.aspx"><span class="feature-link"><strong>Reverend's Bazaar - Don't Trend On Me  </strong></span></a><br /><span class="publish-date">8/2/2012 by Reverend Campbell</span><img align="left" src="http://community2.metalreview.com/cfs-file.ashx/__key/CommunityServer.Components.Avatars/00.00.00.21.18/4TM06FD0ND4G.png" alt="Reverend Campbell's Avatar" width="34px" height="40px" border="0" /></div>
+<div class="feature-feed-line"><a href="http://community2.metalreview.com/blogs/editorials/archive/2012/08/01/grand-theft-metal-three-for-free.aspx"><span class="feature-link"><strong>Grand Theft Metal - Free Four All  </strong></span></a><br /><span class="publish-date">8/2/2012 by Dave</span><img align="left" src="http://community2.metalreview.com/cfs-file.ashx/__key/CommunityServer.Components.Avatars/00.00.00.22.16/4TKJCJQ00VFO.jpg" alt="Dave's Avatar" width="33px" height="40px" border="0" /></div>
+<div class="feature-feed-line"><a href="http://community2.metalreview.com/blogs/editorials/archive/2012/07/29/monday-with-moonspell-the-interview.aspx"><span class="feature-link"><strong>A Monday with Moonspell: The Interview</strong></span></a><br /><span class="publish-date">7/29/2012 by raetamacue</span><img align="left" src="http://community2.metalreview.com/cfs-file.ashx/__key/CommunityServer.Components.Avatars/00.00.00.71.26/4TLIHKLUSXF4.jpg" alt="raetamacue's Avatar" width="37px" height="40px" border="0" /></div>
+<div class="feature-feed-line"><a href="http://community2.metalreview.com/blogs/editorials/archive/2012/07/26/riffology-kreative-evolution-part-ii.aspx"><span class="feature-link"><strong>Riffology: Kreative Evolution Part II</strong></span></a><br /><span class="publish-date">7/26/2012 by Achilles</span><img align="left" src="http://community2.metalreview.com/cfs-file.ashx/__key/CommunityServer.Components.Avatars/00.00.00.21.44/4THUGH622I68.jpg" alt="Achilles's Avatar" width="40px" height="39px" border="0" /></div>
+<div class="feature-feed-line"><a href="http://community2.metalreview.com/blogs/editorials/archive/2012/07/24/shadow-kingdom-records-giveaway.aspx"><span class="feature-link"><strong>WINNERS ANNOUNCED -- Shadow Kingdom Records Give...</strong></span></a><br /><span class="publish-date">7/24/2012 by Metal Review</span><img align="left" src="http://community2.metalreview.com/cfs-file.ashx/__key/CommunityServer.Components.Avatars/00.00.00.59.06/4TFD2N58B7BS.png" alt="Metal Review's Avatar" width="34px" height="40px" border="0" /></div>
+
+<br />
+<a href="http://community2.metalreview.com/blogs/editorials/default.aspx"><strong>More Editorials</strong></a>
+</div>
+
+
+
+					</div>
+				</div>
+			</div>
+		</div>
+		<div class="yui-b">
+			
+    
+
+<script src="/Scripts/jquery.cycle.all.min.js" type="text/javascript"></script>
+<div id="slider-next-button"><img id="slider-next" src="/Content/Images/Backgrounds/rotator-next-button.png" alt="Goto Next Group" title="Goto Next Group" /></div>
+<div id="slider-back-button"><img id="slider-back" src="/Content/Images/Backgrounds/rotator-back-button.png" alt="Goto Previous Group" title="Goto Previous Group" /></div>
+<div id="latest-reviews-slider">
+<div class="slider-row">
+<div class="slider-item"><a href="/reviews/6795/midnight-complete-and-total-hell"><img src="album.cover?art=6795" alt="Midnight Complete and Total Hell" /><br /><strong>Midnight</strong><br /><em>Complete and Total Hell</em></a><div class="score">8.5</div></div>
+<div class="slider-item"><a href="/reviews/6842/over-your-threshold-facticity"><img src="album.cover?art=6842" alt="Over Your Threshold Facticity" /><br /><strong>Over Your Threshold</strong><br /><em>Facticity</em></a><div class="score">6.0</div></div>
+<div class="slider-item"><a href="/reviews/6813/nuclear-death-terror-chaos-reigns"><img src="album.cover?art=6813" alt="Nuclear Death Terror Chaos Reigns" /><br /><strong>Nuclear Death Terror</strong><br /><em>Chaos Reigns</em></a><div class="score">7.5</div></div>
+<div class="slider-item"><a href="/reviews/6811/evoken-atra-mors"><img src="album.cover?art=6811" alt="Evoken Atra Mors" /><br /><strong>Evoken</strong><br /><em>Atra Mors</em></a><div class="score">9.5</div></div>
+<div class="slider-item"><a href="/reviews/6807/blacklodge-machination"><img src="album.cover?art=6807" alt="Blacklodge MachinatioN" /><br /><strong>Blacklodge</strong><br /><em>MachinatioN</em></a><div class="score">5.5</div></div>
+<div class="slider-item"><a href="/reviews/6832/prototype-catalyst"><img src="album.cover?art=6832" alt="Prototype Catalyst" /><br /><strong>Prototype</strong><br /><em>Catalyst</em></a><div class="score">8.0</div></div>
+<div class="slider-item"><a href="/reviews/6822/hypnosia-horror-infernal"><img src="album.cover?art=6822" alt="Hypnosia Horror Infernal" /><br /><strong>Hypnosia</strong><br /><em>Horror Infernal</em></a><div class="score">7.0</div></div>
+<div class="slider-item"><a href="/reviews/6787/om-advaitic-songs"><img src="album.cover?art=6787" alt="OM Advaitic Songs" /><br /><strong>OM</strong><br /><em>Advaitic Songs</em></a><div class="score">8.0</div></div>
+<div class="slider-item"><a href="/reviews/6765/afgrund-the-age-of-dumb"><img src="album.cover?art=6765" alt="Afgrund The Age Of Dumb" /><br /><strong>Afgrund</strong><br /><em>The Age Of Dumb</em></a><div class="score">8.5</div></div>
+<div class="slider-item"><a href="/reviews/6773/binah-hallucinating-in-resurrecture"><img src="album.cover?art=6773" alt="Binah Hallucinating in Resurrecture" /><br /><strong>Binah</strong><br /><em>Hallucinating in Resurrecture</em></a><div class="score">8.5</div></div>
+</div>
+<div class="slider-row">
+<div class="slider-item"><a href="/reviews/6802/deiphago-satan-alpha-omega"><img src="album.cover?art=6802" alt="Deiphago Satan Alpha Omega" /><br /><strong>Deiphago</strong><br /><em>Satan Alpha Omega</em></a><div class="score">8.0</div></div>
+<div class="slider-item"><a href="/reviews/6719/conan-monnos"><img src="album.cover?art=6719" alt="Conan Monnos" /><br /><strong>Conan</strong><br /><em>Monnos</em></a><div class="score">8.0</div></div>
+<div class="slider-item"><a href="/reviews/6702/alaric-alaric-atriarch---split-lp"><img src="album.cover?art=6702" alt="Alaric Alaric/Atriarch - Split LP" /><br /><strong>Alaric</strong><br /><em>Alaric/Atriarch - Split LP</em></a><div class="score">8.5</div></div>
+<div class="slider-item"><a href="/reviews/6780/coven-worship-new-gods-(reissue)"><img src="album.cover?art=6780" alt="Coven Worship New Gods (Reissue)" /><br /><strong>Coven</strong><br /><em>Worship New Gods (Reissue)</em></a><div class="score">5.0</div></div>
+<div class="slider-item"><a href="/reviews/6831/the-foreshadowing-second-world"><img src="album.cover?art=6831" alt="The Foreshadowing Second World" /><br /><strong>The Foreshadowing</strong><br /><em>Second World</em></a><div class="score">5.5</div></div>
+<div class="slider-item"><a href="/reviews/6815/nether-regions-into-the-breach"><img src="album.cover?art=6815" alt="Nether Regions Into The Breach" /><br /><strong>Nether Regions</strong><br /><em>Into The Breach</em></a><div class="score">7.0</div></div>
+<div class="slider-item"><a href="/reviews/6824/agalloch-faustian-echoes"><img src="album.cover?art=6824" alt="Agalloch Faustian Echoes" /><br /><strong>Agalloch</strong><br /><em>Faustian Echoes</em></a><div class="score">9.0</div></div>
+<div class="slider-item"><a href="/reviews/6805/a-forest-of-stars-a-shadowplay-for-yesterdays"><img src="album.cover?art=6805" alt="A Forest Of Stars A Shadowplay For Yesterdays" /><br /><strong>A Forest Of Stars</strong><br /><em>A Shadowplay For Yesterdays</em></a><div class="score">9.0</div></div>
+<div class="slider-item"><a href="/reviews/6763/de-profundis-the-emptiness-within"><img src="album.cover?art=6763" alt="De Profundis The Emptiness Within" /><br /><strong>De Profundis</strong><br /><em>The Emptiness Within</em></a><div class="score">7.5</div></div>
+<div class="slider-item"><a href="/reviews/6826/ozzy-osbourne-speak-of-the-devil"><img src="album.cover?art=6826" alt="Ozzy Osbourne Speak of the Devil" /><br /><strong>Ozzy Osbourne</strong><br /><em>Speak of the Devil</em></a><div class="score">7.5</div></div>
+</div>
+<div class="slider-row">
+<div class="slider-item"><a href="/reviews/6825/testament-dark-roots-of-earth"><img src="album.cover?art=6825" alt="Testament Dark Roots of Earth" /><br /><strong>Testament</strong><br /><em>Dark Roots of Earth</em></a><div class="score">8.0</div></div>
+<div class="slider-item"><a href="/reviews/6796/eagle-twin-the-feather-tipped-the-serpents-scale"><img src="album.cover?art=6796" alt="Eagle Twin The Feather Tipped The Serpent's Scale" /><br /><strong>Eagle Twin</strong><br /><em>The Feather Tipped The Serpent's Scale</em></a><div class="score">8.5</div></div>
+<div class="slider-item"><a href="/reviews/6609/king-forged-by-satans-doctrine"><img src="album.cover?art=6609" alt="King Forged by Satan's Doctrine" /><br /><strong>King</strong><br /><em>Forged by Satan's Doctrine</em></a><div class="score">5.5</div></div>
+<div class="slider-item"><a href="/reviews/6798/khors-wisdom-of-centuries"><img src="album.cover?art=6798" alt="Khors Wisdom of Centuries" /><br /><strong>Khors</strong><br /><em>Wisdom of Centuries</em></a><div class="score">8.5</div></div>
+<div class="slider-item"><a href="/reviews/6776/samothrace-reverence-to-stone"><img src="album.cover?art=6776" alt="Samothrace Reverence To Stone" /><br /><strong>Samothrace</strong><br /><em>Reverence To Stone</em></a><div class="score">8.0</div></div>
+<div class="slider-item"><a href="/reviews/6784/horseback-on-the-eclipse"><img src="album.cover?art=6784" alt="Horseback On the Eclipse" /><br /><strong>Horseback</strong><br /><em>On the Eclipse</em></a><div class="score">8.0</div></div>
+<div class="slider-item"><a href="/reviews/6690/incoming-cerebral-overdrive-le-stelle--a-voyage-adrift"><img src="album.cover?art=6690" alt="Incoming Cerebral Overdrive Le Stelle: A Voyage Adrift" /><br /><strong>Incoming Cerebral Overdrive</strong><br /><em>Le Stelle: A Voyage Adrift</em></a><div class="score">7.5</div></div>
+<div class="slider-item"><a href="/reviews/6658/struck-by-lightning-true-predation"><img src="album.cover?art=6658" alt="Struck By Lightning True Predation" /><br /><strong>Struck By Lightning</strong><br /><em>True Predation</em></a><div class="score">7.0</div></div>
+<div class="slider-item"><a href="/reviews/6772/offending-age-of-perversion"><img src="album.cover?art=6772" alt="Offending Age of Perversion" /><br /><strong>Offending</strong><br /><em>Age of Perversion</em></a><div class="score">7.5</div></div>
+<div class="slider-item"><a href="/reviews/6804/king-of-asgard----to-north"><img src="album.cover?art=6804" alt="King Of Asgard ...to North" /><br /><strong>King Of Asgard</strong><br /><em>...to North</em></a><div class="score">7.5</div></div>
+</div>
+<div class="slider-row">
+<div class="slider-item"><a href="/reviews/6783/burning-love-rotten-thing-to-say"><img src="album.cover?art=6783" alt="Burning Love Rotten Thing to Say" /><br /><strong>Burning Love</strong><br /><em>Rotten Thing to Say</em></a><div class="score">7.0</div></div>
+<div class="slider-item"><a href="/reviews/6770/high-on-fire-the-art-of-self-defense-(reissue)"><img src="album.cover?art=6770" alt="High On Fire The Art Of Self Defense (Reissue)" /><br /><strong>High On Fire</strong><br /><em>The Art Of Self Defense (Reissue)</em></a><div class="score">7.5</div></div>
+<div class="slider-item"><a href="/reviews/6660/horseback-half-blood"><img src="album.cover?art=6660" alt="Horseback Half Blood" /><br /><strong>Horseback</strong><br /><em>Half Blood</em></a><div class="score">6.5</div></div>
+<div class="slider-item"><a href="/reviews/6732/aldebaran-embracing-the-lightless-depths"><img src="album.cover?art=6732" alt="Aldebaran Embracing the Lightless Depths" /><br /><strong>Aldebaran</strong><br /><em>Embracing the Lightless Depths</em></a><div class="score">8.0</div></div>
+<div class="slider-item"><a href="/reviews/6778/tank-war-nation"><img src="album.cover?art=6778" alt="Tank War Nation" /><br /><strong>Tank</strong><br /><em>War Nation</em></a><div class="score">6.5</div></div>
+<div class="slider-item"><a href="/reviews/6793/satanic-bloodspraying-at-the-mercy-of-satan"><img src="album.cover?art=6793" alt="Satanic Bloodspraying At the Mercy of Satan" /><br /><strong>Satanic Bloodspraying</strong><br /><em>At the Mercy of Satan</em></a><div class="score">8.5</div></div>
+<div class="slider-item"><a href="/reviews/6791/from-ashes-rise-rejoice-the-end---rage-of-sanity"><img src="album.cover?art=6791" alt="From Ashes Rise Rejoice The End / Rage Of Sanity" /><br /><strong>From Ashes Rise</strong><br /><em>Rejoice The End / Rage Of Sanity</em></a><div class="score">8.0</div></div>
+<div class="slider-item"><a href="/reviews/6743/ereb-altor-gastrike"><img src="album.cover?art=6743" alt="Ereb Altor Gastrike" /><br /><strong>Ereb Altor</strong><br /><em>Gastrike</em></a><div class="score">8.0</div></div>
+<div class="slider-item"><a href="/reviews/6794/catheter-southwest-doom-violence"><img src="album.cover?art=6794" alt="Catheter Southwest Doom Violence" /><br /><strong>Catheter</strong><br /><em>Southwest Doom Violence</em></a><div class="score">7.0</div></div>
+<div class="slider-item"><a href="/reviews/6759/power-theory-an-axe-to-grind"><img src="album.cover?art=6759" alt="Power Theory An Axe to Grind" /><br /><strong>Power Theory</strong><br /><em>An Axe to Grind</em></a><div class="score">6.0</div></div>
+</div>
+
+ </div>
+  
+
+ <script type="text/javascript">
+     $(document).ready(function () {
+         $('#latest-reviews-slider').cycle({
+             fx: 'scrollRight',
+             speed: 'fast',
+             timeout: 0,
+             next: '#slider-next-button',
+             prev: '#slider-back-button'
+         });
+     });
+</script>
+
+<div id="homepage-mid-horizontal-zone">
+    <script language="javascript" type="text/javascript" src="http://metalreview.com/bannermgr/abm.aspx?z=1"></script>
+</div>
+
+
+
+
+<div id="news-feed">
+<h2>News</h2><div class="news-feed-line"><a href="http://www.bravewords.com/news/190057" target="_blank"><span class="news-link"><strong><span class="new-news">NEW</span> CENTURIAN To Release Contra Rationem Album This Winter</strong></span></a><br /><span class="publish-date">9/12/2012</span></div><br />
+<div class="news-feed-line"><a href="http://www.bravewords.com/news/190056" target="_blank"><span class="news-link"><strong><span class="new-news">NEW</span> Southwest Terror Fest 2012 - Lineup Changes Announced</strong></span></a><br /><span class="publish-date">9/12/2012</span></div><br />
+<div class="news-feed-line"><a href="http://www.bravewords.com/news/190055" target="_blank"><span class="news-link"><strong><span class="new-news">NEW</span> ROB ZOMBIE Premiers The Lords Of Salem At TIFF; Q&A Video Posted</strong></span></a><br /><span class="publish-date">9/12/2012</span></div><br />
+<div class="news-feed-line"><a href="http://www.bravewords.com/news/190054" target="_blank"><span class="news-link"><strong><span class="new-news">NEW</span> THIN LIZZY Keyboardist Darren Wharton's DARE - Calm Before The Storm 2 Album Details Revealed</strong></span></a><br /><span class="publish-date">9/12/2012</span></div><br />
+<div class="news-feed-line"><a href="http://www.bravewords.com/news/190053" target="_blank"><span class="news-link"><strong><span class="new-news">NEW</span> Japan's LIV MOON To Release Fourth Album; Features Past/Present Members Of EUROPE, ANGRA, HAMMERFALL</strong></span></a><br /><span class="publish-date">9/12/2012</span></div><br />
+<div class="news-feed-line"><a href="http://www.bravewords.com/news/190052" target="_blank"><span class="news-link"><strong><span class="new-news">NEW</span> SLASH - Sydney Show To Premier This Friday, Free And In HD; Trailer Posted</strong></span></a><br /><span class="publish-date">9/12/2012</span></div><br />
+<div class="news-feed-line"><a href="http://www.bravewords.com/news/190051" target="_blank"><span class="news-link"><strong><span class="new-news">NEW</span> KHAØS - New Band Featuring Members Of OUTLOUD, TRIBAL, JORN And ELIS To Release New EP In October; Teaser Posted </strong></span></a><br /><span class="publish-date">9/12/2012</span></div><br />
+<div class="news-feed-line"><a href="http://www.bravewords.com/news/190050" target="_blank"><span class="news-link"><strong><span class="new-news">NEW</span> RECKLESS LOVE Confirm Guests For London Residency Shows In October</strong></span></a><br /><span class="publish-date">9/12/2012</span></div><br />
+<div class="news-feed-line"><a href="http://www.bravewords.com/news/190049" target="_blank"><span class="news-link"><strong>NASHVILLE PUSSY Add Dates In France, Sweden To European Tour Schedule; Bassist Karen Cuda Sidelined With Back Injury	</strong></span></a><br /><span class="publish-date">9/12/2012</span></div><br />
+<div class="news-feed-line"><a href="http://www.bravewords.com/news/190048" target="_blank"><span class="news-link"><strong>CALIBAN Post Behind-The-Scenes Tour Footage</strong></span></a><br /><span class="publish-date">9/12/2012</span></div><br />
+<div class="news-feed-line"><a href="http://www.bravewords.com/news/190047" target="_blank"><span class="news-link"><strong>Ex-MERCYFUL FATE Drummer Kim Ruzz Forms New Band METALRUZZ</strong></span></a><br /><span class="publish-date">9/12/2012</span></div><br />
+<div class="news-feed-line"><a href="http://www.bravewords.com/news/190046" target="_blank"><span class="news-link"><strong>GRAVE Mainman On Endless Procession Of Souls - "These Are The Most ‘Song-Oriented’ Tracks We’ve Done In A Long Time"</strong></span></a><br /><span class="publish-date">9/12/2012</span></div><br />
+
+</div>
+
+
+<div id="lashes-feed">
+<h2>Lashes</h2>
+<div class="lashes-feed-line"><a href="/reviews/6855/katatonia-dead-end-kings#81760"><span class="new-lash">NEW</span> <span class="lashes-link"><strong>Katatonia - Dead End Kings</strong></span></a><br /><span class="publish-date">45 minutes ago by Chaosjunkie</span></div>
+<div class="lashes-feed-line"><a href="/reviews/6855/katatonia-dead-end-kings#81759"><span class="lashes-link"><strong>Katatonia - Dead End Kings</strong></span></a><br /><span class="publish-date">1 hour ago by Harry Dick Rotten</span></div>
+<div class="lashes-feed-line"><a href="/reviews/6746/resurgency-false-enlightenment#81758"><span class="lashes-link"><strong>Resurgency - False Enlightenment</strong></span></a><br /><span class="publish-date">3 hours ago by Anonymous</span></div>
+<div class="lashes-feed-line"><a href="/reviews/4095/witchcraft-the-alchemist#81757"><span class="lashes-link"><strong>Witchcraft - The Alchemist</strong></span></a><br /><span class="publish-date">5 hours ago by Luke_22</span></div>
+<div class="lashes-feed-line"><a href="/reviews/6855/katatonia-dead-end-kings#81756"><span class="lashes-link"><strong>Katatonia - Dead End Kings</strong></span></a><br /><span class="publish-date">9 hours ago by chaosjunkie</span></div>
+<div class="lashes-feed-line"><a href="/reviews/6855/katatonia-dead-end-kings#81755"><span class="lashes-link"><strong>Katatonia - Dead End Kings</strong></span></a><br /><span class="publish-date">10 hours ago by Compeller</span></div>
+<div class="lashes-feed-line"><a href="/reviews/6827/manetheren-time#81754"><span class="lashes-link"><strong>Manetheren - Time</strong></span></a><br /><span class="publish-date">10 hours ago by xpmule</span></div>
+<div class="lashes-feed-line"><a href="/reviews/6835/ufomammut-oro--opus-alter#81753"><span class="lashes-link"><strong>Ufomammut - Oro: Opus Alter</strong></span></a><br /><span class="publish-date">16 hours ago by Anonymous</span></div>
+<div class="lashes-feed-line"><a href="/reviews/6835/ufomammut-oro--opus-alter#81752"><span class="lashes-link"><strong>Ufomammut - Oro: Opus Alter</strong></span></a><br /><span class="publish-date">17 hours ago by Harry Dick Rotten</span></div>
+<div class="lashes-feed-line"><a href="/reviews/6855/katatonia-dead-end-kings#81751"><span class="lashes-link"><strong>Katatonia - Dead End Kings</strong></span></a><br /><span class="publish-date">yesterday by Chaosjunkie</span></div>
+<div class="lashes-feed-line"><a href="/reviews/6855/katatonia-dead-end-kings#81750"><span class="lashes-link"><strong>Katatonia - Dead End Kings</strong></span></a><br /><span class="publish-date">yesterday by Anonymous</span></div>
+<div class="lashes-feed-line"><a href="/reviews/6855/katatonia-dead-end-kings#81749"><span class="lashes-link"><strong>Katatonia - Dead End Kings</strong></span></a><br /><span class="publish-date">yesterday by Anonymous</span></div>
+<div class="lashes-feed-line"><a href="/reviews/6855/katatonia-dead-end-kings#81748"><span class="lashes-link"><strong>Katatonia - Dead End Kings</strong></span></a><br /><span class="publish-date">yesterday by Anonymous</span></div>
+<div class="lashes-feed-line"><a href="/reviews/6855/katatonia-dead-end-kings#81747"><span class="lashes-link"><strong>Katatonia - Dead End Kings</strong></span></a><br /><span class="publish-date">yesterday by frantic</span></div>
+<div class="lashes-feed-line"><a href="/reviews/6829/blut-aus-nord-777---cosmosophy#81746"><span class="lashes-link"><strong>Blut Aus Nord - 777 - Cosmosophy</strong></span></a><br /><span class="publish-date">yesterday by Dimensional Bleedthrough</span></div>
+
+ </div>
+
+		</div>
+	</div>
+	<div id="ft">
+		
+
+<div id="template-footer">
+    <div class="left-column">
+        <ul>
+            <li><a href="/">Home</a></li>
+            <li><a href="/reviews/browse">Reviews</a></li>
+            <li><a href="/tags">Genre Tags</a></li>
+            <li><a href="http://community2.metalreview.com/blogs/editorials/default.aspx">Features</a></li>
+            <li><a href="/artists/browse">Artists</a></li>
+            <li><a href="/reviews/pipeline">Pipeline</a></li>
+            <li><a href="http://community2.metalreview.com/forums">Forums</a></li>
+            <li><a href="/aboutus">About Us</a></li>
+        </ul>
+    </div>
+    <div class="middle-column">
+        <ul>
+            <li><a href="/aboutus/disclaimer">Disclaimer</a></li>
+            <li><a href="/aboutus/privacypolicy">Privacy Policy</a></li>
+            <li><a href="/aboutus/advertising">Advertising</a></li>
+            <li><a href="http://community2.metalreview.com/blogs/eminor/archive/2008/10/27/write-for-metal-review.aspx">Write For Us</a></li>
+            <li><a href="/contactus">Contact Us</a></li>
+            <li><a href="/contactus">Digital Promos</a></li>
+            <li><a href="/contactus">Mailing Address</a></li>
+        </ul>
+    </div>
+    <div class="right-column">
+        <ul>
+            <li><a href="http://feeds.feedburner.com/metalreviews">Reviews RSS Feed</a></li>
+            <li><a href="http://twitter.com/metalreview">Twitter</a></li>
+            <li><a href="http://www.myspace.com/metalreviewdotcom">MySpace</a></li>
+            <li><a href="http://www.last.fm/group/MetalReview.com">Last.fm</a></li>
+            <li><a href="http://www.facebook.com/pages/MetalReviewcom/48371319443">Facebook</a></li>
+        </ul>
+    </div>
+    <div class="square-ad">
+        
+
+<!--JavaScript Tag // Tag for network 5110: Fixion Media // Website: Metalreview // Page: ROS // Placement: ROS-Middle-300 x 250 (1127996) // created at: Oct 19, 2009 6:48:27 PM-->
+<script type="text/javascript" language="javascript"><!--
+    document.write('<scr' + 'ipt language="javascript1.1" src="http://adserver.adtechus.com/addyn/3.0/5110/1127996/0/170/ADTECH;loc=100;target=_blank;key=key1+key2+key3+key4;grp=[group];misc=' + new Date().getTime() + '"></scri' + 'pt>');
+//-->
+</script><noscript><a href="http://adserver.adtechus.com/adlink/3.0/5110/1127996/0/170/ADTECH;loc=300;key=key1+key2+key3+key4;grp=[group]" target="_blank"><img src="http://adserver.adtechus.com/adserv/3.0/5110/1127996/0/170/ADTECH;loc=300;key=key1+key2+key3+key4;grp=[group]" border="0" width="300" height="250"></a></noscript>
+<!-- End of JavaScript Tag -->
+    </div>
+</div>
+	</div>
+</div>
+
+    <script type="text/javascript">
+        var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
+        document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
+    </script>
+
+    <script type="text/javascript">
+        var pageTracker = _gat._getTracker("UA-3455310-1");
+        pageTracker._initData();
+        pageTracker._trackPageview();
+    </script>
+
+    <!--JavaScript Tag // Tag for network 5110: Fixion Media // Website: Metalreview // Page: BACKGROUND ADS // Placement: BACKGROUND ADS-Top-1 x 1 (2186116) // created at: Aug 18, 2011 7:20:38 PM-->
+    <script language="javascript"><!--
+        document.write('<scr' + 'ipt language="javascript1.1" src="http://adserver.adtechus.com/addyn/3.0/5110/2186116/0/16/ADTECH;loc=100;target=_blank;key=key1+key2+key3+key4;grp=[group];misc=' + new Date().getTime() + '"></scri' + 'pt>');
+    //-->
+    </script><noscript><a href="http://adserver.adtechus.com/adlink/3.0/5110/2186116/0/16/ADTECH;loc=300;key=key1+key2+key3+key4;grp=[group]" target="_blank"><img src="http://adserver.adtechus.com/adserv/3.0/5110/2186116/0/16/ADTECH;loc=300;key=key1+key2+key3+key4;grp=[group]" border="0" width="1" height="1"></a></noscript>
+    <!-- End of JavaScript Tag -->
+    
+</body>
+</html>

+ 102 - 0
src/github.com/PuerkitoBio/goquery/testdata/page.html

@@ -0,0 +1,102 @@
+<!DOCTYPE html>
+<html lang="en" ng-app="app">
+    <head>
+        <meta charset="utf-8">
+        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+        <title>
+            Provok.in
+        </title>
+        <meta name="viewport" content="width=device-width, initial-scale=1.0">
+        <meta name="description" content="Provok.in - Prove your point. State an affirmation, back it up with evidence, unveil the truth.">
+        <meta name="author" content="Martin Angers">
+        <link href="http://fonts.googleapis.com/css?family=Belgrano" rel="stylesheet" type="text/css">
+        <!--[if lt IE 9]><link href="http://fonts.googleapis.com/css?family=Belgrano" rel="stylesheet" type="text/css"><link href="http://fonts.googleapis.com/css?family=Belgrano:400italic" rel="stylesheet" type="text/css"><link href="http://fonts.googleapis.com/css?family=Belgrano:700" rel="stylesheet" type="text/css"><link href="http://fonts.googleapis.com/css?family=Belgrano:700italic" rel="stylesheet" type="text/css"><![endif]-->
+        <link href="/css/pvk.min.css" rel="stylesheet" type="text/css">
+    </head>
+    <body>
+        <div class="container-fluid" id="cf1">
+            <div class="row-fluid">
+                <div class="pvk-gutter">
+                    &nbsp;
+                </div>
+                <div class="pvk-content" id="pc1">
+                    <div ng-controller="HeroCtrl" class="hero-unit">
+                        <div class="container-fluid" id="cf2">
+                            <div class="row-fluid" id="cf2-1">
+                                <div class="span12">
+                                    <h1>
+                                        <a href="/">Provok<span class="green">.</span><span class="red">i</span>n</a>
+                                    </h1>
+                                    <p>
+                                        Prove your point.
+                                    </p>
+                                </div>
+                            </div>
+                            <div class="row-fluid" id="cf2-2">
+                                <div class="span12 alert alert-error">
+                                    <strong>Beta Version.</strong> Things may change. Or disappear. Or fail miserably. If it's the latter, <a href="https://github.com/PuerkitoBio/Provok.in-issues" target="_blank" class="link">please file an issue.</a>
+                                </div>
+                            </div>
+                            <div ng-cloak="" ng-show="isLoggedOut() &amp;&amp; !hideLogin" class="row-fluid" id="cf2-3">
+                                <a ng-href="{{ROUTES.login}}" class="btn btn-primary">Sign in. Painless.</a> <span>or</span> <a ng-href="{{ROUTES.help}}" class="link">learn more about provok.in.</a>
+                            </div>
+                            <div ng-cloak="" ng-show="isLoggedIn()" class="row-fluid logged-in-state" id="cf2-4">
+                                <span>Welcome,</span> <a ng-href="{{ROUTES.profile}}" class="link">{{getUserName()}}</a> <span>(</span> <a ng-click="doLogout($event)" class="link">logout</a> <span>)</span>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+                <div class="pvk-gutter">
+                    &nbsp;
+                </div>
+            </div>
+            <div class="row-fluid">
+                <div class="pvk-gutter">
+                    &nbsp;
+                </div>
+                <div class="pvk-content" id="pc2">
+                    <div class="container-fluid" id="cf3">
+                        <div class="row-fluid">
+                            <div ng-cloak="" view-on-display="" ng-controller="MsgCtrl" ng-class="{'displayed': blockIsDisplayed}" class="message-box">
+                                <div ng-class="{'alert-info': isInfo, 'alert-error': !isInfo, 'displayed': isDisplayed}" class="alert">
+                                    <a ng-click="hideMessage(true, $event)" class="close">×</a>
+                                    <h4 class="alert-heading">
+                                        {{ title }}
+                                    </h4>
+                                    <p>
+                                        {{ message }}
+                                    </p>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                    <div class="container-fluid" id="cf4">
+                        <div ng-controller="ShareCtrl" ng-hide="isHidden" class="row-fluid center-content"></div>
+                    </div>
+                    <div ng-view=""></div>
+                </div>
+                <div class="pvk-gutter">
+                    &nbsp;
+                </div>
+            </div>
+            <div class="row-fluid">
+                <div class="pvk-gutter">
+                    &nbsp;
+                </div>
+                <div class="pvk-content">
+                    <div class="footer">
+                        <p>
+                            <a href="/" class="link">Home</a> <span>|</span> <a href="/about" class="link">About</a> <span>|</span> <a href="/help" class="link">Help</a>
+                        </p>
+                        <p>
+                            <small>© 2012 Martin Angers</small>
+                        </p>
+                    </div>
+                </div>
+                <div class="pvk-gutter">
+                    &nbsp;
+                </div>
+            </div>
+        </div>
+    </body>
+</html>

+ 24 - 0
src/github.com/PuerkitoBio/goquery/testdata/page2.html

@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>Tests for siblings</title>
+  </head>
+  <BODY>
+    <div id="main">
+      <div id="n1" class="one even row"></div>
+      <div id="n2" class="two odd row"></div>
+      <div id="n3" class="three even row"></div>
+      <div id="n4" class="four odd row"></div>
+      <div id="n5" class="five even row"></div>
+      <div id="n6" class="six odd row"></div>
+    </div>
+    <div id="foot">
+      <div id="nf1" class="one even row"></div>
+      <div id="nf2" class="two odd row"></div>
+      <div id="nf3" class="three even row"></div>
+      <div id="nf4" class="four odd row"></div>
+      <div id="nf5" class="five even row odder"></div>
+      <div id="nf6" class="six odd row"></div>
+    </div>
+  </BODY>
+</html>

+ 24 - 0
src/github.com/PuerkitoBio/goquery/testdata/page3.html

@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>Tests for siblings</title>
+  </head>
+  <BODY>
+    <div id="main">
+      <div id="n1" class="one even row">hello</div>
+      <div id="n2" class="two odd row"></div>
+      <div id="n3" class="three even row"></div>
+      <div id="n4" class="four odd row"></div>
+      <div id="n5" class="five even row"></div>
+      <div id="n6" class="six odd row"></div>
+    </div>
+    <div id="foot">
+      <div id="nf1" class="one even row">text</div>
+      <div id="nf2" class="two odd row"></div>
+      <div id="nf3" class="three even row"></div>
+      <div id="nf4" class="four odd row"></div>
+      <div id="nf5" class="five even row odder"></div>
+      <div id="nf6" class="six odd row"></div>
+    </div>
+  </BODY>
+</html>

+ 31 - 29
src/github.com/PuerkitoBio/goquery/traversal.go

@@ -1,9 +1,6 @@
 package goquery
 
-import (
-	"github.com/andybalholm/cascadia"
-	"golang.org/x/net/html"
-)
+import "golang.org/x/net/html"
 
 type siblingType int
 
@@ -24,7 +21,7 @@ const (
 // elements, filtered by a selector. It returns a new Selection object
 // containing these matched elements.
 func (s *Selection) Find(selector string) *Selection {
-	return pushStack(s, findWithMatcher(s.Nodes, cascadia.MustCompile(selector)))
+	return pushStack(s, findWithMatcher(s.Nodes, compileMatcher(selector)))
 }
 
 // FindMatcher gets the descendants of each element in the current set of matched
@@ -93,7 +90,7 @@ func (s *Selection) Children() *Selection {
 // filtered by the specified selector. It returns a new
 // Selection object containing these elements.
 func (s *Selection) ChildrenFiltered(selector string) *Selection {
-	return filterAndPush(s, getChildrenNodes(s.Nodes, siblingAll), cascadia.MustCompile(selector))
+	return filterAndPush(s, getChildrenNodes(s.Nodes, siblingAll), compileMatcher(selector))
 }
 
 // ChildrenMatcher gets the child elements of each element in the Selection,
@@ -112,7 +109,7 @@ func (s *Selection) Parent() *Selection {
 // ParentFiltered gets the parent of each element in the Selection filtered by a
 // selector. It returns a new Selection object containing the matched elements.
 func (s *Selection) ParentFiltered(selector string) *Selection {
-	return filterAndPush(s, getParentNodes(s.Nodes), cascadia.MustCompile(selector))
+	return filterAndPush(s, getParentNodes(s.Nodes), compileMatcher(selector))
 }
 
 // ParentMatcher gets the parent of each element in the Selection filtered by a
@@ -124,7 +121,7 @@ func (s *Selection) ParentMatcher(m Matcher) *Selection {
 // Closest gets the first element that matches the selector by testing the
 // element itself and traversing up through its ancestors in the DOM tree.
 func (s *Selection) Closest(selector string) *Selection {
-	cs := cascadia.MustCompile(selector)
+	cs := compileMatcher(selector)
 	return s.ClosestMatcher(cs)
 }
 
@@ -146,11 +143,15 @@ func (s *Selection) ClosestMatcher(m Matcher) *Selection {
 // ClosestNodes gets the first element that matches one of the nodes by testing the
 // element itself and traversing up through its ancestors in the DOM tree.
 func (s *Selection) ClosestNodes(nodes ...*html.Node) *Selection {
+	set := make(map[*html.Node]bool)
+	for _, n := range nodes {
+		set[n] = true
+	}
 	return pushStack(s, mapNodes(s.Nodes, func(i int, n *html.Node) []*html.Node {
 		// For each node in the selection, test the node itself, then each parent
 		// until a match is found.
 		for ; n != nil; n = n.Parent {
-			if isInSlice(nodes, n) {
+			if set[n] {
 				return []*html.Node{n}
 			}
 		}
@@ -177,7 +178,7 @@ func (s *Selection) Parents() *Selection {
 // ParentsFiltered gets the ancestors of each element in the current
 // Selection. It returns a new Selection object with the matched elements.
 func (s *Selection) ParentsFiltered(selector string) *Selection {
-	return filterAndPush(s, getParentsNodes(s.Nodes, nil, nil), cascadia.MustCompile(selector))
+	return filterAndPush(s, getParentsNodes(s.Nodes, nil, nil), compileMatcher(selector))
 }
 
 // ParentsMatcher gets the ancestors of each element in the current
@@ -190,7 +191,7 @@ func (s *Selection) ParentsMatcher(m Matcher) *Selection {
 // not including the element matched by the selector. It returns a new Selection
 // object containing the matched elements.
 func (s *Selection) ParentsUntil(selector string) *Selection {
-	return pushStack(s, getParentsNodes(s.Nodes, cascadia.MustCompile(selector), nil))
+	return pushStack(s, getParentsNodes(s.Nodes, compileMatcher(selector), nil))
 }
 
 // ParentsUntilMatcher gets the ancestors of each element in the Selection, up to but
@@ -221,7 +222,7 @@ func (s *Selection) ParentsUntilNodes(nodes ...*html.Node) *Selection {
 // results based on a selector string. It returns a new Selection
 // object containing the matched elements.
 func (s *Selection) ParentsFilteredUntil(filterSelector, untilSelector string) *Selection {
-	return filterAndPush(s, getParentsNodes(s.Nodes, cascadia.MustCompile(untilSelector), nil), cascadia.MustCompile(filterSelector))
+	return filterAndPush(s, getParentsNodes(s.Nodes, compileMatcher(untilSelector), nil), compileMatcher(filterSelector))
 }
 
 // ParentsFilteredUntilMatcher is like ParentsUntilMatcher, with the option to filter the
@@ -234,7 +235,7 @@ func (s *Selection) ParentsFilteredUntilMatcher(filter, until Matcher) *Selectio
 // option to filter the results based on a selector string. It returns a new
 // Selection object containing the matched elements.
 func (s *Selection) ParentsFilteredUntilSelection(filterSelector string, sel *Selection) *Selection {
-	return s.ParentsMatcherUntilSelection(cascadia.MustCompile(filterSelector), sel)
+	return s.ParentsMatcherUntilSelection(compileMatcher(filterSelector), sel)
 }
 
 // ParentsMatcherUntilSelection is like ParentsUntilSelection, with the
@@ -251,7 +252,7 @@ func (s *Selection) ParentsMatcherUntilSelection(filter Matcher, sel *Selection)
 // option to filter the results based on a selector string. It returns a new
 // Selection object containing the matched elements.
 func (s *Selection) ParentsFilteredUntilNodes(filterSelector string, nodes ...*html.Node) *Selection {
-	return filterAndPush(s, getParentsNodes(s.Nodes, nil, nodes), cascadia.MustCompile(filterSelector))
+	return filterAndPush(s, getParentsNodes(s.Nodes, nil, nodes), compileMatcher(filterSelector))
 }
 
 // ParentsMatcherUntilNodes is like ParentsUntilNodes, with the
@@ -271,7 +272,7 @@ func (s *Selection) Siblings() *Selection {
 // filtered by a selector. It returns a new Selection object containing the
 // matched elements.
 func (s *Selection) SiblingsFiltered(selector string) *Selection {
-	return filterAndPush(s, getSiblingNodes(s.Nodes, siblingAll, nil, nil), cascadia.MustCompile(selector))
+	return filterAndPush(s, getSiblingNodes(s.Nodes, siblingAll, nil, nil), compileMatcher(selector))
 }
 
 // SiblingsMatcher gets the siblings of each element in the Selection
@@ -291,7 +292,7 @@ func (s *Selection) Next() *Selection {
 // Selection filtered by a selector. It returns a new Selection object
 // containing the matched elements.
 func (s *Selection) NextFiltered(selector string) *Selection {
-	return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNext, nil, nil), cascadia.MustCompile(selector))
+	return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNext, nil, nil), compileMatcher(selector))
 }
 
 // NextMatcher gets the immediately following sibling of each element in the
@@ -311,7 +312,7 @@ func (s *Selection) NextAll() *Selection {
 // Selection filtered by a selector. It returns a new Selection object
 // containing the matched elements.
 func (s *Selection) NextAllFiltered(selector string) *Selection {
-	return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNextAll, nil, nil), cascadia.MustCompile(selector))
+	return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNextAll, nil, nil), compileMatcher(selector))
 }
 
 // NextAllMatcher gets all the following siblings of each element in the
@@ -331,7 +332,7 @@ func (s *Selection) Prev() *Selection {
 // Selection filtered by a selector. It returns a new Selection object
 // containing the matched elements.
 func (s *Selection) PrevFiltered(selector string) *Selection {
-	return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrev, nil, nil), cascadia.MustCompile(selector))
+	return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrev, nil, nil), compileMatcher(selector))
 }
 
 // PrevMatcher gets the immediately preceding sibling of each element in the
@@ -351,7 +352,7 @@ func (s *Selection) PrevAll() *Selection {
 // Selection filtered by a selector. It returns a new Selection object
 // containing the matched elements.
 func (s *Selection) PrevAllFiltered(selector string) *Selection {
-	return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrevAll, nil, nil), cascadia.MustCompile(selector))
+	return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrevAll, nil, nil), compileMatcher(selector))
 }
 
 // PrevAllMatcher gets all the preceding siblings of each element in the
@@ -366,7 +367,7 @@ func (s *Selection) PrevAllMatcher(m Matcher) *Selection {
 // object containing the matched elements.
 func (s *Selection) NextUntil(selector string) *Selection {
 	return pushStack(s, getSiblingNodes(s.Nodes, siblingNextUntil,
-		cascadia.MustCompile(selector), nil))
+		compileMatcher(selector), nil))
 }
 
 // NextUntilMatcher gets all following siblings of each element up to but not
@@ -400,7 +401,7 @@ func (s *Selection) NextUntilNodes(nodes ...*html.Node) *Selection {
 // object containing the matched elements.
 func (s *Selection) PrevUntil(selector string) *Selection {
 	return pushStack(s, getSiblingNodes(s.Nodes, siblingPrevUntil,
-		cascadia.MustCompile(selector), nil))
+		compileMatcher(selector), nil))
 }
 
 // PrevUntilMatcher gets all preceding siblings of each element up to but not
@@ -434,7 +435,7 @@ func (s *Selection) PrevUntilNodes(nodes ...*html.Node) *Selection {
 // It returns a new Selection object containing the matched elements.
 func (s *Selection) NextFilteredUntil(filterSelector, untilSelector string) *Selection {
 	return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNextUntil,
-		cascadia.MustCompile(untilSelector), nil), cascadia.MustCompile(filterSelector))
+		compileMatcher(untilSelector), nil), compileMatcher(filterSelector))
 }
 
 // NextFilteredUntilMatcher is like NextUntilMatcher, with the option to filter
@@ -449,7 +450,7 @@ func (s *Selection) NextFilteredUntilMatcher(filter, until Matcher) *Selection {
 // option to filter the results based on a selector string. It returns a new
 // Selection object containing the matched elements.
 func (s *Selection) NextFilteredUntilSelection(filterSelector string, sel *Selection) *Selection {
-	return s.NextMatcherUntilSelection(cascadia.MustCompile(filterSelector), sel)
+	return s.NextMatcherUntilSelection(compileMatcher(filterSelector), sel)
 }
 
 // NextMatcherUntilSelection is like NextUntilSelection, with the
@@ -467,7 +468,7 @@ func (s *Selection) NextMatcherUntilSelection(filter Matcher, sel *Selection) *S
 // Selection object containing the matched elements.
 func (s *Selection) NextFilteredUntilNodes(filterSelector string, nodes ...*html.Node) *Selection {
 	return filterAndPush(s, getSiblingNodes(s.Nodes, siblingNextUntil,
-		nil, nodes), cascadia.MustCompile(filterSelector))
+		nil, nodes), compileMatcher(filterSelector))
 }
 
 // NextMatcherUntilNodes is like NextUntilNodes, with the
@@ -483,7 +484,7 @@ func (s *Selection) NextMatcherUntilNodes(filter Matcher, nodes ...*html.Node) *
 // It returns a new Selection object containing the matched elements.
 func (s *Selection) PrevFilteredUntil(filterSelector, untilSelector string) *Selection {
 	return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrevUntil,
-		cascadia.MustCompile(untilSelector), nil), cascadia.MustCompile(filterSelector))
+		compileMatcher(untilSelector), nil), compileMatcher(filterSelector))
 }
 
 // PrevFilteredUntilMatcher is like PrevUntilMatcher, with the option to filter
@@ -498,7 +499,7 @@ func (s *Selection) PrevFilteredUntilMatcher(filter, until Matcher) *Selection {
 // option to filter the results based on a selector string. It returns a new
 // Selection object containing the matched elements.
 func (s *Selection) PrevFilteredUntilSelection(filterSelector string, sel *Selection) *Selection {
-	return s.PrevMatcherUntilSelection(cascadia.MustCompile(filterSelector), sel)
+	return s.PrevMatcherUntilSelection(compileMatcher(filterSelector), sel)
 }
 
 // PrevMatcherUntilSelection is like PrevUntilSelection, with the
@@ -516,7 +517,7 @@ func (s *Selection) PrevMatcherUntilSelection(filter Matcher, sel *Selection) *S
 // Selection object containing the matched elements.
 func (s *Selection) PrevFilteredUntilNodes(filterSelector string, nodes ...*html.Node) *Selection {
 	return filterAndPush(s, getSiblingNodes(s.Nodes, siblingPrevUntil,
-		nil, nodes), cascadia.MustCompile(filterSelector))
+		nil, nodes), compileMatcher(filterSelector))
 }
 
 // PrevMatcherUntilNodes is like PrevUntilNodes, with the
@@ -687,10 +688,11 @@ func getParentNodes(nodes []*html.Node) []*html.Node {
 // Returns an array of nodes mapped by calling the callback function once for
 // each node in the source nodes.
 func mapNodes(nodes []*html.Node, f func(int, *html.Node) []*html.Node) (result []*html.Node) {
+	set := make(map[*html.Node]bool)
 	for i, n := range nodes {
 		if vals := f(i, n); len(vals) > 0 {
-			result = appendWithoutDuplicates(result, vals)
+			result = appendWithoutDuplicates(result, vals, set)
 		}
 	}
-	return
+	return result
 }

+ 99 - 3
src/github.com/PuerkitoBio/goquery/traversal_test.go

@@ -21,9 +21,19 @@ func TestFindNotSelf(t *testing.T) {
 	assertLength(t, sel.Nodes, 0)
 }
 
-func TestFindInvalidSelector(t *testing.T) {
-	defer assertPanic(t)
-	Doc().Find(":+ ^")
+func TestFindInvalid(t *testing.T) {
+	sel := Doc().Find(":+ ^")
+	assertLength(t, sel.Nodes, 0)
+}
+
+func TestFindBig(t *testing.T) {
+	doc := DocW()
+	sel := doc.Find("li")
+	assertLength(t, sel.Nodes, 373)
+	sel2 := doc.Find("span")
+	assertLength(t, sel2.Nodes, 448)
+	sel3 := sel.FindSelection(sel2)
+	assertLength(t, sel3.Nodes, 248)
 }
 
 func TestChainedFind(t *testing.T) {
@@ -31,6 +41,11 @@ func TestChainedFind(t *testing.T) {
 	assertLength(t, sel.Nodes, 4)
 }
 
+func TestChainedFindInvalid(t *testing.T) {
+	sel := Doc().Find("div.hero-unit").Find("")
+	assertLength(t, sel.Nodes, 0)
+}
+
 func TestChildren(t *testing.T) {
 	sel := Doc().Find(".pvk-content").Children()
 	assertLength(t, sel.Nodes, 5)
@@ -58,6 +73,11 @@ func TestChildrenFiltered(t *testing.T) {
 	assertLength(t, sel.Nodes, 1)
 }
 
+func TestChildrenFilteredInvalid(t *testing.T) {
+	sel := Doc().Find(".pvk-content").ChildrenFiltered("")
+	assertLength(t, sel.Nodes, 0)
+}
+
 func TestChildrenFilteredRollback(t *testing.T) {
 	sel := Doc().Find(".pvk-content")
 	sel2 := sel.ChildrenFiltered(".hero-unit").End()
@@ -69,6 +89,11 @@ func TestContentsFiltered(t *testing.T) {
 	assertLength(t, sel.Nodes, 1)
 }
 
+func TestContentsFilteredInvalid(t *testing.T) {
+	sel := Doc().Find(".pvk-content").ContentsFiltered("~")
+	assertLength(t, sel.Nodes, 0)
+}
+
 func TestContentsFilteredRollback(t *testing.T) {
 	sel := Doc().Find(".pvk-content")
 	sel2 := sel.ContentsFiltered(".hero-unit").End()
@@ -102,6 +127,11 @@ func TestParentFiltered(t *testing.T) {
 	assertClass(t, sel, "hero-unit")
 }
 
+func TestParentFilteredInvalid(t *testing.T) {
+	sel := Doc().Find(".container-fluid").ParentFiltered("")
+	assertLength(t, sel.Nodes, 0)
+}
+
 func TestParentFilteredRollback(t *testing.T) {
 	sel := Doc().Find(".container-fluid")
 	sel2 := sel.ParentFiltered(".hero-unit").End()
@@ -130,6 +160,11 @@ func TestParentsFiltered(t *testing.T) {
 	assertLength(t, sel.Nodes, 1)
 }
 
+func TestParentsFilteredInvalid(t *testing.T) {
+	sel := Doc().Find(".container-fluid").ParentsFiltered("")
+	assertLength(t, sel.Nodes, 0)
+}
+
 func TestParentsFilteredRollback(t *testing.T) {
 	sel := Doc().Find(".container-fluid")
 	sel2 := sel.ParentsFiltered("body").End()
@@ -141,6 +176,11 @@ func TestParentsUntil(t *testing.T) {
 	assertLength(t, sel.Nodes, 6)
 }
 
+func TestParentsUntilInvalid(t *testing.T) {
+	sel := Doc().Find(".container-fluid").ParentsUntil("")
+	assertLength(t, sel.Nodes, 8)
+}
+
 func TestParentsUntilRollback(t *testing.T) {
 	sel := Doc().Find(".container-fluid")
 	sel2 := sel.ParentsUntil("body").End()
@@ -180,6 +220,11 @@ func TestParentsFilteredUntil(t *testing.T) {
 	assertLength(t, sel.Nodes, 2)
 }
 
+func TestParentsFilteredUntilInvalid(t *testing.T) {
+	sel := Doc().Find(".container-fluid").ParentsFilteredUntil("", "")
+	assertLength(t, sel.Nodes, 0)
+}
+
 func TestParentsFilteredUntilRollback(t *testing.T) {
 	sel := Doc().Find(".container-fluid")
 	sel2 := sel.ParentsFilteredUntil(".pvk-content", "body").End()
@@ -240,6 +285,11 @@ func TestSiblingsFiltered(t *testing.T) {
 	assertLength(t, sel.Nodes, 3)
 }
 
+func TestSiblingsFilteredInvalid(t *testing.T) {
+	sel := Doc().Find(".pvk-gutter").SiblingsFiltered("")
+	assertLength(t, sel.Nodes, 0)
+}
+
 func TestSiblingsFilteredRollback(t *testing.T) {
 	sel := Doc().Find(".pvk-gutter")
 	sel2 := sel.SiblingsFiltered(".pvk-content").End()
@@ -272,6 +322,11 @@ func TestNextFiltered(t *testing.T) {
 	assertLength(t, sel.Nodes, 2)
 }
 
+func TestNextFilteredInvalid(t *testing.T) {
+	sel := Doc().Find(".container-fluid").NextFiltered("")
+	assertLength(t, sel.Nodes, 0)
+}
+
 func TestNextFilteredRollback(t *testing.T) {
 	sel := Doc().Find(".container-fluid")
 	sel2 := sel.NextFiltered("div").End()
@@ -310,6 +365,11 @@ func TestPrevFiltered(t *testing.T) {
 	assertLength(t, sel.Nodes, 5)
 }
 
+func TestPrevFilteredInvalid(t *testing.T) {
+	sel := Doc().Find(".row-fluid").PrevFiltered("")
+	assertLength(t, sel.Nodes, 0)
+}
+
 func TestPrevFilteredRollback(t *testing.T) {
 	sel := Doc().Find(".row-fluid")
 	sel2 := sel.PrevFiltered(".row-fluid").End()
@@ -342,6 +402,11 @@ func TestNextAllFiltered(t *testing.T) {
 	assertLength(t, sel.Nodes, 2)
 }
 
+func TestNextAllFilteredInvalid(t *testing.T) {
+	sel := Doc().Find("#cf2 .row-fluid").NextAllFiltered("")
+	assertLength(t, sel.Nodes, 0)
+}
+
 func TestNextAllFilteredRollback(t *testing.T) {
 	sel := Doc().Find("#cf2 .row-fluid")
 	sel2 := sel.NextAllFiltered("[ng-cloak]").End()
@@ -380,6 +445,11 @@ func TestPrevAllFiltered(t *testing.T) {
 	assertLength(t, sel.Nodes, 3)
 }
 
+func TestPrevAllFilteredInvalid(t *testing.T) {
+	sel := Doc().Find(".pvk-gutter").PrevAllFiltered("")
+	assertLength(t, sel.Nodes, 0)
+}
+
 func TestPrevAllFilteredRollback(t *testing.T) {
 	sel := Doc().Find(".pvk-gutter")
 	sel2 := sel.PrevAllFiltered(".pvk-content").End()
@@ -392,6 +462,11 @@ func TestNextUntil(t *testing.T) {
 	assertSelectionIs(t, sel, "h4")
 }
 
+func TestNextUntilInvalid(t *testing.T) {
+	sel := Doc().Find(".alert a").NextUntil("")
+	assertLength(t, sel.Nodes, 2)
+}
+
 func TestNextUntil2(t *testing.T) {
 	sel := Doc().Find("#cf2-1").NextUntil("[ng-cloak]")
 	assertLength(t, sel.Nodes, 1)
@@ -446,6 +521,11 @@ func TestPrevUntil(t *testing.T) {
 	assertSelectionIs(t, sel, "h4")
 }
 
+func TestPrevUntilInvalid(t *testing.T) {
+	sel := Doc().Find(".alert p").PrevUntil("")
+	assertLength(t, sel.Nodes, 2)
+}
+
 func TestPrevUntil2(t *testing.T) {
 	sel := Doc().Find("[ng-cloak]").PrevUntil(":not([ng-cloak])")
 	assertLength(t, sel.Nodes, 1)
@@ -500,6 +580,11 @@ func TestNextFilteredUntil(t *testing.T) {
 	assertSelectionIs(t, sel, "#n3", "#n5", "#nf3", "#nf5")
 }
 
+func TestNextFilteredUntilInvalid(t *testing.T) {
+	sel := Doc2().Find(".two").NextFilteredUntil("", "")
+	assertLength(t, sel.Nodes, 0)
+}
+
 func TestNextFilteredUntilRollback(t *testing.T) {
 	sel := Doc2().Find(".two")
 	sel2 := sel.NextFilteredUntil(".even", ".six").End()
@@ -542,6 +627,11 @@ func TestPrevFilteredUntil(t *testing.T) {
 	assertSelectionIs(t, sel, "#n4", "#n2", "#nf4", "#nf2")
 }
 
+func TestPrevFilteredUntilInvalid(t *testing.T) {
+	sel := Doc2().Find(".five").PrevFilteredUntil("", "")
+	assertLength(t, sel.Nodes, 0)
+}
+
 func TestPrevFilteredUntilRollback(t *testing.T) {
 	sel := Doc2().Find(".four")
 	sel2 := sel.PrevFilteredUntil(".odd", ".one").End()
@@ -598,6 +688,12 @@ func TestClosestNone(t *testing.T) {
 	assertLength(t, sel2.Nodes, 0)
 }
 
+func TestClosestInvalid(t *testing.T) {
+	sel := Doc().Find("h4")
+	sel2 := sel.Closest("")
+	assertLength(t, sel2.Nodes, 0)
+}
+
 func TestClosestMany(t *testing.T) {
 	sel := Doc().Find(".container-fluid")
 	sel2 := sel.Closest(".pvk-content")

+ 33 - 5
src/github.com/PuerkitoBio/goquery/type.go

@@ -6,6 +6,8 @@ import (
 	"net/http"
 	"net/url"
 
+	"github.com/andybalholm/cascadia"
+
 	"golang.org/x/net/html"
 )
 
@@ -29,6 +31,10 @@ func NewDocumentFromNode(root *html.Node) *Document {
 // NewDocument is a Document constructor that takes a string URL as argument.
 // It loads the specified document, parses it, and stores the root Document
 // node, ready to be manipulated.
+//
+// Deprecated: Use the net/http standard library package to make the request
+// and validate the response before calling goquery.NewDocumentFromReader
+// with the response's body.
 func NewDocument(url string) (*Document, error) {
 	// Load the URL
 	res, e := http.Get(url)
@@ -38,10 +44,10 @@ func NewDocument(url string) (*Document, error) {
 	return NewDocumentFromResponse(res)
 }
 
-// NewDocumentFromReader returns a Document from a generic reader.
+// NewDocumentFromReader returns a Document from an io.Reader.
 // It returns an error as second value if the reader's data cannot be parsed
-// as html. It does *not* check if the reader is also an io.Closer, so the
-// provided reader is never closed by this call, it is the responsibility
+// as html. It does not check if the reader is also an io.Closer, the
+// provided reader is never closed by this call. It is the responsibility
 // of the caller to close it if required.
 func NewDocumentFromReader(r io.Reader) (*Document, error) {
 	root, e := html.Parse(r)
@@ -54,12 +60,16 @@ func NewDocumentFromReader(r io.Reader) (*Document, error) {
 // NewDocumentFromResponse is another Document constructor that takes an http response as argument.
 // It loads the specified response's document, parses it, and stores the root Document
 // node, ready to be manipulated. The response's body is closed on return.
+//
+// Deprecated: Use goquery.NewDocumentFromReader with the response's body.
 func NewDocumentFromResponse(res *http.Response) (*Document, error) {
 	if res == nil {
-		return nil, errors.New("Response is nil pointer")
+		return nil, errors.New("Response is nil")
 	}
-
 	defer res.Body.Close()
+	if res.Request == nil {
+		return nil, errors.New("Response.Request is nil")
+	}
 
 	// Parse the HTML into nodes
 	root, e := html.Parse(res.Body)
@@ -111,3 +121,21 @@ type Matcher interface {
 	MatchAll(*html.Node) []*html.Node
 	Filter([]*html.Node) []*html.Node
 }
+
+// compileMatcher compiles the selector string s and returns
+// the corresponding Matcher. If s is an invalid selector string,
+// it returns a Matcher that fails all matches.
+func compileMatcher(s string) Matcher {
+	cs, err := cascadia.Compile(s)
+	if err != nil {
+		return invalidMatcher{}
+	}
+	return cs
+}
+
+// invalidMatcher is a Matcher that always fails to match.
+type invalidMatcher struct{}
+
+func (invalidMatcher) Match(n *html.Node) bool             { return false }
+func (invalidMatcher) MatchAll(n *html.Node) []*html.Node  { return nil }
+func (invalidMatcher) Filter(ns []*html.Node) []*html.Node { return nil }

+ 19 - 9
src/github.com/PuerkitoBio/goquery/type_test.go

@@ -4,6 +4,7 @@ import (
 	"bytes"
 	"fmt"
 	"os"
+	"strings"
 	"testing"
 
 	"golang.org/x/net/html"
@@ -22,45 +23,42 @@ func Doc() *Document {
 	}
 	return doc
 }
-func DocClone() *Document {
-	return CloneDocument(Doc())
-}
+
 func Doc2() *Document {
 	if doc2 == nil {
 		doc2 = loadDoc("page2.html")
 	}
 	return doc2
 }
+
 func Doc2Clone() *Document {
 	return CloneDocument(Doc2())
 }
+
 func Doc3() *Document {
 	if doc3 == nil {
 		doc3 = loadDoc("page3.html")
 	}
 	return doc3
 }
+
 func Doc3Clone() *Document {
 	return CloneDocument(Doc3())
 }
+
 func DocB() *Document {
 	if docB == nil {
 		docB = loadDoc("gotesting.html")
 	}
 	return docB
 }
-func DocBClone() *Document {
-	return CloneDocument(DocB())
-}
+
 func DocW() *Document {
 	if docW == nil {
 		docW = loadDoc("gowiki.html")
 	}
 	return docW
 }
-func DocWClone() *Document {
-	return CloneDocument(DocW())
-}
 
 func assertLength(t *testing.T, nodes []*html.Node, length int) {
 	if len(nodes) != length {
@@ -190,3 +188,15 @@ func TestNewDocumentFromResponseNil(t *testing.T) {
 		t.Error("Expected error, got none")
 	}
 }
+
+func TestIssue103(t *testing.T) {
+	d, err := NewDocumentFromReader(strings.NewReader("<html><title>Scientists Stored These Images in DNA—Then Flawlessly Retrieved Them</title></html>"))
+	if err != nil {
+		t.Error(err)
+	}
+	text := d.Find("title").Text()
+	for i, r := range text {
+		t.Logf("%d: %d - %q\n", i, r, string(r))
+	}
+	t.Log(text)
+}

+ 84 - 7
src/github.com/PuerkitoBio/goquery/utilities.go

@@ -1,14 +1,70 @@
 package goquery
 
 import (
+	"bytes"
+
 	"golang.org/x/net/html"
 )
 
-func getChildren(n *html.Node) (result []*html.Node) {
-	for c := n.FirstChild; c != nil; c = c.NextSibling {
-		result = append(result, c)
+// used to determine if a set (map[*html.Node]bool) should be used
+// instead of iterating over a slice. The set uses more memory and
+// is slower than slice iteration for small N.
+const minNodesForSet = 1000
+
+var nodeNames = []string{
+	html.ErrorNode:    "#error",
+	html.TextNode:     "#text",
+	html.DocumentNode: "#document",
+	html.CommentNode:  "#comment",
+}
+
+// NodeName returns the node name of the first element in the selection.
+// It tries to behave in a similar way as the DOM's nodeName property
+// (https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeName).
+//
+// Go's net/html package defines the following node types, listed with
+// the corresponding returned value from this function:
+//
+//     ErrorNode : #error
+//     TextNode : #text
+//     DocumentNode : #document
+//     ElementNode : the element's tag name
+//     CommentNode : #comment
+//     DoctypeNode : the name of the document type
+//
+func NodeName(s *Selection) string {
+	if s.Length() == 0 {
+		return ""
 	}
-	return result
+	switch n := s.Get(0); n.Type {
+	case html.ElementNode, html.DoctypeNode:
+		return n.Data
+	default:
+		if n.Type >= 0 && int(n.Type) < len(nodeNames) {
+			return nodeNames[n.Type]
+		}
+		return ""
+	}
+}
+
+// OuterHtml returns the outer HTML rendering of the first item in
+// the selection - that is, the HTML including the first element's
+// tag and attributes.
+//
+// Unlike InnerHtml, this is a function and not a method on the Selection,
+// because this is not a jQuery method (in javascript-land, this is
+// a property provided by the DOM).
+func OuterHtml(s *Selection) (string, error) {
+	var buf bytes.Buffer
+
+	if s.Length() == 0 {
+		return "", nil
+	}
+	n := s.Get(0)
+	if err := html.Render(&buf, n); err != nil {
+		return "", err
+	}
+	return buf.String(), nil
 }
 
 // Loop through all container nodes to search for the target node.
@@ -54,11 +110,32 @@ func indexInSlice(slice []*html.Node, node *html.Node) int {
 // Appends the new nodes to the target slice, making sure no duplicate is added.
 // There is no check to the original state of the target slice, so it may still
 // contain duplicates. The target slice is returned because append() may create
-// a new underlying array.
-func appendWithoutDuplicates(target []*html.Node, nodes []*html.Node) []*html.Node {
+// a new underlying array. If targetSet is nil, a local set is created with the
+// target if len(target) + len(nodes) is greater than minNodesForSet.
+func appendWithoutDuplicates(target []*html.Node, nodes []*html.Node, targetSet map[*html.Node]bool) []*html.Node {
+	// if there are not that many nodes, don't use the map, faster to just use nested loops
+	// (unless a non-nil targetSet is passed, in which case the caller knows better).
+	if targetSet == nil && len(target)+len(nodes) < minNodesForSet {
+		for _, n := range nodes {
+			if !isInSlice(target, n) {
+				target = append(target, n)
+			}
+		}
+		return target
+	}
+
+	// if a targetSet is passed, then assume it is reliable, otherwise create one
+	// and initialize it with the current target contents.
+	if targetSet == nil {
+		targetSet = make(map[*html.Node]bool, len(target))
+		for _, n := range target {
+			targetSet[n] = true
+		}
+	}
 	for _, n := range nodes {
-		if !isInSlice(target, n) {
+		if !targetSet[n] {
 			target = append(target, n)
+			targetSet[n] = true
 		}
 	}
 

+ 128 - 0
src/github.com/PuerkitoBio/goquery/utilities_test.go

@@ -0,0 +1,128 @@
+package goquery
+
+import (
+	"reflect"
+	"sort"
+	"strings"
+	"testing"
+
+	"golang.org/x/net/html"
+)
+
+var allNodes = `<!doctype html>
+<html>
+	<head>
+		<meta a="b">
+	</head>
+	<body>
+		<p><!-- this is a comment -->
+		This is some text.
+		</p>
+		<div></div>
+		<h1 class="header"></h1>
+		<h2 class="header"></h2>
+	</body>
+</html>`
+
+func TestNodeName(t *testing.T) {
+	doc, err := NewDocumentFromReader(strings.NewReader(allNodes))
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	n0 := doc.Nodes[0]
+	nDT := n0.FirstChild
+	sMeta := doc.Find("meta")
+	nMeta := sMeta.Get(0)
+	sP := doc.Find("p")
+	nP := sP.Get(0)
+	nComment := nP.FirstChild
+	nText := nComment.NextSibling
+
+	cases := []struct {
+		node *html.Node
+		typ  html.NodeType
+		want string
+	}{
+		{n0, html.DocumentNode, nodeNames[html.DocumentNode]},
+		{nDT, html.DoctypeNode, "html"},
+		{nMeta, html.ElementNode, "meta"},
+		{nP, html.ElementNode, "p"},
+		{nComment, html.CommentNode, nodeNames[html.CommentNode]},
+		{nText, html.TextNode, nodeNames[html.TextNode]},
+	}
+	for i, c := range cases {
+		got := NodeName(newSingleSelection(c.node, doc))
+		if c.node.Type != c.typ {
+			t.Errorf("%d: want type %v, got %v", i, c.typ, c.node.Type)
+		}
+		if got != c.want {
+			t.Errorf("%d: want %q, got %q", i, c.want, got)
+		}
+	}
+}
+
+func TestNodeNameMultiSel(t *testing.T) {
+	doc, err := NewDocumentFromReader(strings.NewReader(allNodes))
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	in := []string{"p", "h1", "div"}
+	var out []string
+	doc.Find(strings.Join(in, ", ")).Each(func(i int, s *Selection) {
+		got := NodeName(s)
+		out = append(out, got)
+	})
+	sort.Strings(in)
+	sort.Strings(out)
+	if !reflect.DeepEqual(in, out) {
+		t.Errorf("want %v, got %v", in, out)
+	}
+}
+
+func TestOuterHtml(t *testing.T) {
+	doc, err := NewDocumentFromReader(strings.NewReader(allNodes))
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	n0 := doc.Nodes[0]
+	nDT := n0.FirstChild
+	sMeta := doc.Find("meta")
+	sP := doc.Find("p")
+	nP := sP.Get(0)
+	nComment := nP.FirstChild
+	nText := nComment.NextSibling
+	sHeaders := doc.Find(".header")
+
+	cases := []struct {
+		node *html.Node
+		sel  *Selection
+		want string
+	}{
+		{nDT, nil, "<!DOCTYPE html>"}, // render makes DOCTYPE all caps
+		{nil, sMeta, `<meta a="b"/>`}, // and auto-closes the meta
+		{nil, sP, `<p><!-- this is a comment -->
+		This is some text.
+		</p>`},
+		{nComment, nil, "<!-- this is a comment -->"},
+		{nText, nil, `
+		This is some text.
+		`},
+		{nil, sHeaders, `<h1 class="header"></h1>`},
+	}
+	for i, c := range cases {
+		if c.sel == nil {
+			c.sel = newSingleSelection(c.node, doc)
+		}
+		got, err := OuterHtml(c.sel)
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		if got != c.want {
+			t.Errorf("%d: want %q, got %q", i, c.want, got)
+		}
+	}
+}

+ 6 - 0
src/github.com/shopspring/decimal/.gitignore

@@ -0,0 +1,6 @@
+.git
+*.swp
+
+# IntelliJ
+.idea/
+*.iml

+ 13 - 0
src/github.com/shopspring/decimal/.travis.yml

@@ -0,0 +1,13 @@
+language: go
+
+go:
+  - 1.7.x
+  - 1.12.x
+  - 1.13.x
+  - tip
+
+install:
+  - go build .
+
+script:
+  - go test -v

+ 19 - 0
src/github.com/shopspring/decimal/CHANGELOG.md

@@ -0,0 +1,19 @@
+## Decimal v1.2.0 
+
+#### BREAKING
+- Drop support for Go version older than 1.7 [#172](https://github.com/shopspring/decimal/pull/172)
+
+#### FEATURES
+- Add NewFromInt and NewFromInt32 initializers [#72](https://github.com/shopspring/decimal/pull/72)
+- Add support for Go modules [#157](https://github.com/shopspring/decimal/pull/157)
+- Add BigInt, BigFloat helper methods [#171](https://github.com/shopspring/decimal/pull/171)
+
+#### ENHANCEMENTS
+- Memory usage optimization [#160](https://github.com/shopspring/decimal/pull/160)
+- Updated travis CI golang versions [#156](https://github.com/shopspring/decimal/pull/156)
+- Update documentation [#173](https://github.com/shopspring/decimal/pull/173)
+- Improve code quality [#174](https://github.com/shopspring/decimal/pull/174)
+
+#### BUGFIXES
+- Revert remove insignificant digits [#159](https://github.com/shopspring/decimal/pull/159)
+- Remove 15 interval for RoundCash [#166](https://github.com/shopspring/decimal/pull/166)

+ 45 - 0
src/github.com/shopspring/decimal/LICENSE

@@ -0,0 +1,45 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Spring, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+- Based on https://github.com/oguzbilgic/fpd, which has the following license:
+"""
+The MIT License (MIT)
+
+Copyright (c) 2013 Oguz Bilgic
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+"""

+ 130 - 0
src/github.com/shopspring/decimal/README.md

@@ -0,0 +1,130 @@
+# decimal
+
+[![Build Status](https://travis-ci.org/shopspring/decimal.png?branch=master)](https://travis-ci.org/shopspring/decimal) [![GoDoc](https://godoc.org/github.com/shopspring/decimal?status.svg)](https://godoc.org/github.com/shopspring/decimal) [![Go Report Card](https://goreportcard.com/badge/github.com/shopspring/decimal)](https://goreportcard.com/report/github.com/shopspring/decimal)
+
+Arbitrary-precision fixed-point decimal numbers in go.
+
+_Note:_ Decimal library can "only" represent numbers with a maximum of 2^31 digits after the decimal point.
+
+## Features
+
+ * The zero-value is 0, and is safe to use without initialization
+ * Addition, subtraction, multiplication with no loss of precision
+ * Division with specified precision
+ * Database/sql serialization/deserialization
+ * JSON and XML serialization/deserialization
+
+## Install
+
+Run `go get github.com/shopspring/decimal`
+
+## Requirements 
+
+Decimal library requires Go version `>=1.7`
+
+## Usage
+
+```go
+package main
+
+import (
+	"fmt"
+	"github.com/shopspring/decimal"
+)
+
+func main() {
+	price, err := decimal.NewFromString("136.02")
+	if err != nil {
+		panic(err)
+	}
+
+	quantity := decimal.NewFromInt(3)
+
+	fee, _ := decimal.NewFromString(".035")
+	taxRate, _ := decimal.NewFromString(".08875")
+
+	subtotal := price.Mul(quantity)
+
+	preTax := subtotal.Mul(fee.Add(decimal.NewFromFloat(1)))
+
+	total := preTax.Mul(taxRate.Add(decimal.NewFromFloat(1)))
+
+	fmt.Println("Subtotal:", subtotal)                      // Subtotal: 408.06
+	fmt.Println("Pre-tax:", preTax)                         // Pre-tax: 422.3421
+	fmt.Println("Taxes:", total.Sub(preTax))                // Taxes: 37.482861375
+	fmt.Println("Total:", total)                            // Total: 459.824961375
+	fmt.Println("Tax rate:", total.Sub(preTax).Div(preTax)) // Tax rate: 0.08875
+}
+```
+
+## Documentation
+
+http://godoc.org/github.com/shopspring/decimal
+
+## Production Usage
+
+* [Spring](https://shopspring.com/), since August 14, 2014.
+* If you are using this in production, please let us know!
+
+## FAQ
+
+#### Why don't you just use float64?
+
+Because float64 (or any binary floating point type, actually) can't represent
+numbers such as `0.1` exactly.
+
+Consider this code: http://play.golang.org/p/TQBd4yJe6B You might expect that
+it prints out `10`, but it actually prints `9.999999999999831`. Over time,
+these small errors can really add up!
+
+#### Why don't you just use big.Rat?
+
+big.Rat is fine for representing rational numbers, but Decimal is better for
+representing money. Why? Here's a (contrived) example:
+
+Let's say you use big.Rat, and you have two numbers, x and y, both
+representing 1/3, and you have `z = 1 - x - y = 1/3`. If you print each one
+out, the string output has to stop somewhere (let's say it stops at 3 decimal
+digits, for simplicity), so you'll get 0.333, 0.333, and 0.333. But where did
+the other 0.001 go?
+
+Here's the above example as code: http://play.golang.org/p/lCZZs0w9KE
+
+With Decimal, the strings being printed out represent the number exactly. So,
+if you have `x = y = 1/3` (with precision 3), they will actually be equal to
+0.333, and when you do `z = 1 - x - y`, `z` will be equal to .334. No money is
+unaccounted for!
+
+You still have to be careful. If you want to split a number `N` 3 ways, you
+can't just send `N/3` to three different people. You have to pick one to send
+`N - (2/3*N)` to. That person will receive the fraction of a penny remainder.
+
+But, it is much easier to be careful with Decimal than with big.Rat.
+
+#### Why isn't the API similar to big.Int's?
+
+big.Int's API is built to reduce the number of memory allocations for maximal
+performance. This makes sense for its use-case, but the trade-off is that the
+API is awkward and easy to misuse.
+
+For example, to add two big.Ints, you do: `z := new(big.Int).Add(x, y)`. A
+developer unfamiliar with this API might try to do `z := a.Add(a, b)`. This
+modifies `a` and sets `z` as an alias for `a`, which they might not expect. It
+also modifies any other aliases to `a`.
+
+Here's an example of the subtle bugs you can introduce with big.Int's API:
+https://play.golang.org/p/x2R_78pa8r
+
+In contrast, it's difficult to make such mistakes with decimal. Decimals
+behave like other go numbers types: even though `a = b` will not deep copy
+`b` into `a`, it is impossible to modify a Decimal, since all Decimal methods
+return new Decimals and do not modify the originals. The downside is that
+this causes extra allocations, so Decimal is less performant.  My assumption
+is that if you're using Decimals, you probably care more about correctness
+than performance.
+
+## License
+
+The MIT License (MIT)
+
+This is a heavily modified fork of [fpd.Decimal](https://github.com/oguzbilgic/fpd), which was also released under the MIT License.

+ 415 - 0
src/github.com/shopspring/decimal/decimal-go.go

@@ -0,0 +1,415 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Multiprecision decimal numbers.
+// For floating-point formatting only; not general purpose.
+// Only operations are assign and (binary) left/right shift.
+// Can do binary floating point in multiprecision decimal precisely
+// because 2 divides 10; cannot do decimal floating point
+// in multiprecision binary precisely.
+
+package decimal
+
+type decimal struct {
+	d     [800]byte // digits, big-endian representation
+	nd    int       // number of digits used
+	dp    int       // decimal point
+	neg   bool      // negative flag
+	trunc bool      // discarded nonzero digits beyond d[:nd]
+}
+
+func (a *decimal) String() string {
+	n := 10 + a.nd
+	if a.dp > 0 {
+		n += a.dp
+	}
+	if a.dp < 0 {
+		n += -a.dp
+	}
+
+	buf := make([]byte, n)
+	w := 0
+	switch {
+	case a.nd == 0:
+		return "0"
+
+	case a.dp <= 0:
+		// zeros fill space between decimal point and digits
+		buf[w] = '0'
+		w++
+		buf[w] = '.'
+		w++
+		w += digitZero(buf[w : w+-a.dp])
+		w += copy(buf[w:], a.d[0:a.nd])
+
+	case a.dp < a.nd:
+		// decimal point in middle of digits
+		w += copy(buf[w:], a.d[0:a.dp])
+		buf[w] = '.'
+		w++
+		w += copy(buf[w:], a.d[a.dp:a.nd])
+
+	default:
+		// zeros fill space between digits and decimal point
+		w += copy(buf[w:], a.d[0:a.nd])
+		w += digitZero(buf[w : w+a.dp-a.nd])
+	}
+	return string(buf[0:w])
+}
+
+func digitZero(dst []byte) int {
+	for i := range dst {
+		dst[i] = '0'
+	}
+	return len(dst)
+}
+
+// trim trailing zeros from number.
+// (They are meaningless; the decimal point is tracked
+// independent of the number of digits.)
+func trim(a *decimal) {
+	for a.nd > 0 && a.d[a.nd-1] == '0' {
+		a.nd--
+	}
+	if a.nd == 0 {
+		a.dp = 0
+	}
+}
+
+// Assign v to a.
+func (a *decimal) Assign(v uint64) {
+	var buf [24]byte
+
+	// Write reversed decimal in buf.
+	n := 0
+	for v > 0 {
+		v1 := v / 10
+		v -= 10 * v1
+		buf[n] = byte(v + '0')
+		n++
+		v = v1
+	}
+
+	// Reverse again to produce forward decimal in a.d.
+	a.nd = 0
+	for n--; n >= 0; n-- {
+		a.d[a.nd] = buf[n]
+		a.nd++
+	}
+	a.dp = a.nd
+	trim(a)
+}
+
+// Maximum shift that we can do in one pass without overflow.
+// A uint has 32 or 64 bits, and we have to be able to accommodate 9<<k.
+const uintSize = 32 << (^uint(0) >> 63)
+const maxShift = uintSize - 4
+
+// Binary shift right (/ 2) by k bits.  k <= maxShift to avoid overflow.
+func rightShift(a *decimal, k uint) {
+	r := 0 // read pointer
+	w := 0 // write pointer
+
+	// Pick up enough leading digits to cover first shift.
+	var n uint
+	for ; n>>k == 0; r++ {
+		if r >= a.nd {
+			if n == 0 {
+				// a == 0; shouldn't get here, but handle anyway.
+				a.nd = 0
+				return
+			}
+			for n>>k == 0 {
+				n = n * 10
+				r++
+			}
+			break
+		}
+		c := uint(a.d[r])
+		n = n*10 + c - '0'
+	}
+	a.dp -= r - 1
+
+	var mask uint = (1 << k) - 1
+
+	// Pick up a digit, put down a digit.
+	for ; r < a.nd; r++ {
+		c := uint(a.d[r])
+		dig := n >> k
+		n &= mask
+		a.d[w] = byte(dig + '0')
+		w++
+		n = n*10 + c - '0'
+	}
+
+	// Put down extra digits.
+	for n > 0 {
+		dig := n >> k
+		n &= mask
+		if w < len(a.d) {
+			a.d[w] = byte(dig + '0')
+			w++
+		} else if dig > 0 {
+			a.trunc = true
+		}
+		n = n * 10
+	}
+
+	a.nd = w
+	trim(a)
+}
+
+// Cheat sheet for left shift: table indexed by shift count giving
+// number of new digits that will be introduced by that shift.
+//
+// For example, leftcheats[4] = {2, "625"}.  That means that
+// if we are shifting by 4 (multiplying by 16), it will add 2 digits
+// when the string prefix is "625" through "999", and one fewer digit
+// if the string prefix is "000" through "624".
+//
+// Credit for this trick goes to Ken.
+
+type leftCheat struct {
+	delta  int    // number of new digits
+	cutoff string // minus one digit if original < a.
+}
+
+var leftcheats = []leftCheat{
+	// Leading digits of 1/2^i = 5^i.
+	// 5^23 is not an exact 64-bit floating point number,
+	// so have to use bc for the math.
+	// Go up to 60 to be large enough for 32bit and 64bit platforms.
+	/*
+		seq 60 | sed 's/^/5^/' | bc |
+		awk 'BEGIN{ print "\t{ 0, \"\" }," }
+		{
+			log2 = log(2)/log(10)
+			printf("\t{ %d, \"%s\" },\t// * %d\n",
+				int(log2*NR+1), $0, 2**NR)
+		}'
+	*/
+	{0, ""},
+	{1, "5"},                                           // * 2
+	{1, "25"},                                          // * 4
+	{1, "125"},                                         // * 8
+	{2, "625"},                                         // * 16
+	{2, "3125"},                                        // * 32
+	{2, "15625"},                                       // * 64
+	{3, "78125"},                                       // * 128
+	{3, "390625"},                                      // * 256
+	{3, "1953125"},                                     // * 512
+	{4, "9765625"},                                     // * 1024
+	{4, "48828125"},                                    // * 2048
+	{4, "244140625"},                                   // * 4096
+	{4, "1220703125"},                                  // * 8192
+	{5, "6103515625"},                                  // * 16384
+	{5, "30517578125"},                                 // * 32768
+	{5, "152587890625"},                                // * 65536
+	{6, "762939453125"},                                // * 131072
+	{6, "3814697265625"},                               // * 262144
+	{6, "19073486328125"},                              // * 524288
+	{7, "95367431640625"},                              // * 1048576
+	{7, "476837158203125"},                             // * 2097152
+	{7, "2384185791015625"},                            // * 4194304
+	{7, "11920928955078125"},                           // * 8388608
+	{8, "59604644775390625"},                           // * 16777216
+	{8, "298023223876953125"},                          // * 33554432
+	{8, "1490116119384765625"},                         // * 67108864
+	{9, "7450580596923828125"},                         // * 134217728
+	{9, "37252902984619140625"},                        // * 268435456
+	{9, "186264514923095703125"},                       // * 536870912
+	{10, "931322574615478515625"},                      // * 1073741824
+	{10, "4656612873077392578125"},                     // * 2147483648
+	{10, "23283064365386962890625"},                    // * 4294967296
+	{10, "116415321826934814453125"},                   // * 8589934592
+	{11, "582076609134674072265625"},                   // * 17179869184
+	{11, "2910383045673370361328125"},                  // * 34359738368
+	{11, "14551915228366851806640625"},                 // * 68719476736
+	{12, "72759576141834259033203125"},                 // * 137438953472
+	{12, "363797880709171295166015625"},                // * 274877906944
+	{12, "1818989403545856475830078125"},               // * 549755813888
+	{13, "9094947017729282379150390625"},               // * 1099511627776
+	{13, "45474735088646411895751953125"},              // * 2199023255552
+	{13, "227373675443232059478759765625"},             // * 4398046511104
+	{13, "1136868377216160297393798828125"},            // * 8796093022208
+	{14, "5684341886080801486968994140625"},            // * 17592186044416
+	{14, "28421709430404007434844970703125"},           // * 35184372088832
+	{14, "142108547152020037174224853515625"},          // * 70368744177664
+	{15, "710542735760100185871124267578125"},          // * 140737488355328
+	{15, "3552713678800500929355621337890625"},         // * 281474976710656
+	{15, "17763568394002504646778106689453125"},        // * 562949953421312
+	{16, "88817841970012523233890533447265625"},        // * 1125899906842624
+	{16, "444089209850062616169452667236328125"},       // * 2251799813685248
+	{16, "2220446049250313080847263336181640625"},      // * 4503599627370496
+	{16, "11102230246251565404236316680908203125"},     // * 9007199254740992
+	{17, "55511151231257827021181583404541015625"},     // * 18014398509481984
+	{17, "277555756156289135105907917022705078125"},    // * 36028797018963968
+	{17, "1387778780781445675529539585113525390625"},   // * 72057594037927936
+	{18, "6938893903907228377647697925567626953125"},   // * 144115188075855872
+	{18, "34694469519536141888238489627838134765625"},  // * 288230376151711744
+	{18, "173472347597680709441192448139190673828125"}, // * 576460752303423488
+	{19, "867361737988403547205962240695953369140625"}, // * 1152921504606846976
+}
+
+// Is the leading prefix of b lexicographically less than s?
+func prefixIsLessThan(b []byte, s string) bool {
+	for i := 0; i < len(s); i++ {
+		if i >= len(b) {
+			return true
+		}
+		if b[i] != s[i] {
+			return b[i] < s[i]
+		}
+	}
+	return false
+}
+
+// Binary shift left (* 2) by k bits.  k <= maxShift to avoid overflow.
+func leftShift(a *decimal, k uint) {
+	delta := leftcheats[k].delta
+	if prefixIsLessThan(a.d[0:a.nd], leftcheats[k].cutoff) {
+		delta--
+	}
+
+	r := a.nd         // read index
+	w := a.nd + delta // write index
+
+	// Pick up a digit, put down a digit.
+	var n uint
+	for r--; r >= 0; r-- {
+		n += (uint(a.d[r]) - '0') << k
+		quo := n / 10
+		rem := n - 10*quo
+		w--
+		if w < len(a.d) {
+			a.d[w] = byte(rem + '0')
+		} else if rem != 0 {
+			a.trunc = true
+		}
+		n = quo
+	}
+
+	// Put down extra digits.
+	for n > 0 {
+		quo := n / 10
+		rem := n - 10*quo
+		w--
+		if w < len(a.d) {
+			a.d[w] = byte(rem + '0')
+		} else if rem != 0 {
+			a.trunc = true
+		}
+		n = quo
+	}
+
+	a.nd += delta
+	if a.nd >= len(a.d) {
+		a.nd = len(a.d)
+	}
+	a.dp += delta
+	trim(a)
+}
+
+// Binary shift left (k > 0) or right (k < 0).
+func (a *decimal) Shift(k int) {
+	switch {
+	case a.nd == 0:
+		// nothing to do: a == 0
+	case k > 0:
+		for k > maxShift {
+			leftShift(a, maxShift)
+			k -= maxShift
+		}
+		leftShift(a, uint(k))
+	case k < 0:
+		for k < -maxShift {
+			rightShift(a, maxShift)
+			k += maxShift
+		}
+		rightShift(a, uint(-k))
+	}
+}
+
+// If we chop a at nd digits, should we round up?
+func shouldRoundUp(a *decimal, nd int) bool {
+	if nd < 0 || nd >= a.nd {
+		return false
+	}
+	if a.d[nd] == '5' && nd+1 == a.nd { // exactly halfway - round to even
+		// if we truncated, a little higher than what's recorded - always round up
+		if a.trunc {
+			return true
+		}
+		return nd > 0 && (a.d[nd-1]-'0')%2 != 0
+	}
+	// not halfway - digit tells all
+	return a.d[nd] >= '5'
+}
+
+// Round a to nd digits (or fewer).
+// If nd is zero, it means we're rounding
+// just to the left of the digits, as in
+// 0.09 -> 0.1.
+func (a *decimal) Round(nd int) {
+	if nd < 0 || nd >= a.nd {
+		return
+	}
+	if shouldRoundUp(a, nd) {
+		a.RoundUp(nd)
+	} else {
+		a.RoundDown(nd)
+	}
+}
+
+// Round a down to nd digits (or fewer).
+func (a *decimal) RoundDown(nd int) {
+	if nd < 0 || nd >= a.nd {
+		return
+	}
+	a.nd = nd
+	trim(a)
+}
+
+// Round a up to nd digits (or fewer).
+func (a *decimal) RoundUp(nd int) {
+	if nd < 0 || nd >= a.nd {
+		return
+	}
+
+	// round up
+	for i := nd - 1; i >= 0; i-- {
+		c := a.d[i]
+		if c < '9' { // can stop after this digit
+			a.d[i]++
+			a.nd = i + 1
+			return
+		}
+	}
+
+	// Number is all 9s.
+	// Change to single 1 with adjusted decimal point.
+	a.d[0] = '1'
+	a.nd = 1
+	a.dp++
+}
+
+// Extract integer part, rounded appropriately.
+// No guarantees about overflow.
+func (a *decimal) RoundedInteger() uint64 {
+	if a.dp > 20 {
+		return 0xFFFFFFFFFFFFFFFF
+	}
+	var i int
+	n := uint64(0)
+	for i = 0; i < a.dp && i < a.nd; i++ {
+		n = n*10 + uint64(a.d[i]-'0')
+	}
+	for ; i < a.dp; i++ {
+		n *= 10
+	}
+	if shouldRoundUp(a, a.dp) {
+		n++
+	}
+	return n
+}

+ 1504 - 0
src/github.com/shopspring/decimal/decimal.go

@@ -0,0 +1,1504 @@
+// Package decimal implements an arbitrary precision fixed-point decimal.
+//
+// The zero-value of a Decimal is 0, as you would expect.
+//
+// The best way to create a new Decimal is to use decimal.NewFromString, ex:
+//
+//     n, err := decimal.NewFromString("-123.4567")
+//     n.String() // output: "-123.4567"
+//
+// To use Decimal as part of a struct:
+//
+//     type Struct struct {
+//         Number Decimal
+//     }
+//
+// Note: This can "only" represent numbers with a maximum of 2^31 digits after the decimal point.
+package decimal
+
+import (
+	"database/sql/driver"
+	"encoding/binary"
+	"fmt"
+	"math"
+	"math/big"
+	"strconv"
+	"strings"
+)
+
+// DivisionPrecision is the number of decimal places in the result when it
+// doesn't divide exactly.
+//
+// Example:
+//
+//     d1 := decimal.NewFromFloat(2).Div(decimal.NewFromFloat(3))
+//     d1.String() // output: "0.6666666666666667"
+//     d2 := decimal.NewFromFloat(2).Div(decimal.NewFromFloat(30000))
+//     d2.String() // output: "0.0000666666666667"
+//     d3 := decimal.NewFromFloat(20000).Div(decimal.NewFromFloat(3))
+//     d3.String() // output: "6666.6666666666666667"
+//     decimal.DivisionPrecision = 3
+//     d4 := decimal.NewFromFloat(2).Div(decimal.NewFromFloat(3))
+//     d4.String() // output: "0.667"
+//
+var DivisionPrecision = 16
+
+// MarshalJSONWithoutQuotes should be set to true if you want the decimal to
+// be JSON marshaled as a number, instead of as a string.
+// WARNING: this is dangerous for decimals with many digits, since many JSON
+// unmarshallers (ex: Javascript's) will unmarshal JSON numbers to IEEE 754
+// double-precision floating point numbers, which means you can potentially
+// silently lose precision.
+var MarshalJSONWithoutQuotes = false
+
+// Zero constant, to make computations faster.
+// Zero should never be compared with == or != directly, please use decimal.Equal or decimal.Cmp instead.
+var Zero = New(0, 1)
+
+var zeroInt = big.NewInt(0)
+var oneInt = big.NewInt(1)
+var twoInt = big.NewInt(2)
+var fourInt = big.NewInt(4)
+var fiveInt = big.NewInt(5)
+var tenInt = big.NewInt(10)
+var twentyInt = big.NewInt(20)
+
+// Decimal represents a fixed-point decimal. It is immutable.
+// number = value * 10 ^ exp
+type Decimal struct {
+	value *big.Int
+
+	// NOTE(vadim): this must be an int32, because we cast it to float64 during
+	// calculations. If exp is 64 bit, we might lose precision.
+	// If we cared about being able to represent every possible decimal, we
+	// could make exp a *big.Int but it would hurt performance and numbers
+	// like that are unrealistic.
+	exp int32
+}
+
+// New returns a new fixed-point decimal, value * 10 ^ exp.
+func New(value int64, exp int32) Decimal {
+	return Decimal{
+		value: big.NewInt(value),
+		exp:   exp,
+	}
+}
+
+// NewFromInt converts a int64 to Decimal.
+//
+// Example:
+//
+//     NewFromInt(123).String() // output: "123"
+//     NewFromInt(-10).String() // output: "-10"
+func NewFromInt(value int64) Decimal {
+	return Decimal{
+		value: big.NewInt(value),
+		exp:   0,
+	}
+}
+
+// NewFromInt32 converts a int32 to Decimal.
+//
+// Example:
+//
+//     NewFromInt(123).String() // output: "123"
+//     NewFromInt(-10).String() // output: "-10"
+func NewFromInt32(value int32) Decimal {
+	return Decimal{
+		value: big.NewInt(int64(value)),
+		exp:   0,
+	}
+}
+
+// NewFromBigInt returns a new Decimal from a big.Int, value * 10 ^ exp
+func NewFromBigInt(value *big.Int, exp int32) Decimal {
+	return Decimal{
+		value: big.NewInt(0).Set(value),
+		exp:   exp,
+	}
+}
+
+// NewFromString returns a new Decimal from a string representation.
+// Trailing zeroes are not trimmed.
+//
+// Example:
+//
+//     d, err := NewFromString("-123.45")
+//     d2, err := NewFromString(".0001")
+//     d3, err := NewFromString("1.47000")
+//
+func NewFromString(value string) (Decimal, error) {
+	originalInput := value
+	var intString string
+	var exp int64
+
+	// Check if number is using scientific notation
+	eIndex := strings.IndexAny(value, "Ee")
+	if eIndex != -1 {
+		expInt, err := strconv.ParseInt(value[eIndex+1:], 10, 32)
+		if err != nil {
+			if e, ok := err.(*strconv.NumError); ok && e.Err == strconv.ErrRange {
+				return Decimal{}, fmt.Errorf("can't convert %s to decimal: fractional part too long", value)
+			}
+			return Decimal{}, fmt.Errorf("can't convert %s to decimal: exponent is not numeric", value)
+		}
+		value = value[:eIndex]
+		exp = expInt
+	}
+
+	parts := strings.Split(value, ".")
+	if len(parts) == 1 {
+		// There is no decimal point, we can just parse the original string as
+		// an int
+		intString = value
+	} else if len(parts) == 2 {
+		intString = parts[0] + parts[1]
+		expInt := -len(parts[1])
+		exp += int64(expInt)
+	} else {
+		return Decimal{}, fmt.Errorf("can't convert %s to decimal: too many .s", value)
+	}
+
+	dValue := new(big.Int)
+	_, ok := dValue.SetString(intString, 10)
+	if !ok {
+		return Decimal{}, fmt.Errorf("can't convert %s to decimal", value)
+	}
+
+	if exp < math.MinInt32 || exp > math.MaxInt32 {
+		// NOTE(vadim): I doubt a string could realistically be this long
+		return Decimal{}, fmt.Errorf("can't convert %s to decimal: fractional part too long", originalInput)
+	}
+
+	return Decimal{
+		value: dValue,
+		exp:   int32(exp),
+	}, nil
+}
+
+// RequireFromString returns a new Decimal from a string representation
+// or panics if NewFromString would have returned an error.
+//
+// Example:
+//
+//     d := RequireFromString("-123.45")
+//     d2 := RequireFromString(".0001")
+//
+func RequireFromString(value string) Decimal {
+	dec, err := NewFromString(value)
+	if err != nil {
+		panic(err)
+	}
+	return dec
+}
+
+// NewFromFloat converts a float64 to Decimal.
+//
+// The converted number will contain the number of significant digits that can be
+// represented in a float with reliable roundtrip.
+// This is typically 15 digits, but may be more in some cases.
+// See https://www.exploringbinary.com/decimal-precision-of-binary-floating-point-numbers/ for more information.
+//
+// For slightly faster conversion, use NewFromFloatWithExponent where you can specify the precision in absolute terms.
+//
+// NOTE: this will panic on NaN, +/-inf
+func NewFromFloat(value float64) Decimal {
+	if value == 0 {
+		return New(0, 0)
+	}
+	return newFromFloat(value, math.Float64bits(value), &float64info)
+}
+
+// NewFromFloat32 converts a float32 to Decimal.
+//
+// The converted number will contain the number of significant digits that can be
+// represented in a float with reliable roundtrip.
+// This is typically 6-8 digits depending on the input.
+// See https://www.exploringbinary.com/decimal-precision-of-binary-floating-point-numbers/ for more information.
+//
+// For slightly faster conversion, use NewFromFloatWithExponent where you can specify the precision in absolute terms.
+//
+// NOTE: this will panic on NaN, +/-inf
+func NewFromFloat32(value float32) Decimal {
+	if value == 0 {
+		return New(0, 0)
+	}
+	// XOR is workaround for https://github.com/golang/go/issues/26285
+	a := math.Float32bits(value) ^ 0x80808080
+	return newFromFloat(float64(value), uint64(a)^0x80808080, &float32info)
+}
+
+func newFromFloat(val float64, bits uint64, flt *floatInfo) Decimal {
+	if math.IsNaN(val) || math.IsInf(val, 0) {
+		panic(fmt.Sprintf("Cannot create a Decimal from %v", val))
+	}
+	exp := int(bits>>flt.mantbits) & (1<<flt.expbits - 1)
+	mant := bits & (uint64(1)<<flt.mantbits - 1)
+
+	switch exp {
+	case 0:
+		// denormalized
+		exp++
+
+	default:
+		// add implicit top bit
+		mant |= uint64(1) << flt.mantbits
+	}
+	exp += flt.bias
+
+	var d decimal
+	d.Assign(mant)
+	d.Shift(exp - int(flt.mantbits))
+	d.neg = bits>>(flt.expbits+flt.mantbits) != 0
+
+	roundShortest(&d, mant, exp, flt)
+	// If less than 19 digits, we can do calculation in an int64.
+	if d.nd < 19 {
+		tmp := int64(0)
+		m := int64(1)
+		for i := d.nd - 1; i >= 0; i-- {
+			tmp += m * int64(d.d[i]-'0')
+			m *= 10
+		}
+		if d.neg {
+			tmp *= -1
+		}
+		return Decimal{value: big.NewInt(tmp), exp: int32(d.dp) - int32(d.nd)}
+	}
+	dValue := new(big.Int)
+	dValue, ok := dValue.SetString(string(d.d[:d.nd]), 10)
+	if ok {
+		return Decimal{value: dValue, exp: int32(d.dp) - int32(d.nd)}
+	}
+
+	return NewFromFloatWithExponent(val, int32(d.dp)-int32(d.nd))
+}
+
+// NewFromFloatWithExponent converts a float64 to Decimal, with an arbitrary
+// number of fractional digits.
+//
+// Example:
+//
+//     NewFromFloatWithExponent(123.456, -2).String() // output: "123.46"
+//
+func NewFromFloatWithExponent(value float64, exp int32) Decimal {
+	if math.IsNaN(value) || math.IsInf(value, 0) {
+		panic(fmt.Sprintf("Cannot create a Decimal from %v", value))
+	}
+
+	bits := math.Float64bits(value)
+	mant := bits & (1<<52 - 1)
+	exp2 := int32((bits >> 52) & (1<<11 - 1))
+	sign := bits >> 63
+
+	if exp2 == 0 {
+		// specials
+		if mant == 0 {
+			return Decimal{}
+		}
+		// subnormal
+		exp2++
+	} else {
+		// normal
+		mant |= 1 << 52
+	}
+
+	exp2 -= 1023 + 52
+
+	// normalizing base-2 values
+	for mant&1 == 0 {
+		mant = mant >> 1
+		exp2++
+	}
+
+	// maximum number of fractional base-10 digits to represent 2^N exactly cannot be more than -N if N<0
+	if exp < 0 && exp < exp2 {
+		if exp2 < 0 {
+			exp = exp2
+		} else {
+			exp = 0
+		}
+	}
+
+	// representing 10^M * 2^N as 5^M * 2^(M+N)
+	exp2 -= exp
+
+	temp := big.NewInt(1)
+	dMant := big.NewInt(int64(mant))
+
+	// applying 5^M
+	if exp > 0 {
+		temp = temp.SetInt64(int64(exp))
+		temp = temp.Exp(fiveInt, temp, nil)
+	} else if exp < 0 {
+		temp = temp.SetInt64(-int64(exp))
+		temp = temp.Exp(fiveInt, temp, nil)
+		dMant = dMant.Mul(dMant, temp)
+		temp = temp.SetUint64(1)
+	}
+
+	// applying 2^(M+N)
+	if exp2 > 0 {
+		dMant = dMant.Lsh(dMant, uint(exp2))
+	} else if exp2 < 0 {
+		temp = temp.Lsh(temp, uint(-exp2))
+	}
+
+	// rounding and downscaling
+	if exp > 0 || exp2 < 0 {
+		halfDown := new(big.Int).Rsh(temp, 1)
+		dMant = dMant.Add(dMant, halfDown)
+		dMant = dMant.Quo(dMant, temp)
+	}
+
+	if sign == 1 {
+		dMant = dMant.Neg(dMant)
+	}
+
+	return Decimal{
+		value: dMant,
+		exp:   exp,
+	}
+}
+
+// rescale returns a rescaled version of the decimal. Returned
+// decimal may be less precise if the given exponent is bigger
+// than the initial exponent of the Decimal.
+// NOTE: this will truncate, NOT round
+//
+// Example:
+//
+// 	d := New(12345, -4)
+//	d2 := d.rescale(-1)
+//	d3 := d2.rescale(-4)
+//	println(d1)
+//	println(d2)
+//	println(d3)
+//
+// Output:
+//
+//	1.2345
+//	1.2
+//	1.2000
+//
+func (d Decimal) rescale(exp int32) Decimal {
+	d.ensureInitialized()
+
+	if d.exp == exp {
+		return Decimal{
+			new(big.Int).Set(d.value),
+			d.exp,
+		}
+	}
+
+	// NOTE(vadim): must convert exps to float64 before - to prevent overflow
+	diff := math.Abs(float64(exp) - float64(d.exp))
+	value := new(big.Int).Set(d.value)
+
+	expScale := new(big.Int).Exp(tenInt, big.NewInt(int64(diff)), nil)
+	if exp > d.exp {
+		value = value.Quo(value, expScale)
+	} else if exp < d.exp {
+		value = value.Mul(value, expScale)
+	}
+
+	return Decimal{
+		value: value,
+		exp:   exp,
+	}
+}
+
+// Abs returns the absolute value of the decimal.
+func (d Decimal) Abs() Decimal {
+	d.ensureInitialized()
+	d2Value := new(big.Int).Abs(d.value)
+	return Decimal{
+		value: d2Value,
+		exp:   d.exp,
+	}
+}
+
+// Add returns d + d2.
+func (d Decimal) Add(d2 Decimal) Decimal {
+	rd, rd2 := RescalePair(d, d2)
+
+	d3Value := new(big.Int).Add(rd.value, rd2.value)
+	return Decimal{
+		value: d3Value,
+		exp:   rd.exp,
+	}
+}
+
+// Sub returns d - d2.
+func (d Decimal) Sub(d2 Decimal) Decimal {
+	rd, rd2 := RescalePair(d, d2)
+
+	d3Value := new(big.Int).Sub(rd.value, rd2.value)
+	return Decimal{
+		value: d3Value,
+		exp:   rd.exp,
+	}
+}
+
+// Neg returns -d.
+func (d Decimal) Neg() Decimal {
+	d.ensureInitialized()
+	val := new(big.Int).Neg(d.value)
+	return Decimal{
+		value: val,
+		exp:   d.exp,
+	}
+}
+
+// Mul returns d * d2.
+func (d Decimal) Mul(d2 Decimal) Decimal {
+	d.ensureInitialized()
+	d2.ensureInitialized()
+
+	expInt64 := int64(d.exp) + int64(d2.exp)
+	if expInt64 > math.MaxInt32 || expInt64 < math.MinInt32 {
+		// NOTE(vadim): better to panic than give incorrect results, as
+		// Decimals are usually used for money
+		panic(fmt.Sprintf("exponent %v overflows an int32!", expInt64))
+	}
+
+	d3Value := new(big.Int).Mul(d.value, d2.value)
+	return Decimal{
+		value: d3Value,
+		exp:   int32(expInt64),
+	}
+}
+
+// Shift shifts the decimal in base 10.
+// It shifts left when shift is positive and right if shift is negative.
+// In simpler terms, the given value for shift is added to the exponent
+// of the decimal.
+func (d Decimal) Shift(shift int32) Decimal {
+	d.ensureInitialized()
+	return Decimal{
+		value: new(big.Int).Set(d.value),
+		exp:   d.exp + shift,
+	}
+}
+
+// Div returns d / d2. If it doesn't divide exactly, the result will have
+// DivisionPrecision digits after the decimal point.
+func (d Decimal) Div(d2 Decimal) Decimal {
+	return d.DivRound(d2, int32(DivisionPrecision))
+}
+
+// QuoRem does divsion with remainder
+// d.QuoRem(d2,precision) returns quotient q and remainder r such that
+//   d = d2 * q + r, q an integer multiple of 10^(-precision)
+//   0 <= r < abs(d2) * 10 ^(-precision) if d>=0
+//   0 >= r > -abs(d2) * 10 ^(-precision) if d<0
+// Note that precision<0 is allowed as input.
+func (d Decimal) QuoRem(d2 Decimal, precision int32) (Decimal, Decimal) {
+	d.ensureInitialized()
+	d2.ensureInitialized()
+	if d2.value.Sign() == 0 {
+		panic("decimal division by 0")
+	}
+	scale := -precision
+	e := int64(d.exp - d2.exp - scale)
+	if e > math.MaxInt32 || e < math.MinInt32 {
+		panic("overflow in decimal QuoRem")
+	}
+	var aa, bb, expo big.Int
+	var scalerest int32
+	// d = a 10^ea
+	// d2 = b 10^eb
+	if e < 0 {
+		aa = *d.value
+		expo.SetInt64(-e)
+		bb.Exp(tenInt, &expo, nil)
+		bb.Mul(d2.value, &bb)
+		scalerest = d.exp
+		// now aa = a
+		//     bb = b 10^(scale + eb - ea)
+	} else {
+		expo.SetInt64(e)
+		aa.Exp(tenInt, &expo, nil)
+		aa.Mul(d.value, &aa)
+		bb = *d2.value
+		scalerest = scale + d2.exp
+		// now aa = a ^ (ea - eb - scale)
+		//     bb = b
+	}
+	var q, r big.Int
+	q.QuoRem(&aa, &bb, &r)
+	dq := Decimal{value: &q, exp: scale}
+	dr := Decimal{value: &r, exp: scalerest}
+	return dq, dr
+}
+
+// DivRound divides and rounds to a given precision
+// i.e. to an integer multiple of 10^(-precision)
+//   for a positive quotient digit 5 is rounded up, away from 0
+//   if the quotient is negative then digit 5 is rounded down, away from 0
+// Note that precision<0 is allowed as input.
+func (d Decimal) DivRound(d2 Decimal, precision int32) Decimal {
+	// QuoRem already checks initialization
+	q, r := d.QuoRem(d2, precision)
+	// the actual rounding decision is based on comparing r*10^precision and d2/2
+	// instead compare 2 r 10 ^precision and d2
+	var rv2 big.Int
+	rv2.Abs(r.value)
+	rv2.Lsh(&rv2, 1)
+	// now rv2 = abs(r.value) * 2
+	r2 := Decimal{value: &rv2, exp: r.exp + precision}
+	// r2 is now 2 * r * 10 ^ precision
+	var c = r2.Cmp(d2.Abs())
+
+	if c < 0 {
+		return q
+	}
+
+	if d.value.Sign()*d2.value.Sign() < 0 {
+		return q.Sub(New(1, -precision))
+	}
+
+	return q.Add(New(1, -precision))
+}
+
+// Mod returns d % d2.
+func (d Decimal) Mod(d2 Decimal) Decimal {
+	quo := d.Div(d2).Truncate(0)
+	return d.Sub(d2.Mul(quo))
+}
+
+// Pow returns d to the power d2
+func (d Decimal) Pow(d2 Decimal) Decimal {
+	var temp Decimal
+	if d2.IntPart() == 0 {
+		return NewFromFloat(1)
+	}
+	temp = d.Pow(d2.Div(NewFromFloat(2)))
+	if d2.IntPart()%2 == 0 {
+		return temp.Mul(temp)
+	}
+	if d2.IntPart() > 0 {
+		return temp.Mul(temp).Mul(d)
+	}
+	return temp.Mul(temp).Div(d)
+}
+
+// IsInteger returns true when decimal can be represented as an integer value, otherwise, it returns false.
+func (d Decimal) IsInteger() bool {
+	// The most typical case, all decimal with exponent higher or equal 0 can be represented as integer
+	if d.exp >= 0 {
+		return true
+	}
+	// When the exponent is negative we have to check every number after the decimal place
+	// If all of them are zeroes, we are sure that given decimal can be represented as an integer
+	var r big.Int
+	q := big.NewInt(0).Set(d.value)
+	for z := abs(d.exp); z > 0; z-- {
+		q.QuoRem(q, tenInt, &r)
+		if r.Cmp(zeroInt) != 0 {
+			return false
+		}
+	}
+	return true
+}
+
+// Abs calculates absolute value of any int32. Used for calculating absolute value of decimal's exponent.
+func abs(n int32) int32 {
+	if n < 0 {
+		return -n
+	}
+	return n
+}
+
+// Cmp compares the numbers represented by d and d2 and returns:
+//
+//     -1 if d <  d2
+//      0 if d == d2
+//     +1 if d >  d2
+//
+func (d Decimal) Cmp(d2 Decimal) int {
+	d.ensureInitialized()
+	d2.ensureInitialized()
+
+	if d.exp == d2.exp {
+		return d.value.Cmp(d2.value)
+	}
+
+	rd, rd2 := RescalePair(d, d2)
+
+	return rd.value.Cmp(rd2.value)
+}
+
+// Equal returns whether the numbers represented by d and d2 are equal.
+func (d Decimal) Equal(d2 Decimal) bool {
+	return d.Cmp(d2) == 0
+}
+
+// Equals is deprecated, please use Equal method instead
+func (d Decimal) Equals(d2 Decimal) bool {
+	return d.Equal(d2)
+}
+
+// GreaterThan (GT) returns true when d is greater than d2.
+func (d Decimal) GreaterThan(d2 Decimal) bool {
+	return d.Cmp(d2) == 1
+}
+
+// GreaterThanOrEqual (GTE) returns true when d is greater than or equal to d2.
+func (d Decimal) GreaterThanOrEqual(d2 Decimal) bool {
+	cmp := d.Cmp(d2)
+	return cmp == 1 || cmp == 0
+}
+
+// LessThan (LT) returns true when d is less than d2.
+func (d Decimal) LessThan(d2 Decimal) bool {
+	return d.Cmp(d2) == -1
+}
+
+// LessThanOrEqual (LTE) returns true when d is less than or equal to d2.
+func (d Decimal) LessThanOrEqual(d2 Decimal) bool {
+	cmp := d.Cmp(d2)
+	return cmp == -1 || cmp == 0
+}
+
+// Sign returns:
+//
+//	-1 if d <  0
+//	 0 if d == 0
+//	+1 if d >  0
+//
+func (d Decimal) Sign() int {
+	if d.value == nil {
+		return 0
+	}
+	return d.value.Sign()
+}
+
+// IsPositive return
+//
+//	true if d > 0
+//	false if d == 0
+//	false if d < 0
+func (d Decimal) IsPositive() bool {
+	return d.Sign() == 1
+}
+
+// IsNegative return
+//
+//	true if d < 0
+//	false if d == 0
+//	false if d > 0
+func (d Decimal) IsNegative() bool {
+	return d.Sign() == -1
+}
+
+// IsZero return
+//
+//	true if d == 0
+//	false if d > 0
+//	false if d < 0
+func (d Decimal) IsZero() bool {
+	return d.Sign() == 0
+}
+
+// Exponent returns the exponent, or scale component of the decimal.
+func (d Decimal) Exponent() int32 {
+	return d.exp
+}
+
+// Coefficient returns the coefficient of the decimal.  It is scaled by 10^Exponent()
+func (d Decimal) Coefficient() *big.Int {
+	d.ensureInitialized()
+	// we copy the coefficient so that mutating the result does not mutate the
+	// Decimal.
+	return big.NewInt(0).Set(d.value)
+}
+
+// IntPart returns the integer component of the decimal.
+func (d Decimal) IntPart() int64 {
+	scaledD := d.rescale(0)
+	return scaledD.value.Int64()
+}
+
+// BigInt returns integer component of the decimal as a BigInt.
+func (d Decimal) BigInt() *big.Int {
+	scaledD := d.rescale(0)
+	i := &big.Int{}
+	i.SetString(scaledD.String(), 10)
+	return i
+}
+
+// BigFloat returns decimal as BigFloat.
+// Be aware that casting decimal to BigFloat might cause a loss of precision.
+func (d Decimal) BigFloat() *big.Float {
+	f := &big.Float{}
+	f.SetString(d.String())
+	return f
+}
+
+// Rat returns a rational number representation of the decimal.
+func (d Decimal) Rat() *big.Rat {
+	d.ensureInitialized()
+	if d.exp <= 0 {
+		// NOTE(vadim): must negate after casting to prevent int32 overflow
+		denom := new(big.Int).Exp(tenInt, big.NewInt(-int64(d.exp)), nil)
+		return new(big.Rat).SetFrac(d.value, denom)
+	}
+
+	mul := new(big.Int).Exp(tenInt, big.NewInt(int64(d.exp)), nil)
+	num := new(big.Int).Mul(d.value, mul)
+	return new(big.Rat).SetFrac(num, oneInt)
+}
+
+// Float64 returns the nearest float64 value for d and a bool indicating
+// whether f represents d exactly.
+// For more details, see the documentation for big.Rat.Float64
+func (d Decimal) Float64() (f float64, exact bool) {
+	return d.Rat().Float64()
+}
+
+// String returns the string representation of the decimal
+// with the fixed point.
+//
+// Example:
+//
+//     d := New(-12345, -3)
+//     println(d.String())
+//
+// Output:
+//
+//     -12.345
+//
+func (d Decimal) String() string {
+	return d.string(true)
+}
+
+// StringFixed returns a rounded fixed-point string with places digits after
+// the decimal point.
+//
+// Example:
+//
+// 	   NewFromFloat(0).StringFixed(2) // output: "0.00"
+// 	   NewFromFloat(0).StringFixed(0) // output: "0"
+// 	   NewFromFloat(5.45).StringFixed(0) // output: "5"
+// 	   NewFromFloat(5.45).StringFixed(1) // output: "5.5"
+// 	   NewFromFloat(5.45).StringFixed(2) // output: "5.45"
+// 	   NewFromFloat(5.45).StringFixed(3) // output: "5.450"
+// 	   NewFromFloat(545).StringFixed(-1) // output: "550"
+//
+func (d Decimal) StringFixed(places int32) string {
+	rounded := d.Round(places)
+	return rounded.string(false)
+}
+
+// StringFixedBank returns a banker rounded fixed-point string with places digits
+// after the decimal point.
+//
+// Example:
+//
+// 	   NewFromFloat(0).StringFixedBank(2) // output: "0.00"
+// 	   NewFromFloat(0).StringFixedBank(0) // output: "0"
+// 	   NewFromFloat(5.45).StringFixedBank(0) // output: "5"
+// 	   NewFromFloat(5.45).StringFixedBank(1) // output: "5.4"
+// 	   NewFromFloat(5.45).StringFixedBank(2) // output: "5.45"
+// 	   NewFromFloat(5.45).StringFixedBank(3) // output: "5.450"
+// 	   NewFromFloat(545).StringFixedBank(-1) // output: "540"
+//
+func (d Decimal) StringFixedBank(places int32) string {
+	rounded := d.RoundBank(places)
+	return rounded.string(false)
+}
+
+// StringFixedCash returns a Swedish/Cash rounded fixed-point string. For
+// more details see the documentation at function RoundCash.
+func (d Decimal) StringFixedCash(interval uint8) string {
+	rounded := d.RoundCash(interval)
+	return rounded.string(false)
+}
+
+// Round rounds the decimal to places decimal places.
+// If places < 0, it will round the integer part to the nearest 10^(-places).
+//
+// Example:
+//
+// 	   NewFromFloat(5.45).Round(1).String() // output: "5.5"
+// 	   NewFromFloat(545).Round(-1).String() // output: "550"
+//
+func (d Decimal) Round(places int32) Decimal {
+	// truncate to places + 1
+	ret := d.rescale(-places - 1)
+
+	// add sign(d) * 0.5
+	if ret.value.Sign() < 0 {
+		ret.value.Sub(ret.value, fiveInt)
+	} else {
+		ret.value.Add(ret.value, fiveInt)
+	}
+
+	// floor for positive numbers, ceil for negative numbers
+	_, m := ret.value.DivMod(ret.value, tenInt, new(big.Int))
+	ret.exp++
+	if ret.value.Sign() < 0 && m.Cmp(zeroInt) != 0 {
+		ret.value.Add(ret.value, oneInt)
+	}
+
+	return ret
+}
+
+// RoundBank rounds the decimal to places decimal places.
+// If the final digit to round is equidistant from the nearest two integers the
+// rounded value is taken as the even number
+//
+// If places < 0, it will round the integer part to the nearest 10^(-places).
+//
+// Examples:
+//
+// 	   NewFromFloat(5.45).Round(1).String() // output: "5.4"
+// 	   NewFromFloat(545).Round(-1).String() // output: "540"
+// 	   NewFromFloat(5.46).Round(1).String() // output: "5.5"
+// 	   NewFromFloat(546).Round(-1).String() // output: "550"
+// 	   NewFromFloat(5.55).Round(1).String() // output: "5.6"
+// 	   NewFromFloat(555).Round(-1).String() // output: "560"
+//
+func (d Decimal) RoundBank(places int32) Decimal {
+
+	round := d.Round(places)
+	remainder := d.Sub(round).Abs()
+
+	half := New(5, -places-1)
+	if remainder.Cmp(half) == 0 && round.value.Bit(0) != 0 {
+		if round.value.Sign() < 0 {
+			round.value.Add(round.value, oneInt)
+		} else {
+			round.value.Sub(round.value, oneInt)
+		}
+	}
+
+	return round
+}
+
+// RoundCash aka Cash/Penny/öre rounding rounds decimal to a specific
+// interval. The amount payable for a cash transaction is rounded to the nearest
+// multiple of the minimum currency unit available. The following intervals are
+// available: 5, 10, 25, 50 and 100; any other number throws a panic.
+//	    5:   5 cent rounding 3.43 => 3.45
+// 	   10:  10 cent rounding 3.45 => 3.50 (5 gets rounded up)
+// 	   25:  25 cent rounding 3.41 => 3.50
+// 	   50:  50 cent rounding 3.75 => 4.00
+// 	  100: 100 cent rounding 3.50 => 4.00
+// For more details: https://en.wikipedia.org/wiki/Cash_rounding
+func (d Decimal) RoundCash(interval uint8) Decimal {
+	var iVal *big.Int
+	switch interval {
+	case 5:
+		iVal = twentyInt
+	case 10:
+		iVal = tenInt
+	case 25:
+		iVal = fourInt
+	case 50:
+		iVal = twoInt
+	case 100:
+		iVal = oneInt
+	default:
+		panic(fmt.Sprintf("Decimal does not support this Cash rounding interval `%d`. Supported: 5, 10, 25, 50, 100", interval))
+	}
+	dVal := Decimal{
+		value: iVal,
+	}
+
+	// TODO: optimize those calculations to reduce the high allocations (~29 allocs).
+	return d.Mul(dVal).Round(0).Div(dVal).Truncate(2)
+}
+
+// Floor returns the nearest integer value less than or equal to d.
+func (d Decimal) Floor() Decimal {
+	d.ensureInitialized()
+
+	if d.exp >= 0 {
+		return d
+	}
+
+	exp := big.NewInt(10)
+
+	// NOTE(vadim): must negate after casting to prevent int32 overflow
+	exp.Exp(exp, big.NewInt(-int64(d.exp)), nil)
+
+	z := new(big.Int).Div(d.value, exp)
+	return Decimal{value: z, exp: 0}
+}
+
+// Ceil returns the nearest integer value greater than or equal to d.
+func (d Decimal) Ceil() Decimal {
+	d.ensureInitialized()
+
+	if d.exp >= 0 {
+		return d
+	}
+
+	exp := big.NewInt(10)
+
+	// NOTE(vadim): must negate after casting to prevent int32 overflow
+	exp.Exp(exp, big.NewInt(-int64(d.exp)), nil)
+
+	z, m := new(big.Int).DivMod(d.value, exp, new(big.Int))
+	if m.Cmp(zeroInt) != 0 {
+		z.Add(z, oneInt)
+	}
+	return Decimal{value: z, exp: 0}
+}
+
+// Truncate truncates off digits from the number, without rounding.
+//
+// NOTE: precision is the last digit that will not be truncated (must be >= 0).
+//
+// Example:
+//
+//     decimal.NewFromString("123.456").Truncate(2).String() // "123.45"
+//
+func (d Decimal) Truncate(precision int32) Decimal {
+	d.ensureInitialized()
+	if precision >= 0 && -precision > d.exp {
+		return d.rescale(-precision)
+	}
+	return d
+}
+
+// UnmarshalJSON implements the json.Unmarshaler interface.
+func (d *Decimal) UnmarshalJSON(decimalBytes []byte) error {
+	if string(decimalBytes) == "null" {
+		return nil
+	}
+
+	str, err := unquoteIfQuoted(decimalBytes)
+	if err != nil {
+		return fmt.Errorf("error decoding string '%s': %s", decimalBytes, err)
+	}
+
+	decimal, err := NewFromString(str)
+	*d = decimal
+	if err != nil {
+		return fmt.Errorf("error decoding string '%s': %s", str, err)
+	}
+	return nil
+}
+
+// MarshalJSON implements the json.Marshaler interface.
+func (d Decimal) MarshalJSON() ([]byte, error) {
+	var str string
+	if MarshalJSONWithoutQuotes {
+		str = d.String()
+	} else {
+		str = "\"" + d.String() + "\""
+	}
+	return []byte(str), nil
+}
+
+// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface. As a string representation
+// is already used when encoding to text, this method stores that string as []byte
+func (d *Decimal) UnmarshalBinary(data []byte) error {
+	// Extract the exponent
+	d.exp = int32(binary.BigEndian.Uint32(data[:4]))
+
+	// Extract the value
+	d.value = new(big.Int)
+	return d.value.GobDecode(data[4:])
+}
+
+// MarshalBinary implements the encoding.BinaryMarshaler interface.
+func (d Decimal) MarshalBinary() (data []byte, err error) {
+	// Write the exponent first since it's a fixed size
+	v1 := make([]byte, 4)
+	binary.BigEndian.PutUint32(v1, uint32(d.exp))
+
+	// Add the value
+	var v2 []byte
+	if v2, err = d.value.GobEncode(); err != nil {
+		return
+	}
+
+	// Return the byte array
+	data = append(v1, v2...)
+	return
+}
+
+// Scan implements the sql.Scanner interface for database deserialization.
+func (d *Decimal) Scan(value interface{}) error {
+	// first try to see if the data is stored in database as a Numeric datatype
+	switch v := value.(type) {
+
+	case float32:
+		*d = NewFromFloat(float64(v))
+		return nil
+
+	case float64:
+		// numeric in sqlite3 sends us float64
+		*d = NewFromFloat(v)
+		return nil
+
+	case int64:
+		// at least in sqlite3 when the value is 0 in db, the data is sent
+		// to us as an int64 instead of a float64 ...
+		*d = New(v, 0)
+		return nil
+
+	default:
+		// default is trying to interpret value stored as string
+		str, err := unquoteIfQuoted(v)
+		if err != nil {
+			return err
+		}
+		*d, err = NewFromString(str)
+		return err
+	}
+}
+
+// Value implements the driver.Valuer interface for database serialization.
+func (d Decimal) Value() (driver.Value, error) {
+	return d.String(), nil
+}
+
+// UnmarshalText implements the encoding.TextUnmarshaler interface for XML
+// deserialization.
+func (d *Decimal) UnmarshalText(text []byte) error {
+	str := string(text)
+
+	dec, err := NewFromString(str)
+	*d = dec
+	if err != nil {
+		return fmt.Errorf("error decoding string '%s': %s", str, err)
+	}
+
+	return nil
+}
+
+// MarshalText implements the encoding.TextMarshaler interface for XML
+// serialization.
+func (d Decimal) MarshalText() (text []byte, err error) {
+	return []byte(d.String()), nil
+}
+
+// GobEncode implements the gob.GobEncoder interface for gob serialization.
+func (d Decimal) GobEncode() ([]byte, error) {
+	return d.MarshalBinary()
+}
+
+// GobDecode implements the gob.GobDecoder interface for gob serialization.
+func (d *Decimal) GobDecode(data []byte) error {
+	return d.UnmarshalBinary(data)
+}
+
+// StringScaled first scales the decimal then calls .String() on it.
+// NOTE: buggy, unintuitive, and DEPRECATED! Use StringFixed instead.
+func (d Decimal) StringScaled(exp int32) string {
+	return d.rescale(exp).String()
+}
+
+func (d Decimal) string(trimTrailingZeros bool) string {
+	if d.exp >= 0 {
+		return d.rescale(0).value.String()
+	}
+
+	abs := new(big.Int).Abs(d.value)
+	str := abs.String()
+
+	var intPart, fractionalPart string
+
+	// NOTE(vadim): this cast to int will cause bugs if d.exp == INT_MIN
+	// and you are on a 32-bit machine. Won't fix this super-edge case.
+	dExpInt := int(d.exp)
+	if len(str) > -dExpInt {
+		intPart = str[:len(str)+dExpInt]
+		fractionalPart = str[len(str)+dExpInt:]
+	} else {
+		intPart = "0"
+
+		num0s := -dExpInt - len(str)
+		fractionalPart = strings.Repeat("0", num0s) + str
+	}
+
+	if trimTrailingZeros {
+		i := len(fractionalPart) - 1
+		for ; i >= 0; i-- {
+			if fractionalPart[i] != '0' {
+				break
+			}
+		}
+		fractionalPart = fractionalPart[:i+1]
+	}
+
+	number := intPart
+	if len(fractionalPart) > 0 {
+		number += "." + fractionalPart
+	}
+
+	if d.value.Sign() < 0 {
+		return "-" + number
+	}
+
+	return number
+}
+
+func (d *Decimal) ensureInitialized() {
+	if d.value == nil {
+		d.value = new(big.Int)
+	}
+}
+
+// Min returns the smallest Decimal that was passed in the arguments.
+//
+// To call this function with an array, you must do:
+//
+//     Min(arr[0], arr[1:]...)
+//
+// This makes it harder to accidentally call Min with 0 arguments.
+func Min(first Decimal, rest ...Decimal) Decimal {
+	ans := first
+	for _, item := range rest {
+		if item.Cmp(ans) < 0 {
+			ans = item
+		}
+	}
+	return ans
+}
+
+// Max returns the largest Decimal that was passed in the arguments.
+//
+// To call this function with an array, you must do:
+//
+//     Max(arr[0], arr[1:]...)
+//
+// This makes it harder to accidentally call Max with 0 arguments.
+func Max(first Decimal, rest ...Decimal) Decimal {
+	ans := first
+	for _, item := range rest {
+		if item.Cmp(ans) > 0 {
+			ans = item
+		}
+	}
+	return ans
+}
+
+// Sum returns the combined total of the provided first and rest Decimals
+func Sum(first Decimal, rest ...Decimal) Decimal {
+	total := first
+	for _, item := range rest {
+		total = total.Add(item)
+	}
+
+	return total
+}
+
+// Avg returns the average value of the provided first and rest Decimals
+func Avg(first Decimal, rest ...Decimal) Decimal {
+	count := New(int64(len(rest)+1), 0)
+	sum := Sum(first, rest...)
+	return sum.Div(count)
+}
+
+// RescalePair rescales two decimals to common exponential value (minimal exp of both decimals)
+func RescalePair(d1 Decimal, d2 Decimal) (Decimal, Decimal) {
+	d1.ensureInitialized()
+	d2.ensureInitialized()
+
+	if d1.exp == d2.exp {
+		return d1, d2
+	}
+
+	baseScale := min(d1.exp, d2.exp)
+	if baseScale != d1.exp {
+		return d1.rescale(baseScale), d2
+	}
+	return d1, d2.rescale(baseScale)
+}
+
+func min(x, y int32) int32 {
+	if x >= y {
+		return y
+	}
+	return x
+}
+
+func unquoteIfQuoted(value interface{}) (string, error) {
+	var bytes []byte
+
+	switch v := value.(type) {
+	case string:
+		bytes = []byte(v)
+	case []byte:
+		bytes = v
+	default:
+		return "", fmt.Errorf("could not convert value '%+v' to byte array of type '%T'",
+			value, value)
+	}
+
+	// If the amount is quoted, strip the quotes
+	if len(bytes) > 2 && bytes[0] == '"' && bytes[len(bytes)-1] == '"' {
+		bytes = bytes[1 : len(bytes)-1]
+	}
+	return string(bytes), nil
+}
+
+// NullDecimal represents a nullable decimal with compatibility for
+// scanning null values from the database.
+type NullDecimal struct {
+	Decimal Decimal
+	Valid   bool
+}
+
+// Scan implements the sql.Scanner interface for database deserialization.
+func (d *NullDecimal) Scan(value interface{}) error {
+	if value == nil {
+		d.Valid = false
+		return nil
+	}
+	d.Valid = true
+	return d.Decimal.Scan(value)
+}
+
+// Value implements the driver.Valuer interface for database serialization.
+func (d NullDecimal) Value() (driver.Value, error) {
+	if !d.Valid {
+		return nil, nil
+	}
+	return d.Decimal.Value()
+}
+
+// UnmarshalJSON implements the json.Unmarshaler interface.
+func (d *NullDecimal) UnmarshalJSON(decimalBytes []byte) error {
+	if string(decimalBytes) == "null" {
+		d.Valid = false
+		return nil
+	}
+	d.Valid = true
+	return d.Decimal.UnmarshalJSON(decimalBytes)
+}
+
+// MarshalJSON implements the json.Marshaler interface.
+func (d NullDecimal) MarshalJSON() ([]byte, error) {
+	if !d.Valid {
+		return []byte("null"), nil
+	}
+	return d.Decimal.MarshalJSON()
+}
+
+// Trig functions
+
+// Atan returns the arctangent, in radians, of x.
+func (d Decimal) Atan() Decimal {
+	if d.Equal(NewFromFloat(0.0)) {
+		return d
+	}
+	if d.GreaterThan(NewFromFloat(0.0)) {
+		return d.satan()
+	}
+	return d.Neg().satan().Neg()
+}
+
+func (d Decimal) xatan() Decimal {
+	P0 := NewFromFloat(-8.750608600031904122785e-01)
+	P1 := NewFromFloat(-1.615753718733365076637e+01)
+	P2 := NewFromFloat(-7.500855792314704667340e+01)
+	P3 := NewFromFloat(-1.228866684490136173410e+02)
+	P4 := NewFromFloat(-6.485021904942025371773e+01)
+	Q0 := NewFromFloat(2.485846490142306297962e+01)
+	Q1 := NewFromFloat(1.650270098316988542046e+02)
+	Q2 := NewFromFloat(4.328810604912902668951e+02)
+	Q3 := NewFromFloat(4.853903996359136964868e+02)
+	Q4 := NewFromFloat(1.945506571482613964425e+02)
+	z := d.Mul(d)
+	b1 := P0.Mul(z).Add(P1).Mul(z).Add(P2).Mul(z).Add(P3).Mul(z).Add(P4).Mul(z)
+	b2 := z.Add(Q0).Mul(z).Add(Q1).Mul(z).Add(Q2).Mul(z).Add(Q3).Mul(z).Add(Q4)
+	z = b1.Div(b2)
+	z = d.Mul(z).Add(d)
+	return z
+}
+
+// satan reduces its argument (known to be positive)
+// to the range [0, 0.66] and calls xatan.
+func (d Decimal) satan() Decimal {
+	Morebits := NewFromFloat(6.123233995736765886130e-17) // pi/2 = PIO2 + Morebits
+	Tan3pio8 := NewFromFloat(2.41421356237309504880)      // tan(3*pi/8)
+	pi := NewFromFloat(3.14159265358979323846264338327950288419716939937510582097494459)
+
+	if d.LessThanOrEqual(NewFromFloat(0.66)) {
+		return d.xatan()
+	}
+	if d.GreaterThan(Tan3pio8) {
+		return pi.Div(NewFromFloat(2.0)).Sub(NewFromFloat(1.0).Div(d).xatan()).Add(Morebits)
+	}
+	return pi.Div(NewFromFloat(4.0)).Add((d.Sub(NewFromFloat(1.0)).Div(d.Add(NewFromFloat(1.0)))).xatan()).Add(NewFromFloat(0.5).Mul(Morebits))
+}
+
+// sin coefficients
+var _sin = [...]Decimal{
+	NewFromFloat(1.58962301576546568060e-10), // 0x3de5d8fd1fd19ccd
+	NewFromFloat(-2.50507477628578072866e-8), // 0xbe5ae5e5a9291f5d
+	NewFromFloat(2.75573136213857245213e-6),  // 0x3ec71de3567d48a1
+	NewFromFloat(-1.98412698295895385996e-4), // 0xbf2a01a019bfdf03
+	NewFromFloat(8.33333333332211858878e-3),  // 0x3f8111111110f7d0
+	NewFromFloat(-1.66666666666666307295e-1), // 0xbfc5555555555548
+}
+
+// Sin returns the sine of the radian argument x.
+func (d Decimal) Sin() Decimal {
+	PI4A := NewFromFloat(7.85398125648498535156e-1)                             // 0x3fe921fb40000000, Pi/4 split into three parts
+	PI4B := NewFromFloat(3.77489470793079817668e-8)                             // 0x3e64442d00000000,
+	PI4C := NewFromFloat(2.69515142907905952645e-15)                            // 0x3ce8469898cc5170,
+	M4PI := NewFromFloat(1.273239544735162542821171882678754627704620361328125) // 4/pi
+
+	if d.Equal(NewFromFloat(0.0)) {
+		return d
+	}
+	// make argument positive but save the sign
+	sign := false
+	if d.LessThan(NewFromFloat(0.0)) {
+		d = d.Neg()
+		sign = true
+	}
+
+	j := d.Mul(M4PI).IntPart()    // integer part of x/(Pi/4), as integer for tests on the phase angle
+	y := NewFromFloat(float64(j)) // integer part of x/(Pi/4), as float
+
+	// map zeros to origin
+	if j&1 == 1 {
+		j++
+		y = y.Add(NewFromFloat(1.0))
+	}
+	j &= 7 // octant modulo 2Pi radians (360 degrees)
+	// reflect in x axis
+	if j > 3 {
+		sign = !sign
+		j -= 4
+	}
+	z := d.Sub(y.Mul(PI4A)).Sub(y.Mul(PI4B)).Sub(y.Mul(PI4C)) // Extended precision modular arithmetic
+	zz := z.Mul(z)
+
+	if j == 1 || j == 2 {
+		w := zz.Mul(zz).Mul(_cos[0].Mul(zz).Add(_cos[1]).Mul(zz).Add(_cos[2]).Mul(zz).Add(_cos[3]).Mul(zz).Add(_cos[4]).Mul(zz).Add(_cos[5]))
+		y = NewFromFloat(1.0).Sub(NewFromFloat(0.5).Mul(zz)).Add(w)
+	} else {
+		y = z.Add(z.Mul(zz).Mul(_sin[0].Mul(zz).Add(_sin[1]).Mul(zz).Add(_sin[2]).Mul(zz).Add(_sin[3]).Mul(zz).Add(_sin[4]).Mul(zz).Add(_sin[5])))
+	}
+	if sign {
+		y = y.Neg()
+	}
+	return y
+}
+
+// cos coefficients
+var _cos = [...]Decimal{
+	NewFromFloat(-1.13585365213876817300e-11), // 0xbda8fa49a0861a9b
+	NewFromFloat(2.08757008419747316778e-9),   // 0x3e21ee9d7b4e3f05
+	NewFromFloat(-2.75573141792967388112e-7),  // 0xbe927e4f7eac4bc6
+	NewFromFloat(2.48015872888517045348e-5),   // 0x3efa01a019c844f5
+	NewFromFloat(-1.38888888888730564116e-3),  // 0xbf56c16c16c14f91
+	NewFromFloat(4.16666666666665929218e-2),   // 0x3fa555555555554b
+}
+
+// Cos returns the cosine of the radian argument x.
+func (d Decimal) Cos() Decimal {
+
+	PI4A := NewFromFloat(7.85398125648498535156e-1)                             // 0x3fe921fb40000000, Pi/4 split into three parts
+	PI4B := NewFromFloat(3.77489470793079817668e-8)                             // 0x3e64442d00000000,
+	PI4C := NewFromFloat(2.69515142907905952645e-15)                            // 0x3ce8469898cc5170,
+	M4PI := NewFromFloat(1.273239544735162542821171882678754627704620361328125) // 4/pi
+
+	// make argument positive
+	sign := false
+	if d.LessThan(NewFromFloat(0.0)) {
+		d = d.Neg()
+	}
+
+	j := d.Mul(M4PI).IntPart()    // integer part of x/(Pi/4), as integer for tests on the phase angle
+	y := NewFromFloat(float64(j)) // integer part of x/(Pi/4), as float
+
+	// map zeros to origin
+	if j&1 == 1 {
+		j++
+		y = y.Add(NewFromFloat(1.0))
+	}
+	j &= 7 // octant modulo 2Pi radians (360 degrees)
+	// reflect in x axis
+	if j > 3 {
+		sign = !sign
+		j -= 4
+	}
+	if j > 1 {
+		sign = !sign
+	}
+
+	z := d.Sub(y.Mul(PI4A)).Sub(y.Mul(PI4B)).Sub(y.Mul(PI4C)) // Extended precision modular arithmetic
+	zz := z.Mul(z)
+
+	if j == 1 || j == 2 {
+		y = z.Add(z.Mul(zz).Mul(_sin[0].Mul(zz).Add(_sin[1]).Mul(zz).Add(_sin[2]).Mul(zz).Add(_sin[3]).Mul(zz).Add(_sin[4]).Mul(zz).Add(_sin[5])))
+	} else {
+		w := zz.Mul(zz).Mul(_cos[0].Mul(zz).Add(_cos[1]).Mul(zz).Add(_cos[2]).Mul(zz).Add(_cos[3]).Mul(zz).Add(_cos[4]).Mul(zz).Add(_cos[5]))
+		y = NewFromFloat(1.0).Sub(NewFromFloat(0.5).Mul(zz)).Add(w)
+	}
+	if sign {
+		y = y.Neg()
+	}
+	return y
+}
+
+var _tanP = [...]Decimal{
+	NewFromFloat(-1.30936939181383777646e+4), // 0xc0c992d8d24f3f38
+	NewFromFloat(1.15351664838587416140e+6),  // 0x413199eca5fc9ddd
+	NewFromFloat(-1.79565251976484877988e+7), // 0xc1711fead3299176
+}
+var _tanQ = [...]Decimal{
+	NewFromFloat(1.00000000000000000000e+0),
+	NewFromFloat(1.36812963470692954678e+4),  //0x40cab8a5eeb36572
+	NewFromFloat(-1.32089234440210967447e+6), //0xc13427bc582abc96
+	NewFromFloat(2.50083801823357915839e+7),  //0x4177d98fc2ead8ef
+	NewFromFloat(-5.38695755929454629881e+7), //0xc189afe03cbe5a31
+}
+
+// Tan returns the tangent of the radian argument x.
+func (d Decimal) Tan() Decimal {
+
+	PI4A := NewFromFloat(7.85398125648498535156e-1)                             // 0x3fe921fb40000000, Pi/4 split into three parts
+	PI4B := NewFromFloat(3.77489470793079817668e-8)                             // 0x3e64442d00000000,
+	PI4C := NewFromFloat(2.69515142907905952645e-15)                            // 0x3ce8469898cc5170,
+	M4PI := NewFromFloat(1.273239544735162542821171882678754627704620361328125) // 4/pi
+
+	if d.Equal(NewFromFloat(0.0)) {
+		return d
+	}
+
+	// make argument positive but save the sign
+	sign := false
+	if d.LessThan(NewFromFloat(0.0)) {
+		d = d.Neg()
+		sign = true
+	}
+
+	j := d.Mul(M4PI).IntPart()    // integer part of x/(Pi/4), as integer for tests on the phase angle
+	y := NewFromFloat(float64(j)) // integer part of x/(Pi/4), as float
+
+	// map zeros to origin
+	if j&1 == 1 {
+		j++
+		y = y.Add(NewFromFloat(1.0))
+	}
+
+	z := d.Sub(y.Mul(PI4A)).Sub(y.Mul(PI4B)).Sub(y.Mul(PI4C)) // Extended precision modular arithmetic
+	zz := z.Mul(z)
+
+	if zz.GreaterThan(NewFromFloat(1e-14)) {
+		w := zz.Mul(_tanP[0].Mul(zz).Add(_tanP[1]).Mul(zz).Add(_tanP[2]))
+		x := zz.Add(_tanQ[1]).Mul(zz).Add(_tanQ[2]).Mul(zz).Add(_tanQ[3]).Mul(zz).Add(_tanQ[4])
+		y = z.Add(z.Mul(w.Div(x)))
+	} else {
+		y = z
+	}
+	if j&2 == 2 {
+		y = NewFromFloat(-1.0).Div(y)
+	}
+	if sign {
+		y = y.Neg()
+	}
+	return y
+}

+ 185 - 0
src/github.com/shopspring/decimal/decimal_bench_test.go

@@ -0,0 +1,185 @@
+package decimal
+
+import (
+	"math"
+	"math/rand"
+	"sort"
+	"strconv"
+	"testing"
+)
+
+type DecimalSlice []Decimal
+
+func (p DecimalSlice) Len() int           { return len(p) }
+func (p DecimalSlice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
+func (p DecimalSlice) Less(i, j int) bool { return p[i].Cmp(p[j]) < 0 }
+
+func BenchmarkNewFromFloatWithExponent(b *testing.B) {
+	rng := rand.New(rand.NewSource(0xdead1337))
+	in := make([]float64, b.N)
+	for i := range in {
+		in[i] = rng.NormFloat64() * 10e20
+	}
+	b.ReportAllocs()
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		in := rng.NormFloat64() * 10e20
+		_ = NewFromFloatWithExponent(in, math.MinInt32)
+	}
+}
+
+func BenchmarkNewFromFloat(b *testing.B) {
+	rng := rand.New(rand.NewSource(0xdead1337))
+	in := make([]float64, b.N)
+	for i := range in {
+		in[i] = rng.NormFloat64() * 10e20
+	}
+	b.ReportAllocs()
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		_ = NewFromFloat(in[i])
+	}
+}
+
+func BenchmarkNewFromStringFloat(b *testing.B) {
+	rng := rand.New(rand.NewSource(0xdead1337))
+	in := make([]float64, b.N)
+	for i := range in {
+		in[i] = rng.NormFloat64() * 10e20
+	}
+	b.ReportAllocs()
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		in := strconv.FormatFloat(in[i], 'f', -1, 64)
+		_, _ = NewFromString(in)
+	}
+}
+
+func Benchmark_FloorFast(b *testing.B) {
+	input := New(200, 2)
+	b.ResetTimer()
+	for i := 0; i < b.N; i++ {
+		input.Floor()
+	}
+}
+
+func Benchmark_FloorRegular(b *testing.B) {
+	input := New(200, -2)
+	b.ResetTimer()
+	for i := 0; i < b.N; i++ {
+		input.Floor()
+	}
+}
+
+func Benchmark_DivideOriginal(b *testing.B) {
+	tcs := createDivTestCases()
+	b.ResetTimer()
+	for i := 0; i < b.N; i++ {
+		for _, tc := range tcs {
+			d := tc.d
+			if sign(tc.d2) == 0 {
+				continue
+			}
+			d2 := tc.d2
+			prec := tc.prec
+			a := d.DivOld(d2, int(prec))
+			if sign(a) > 2 {
+				panic("dummy panic")
+			}
+		}
+	}
+}
+
+func Benchmark_DivideNew(b *testing.B) {
+	tcs := createDivTestCases()
+	b.ResetTimer()
+	for i := 0; i < b.N; i++ {
+		for _, tc := range tcs {
+			d := tc.d
+			if sign(tc.d2) == 0 {
+				continue
+			}
+			d2 := tc.d2
+			prec := tc.prec
+			a := d.DivRound(d2, prec)
+			if sign(a) > 2 {
+				panic("dummy panic")
+			}
+		}
+	}
+}
+
+func BenchmarkDecimal_RoundCash_Five(b *testing.B) {
+	const want = "3.50"
+	for i := 0; i < b.N; i++ {
+		val := New(3478, -3)
+		if have := val.StringFixedCash(5); have != want {
+			b.Fatalf("\nHave: %q\nWant: %q", have, want)
+		}
+	}
+}
+
+func Benchmark_Cmp(b *testing.B) {
+	decimals := DecimalSlice([]Decimal{})
+	for i := 0; i < 1000000; i++ {
+		decimals = append(decimals, New(int64(i), 0))
+	}
+	b.ResetTimer()
+	for i := 0; i < b.N; i++ {
+		sort.Sort(decimals)
+	}
+}
+
+func Benchmark_decimal_Decimal_Add_different_precision(b *testing.B) {
+	d1 := NewFromFloat(1000.123)
+	d2 := NewFromFloat(500).Mul(NewFromFloat(0.12))
+
+	b.ReportAllocs()
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		d1.Add(d2)
+	}
+}
+
+func Benchmark_decimal_Decimal_Sub_different_precision(b *testing.B) {
+	d1 := NewFromFloat(1000.123)
+	d2 := NewFromFloat(500).Mul(NewFromFloat(0.12))
+
+	b.ReportAllocs()
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		d1.Sub(d2)
+	}
+}
+
+func Benchmark_decimal_Decimal_Add_same_precision(b *testing.B) {
+	d1 := NewFromFloat(1000.123)
+	d2 := NewFromFloat(500.123)
+
+	b.ReportAllocs()
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		d1.Add(d2)
+	}
+}
+
+func Benchmark_decimal_Decimal_Sub_same_precision(b *testing.B) {
+	d1 := NewFromFloat(1000.123)
+	d2 := NewFromFloat(500.123)
+
+	b.ReportAllocs()
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		d1.Add(d2)
+	}
+}
+
+func BenchmarkDecimal_IsInteger(b *testing.B) {
+	d := RequireFromString("12.000")
+
+	b.ReportAllocs()
+	b.StartTimer()
+	for i := 0; i < b.N; i++ {
+		d.IsInteger()
+	}
+}

+ 2595 - 0
src/github.com/shopspring/decimal/decimal_test.go

@@ -0,0 +1,2595 @@
+package decimal
+
+import (
+	"database/sql/driver"
+	"encoding/json"
+	"encoding/xml"
+	"fmt"
+	"math"
+	"math/big"
+	"math/rand"
+	"reflect"
+	"strconv"
+	"strings"
+	"testing"
+	"testing/quick"
+	"time"
+)
+
+type testEnt struct {
+	float   float64
+	short   string
+	exact   string
+	inexact string
+}
+
+var testTable = []*testEnt{
+	{3.141592653589793, "3.141592653589793", "", "3.14159265358979300000000000000000000000000000000000004"},
+	{3, "3", "", "3.0000000000000000000000002"},
+	{1234567890123456, "1234567890123456", "", "1234567890123456.00000000000000002"},
+	{1234567890123456000, "1234567890123456000", "", "1234567890123456000.0000000000000008"},
+	{1234.567890123456, "1234.567890123456", "", "1234.5678901234560000000000000009"},
+	{.1234567890123456, "0.1234567890123456", "", "0.12345678901234560000000000006"},
+	{0, "0", "", "0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001"},
+	{.1111111111111110, "0.111111111111111", "", "0.111111111111111000000000000000009"},
+	{.1111111111111111, "0.1111111111111111", "", "0.111111111111111100000000000000000000023423545644534234"},
+	{.1111111111111119, "0.1111111111111119", "", "0.111111111111111900000000000000000000000000000000000134123984192834"},
+	{.000000000000000001, "0.000000000000000001", "", "0.00000000000000000100000000000000000000000000000000012341234"},
+	{.000000000000000002, "0.000000000000000002", "", "0.0000000000000000020000000000000000000012341234123"},
+	{.000000000000000003, "0.000000000000000003", "", "0.00000000000000000299999999999999999999999900000000000123412341234"},
+	{.000000000000000005, "0.000000000000000005", "", "0.00000000000000000500000000000000000023412341234"},
+	{.000000000000000008, "0.000000000000000008", "", "0.0000000000000000080000000000000000001241234432"},
+	{.1000000000000001, "0.1000000000000001", "", "0.10000000000000010000000000000012341234"},
+	{.1000000000000002, "0.1000000000000002", "", "0.10000000000000020000000000001234123412"},
+	{.1000000000000003, "0.1000000000000003", "", "0.1000000000000003000000000000001234123412"},
+	{.1000000000000005, "0.1000000000000005", "", "0.1000000000000005000000000000000006441234"},
+	{.1000000000000008, "0.1000000000000008", "", "0.100000000000000800000000000000000009999999999999999999999999999"},
+	{1e25, "10000000000000000000000000", "", ""},
+	{1.5e14, "150000000000000", "", ""},
+	{1.5e15, "1500000000000000", "", ""},
+	{1.5e16, "15000000000000000", "", ""},
+	{1.0001e25, "10001000000000000000000000", "", ""},
+	{1.0001000000000000033e25, "10001000000000000000000000", "", ""},
+	{2e25, "20000000000000000000000000", "", ""},
+	{4e25, "40000000000000000000000000", "", ""},
+	{8e25, "80000000000000000000000000", "", ""},
+	{1e250, "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "", ""},
+	{2e250, "20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "", ""},
+	{math.MaxInt64, strconv.FormatFloat(float64(math.MaxInt64), 'f', -1, 64), "", strconv.FormatInt(math.MaxInt64, 10)},
+	{1.29067116156722e-309, "0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000129067116156722", "", "0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001290671161567218558822290567835270536800098852722416870074139002112543896676308448335063375297788379444685193974290737962187240854947838776604607190387984577130572928111657710645015086812756013489109884753559084166516937690932698276436869274093950997935137476803610007959500457935217950764794724766740819156974617155861568214427828145972181876775307023388139991104942469299524961281641158436752347582767153796914843896176260096039358494077706152272661453132497761307744086665088096215425146090058519888494342944692629602847826300550628670375451325582843627504604013541465361435761965354140678551369499812124085312128659002910905639984075064968459581691226705666561364681985266583563078466180095375402399087817404368974165082030458595596655868575908243656158447265625000000000000000000000000000000000000004440000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"},
+	// go Issue 29491.
+	{498484681984085570, "498484681984085570", "", ""},
+	{5.8339553793802237e+23, "583395537938022370000000", "", ""},
+}
+
+var testTableScientificNotation = map[string]string{
+	"1e9":        "1000000000",
+	"2.41E-3":    "0.00241",
+	"24.2E-4":    "0.00242",
+	"243E-5":     "0.00243",
+	"1e-5":       "0.00001",
+	"245E3":      "245000",
+	"1.2345E-1":  "0.12345",
+	"0e5":        "0",
+	"0e-5":       "0",
+	"0.e0":       "0",
+	".0e0":       "0",
+	"123.456e0":  "123.456",
+	"123.456e2":  "12345.6",
+	"123.456e10": "1234560000000",
+}
+
+func init() {
+	for _, s := range testTable {
+		s.exact = strconv.FormatFloat(s.float, 'f', 1500, 64)
+		if strings.ContainsRune(s.exact, '.') {
+			s.exact = strings.TrimRight(s.exact, "0")
+			s.exact = strings.TrimRight(s.exact, ".")
+		}
+	}
+
+	// add negatives
+	withNeg := testTable[:]
+	for _, s := range testTable {
+		if s.float > 0 && s.short != "0" && s.exact != "0" {
+			withNeg = append(withNeg, &testEnt{-s.float, "-" + s.short, "-" + s.exact, "-" + s.inexact})
+		}
+	}
+	testTable = withNeg
+
+	for e, s := range testTableScientificNotation {
+		if string(e[0]) != "-" && s != "0" {
+			testTableScientificNotation["-"+e] = "-" + s
+		}
+	}
+}
+
+func TestNewFromFloat(t *testing.T) {
+	for _, x := range testTable {
+		s := x.short
+		d := NewFromFloat(x.float)
+		if d.String() != s {
+			t.Errorf("expected %s, got %s (float: %v) (%s, %d)",
+				s, d.String(), x.float,
+				d.value.String(), d.exp)
+		}
+	}
+
+	shouldPanicOn := []float64{
+		math.NaN(),
+		math.Inf(1),
+		math.Inf(-1),
+	}
+
+	for _, n := range shouldPanicOn {
+		var d Decimal
+		if !didPanic(func() { d = NewFromFloat(n) }) {
+			t.Fatalf("Expected panic when creating a Decimal from %v, got %v instead", n, d.String())
+		}
+	}
+}
+
+func TestNewFromFloatRandom(t *testing.T) {
+	n := 0
+	rng := rand.New(rand.NewSource(0xdead1337))
+	for {
+		n++
+		if n == 10 {
+			break
+		}
+		in := (rng.Float64() - 0.5) * math.MaxFloat64 * 2
+		want, err := NewFromString(strconv.FormatFloat(in, 'f', -1, 64))
+		if err != nil {
+			t.Error(err)
+			continue
+		}
+		got := NewFromFloat(in)
+		if !want.Equal(got) {
+			t.Errorf("in: %v, expected %s (%s, %d), got %s (%s, %d) ",
+				in, want.String(), want.value.String(), want.exp,
+				got.String(), got.value.String(), got.exp)
+		}
+	}
+}
+
+func TestNewFromFloatQuick(t *testing.T) {
+	err := quick.Check(func(f float64) bool {
+		want, werr := NewFromString(strconv.FormatFloat(f, 'f', -1, 64))
+		if werr != nil {
+			return true
+		}
+		got := NewFromFloat(f)
+		return got.Equal(want)
+	}, &quick.Config{})
+	if err != nil {
+		t.Error(err)
+	}
+}
+
+func TestNewFromFloat32Random(t *testing.T) {
+	n := 0
+	rng := rand.New(rand.NewSource(0xdead1337))
+	for {
+		n++
+		if n == 10 {
+			break
+		}
+		in := float32((rng.Float64() - 0.5) * math.MaxFloat32 * 2)
+		want, err := NewFromString(strconv.FormatFloat(float64(in), 'f', -1, 32))
+		if err != nil {
+			t.Error(err)
+			continue
+		}
+		got := NewFromFloat32(in)
+		if !want.Equal(got) {
+			t.Errorf("in: %v, expected %s (%s, %d), got %s (%s, %d) ",
+				in, want.String(), want.value.String(), want.exp,
+				got.String(), got.value.String(), got.exp)
+		}
+	}
+}
+
+func TestNewFromFloat32Quick(t *testing.T) {
+	err := quick.Check(func(f float32) bool {
+		want, werr := NewFromString(strconv.FormatFloat(float64(f), 'f', -1, 32))
+		if werr != nil {
+			return true
+		}
+		got := NewFromFloat32(f)
+		return got.Equal(want)
+	}, &quick.Config{})
+	if err != nil {
+		t.Error(err)
+	}
+}
+
+func TestNewFromString(t *testing.T) {
+	for _, x := range testTable {
+		s := x.short
+		d, err := NewFromString(s)
+		if err != nil {
+			t.Errorf("error while parsing %s", s)
+		} else if d.String() != s {
+			t.Errorf("expected %s, got %s (%s, %d)",
+				s, d.String(),
+				d.value.String(), d.exp)
+		}
+	}
+
+	for _, x := range testTable {
+		s := x.exact
+		d, err := NewFromString(s)
+		if err != nil {
+			t.Errorf("error while parsing %s", s)
+		} else if d.String() != s {
+			t.Errorf("expected %s, got %s (%s, %d)",
+				s, d.String(),
+				d.value.String(), d.exp)
+		}
+	}
+
+	for e, s := range testTableScientificNotation {
+		d, err := NewFromString(e)
+		if err != nil {
+			t.Errorf("error while parsing %s", e)
+		} else if d.String() != s {
+			t.Errorf("expected %s, got %s (%s, %d)",
+				s, d.String(),
+				d.value.String(), d.exp)
+		}
+	}
+}
+
+func TestFloat64(t *testing.T) {
+	for _, x := range testTable {
+		if x.inexact == "" || x.inexact == "-" {
+			continue
+		}
+		s := x.exact
+		d, err := NewFromString(s)
+		if err != nil {
+			t.Errorf("error while parsing %s", s)
+		} else if f, exact := d.Float64(); !exact || f != x.float {
+			t.Errorf("cannot represent exactly %s", s)
+		}
+		s = x.inexact
+		d, err = NewFromString(s)
+		if err != nil {
+			t.Errorf("error while parsing %s", s)
+		} else if f, exact := d.Float64(); exact || f != x.float {
+			t.Errorf("%s should be represented inexactly", s)
+		}
+	}
+}
+
+func TestNewFromStringErrs(t *testing.T) {
+	tests := []string{
+		"",
+		"qwert",
+		"-",
+		".",
+		"-.",
+		".-",
+		"234-.56",
+		"234-56",
+		"2-",
+		"..",
+		"2..",
+		"..2",
+		".5.2",
+		"8..2",
+		"8.1.",
+		"1e",
+		"1-e",
+		"1e9e",
+		"1ee9",
+		"1ee",
+		"1eE",
+		"1e-",
+		"1e-.",
+		"1e1.2",
+		"123.456e1.3",
+		"1e-1.2",
+		"123.456e-1.3",
+		"123.456Easdf",
+		"123.456e" + strconv.FormatInt(math.MinInt64, 10),
+		"123.456e" + strconv.FormatInt(math.MinInt32, 10),
+	}
+
+	for _, s := range tests {
+		_, err := NewFromString(s)
+
+		if err == nil {
+			t.Errorf("error expected when parsing %s", s)
+		}
+	}
+}
+
+func TestNewFromStringDeepEquals(t *testing.T) {
+	type StrCmp struct {
+		str1     string
+		str2     string
+		expected bool
+	}
+	tests := []StrCmp{
+		{"1", "1", true},
+		{"1.0", "1.0", true},
+		{"10", "10.0", false},
+		{"1.1", "1.10", false},
+		{"1.001", "1.01", false},
+	}
+
+	for _, cmp := range tests {
+		d1, err1 := NewFromString(cmp.str1)
+		d2, err2 := NewFromString(cmp.str2)
+
+		if err1 != nil || err2 != nil {
+			t.Errorf("error parsing strings to decimals")
+		}
+
+		if reflect.DeepEqual(d1, d2) != cmp.expected {
+			t.Errorf("comparison result is different from expected results for %s and %s",
+				cmp.str1, cmp.str2)
+		}
+	}
+}
+
+func TestRequireFromString(t *testing.T) {
+	s := "1.23"
+	defer func() {
+		err := recover()
+		if err != nil {
+			t.Errorf("error while parsing %s", s)
+		}
+	}()
+
+	d := RequireFromString(s)
+	if d.String() != s {
+		t.Errorf("expected %s, got %s (%s, %d)",
+			s, d.String(),
+			d.value.String(), d.exp)
+	}
+}
+
+func TestRequireFromStringErrs(t *testing.T) {
+	s := "qwert"
+	var d Decimal
+	var err interface{}
+
+	func(d Decimal) {
+		defer func() {
+			err = recover()
+		}()
+
+		RequireFromString(s)
+	}(d)
+
+	if err == nil {
+		t.Errorf("panic expected when parsing %s", s)
+	}
+}
+
+func TestNewFromFloatWithExponent(t *testing.T) {
+	type Inp struct {
+		float float64
+		exp   int32
+	}
+	// some tests are taken from here https://www.cockroachlabs.com/blog/rounding-implementations-in-go/
+	tests := map[Inp]string{
+		Inp{123.4, -3}:                 "123.4",
+		Inp{123.4, -1}:                 "123.4",
+		Inp{123.412345, 1}:             "120",
+		Inp{123.412345, 0}:             "123",
+		Inp{123.412345, -5}:            "123.41235",
+		Inp{123.412345, -6}:            "123.412345",
+		Inp{123.412345, -7}:            "123.412345",
+		Inp{123.412345, -28}:           "123.4123450000000019599610823207",
+		Inp{1230000000, 3}:             "1230000000",
+		Inp{123.9999999999999999, -7}:  "124",
+		Inp{123.8989898999999999, -7}:  "123.8989899",
+		Inp{0.49999999999999994, 0}:    "0",
+		Inp{0.5, 0}:                    "1",
+		Inp{0., -1000}:                 "0",
+		Inp{0.5000000000000001, 0}:     "1",
+		Inp{1.390671161567e-309, 0}:    "0",
+		Inp{4.503599627370497e+15, 0}:  "4503599627370497",
+		Inp{4.503599627370497e+60, 0}:  "4503599627370497110902645731364739935039854989106233267453952",
+		Inp{4.503599627370497e+60, 1}:  "4503599627370497110902645731364739935039854989106233267453950",
+		Inp{4.503599627370497e+60, -1}: "4503599627370497110902645731364739935039854989106233267453952",
+		Inp{50, 2}:                     "100",
+		Inp{49, 2}:                     "0",
+		Inp{50, 3}:                     "0",
+		// subnormals
+		Inp{1.390671161567e-309, -2000}: "0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001390671161567000864431395448332752540137009987788957394095829635554502771758698872408926974382819387852542087331897381878220271350970912568035007740861074263206736245957501456549756342151614772544950978154339064833880234531754156635411349342950306987480369774780312897442981323940546749863054846093718407237782253156822124910364044261653195961209878120072488178603782495270845071470243842997312255994555557251870400944414666445871039673491570643357351279578519863428540219295076767898526278029257129758694673164251056158277568765100904638511604478844087596428177947970563689475826736810456067108202083804368114484417399279328807983736233036662284338182105684628835292230438999173947056675615385756827890872955322265625",
+		Inp{1.390671161567e-309, -862}:  "0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013906711615670008644313954483327525401370099877889573940958296355545027717586988724089269743828193878525420873318973818782202713509709125680350077408610742632067362459575014565497563421516147725449509781543390648338802345317541566354113493429503069874803697747803128974429813239405467498630548460937184072377822531568221249103640442616531959612098781200724881786037824952708450714702438429973122559945555572518704009444146664458710396734915706433573512795785198634285402192950767678985262780292571297586946731642510561582775687651009046385116044788440876",
+		Inp{1.390671161567e-309, -863}:  "0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013906711615670008644313954483327525401370099877889573940958296355545027717586988724089269743828193878525420873318973818782202713509709125680350077408610742632067362459575014565497563421516147725449509781543390648338802345317541566354113493429503069874803697747803128974429813239405467498630548460937184072377822531568221249103640442616531959612098781200724881786037824952708450714702438429973122559945555572518704009444146664458710396734915706433573512795785198634285402192950767678985262780292571297586946731642510561582775687651009046385116044788440876",
+	}
+
+	// add negatives
+	for p, s := range tests {
+		if p.float > 0 {
+			if s != "0" {
+				tests[Inp{-p.float, p.exp}] = "-" + s
+			} else {
+				tests[Inp{-p.float, p.exp}] = "0"
+			}
+		}
+	}
+
+	for input, s := range tests {
+		d := NewFromFloatWithExponent(input.float, input.exp)
+		if d.String() != s {
+			t.Errorf("expected %s, got %s (%s, %d)",
+				s, d.String(),
+				d.value.String(), d.exp)
+		}
+	}
+
+	shouldPanicOn := []float64{
+		math.NaN(),
+		math.Inf(1),
+		math.Inf(-1),
+	}
+
+	for _, n := range shouldPanicOn {
+		var d Decimal
+		if !didPanic(func() { d = NewFromFloatWithExponent(n, 0) }) {
+			t.Fatalf("Expected panic when creating a Decimal from %v, got %v instead", n, d.String())
+		}
+	}
+}
+
+func TestNewFromInt(t *testing.T) {
+	tests := map[int64]string{
+		0:                   "0",
+		1:                   "1",
+		323412345:           "323412345",
+		9223372036854775807: "9223372036854775807",
+	}
+
+	// add negatives
+	for p, s := range tests {
+		if p > 0 {
+			tests[-p] = "-" + s
+		}
+	}
+
+	for input, s := range tests {
+		d := NewFromInt(input)
+		if d.String() != s {
+			t.Errorf("expected %s, got %s (%s, %d)",
+				s, d.String(),
+				d.value.String(), d.exp)
+		}
+	}
+}
+
+func TestNewFromInt32(t *testing.T) {
+	tests := map[int32]string{
+		0:          "0",
+		1:          "1",
+		323412345:  "323412345",
+		2147483647: "2147483647",
+	}
+
+	// add negatives
+	for p, s := range tests {
+		if p > 0 {
+			tests[-p] = "-" + s
+		}
+	}
+
+	for input, s := range tests {
+		d := NewFromInt32(input)
+		if d.String() != s {
+			t.Errorf("expected %s, got %s (%s, %d)",
+				s, d.String(),
+				d.value.String(), d.exp)
+		}
+	}
+}
+
+func TestNewFromBigIntWithExponent(t *testing.T) {
+	type Inp struct {
+		val *big.Int
+		exp int32
+	}
+	tests := map[Inp]string{
+		Inp{big.NewInt(123412345), -3}: "123412.345",
+		Inp{big.NewInt(2234), -1}:      "223.4",
+		Inp{big.NewInt(323412345), 1}:  "3234123450",
+		Inp{big.NewInt(423412345), 0}:  "423412345",
+		Inp{big.NewInt(52341235), -5}:  "523.41235",
+		Inp{big.NewInt(623412345), -6}: "623.412345",
+		Inp{big.NewInt(723412345), -7}: "72.3412345",
+	}
+
+	// add negatives
+	for p, s := range tests {
+		if p.val.Cmp(Zero.value) > 0 {
+			tests[Inp{p.val.Neg(p.val), p.exp}] = "-" + s
+		}
+	}
+
+	for input, s := range tests {
+		d := NewFromBigInt(input.val, input.exp)
+		if d.String() != s {
+			t.Errorf("expected %s, got %s (%s, %d)",
+				s, d.String(),
+				d.value.String(), d.exp)
+		}
+	}
+}
+
+func TestJSON(t *testing.T) {
+	for _, x := range testTable {
+		s := x.short
+		var doc struct {
+			Amount Decimal `json:"amount"`
+		}
+		docStr := `{"amount":"` + s + `"}`
+		docStrNumber := `{"amount":` + s + `}`
+		err := json.Unmarshal([]byte(docStr), &doc)
+		if err != nil {
+			t.Errorf("error unmarshaling %s: %v", docStr, err)
+		} else if doc.Amount.String() != s {
+			t.Errorf("expected %s, got %s (%s, %d)",
+				s, doc.Amount.String(),
+				doc.Amount.value.String(), doc.Amount.exp)
+		}
+
+		out, err := json.Marshal(&doc)
+		if err != nil {
+			t.Errorf("error marshaling %+v: %v", doc, err)
+		} else if string(out) != docStr {
+			t.Errorf("expected %s, got %s", docStr, string(out))
+		}
+
+		// make sure unquoted marshalling works too
+		MarshalJSONWithoutQuotes = true
+		out, err = json.Marshal(&doc)
+		if err != nil {
+			t.Errorf("error marshaling %+v: %v", doc, err)
+		} else if string(out) != docStrNumber {
+			t.Errorf("expected %s, got %s", docStrNumber, string(out))
+		}
+		MarshalJSONWithoutQuotes = false
+	}
+}
+
+func TestUnmarshalJSONNull(t *testing.T) {
+	var doc struct {
+		Amount Decimal `json:"amount"`
+	}
+	docStr := `{"amount": null}`
+	err := json.Unmarshal([]byte(docStr), &doc)
+	if err != nil {
+		t.Errorf("error unmarshaling %s: %v", docStr, err)
+	} else if !doc.Amount.Equal(Zero) {
+		t.Errorf("expected Zero, got %s (%s, %d)",
+			doc.Amount.String(),
+			doc.Amount.value.String(), doc.Amount.exp)
+	}
+}
+
+func TestBadJSON(t *testing.T) {
+	for _, testCase := range []string{
+		"]o_o[",
+		"{",
+		`{"amount":""`,
+		`{"amount":""}`,
+		`{"amount":"nope"}`,
+		`0.333`,
+	} {
+		var doc struct {
+			Amount Decimal `json:"amount"`
+		}
+		err := json.Unmarshal([]byte(testCase), &doc)
+		if err == nil {
+			t.Errorf("expected error, got %+v", doc)
+		}
+	}
+}
+
+func TestNullDecimalJSON(t *testing.T) {
+	for _, x := range testTable {
+		s := x.short
+		var doc struct {
+			Amount NullDecimal `json:"amount"`
+		}
+		docStr := `{"amount":"` + s + `"}`
+		docStrNumber := `{"amount":` + s + `}`
+		err := json.Unmarshal([]byte(docStr), &doc)
+		if err != nil {
+			t.Errorf("error unmarshaling %s: %v", docStr, err)
+		} else {
+			if !doc.Amount.Valid {
+				t.Errorf("expected %s to be valid (not NULL), got Valid = false", s)
+			}
+			if doc.Amount.Decimal.String() != s {
+				t.Errorf("expected %s, got %s (%s, %d)",
+					s, doc.Amount.Decimal.String(),
+					doc.Amount.Decimal.value.String(), doc.Amount.Decimal.exp)
+			}
+		}
+
+		out, err := json.Marshal(&doc)
+		if err != nil {
+			t.Errorf("error marshaling %+v: %v", doc, err)
+		} else if string(out) != docStr {
+			t.Errorf("expected %s, got %s", docStr, string(out))
+		}
+
+		// make sure unquoted marshalling works too
+		MarshalJSONWithoutQuotes = true
+		out, err = json.Marshal(&doc)
+		if err != nil {
+			t.Errorf("error marshaling %+v: %v", doc, err)
+		} else if string(out) != docStrNumber {
+			t.Errorf("expected %s, got %s", docStrNumber, string(out))
+		}
+		MarshalJSONWithoutQuotes = false
+	}
+
+	var doc struct {
+		Amount NullDecimal `json:"amount"`
+	}
+	docStr := `{"amount": null}`
+	err := json.Unmarshal([]byte(docStr), &doc)
+	if err != nil {
+		t.Errorf("error unmarshaling %s: %v", docStr, err)
+	} else if doc.Amount.Valid {
+		t.Errorf("expected null value to have Valid = false, got Valid = true and Decimal = %s (%s, %d)",
+			doc.Amount.Decimal.String(),
+			doc.Amount.Decimal.value.String(), doc.Amount.Decimal.exp)
+	}
+
+	expected := `{"amount":null}`
+	out, err := json.Marshal(&doc)
+	if err != nil {
+		t.Errorf("error marshaling %+v: %v", doc, err)
+	} else if string(out) != expected {
+		t.Errorf("expected %s, got %s", expected, string(out))
+	}
+
+	// make sure unquoted marshalling works too
+	MarshalJSONWithoutQuotes = true
+	expectedUnquoted := `{"amount":null}`
+	out, err = json.Marshal(&doc)
+	if err != nil {
+		t.Errorf("error marshaling %+v: %v", doc, err)
+	} else if string(out) != expectedUnquoted {
+		t.Errorf("expected %s, got %s", expectedUnquoted, string(out))
+	}
+	MarshalJSONWithoutQuotes = false
+}
+
+func TestNullDecimalBadJSON(t *testing.T) {
+	for _, testCase := range []string{
+		"]o_o[",
+		"{",
+		`{"amount":""`,
+		`{"amount":""}`,
+		`{"amount":"nope"}`,
+		`{"amount":nope}`,
+		`0.333`,
+	} {
+		var doc struct {
+			Amount NullDecimal `json:"amount"`
+		}
+		err := json.Unmarshal([]byte(testCase), &doc)
+		if err == nil {
+			t.Errorf("expected error, got %+v", doc)
+		}
+	}
+}
+
+func TestXML(t *testing.T) {
+	for _, x := range testTable {
+		s := x.short
+		var doc struct {
+			XMLName xml.Name `xml:"account"`
+			Amount  Decimal  `xml:"amount"`
+		}
+		docStr := `<account><amount>` + s + `</amount></account>`
+		err := xml.Unmarshal([]byte(docStr), &doc)
+		if err != nil {
+			t.Errorf("error unmarshaling %s: %v", docStr, err)
+		} else if doc.Amount.String() != s {
+			t.Errorf("expected %s, got %s (%s, %d)",
+				s, doc.Amount.String(),
+				doc.Amount.value.String(), doc.Amount.exp)
+		}
+
+		out, err := xml.Marshal(&doc)
+		if err != nil {
+			t.Errorf("error marshaling %+v: %v", doc, err)
+		} else if string(out) != docStr {
+			t.Errorf("expected %s, got %s", docStr, string(out))
+		}
+	}
+}
+
+func TestBadXML(t *testing.T) {
+	for _, testCase := range []string{
+		"o_o",
+		"<abc",
+		"<account><amount>7",
+		`<html><body></body></html>`,
+		`<account><amount></amount></account>`,
+		`<account><amount>nope</amount></account>`,
+		`0.333`,
+	} {
+		var doc struct {
+			XMLName xml.Name `xml:"account"`
+			Amount  Decimal  `xml:"amount"`
+		}
+		err := xml.Unmarshal([]byte(testCase), &doc)
+		if err == nil {
+			t.Errorf("expected error, got %+v", doc)
+		}
+	}
+}
+
+func TestDecimal_rescale(t *testing.T) {
+	type Inp struct {
+		int     int64
+		exp     int32
+		rescale int32
+	}
+	tests := map[Inp]string{
+		Inp{1234, -3, -5}: "1.234",
+		Inp{1234, -3, 0}:  "1",
+		Inp{1234, 3, 0}:   "1234000",
+		Inp{1234, -4, -4}: "0.1234",
+	}
+
+	// add negatives
+	for p, s := range tests {
+		if p.int > 0 {
+			tests[Inp{-p.int, p.exp, p.rescale}] = "-" + s
+		}
+	}
+
+	for input, s := range tests {
+		d := New(input.int, input.exp).rescale(input.rescale)
+
+		if d.String() != s {
+			t.Errorf("expected %s, got %s (%s, %d)",
+				s, d.String(),
+				d.value.String(), d.exp)
+		}
+
+		// test StringScaled
+		s2 := New(input.int, input.exp).StringScaled(input.rescale)
+		if s2 != s {
+			t.Errorf("expected %s, got %s", s, s2)
+		}
+	}
+}
+
+func TestDecimal_Floor(t *testing.T) {
+	assertFloor := func(input, expected Decimal) {
+		got := input.Floor()
+		if !got.Equal(expected) {
+			t.Errorf("Floor(%s): got %s, expected %s", input, got, expected)
+		}
+	}
+	type testDataString struct {
+		input    string
+		expected string
+	}
+	testsWithStrings := []testDataString{
+		{"1.999", "1"},
+		{"1", "1"},
+		{"1.01", "1"},
+		{"0", "0"},
+		{"0.9", "0"},
+		{"0.1", "0"},
+		{"-0.9", "-1"},
+		{"-0.1", "-1"},
+		{"-1.00", "-1"},
+		{"-1.01", "-2"},
+		{"-1.999", "-2"},
+	}
+	for _, test := range testsWithStrings {
+		expected, _ := NewFromString(test.expected)
+		input, _ := NewFromString(test.input)
+		assertFloor(input, expected)
+	}
+
+	type testDataDecimal struct {
+		input    Decimal
+		expected string
+	}
+	testsWithDecimals := []testDataDecimal{
+		{New(100, -1), "10"},
+		{New(10, 0), "10"},
+		{New(1, 1), "10"},
+		{New(1999, -3), "1"},
+		{New(101, -2), "1"},
+		{New(1, 0), "1"},
+		{New(0, 0), "0"},
+		{New(9, -1), "0"},
+		{New(1, -1), "0"},
+		{New(-1, -1), "-1"},
+		{New(-9, -1), "-1"},
+		{New(-1, 0), "-1"},
+		{New(-101, -2), "-2"},
+		{New(-1999, -3), "-2"},
+	}
+	for _, test := range testsWithDecimals {
+		expected, _ := NewFromString(test.expected)
+		assertFloor(test.input, expected)
+	}
+}
+
+func TestDecimal_Ceil(t *testing.T) {
+	assertCeil := func(input, expected Decimal) {
+		got := input.Ceil()
+		if !got.Equal(expected) {
+			t.Errorf("Ceil(%s): got %s, expected %s", input, got, expected)
+		}
+	}
+	type testDataString struct {
+		input    string
+		expected string
+	}
+	testsWithStrings := []testDataString{
+		{"1.999", "2"},
+		{"1", "1"},
+		{"1.01", "2"},
+		{"0", "0"},
+		{"0.9", "1"},
+		{"0.1", "1"},
+		{"-0.9", "0"},
+		{"-0.1", "0"},
+		{"-1.00", "-1"},
+		{"-1.01", "-1"},
+		{"-1.999", "-1"},
+	}
+	for _, test := range testsWithStrings {
+		expected, _ := NewFromString(test.expected)
+		input, _ := NewFromString(test.input)
+		assertCeil(input, expected)
+	}
+
+	type testDataDecimal struct {
+		input    Decimal
+		expected string
+	}
+	testsWithDecimals := []testDataDecimal{
+		{New(100, -1), "10"},
+		{New(10, 0), "10"},
+		{New(1, 1), "10"},
+		{New(1999, -3), "2"},
+		{New(101, -2), "2"},
+		{New(1, 0), "1"},
+		{New(0, 0), "0"},
+		{New(9, -1), "1"},
+		{New(1, -1), "1"},
+		{New(-1, -1), "0"},
+		{New(-9, -1), "0"},
+		{New(-1, 0), "-1"},
+		{New(-101, -2), "-1"},
+		{New(-1999, -3), "-1"},
+	}
+	for _, test := range testsWithDecimals {
+		expected, _ := NewFromString(test.expected)
+		assertCeil(test.input, expected)
+	}
+}
+
+func TestDecimal_RoundAndStringFixed(t *testing.T) {
+	type testData struct {
+		input         string
+		places        int32
+		expected      string
+		expectedFixed string
+	}
+	tests := []testData{
+		{"1.454", 0, "1", ""},
+		{"1.454", 1, "1.5", ""},
+		{"1.454", 2, "1.45", ""},
+		{"1.454", 3, "1.454", ""},
+		{"1.454", 4, "1.454", "1.4540"},
+		{"1.454", 5, "1.454", "1.45400"},
+		{"1.554", 0, "2", ""},
+		{"1.554", 1, "1.6", ""},
+		{"1.554", 2, "1.55", ""},
+		{"0.554", 0, "1", ""},
+		{"0.454", 0, "0", ""},
+		{"0.454", 5, "0.454", "0.45400"},
+		{"0", 0, "0", ""},
+		{"0", 1, "0", "0.0"},
+		{"0", 2, "0", "0.00"},
+		{"0", -1, "0", ""},
+		{"5", 2, "5", "5.00"},
+		{"5", 1, "5", "5.0"},
+		{"5", 0, "5", ""},
+		{"500", 2, "500", "500.00"},
+		{"545", -1, "550", ""},
+		{"545", -2, "500", ""},
+		{"545", -3, "1000", ""},
+		{"545", -4, "0", ""},
+		{"499", -3, "0", ""},
+		{"499", -4, "0", ""},
+	}
+
+	// add negative number tests
+	for _, test := range tests {
+		expected := test.expected
+		if expected != "0" {
+			expected = "-" + expected
+		}
+		expectedStr := test.expectedFixed
+		if strings.ContainsAny(expectedStr, "123456789") && expectedStr != "" {
+			expectedStr = "-" + expectedStr
+		}
+		tests = append(tests,
+			testData{"-" + test.input, test.places, expected, expectedStr})
+	}
+
+	for _, test := range tests {
+		d, err := NewFromString(test.input)
+		if err != nil {
+			panic(err)
+		}
+
+		// test Round
+		expected, err := NewFromString(test.expected)
+		if err != nil {
+			panic(err)
+		}
+		got := d.Round(test.places)
+		if !got.Equal(expected) {
+			t.Errorf("Rounding %s to %d places, got %s, expected %s",
+				d, test.places, got, expected)
+		}
+
+		// test StringFixed
+		if test.expectedFixed == "" {
+			test.expectedFixed = test.expected
+		}
+		gotStr := d.StringFixed(test.places)
+		if gotStr != test.expectedFixed {
+			t.Errorf("(%s).StringFixed(%d): got %s, expected %s",
+				d, test.places, gotStr, test.expectedFixed)
+		}
+	}
+}
+
+func TestDecimal_BankRoundAndStringFixed(t *testing.T) {
+	type testData struct {
+		input         string
+		places        int32
+		expected      string
+		expectedFixed string
+	}
+	tests := []testData{
+		{"1.454", 0, "1", ""},
+		{"1.454", 1, "1.5", ""},
+		{"1.454", 2, "1.45", ""},
+		{"1.454", 3, "1.454", ""},
+		{"1.454", 4, "1.454", "1.4540"},
+		{"1.454", 5, "1.454", "1.45400"},
+		{"1.554", 0, "2", ""},
+		{"1.554", 1, "1.6", ""},
+		{"1.554", 2, "1.55", ""},
+		{"0.554", 0, "1", ""},
+		{"0.454", 0, "0", ""},
+		{"0.454", 5, "0.454", "0.45400"},
+		{"0", 0, "0", ""},
+		{"0", 1, "0", "0.0"},
+		{"0", 2, "0", "0.00"},
+		{"0", -1, "0", ""},
+		{"5", 2, "5", "5.00"},
+		{"5", 1, "5", "5.0"},
+		{"5", 0, "5", ""},
+		{"500", 2, "500", "500.00"},
+		{"545", -2, "500", ""},
+		{"545", -3, "1000", ""},
+		{"545", -4, "0", ""},
+		{"499", -3, "0", ""},
+		{"499", -4, "0", ""},
+		{"1.45", 1, "1.4", ""},
+		{"1.55", 1, "1.6", ""},
+		{"1.65", 1, "1.6", ""},
+		{"545", -1, "540", ""},
+		{"565", -1, "560", ""},
+		{"555", -1, "560", ""},
+	}
+
+	// add negative number tests
+	for _, test := range tests {
+		expected := test.expected
+		if expected != "0" {
+			expected = "-" + expected
+		}
+		expectedStr := test.expectedFixed
+		if strings.ContainsAny(expectedStr, "123456789") && expectedStr != "" {
+			expectedStr = "-" + expectedStr
+		}
+		tests = append(tests,
+			testData{"-" + test.input, test.places, expected, expectedStr})
+	}
+
+	for _, test := range tests {
+		d, err := NewFromString(test.input)
+		if err != nil {
+			panic(err)
+		}
+
+		// test Round
+		expected, err := NewFromString(test.expected)
+		if err != nil {
+			panic(err)
+		}
+		got := d.RoundBank(test.places)
+		if !got.Equal(expected) {
+			t.Errorf("Bank Rounding %s to %d places, got %s, expected %s",
+				d, test.places, got, expected)
+		}
+
+		// test StringFixed
+		if test.expectedFixed == "" {
+			test.expectedFixed = test.expected
+		}
+		gotStr := d.StringFixedBank(test.places)
+		if gotStr != test.expectedFixed {
+			t.Errorf("(%s).StringFixed(%d): got %s, expected %s",
+				d, test.places, gotStr, test.expectedFixed)
+		}
+	}
+}
+
+func TestDecimal_Uninitialized(t *testing.T) {
+	a := Decimal{}
+	b := Decimal{}
+
+	decs := []Decimal{
+		a,
+		a.rescale(10),
+		a.Abs(),
+		a.Add(b),
+		a.Sub(b),
+		a.Mul(b),
+		a.Shift(0),
+		a.Div(New(1, -1)),
+		a.Round(2),
+		a.Floor(),
+		a.Ceil(),
+		a.Truncate(2),
+	}
+
+	for _, d := range decs {
+		if d.String() != "0" {
+			t.Errorf("expected 0, got %s", d.String())
+		}
+		if d.StringFixed(3) != "0.000" {
+			t.Errorf("expected 0, got %s", d.StringFixed(3))
+		}
+		if d.StringScaled(-2) != "0" {
+			t.Errorf("expected 0, got %s", d.StringScaled(-2))
+		}
+	}
+
+	if a.Cmp(b) != 0 {
+		t.Errorf("a != b")
+	}
+	if a.Sign() != 0 {
+		t.Errorf("a.Sign() != 0")
+	}
+	if a.Exponent() != 0 {
+		t.Errorf("a.Exponent() != 0")
+	}
+	if a.IntPart() != 0 {
+		t.Errorf("a.IntPar() != 0")
+	}
+	f, _ := a.Float64()
+	if f != 0 {
+		t.Errorf("a.Float64() != 0")
+	}
+	if a.Rat().RatString() != "0" {
+		t.Errorf("a.Rat() != 0, got %s", a.Rat().RatString())
+	}
+}
+
+func TestDecimal_Add(t *testing.T) {
+	type Inp struct {
+		a string
+		b string
+	}
+
+	inputs := map[Inp]string{
+		Inp{"2", "3"}:                     "5",
+		Inp{"2454495034", "3451204593"}:   "5905699627",
+		Inp{"24544.95034", ".3451204593"}: "24545.2954604593",
+		Inp{".1", ".1"}:                   "0.2",
+		Inp{".1", "-.1"}:                  "0",
+		Inp{"0", "1.001"}:                 "1.001",
+	}
+
+	for inp, res := range inputs {
+		a, err := NewFromString(inp.a)
+		if err != nil {
+			t.FailNow()
+		}
+		b, err := NewFromString(inp.b)
+		if err != nil {
+			t.FailNow()
+		}
+		c := a.Add(b)
+		if c.String() != res {
+			t.Errorf("expected %s, got %s", res, c.String())
+		}
+	}
+}
+
+func TestDecimal_Sub(t *testing.T) {
+	type Inp struct {
+		a string
+		b string
+	}
+
+	inputs := map[Inp]string{
+		Inp{"2", "3"}:                     "-1",
+		Inp{"12", "3"}:                    "9",
+		Inp{"-2", "9"}:                    "-11",
+		Inp{"2454495034", "3451204593"}:   "-996709559",
+		Inp{"24544.95034", ".3451204593"}: "24544.6052195407",
+		Inp{".1", "-.1"}:                  "0.2",
+		Inp{".1", ".1"}:                   "0",
+		Inp{"0", "1.001"}:                 "-1.001",
+		Inp{"1.001", "0"}:                 "1.001",
+		Inp{"2.3", ".3"}:                  "2",
+	}
+
+	for inp, res := range inputs {
+		a, err := NewFromString(inp.a)
+		if err != nil {
+			t.FailNow()
+		}
+		b, err := NewFromString(inp.b)
+		if err != nil {
+			t.FailNow()
+		}
+		c := a.Sub(b)
+		if c.String() != res {
+			t.Errorf("expected %s, got %s", res, c.String())
+		}
+	}
+}
+
+func TestDecimal_Neg(t *testing.T) {
+	inputs := map[string]string{
+		"0":     "0",
+		"10":    "-10",
+		"5.56":  "-5.56",
+		"-10":   "10",
+		"-5.56": "5.56",
+	}
+
+	for inp, res := range inputs {
+		a, err := NewFromString(inp)
+		if err != nil {
+			t.FailNow()
+		}
+		b := a.Neg()
+		if b.String() != res {
+			t.Errorf("expected %s, got %s", res, b.String())
+		}
+	}
+}
+
+func TestDecimal_NegFromEmpty(t *testing.T) {
+	a := Decimal{}
+	b := a.Neg()
+	if b.String() != "0" {
+		t.Errorf("expected %s, got %s", "0", b)
+	}
+}
+
+func TestDecimal_Mul(t *testing.T) {
+	type Inp struct {
+		a string
+		b string
+	}
+
+	inputs := map[Inp]string{
+		Inp{"2", "3"}:                     "6",
+		Inp{"2454495034", "3451204593"}:   "8470964534836491162",
+		Inp{"24544.95034", ".3451204593"}: "8470.964534836491162",
+		Inp{".1", ".1"}:                   "0.01",
+		Inp{"0", "1.001"}:                 "0",
+	}
+
+	for inp, res := range inputs {
+		a, err := NewFromString(inp.a)
+		if err != nil {
+			t.FailNow()
+		}
+		b, err := NewFromString(inp.b)
+		if err != nil {
+			t.FailNow()
+		}
+		c := a.Mul(b)
+		if c.String() != res {
+			t.Errorf("expected %s, got %s", res, c.String())
+		}
+	}
+
+	// positive scale
+	c := New(1234, 5).Mul(New(45, -1))
+	if c.String() != "555300000" {
+		t.Errorf("Expected %s, got %s", "555300000", c.String())
+	}
+}
+
+func TestDecimal_Shift(t *testing.T) {
+	type Inp struct {
+		a string
+		b int32
+	}
+
+	inputs := map[Inp]string{
+		Inp{"6", 3}:                         "6000",
+		Inp{"10", -2}:                       "0.1",
+		Inp{"2.2", 1}:                       "22",
+		Inp{"-2.2", -1}:                     "-0.22",
+		Inp{"12.88", 5}:                     "1288000",
+		Inp{"-10234274355545544493", -3}:    "-10234274355545544.493",
+		Inp{"-4612301402398.4753343454", 5}: "-461230140239847533.43454",
+	}
+
+	for inp, expectedStr := range inputs {
+		num, _ := NewFromString(inp.a)
+
+		got := num.Shift(inp.b)
+		expected, _ := NewFromString(expectedStr)
+		if !got.Equal(expected) {
+			t.Errorf("expected %v when shifting %v by %v, got %v",
+				expected, num, inp.b, got)
+		}
+	}
+}
+
+func TestDecimal_Div(t *testing.T) {
+	type Inp struct {
+		a string
+		b string
+	}
+
+	inputs := map[Inp]string{
+		Inp{"6", "3"}:                            "2",
+		Inp{"10", "2"}:                           "5",
+		Inp{"2.2", "1.1"}:                        "2",
+		Inp{"-2.2", "-1.1"}:                      "2",
+		Inp{"12.88", "5.6"}:                      "2.3",
+		Inp{"1023427554493", "43432632"}:         "23563.5628642767953828", // rounded
+		Inp{"1", "434324545566634"}:              "0.0000000000000023",
+		Inp{"1", "3"}:                            "0.3333333333333333",
+		Inp{"2", "3"}:                            "0.6666666666666667", // rounded
+		Inp{"10000", "3"}:                        "3333.3333333333333333",
+		Inp{"10234274355545544493", "-3"}:        "-3411424785181848164.3333333333333333",
+		Inp{"-4612301402398.4753343454", "23.5"}: "-196268144782.9138440146978723",
+	}
+
+	for inp, expectedStr := range inputs {
+		num, err := NewFromString(inp.a)
+		if err != nil {
+			t.FailNow()
+		}
+		denom, err := NewFromString(inp.b)
+		if err != nil {
+			t.FailNow()
+		}
+		got := num.Div(denom)
+		expected, _ := NewFromString(expectedStr)
+		if !got.Equal(expected) {
+			t.Errorf("expected %v when dividing %v by %v, got %v",
+				expected, num, denom, got)
+		}
+		got2 := num.DivRound(denom, int32(DivisionPrecision))
+		if !got2.Equal(expected) {
+			t.Errorf("expected %v on DivRound (%v,%v), got %v", expected, num, denom, got2)
+		}
+	}
+
+	type Inp2 struct {
+		n    int64
+		exp  int32
+		n2   int64
+		exp2 int32
+	}
+
+	// test code path where exp > 0
+	inputs2 := map[Inp2]string{
+		Inp2{124, 10, 3, 1}: "41333333333.3333333333333333",
+		Inp2{124, 10, 3, 0}: "413333333333.3333333333333333",
+		Inp2{124, 10, 6, 1}: "20666666666.6666666666666667",
+		Inp2{124, 10, 6, 0}: "206666666666.6666666666666667",
+		Inp2{10, 10, 10, 1}: "1000000000",
+	}
+
+	for inp, expectedAbs := range inputs2 {
+		for i := -1; i <= 1; i += 2 {
+			for j := -1; j <= 1; j += 2 {
+				n := inp.n * int64(i)
+				n2 := inp.n2 * int64(j)
+				num := New(n, inp.exp)
+				denom := New(n2, inp.exp2)
+				expected := expectedAbs
+				if i != j {
+					expected = "-" + expectedAbs
+				}
+				got := num.Div(denom)
+				if got.String() != expected {
+					t.Errorf("expected %s when dividing %v by %v, got %v",
+						expected, num, denom, got)
+				}
+			}
+		}
+	}
+}
+
+func TestDecimal_QuoRem(t *testing.T) {
+	type Inp4 struct {
+		d   string
+		d2  string
+		exp int32
+		q   string
+		r   string
+	}
+	cases := []Inp4{
+		{"10", "1", 0, "10", "0"},
+		{"1", "10", 0, "0", "1"},
+		{"1", "4", 2, "0.25", "0"},
+		{"1", "8", 2, "0.12", "0.04"},
+		{"10", "3", 1, "3.3", "0.1"},
+		{"100", "3", 1, "33.3", "0.1"},
+		{"1000", "10", -3, "0", "1000"},
+		{"1e-3", "2e-5", 0, "50", "0"},
+		{"1e-3", "2e-3", 1, "0.5", "0"},
+		{"4e-3", "0.8", 4, "5e-3", "0"},
+		{"4.1e-3", "0.8", 3, "5e-3", "1e-4"},
+		{"-4", "-3", 0, "1", "-1"},
+		{"-4", "3", 0, "-1", "-1"},
+	}
+
+	for _, inp4 := range cases {
+		d, _ := NewFromString(inp4.d)
+		d2, _ := NewFromString(inp4.d2)
+		prec := inp4.exp
+		q, r := d.QuoRem(d2, prec)
+		expectedQ, _ := NewFromString(inp4.q)
+		expectedR, _ := NewFromString(inp4.r)
+		if !q.Equal(expectedQ) || !r.Equal(expectedR) {
+			t.Errorf("bad QuoRem division %s , %s , %d got %v, %v expected %s , %s",
+				inp4.d, inp4.d2, prec, q, r, inp4.q, inp4.r)
+		}
+		if !d.Equal(d2.Mul(q).Add(r)) {
+			t.Errorf("not fitting: d=%v, d2= %v, prec=%d, q=%v, r=%v",
+				d, d2, prec, q, r)
+		}
+		if !q.Equal(q.Truncate(prec)) {
+			t.Errorf("quotient wrong precision: d=%v, d2= %v, prec=%d, q=%v, r=%v",
+				d, d2, prec, q, r)
+		}
+		if r.Abs().Cmp(d2.Abs().Mul(New(1, -prec))) >= 0 {
+			t.Errorf("remainder too large: d=%v, d2= %v, prec=%d, q=%v, r=%v",
+				d, d2, prec, q, r)
+		}
+		if r.value.Sign()*d.value.Sign() < 0 {
+			t.Errorf("signum of divisor and rest do not match: d=%v, d2= %v, prec=%d, q=%v, r=%v",
+				d, d2, prec, q, r)
+		}
+	}
+}
+
+type DivTestCase struct {
+	d    Decimal
+	d2   Decimal
+	prec int32
+}
+
+func createDivTestCases() []DivTestCase {
+	res := make([]DivTestCase, 0)
+	var n int32 = 5
+	a := []int{1, 2, 3, 6, 7, 10, 100, 14, 5, 400, 0, 1000000, 1000000 + 1, 1000000 - 1}
+	for s := -1; s < 2; s = s + 2 { // 2
+		for s2 := -1; s2 < 2; s2 = s2 + 2 { // 2
+			for e1 := -n; e1 <= n; e1++ { // 2n+1
+				for e2 := -n; e2 <= n; e2++ { // 2n+1
+					var prec int32
+					for prec = -n; prec <= n; prec++ { // 2n+1
+						for _, v1 := range a { // 11
+							for _, v2 := range a { // 11, even if 0 is skipped
+								sign1 := New(int64(s), 0)
+								sign2 := New(int64(s2), 0)
+								d := sign1.Mul(New(int64(v1), e1))
+								d2 := sign2.Mul(New(int64(v2), e2))
+								res = append(res, DivTestCase{d, d2, prec})
+							}
+						}
+					}
+				}
+			}
+		}
+	}
+	return res
+}
+
+func TestDecimal_QuoRem2(t *testing.T) {
+	for _, tc := range createDivTestCases() {
+		d := tc.d
+		if sign(tc.d2) == 0 {
+			continue
+		}
+		d2 := tc.d2
+		prec := tc.prec
+		q, r := d.QuoRem(d2, prec)
+		// rule 1: d = d2*q +r
+		if !d.Equal(d2.Mul(q).Add(r)) {
+			t.Errorf("not fitting, d=%v, d2=%v, prec=%d, q=%v, r=%v",
+				d, d2, prec, q, r)
+		}
+		// rule 2: q is integral multiple of 10^(-prec)
+		if !q.Equal(q.Truncate(prec)) {
+			t.Errorf("quotient wrong precision, d=%v, d2=%v, prec=%d, q=%v, r=%v",
+				d, d2, prec, q, r)
+		}
+		// rule 3: abs(r)<abs(d) * 10^(-prec)
+		if r.Abs().Cmp(d2.Abs().Mul(New(1, -prec))) >= 0 {
+			t.Errorf("remainder too large, d=%v, d2=%v, prec=%d, q=%v, r=%v",
+				d, d2, prec, q, r)
+		}
+		// rule 4: r and d have the same sign
+		if r.value.Sign()*d.value.Sign() < 0 {
+			t.Errorf("signum of divisor and rest do not match, "+
+				"d=%v, d2=%v, prec=%d, q=%v, r=%v",
+				d, d2, prec, q, r)
+		}
+	}
+}
+
+// this is the old Div method from decimal
+// Div returns d / d2. If it doesn't divide exactly, the result will have
+// DivisionPrecision digits after the decimal point.
+func (d Decimal) DivOld(d2 Decimal, prec int) Decimal {
+	// NOTE(vadim): division is hard, use Rat to do it
+	ratNum := d.Rat()
+	ratDenom := d2.Rat()
+
+	quoRat := big.NewRat(0, 1).Quo(ratNum, ratDenom)
+
+	// HACK(vadim): converting from Rat to Decimal inefficiently for now
+	ret, err := NewFromString(quoRat.FloatString(prec))
+	if err != nil {
+		panic(err) // this should never happen
+	}
+	return ret
+}
+
+func sign(d Decimal) int {
+	return d.value.Sign()
+}
+
+// rules for rounded divide, rounded to integer
+// rounded_divide(d,d2) = q
+// sign q * sign (d/d2) >= 0
+// for d and d2 >0 :
+// q is already rounded
+// q = d/d2 + r , with r > -0.5 and r <= 0.5
+// thus q-d/d2 = r, with r > -0.5 and r <= 0.5
+// and d2 q -d = r d2 with r d2 > -d2/2 and r d2 <= d2/2
+// and 2 (d2 q -d) = x with x > -d2 and x <= d2
+// if we factor in precision then x > -d2 * 10^(-precision) and x <= d2 * 10(-precision)
+
+func TestDecimal_DivRound(t *testing.T) {
+	cases := []struct {
+		d      string
+		d2     string
+		prec   int32
+		result string
+	}{
+		{"2", "2", 0, "1"},
+		{"1", "2", 0, "1"},
+		{"-1", "2", 0, "-1"},
+		{"-1", "-2", 0, "1"},
+		{"1", "-2", 0, "-1"},
+		{"1", "-20", 1, "-0.1"},
+		{"1", "-20", 2, "-0.05"},
+		{"1", "20.0000000000000000001", 1, "0"},
+		{"1", "19.9999999999999999999", 1, "0.1"},
+	}
+	for _, s := range cases {
+		d, _ := NewFromString(s.d)
+		d2, _ := NewFromString(s.d2)
+		result, _ := NewFromString(s.result)
+		prec := s.prec
+		q := d.DivRound(d2, prec)
+		if sign(q)*sign(d)*sign(d2) < 0 {
+			t.Errorf("sign of quotient wrong, got: %v/%v is about %v", d, d2, q)
+		}
+		x := q.Mul(d2).Abs().Sub(d.Abs()).Mul(New(2, 0))
+		if x.Cmp(d2.Abs().Mul(New(1, -prec))) > 0 {
+			t.Errorf("wrong rounding, got: %v/%v prec=%d is about %v", d, d2, prec, q)
+		}
+		if x.Cmp(d2.Abs().Mul(New(-1, -prec))) <= 0 {
+			t.Errorf("wrong rounding, got: %v/%v prec=%d is about %v", d, d2, prec, q)
+		}
+		if !q.Equal(result) {
+			t.Errorf("rounded division wrong %s / %s scale %d = %s, got %v", s.d, s.d2, prec, s.result, q)
+		}
+	}
+}
+
+func TestDecimal_DivRound2(t *testing.T) {
+	for _, tc := range createDivTestCases() {
+		d := tc.d
+		if sign(tc.d2) == 0 {
+			continue
+		}
+		d2 := tc.d2
+		prec := tc.prec
+		q := d.DivRound(d2, prec)
+		if sign(q)*sign(d)*sign(d2) < 0 {
+			t.Errorf("sign of quotient wrong, got: %v/%v is about %v", d, d2, q)
+		}
+		x := q.Mul(d2).Abs().Sub(d.Abs()).Mul(New(2, 0))
+		if x.Cmp(d2.Abs().Mul(New(1, -prec))) > 0 {
+			t.Errorf("wrong rounding, got: %v/%v prec=%d is about %v", d, d2, prec, q)
+		}
+		if x.Cmp(d2.Abs().Mul(New(-1, -prec))) <= 0 {
+			t.Errorf("wrong rounding, got: %v/%v prec=%d is about %v", d, d2, prec, q)
+		}
+	}
+}
+
+func TestDecimal_RoundCash(t *testing.T) {
+	tests := []struct {
+		d        string
+		interval uint8
+		result   string
+	}{
+		{"3.44", 5, "3.45"},
+		{"3.43", 5, "3.45"},
+		{"3.42", 5, "3.40"},
+		{"3.425", 5, "3.45"},
+		{"3.47", 5, "3.45"},
+		{"3.478", 5, "3.50"},
+		{"3.48", 5, "3.50"},
+		{"348", 5, "348"},
+
+		{"3.23", 10, "3.20"},
+		{"3.33", 10, "3.30"},
+		{"3.53", 10, "3.50"},
+		{"3.949", 10, "3.90"},
+		{"3.95", 10, "4.00"},
+		{"395", 10, "395"},
+
+		{"3.23", 25, "3.25"},
+		{"3.33", 25, "3.25"},
+		{"3.53", 25, "3.50"},
+		{"3.93", 25, "4.00"},
+		{"3.41", 25, "3.50"},
+
+		{"3.249", 50, "3.00"},
+		{"3.33", 50, "3.50"},
+		{"3.749999999", 50, "3.50"},
+		{"3.75", 50, "4.00"},
+		{"3.93", 50, "4.00"},
+		{"393", 50, "393"},
+
+		{"3.249", 100, "3.00"},
+		{"3.49999", 100, "3.00"},
+		{"3.50", 100, "4.00"},
+		{"3.75", 100, "4.00"},
+		{"3.93", 100, "4.00"},
+		{"393", 100, "393"},
+	}
+	for i, test := range tests {
+		d, _ := NewFromString(test.d)
+		haveRounded := d.RoundCash(test.interval)
+		result, _ := NewFromString(test.result)
+
+		if !haveRounded.Equal(result) {
+			t.Errorf("Index %d: Cash rounding for %q interval %d want %q, have %q", i, test.d, test.interval, test.result, haveRounded)
+		}
+	}
+}
+
+func TestDecimal_RoundCash_Panic(t *testing.T) {
+	defer func() {
+		if r := recover(); r != nil {
+			if have, ok := r.(string); ok {
+				const want = "Decimal does not support this Cash rounding interval `231`. Supported: 5, 10, 25, 50, 100"
+				if want != have {
+					t.Errorf("\nWant: %q\nHave: %q", want, have)
+				}
+			} else {
+				t.Errorf("Panic should contain an error string but got:\n%+v", r)
+			}
+		} else {
+			t.Error("Expecting a panic but got nothing")
+		}
+	}()
+	d, _ := NewFromString("1")
+	d.RoundCash(231)
+}
+
+func TestDecimal_Mod(t *testing.T) {
+	type Inp struct {
+		a string
+		b string
+	}
+
+	inputs := map[Inp]string{
+		Inp{"3", "2"}:                     "1",
+		Inp{"3451204593", "2454495034"}:   "996709559",
+		Inp{"24544.95034", ".3451204593"}: "0.3283950433",
+		Inp{".1", ".1"}:                   "0",
+		Inp{"0", "1.001"}:                 "0",
+		Inp{"-7.5", "2"}:                  "-1.5",
+		Inp{"7.5", "-2"}:                  "1.5",
+		Inp{"-7.5", "-2"}:                 "-1.5",
+	}
+
+	for inp, res := range inputs {
+		a, err := NewFromString(inp.a)
+		if err != nil {
+			t.FailNow()
+		}
+		b, err := NewFromString(inp.b)
+		if err != nil {
+			t.FailNow()
+		}
+		c := a.Mod(b)
+		if c.String() != res {
+			t.Errorf("expected %s, got %s", res, c.String())
+		}
+	}
+}
+
+func TestDecimal_Overflow(t *testing.T) {
+	if !didPanic(func() { New(1, math.MinInt32).Mul(New(1, math.MinInt32)) }) {
+		t.Fatalf("should have gotten an overflow panic")
+	}
+	if !didPanic(func() { New(1, math.MaxInt32).Mul(New(1, math.MaxInt32)) }) {
+		t.Fatalf("should have gotten an overflow panic")
+	}
+}
+
+func TestDecimal_ExtremeValues(t *testing.T) {
+	// NOTE(vadim): this test takes pretty much forever
+	if testing.Short() {
+		t.Skip()
+	}
+
+	// NOTE(vadim): Seriously, the numbers involved are so large that this
+	// test will take way too long, so mark it as success if it takes over
+	// 1 second. The way this test typically fails (integer overflow) is that
+	// a wrong result appears quickly, so if it takes a long time then it is
+	// probably working properly.
+	// Why even bother testing this? Completeness, I guess. -Vadim
+	const timeLimit = 1 * time.Second
+	test := func(f func()) {
+		c := make(chan bool)
+		go func() {
+			f()
+			close(c)
+		}()
+		select {
+		case <-c:
+		case <-time.After(timeLimit):
+		}
+	}
+
+	test(func() {
+		got := New(123, math.MinInt32).Floor()
+		if !got.Equal(NewFromFloat(0)) {
+			t.Errorf("Error: got %s, expected 0", got)
+		}
+	})
+	test(func() {
+		got := New(123, math.MinInt32).Ceil()
+		if !got.Equal(NewFromFloat(1)) {
+			t.Errorf("Error: got %s, expected 1", got)
+		}
+	})
+	test(func() {
+		got := New(123, math.MinInt32).Rat().FloatString(10)
+		expected := "0.0000000000"
+		if got != expected {
+			t.Errorf("Error: got %s, expected %s", got, expected)
+		}
+	})
+}
+
+func TestIntPart(t *testing.T) {
+	for _, testCase := range []struct {
+		Dec     string
+		IntPart int64
+	}{
+		{"0.01", 0},
+		{"12.1", 12},
+		{"9999.999", 9999},
+		{"-32768.01234", -32768},
+	} {
+		d, err := NewFromString(testCase.Dec)
+		if err != nil {
+			t.Fatal(err)
+		}
+		if d.IntPart() != testCase.IntPart {
+			t.Errorf("expect %d, got %d", testCase.IntPart, d.IntPart())
+		}
+	}
+}
+
+func TestBigInt(t *testing.T) {
+	testCases := []struct {
+		Dec       string
+		BigIntRep string
+	}{
+		{"0.0", "0"},
+		{"0.00000", "0"},
+		{"0.01", "0"},
+		{"12.1", "12"},
+		{"9999.999", "9999"},
+		{"-32768.01234", "-32768"},
+		{"-572372.0000000001", "-572372"},
+	}
+
+	for _, testCase := range testCases {
+		d, err := NewFromString(testCase.Dec)
+		if err != nil {
+			t.Fatal(err)
+		}
+		if d.BigInt().String() != testCase.BigIntRep {
+			t.Errorf("expect %s, got %s", testCase.BigIntRep, d.BigInt())
+		}
+	}
+}
+
+func TestBigFloat(t *testing.T) {
+	testCases := []struct {
+		Dec         string
+		BigFloatRep string
+	}{
+		{"0.0", "0"},
+		{"0.00000", "0"},
+		{"0.01", "0.01"},
+		{"12.1", "12.1"},
+		{"9999.999", "9999.999"},
+		{"-32768.01234", "-32768.01234"},
+		{"-572372.0000000001", "-572372"},
+		{"512.012345123451234512345", "512.0123451"},
+		{"1.010101010101010101010101010101", "1.01010101"},
+		{"55555555.555555555555555555555", "55555555.56"},
+	}
+
+	for _, testCase := range testCases {
+		d, err := NewFromString(testCase.Dec)
+		if err != nil {
+			t.Fatal(err)
+		}
+		if d.BigFloat().String() != testCase.BigFloatRep {
+			t.Errorf("expect %s, got %s", testCase.BigFloatRep, d.BigFloat())
+		}
+	}
+}
+
+func TestDecimal_Min(t *testing.T) {
+	// the first element in the array is the expected answer, rest are inputs
+	testCases := [][]float64{
+		{0, 0},
+		{1, 1},
+		{-1, -1},
+		{1, 1, 2},
+		{-2, 1, 2, -2},
+		{-3, 0, 2, -2, -3},
+	}
+
+	for _, test := range testCases {
+		expected, input := test[0], test[1:]
+		expectedDecimal := NewFromFloat(expected)
+		decimalInput := []Decimal{}
+		for _, inp := range input {
+			d := NewFromFloat(inp)
+			decimalInput = append(decimalInput, d)
+		}
+		got := Min(decimalInput[0], decimalInput[1:]...)
+		if !got.Equal(expectedDecimal) {
+			t.Errorf("Expected %v, got %v, input=%+v", expectedDecimal, got,
+				decimalInput)
+		}
+	}
+}
+
+func TestDecimal_Max(t *testing.T) {
+	// the first element in the array is the expected answer, rest are inputs
+	testCases := [][]float64{
+		{0, 0},
+		{1, 1},
+		{-1, -1},
+		{2, 1, 2},
+		{2, 1, 2, -2},
+		{3, 0, 3, -2},
+		{-2, -3, -2},
+	}
+
+	for _, test := range testCases {
+		expected, input := test[0], test[1:]
+		expectedDecimal := NewFromFloat(expected)
+		decimalInput := []Decimal{}
+		for _, inp := range input {
+			d := NewFromFloat(inp)
+			decimalInput = append(decimalInput, d)
+		}
+		got := Max(decimalInput[0], decimalInput[1:]...)
+		if !got.Equal(expectedDecimal) {
+			t.Errorf("Expected %v, got %v, input=%+v", expectedDecimal, got,
+				decimalInput)
+		}
+	}
+}
+
+func TestDecimal_Scan(t *testing.T) {
+	// test the Scan method that implements the
+	// sql.Scanner interface
+	// check for the for different type of values
+	// that are possible to be received from the database
+	// drivers
+
+	// in normal operations the db driver (sqlite at least)
+	// will return an int64 if you specified a numeric format
+	a := Decimal{}
+	dbvalue := 54.33
+	expected := NewFromFloat(dbvalue)
+
+	err := a.Scan(dbvalue)
+	if err != nil {
+		// Scan failed... no need to test result value
+		t.Errorf("a.Scan(54.33) failed with message: %s", err)
+
+	} else {
+		// Scan succeeded... test resulting values
+		if !a.Equal(expected) {
+			t.Errorf("%s does not equal to %s", a, expected)
+		}
+	}
+
+	// apparently MySQL 5.7.16 and returns these as float32 so we need
+	// to handle these as well
+	dbvalueFloat32 := float32(54.33)
+	expected = NewFromFloat(float64(dbvalueFloat32))
+
+	err = a.Scan(dbvalueFloat32)
+	if err != nil {
+		// Scan failed... no need to test result value
+		t.Errorf("a.Scan(54.33) failed with message: %s", err)
+
+	} else {
+		// Scan succeeded... test resulting values
+		if !a.Equal(expected) {
+			t.Errorf("%s does not equal to %s", a, expected)
+		}
+	}
+
+	// at least SQLite returns an int64 when 0 is stored in the db
+	// and you specified a numeric format on the schema
+	dbvalueInt := int64(0)
+	expected = New(dbvalueInt, 0)
+
+	err = a.Scan(dbvalueInt)
+	if err != nil {
+		// Scan failed... no need to test result value
+		t.Errorf("a.Scan(0) failed with message: %s", err)
+
+	} else {
+		// Scan succeeded... test resulting values
+		if !a.Equal(expected) {
+			t.Errorf("%s does not equal to %s", a, expected)
+		}
+	}
+
+	// in case you specified a varchar in your SQL schema,
+	// the database driver will return byte slice []byte
+	valueStr := "535.666"
+	dbvalueStr := []byte(valueStr)
+	expected, err = NewFromString(valueStr)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	err = a.Scan(dbvalueStr)
+	if err != nil {
+		// Scan failed... no need to test result value
+		t.Errorf("a.Scan('535.666') failed with message: %s", err)
+
+	} else {
+		// Scan succeeded... test resulting values
+		if !a.Equal(expected) {
+			t.Errorf("%s does not equal to %s", a, expected)
+		}
+	}
+
+	// lib/pq can also return strings
+	expected, err = NewFromString(valueStr)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	err = a.Scan(valueStr)
+	if err != nil {
+		// Scan failed... no need to test result value
+		t.Errorf("a.Scan('535.666') failed with message: %s", err)
+	} else {
+		// Scan succeeded... test resulting values
+		if !a.Equal(expected) {
+			t.Errorf("%s does not equal to %s", a, expected)
+		}
+	}
+
+	type foo struct{}
+	err = a.Scan(foo{})
+	if err == nil {
+		t.Errorf("a.Scan(Foo{}) should have thrown an error but did not")
+	}
+}
+
+func TestDecimal_Value(t *testing.T) {
+	// Make sure this does implement the database/sql's driver.Valuer interface
+	var d Decimal
+	if _, ok := interface{}(d).(driver.Valuer); !ok {
+		t.Error("Decimal does not implement driver.Valuer")
+	}
+
+	// check that normal case is handled appropriately
+	a := New(1234, -2)
+	expected := "12.34"
+	value, err := a.Value()
+	if err != nil {
+		t.Errorf("Decimal(12.34).Value() failed with message: %s", err)
+	} else if value.(string) != expected {
+		t.Errorf("%s does not equal to %s", a, expected)
+	}
+}
+
+// old tests after this line
+
+func TestDecimal_Scale(t *testing.T) {
+	a := New(1234, -3)
+	if a.Exponent() != -3 {
+		t.Errorf("error")
+	}
+}
+
+func TestDecimal_Abs1(t *testing.T) {
+	a := New(-1234, -4)
+	b := New(1234, -4)
+
+	c := a.Abs()
+	if c.Cmp(b) != 0 {
+		t.Errorf("error")
+	}
+}
+
+func TestDecimal_Abs2(t *testing.T) {
+	a := New(-1234, -4)
+	b := New(1234, -4)
+
+	c := b.Abs()
+	if c.Cmp(a) == 0 {
+		t.Errorf("error")
+	}
+}
+
+func TestDecimal_Equalities(t *testing.T) {
+	a := New(1234, 3)
+	b := New(1234, 3)
+	c := New(1234, 4)
+
+	if !a.Equal(b) {
+		t.Errorf("%q should equal %q", a, b)
+	}
+	if a.Equal(c) {
+		t.Errorf("%q should not equal %q", a, c)
+	}
+
+	// note, this block should be deprecated, here for backwards compatibility
+	if !a.Equals(b) {
+		t.Errorf("%q should equal %q", a, b)
+	}
+
+	if !c.GreaterThan(b) {
+		t.Errorf("%q should be greater than  %q", c, b)
+	}
+	if b.GreaterThan(c) {
+		t.Errorf("%q should not be greater than  %q", b, c)
+	}
+	if !a.GreaterThanOrEqual(b) {
+		t.Errorf("%q should be greater or equal %q", a, b)
+	}
+	if !c.GreaterThanOrEqual(b) {
+		t.Errorf("%q should be greater or equal %q", c, b)
+	}
+	if b.GreaterThanOrEqual(c) {
+		t.Errorf("%q should not be greater or equal %q", b, c)
+	}
+	if !b.LessThan(c) {
+		t.Errorf("%q should be less than %q", a, b)
+	}
+	if c.LessThan(b) {
+		t.Errorf("%q should not be less than  %q", a, b)
+	}
+	if !a.LessThanOrEqual(b) {
+		t.Errorf("%q should be less than or equal %q", a, b)
+	}
+	if !b.LessThanOrEqual(c) {
+		t.Errorf("%q should be less than or equal  %q", a, b)
+	}
+	if c.LessThanOrEqual(b) {
+		t.Errorf("%q should not be less than or equal %q", a, b)
+	}
+}
+
+func TestDecimal_ScalesNotEqual(t *testing.T) {
+	a := New(1234, 2)
+	b := New(1234, 3)
+	if a.Equal(b) {
+		t.Errorf("%q should not equal %q", a, b)
+	}
+}
+
+func TestDecimal_Cmp1(t *testing.T) {
+	a := New(123, 3)
+	b := New(-1234, 2)
+
+	if a.Cmp(b) != 1 {
+		t.Errorf("Error")
+	}
+}
+
+func TestDecimal_Cmp2(t *testing.T) {
+	a := New(123, 3)
+	b := New(1234, 2)
+
+	if a.Cmp(b) != -1 {
+		t.Errorf("Error")
+	}
+}
+
+func TestPow(t *testing.T) {
+	a := New(4, 0)
+	b := New(2, 0)
+	x := a.Pow(b)
+	if x.String() != "16" {
+		t.Errorf("Error, saw %s", x.String())
+	}
+}
+
+func TestNegativePow(t *testing.T) {
+	a := New(4, 0)
+	b := New(-2, 0)
+	x := a.Pow(b)
+	if x.String() != "0.0625" {
+		t.Errorf("Error, saw %s", x.String())
+	}
+}
+
+func TestDecimal_IsInteger(t *testing.T) {
+	for _, testCase := range []struct {
+		Dec       string
+		IsInteger bool
+	}{
+		{"0", true},
+		{"0.0000", true},
+		{"0.01", false},
+		{"0.01010101010000", false},
+		{"12.0", true},
+		{"12.00000000000000", true},
+		{"12.10000", false},
+		{"9999.0000", true},
+		{"99999999.000000000", true},
+		{"-656323444.0000000000000", true},
+		{"-32768.01234", false},
+		{"-32768.0123423562623600000", false},
+	} {
+		d, err := NewFromString(testCase.Dec)
+		if err != nil {
+			t.Fatal(err)
+		}
+		if d.IsInteger() != testCase.IsInteger {
+			t.Errorf("expect %t, got %t, for %s", testCase.IsInteger, d.IsInteger(), testCase.Dec)
+		}
+	}
+}
+
+func TestDecimal_Sign(t *testing.T) {
+	if Zero.Sign() != 0 {
+		t.Errorf("%q should have sign 0", Zero)
+	}
+
+	one := New(1, 0)
+	if one.Sign() != 1 {
+		t.Errorf("%q should have sign 1", one)
+	}
+
+	mone := New(-1, 0)
+	if mone.Sign() != -1 {
+		t.Errorf("%q should have sign -1", mone)
+	}
+}
+
+func didPanic(f func()) bool {
+	ret := false
+	func() {
+
+		defer func() {
+			if message := recover(); message != nil {
+				ret = true
+			}
+		}()
+
+		// call the target function
+		f()
+
+	}()
+
+	return ret
+
+}
+
+func TestDecimal_Coefficient(t *testing.T) {
+	d := New(123, 0)
+	co := d.Coefficient()
+	if co.Int64() != 123 {
+		t.Error("Coefficient should be 123; Got:", co)
+	}
+	co.Set(big.NewInt(0))
+	if d.IntPart() != 123 {
+		t.Error("Modifying coefficient modified Decimal; Got:", d)
+	}
+}
+
+func TestNullDecimal_Scan(t *testing.T) {
+	// test the Scan method that implements the
+	// sql.Scanner interface
+	// check for the for different type of values
+	// that are possible to be received from the database
+	// drivers
+
+	// in normal operations the db driver (sqlite at least)
+	// will return an int64 if you specified a numeric format
+
+	// Make sure handles nil values
+	a := NullDecimal{}
+	var dbvaluePtr interface{}
+	err := a.Scan(dbvaluePtr)
+	if err != nil {
+		// Scan failed... no need to test result value
+		t.Errorf("a.Scan(nil) failed with message: %s", err)
+	} else {
+		if a.Valid {
+			t.Errorf("%s is not null", a.Decimal)
+		}
+	}
+
+	dbvalue := 54.33
+	expected := NewFromFloat(dbvalue)
+
+	err = a.Scan(dbvalue)
+	if err != nil {
+		// Scan failed... no need to test result value
+		t.Errorf("a.Scan(54.33) failed with message: %s", err)
+
+	} else {
+		// Scan succeeded... test resulting values
+		if !a.Valid {
+			t.Errorf("%s is null", a.Decimal)
+		} else if !a.Decimal.Equals(expected) {
+			t.Errorf("%s does not equal to %s", a.Decimal, expected)
+		}
+	}
+
+	// at least SQLite returns an int64 when 0 is stored in the db
+	// and you specified a numeric format on the schema
+	dbvalueInt := int64(0)
+	expected = New(dbvalueInt, 0)
+
+	err = a.Scan(dbvalueInt)
+	if err != nil {
+		// Scan failed... no need to test result value
+		t.Errorf("a.Scan(0) failed with message: %s", err)
+
+	} else {
+		// Scan succeeded... test resulting values
+		if !a.Valid {
+			t.Errorf("%s is null", a.Decimal)
+		} else if !a.Decimal.Equals(expected) {
+			t.Errorf("%v does not equal %v", a, expected)
+		}
+	}
+
+	// in case you specified a varchar in your SQL schema,
+	// the database driver will return byte slice []byte
+	valueStr := "535.666"
+	dbvalueStr := []byte(valueStr)
+	expected, err = NewFromString(valueStr)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	err = a.Scan(dbvalueStr)
+	if err != nil {
+		// Scan failed... no need to test result value
+		t.Errorf("a.Scan('535.666') failed with message: %s", err)
+
+	} else {
+		// Scan succeeded... test resulting values
+		if !a.Valid {
+			t.Errorf("%s is null", a.Decimal)
+		} else if !a.Decimal.Equals(expected) {
+			t.Errorf("%v does not equal %v", a, expected)
+		}
+	}
+
+	// lib/pq can also return strings
+	expected, err = NewFromString(valueStr)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	err = a.Scan(valueStr)
+	if err != nil {
+		// Scan failed... no need to test result value
+		t.Errorf("a.Scan('535.666') failed with message: %s", err)
+	} else {
+		// Scan succeeded... test resulting values
+		if !a.Valid {
+			t.Errorf("%s is null", a.Decimal)
+		} else if !a.Decimal.Equals(expected) {
+			t.Errorf("%v does not equal %v", a, expected)
+		}
+	}
+}
+
+func TestNullDecimal_Value(t *testing.T) {
+	// Make sure this does implement the database/sql's driver.Valuer interface
+	var nullDecimal NullDecimal
+	if _, ok := interface{}(nullDecimal).(driver.Valuer); !ok {
+		t.Error("NullDecimal does not implement driver.Valuer")
+	}
+
+	// check that null is handled appropriately
+	value, err := nullDecimal.Value()
+	if err != nil {
+		t.Errorf("NullDecimal{}.Valid() failed with message: %s", err)
+	} else if value != nil {
+		t.Errorf("%v is not nil", value)
+	}
+
+	// check that normal case is handled appropriately
+	a := NullDecimal{Decimal: New(1234, -2), Valid: true}
+	expected := "12.34"
+	value, err = a.Value()
+	if err != nil {
+		t.Errorf("NullDecimal(12.34).Value() failed with message: %s", err)
+	} else if value.(string) != expected {
+		t.Errorf("%v does not equal %v", a, expected)
+	}
+}
+
+func TestBinary(t *testing.T) {
+	for _, y := range testTable {
+		x := y.float
+
+		// Create the decimal
+		d1 := NewFromFloat(x)
+
+		// Encode to binary
+		b, err := d1.MarshalBinary()
+		if err != nil {
+			t.Errorf("error marshalling %v to binary: %v", d1, err)
+		}
+
+		// Restore from binary
+		var d2 Decimal
+		err = (&d2).UnmarshalBinary(b)
+		if err != nil {
+			t.Errorf("error unmarshalling from binary: %v", err)
+		}
+
+		// The restored decimal should equal the original
+		if !d1.Equals(d2) {
+			t.Errorf("expected %v when restoring, got %v", d1, d2)
+		}
+	}
+}
+
+func slicesEqual(a, b []byte) bool {
+	for i, val := range a {
+		if b[i] != val {
+			return false
+		}
+	}
+	return true
+}
+
+func TestGobEncode(t *testing.T) {
+	for _, y := range testTable {
+		x := y.float
+		d1 := NewFromFloat(x)
+
+		b1, err := d1.GobEncode()
+		if err != nil {
+			t.Errorf("error encoding %v to binary: %v", d1, err)
+		}
+
+		d2 := NewFromFloat(x)
+
+		b2, err := d2.GobEncode()
+		if err != nil {
+			t.Errorf("error encoding %v to binary: %v", d2, err)
+		}
+
+		if !slicesEqual(b1, b2) {
+			t.Errorf("something about the gobencode is not working properly \n%v\n%v", b1, b2)
+		}
+
+		var d3 Decimal
+		err = d3.GobDecode(b1)
+		if err != nil {
+			t.Errorf("Error gobdecoding %v, got %v", b1, d3)
+		}
+		var d4 Decimal
+		err = d4.GobDecode(b2)
+		if err != nil {
+			t.Errorf("Error gobdecoding %v, got %v", b2, d4)
+		}
+
+		eq := d3.Equal(d4)
+		if eq != true {
+			t.Errorf("Encoding then decoding mutated Decimal")
+		}
+
+		eq = d1.Equal(d3)
+		if eq != true {
+			t.Errorf("Error gobencoding/decoding %v, got %v", d1, d3)
+		}
+	}
+}
+
+func TestSum(t *testing.T) {
+	vals := make([]Decimal, 10)
+	var i = int64(0)
+
+	for key := range vals {
+		vals[key] = New(i, 0)
+		i++
+	}
+
+	sum := Sum(vals[0], vals[1:]...)
+	if !sum.Equal(New(45, 0)) {
+		t.Errorf("Failed to calculate sum, expected %s got %s", New(45, 0), sum)
+	}
+}
+
+func TestAvg(t *testing.T) {
+	vals := make([]Decimal, 10)
+	var i = int64(0)
+
+	for key := range vals {
+		vals[key] = New(i, 0)
+		i++
+	}
+
+	avg := Avg(vals[0], vals[1:]...)
+	if !avg.Equal(NewFromFloat(4.5)) {
+		t.Errorf("Failed to calculate average, expected %s got %s", NewFromFloat(4.5).String(), avg.String())
+	}
+}
+
+func TestRoundBankAnomaly(t *testing.T) {
+	a := New(25, -1)
+	b := New(250, -2)
+
+	if !a.Equal(b) {
+		t.Errorf("Expected %s to equal %s", a, b)
+	}
+
+	expected := New(2, 0)
+
+	aRounded := a.RoundBank(0)
+	if !aRounded.Equal(expected) {
+		t.Errorf("Expected bank rounding %s to equal %s, but it was %s", a, expected, aRounded)
+	}
+
+	bRounded := b.RoundBank(0)
+	if !bRounded.Equal(expected) {
+		t.Errorf("Expected bank rounding %s to equal %s, but it was %s", b, expected, bRounded)
+	}
+}
+
+// Trig tests
+
+// For Atan
+func TestAtan(t *testing.T) {
+	inps := []string{
+		"-2.91919191919191919",
+		"-1.0",
+		"-0.25",
+		"0.0",
+		"0.33",
+		"1.0",
+		"5.0",
+		"10",
+		"11000020.2407442310156021090304691671842603586882014729198302312846062338790031898128063403419218957424",
+	}
+	sols := []string{
+		"-1.24076438822058001027437062753106",
+		"-0.78539816339744833061616997868383",
+		"-0.24497866312686415",
+		"0.0",
+		"0.318747560420644443",
+		"0.78539816339744833061616997868383",
+		"1.37340076694501580123233995736766",
+		"1.47112767430373453123233995736766",
+		"1.57079623588597296123259450235374",
+	}
+	for i, inp := range inps {
+		d, err := NewFromString(inp)
+		if err != nil {
+			t.FailNow()
+		}
+		s, err := NewFromString(sols[i])
+		if err != nil {
+			t.FailNow()
+		}
+		a := d.Atan()
+		if !a.Equal(s) {
+			t.Errorf("expected %s, got %s", s, a)
+		}
+	}
+}
+
+// For Sin
+func TestSin(t *testing.T) {
+	inps := []string{
+		"-2.91919191919191919",
+		"-1.0",
+		"-0.25",
+		"0.0",
+		"0.33",
+		"1.0",
+		"5.0",
+		"10",
+		"11000020.2407442310156021090304691671842603586882014729198302312846062338790031898128063403419218957424",
+	}
+	sols := []string{"-0.22057186252002995641471297726318877448242875710373383657841216606788849153474483300147427943530288911869356126149550184271061369789963810497434594683859566879253561990821788142048867910104964466745284318577343435957806286762494529983369776697504436326725441516925396488258485248699247367113416543705253919473126183478178486954138205996912770183192357029798618739277146694040778731661407420114923656224752540889120768",
+		"-0.841470984807896544828551915928318375739843472469519282898610111931110319333748010828751784005573402229699531838022117989945539661588502120624574802425114599802714611508860519655182175315926637327774878594985045816542706701485174683683726979309922117859910272413672784175028365607893544855897795184024100973080880074046886009375162838756876336134083638363801171409953672944184918309063800980214873465660723218405962257950683415203634506166523593278",
+		"-0.2474039592545229296662577977006816864013671875",
+		"0",
+		"0.3240430283948683457891331120415701894104386268737728",
+		"0.841470984807896544828551915928318375739843472469519282898610111931110319333748010828751784005573402229699531838022117989945539661588502120624574802425114599802714611508860519655182175315926637327774878594985045816542706701485174683683726979309922117859910272413672784175028365607893544855897795184024100973080880074046886009375162838756876336134083638363801171409953672944184918309063800980214873465660723218405962257950683415203634506166523593278",
+		"-0.958924274663138409032065951037351417114444405831206421994322505831797734568720303321152847999323782235893449831846516332891972309733806145798957570823292783131379570446989311599459252931842975162373777189193072018951049969744350662993214861042908755303566670204873618202680865638534865944483058650517380292320436016362659617294570185140789829574277032406195741535138712427510938542219940873171248862329526140744770994303733112530324791184417282382",
+		"-0.54402111088937016772477554483765124109312606762621462357463994520238396180161585438877562935656067241573063207614488370477645194661241525080677431257416988398683714890165970942834453391033857378247849486306346743023618509617104937236345831462093934032592562972419977883837745736210439651143668255744843041350221801750331646628192115694352540293150183983357476391787825596543270240461102629075832777618592034309799936",
+		"-0.564291758480422881634770440632390475980828840253516895637281099241819037882007239070203007530085741820184955492382572029153491807930868879341091067301689987699567034024159005627332722089169680203292567574310010066799858914647295684974242359142300929248173166551428537696685165964880390889406578530338963341989826231514301546476672476399906348023294571001061677668735117509440368611093448917120819545826797975989350435900286332895885871219875665471968941335407351099209738417818747252638912592184093301853338763294381446907254104878969784040526201729163408095795934201105630182851806342356035203279670146684553491616847294749721014579109870396804713831114709372638323643327823671187472335866664108658093206409882794958673673978956925250261545083579947618620746006004554405785185537391110314728988164693223775249484198058394348289545771967707968288542718255197272633789792059019367104377340604030147471453833808674013259696102003732963091159662478879760121731138091114134586544668859915547568540172541576138084166990547345181184322550297604278946942918844039406876827936831612756344331500301118652183156052728447906384772901595431751550607818380262138322673253023464533931883787069611052589166000316238423939491520880451263927981787175602294299295744",
+	}
+	for i, inp := range inps {
+		d, err := NewFromString(inp)
+		if err != nil {
+			t.FailNow()
+		}
+		s, err := NewFromString(sols[i])
+		if err != nil {
+			t.FailNow()
+		}
+		a := d.Sin()
+		if !a.Equal(s) {
+			t.Errorf("expected %s, got %s", s, a)
+		}
+	}
+}
+
+// For Cos
+func TestCos(t *testing.T) {
+	inps := []string{
+		"-2.91919191919191919",
+		"-1.0",
+		"-0.25",
+		"0.0",
+		"0.33",
+		"1.0",
+		"5.0",
+		"10",
+		"11000020.2407442310156021090304691671842603586882014729198302312846062338790031898128063403419218957424",
+	}
+	sols := []string{
+		"-0.975370726167463467746508538219884948528729295145689640359666742268127382748782064668565276308334226452812521220478854320025773591423493734486361306323829818426063430805234608660356853863442937297855742231573288105774823103008774355455799906250461848079705023428527473474556899228935370709945979509634251305018978306493011197513482210179171510947538040406781879762352211326273272515279567525396877609653501706919545667682725671944948392322552266752",
+		"0.54030230586813965874561515067176071767603141150991567490927772778673118786033739102174242337864109186439207498973007363884202112942385976796862442063752663646870430360736682397798633852405003167527051283327366631405990604840629657123985368031838052877290142895506386796217551784101265975360960112885444847880134909594560331781699767647860744559228420471946006511861233129745921297270844542687374552066388998112901504",
+		"0.968912421710644784099084544806854121387004852294921875",
+		"1",
+		"0.9460423435283869715490383692051286742343482760977712222",
+		"0.54030230586813965874561515067176071767603141150991567490927772778673118786033739102174242337864109186439207498973007363884202112942385976796862442063752663646870430360736682397798633852405003167527051283327366631405990604840629657123985368031838052877290142895506386796217551784101265975360960112885444847880134909594560331781699767647860744559228420471946006511861233129745921297270844542687374552066388998112901504",
+		"0.28366218546322646623291670213892426815646045792775066552057877370468842342090306560693620285882975471913545189522117672866861003904575909174769890684057564495184019705963607555427518763375472432216131070235796577209064861003009894615394882021220247535890708789312783718414424224334988974848162884228012265684775651099758365989567444515619764427493598258393280941942356912304265535918025036942025858493361644535438208",
+		"-0.839071529076452222947082170022504835457755803801719612447629165523199043803440231769716865070163209041973184176293170330332317060558438085478980463542480791358920580076809381102480339018809694514100495572097422057215638383077242523713704127605770444906854175870243452753002238589530499630034663296166308443155999957196346563161387705205277189957388653461251461388391745795979375660087266037741360406956289962327970672363315696841378765492754546688",
+		"-0.82557544253149396284458404188071504476091346830440347376462206521981928020803354950315062147200396866217255527509254080721982393941347365824137698491042935894213870423296625749297033966815252917361266452901192457318047750698424190124169875103436588397415032138037063155981648677895645409699825582226442363080800781881653440538927704569142007751338851079530521979429507520541625303794665680584709171813053216867014700596866196844144286737568957809383224972108999354839705480223052622003994027222120126949093911643497423100187973906980635670000034664323357488815820848035808846624518774608931622703631130673844138378087837990739103263093532314835289302930152150130664948083902949999427848344301686172490282395687167681679607401220592559832932068966455384902377056623736013617949634746332323529256184776892339963173795176200590119077305668901887229709592836744082027738666294887303249770621722032438202753270710379312736193201366287952361100525126056993039858894987153270630277483613793395809214871734783742285495171911648254647287555645360520115341268930844095156502348405343740866836850201634640011708462641462047870611041595707018966032206807675586825362640000739202116391403514629284000986232673698892843586989003952425039512325844566790376383098534975022847888104706525937115931692008959513984157709954859352131323440787667052399474107219968",
+	}
+	for i, inp := range inps {
+		d, err := NewFromString(inp)
+		if err != nil {
+			t.FailNow()
+		}
+		s, err := NewFromString(sols[i])
+		if err != nil {
+			t.FailNow()
+		}
+		a := d.Cos()
+		if !a.Equal(s) {
+			t.Errorf("expected %s, got %s", s, a)
+		}
+	}
+}
+
+// For Tan
+func TestTan(t *testing.T) {
+	inps := []string{
+		"-2.91919191919191919",
+		"-1.0",
+		"-0.25",
+		"0.0",
+		"0.33",
+		"1.0",
+		"5.0",
+		"10",
+		"11000020.2407442310156021090304691671842603586882014729198302312846062338790031898128063403419218957424",
+	}
+	sols := []string{
+		"0.2261415650505790298980791606748881031998682652",
+		"-1.5574077246549025",
+		"-0.255341921221036275",
+		"0.0",
+		"0.342524867530038963",
+		"1.5574077246549025",
+		"-3.3805150062465829",
+		"0.6483608274590872485524085572681343280321117494",
+		"0.68351325561491170753499935023939368502774607234006019034769919811202010905597996164029250820702097041244539696",
+	}
+	for i, inp := range inps {
+		d, err := NewFromString(inp)
+		if err != nil {
+			t.FailNow()
+		}
+		s, err := NewFromString(sols[i])
+		if err != nil {
+			t.FailNow()
+		}
+		a := d.Tan()
+		if !a.Equal(s) {
+			t.Errorf("expected %s, got %s", s, a)
+		}
+	}
+}
+
+func ExampleNewFromFloat32() {
+	fmt.Println(NewFromFloat32(123.123123123123).String())
+	fmt.Println(NewFromFloat32(.123123123123123).String())
+	fmt.Println(NewFromFloat32(-1e13).String())
+	// OUTPUT:
+	//123.12312
+	//0.123123124
+	//-10000000000000
+}
+
+func ExampleNewFromFloat() {
+	fmt.Println(NewFromFloat(123.123123123123).String())
+	fmt.Println(NewFromFloat(.123123123123123).String())
+	fmt.Println(NewFromFloat(-1e13).String())
+	// OUTPUT:
+	//123.123123123123
+	//0.123123123123123
+	//-10000000000000
+}

+ 3 - 0
src/github.com/shopspring/decimal/go.mod

@@ -0,0 +1,3 @@
+module github.com/shopspring/decimal
+
+go 1.13

+ 160 - 0
src/github.com/shopspring/decimal/rounding.go

@@ -0,0 +1,160 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Multiprecision decimal numbers.
+// For floating-point formatting only; not general purpose.
+// Only operations are assign and (binary) left/right shift.
+// Can do binary floating point in multiprecision decimal precisely
+// because 2 divides 10; cannot do decimal floating point
+// in multiprecision binary precisely.
+
+package decimal
+
+type floatInfo struct {
+	mantbits uint
+	expbits  uint
+	bias     int
+}
+
+var float32info = floatInfo{23, 8, -127}
+var float64info = floatInfo{52, 11, -1023}
+
+// roundShortest rounds d (= mant * 2^exp) to the shortest number of digits
+// that will let the original floating point value be precisely reconstructed.
+func roundShortest(d *decimal, mant uint64, exp int, flt *floatInfo) {
+	// If mantissa is zero, the number is zero; stop now.
+	if mant == 0 {
+		d.nd = 0
+		return
+	}
+
+	// Compute upper and lower such that any decimal number
+	// between upper and lower (possibly inclusive)
+	// will round to the original floating point number.
+
+	// We may see at once that the number is already shortest.
+	//
+	// Suppose d is not denormal, so that 2^exp <= d < 10^dp.
+	// The closest shorter number is at least 10^(dp-nd) away.
+	// The lower/upper bounds computed below are at distance
+	// at most 2^(exp-mantbits).
+	//
+	// So the number is already shortest if 10^(dp-nd) > 2^(exp-mantbits),
+	// or equivalently log2(10)*(dp-nd) > exp-mantbits.
+	// It is true if 332/100*(dp-nd) >= exp-mantbits (log2(10) > 3.32).
+	minexp := flt.bias + 1 // minimum possible exponent
+	if exp > minexp && 332*(d.dp-d.nd) >= 100*(exp-int(flt.mantbits)) {
+		// The number is already shortest.
+		return
+	}
+
+	// d = mant << (exp - mantbits)
+	// Next highest floating point number is mant+1 << exp-mantbits.
+	// Our upper bound is halfway between, mant*2+1 << exp-mantbits-1.
+	upper := new(decimal)
+	upper.Assign(mant*2 + 1)
+	upper.Shift(exp - int(flt.mantbits) - 1)
+
+	// d = mant << (exp - mantbits)
+	// Next lowest floating point number is mant-1 << exp-mantbits,
+	// unless mant-1 drops the significant bit and exp is not the minimum exp,
+	// in which case the next lowest is mant*2-1 << exp-mantbits-1.
+	// Either way, call it mantlo << explo-mantbits.
+	// Our lower bound is halfway between, mantlo*2+1 << explo-mantbits-1.
+	var mantlo uint64
+	var explo int
+	if mant > 1<<flt.mantbits || exp == minexp {
+		mantlo = mant - 1
+		explo = exp
+	} else {
+		mantlo = mant*2 - 1
+		explo = exp - 1
+	}
+	lower := new(decimal)
+	lower.Assign(mantlo*2 + 1)
+	lower.Shift(explo - int(flt.mantbits) - 1)
+
+	// The upper and lower bounds are possible outputs only if
+	// the original mantissa is even, so that IEEE round-to-even
+	// would round to the original mantissa and not the neighbors.
+	inclusive := mant%2 == 0
+
+	// As we walk the digits we want to know whether rounding up would fall
+	// within the upper bound. This is tracked by upperdelta:
+	//
+	// If upperdelta == 0, the digits of d and upper are the same so far.
+	//
+	// If upperdelta == 1, we saw a difference of 1 between d and upper on a
+	// previous digit and subsequently only 9s for d and 0s for upper.
+	// (Thus rounding up may fall outside the bound, if it is exclusive.)
+	//
+	// If upperdelta == 2, then the difference is greater than 1
+	// and we know that rounding up falls within the bound.
+	var upperdelta uint8
+
+	// Now we can figure out the minimum number of digits required.
+	// Walk along until d has distinguished itself from upper and lower.
+	for ui := 0; ; ui++ {
+		// lower, d, and upper may have the decimal points at different
+		// places. In this case upper is the longest, so we iterate from
+		// ui==0 and start li and mi at (possibly) -1.
+		mi := ui - upper.dp + d.dp
+		if mi >= d.nd {
+			break
+		}
+		li := ui - upper.dp + lower.dp
+		l := byte('0') // lower digit
+		if li >= 0 && li < lower.nd {
+			l = lower.d[li]
+		}
+		m := byte('0') // middle digit
+		if mi >= 0 {
+			m = d.d[mi]
+		}
+		u := byte('0') // upper digit
+		if ui < upper.nd {
+			u = upper.d[ui]
+		}
+
+		// Okay to round down (truncate) if lower has a different digit
+		// or if lower is inclusive and is exactly the result of rounding
+		// down (i.e., and we have reached the final digit of lower).
+		okdown := l != m || inclusive && li+1 == lower.nd
+
+		switch {
+		case upperdelta == 0 && m+1 < u:
+			// Example:
+			// m = 12345xxx
+			// u = 12347xxx
+			upperdelta = 2
+		case upperdelta == 0 && m != u:
+			// Example:
+			// m = 12345xxx
+			// u = 12346xxx
+			upperdelta = 1
+		case upperdelta == 1 && (m != '9' || u != '0'):
+			// Example:
+			// m = 1234598x
+			// u = 1234600x
+			upperdelta = 2
+		}
+		// Okay to round up if upper has a different digit and either upper
+		// is inclusive or upper is bigger than the result of rounding up.
+		okup := upperdelta > 0 && (inclusive || upperdelta > 1 || ui+1 < upper.nd)
+
+		// If it's okay to do either, then round to the nearest one.
+		// If it's okay to do only one, do it.
+		switch {
+		case okdown && okup:
+			d.Round(mi + 1)
+			return
+		case okdown:
+			d.RoundDown(mi + 1)
+			return
+		case okup:
+			d.RoundUp(mi + 1)
+			return
+		}
+	}
+}

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно