Repository: gonum/gonum
Pull request #1953 (2024-03-06 21:48:46)

internal: generify and move ordered.BySliceValues to top-level place
This PR introduces a modernization change that is not necessarily related to #617 but which was spun out from my attempts at generifying the leaves of `graph`.

Although it may not provide any value in of itself, it will be used by forthcoming PRs to remove duplicate code.

Benchmarks to follow.

Blocked by https://github.com/gonum/gonum/pull/1940.

<!--
Checklist:

- API changes have been discussed
- code is goformated correctly (goimports)
- packages with generated code have had code generation run
- tests pass locally
- linked to relevant issues

Please make sure your commit message summary line and pull request
title match the Go convention; a one-line summary of the change,
prefixed by the primary affected package that should complete the
sentence, "This change modifies Gonum to _____."
-->


jbduncan: I've just force-pushed a fix to the `internal: generify and move ordered.BySliceValues to top-level place` commit to make it run successfully on AppVeyor. I'll see if I can rebase all other PRs dependent on this one.
jbduncan: Looking at all direct and indirect usages of `BySliceValues`, its only usages are tests, and the only other changes in this PR are in test functions directly. Therefore, there are no benchmarks to run.
jbduncan: > This makes the `BySliceValues` function available outside graph. I don't see why we need to do this. Nor do I see why it needs to be made generic. Are there reasons that you think this is needed?

@kortschak Good question! It's because the contents of `BySliceValues` was duplicated in `spatial/vptree/vptree_test.go`, presumably because generics didn't exist when it was first written, and I wanted to avoid having `spatial` depend on `graph`.

So I thought it best to move `BySlicesValues` to an internal package that both `graph` and `spatial` could use, then fix `spatial` in PR https://github.com/gonum/gonum/pull/1955.

What do you think?
jbduncan: Anyway, as discussed in https://github.com/gonum/gonum/pull/1956#issuecomment-1988326786, I'm more than happy to double check if new benchmarks are needed and to run them all.
kortschak: This needs to pick up the changes in master.
jbduncan: > This needs to pick up the changes in master.

Done!

Let me know if my previous comment about why this PR exists makes sense or not. I'd be more than happy to rephrase things and even close this PR if you don't think it's valuable.
codecov-commenter: ## [Codecov](https://app.codecov.io/gh/gonum/gonum/pull/1953?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=gonum) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 73.95%. Comparing base [(`43d122b`)](https://app.codecov.io/gh/gonum/gonum/commit/43d122ba357012c9bf66038e4b9d1cac21094aa3?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=gonum) to head [(`1ae9aa7`)](https://app.codecov.io/gh/gonum/gonum/pull/1953?dropdown=coverage&src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=gonum).
> Report is 2 commits behind head on master.


:exclamation: Your organization needs to install the [Codecov GitHub app](https://github.com/apps/codecov/installations/select_target) to enable full functionality.

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master    #1953      +/-   ##
==========================================
+ Coverage   72.34%   73.95%   +1.61%     
==========================================
  Files         555      527      -28     
  Lines       65005    63305    -1700     
==========================================
- Hits        47026    46817     -209     
+ Misses      15380    13914    -1466     
+ Partials     2599     2574      -25     
```



</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/gonum/gonum/pull/1953?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=gonum).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=gonum).

jbduncan: > This is a subset of #1955. Why are they separate or not a chain?

@kortschak Good question. It completely escaped me that I could merge them into one PR but keep the commits separate. Even though #1957 also depends on this PR, it should just work!

When I get to this PR, I'll copy #1955 over, benchmark everything and let you know when things are ready to review.
kortschak: The name proliferation here is making me baulk at this. I am thinking that it is less worse to just bring everything out to internal/ordered.
jbduncan: > The name proliferation here is making me baulk at this. I am thinking that it is less worse to just bring everything out to internal/order (note change in name from "ordered" since the user semantics are now different; it was previously "sort orderd by O", while now it would be "order by O" — I should have noticed this earlier).

Okay, that makes a lot of sense!

I've force-pushed some changes which should address this.

I intend to fix the merge conflicts and run the benchmarks when the other PRs are merged in.
jbduncan: I've decided to rebase anyway, so I'll run the benchmarks later this evening. :)
jbduncan: I've finally found the time to upload my benchmark results.

Strangely, the `AStarUndirected` benchmarks seem to be all over the place, ranging from -13% to +17%. That being said, the absolute times are in microseconds, so I could chalk it up to my machine.

@kortschak What do you think?

```
goos: darwin
goarch: arm64
pkg: gonum.org/v1/gonum/graph/coloring
                                                       │  bench.old   │              bench.new              │
                                                       │    sec/op    │    sec/op     vs base               │
Coloring/empty/Dsatur-8                                  3.504n ±  0%   3.465n ±  0%  -1.13% (p=0.000 n=10)
Coloring/empty/DsaturExact-8                             3.188n ±  0%   3.163n ±  0%  -0.78% (p=0.000 n=10)
Coloring/empty/RecursiveLargestFirst-8                   3.189n ±  0%   3.159n ±  0%  -0.93% (p=0.000 n=10)
Coloring/empty/SanSegundo-8                              3.189n ±  0%   3.166n ±  0%  -0.71% (p=0.000 n=10)
Coloring/empty/WelshPowell-8                             3.188n ±  0%   3.172n ±  0%  -0.49% (p=0.001 n=10)
Coloring/singleton/Dsatur-8                              414.8n ±  0%   409.4n ±  0%  -1.29% (p=0.000 n=10)
Coloring/singleton/DsaturExact-8                         960.5n ±  0%   949.2n ±  0%  -1.18% (p=0.000 n=10)
Coloring/singleton/RecursiveLargestFirst-8               298.8n ±  0%   295.7n ±  0%  -1.04% (p=0.000 n=10)
Coloring/singleton/SanSegundo-8                          415.0n ±  0%   408.8n ±  0%  -1.48% (p=0.001 n=10)
Coloring/singleton/WelshPowell-8                         289.8n ±  0%   286.5n ±  0%  -1.12% (p=0.000 n=10)
Coloring/kite/Dsatur-8                                   3.322µ ±  0%   3.288µ ±  0%  -1.01% (p=0.000 n=10)
Coloring/kite/DsaturExact-8                              18.04µ ±  0%   17.85µ ±  0%  -1.05% (p=0.002 n=10)
Coloring/kite/RecursiveLargestFirst-8                    2.246µ ±  0%   2.232µ ±  0%  -0.65% (p=0.001 n=10)
Coloring/kite/SanSegundo-8                               4.042µ ±  0%   4.007µ ±  0%  -0.87% (p=0.000 n=10)
Coloring/kite/WelshPowell-8                              1.407µ ±  0%   1.396µ ±  0%  -0.78% (p=0.000 n=10)
Coloring/triangle+1/Dsatur-8                             2.101µ ±  0%   2.083µ ±  0%  -0.86% (p=0.000 n=10)
Coloring/triangle+1/DsaturExact-8                        10.17µ ±  0%   10.09µ ±  0%  -0.80% (p=0.001 n=10)
Coloring/triangle+1/RecursiveLargestFirst-8              1.438µ ±  0%   1.428µ ±  0%  -0.70% (p=0.000 n=10)
Coloring/triangle+1/SanSegundo-8                         2.435µ ±  0%   2.417µ ±  0%  -0.78% (p=0.000 n=10)
Coloring/triangle+1/WelshPowell-8                        980.0n ±  0%   972.1n ±  0%  -0.81% (p=0.000 n=10)
Coloring/bipartite_halves/Dsatur-8                       8.015µ ±  0%   7.975µ ±  1%  -0.50% (p=0.022 n=10)
Coloring/bipartite_halves/DsaturExact-8                  33.30µ ±  0%   33.12µ ±  0%  -0.52% (p=0.000 n=10)
Coloring/bipartite_halves/RecursiveLargestFirst-8        3.982µ ±  0%   3.970µ ±  0%  -0.31% (p=0.000 n=10)
Coloring/bipartite_halves/SanSegundo-8                   15.22µ ±  0%   15.15µ ±  0%  -0.48% (p=0.000 n=10)
Coloring/bipartite_halves/WelshPowell-8                  2.394µ ±  0%   2.384µ ±  1%       ~ (p=0.055 n=10)
Coloring/bipartite_alternating/Dsatur-8                  7.885µ ±  0%   7.875µ ±  0%  -0.12% (p=0.041 n=10)
Coloring/bipartite_alternating/DsaturExact-8             33.11µ ±  0%   32.97µ ±  0%  -0.41% (p=0.000 n=10)
Coloring/bipartite_alternating/RecursiveLargestFirst-8   3.950µ ±  0%   3.934µ ±  1%  -0.42% (p=0.027 n=10)
Coloring/bipartite_alternating/SanSegundo-8              15.32µ ±  0%   15.25µ ±  0%  -0.45% (p=0.000 n=10)
Coloring/bipartite_alternating/WelshPowell-8             2.486µ ±  0%   2.482µ ±  1%       ~ (p=0.341 n=10)
Coloring/3/4_bipartite/Dsatur-8                          6.412µ ±  0%   6.401µ ±  0%  -0.17% (p=0.008 n=10)
Coloring/3/4_bipartite/DsaturExact-8                     30.54µ ±  0%   30.41µ ±  0%  -0.42% (p=0.002 n=10)
Coloring/3/4_bipartite/RecursiveLargestFirst-8           3.529µ ±  0%   3.510µ ±  0%  -0.54% (p=0.000 n=10)
Coloring/3/4_bipartite/SanSegundo-8                      13.38µ ±  0%   13.33µ ±  0%  -0.34% (p=0.005 n=10)
Coloring/3/4_bipartite/WelshPowell-8                     2.235µ ±  0%   2.230µ ±  0%  -0.25% (p=0.000 n=10)
Coloring/cubical/Dsatur-8                                8.019µ ±  0%   7.996µ ±  0%  -0.29% (p=0.001 n=10)
Coloring/cubical/DsaturExact-8                           33.51µ ±  0%   33.43µ ±  0%  -0.24% (p=0.009 n=10)
Coloring/cubical/RecursiveLargestFirst-8                 4.056µ ±  0%   4.040µ ±  0%  -0.38% (p=0.000 n=10)
Coloring/cubical/SanSegundo-8                            15.39µ ±  0%   15.33µ ±  1%  -0.36% (p=0.037 n=10)
Coloring/cubical/WelshPowell-8                           2.513µ ±  0%   2.509µ ±  1%       ~ (p=0.515 n=10)
Coloring/HoG33106/Dsatur-8                               19.06µ ±  0%   19.01µ ±  0%  -0.28% (p=0.002 n=10)
Coloring/HoG33106/DsaturExact-8                          73.89µ ±  0%   73.62µ ±  0%  -0.37% (p=0.000 n=10)
Coloring/HoG33106/RecursiveLargestFirst-8                8.845µ ±  0%   8.825µ ±  0%  -0.23% (p=0.001 n=10)
Coloring/HoG33106/SanSegundo-8                           69.14µ ±  0%   68.94µ ±  0%  -0.30% (p=0.015 n=10)
Coloring/HoG33106/WelshPowell-8                          5.000µ ±  0%   4.972µ ±  0%  -0.56% (p=0.000 n=10)
Coloring/HoG41237/Dsatur-8                               217.2m ±  1%   219.2m ±  1%  +0.93% (p=0.001 n=10)
Coloring/HoG41237/DsaturExact-8                          239.9m ±  1%   239.6m ±  1%       ~ (p=0.631 n=10)
Coloring/HoG41237/RecursiveLargestFirst-8                8.899m ±  1%   8.888m ±  1%       ~ (p=0.796 n=10)
Coloring/HoG41237/SanSegundo-8                           506.7m ±  1%   507.4m ±  0%       ~ (p=0.190 n=10)
Coloring/HoG41237/WelshPowell-8                          3.663m ±  1%   3.679m ±  0%       ~ (p=0.165 n=10)
Coloring/exams/Dsatur-8                                  4.660µ ±  0%   4.656µ ±  0%       ~ (p=0.236 n=10)
Coloring/exams/DsaturExact-8                             26.84µ ±  0%   26.80µ ±  0%       ~ (p=0.137 n=10)
Coloring/exams/RecursiveLargestFirst-8                   3.057µ ±  0%   3.052µ ±  0%       ~ (p=0.118 n=10)
Coloring/exams/SanSegundo-8                              7.487µ ±  0%   7.468µ ±  1%  -0.25% (p=0.024 n=10)
Coloring/exams/WelshPowell-8                             1.837µ ±  0%   1.834µ ±  0%       ~ (p=0.107 n=10)
Coloring/exam_scheduler_1/Dsatur-8                       18.01µ ±  0%   17.94µ ±  0%  -0.39% (p=0.001 n=10)
Coloring/exam_scheduler_1/DsaturExact-8                  100.2µ ±  0%   100.0µ ±  0%  -0.18% (p=0.023 n=10)
Coloring/exam_scheduler_1/RecursiveLargestFirst-8        11.41µ ±  0%   11.37µ ±  0%  -0.36% (p=0.000 n=10)
Coloring/exam_scheduler_1/SanSegundo-8                   27.72µ ±  0%   27.72µ ±  0%       ~ (p=0.853 n=10)
Coloring/exam_scheduler_1/WelshPowell-8                  4.740µ ±  0%   4.728µ ±  0%  -0.25% (p=0.005 n=10)
Coloring/exam_scheduler_2/Dsatur-8                       8.370µ ±  0%   8.353µ ±  0%  -0.21% (p=0.001 n=10)
Coloring/exam_scheduler_2/DsaturExact-8                  48.60µ ±  0%   48.39µ ±  0%  -0.43% (p=0.000 n=10)
Coloring/exam_scheduler_2/RecursiveLargestFirst-8        5.192µ ±  0%   5.181µ ±  0%       ~ (p=0.159 n=10)
Coloring/exam_scheduler_2/SanSegundo-8                   12.86µ ±  0%   12.81µ ±  0%  -0.45% (p=0.001 n=10)
Coloring/exam_scheduler_2/WelshPowell-8                  2.754µ ±  0%   2.743µ ±  0%  -0.40% (p=0.005 n=10)
Coloring/Brélaz/Dsatur-8                                 12.12µ ±  0%   12.09µ ±  0%  -0.25% (p=0.004 n=10)
Coloring/Brélaz/DsaturExact-8                            59.96µ ±  0%   59.77µ ±  0%       ~ (p=0.052 n=10)
Coloring/Brélaz/RecursiveLargestFirst-8                  7.611µ ±  0%   7.583µ ±  0%  -0.37% (p=0.009 n=10)
Coloring/Brélaz/SanSegundo-8                             18.52µ ±  0%   18.47µ ±  0%  -0.28% (p=0.005 n=10)
Coloring/Brélaz/WelshPowell-8                            3.721µ ±  0%   3.711µ ±  0%  -0.28% (p=0.014 n=10)
Coloring/Lima_Carmo/Dsatur-8                             7.572µ ±  1%   7.562µ ±  0%       ~ (p=0.078 n=10)
Coloring/Lima_Carmo/DsaturExact-8                        32.98µ ±  0%   33.00µ ±  1%       ~ (p=0.579 n=10)
Coloring/Lima_Carmo/RecursiveLargestFirst-8              4.178µ ±  0%   4.166µ ±  0%  -0.28% (p=0.005 n=10)
Coloring/Lima_Carmo/SanSegundo-8                         11.28µ ±  0%   11.25µ ±  0%  -0.25% (p=0.003 n=10)
Coloring/Lima_Carmo/WelshPowell-8                        2.486µ ±  0%   2.477µ ±  0%  -0.36% (p=0.001 n=10)
Coloring/San_Segundo/Dsatur-8                            12.84µ ±  0%   12.81µ ±  1%       ~ (p=0.280 n=10)
Coloring/San_Segundo/DsaturExact-8                       120.7µ ±  0%   120.2µ ±  0%  -0.39% (p=0.029 n=10)
Coloring/San_Segundo/RecursiveLargestFirst-8             9.222µ ±  0%   9.208µ ±  0%  -0.15% (p=0.045 n=10)
Coloring/San_Segundo/SanSegundo-8                        22.69µ ±  0%   22.67µ ±  0%       ~ (p=0.149 n=10)
Coloring/San_Segundo/WelshPowell-8                       3.621µ ±  1%   3.604µ ±  0%  -0.47% (p=0.011 n=10)
Coloring/tetrahedron/Dsatur-8                            2.391µ ±  0%   2.388µ ±  1%       ~ (p=0.753 n=10)
Coloring/tetrahedron/DsaturExact-8                       13.74µ ±  0%   13.72µ ±  0%  -0.14% (p=0.011 n=10)
Coloring/tetrahedron/RecursiveLargestFirst-8             1.860µ ±  0%   1.854µ ±  0%  -0.32% (p=0.001 n=10)
Coloring/tetrahedron/SanSegundo-8                        3.338µ ±  0%   3.326µ ±  0%  -0.34% (p=0.005 n=10)
Coloring/tetrahedron/WelshPowell-8                       1.069µ ±  0%   1.066µ ±  0%  -0.33% (p=0.000 n=10)
Coloring/triangle/Dsatur-8                               1.487µ ±  0%   1.483µ ±  0%       ~ (p=0.061 n=10)
Coloring/triangle/DsaturExact-8                          7.328µ ±  0%   7.321µ ±  0%       ~ (p=0.117 n=10)
Coloring/triangle/RecursiveLargestFirst-8                1.157µ ±  0%   1.153µ ±  0%  -0.39% (p=0.000 n=10)
Coloring/triangle/SanSegundo-8                           1.828µ ±  0%   1.824µ ±  0%  -0.25% (p=0.026 n=10)
Coloring/triangle/WelshPowell-8                          774.6n ±  0%   771.6n ±  0%  -0.38% (p=0.007 n=10)
Coloring/square/Dsatur-8                                 2.206µ ±  0%   2.202µ ±  0%       ~ (p=0.085 n=10)
Coloring/square/DsaturExact-8                            10.96µ ±  0%   10.93µ ±  0%  -0.22% (p=0.020 n=10)
Coloring/square/RecursiveLargestFirst-8                  1.428µ ±  0%   1.423µ ±  0%  -0.35% (p=0.000 n=10)
Coloring/square/SanSegundo-8                             2.821µ ±  0%   2.814µ ±  0%  -0.27% (p=0.001 n=10)
Coloring/square/WelshPowell-8                            1.024µ ±  1%   1.023µ ±  0%       ~ (p=0.178 n=10)
Coloring/cycle-5/Dsatur-8                                3.238µ ±  1%   3.232µ ±  1%       ~ (p=0.254 n=10)
Coloring/cycle-5/DsaturExact-8                           19.36µ ±  0%   19.31µ ±  0%  -0.26% (p=0.009 n=10)
Coloring/cycle-5/RecursiveLargestFirst-8                 2.122µ ±  0%   2.120µ ±  0%       ~ (p=0.083 n=10)
Coloring/cycle-5/SanSegundo-8                            4.167µ ±  0%   4.155µ ±  0%  -0.29% (p=0.000 n=10)
Coloring/cycle-5/WelshPowell-8                           1.366µ ±  0%   1.365µ ±  0%       ~ (p=0.342 n=10)
Coloring/cycle-6/Dsatur-8                                4.319µ ±  0%   4.314µ ±  0%       ~ (p=0.403 n=10)
Coloring/cycle-6/DsaturExact-8                           18.43µ ±  0%   18.43µ ±  0%       ~ (p=0.868 n=10)
Coloring/cycle-6/RecursiveLargestFirst-8                 2.496µ ±  0%   2.494µ ±  0%       ~ (p=0.381 n=10)
Coloring/cycle-6/SanSegundo-8                            5.607µ ±  0%   5.588µ ±  0%  -0.34% (p=0.004 n=10)
Coloring/cycle-6/WelshPowell-8                           1.673µ ±  0%   1.670µ ±  1%       ~ (p=0.668 n=10)
Coloring/wheel-5/Dsatur-8                                3.458µ ±  0%   3.457µ ±  0%       ~ (p=0.115 n=10)
Coloring/wheel-5/DsaturExact-8                           23.11µ ±  0%   23.06µ ±  0%  -0.21% (p=0.020 n=10)
Coloring/wheel-5/RecursiveLargestFirst-8                 2.393µ ±  0%   2.389µ ±  0%  -0.19% (p=0.011 n=10)
Coloring/wheel-5/SanSegundo-8                            5.205µ ±  0%   5.192µ ±  0%  -0.26% (p=0.015 n=10)
Coloring/wheel-5/WelshPowell-8                           1.402µ ±  0%   1.401µ ±  1%       ~ (p=0.515 n=10)
Coloring/wheel-6/Dsatur-8                                4.867µ ±  0%   4.859µ ±  0%  -0.14% (p=0.024 n=10)
Coloring/wheel-6/DsaturExact-8                           38.28µ ±  0%   38.24µ ±  0%       ~ (p=0.393 n=10)
Coloring/wheel-6/RecursiveLargestFirst-8                 3.323µ ±  0%   3.319µ ±  0%       ~ (p=0.254 n=10)
Coloring/wheel-6/SanSegundo-8                            7.615µ ±  0%   7.602µ ±  0%       ~ (p=0.240 n=10)
Coloring/wheel-6/WelshPowell-8                           1.813µ ±  0%   1.811µ ±  0%       ~ (p=0.693 n=10)
Coloring/wheel-7/Dsatur-8                                6.346µ ±  0%   6.332µ ±  0%  -0.22% (p=0.003 n=10)
Coloring/wheel-7/DsaturExact-8                           39.74µ ±  0%   39.76µ ±  0%       ~ (p=0.529 n=10)
Coloring/wheel-7/RecursiveLargestFirst-8                 3.950µ ±  0%   3.955µ ±  0%       ~ (p=0.183 n=10)
Coloring/wheel-7/SanSegundo-8                            10.21µ ±  0%   10.22µ ±  0%       ~ (p=0.754 n=10)
Coloring/wheel-7/WelshPowell-8                           2.192µ ±  0%   2.186µ ±  0%       ~ (p=0.083 n=10)
Coloring/sudoku_board/Dsatur-8                           21.54m ±  0%   21.46m ±  0%  -0.36% (p=0.000 n=10)
Coloring/sudoku_board/DsaturExact-8                      86.10m ±  0%   86.28m ±  1%       ~ (p=0.218 n=10)
Coloring/sudoku_board/RecursiveLargestFirst-8            3.930m ±  0%   3.936m ±  0%       ~ (p=0.436 n=10)
Coloring/sudoku_board/SanSegundo-8                       25.49m ±  0%   25.51m ±  0%       ~ (p=0.796 n=10)
Coloring/sudoku_board/WelshPowell-8                      691.2µ ±  0%   688.0µ ±  0%  -0.46% (p=0.000 n=10)
Coloring/sudoku_problem/Dsatur-8                         32.66m ±  0%   32.65m ±  0%       ~ (p=0.796 n=10)
Coloring/sudoku_problem/DsaturExact-8                     5.928 ± 50%    5.383 ± 64%       ~ (p=0.481 n=10)
Coloring/sudoku_problem/RecursiveLargestFirst-8          5.886m ±  0%   5.871m ±  0%       ~ (p=0.052 n=10)
Coloring/sudoku_problem/SanSegundo-8                     35.64m ±  0%   35.70m ±  0%       ~ (p=0.353 n=10)
Coloring/sudoku_problem/WelshPowell-8                    963.5µ ±  0%   963.7µ ±  1%       ~ (p=0.315 n=10)
Coloring/cs_shc/Dsatur-8                                 3.365µ ±  0%   3.364µ ±  0%       ~ (p=0.896 n=10)
Coloring/cs_shc/DsaturExact-8                            20.55µ ±  0%   20.59µ ±  0%  +0.18% (p=0.043 n=10)
Coloring/cs_shc/RecursiveLargestFirst-8                  2.392µ ±  1%   2.382µ ±  0%  -0.40% (p=0.001 n=10)
Coloring/cs_shc/SanSegundo-8                             5.141µ ±  0%   5.148µ ±  1%       ~ (p=0.066 n=10)
Coloring/cs_shc/WelshPowell-8                            1.426µ ±  0%   1.425µ ±  0%       ~ (p=0.345 n=10)
Coloring/gis_hc/Dsatur-8                                 3.995µ ±  0%   3.995µ ±  0%       ~ (p=0.752 n=10)
Coloring/gis_hc/DsaturExact-8                            16.91µ ±  0%   16.92µ ±  0%       ~ (p=0.956 n=10)
Coloring/gis_hc/RecursiveLargestFirst-8                  2.318µ ±  0%   2.312µ ±  0%       ~ (p=0.076 n=10)
Coloring/gis_hc/SanSegundo-8                             7.495µ ±  0%   7.493µ ±  1%       ~ (p=0.985 n=10)
Coloring/gis_hc/WelshPowell-8                            1.655µ ±  0%   1.652µ ±  0%       ~ (p=0.272 n=10)
Coloring/gis_shc|rs_shc/Dsatur-8                         2.081µ ±  0%   2.079µ ±  0%       ~ (p=0.134 n=10)
Coloring/gis_shc|rs_shc/DsaturExact-8                    9.811µ ±  0%   9.827µ ±  0%       ~ (p=0.060 n=10)
Coloring/gis_shc|rs_shc/RecursiveLargestFirst-8          1.348µ ±  0%   1.346µ ±  0%       ~ (p=0.052 n=10)
Coloring/gis_shc|rs_shc/SanSegundo-8                     2.713µ ±  0%   2.710µ ±  1%       ~ (p=0.541 n=10)
Coloring/gis_shc|rs_shc/WelshPowell-8                    1.020µ ±  0%   1.018µ ±  0%       ~ (p=0.062 n=10)
Coloring/lf_hc/Dsatur-8                                  6.129µ ±  0%   6.142µ ±  0%       ~ (p=0.109 n=10)
Coloring/lf_hc/DsaturExact-8                             32.56µ ±  0%   32.78µ ±  0%  +0.66% (p=0.003 n=10)
Coloring/lf_hc/RecursiveLargestFirst-8                   3.733µ ±  1%   3.738µ ±  0%       ~ (p=0.343 n=10)
Coloring/lf_hc/SanSegundo-8                              10.33µ ±  0%   10.34µ ±  1%       ~ (p=0.148 n=10)
Coloring/lf_hc/WelshPowell-8                             2.073µ ±  0%   2.072µ ±  0%       ~ (p=0.900 n=10)
Coloring/lf_shc/Dsatur-8                                 4.088µ ±  0%   4.097µ ±  0%  +0.21% (p=0.017 n=10)
Coloring/lf_shc/DsaturExact-8                            16.96µ ±  0%   16.95µ ±  0%       ~ (p=0.382 n=10)
Coloring/lf_shc/RecursiveLargestFirst-8                  2.420µ ±  1%   2.418µ ±  0%       ~ (p=0.146 n=10)
Coloring/lf_shc/SanSegundo-8                             4.460µ ±  0%   4.455µ ±  1%       ~ (p=0.196 n=10)
Coloring/lf_shc/WelshPowell-8                            1.599µ ±  0%   1.597µ ±  0%       ~ (p=0.058 n=10)
Coloring/lfi_hc/Dsatur-8                                 10.50µ ±  0%   10.50µ ±  0%       ~ (p=0.956 n=10)
Coloring/lfi_hc/DsaturExact-8                            67.65µ ±  0%   67.58µ ±  0%       ~ (p=0.271 n=10)
Coloring/lfi_hc/RecursiveLargestFirst-8                  5.940µ ±  0%   5.941µ ±  0%       ~ (p=0.782 n=10)
Coloring/lfi_hc/SanSegundo-8                             14.03µ ±  0%   14.02µ ±  0%       ~ (p=0.055 n=10)
Coloring/lfi_hc/WelshPowell-8                            3.130µ ±  0%   3.126µ ±  0%       ~ (p=0.208 n=10)
Coloring/lfi_shc|slf_shc/Dsatur-8                        6.223µ ±  0%   6.238µ ±  0%       ~ (p=0.072 n=10)
Coloring/lfi_shc|slf_shc/DsaturExact-8                   39.31µ ±  0%   39.22µ ±  0%  -0.24% (p=0.015 n=10)
Coloring/lfi_shc|slf_shc/RecursiveLargestFirst-8         3.552µ ±  0%   3.536µ ±  1%  -0.45% (p=0.018 n=10)
Coloring/lfi_shc|slf_shc/SanSegundo-8                    7.167µ ±  0%   7.158µ ±  0%  -0.13% (p=0.050 n=10)
Coloring/lfi_shc|slf_shc/WelshPowell-8                   2.139µ ±  0%   2.137µ ±  0%       ~ (p=0.145 n=10)
Coloring/no_solo/Dsatur-8                                16.80µ ±  0%   16.78µ ±  0%       ~ (p=0.060 n=10)
Coloring/no_solo/DsaturExact-8                           54.63µ ±  0%   54.64µ ±  1%       ~ (p=0.956 n=10)
Coloring/no_solo/RecursiveLargestFirst-8                 7.693µ ±  0%   7.674µ ±  0%  -0.23% (p=0.012 n=10)
Coloring/no_solo/SanSegundo-8                            25.33µ ±  0%   25.28µ ±  0%       ~ (p=0.127 n=10)
Coloring/no_solo/WelshPowell-8                           4.412µ ±  0%   4.404µ ±  0%  -0.17% (p=0.012 n=10)
Coloring/sl_hc/Dsatur-8                                  8.790µ ±  0%   8.775µ ±  0%       ~ (p=0.093 n=10)
Coloring/sl_hc/DsaturExact-8                             74.83µ ±  0%   74.74µ ±  0%       ~ (p=0.143 n=10)
Coloring/sl_hc/RecursiveLargestFirst-8                   5.267µ ±  0%   5.261µ ±  0%       ~ (p=0.171 n=10)
Coloring/sl_hc/SanSegundo-8                              12.05µ ±  0%   12.03µ ±  0%  -0.20% (p=0.011 n=10)
Coloring/sl_hc/WelshPowell-8                             2.692µ ±  0%   2.690µ ±  0%       ~ (p=0.223 n=10)
Coloring/sl_shc/Dsatur-8                                 4.810µ ±  0%   4.802µ ±  0%       ~ (p=0.100 n=10)
Coloring/sl_shc/DsaturExact-8                            25.46µ ±  0%   25.43µ ±  0%       ~ (p=0.118 n=10)
Coloring/sl_shc/RecursiveLargestFirst-8                  2.996µ ±  0%   2.994µ ±  0%       ~ (p=0.904 n=10)
Coloring/sl_shc/SanSegundo-8                             7.029µ ±  0%   7.029µ ±  1%       ~ (p=0.868 n=10)
Coloring/sl_shc/WelshPowell-8                            1.761µ ±  0%   1.759µ ±  0%       ~ (p=0.445 n=10)
Coloring/slf_hc/Dsatur-8                                 7.923µ ±  0%   7.928µ ±  0%       ~ (p=0.643 n=10)
Coloring/slf_hc/DsaturExact-8                            50.23µ ±  0%   50.11µ ±  0%       ~ (p=0.089 n=10)
Coloring/slf_hc/RecursiveLargestFirst-8                  4.644µ ±  0%   4.645µ ±  0%       ~ (p=0.753 n=10)
Coloring/slf_hc/SanSegundo-8                             13.27µ ±  1%   13.25µ ±  0%       ~ (p=0.288 n=10)
Coloring/slf_hc/WelshPowell-8                            2.569µ ±  0%   2.567µ ±  0%       ~ (p=0.642 n=10)
Coloring/sli_hc/Dsatur-8                                 12.41µ ±  0%   12.38µ ±  0%  -0.31% (p=0.023 n=10)
Coloring/sli_hc/DsaturExact-8                            128.8µ ±  0%   128.7µ ±  0%       ~ (p=0.684 n=10)
Coloring/sli_hc/RecursiveLargestFirst-8                  8.423µ ±  0%   8.425µ ±  0%       ~ (p=0.926 n=10)
Coloring/sli_hc/SanSegundo-8                             16.78µ ±  0%   16.77µ ±  0%       ~ (p=0.362 n=10)
Coloring/sli_hc/WelshPowell-8                            3.401µ ±  0%   3.391µ ±  0%       ~ (p=0.065 n=10)
Coloring/sli_shc/Dsatur-8                                6.296µ ±  0%   6.283µ ±  0%  -0.20% (p=0.034 n=10)
Coloring/sli_shc/DsaturExact-8                           31.26µ ±  0%   31.34µ ±  0%  +0.25% (p=0.001 n=10)
Coloring/sli_shc/RecursiveLargestFirst-8                 3.814µ ±  0%   3.803µ ±  0%  -0.28% (p=0.006 n=10)
Coloring/sli_shc/SanSegundo-8                            10.21µ ±  0%   10.22µ ±  1%       ~ (p=0.425 n=10)
Coloring/sli_shc/WelshPowell-8                           2.156µ ±  0%   2.153µ ±  0%       ~ (p=0.135 n=10)
Coloring/rsi_shc/Dsatur-8                                4.559µ ±  0%   4.563µ ±  0%       ~ (p=0.697 n=10)
Coloring/rsi_shc/DsaturExact-8                           23.65µ ±  0%   23.69µ ±  0%  +0.17% (p=0.041 n=10)
Coloring/rsi_shc/RecursiveLargestFirst-8                 2.879µ ±  0%   2.881µ ±  0%       ~ (p=0.446 n=10)
Coloring/rsi_shc/SanSegundo-8                            5.637µ ±  0%   5.633µ ±  1%       ~ (p=0.207 n=10)
Coloring/rsi_shc/WelshPowell-8                           1.725µ ±  0%   1.725µ ±  0%       ~ (p=0.808 n=10)
Coloring/V_plus_not_in_A_cal/Dsatur-8                    8.929µ ±  0%   8.929µ ±  0%       ~ (p=0.926 n=10)
Coloring/V_plus_not_in_A_cal/DsaturExact-8               33.13µ ±  0%   33.07µ ±  0%       ~ (p=0.143 n=10)
Coloring/V_plus_not_in_A_cal/RecursiveLargestFirst-8     4.872µ ±  0%   4.867µ ±  0%  -0.10% (p=0.041 n=10)
Coloring/V_plus_not_in_A_cal/SanSegundo-8                10.72µ ±  0%   10.72µ ±  0%       ~ (p=0.515 n=10)
Coloring/V_plus_not_in_A_cal/WelshPowell-8               2.811µ ±  0%   2.806µ ±  0%  -0.16% (p=0.015 n=10)
Coloring/queen5_5/Dsatur-8                               286.1µ ±  0%   285.4µ ±  0%       ~ (p=0.075 n=10)
Coloring/queen5_5/DsaturExact-8                          2.459m ±  0%   2.457m ±  0%       ~ (p=0.436 n=10)
Coloring/queen5_5/RecursiveLargestFirst-8                101.0µ ±  1%   100.4µ ±  0%  -0.58% (p=0.002 n=10)
Coloring/queen5_5/SanSegundo-8                           588.0µ ±  0%   586.7µ ±  0%  -0.22% (p=0.004 n=10)
Coloring/queen5_5/WelshPowell-8                          32.25µ ±  0%   32.16µ ±  0%  -0.28% (p=0.002 n=10)
Coloring/queen6_6/Dsatur-8                               1.147m ±  0%   1.147m ±  0%       ~ (p=0.529 n=10)
Coloring/queen6_6/DsaturExact-8                          166.8m ± 22%   176.0m ± 23%       ~ (p=0.436 n=10)
Coloring/queen6_6/RecursiveLargestFirst-8                375.3µ ±  0%   375.3µ ±  0%       ~ (p=0.481 n=10)
Coloring/queen6_6/SanSegundo-8                           1.453m ±  1%   1.450m ±  1%       ~ (p=0.436 n=10)
Coloring/queen6_6/WelshPowell-8                          86.52µ ±  0%   86.61µ ±  0%       ~ (p=0.247 n=10)
Coloring/queen7_7/Dsatur-8                               3.635m ±  0%   3.631m ±  1%       ~ (p=0.971 n=10)
Coloring/queen7_7/DsaturExact-8                           1.806 ± 65%    2.592 ± 79%       ~ (p=0.684 n=10)
Coloring/queen7_7/RecursiveLargestFirst-8                984.4µ ±  0%   985.3µ ±  0%       ~ (p=0.165 n=10)
Coloring/queen7_7/SanSegundo-8                           4.303m ±  1%   4.293m ±  0%       ~ (p=0.218 n=10)
Coloring/queen7_7/WelshPowell-8                          197.1µ ±  0%   196.9µ ±  0%       ~ (p=0.579 n=10)
Coloring/queen8_8/Dsatur-8                               10.26m ±  1%   10.27m ±  0%       ~ (p=0.739 n=10)
Coloring/queen8_8/RecursiveLargestFirst-8                2.343m ±  0%   2.339m ±  1%       ~ (p=0.105 n=10)
Coloring/queen8_8/SanSegundo-8                           11.58m ±  1%   11.59m ±  0%       ~ (p=0.796 n=10)
Coloring/queen8_8/WelshPowell-8                          426.3µ ±  0%   426.7µ ±  0%       ~ (p=0.436 n=10)
geomean                                                  14.42µ         14.41µ        -0.07%

pkg: gonum.org/v1/gonum/graph/community
                                                  │  bench.old  │             bench.new              │
                                                  │   sec/op    │   sec/op     vs base               │
KCliqueCommunities/simple-k=3-8                     33.57µ ± 0%   33.58µ ± 0%       ~ (p=0.684 n=10)
KCliqueCommunities/Batagelj-Zaversnik_Graph-k=3-8   101.7µ ± 0%   101.6µ ± 0%       ~ (p=0.197 n=10)
KCliqueCommunities/Batagelj-Zaversnik_Graph-k=4-8   101.4µ ± 0%   101.9µ ± 0%  +0.49% (p=0.000 n=10)
LouvainDirectedMultiplex-8                          11.71m ± 0%   11.67m ± 0%  -0.34% (p=0.007 n=10)
LouvainDirected-8                                   10.05m ± 0%   10.08m ± 0%       ~ (p=0.089 n=10)
LouvainMultiplex-8                                  7.726m ± 0%   7.722m ± 0%       ~ (p=0.853 n=10)
Louvain-8                                           6.574m ± 0%   6.577m ± 0%       ~ (p=0.912 n=10)
geomean                                             1.109m        1.110m       +0.04%

pkg: gonum.org/v1/gonum/graph/flow
                                              │  bench.old  │             bench.new              │
                                              │   sec/op    │   sec/op     vs base               │
RandomGraphDominators/gnm-n=1e3-m=1e3-8         702.7µ ± 0%   703.3µ ± 0%       ~ (p=0.123 n=10)
RandomGraphDominators/gnm-n=1e3-m=3e3-8         890.4µ ± 0%   898.8µ ± 0%  +0.94% (p=0.000 n=10)
RandomGraphDominators/gnm-n=1e3-m=1e4-8         1.595m ± 0%   1.618m ± 1%  +1.48% (p=0.000 n=10)
RandomGraphDominators/gnm-n=1e3-m=3e4-8         3.000m ± 0%   3.005m ± 0%       ~ (p=1.000 n=10)
RandomGraphDominators/gnm-n=1e4-m=1e4-8         7.711m ± 0%   7.753m ± 3%       ~ (p=0.063 n=10)
RandomGraphDominators/gnm-n=1e4-m=3e4-8         10.60m ± 0%   10.51m ± 1%  -0.91% (p=0.009 n=10)
RandomGraphDominators/gnm-n=1e4-m=1e5-8         19.60m ± 1%   19.65m ± 0%       ~ (p=0.436 n=10)
RandomGraphDominators/gnm-n=1e4-m=3e5-8         40.86m ± 1%   40.68m ± 1%       ~ (p=0.631 n=10)
RandomGraphDominators/gnm-n=1e5-m=1e5-8         116.9m ± 1%   116.3m ± 2%       ~ (p=0.143 n=10)
RandomGraphDominators/gnm-n=1e5-m=3e5-8         192.6m ± 3%   189.5m ± 1%       ~ (p=0.105 n=10)
RandomGraphDominators/gnm-n=1e5-m=1e6-8         337.9m ± 1%   340.3m ± 1%       ~ (p=0.393 n=10)
RandomGraphDominators/gnm-n=1e5-m=3e6-8         710.1m ± 1%   707.4m ± 1%       ~ (p=0.393 n=10)
RandomGraphDominators/gnm-n=1e6-m=1e6-8          1.843 ± 1%    1.845 ± 1%       ~ (p=0.912 n=10)
RandomGraphDominators/gnm-n=1e6-m=3e6-8          3.330 ± 6%    3.438 ± 9%       ~ (p=0.853 n=10)
RandomGraphDominators/gnm-n=1e6-m=1e7-8          6.426 ± 0%    6.430 ± 0%       ~ (p=0.739 n=10)
RandomGraphDominators/gnm-n=1e6-m=3e7-8          15.26 ± 0%    15.02 ± 0%  -1.58% (p=0.000 n=10)
RandomGraphDominators/dup-n=1e3-d=0.8-a=0.1-8   851.9µ ± 2%   855.0µ ± 0%       ~ (p=0.075 n=10)
RandomGraphDominators/dup-n=1e3-d=0.5-a=0.2-8   1.904m ± 0%   1.902m ± 1%       ~ (p=0.165 n=10)
RandomGraphDominators/dup-n=1e4-d=0.8-a=0.1-8   9.421m ± 0%   9.345m ± 1%  -0.80% (p=0.000 n=10)
RandomGraphDominators/dup-n=1e4-d=0.5-a=0.2-8   50.37m ± 1%   50.38m ± 0%       ~ (p=0.481 n=10)
RandomGraphDominators/dup-n=1e5-d=0.8-a=0.1-8   133.3m ± 2%   133.2m ± 1%       ~ (p=0.796 n=10)
RandomGraphDominators/dup-n=1e5-d=0.5-a=0.2-8    2.273 ± 0%    2.256 ± 1%  -0.76% (p=0.003 n=10)
geomean                                         54.34m        54.35m       +0.03%

pkg: gonum.org/v1/gonum/graph/formats/rdf
                                                │  bench.old  │             bench.new              │
                                                │   sec/op    │   sec/op     vs base               │
IsoCanonicalHashes/test019-in.nq/decomp=false-8   13.49µ ± 0%   13.48µ ± 0%       ~ (p=0.516 n=10)
IsoCanonicalHashes/test019-in.nq/decomp=true-8    14.75µ ± 0%   14.77µ ± 1%       ~ (p=0.224 n=10)
IsoCanonicalHashes/test044-in.nq/decomp=false-8   237.1m ± 1%   237.7m ± 0%       ~ (p=0.631 n=10)
IsoCanonicalHashes/test044-in.nq/decomp=true-8    249.3m ± 0%   249.3m ± 0%       ~ (p=1.000 n=10)
URNA/test019-in.nq/URDNA2015-8                    3.264µ ± 0%   3.267µ ± 0%       ~ (p=0.838 n=10)
URNA/test019-in.nq/URGNA2012-8                    3.308µ ± 0%   3.308µ ± 0%       ~ (p=0.809 n=10)
URNA/test044-in.nq/URDNA2015-8                    2.770m ± 0%   2.770m ± 1%       ~ (p=0.393 n=10)
URNA/test044-in.nq/URGNA2012-8                    2.692m ± 0%   2.694m ± 0%       ~ (p=0.218 n=10)
geomean                                           418.8µ        419.1µ       +0.07%

pkg: gonum.org/v1/gonum/graph/path
                                                      │  bench.old   │              bench.new              │
                                                      │    sec/op    │   sec/op     vs base                │
AStarUndirected/GNP_Undirected_10_tenth-8               2.129µ ±  0%   2.127µ ± 0%        ~ (p=0.867 n=10)
AStarUndirected/GNP_Undirected_100_tenth-8              141.6µ ±  1%   141.6µ ± 0%        ~ (p=0.811 n=10)
AStarUndirected/GNP_Undirected_1000_tenth-8             7.671m ±  4%   7.062m ± 5%   -7.93% (p=0.000 n=10)
AStarUndirected/GNP_Undirected_10_half-8                5.407µ ±  0%   5.441µ ± 0%   +0.63% (p=0.006 n=10)
AStarUndirected/GNP_Undirected_100_half-8               482.9µ ±  0%   472.2µ ± 0%   -2.23% (p=0.000 n=10)
AStarUndirected/GNP_Undirected_1000_half-8              15.07m ± 10%   15.08m ± 7%        ~ (p=0.684 n=10)
AStarUndirected/NSW_Undirected_10_2_2_2-8               32.49µ ±  1%   38.26µ ± 1%  +17.73% (p=0.000 n=10)
AStarUndirected/NSW_Undirected_10_2_2_2_heuristic-8     6.380µ ±  1%   6.438µ ± 0%   +0.91% (p=0.005 n=10)
AStarUndirected/NSW_Undirected_10_2_5_2-8               51.35µ ±  1%   44.32µ ± 1%  -13.69% (p=0.000 n=10)
AStarUndirected/NSW_Undirected_10_2_5_2_heuristic-8     6.798µ ±  1%   6.691µ ± 0%   -1.57% (p=0.000 n=10)
AStarUndirected/NSW_Undirected_100_5_10_2-8             855.3µ ±  2%   757.0µ ± 1%  -11.49% (p=0.000 n=10)
AStarUndirected/NSW_Undirected_100_5_10_2_heuristic-8   31.20µ ±  1%   30.88µ ± 1%   -1.02% (p=0.001 n=10)
AStarUndirected/NSW_Undirected_100_5_20_2-8             2.431m ±  2%   2.223m ± 5%   -8.56% (p=0.000 n=10)
AStarUndirected/NSW_Undirected_100_5_20_2_heuristic-8   61.90µ ±  0%   62.57µ ± 1%   +1.09% (p=0.000 n=10)
BellmanFordFrom/500_tenth-8                             2.278m ±  1%   2.258m ± 0%   -0.86% (p=0.000 n=10)
BellmanFordFrom/500_tenth_incremental-8                 2.299m ±  0%   2.276m ± 0%   -0.97% (p=0.000 n=10)
BellmanFordFrom/1000_tenth-8                            9.035m ±  0%   9.022m ± 0%        ~ (p=0.165 n=10)
BellmanFordFrom/1000_tenth_incremental-8                9.071m ±  0%   9.086m ± 0%   +0.16% (p=0.023 n=10)
BellmanFordFrom/2000_tenth-8                            41.24m ±  0%   41.24m ± 0%        ~ (p=0.579 n=10)
BellmanFordFrom/2000_tenth_incremental-8                41.63m ±  0%   41.59m ± 0%        ~ (p=0.105 n=10)
BellmanFordFrom/500_half-8                              9.428m ±  0%   9.497m ± 0%   +0.73% (p=0.000 n=10)
BellmanFordFrom/500_half_incremental-8                  9.516m ±  0%   9.562m ± 0%   +0.47% (p=0.000 n=10)
BellmanFordFrom/1000_half-8                             35.63m ±  0%   35.67m ± 0%        ~ (p=0.089 n=10)
BellmanFordFrom/1000_half_incremental-8                 36.06m ±  0%   36.09m ± 0%        ~ (p=0.739 n=10)
BellmanFordFrom/2000_half-8                             170.2m ±  0%   169.4m ± 0%   -0.47% (p=0.002 n=10)
BellmanFordFrom/2000_half_incremental-8                 173.1m ±  0%   171.6m ± 0%   -0.83% (p=0.000 n=10)
BellmanFordFrom/500_full-8                              16.26m ±  0%   16.19m ± 0%   -0.45% (p=0.005 n=10)
BellmanFordFrom/500_full_incremental-8                  16.41m ±  0%   16.35m ± 0%   -0.38% (p=0.000 n=10)
BellmanFordFrom/1000_full-8                             58.13m ±  0%   58.30m ± 0%   +0.28% (p=0.000 n=10)
BellmanFordFrom/1000_full_incremental-8                 58.74m ±  0%   58.90m ± 0%   +0.28% (p=0.015 n=10)
BellmanFordFrom/2000_full-8                             328.0m ±  0%   327.3m ± 0%        ~ (p=0.315 n=10)
BellmanFordFrom/2000_full_incremental-8                 333.4m ±  0%   332.8m ± 0%        ~ (p=0.105 n=10)
ShortestAlts/AllTo_100×2|0.5(17)-8                      4.356µ ±  2%   4.393µ ± 2%        ~ (p=0.325 n=10)
ShortestAlts/AllToFunc_100×2|0.5(17)-8                  3.068µ ±  1%   3.038µ ± 3%        ~ (p=0.225 n=10)
ShortestAlts/AllTo_1000×2|0.5(51)-8                     27.89µ ±  1%   27.78µ ± 1%        ~ (p=0.912 n=10)
ShortestAlts/AllToFunc_1000×2|0.5(51)-8                 25.57µ ±  2%   25.42µ ± 4%        ~ (p=0.481 n=10)
ShortestAlts/AllTo_100×4|0.25(53)-8                     7.405µ ±  1%   7.411µ ± 1%        ~ (p=0.698 n=10)
ShortestAlts/AllToFunc_100×4|0.25(53)-8                 4.650µ ±  1%   4.669µ ± 1%        ~ (p=0.137 n=10)
ShortestAlts/AllTo_1000×4|0.25(574)-8                   155.0µ ±  2%   155.7µ ± 1%        ~ (p=0.739 n=10)
ShortestAlts/AllToFunc_1000×4|0.25(574)-8               123.8µ ±  1%   124.1µ ± 3%        ~ (p=0.684 n=10)
ShortestAlts/AllTo_100×16|0.1(76)-8                     10.60µ ±  1%   10.61µ ± 1%        ~ (p=0.494 n=10)
ShortestAlts/AllToFunc_100×16|0.1(76)-8                 6.520µ ±  1%   6.518µ ± 1%        ~ (p=0.699 n=10)
ShortestAlts/AllTo_1000×16|0.1(822)-8                   222.6µ ±  3%   222.6µ ± 1%        ~ (p=0.739 n=10)
ShortestAlts/AllToFunc_1000×16|0.1(822)-8               180.8µ ±  1%   181.8µ ± 1%        ~ (p=0.218 n=10)
AllShortest/AllBetween_100×2|0.5(17)-8                  3.894µ ±  1%   3.857µ ± 0%   -0.94% (p=0.030 n=10)
AllShortest/AllBetweenFunc_100×2|0.5(17)-8              2.637µ ±  1%   2.653µ ± 1%   +0.63% (p=0.035 n=10)
AllShortest/AllBetween_1000×2|0.5(51)-8                 27.16µ ±  4%   27.25µ ± 4%        ~ (p=0.912 n=10)
AllShortest/AllBetweenFunc_1000×2|0.5(51)-8             23.40µ ±  2%   23.35µ ± 2%        ~ (p=0.739 n=10)
AllShortest/AllBetween_100×4|0.25(53)-8                 7.162µ ±  1%   7.164µ ± 1%        ~ (p=0.739 n=10)
AllShortest/AllBetweenFunc_100×4|0.25(53)-8             4.428µ ±  1%   4.440µ ± 1%        ~ (p=0.183 n=10)
AllShortest/AllBetween_1000×4|0.25(574)-8               154.8µ ±  1%   155.5µ ± 1%        ~ (p=0.148 n=10)
AllShortest/AllBetweenFunc_1000×4|0.25(574)-8           122.4µ ±  1%   122.8µ ± 1%        ~ (p=0.123 n=10)
AllShortest/AllBetween_100×16|0.1(76)-8                 10.32µ ±  1%   10.30µ ± 1%        ~ (p=0.870 n=10)
AllShortest/AllBetweenFunc_100×16|0.1(76)-8             6.290µ ±  1%   6.291µ ± 1%        ~ (p=0.810 n=10)
AllShortest/AllBetween_1000×16|0.1(822)-8               214.4µ ±  2%   214.9µ ± 2%        ~ (p=0.529 n=10)
AllShortest/AllBetweenFunc_1000×16|0.1(822)-8           179.5µ ±  2%   178.2µ ± 1%        ~ (p=0.247 n=10)
geomean                                                 330.2µ         328.4µ        -0.56%

pkg: gonum.org/v1/gonum/graph/product
                                        │  bench.old   │              bench.new              │
                                        │    sec/op    │    sec/op     vs base               │
Product/Cartesian_50-0.05-8               5.877m ±  9%   5.927m ±  7%       ~ (p=0.393 n=10)
Product/Cartesian_100-0.05-8              45.02m ±  6%   45.49m ±  4%       ~ (p=0.739 n=10)
Product/Cartesian_50-0.25-8               26.04m ±  4%   25.98m ±  4%       ~ (p=0.971 n=10)
Product/Cartesian_100-0.25-8              234.4m ±  3%   233.0m ±  3%       ~ (p=0.684 n=10)
Product/Cartesian_50-0.50-8               50.62m ±  2%   50.80m ±  3%       ~ (p=0.853 n=10)
Product/Cartesian_100-0.50-8              464.4m ±  3%   461.9m ±  3%       ~ (p=0.912 n=10)
Product/Cartesian_50-0.75-8               79.03m ±  1%   78.83m ±  1%       ~ (p=0.481 n=10)
Product/Cartesian_100-0.75-8              719.5m ±  1%   723.1m ±  2%       ~ (p=0.190 n=10)
Product/Cartesian_50-0.95-8               93.83m ±  2%   94.12m ±  1%       ~ (p=0.971 n=10)
Product/Cartesian_100-0.95-8              859.7m ±  1%   858.0m ±  1%       ~ (p=0.684 n=10)
Product/Cartesian_naive_50-0.05-8         210.7m ±  2%   209.7m ±  2%       ~ (p=0.796 n=10)
Product/Cartesian_naive_100-0.05-8         3.344 ±  1%    3.336 ±  1%       ~ (p=0.684 n=10)
Product/Cartesian_naive_50-0.25-8         342.9m ±  3%   343.0m ±  3%       ~ (p=0.912 n=10)
Product/Cartesian_naive_100-0.25-8         5.425 ±  1%    5.430 ±  1%       ~ (p=0.684 n=10)
Product/Cartesian_naive_50-0.50-8         490.8m ±  2%   489.6m ±  3%       ~ (p=1.000 n=10)
Product/Cartesian_naive_100-0.50-8         7.281 ±  1%    7.306 ±  1%       ~ (p=0.315 n=10)
Product/Cartesian_naive_50-0.75-8         604.9m ±  1%   602.4m ±  1%       ~ (p=0.684 n=10)
Product/Cartesian_naive_100-0.75-8         8.771 ±  0%    8.764 ±  1%       ~ (p=0.631 n=10)
Product/Cartesian_naive_50-0.95-8         690.7m ±  1%   688.5m ±  1%       ~ (p=0.315 n=10)
Product/Cartesian_naive_100-0.95-8         9.961 ±  1%    9.971 ±  1%       ~ (p=0.912 n=10)
Product/CoNormal_50-0.05-8                190.3m ± 12%   198.5m ±  8%       ~ (p=0.529 n=10)
Product/CoNormal_50-0.25-8                 1.133 ±  4%    1.135 ±  6%       ~ (p=0.684 n=10)
Product/CoNormal_50-0.50-8                 2.453 ±  7%    2.465 ±  4%       ~ (p=0.853 n=10)
Product/CoNormal_50-0.75-8                 3.494 ±  2%    3.490 ±  2%       ~ (p=1.000 n=10)
Product/CoNormal_50-0.95-8                 4.276 ±  2%    4.215 ±  1%  -1.44% (p=0.043 n=10)
Product/CoNormal_naive_50-0.05-8          420.8m ±  9%   423.8m ±  8%       ~ (p=0.971 n=10)
Product/CoNormal_naive_50-0.25-8           1.541 ±  3%    1.546 ±  2%       ~ (p=0.684 n=10)
Product/CoNormal_naive_50-0.50-8           2.688 ±  5%    2.640 ±  5%       ~ (p=0.165 n=10)
Product/CoNormal_naive_50-0.75-8           3.209 ±  2%    3.197 ±  2%       ~ (p=0.796 n=10)
Product/CoNormal_naive_50-0.95-8           3.376 ±  2%    3.360 ±  3%       ~ (p=0.912 n=10)
Product/Lexicographical_50-0.05-8         98.12m ± 15%   96.50m ± 17%       ~ (p=0.739 n=10)
Product/Lexicographical_50-0.25-8         533.1m ±  5%   525.1m ±  7%       ~ (p=0.796 n=10)
Product/Lexicographical_50-0.50-8          1.153 ±  3%    1.141 ±  3%       ~ (p=0.971 n=10)
Product/Lexicographical_50-0.75-8          1.868 ±  3%    1.847 ±  2%       ~ (p=0.089 n=10)
Product/Lexicographical_50-0.95-8          2.302 ±  4%    2.304 ±  1%       ~ (p=0.912 n=10)
Product/Lexicographical_naive_50-0.05-8   309.4m ±  6%   301.3m ± 10%       ~ (p=0.739 n=10)
Product/Lexicographical_naive_50-0.25-8   945.0m ±  6%   945.9m ±  7%       ~ (p=0.912 n=10)
Product/Lexicographical_naive_50-0.50-8    1.806 ±  4%    1.829 ±  4%       ~ (p=0.739 n=10)
Product/Lexicographical_naive_50-0.75-8    2.762 ±  2%    2.814 ±  6%       ~ (p=0.247 n=10)
Product/Lexicographical_naive_50-0.95-8    3.330 ±  3%    3.233 ±  5%       ~ (p=0.353 n=10)
Product/Modular_50-0.05-8                  2.739 ±  4%    2.722 ±  2%       ~ (p=0.739 n=10)
Product/Modular_50-0.25-8                  2.004 ±  3%    1.990 ±  4%       ~ (p=0.684 n=10)
Product/Modular_50-0.50-8                  1.862 ±  2%    1.885 ±  2%       ~ (p=0.075 n=10)
Product/Modular_50-0.75-8                  2.223 ±  3%    2.224 ±  4%       ~ (p=0.529 n=10)
Product/Modular_50-0.95-8                  3.156 ±  2%    3.189 ±  3%       ~ (p=0.353 n=10)
Product/Modular_naive_50-0.05-8            2.753 ±  3%    2.741 ±  3%       ~ (p=0.579 n=10)
Product/Modular_naive_50-0.25-8            1.964 ±  3%    2.004 ±  4%       ~ (p=0.481 n=10)
Product/Modular_naive_50-0.50-8            1.869 ±  2%    1.875 ±  1%       ~ (p=0.912 n=10)
Product/Modular_naive_50-0.75-8            2.223 ±  2%    2.226 ±  3%       ~ (p=0.796 n=10)
Product/Modular_naive_50-0.95-8            3.230 ±  3%    3.185 ±  2%       ~ (p=0.280 n=10)
Product/Strong_50-0.05-8                  13.14m ± 12%   13.52m ± 18%       ~ (p=0.529 n=10)
Product/Strong_50-0.25-8                  189.1m ± 11%   188.5m ±  5%       ~ (p=0.853 n=10)
Product/Strong_50-0.50-8                  722.1m ±  5%   715.3m ±  6%       ~ (p=0.796 n=10)
Product/Strong_50-0.75-8                   1.568 ±  4%    1.619 ±  2%       ~ (p=0.190 n=10)
Product/Strong_50-0.95-8                   2.686 ±  2%    2.677 ±  3%       ~ (p=0.739 n=10)
Product/Strong_naive_50-0.05-8            215.5m ±  3%   216.7m ±  2%       ~ (p=0.912 n=10)
Product/Strong_naive_50-0.25-8            500.5m ±  8%   494.0m ±  6%       ~ (p=0.796 n=10)
Product/Strong_naive_50-0.50-8             1.156 ±  3%    1.184 ±  4%       ~ (p=0.143 n=10)
Product/Strong_naive_50-0.75-8             2.124 ±  2%    2.115 ±  2%       ~ (p=0.684 n=10)
Product/Strong_naive_50-0.95-8             3.229 ±  2%    3.285 ±  2%       ~ (p=0.063 n=10)
Product/Tensor_50-0.05-8                  7.335m ± 15%   7.337m ± 16%       ~ (p=1.000 n=10)
Product/Tensor_50-0.25-8                  159.6m ±  9%   158.2m ±  7%       ~ (p=1.000 n=10)
Product/Tensor_50-0.50-8                  647.7m ±  5%   635.3m ±  4%       ~ (p=0.631 n=10)
Product/Tensor_50-0.75-8                   1.481 ±  2%    1.476 ±  4%       ~ (p=0.739 n=10)
Product/Tensor_50-0.95-8                   2.533 ±  2%    2.521 ±  1%       ~ (p=0.631 n=10)
Product/Tensor_naive_50-0.05-8            192.9m ±  2%   194.5m ±  2%       ~ (p=0.315 n=10)
Product/Tensor_naive_50-0.25-8            450.0m ±  4%   452.1m ±  5%       ~ (p=0.971 n=10)
Product/Tensor_naive_50-0.50-8             1.072 ±  3%    1.068 ±  3%       ~ (p=0.853 n=10)
Product/Tensor_naive_50-0.75-8             2.002 ±  4%    1.997 ±  5%       ~ (p=0.481 n=10)
Product/Tensor_naive_50-0.95-8             3.127 ±  2%    3.133 ±  2%       ~ (p=0.912 n=10)
geomean                                   807.3m         807.4m        +0.00%

pkg: gonum.org/v1/gonum/graph/topo
                                        │  bench.old  │             bench.new              │
                                        │   sec/op    │   sec/op     vs base               │
TarjanSCCGnp_10_tenth-8                   3.766µ ± 1%   3.844µ ± 1%  +2.08% (p=0.000 n=10)
TarjanSCCGnp_100_tenth-8                  153.3µ ± 0%   153.3µ ± 0%       ~ (p=0.353 n=10)
TarjanSCCGnp_1000_tenth-8                 8.853m ± 0%   8.859m ± 1%       ~ (p=0.529 n=10)
TarjanSCCGnp_10_half-8                    7.597µ ± 0%   7.558µ ± 1%  -0.51% (p=0.015 n=10)
TarjanSCCGnp_100_half-8                   591.2µ ± 0%   588.5µ ± 0%  -0.46% (p=0.000 n=10)
TarjanSCCGnp_1000_half-8                  40.09m ± 0%   40.03m ± 1%       ~ (p=0.075 n=10)
BronKerbosch/wikipedia_example-8          11.06µ ± 0%   11.08µ ± 1%       ~ (p=0.101 n=10)
BronKerbosch/Batagelj-Zaversnik_Graph-8   59.00µ ± 1%   59.13µ ± 1%       ~ (p=0.393 n=10)
CliqueGraph/simple-8                      31.22µ ± 1%   31.31µ ± 1%       ~ (p=0.363 n=10)
CliqueGraph/Batagelj-Zaversnik_Graph-8    84.49µ ± 1%   85.22µ ± 1%  +0.86% (p=0.003 n=10)
geomean                                   131.8µ        132.2µ       +0.26%

pkg: gonum.org/v1/gonum/graph/traverse
                                    │  bench.old  │             bench.new              │
                                    │   sec/op    │   sec/op     vs base               │
WalkAllBreadthFirstGnp_10_tenth-8     1.638µ ± 0%   1.633µ ± 0%       ~ (p=0.065 n=10)
WalkAllBreadthFirstGnp_100_tenth-8    74.65µ ± 0%   73.92µ ± 0%  -0.98% (p=0.000 n=10)
WalkAllBreadthFirstGnp_1000_tenth-8   5.765m ± 0%   5.760m ± 0%       ~ (p=0.529 n=10)
WalkAllBreadthFirstGnp_10_half-8      4.074µ ± 0%   4.093µ ± 0%  +0.47% (p=0.001 n=10)
WalkAllBreadthFirstGnp_100_half-8     322.2µ ± 0%   325.6µ ± 0%  +1.03% (p=0.000 n=10)
WalkAllBreadthFirstGnp_1000_half-8    25.35m ± 0%   25.66m ± 0%  +1.20% (p=0.000 n=10)
WalkAllDepthFirstGnp_10_tenth-8       1.661µ ± 0%   1.666µ ± 1%       ~ (p=0.563 n=10)
WalkAllDepthFirstGnp_100_tenth-8      77.61µ ± 0%   76.92µ ± 0%  -0.89% (p=0.000 n=10)
WalkAllDepthFirstGnp_1000_tenth-8     5.732m ± 0%   5.739m ± 0%       ~ (p=0.105 n=10)
WalkAllDepthFirstGnp_10_half-8        4.065µ ± 1%   4.111µ ± 0%  +1.12% (p=0.000 n=10)
WalkAllDepthFirstGnp_100_half-8       346.3µ ± 0%   350.9µ ± 0%  +1.35% (p=0.000 n=10)
WalkAllDepthFirstGnp_1000_half-8      25.08m ± 0%   25.12m ± 0%       ~ (p=0.165 n=10)
geomean                               170.7µ        171.2µ       +0.29%

pkg: gonum.org/v1/gonum/unit
                  │  bench.old  │             bench.new              │
                  │   sec/op    │   sec/op     vs base               │
AddStruct-8         4.329n ± 0%   4.268n ± 0%  -1.42% (p=0.000 n=10)
MulStruct-8         3.521n ± 0%   3.466n ± 0%  -1.55% (p=0.001 n=10)
AddFloat-8          3.444n ± 0%   3.446n ± 0%       ~ (p=0.811 n=10)
MulFloat-8          3.444n ± 0%   3.450n ± 0%  +0.17% (p=0.048 n=10)
AddMapSmall-8       46.93n ± 0%   46.67n ± 0%  -0.55% (p=0.000 n=10)
MulMapSmallDiff-8   38.50n ± 0%   38.08n ± 0%  -1.09% (p=0.000 n=10)
MulMapSmallSame-8   36.90n ± 1%   36.52n ± 0%  -1.04% (p=0.000 n=10)
MulMapLargeDiff-8   121.7n ± 0%   120.4n ± 0%  -1.15% (p=0.000 n=10)
MulMapLargeSame-8   121.9n ± 0%   120.5n ± 1%  -1.15% (p=0.001 n=10)
geomean             17.79n        17.64n       -0.86%
```


jbduncan: Even when running with `-count=30`, `AStarUndirected` is producing some strange results.

- `AStarUndirected/NSW_Undirected_10_2_2_2-8` is even worse now at +52%.
- `AStarUndirected/NSW_Undirected_10_2_5_2-8` has gone from -13% to +24%.

Is `path.AStar` known for having random/inconsistent performance? Otherwise, is it my MacBook scheduling things onto efficiency cores or something, or is it a real regression? I'm not sure!

```
goos: darwin
goarch: arm64
pkg: gonum.org/v1/gonum/graph/path
                                                      │ bench2.old  │             bench2.new              │
                                                      │   sec/op    │   sec/op     vs base                │
AStarUndirected/GNP_Undirected_10_tenth-8               2.110µ ± 0%   2.091µ ± 0%   -0.88% (p=0.000 n=30)
AStarUndirected/GNP_Undirected_100_tenth-8              138.0µ ± 0%   138.6µ ± 0%   +0.45% (p=0.000 n=30)
AStarUndirected/GNP_Undirected_1000_tenth-8             6.885m ± 2%   7.072m ± 1%   +2.72% (p=0.005 n=30)
AStarUndirected/GNP_Undirected_10_half-8                5.426µ ± 0%   5.393µ ± 0%   -0.60% (p=0.000 n=30)
AStarUndirected/GNP_Undirected_100_half-8               468.1µ ± 0%   467.4µ ± 0%        ~ (p=0.390 n=30)
AStarUndirected/GNP_Undirected_1000_half-8              14.68m ± 5%   14.72m ± 2%        ~ (p=0.936 n=30)
AStarUndirected/NSW_Undirected_10_2_2_2-8               23.36µ ± 0%   35.50µ ± 0%  +52.00% (p=0.000 n=30)
AStarUndirected/NSW_Undirected_10_2_2_2_heuristic-8     6.433µ ± 0%   6.327µ ± 0%   -1.66% (p=0.000 n=30)
AStarUndirected/NSW_Undirected_10_2_5_2-8               38.90µ ± 0%   48.14µ ± 0%  +23.77% (p=0.000 n=30)
AStarUndirected/NSW_Undirected_10_2_5_2_heuristic-8     6.641µ ± 0%   6.661µ ± 0%   +0.31% (p=0.002 n=30)
AStarUndirected/NSW_Undirected_100_5_10_2-8             820.6µ ± 1%   827.3µ ± 1%   +0.82% (p=0.003 n=30)
AStarUndirected/NSW_Undirected_100_5_10_2_heuristic-8   30.87µ ± 0%   30.88µ ± 0%        ~ (p=0.728 n=30)
AStarUndirected/NSW_Undirected_100_5_20_2-8             2.444m ± 1%   2.556m ± 1%   +4.59% (p=0.000 n=30)
AStarUndirected/NSW_Undirected_100_5_20_2_heuristic-8   62.09µ ± 0%   62.41µ ± 0%   +0.52% (p=0.000 n=30)
geomean                                                 100.8µ        105.9µ        +5.08%
```
jbduncan: > I don't think we've used the best shaped graphs for those benchmarks (they really aren't ideal for A*), so I think we are fine.

@kortschak Great, good to hear it wasn't a mistake on my part!

> Would you mind changing the commit message for the first commit to
> 
> ```
> internal/order: move package from graph/internal/ordered and make function generic
> ```

Sure! Done. See the latest force-pushed commits.

Ready for another review when you are.

kortschak: We have conflicts now.
jbduncan: > We have conflicts now.

Oh, good catch! It's fixed now. Ready when you are.

Patch:
diff --git a/graph/community/louvain_directed.go b/graph/community/louvain_directed.go
index 4b2fe371..a506e55f 100644
--- a/graph/community/louvain_directed.go
+++ b/graph/community/louvain_directed.go
@@ -11,9 +11,9 @@ import (
 	"golang.org/x/exp/rand"
 
 	"gonum.org/v1/gonum/graph"
-	"gonum.org/v1/gonum/graph/internal/ordered"
 	"gonum.org/v1/gonum/graph/internal/set"
 	"gonum.org/v1/gonum/graph/iterator"
+	"gonum.org/v1/gonum/internal/order"
 )
 
 // qDirected returns the modularity Q score of the graph g subdivided into the
@@ -185,7 +185,7 @@ func reduceDirected(g graph.Directed, communities [][]graph.Node) *ReducedDirect
 		// community provided by the user for a Q calculation.
 		// Probably we should use a function to map the
 		// communities in the test sets to the remapped order.
-		ordered.ByID(nodes)
+		order.ByID(nodes)
 		communities = make([][]graph.Node, len(nodes))
 		for i := range nodes {
 			communities[i] = []graph.Node{node(i)}
diff --git a/graph/community/louvain_directed_multiplex.go b/graph/community/louvain_directed_multiplex.go
index f466eb70..d8127af0 100644
--- a/graph/community/louvain_directed_multiplex.go
+++ b/graph/community/louvain_directed_multiplex.go
@@ -11,9 +11,9 @@ import (
 	"golang.org/x/exp/rand"
 
 	"gonum.org/v1/gonum/graph"
-	"gonum.org/v1/gonum/graph/internal/ordered"
 	"gonum.org/v1/gonum/graph/internal/set"
 	"gonum.org/v1/gonum/graph/iterator"
+	"gonum.org/v1/gonum/internal/order"
 )
 
 // DirectedMultiplex is a directed multiplex graph.
@@ -298,7 +298,7 @@ func reduceDirectedMultiplex(g DirectedMultiplex, communities [][]graph.Node, we
 		// community provided by the user for a Q calculation.
 		// Probably we should use a function to map the
 		// communities in the test sets to the remapped order.
-		ordered.ByID(nodes)
+		order.ByID(nodes)
 		communities = make([][]graph.Node, len(nodes))
 		for i := range nodes {
 			communities[i] = []graph.Node{node(i)}
diff --git a/graph/community/louvain_undirected.go b/graph/community/louvain_undirected.go
index e3ed53ba..b32bcb0a 100644
--- a/graph/community/louvain_undirected.go
+++ b/graph/community/louvain_undirected.go
@@ -11,9 +11,9 @@ import (
 	"golang.org/x/exp/rand"
 
 	"gonum.org/v1/gonum/graph"
-	"gonum.org/v1/gonum/graph/internal/ordered"
 	"gonum.org/v1/gonum/graph/internal/set"
 	"gonum.org/v1/gonum/graph/iterator"
+	"gonum.org/v1/gonum/internal/order"
 )
 
 // qUndirected returns the modularity Q score of the graph g subdivided into the
@@ -183,7 +183,7 @@ func reduceUndirected(g graph.Undirected, communities [][]graph.Node) *ReducedUn
 		// community provided by the user for a Q calculation.
 		// Probably we should use a function to map the
 		// communities in the test sets to the remapped order.
-		ordered.ByID(nodes)
+		order.ByID(nodes)
 		communities = make([][]graph.Node, len(nodes))
 		for i := range nodes {
 			communities[i] = []graph.Node{node(i)}
diff --git a/graph/community/louvain_undirected_multiplex.go b/graph/community/louvain_undirected_multiplex.go
index 8a4ce140..509b17db 100644
--- a/graph/community/louvain_undirected_multiplex.go
+++ b/graph/community/louvain_undirected_multiplex.go
@@ -11,9 +11,9 @@ import (
 	"golang.org/x/exp/rand"
 
 	"gonum.org/v1/gonum/graph"
-	"gonum.org/v1/gonum/graph/internal/ordered"
 	"gonum.org/v1/gonum/graph/internal/set"
 	"gonum.org/v1/gonum/graph/iterator"
+	"gonum.org/v1/gonum/internal/order"
 )
 
 // UndirectedMultiplex is an undirected multiplex graph.
@@ -294,7 +294,7 @@ func reduceUndirectedMultiplex(g UndirectedMultiplex, communities [][]graph.Node
 		// community provided by the user for a Q calculation.
 		// Probably we should use a function to map the
 		// communities in the test sets to the remapped order.
-		ordered.ByID(nodes)
+		order.ByID(nodes)
 		communities = make([][]graph.Node, len(nodes))
 		for i := range nodes {
 			communities[i] = []graph.Node{node(i)}
diff --git a/graph/encoding/digraph6/digraph6.go b/graph/encoding/digraph6/digraph6.go
index 36081483..4f7a3988 100644
--- a/graph/encoding/digraph6/digraph6.go
+++ b/graph/encoding/digraph6/digraph6.go
@@ -11,9 +11,9 @@ import (
 	"strings"
 
 	"gonum.org/v1/gonum/graph"
-	"gonum.org/v1/gonum/graph/internal/ordered"
 	"gonum.org/v1/gonum/graph/iterator"
 	"gonum.org/v1/gonum/graph/simple"
+	"gonum.org/v1/gonum/internal/order"
 )
 
 // Graph is a digraph6-represented directed graph.
@@ -37,7 +37,7 @@ var (
 func Encode(g graph.Graph) Graph {
 	nodes := graph.NodesOf(g.Nodes())
 	n := len(nodes)
-	ordered.ByID(nodes)
+	order.ByID(nodes)
 	indexOf := make(map[int64]int, n)
 	for i, n := range nodes {
 		indexOf[n.ID()] = i
diff --git a/graph/encoding/dot/encode.go b/graph/encoding/dot/encode.go
index 3469a6f3..f8f18668 100644
--- a/graph/encoding/dot/encode.go
+++ b/graph/encoding/dot/encode.go
@@ -14,7 +14,7 @@ import (
 
 	"gonum.org/v1/gonum/graph"
 	"gonum.org/v1/gonum/graph/encoding"
-	"gonum.org/v1/gonum/graph/internal/ordered"
+	"gonum.org/v1/gonum/internal/order"
 )
 
 // Node is a DOT graph node.
@@ -166,7 +166,7 @@ func (p *simpleGraphPrinter) print(g graph.Graph, name string, needsIndent, isSu
 	}
 
 	nodes := graph.NodesOf(g.Nodes())
-	ordered.ByID(nodes)
+	order.ByID(nodes)
 
 	havePrintedNodeHeader := false
 	for _, n := range nodes {
@@ -206,7 +206,7 @@ func (p *simpleGraphPrinter) print(g graph.Graph, name string, needsIndent, isSu
 	for _, n := range nodes {
 		nid := n.ID()
 		to := graph.NodesOf(g.From(nid))
-		ordered.ByID(to)
+		order.ByID(to)
 		for _, t := range to {
 			tid := t.ID()
 			f := edge{inGraph: name, from: nid, to: tid}
@@ -465,7 +465,7 @@ func (p *multiGraphPrinter) print(g graph.Multigraph, name string, needsIndent,
 	}
 
 	nodes := graph.NodesOf(g.Nodes())
-	ordered.ByID(nodes)
+	order.ByID(nodes)
 
 	havePrintedNodeHeader := false
 	for _, n := range nodes {
@@ -505,13 +505,13 @@ func (p *multiGraphPrinter) print(g graph.Multigraph, name string, needsIndent,
 	for _, n := range nodes {
 		nid := n.ID()
 		to := graph.NodesOf(g.From(nid))
-		ordered.ByID(to)
+		order.ByID(to)
 
 		for _, t := range to {
 			tid := t.ID()
 
 			lines := graph.LinesOf(g.Lines(nid, tid))
-			ordered.LinesByIDs(lines)
+			order.LinesByIDs(lines)
 
 			for _, l := range lines {
 				lid := l.ID()
diff --git a/graph/encoding/graph6/graph6.go b/graph/encoding/graph6/graph6.go
index 55f2e28d..341add41 100644
--- a/graph/encoding/graph6/graph6.go
+++ b/graph/encoding/graph6/graph6.go
@@ -11,9 +11,9 @@ import (
 	"strings"
 
 	"gonum.org/v1/gonum/graph"
-	"gonum.org/v1/gonum/graph/internal/ordered"
 	"gonum.org/v1/gonum/graph/iterator"
 	"gonum.org/v1/gonum/graph/simple"
+	"gonum.org/v1/gonum/internal/order"
 )
 
 // Graph is a graph6-represented undirected graph.
@@ -35,7 +35,7 @@ var (
 func Encode(g graph.Graph) Graph {
 	nodes := graph.NodesOf(g.Nodes())
 	n := len(nodes)
-	ordered.ByID(nodes)
+	order.ByID(nodes)
 	indexOf := make(map[int64]int, n)
 	for i, n := range nodes {
 		indexOf[n.ID()] = i
diff --git a/graph/graphs/gen/duplication.go b/graph/graphs/gen/duplication.go
index 23480fb6..06ce3931 100644
--- a/graph/graphs/gen/duplication.go
+++ b/graph/graphs/gen/duplication.go
@@ -11,7 +11,7 @@ import (
 	"golang.org/x/exp/rand"
 
 	"gonum.org/v1/gonum/graph"
-	"gonum.org/v1/gonum/graph/internal/ordered"
+	"gonum.org/v1/gonum/internal/order"
 )
 
 // UndirectedMutator is an undirected graph builder that can remove edges.
@@ -56,7 +56,7 @@ func Duplication(dst UndirectedMutator, n int, delta, alpha, sigma float64, src
 	}
 
 	nodes := graph.NodesOf(dst.Nodes())
-	ordered.ByID(nodes)
+	order.ByID(nodes)
 	if len(nodes) == 0 {
 		n--
 		u := dst.NewNode()
@@ -76,7 +76,7 @@ func Duplication(dst UndirectedMutator, n int, delta, alpha, sigma float64, src
 		for {
 			// Add edges to parent's neighbours.
 			to := graph.NodesOf(dst.From(u.ID()))
-			ordered.ByID(to)
+			order.ByID(to)
 			for _, v := range to {
 				vid := v.ID()
 				if rnd() < delta || dst.HasEdgeBetween(vid, did) {
diff --git a/graph/internal/ordered/doc.go b/graph/internal/ordered/doc.go
deleted file mode 100644
index 563df6f2..00000000
--- a/graph/internal/ordered/doc.go
+++ /dev/null
@@ -1,6 +0,0 @@
-// Copyright ©2017 The Gonum Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Package ordered provides common sort ordering types.
-package ordered // import "gonum.org/v1/gonum/graph/internal/ordered"
diff --git a/graph/product/product.go b/graph/product/product.go
index 48f5e90c..6e3ce5ba 100644
--- a/graph/product/product.go
+++ b/graph/product/product.go
@@ -6,7 +6,7 @@ package product
 
 import (
 	"gonum.org/v1/gonum/graph"
-	"gonum.org/v1/gonum/graph/internal/ordered"
+	"gonum.org/v1/gonum/internal/order"
 	"gonum.org/v1/gonum/stat/combin"
 )
 
@@ -367,7 +367,7 @@ func cartesianNodes(a, b graph.Graph) (aNodes, bNodes []graph.Node, product []No
 // lexicalNodes returns the nodes in g sorted lexically by node ID.
 func lexicalNodes(g graph.Graph) []graph.Node {
 	nodes := graph.NodesOf(g.Nodes())
-	ordered.ByID(nodes)
+	order.ByID(nodes)
 	return nodes
 }
 
diff --git a/graph/simple/dense_directed_matrix.go b/graph/simple/dense_directed_matrix.go
index dbe5dc82..34ce7b0b 100644
--- a/graph/simple/dense_directed_matrix.go
+++ b/graph/simple/dense_directed_matrix.go
@@ -6,8 +6,8 @@ package simple
 
 import (
 	"gonum.org/v1/gonum/graph"
-	"gonum.org/v1/gonum/graph/internal/ordered"
 	"gonum.org/v1/gonum/graph/iterator"
+	"gonum.org/v1/gonum/internal/order"
 	"gonum.org/v1/gonum/mat"
 )
 
@@ -60,7 +60,7 @@ func NewDirectedMatrix(n int, init, self, absent float64) *DirectedMatrix {
 // specifies the cost of self connection, and absent specifies the weight
 // returned for absent edges.
 func NewDirectedMatrixFrom(nodes []graph.Node, init, self, absent float64) *DirectedMatrix {
-	ordered.ByID(nodes)
+	order.ByID(nodes)
 	for i, n := range nodes {
 		if int64(i) != n.ID() {
 			panic("simple: non-contiguous node IDs")
diff --git a/graph/simple/dense_undirected_matrix.go b/graph/simple/dense_undirected_matrix.go
index 27fc5a27..e97e6afb 100644
--- a/graph/simple/dense_undirected_matrix.go
+++ b/graph/simple/dense_undirected_matrix.go
@@ -6,8 +6,8 @@ package simple
 
 import (
 	"gonum.org/v1/gonum/graph"
-	"gonum.org/v1/gonum/graph/internal/ordered"
 	"gonum.org/v1/gonum/graph/iterator"
+	"gonum.org/v1/gonum/internal/order"
 	"gonum.org/v1/gonum/mat"
 )
 
@@ -60,7 +60,7 @@ func NewUndirectedMatrix(n int, init, self, absent float64) *UndirectedMatrix {
 // specifies the cost of self connection, and absent specifies the weight
 // returned for absent edges.
 func NewUndirectedMatrixFrom(nodes []graph.Node, init, self, absent float64) *UndirectedMatrix {
-	ordered.ByID(nodes)
+	order.ByID(nodes)
 	for i, n := range nodes {
 		if int64(i) != n.ID() {
 			panic("simple: non-contiguous node IDs")
diff --git a/graph/topo/clique_graph.go b/graph/topo/clique_graph.go
index 202fa953..2223ca3b 100644
--- a/graph/topo/clique_graph.go
+++ b/graph/topo/clique_graph.go
@@ -6,8 +6,8 @@ package topo
 
 import (
 	"gonum.org/v1/gonum/graph"
-	"gonum.org/v1/gonum/graph/internal/ordered"
 	"gonum.org/v1/gonum/graph/internal/set"
+	"gonum.org/v1/gonum/internal/order"
 )
 
 // Builder is a pure topological graph construction type.
@@ -26,9 +26,9 @@ func CliqueGraph(dst Builder, g graph.Undirected) {
 	// Construct a consistent view of cliques in g. Sorting costs
 	// us a little, but not as much as the cliques themselves.
 	for _, c := range cliques {
-		ordered.ByID(c)
+		order.ByID(c)
 	}
-	ordered.BySliceIDs(cliques)
+	order.BySliceIDs(cliques)
 
 	cliqueNodes := make(cliqueNodeSets, len(cliques))
 	for id, c := range cliques {
@@ -59,7 +59,7 @@ func CliqueGraph(dst Builder, g graph.Undirected) {
 					for _, n := range set.IntersectionOfNodes(uc.nodes, vc.nodes) {
 						edgeNodes = append(edgeNodes, n)
 					}
-					ordered.ByID(edgeNodes)
+					order.ByID(edgeNodes)
 				}
 
 				dst.SetEdge(CliqueGraphEdge{from: uc.Clique, to: vc.Clique, nodes: edgeNodes})
diff --git a/graph/topo/johnson_cycles.go b/graph/topo/johnson_cycles.go
index 3433a63e..0d8407b0 100644
--- a/graph/topo/johnson_cycles.go
+++ b/graph/topo/johnson_cycles.go
@@ -6,9 +6,9 @@ package topo
 
 import (
 	"gonum.org/v1/gonum/graph"
-	"gonum.org/v1/gonum/graph/internal/ordered"
 	"gonum.org/v1/gonum/graph/internal/set"
 	"gonum.org/v1/gonum/graph/iterator"
+	"gonum.org/v1/gonum/internal/order"
 )
 
 // johnson implements Johnson's "Finding all the elementary
@@ -132,7 +132,7 @@ type johnsonGraph struct {
 // johnsonGraphFrom returns a deep copy of the graph g.
 func johnsonGraphFrom(g graph.Directed) johnsonGraph {
 	nodes := graph.NodesOf(g.Nodes())
-	ordered.ByID(nodes)
+	order.ByID(nodes)
 	c := johnsonGraph{
 		orig:  nodes,
 		index: make(map[int64]int, len(nodes)),
diff --git a/graph/topo/tarjan.go b/graph/topo/tarjan.go
index eef7d763..31b16519 100644
--- a/graph/topo/tarjan.go
+++ b/graph/topo/tarjan.go
@@ -9,8 +9,8 @@ import (
 	"slices"
 
 	"gonum.org/v1/gonum/graph"
-	"gonum.org/v1/gonum/graph/internal/ordered"
 	"gonum.org/v1/gonum/graph/internal/set"
+	"gonum.org/v1/gonum/internal/order"
 )
 
 // Unorderable is an error containing sets of unorderable graph.Nodes.
@@ -30,7 +30,7 @@ func (e Unorderable) Error() string {
 	return fmt.Sprintf("topo: no topological ordering: cyclic components: %v", [][]graph.Node(e))
 }
 
-func lexical(nodes []graph.Node) { ordered.ByID(nodes) }
+func lexical(nodes []graph.Node) { order.ByID(nodes) }
 
 // Sort performs a topological sort of the directed graph g returning the 'from' to 'to'
 // sort order. If a topological ordering is not possible, an Unorderable error is returned
diff --git a/graph/topo/topo.go b/graph/topo/topo.go
index a76accb3..34497279 100644
--- a/graph/topo/topo.go
+++ b/graph/topo/topo.go
@@ -6,8 +6,8 @@ package topo
 
 import (
 	"gonum.org/v1/gonum/graph"
-	"gonum.org/v1/gonum/graph/internal/ordered"
 	"gonum.org/v1/gonum/graph/traverse"
+	"gonum.org/v1/gonum/internal/order"
 )
 
 // IsPathIn returns whether path is a path in g.
@@ -80,8 +80,8 @@ func Equal(a, b graph.Graph) bool {
 
 	aNodeSlice := graph.NodesOf(aNodes)
 	bNodeSlice := graph.NodesOf(bNodes)
-	ordered.ByID(aNodeSlice)
-	ordered.ByID(bNodeSlice)
+	order.ByID(aNodeSlice)
+	order.ByID(bNodeSlice)
 	for i, aU := range aNodeSlice {
 		id := aU.ID()
 		if id != bNodeSlice[i].ID() {
@@ -96,8 +96,8 @@ func Equal(a, b graph.Graph) bool {
 
 		aAdjacent := graph.NodesOf(toA)
 		bAdjacent := graph.NodesOf(toB)
-		ordered.ByID(aAdjacent)
-		ordered.ByID(bAdjacent)
+		order.ByID(aAdjacent)
+		order.ByID(bAdjacent)
 		for i, aV := range aAdjacent {
 			id := aV.ID()
 			if id != bAdjacent[i].ID() {
diff --git a/internal/order/doc.go b/internal/order/doc.go
new file mode 100644
index 00000000..6ed2f38c
--- /dev/null
+++ b/internal/order/doc.go
@@ -0,0 +1,6 @@
+// Copyright ©2024 The Gonum Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package order provides common sorting functions.
+package order // import "gonum.org/v1/gonum/internal/order"
diff --git a/graph/internal/ordered/sort.go b/internal/order/order.go
similarity index 73%
rename from graph/internal/ordered/sort.go
rename to internal/order/order.go
index 3c48f688..a6b44edd 100644
--- a/graph/internal/ordered/sort.go
+++ b/internal/order/order.go
@@ -1,10 +1,12 @@
-// Copyright ©2015 The Gonum Authors. All rights reserved.
+// Copyright ©2024 The Gonum Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package ordered
+package order
 
 import (
+	"cmp"
+	"slices"
 	"sort"
 
 	"gonum.org/v1/gonum/graph"
@@ -15,24 +17,17 @@ func ByID(n []graph.Node) {
 	sort.Slice(n, func(i, j int) bool { return n[i].ID() < n[j].ID() })
 }
 
-// BySliceValues sorts a slice of []int64 lexically by the values of the
-// []int64.
-func BySliceValues(c [][]int64) {
-	sort.Slice(c, func(i, j int) bool {
-		a, b := c[i], c[j]
-		l := len(a)
-		if len(b) < l {
-			l = len(b)
-		}
+// BySliceValues sorts a slice of []cmp.Ordered lexically by the values of
+// the []cmp.Ordered.
+func BySliceValues[S interface{ ~[]E }, E cmp.Ordered](c []S) {
+	slices.SortFunc(c, func(a, b S) int {
+		l := min(len(a), len(b))
 		for k, v := range a[:l] {
-			if v < b[k] {
-				return true
-			}
-			if v > b[k] {
-				return false
+			if n := cmp.Compare(v, b[k]); n != 0 {
+				return n
 			}
 		}
-		return len(a) < len(b)
+		return cmp.Compare(len(a), len(b))
 	})
 }
 
diff --git a/stat/roc.go b/stat/roc.go
index 05c6b44d..19add6fa 100644
--- a/stat/roc.go
+++ b/stat/roc.go
@@ -6,6 +6,7 @@ package stat
 
 import (
 	"math"
+	"slices"
 	"sort"
 )
 
@@ -117,13 +118,9 @@ func ROC(cutoffs, y []float64, classes []bool, weights []float64) (tpr, fpr, thr
 		tpr[i] = 1 - float64(tpr[i]*invPos)
 		fpr[i] = 1 - float64(fpr[i]*invNeg)
 	}
-	for i, j := 0, len(tpr)-1; i < j; i, j = i+1, j-1 {
-		tpr[i], tpr[j] = tpr[j], tpr[i]
-		fpr[i], fpr[j] = fpr[j], fpr[i]
-	}
-	for i, j := 0, len(cutoffs)-1; i < j; i, j = i+1, j-1 {
-		cutoffs[i], cutoffs[j] = cutoffs[j], cutoffs[i]
-	}
+	slices.Reverse(tpr)
+	slices.Reverse(fpr)
+	slices.Reverse(cutoffs)
 
 	return tpr, fpr, cutoffs
 }


Test patch:
diff --git a/graph/community/bisect_example_test.go b/graph/community/bisect_example_test.go
index 6b99d315..53e6f374 100644
--- a/graph/community/bisect_example_test.go
+++ b/graph/community/bisect_example_test.go
@@ -11,8 +11,8 @@ import (
 	"golang.org/x/exp/rand"
 
 	"gonum.org/v1/gonum/graph/community"
-	"gonum.org/v1/gonum/graph/internal/ordered"
 	"gonum.org/v1/gonum/graph/simple"
+	"gonum.org/v1/gonum/internal/order"
 )
 
 func ExampleProfile_simple() {
@@ -51,9 +51,9 @@ func ExampleProfile_simple() {
 	for _, d := range p {
 		comm := d.Communities()
 		for _, c := range comm {
-			ordered.ByID(c)
+			order.ByID(c)
 		}
-		ordered.BySliceIDs(comm)
+		order.BySliceIDs(comm)
 		fmt.Printf("Low:%.2v High:%.2v Score:%v Communities:%v Q=%.3v\n",
 			d.Low, d.High, d.Score, comm, community.Q(g, comm, d.Low))
 	}
@@ -199,9 +199,9 @@ func ExampleProfile_multiplex() {
 	for _, d := range p {
 		comm := d.Communities()
 		for _, c := range comm {
-			ordered.ByID(c)
+			order.ByID(c)
 		}
-		ordered.BySliceIDs(comm)
+		order.BySliceIDs(comm)
 		fmt.Printf("Low:%.2v High:%.2v Score:%v Communities:%v Q=%.3v\n",
 			d.Low, d.High, d.Score, comm, community.QMultiplex(g, comm, weights, []float64{d.Low}))
 	}
diff --git a/graph/community/k_communities_test.go b/graph/community/k_communities_test.go
index f2afdc5a..6271ff0b 100644
--- a/graph/community/k_communities_test.go
+++ b/graph/community/k_communities_test.go
@@ -10,8 +10,8 @@ import (
 	"testing"
 
 	"gonum.org/v1/gonum/graph"
-	"gonum.org/v1/gonum/graph/internal/ordered"
 	"gonum.org/v1/gonum/graph/simple"
+	"gonum.org/v1/gonum/internal/order"
 )
 
 // batageljZaversnikGraph is the example graph from
@@ -125,9 +125,9 @@ func TestKCliqueCommunities(t *testing.T) {
 		got := KCliqueCommunities(test.k, g)
 
 		for _, c := range got {
-			ordered.ByID(c)
+			order.ByID(c)
 		}
-		ordered.BySliceIDs(got)
+		order.BySliceIDs(got)
 
 		if !reflect.DeepEqual(got, test.want) {
 			t.Errorf("unexpected k-connected components for %q k=%d:\ngot: %v\nwant:%v", test.name, test.k, got, test.want)
diff --git a/graph/community/louvain_directed_multiplex_test.go b/graph/community/louvain_directed_multiplex_test.go
index f28e89f2..ad762149 100644
--- a/graph/community/louvain_directed_multiplex_test.go
+++ b/graph/community/louvain_directed_multiplex_test.go
@@ -17,8 +17,8 @@ import (
 	"gonum.org/v1/gonum/floats"
 	"gonum.org/v1/gonum/floats/scalar"
 	"gonum.org/v1/gonum/graph"
-	"gonum.org/v1/gonum/graph/internal/ordered"
 	"gonum.org/v1/gonum/graph/simple"
+	"gonum.org/v1/gonum/internal/order"
 )
 
 var communityDirectedMultiplexQTests = []struct {
@@ -310,7 +310,7 @@ func TestCommunityQDirectedMultiplex(t *testing.T) {
 			got := floats.Sum(q)
 			if !scalar.EqualWithinAbsOrRel(got, structure.want, structure.tol, structure.tol) && !math.IsNaN(structure.want) {
 				for _, c := range communities {
-					ordered.ByID(c)
+					order.ByID(c)
 				}
 				t.Errorf("unexpected Q value for %q %v: got: %v %.3v want: %v",
 					test.name, communities, got, q, structure.want)
@@ -338,7 +338,7 @@ tests:
 					communityOf[n] = i
 					communities[i] = append(communities[i], simple.Node(n))
 				}
-				ordered.ByID(communities[i])
+				order.ByID(communities[i])
 			}
 			resolution := []float64{structure.resolution}
 
@@ -359,7 +359,7 @@ tests:
 
 			// This is done to avoid run-to-run
 			// variation due to map iteration order.
-			ordered.ByID(l.nodes)
+			order.ByID(l.nodes)
 
 			l.shuffle(rnd)
 
@@ -376,7 +376,7 @@ tests:
 						}
 						migrated[i] = append(migrated[i], simple.Node(n))
 					}
-					ordered.ByID(migrated[i])
+					order.ByID(migrated[i])
 				}
 
 				for i, c := range structure.memberships {
@@ -454,7 +454,7 @@ tests:
 				for n := range c {
 					communities[i] = append(communities[i], simple.Node(n))
 				}
-				ordered.ByID(communities[i])
+				order.ByID(communities[i])
 			}
 
 			gQ := QMultiplex(g, communities, weights, []float64{structure.resolution})
@@ -540,7 +540,7 @@ func TestMoveLocalDirectedMultiplex(t *testing.T) {
 				for n := range c {
 					communities[i] = append(communities[i], simple.Node(n))
 				}
-				ordered.ByID(communities[i])
+				order.ByID(communities[i])
 			}
 
 			r := reduceDirectedMultiplex(reduceDirectedMultiplex(g, nil, weights), communities, weights)
@@ -580,9 +580,9 @@ func TestLouvainDirectedMultiplex(t *testing.T) {
 			for n := range c {
 				want[i] = append(want[i], simple.Node(n))
 			}
-			ordered.ByID(want[i])
+			order.ByID(want[i])
 		}
-		ordered.BySliceIDs(want)
+		order.BySliceIDs(want)
 
 		var (
 			got   *ReducedDirectedMultiplex
@@ -616,9 +616,9 @@ func TestLouvainDirectedMultiplex(t *testing.T) {
 
 		gotCommunities := got.Communities()
 		for _, c := range gotCommunities {
-			ordered.ByID(c)
+			order.ByID(c)
 		}
-		ordered.BySliceIDs(gotCommunities)
+		order.BySliceIDs(gotCommunities)
 		if !reflect.DeepEqual(gotCommunities, want) {
 			t.Errorf("unexpected community membership for %s Q=%.4v:\n\tgot: %v\n\twant:%v",
 				test.name, bestQ, gotCommunities, want)
@@ -631,9 +631,9 @@ func TestLouvainDirectedMultiplex(t *testing.T) {
 			if p.parent != nil {
 				communities = p.parent.Communities()
 				for _, c := range communities {
-					ordered.ByID(c)
+					order.ByID(c)
 				}
-				ordered.BySliceIDs(communities)
+				order.BySliceIDs(communities)
 			} else {
 				communities = reduceDirectedMultiplex(g, nil, weights).Communities()
 			}
diff --git a/graph/community/louvain_directed_test.go b/graph/community/louvain_directed_test.go
index bebdd081..16ec6873 100644
--- a/graph/community/louvain_directed_test.go
+++ b/graph/community/louvain_directed_test.go
@@ -15,8 +15,8 @@ import (
 
 	"gonum.org/v1/gonum/floats/scalar"
 	"gonum.org/v1/gonum/graph"
-	"gonum.org/v1/gonum/graph/internal/ordered"
 	"gonum.org/v1/gonum/graph/simple"
+	"gonum.org/v1/gonum/internal/order"
 )
 
 type communityDirectedQTest struct {
@@ -245,7 +245,7 @@ func testCommunityQDirected(t *testing.T, test communityDirectedQTest, g graph.D
 		got := Q(g, communities, structure.resolution)
 		if !scalar.EqualWithinAbsOrRel(got, structure.want, structure.tol, structure.tol) && !math.IsNaN(structure.want) {
 			for _, c := range communities {
-				ordered.ByID(c)
+				order.ByID(c)
 			}
 			t.Errorf("unexpected Q value for %q %v: got: %v want: %v",
 				test.name, communities, got, structure.want)
@@ -298,7 +298,7 @@ func testCommunityDeltaQDirected(t *testing.T, test communityDirectedQTest, g gr
 				communityOf[n] = i
 				communities[i] = append(communities[i], simple.Node(n))
 			}
-			ordered.ByID(communities[i])
+			order.ByID(communities[i])
 		}
 
 		before := Q(g, communities, structure.resolution)
@@ -313,7 +313,7 @@ func testCommunityDeltaQDirected(t *testing.T, test communityDirectedQTest, g gr
 
 		// This is done to avoid run-to-run
 		// variation due to map iteration order.
-		ordered.ByID(l.nodes)
+		order.ByID(l.nodes)
 
 		l.shuffle(rnd)
 
@@ -330,7 +330,7 @@ func testCommunityDeltaQDirected(t *testing.T, test communityDirectedQTest, g gr
 					}
 					migrated[i] = append(migrated[i], simple.Node(n))
 				}
-				ordered.ByID(migrated[i])
+				order.ByID(migrated[i])
 			}
 
 			for i, c := range structure.memberships {
@@ -423,7 +423,7 @@ func testReduceQConsistencyDirected(t *testing.T, test communityDirectedQTest, g
 			for n := range c {
 				communities[i] = append(communities[i], simple.Node(n))
 			}
-			ordered.ByID(communities[i])
+			order.ByID(communities[i])
 		}
 
 		gQ := Q(g, communities, structure.resolution)
@@ -537,7 +537,7 @@ func testMoveLocalDirected(t *testing.T, test localDirectedMoveTest, g graph.Dir
 			for n := range c {
 				communities[i] = append(communities[i], simple.Node(n))
 			}
-			ordered.ByID(communities[i])
+			order.ByID(communities[i])
 		}
 
 		r := reduceDirected(reduceDirected(g, nil), communities)
@@ -603,9 +603,9 @@ func testModularizeDirected(t *testing.T, test communityDirectedQTest, g graph.D
 		for n := range c {
 			want[i] = append(want[i], simple.Node(n))
 		}
-		ordered.ByID(want[i])
+		order.ByID(want[i])
 	}
-	ordered.BySliceIDs(want)
+	order.BySliceIDs(want)
 
 	var (
 		got   *ReducedDirected
@@ -639,9 +639,9 @@ func testModularizeDirected(t *testing.T, test communityDirectedQTest, g graph.D
 
 	gotCommunities := got.Communities()
 	for _, c := range gotCommunities {
-		ordered.ByID(c)
+		order.ByID(c)
 	}
-	ordered.BySliceIDs(gotCommunities)
+	order.BySliceIDs(gotCommunities)
 	if !reflect.DeepEqual(gotCommunities, want) {
 		t.Errorf("unexpected community membership for %s Q=%.4v:\n\tgot: %v\n\twant:%v",
 			test.name, bestQ, gotCommunities, want)
@@ -654,9 +654,9 @@ func testModularizeDirected(t *testing.T, test communityDirectedQTest, g graph.D
 		if p.parent != nil {
 			communities = p.parent.Communities()
 			for _, c := range communities {
-				ordered.ByID(c)
+				order.ByID(c)
 			}
-			ordered.BySliceIDs(communities)
+			order.BySliceIDs(communities)
 		} else {
 			communities = reduceDirected(g, nil).Communities()
 		}
diff --git a/graph/community/louvain_undirected_multiplex_test.go b/graph/community/louvain_undirected_multiplex_test.go
index 0e35dbbc..1d6aaff2 100644
--- a/graph/community/louvain_undirected_multiplex_test.go
+++ b/graph/community/louvain_undirected_multiplex_test.go
@@ -17,8 +17,8 @@ import (
 	"gonum.org/v1/gonum/floats"
 	"gonum.org/v1/gonum/floats/scalar"
 	"gonum.org/v1/gonum/graph"
-	"gonum.org/v1/gonum/graph/internal/ordered"
 	"gonum.org/v1/gonum/graph/simple"
+	"gonum.org/v1/gonum/internal/order"
 )
 
 var communityUndirectedMultiplexQTests = []struct {
@@ -279,7 +279,7 @@ func TestCommunityQUndirectedMultiplex(t *testing.T) {
 			got := floats.Sum(q)
 			if !scalar.EqualWithinAbsOrRel(got, structure.want, structure.tol, structure.tol) && !math.IsNaN(structure.want) {
 				for _, c := range communities {
-					ordered.ByID(c)
+					order.ByID(c)
 				}
 				t.Errorf("unexpected Q value for %q %v: got: %v %.3v want: %v",
 					test.name, communities, got, q, structure.want)
@@ -307,7 +307,7 @@ tests:
 					communityOf[n] = i
 					communities[i] = append(communities[i], simple.Node(n))
 				}
-				ordered.ByID(communities[i])
+				order.ByID(communities[i])
 			}
 			resolution := []float64{structure.resolution}
 
@@ -328,7 +328,7 @@ tests:
 
 			// This is done to avoid run-to-run
 			// variation due to map iteration order.
-			ordered.ByID(l.nodes)
+			order.ByID(l.nodes)
 
 			l.shuffle(rnd)
 
@@ -345,7 +345,7 @@ tests:
 						}
 						migrated[i] = append(migrated[i], simple.Node(n))
 					}
-					ordered.ByID(migrated[i])
+					order.ByID(migrated[i])
 				}
 
 				for i, c := range structure.memberships {
@@ -423,7 +423,7 @@ tests:
 				for n := range c {
 					communities[i] = append(communities[i], simple.Node(n))
 				}
-				ordered.ByID(communities[i])
+				order.ByID(communities[i])
 			}
 
 			gQ := QMultiplex(g, communities, weights, []float64{structure.resolution})
@@ -509,7 +509,7 @@ func TestMoveLocalUndirectedMultiplex(t *testing.T) {
 				for n := range c {
 					communities[i] = append(communities[i], simple.Node(n))
 				}
-				ordered.ByID(communities[i])
+				order.ByID(communities[i])
 			}
 
 			r := reduceUndirectedMultiplex(reduceUndirectedMultiplex(g, nil, weights), communities, weights)
@@ -549,9 +549,9 @@ func TestLouvainMultiplex(t *testing.T) {
 			for n := range c {
 				want[i] = append(want[i], simple.Node(n))
 			}
-			ordered.ByID(want[i])
+			order.ByID(want[i])
 		}
-		ordered.BySliceIDs(want)
+		order.BySliceIDs(want)
 
 		var (
 			got   *ReducedUndirectedMultiplex
@@ -585,9 +585,9 @@ func TestLouvainMultiplex(t *testing.T) {
 
 		gotCommunities := got.Communities()
 		for _, c := range gotCommunities {
-			ordered.ByID(c)
+			order.ByID(c)
 		}
-		ordered.BySliceIDs(gotCommunities)
+		order.BySliceIDs(gotCommunities)
 		if !reflect.DeepEqual(gotCommunities, want) {
 			t.Errorf("unexpected community membership for %s Q=%.4v:\n\tgot: %v\n\twant:%v",
 				test.name, bestQ, gotCommunities, want)
@@ -600,9 +600,9 @@ func TestLouvainMultiplex(t *testing.T) {
 			if p.parent != nil {
 				communities = p.parent.Communities()
 				for _, c := range communities {
-					ordered.ByID(c)
+					order.ByID(c)
 				}
-				ordered.BySliceIDs(communities)
+				order.BySliceIDs(communities)
 			} else {
 				communities = reduceUndirectedMultiplex(g, nil, weights).Communities()
 			}
diff --git a/graph/community/louvain_undirected_test.go b/graph/community/louvain_undirected_test.go
index fa63f20c..e77ea378 100644
--- a/graph/community/louvain_undirected_test.go
+++ b/graph/community/louvain_undirected_test.go
@@ -15,8 +15,8 @@ import (
 
 	"gonum.org/v1/gonum/floats/scalar"
 	"gonum.org/v1/gonum/graph"
-	"gonum.org/v1/gonum/graph/internal/ordered"
 	"gonum.org/v1/gonum/graph/simple"
+	"gonum.org/v1/gonum/internal/order"
 )
 
 type communityUndirectedQTest struct {
@@ -308,7 +308,7 @@ func testCommunityQUndirected(t *testing.T, test communityUndirectedQTest, g gra
 		got := Q(g, communities, structure.resolution)
 		if !scalar.EqualWithinAbsOrRel(got, structure.want, structure.tol, structure.tol) && !math.IsNaN(structure.want) {
 			for _, c := range communities {
-				ordered.ByID(c)
+				order.ByID(c)
 			}
 			t.Errorf("unexpected Q value for %q %v: got: %v want: %v",
 				test.name, communities, got, structure.want)
@@ -361,7 +361,7 @@ func testCommunityDeltaQUndirected(t *testing.T, test communityUndirectedQTest,
 				communityOf[n] = i
 				communities[i] = append(communities[i], simple.Node(n))
 			}
-			ordered.ByID(communities[i])
+			order.ByID(communities[i])
 		}
 
 		before := Q(g, communities, structure.resolution)
@@ -376,7 +376,7 @@ func testCommunityDeltaQUndirected(t *testing.T, test communityUndirectedQTest,
 
 		// This is done to avoid run-to-run
 		// variation due to map iteration order.
-		ordered.ByID(l.nodes)
+		order.ByID(l.nodes)
 
 		l.shuffle(rnd)
 
@@ -393,7 +393,7 @@ func testCommunityDeltaQUndirected(t *testing.T, test communityUndirectedQTest,
 					}
 					migrated[i] = append(migrated[i], simple.Node(n))
 				}
-				ordered.ByID(migrated[i])
+				order.ByID(migrated[i])
 			}
 
 			for i, c := range structure.memberships {
@@ -486,7 +486,7 @@ func testReduceQConsistencyUndirected(t *testing.T, test communityUndirectedQTes
 			for n := range c {
 				communities[i] = append(communities[i], simple.Node(n))
 			}
-			ordered.ByID(communities[i])
+			order.ByID(communities[i])
 		}
 
 		gQ := Q(g, communities, structure.resolution)
@@ -600,7 +600,7 @@ func testMoveLocalUndirected(t *testing.T, test localUndirectedMoveTest, g graph
 			for n := range c {
 				communities[i] = append(communities[i], simple.Node(n))
 			}
-			ordered.ByID(communities[i])
+			order.ByID(communities[i])
 		}
 
 		r := reduceUndirected(reduceUndirected(g, nil), communities)
@@ -666,9 +666,9 @@ func testModularizeUndirected(t *testing.T, test communityUndirectedQTest, g gra
 		for n := range c {
 			want[i] = append(want[i], simple.Node(n))
 		}
-		ordered.ByID(want[i])
+		order.ByID(want[i])
 	}
-	ordered.BySliceIDs(want)
+	order.BySliceIDs(want)
 
 	var (
 		got   *ReducedUndirected
@@ -702,9 +702,9 @@ func testModularizeUndirected(t *testing.T, test communityUndirectedQTest, g gra
 
 	gotCommunities := got.Communities()
 	for _, c := range gotCommunities {
-		ordered.ByID(c)
+		order.ByID(c)
 	}
-	ordered.BySliceIDs(gotCommunities)
+	order.BySliceIDs(gotCommunities)
 	if !reflect.DeepEqual(gotCommunities, want) {
 		t.Errorf("unexpected community membership for %s Q=%.4v:\n\tgot: %v\n\twant:%v",
 			test.name, bestQ, gotCommunities, want)
@@ -717,9 +717,9 @@ func testModularizeUndirected(t *testing.T, test communityUndirectedQTest, g gra
 		if p.parent != nil {
 			communities = p.parent.Communities()
 			for _, c := range communities {
-				ordered.ByID(c)
+				order.ByID(c)
 			}
-			ordered.BySliceIDs(communities)
+			order.BySliceIDs(communities)
 		} else {
 			communities = reduceUndirected(g, nil).Communities()
 		}
diff --git a/graph/flow/control_flow_test.go b/graph/flow/control_flow_test.go
index 9d44aa41..7d86fa37 100644
--- a/graph/flow/control_flow_test.go
+++ b/graph/flow/control_flow_test.go
@@ -9,8 +9,8 @@ import (
 	"testing"
 
 	"gonum.org/v1/gonum/graph"
-	"gonum.org/v1/gonum/graph/internal/ordered"
 	"gonum.org/v1/gonum/graph/simple"
+	"gonum.org/v1/gonum/internal/order"
 )
 
 var dominatorsTests = []struct {
@@ -171,7 +171,7 @@ func TestDominators(t *testing.T) {
 				}
 
 				for _, nodes := range got.dominatedBy {
-					ordered.ByID(nodes)
+					order.ByID(nodes)
 				}
 
 				if !reflect.DeepEqual(got.dominatedBy, test.want.dominatedBy) {
diff --git a/graph/graph_test.go b/graph/graph_test.go
index 2ca96872..a1594afb 100644
--- a/graph/graph_test.go
+++ b/graph/graph_test.go
@@ -8,8 +8,8 @@ import (
 	"testing"
 
 	"gonum.org/v1/gonum/graph"
-	"gonum.org/v1/gonum/graph/internal/ordered"
 	"gonum.org/v1/gonum/graph/simple"
+	"gonum.org/v1/gonum/internal/order"
 )
 
 type graphBuilder interface {
@@ -263,8 +263,8 @@ func TestCopyWeighted(t *testing.T) {
 func same(a, b graph.Graph) bool {
 	aNodes := graph.NodesOf(a.Nodes())
 	bNodes := graph.NodesOf(b.Nodes())
-	ordered.ByID(aNodes)
-	ordered.ByID(bNodes)
+	order.ByID(aNodes)
+	order.ByID(bNodes)
 	for i, na := range aNodes {
 		nb := bNodes[i]
 		if na != nb {
@@ -277,8 +277,8 @@ func same(a, b graph.Graph) bool {
 		if len(aFromU) != len(bFromU) {
 			return false
 		}
-		ordered.ByID(aFromU)
-		ordered.ByID(bFromU)
+		order.ByID(aFromU)
+		order.ByID(bFromU)
 		for i, va := range aFromU {
 			vb := bFromU[i]
 			if va != vb {
diff --git a/graph/iterator/nodes_test.go b/graph/iterator/nodes_test.go
index 45fc0c31..df895adb 100644
--- a/graph/iterator/nodes_test.go
+++ b/graph/iterator/nodes_test.go
@@ -9,9 +9,9 @@ import (
 	"testing"
 
 	"gonum.org/v1/gonum/graph"
-	"gonum.org/v1/gonum/graph/internal/ordered"
 	"gonum.org/v1/gonum/graph/iterator"
 	"gonum.org/v1/gonum/graph/simple"
+	"gonum.org/v1/gonum/internal/order"
 )
 
 var orderedNodesTests = []struct {
@@ -740,7 +740,7 @@ func TestNodeSlicers(t *testing.T) {
 				t.Errorf("test %d: unexpected total node count: got:%d want:%d", k, gotLen, wantLen)
 			}
 			got := append(gotIter, gotSlice...)
-			ordered.ByID(got)
+			order.ByID(got)
 			if !reflect.DeepEqual(got, test.want) {
 				t.Errorf("test %d: unexpected node slice:\ngot: %v\nwant:%v", k, got, test.want)
 			}
diff --git a/graph/layout/layout_test.go b/graph/layout/layout_test.go
index bf6cddac..c60e85fa 100644
--- a/graph/layout/layout_test.go
+++ b/graph/layout/layout_test.go
@@ -15,8 +15,8 @@ import (
 	"testing"
 
 	"gonum.org/v1/gonum/graph"
-	"gonum.org/v1/gonum/graph/internal/ordered"
 	"gonum.org/v1/gonum/graph/iterator"
+	"gonum.org/v1/gonum/internal/order"
 	"gonum.org/v1/plot/cmpimg"
 )
 
@@ -29,13 +29,13 @@ type orderedGraph struct {
 
 func (g orderedGraph) Nodes() graph.Nodes {
 	n := graph.NodesOf(g.Graph.Nodes())
-	ordered.ByID(n)
+	order.ByID(n)
 	return iterator.NewOrderedNodes(n)
 }
 
 func (g orderedGraph) From(id int64) graph.Nodes {
 	n := graph.NodesOf(g.Graph.From(id))
-	ordered.ByID(n)
+	order.ByID(n)
 	return iterator.NewOrderedNodes(n)
 }
 
diff --git a/graph/path/bellman_ford_moore_test.go b/graph/path/bellman_ford_moore_test.go
index f5835ca7..70a6048a 100644
--- a/graph/path/bellman_ford_moore_test.go
+++ b/graph/path/bellman_ford_moore_test.go
@@ -10,9 +10,9 @@ import (
 	"testing"
 
 	"gonum.org/v1/gonum/graph"
-	"gonum.org/v1/gonum/graph/internal/ordered"
 	"gonum.org/v1/gonum/graph/path/internal/testgraphs"
 	"gonum.org/v1/gonum/graph/traverse"
+	"gonum.org/v1/gonum/internal/order"
 )
 
 func TestBellmanFordFrom(t *testing.T) {
@@ -163,7 +163,7 @@ func TestBellmanFordAllFrom(t *testing.T) {
 						test.Name, tg.typ, gotPaths)
 				}
 			} else {
-				ordered.BySliceValues(gotPaths)
+				order.BySliceValues(gotPaths)
 				if !reflect.DeepEqual(gotPaths, test.WantPaths) {
 					t.Errorf("testing %q %s: unexpected shortest paths:\ngot: %v\nwant:%v",
 						test.Name, tg.typ, gotPaths, test.WantPaths)
@@ -194,7 +194,7 @@ func TestBellmanFordAllFrom(t *testing.T) {
 						test.Name, tg.typ, gotPaths)
 				}
 			} else {
-				ordered.BySliceValues(gotPaths)
+				order.BySliceValues(gotPaths)
 				if !reflect.DeepEqual(gotPaths, test.WantPaths) {
 					t.Errorf("testing %q %s: unexpected shortest paths:\ngot: %v\nwant:%v",
 						test.Name, tg.typ, gotPaths, test.WantPaths)
diff --git a/graph/path/dijkstra_test.go b/graph/path/dijkstra_test.go
index 45deec27..99762bbf 100644
--- a/graph/path/dijkstra_test.go
+++ b/graph/path/dijkstra_test.go
@@ -10,10 +10,10 @@ import (
 	"testing"
 
 	"gonum.org/v1/gonum/graph"
-	"gonum.org/v1/gonum/graph/internal/ordered"
 	"gonum.org/v1/gonum/graph/path/internal/testgraphs"
 	"gonum.org/v1/gonum/graph/simple"
 	"gonum.org/v1/gonum/graph/traverse"
+	"gonum.org/v1/gonum/internal/order"
 )
 
 func TestDijkstraFrom(t *testing.T) {
@@ -182,7 +182,7 @@ func TestDijkstraAllFrom(t *testing.T) {
 					gotPaths[i] = append(gotPaths[i], v.ID())
 				}
 			}
-			ordered.BySliceValues(gotPaths)
+			order.BySliceValues(gotPaths)
 			if !reflect.DeepEqual(gotPaths, test.WantPaths) {
 				t.Errorf("testing %q: unexpected shortest paths:\ngot: %v\nwant:%v",
 					test.Name, gotPaths, test.WantPaths)
@@ -210,7 +210,7 @@ func TestDijkstraAllFrom(t *testing.T) {
 					gotPaths[i] = append(gotPaths[i], v.ID())
 				}
 			}
-			ordered.BySliceValues(gotPaths)
+			order.BySliceValues(gotPaths)
 			if !reflect.DeepEqual(gotPaths, test.WantPaths) {
 				t.Errorf("testing %q %s: unexpected shortest paths:\ngot: %v\nwant:%v",
 					test.Name, tg.typ, gotPaths, test.WantPaths)
@@ -318,7 +318,7 @@ func TestDijkstraAllPaths(t *testing.T) {
 				got[i] = append(got[i], v.ID())
 			}
 		}
-		ordered.BySliceValues(got)
+		order.BySliceValues(got)
 		if !reflect.DeepEqual(got, test.WantPaths) {
 			t.Errorf("testing %q: unexpected shortest paths:\ngot: %v\nwant:%v",
 				test.Name, got, test.WantPaths)
@@ -337,7 +337,7 @@ func TestDijkstraAllPaths(t *testing.T) {
 				got[i] = append(got[i], v.ID())
 			}
 		}
-		ordered.BySliceValues(got)
+		order.BySliceValues(got)
 		if !reflect.DeepEqual(got, test.WantPaths) {
 			t.Errorf("testing %q: unexpected shortest paths:\ngot: %v\nwant:%v",
 				test.Name, got, test.WantPaths)
diff --git a/graph/path/floydwarshall_test.go b/graph/path/floydwarshall_test.go
index 15afff3a..dadab0f8 100644
--- a/graph/path/floydwarshall_test.go
+++ b/graph/path/floydwarshall_test.go
@@ -10,8 +10,8 @@ import (
 	"testing"
 
 	"gonum.org/v1/gonum/graph"
-	"gonum.org/v1/gonum/graph/internal/ordered"
 	"gonum.org/v1/gonum/graph/path/internal/testgraphs"
+	"gonum.org/v1/gonum/internal/order"
 )
 
 func TestFloydWarshall(t *testing.T) {
@@ -87,7 +87,7 @@ func TestFloydWarshall(t *testing.T) {
 				got[i] = append(got[i], v.ID())
 			}
 		}
-		ordered.BySliceValues(got)
+		order.BySliceValues(got)
 		if !reflect.DeepEqual(got, test.WantPaths) {
 			t.Errorf("testing %q: unexpected shortest paths:\ngot: %v\nwant:%v",
 				test.Name, got, test.WantPaths)
@@ -106,7 +106,7 @@ func TestFloydWarshall(t *testing.T) {
 				got[i] = append(got[i], v.ID())
 			}
 		}
-		ordered.BySliceValues(got)
+		order.BySliceValues(got)
 		if !reflect.DeepEqual(got, test.WantPaths) {
 			t.Errorf("testing %q: unexpected shortest paths:\ngot: %v\nwant:%v",
 				test.Name, got, test.WantPaths)
diff --git a/graph/path/johnson_apsp_test.go b/graph/path/johnson_apsp_test.go
index c796f204..1333bc93 100644
--- a/graph/path/johnson_apsp_test.go
+++ b/graph/path/johnson_apsp_test.go
@@ -10,8 +10,8 @@ import (
 	"testing"
 
 	"gonum.org/v1/gonum/graph"
-	"gonum.org/v1/gonum/graph/internal/ordered"
 	"gonum.org/v1/gonum/graph/path/internal/testgraphs"
+	"gonum.org/v1/gonum/internal/order"
 )
 
 func TestJohnsonAllPaths(t *testing.T) {
@@ -87,7 +87,7 @@ func TestJohnsonAllPaths(t *testing.T) {
 				got[i] = append(got[i], v.ID())
 			}
 		}
-		ordered.BySliceValues(got)
+		order.BySliceValues(got)
 		if !reflect.DeepEqual(got, test.WantPaths) {
 			t.Errorf("testing %q: unexpected shortest paths:\ngot: %v\nwant:%v",
 				test.Name, got, test.WantPaths)
@@ -106,7 +106,7 @@ func TestJohnsonAllPaths(t *testing.T) {
 				got[i] = append(got[i], v.ID())
 			}
 		}
-		ordered.BySliceValues(got)
+		order.BySliceValues(got)
 		if !reflect.DeepEqual(got, test.WantPaths) {
 			t.Errorf("testing %q: unexpected shortest paths:\ngot: %v\nwant:%v",
 				test.Name, got, test.WantPaths)
diff --git a/graph/path/shortest_test.go b/graph/path/shortest_test.go
index 1a494bfb..41e8524a 100644
--- a/graph/path/shortest_test.go
+++ b/graph/path/shortest_test.go
@@ -15,8 +15,8 @@ import (
 
 	"gonum.org/v1/gonum/graph"
 	"gonum.org/v1/gonum/graph/graphs/gen"
-	"gonum.org/v1/gonum/graph/internal/ordered"
 	"gonum.org/v1/gonum/graph/simple"
+	"gonum.org/v1/gonum/internal/order"
 )
 
 var shortestTests = []struct {
@@ -58,7 +58,7 @@ func TestShortestAlts(t *testing.T) {
 							gotPaths[i] = append(gotPaths[i], v.ID())
 						}
 					}
-					ordered.BySliceValues(gotPaths)
+					order.BySliceValues(gotPaths)
 					var wantPaths [][]int64
 					if len(want) != 0 {
 						wantPaths = make([][]int64, len(want))
@@ -68,7 +68,7 @@ func TestShortestAlts(t *testing.T) {
 							wantPaths[i] = append(wantPaths[i], v.ID())
 						}
 					}
-					ordered.BySliceValues(wantPaths)
+					order.BySliceValues(wantPaths)
 					if !reflect.DeepEqual(gotPaths, wantPaths) {
 						t.Errorf("unexpected shortest paths %d --> %d:\ngot: %v\nwant:%v",
 							uid, vid, gotPaths, wantPaths)
@@ -104,7 +104,7 @@ func TestAllShortest(t *testing.T) {
 							gotPaths[i] = append(gotPaths[i], v.ID())
 						}
 					}
-					ordered.BySliceValues(gotPaths)
+					order.BySliceValues(gotPaths)
 					var wantPaths [][]int64
 					if len(want) != 0 {
 						wantPaths = make([][]int64, len(want))
@@ -114,7 +114,7 @@ func TestAllShortest(t *testing.T) {
 							wantPaths[i] = append(wantPaths[i], v.ID())
 						}
 					}
-					ordered.BySliceValues(wantPaths)
+					order.BySliceValues(wantPaths)
 					if !reflect.DeepEqual(gotPaths, wantPaths) {
 						t.Errorf("unexpected shortest paths %d --> %d:\ngot: %v\nwant:%v",
 							uid, vid, gotPaths, wantPaths)
diff --git a/graph/path/yen_ksp_test.go b/graph/path/yen_ksp_test.go
index 70702c66..ebf500be 100644
--- a/graph/path/yen_ksp_test.go
+++ b/graph/path/yen_ksp_test.go
@@ -11,8 +11,8 @@ import (
 	"testing"
 
 	"gonum.org/v1/gonum/graph"
-	"gonum.org/v1/gonum/graph/internal/ordered"
 	"gonum.org/v1/gonum/graph/simple"
+	"gonum.org/v1/gonum/internal/order"
 )
 
 var yenShortestPathTests = []struct {
@@ -389,11 +389,11 @@ func TestYenKSP(t *testing.T) {
 				if w == last {
 					continue
 				}
-				ordered.BySliceValues(gotIDs[first:i])
+				order.BySliceValues(gotIDs[first:i])
 				first = i
 				last = w
 			}
-			ordered.BySliceValues(gotIDs[first:])
+			order.BySliceValues(gotIDs[first:])
 		}
 
 		if !reflect.DeepEqual(test.wantPaths, gotIDs) {
diff --git a/graph/simple/densegraph_test.go b/graph/simple/densegraph_test.go
index 60e38092..7f6275f7 100644
--- a/graph/simple/densegraph_test.go
+++ b/graph/simple/densegraph_test.go
@@ -11,17 +11,17 @@ import (
 	"golang.org/x/exp/rand"
 
 	"gonum.org/v1/gonum/graph"
-	"gonum.org/v1/gonum/graph/internal/ordered"
 	"gonum.org/v1/gonum/graph/internal/set"
 	"gonum.org/v1/gonum/graph/simple"
 	"gonum.org/v1/gonum/graph/testgraph"
+	"gonum.org/v1/gonum/internal/order"
 )
 
 func isZeroContiguousSet(nodes []graph.Node) bool {
 	t := make([]graph.Node, len(nodes))
 	copy(t, nodes)
 	nodes = t
-	ordered.ByID(nodes)
+	order.ByID(nodes)
 	for i, n := range nodes {
 		if int64(i) != n.ID() {
 			return false
@@ -641,7 +641,7 @@ func TestDenseLists(t *testing.T) {
 		t.Fatalf("Wrong number of nodes: got:%v want:%v", len(nodes), 15)
 	}
 
-	ordered.ByID(nodes)
+	order.ByID(nodes)
 
 	for i, node := range graph.NodesOf(dg.Nodes()) {
 		if int64(i) != node.ID() {
diff --git a/graph/spectral/laplacian_test.go b/graph/spectral/laplacian_test.go
index 8a3bc23a..308de9b0 100644
--- a/graph/spectral/laplacian_test.go
+++ b/graph/spectral/laplacian_test.go
@@ -9,9 +9,9 @@ import (
 
 	"gonum.org/v1/gonum/floats/scalar"
 	"gonum.org/v1/gonum/graph"
-	"gonum.org/v1/gonum/graph/internal/ordered"
 	"gonum.org/v1/gonum/graph/iterator"
 	"gonum.org/v1/gonum/graph/simple"
+	"gonum.org/v1/gonum/internal/order"
 	"gonum.org/v1/gonum/mat"
 )
 
@@ -128,7 +128,7 @@ type sortedNodeGraph struct {
 
 func (g sortedNodeGraph) Nodes() graph.Nodes {
 	n := graph.NodesOf(g.Graph.Nodes())
-	ordered.ByID(n)
+	order.ByID(n)
 	return iterator.NewOrderedNodes(n)
 }
 
diff --git a/graph/testgraph/testgraph.go b/graph/testgraph/testgraph.go
index 13e99cd0..6cf82945 100644
--- a/graph/testgraph/testgraph.go
+++ b/graph/testgraph/testgraph.go
@@ -16,8 +16,8 @@ import (
 
 	"gonum.org/v1/gonum/floats/scalar"
 	"gonum.org/v1/gonum/graph"
-	"gonum.org/v1/gonum/graph/internal/ordered"
 	"gonum.org/v1/gonum/graph/internal/set"
+	"gonum.org/v1/gonum/internal/order"
 	"gonum.org/v1/gonum/mat"
 )
 
@@ -126,8 +126,8 @@ func ReturnAllNodes(t *testing.T, b Builder, useEmpty bool) {
 			got = append(got, it.Node())
 		}
 
-		ordered.ByID(got)
-		ordered.ByID(want)
+		order.ByID(got)
+		order.ByID(want)
 
 		if !reflect.DeepEqual(got, want) {
 			t.Errorf("unexpected nodes result for test %q:\ngot: %v\nwant:%v", test.name, got, want)
@@ -165,8 +165,8 @@ func ReturnNodeSlice(t *testing.T, b Builder, useEmpty bool) {
 		}
 		got := s.NodeSlice()
 
-		ordered.ByID(got)
-		ordered.ByID(want)
+		order.ByID(got)
+		order.ByID(want)
 
 		if !reflect.DeepEqual(got, want) {
 			t.Errorf("unexpected nodes result for test %q:\ngot: %v\nwant:%v", test.name, got, want)
@@ -1409,9 +1409,9 @@ func AddNodes(t *testing.T, g NodeAdder, n int) {
 		}
 	}
 
-	ordered.ByID(addedNodes)
+	order.ByID(addedNodes)
 	graphNodes := graph.NodesOf(g.Nodes())
-	ordered.ByID(graphNodes)
+	order.ByID(graphNodes)
 	if !reflect.DeepEqual(addedNodes, graphNodes) {
 		if n > 20 {
 			t.Errorf("unexpected node set after node addition: got len:%v want len:%v", len(graphNodes), len(addedNodes))
@@ -1486,9 +1486,9 @@ func AddArbitraryNodes(t *testing.T, g NodeAdder, add graph.Nodes) {
 
 	add.Reset()
 	addedNodes := graph.NodesOf(add)
-	ordered.ByID(addedNodes)
+	order.ByID(addedNodes)
 	graphNodes := graph.NodesOf(g.Nodes())
-	ordered.ByID(graphNodes)
+	order.ByID(graphNodes)
 	if !reflect.DeepEqual(addedNodes, graphNodes) {
 		t.Errorf("unexpected node set after node addition: got:\n %v\nwant:\n%v", graphNodes, addedNodes)
 	}
diff --git a/graph/topo/bron_kerbosch_test.go b/graph/topo/bron_kerbosch_test.go
index 8728280e..292593a0 100644
--- a/graph/topo/bron_kerbosch_test.go
+++ b/graph/topo/bron_kerbosch_test.go
@@ -10,8 +10,8 @@ import (
 	"testing"
 
 	"gonum.org/v1/gonum/graph"
-	"gonum.org/v1/gonum/graph/internal/ordered"
 	"gonum.org/v1/gonum/graph/simple"
+	"gonum.org/v1/gonum/internal/order"
 )
 
 var vOrderTests = []struct {
@@ -196,7 +196,7 @@ func TestBronKerbosch(t *testing.T) {
 			slices.Sort(ids)
 			got[j] = ids
 		}
-		ordered.BySliceValues(got)
+		order.BySliceValues(got)
 		if !reflect.DeepEqual(got, test.want) {
 			t.Errorf("unexpected cliques for test %q:\ngot: %v\nwant:%v", test.name, got, test.want)
 		}
diff --git a/graph/topo/johnson_cycles_test.go b/graph/topo/johnson_cycles_test.go
index a71b0263..abe81f37 100644
--- a/graph/topo/johnson_cycles_test.go
+++ b/graph/topo/johnson_cycles_test.go
@@ -8,8 +8,8 @@ import (
 	"reflect"
 	"testing"
 
-	"gonum.org/v1/gonum/graph/internal/ordered"
 	"gonum.org/v1/gonum/graph/simple"
+	"gonum.org/v1/gonum/internal/order"
 )
 
 var cyclesInTests = []struct {
@@ -108,7 +108,7 @@ func TestDirectedCyclesIn(t *testing.T) {
 			}
 			got[j] = ids
 		}
-		ordered.BySliceValues(got)
+		order.BySliceValues(got)
 		if !reflect.DeepEqual(got, test.want) {
 			t.Errorf("unexpected johnson result for %d:\n\tgot:%#v\n\twant:%#v", i, got, test.want)
 		}
diff --git a/graph/topo/paton_cycles_test.go b/graph/topo/paton_cycles_test.go
index 677714c2..5973a55f 100644
--- a/graph/topo/paton_cycles_test.go
+++ b/graph/topo/paton_cycles_test.go
@@ -10,8 +10,8 @@ import (
 	"testing"
 
 	"gonum.org/v1/gonum/graph"
-	"gonum.org/v1/gonum/graph/internal/ordered"
 	"gonum.org/v1/gonum/graph/simple"
+	"gonum.org/v1/gonum/internal/order"
 )
 
 var undirectedCyclesInTests = []struct {
@@ -98,7 +98,7 @@ func TestUndirectedCyclesIn(t *testing.T) {
 			ids[len(ids)-1] = ids[0]
 			got[j] = ids
 		}
-		ordered.BySliceValues(got)
+		order.BySliceValues(got)
 		var matched bool
 		for _, want := range test.want {
 			if reflect.DeepEqual(got, want) {
diff --git a/graph/topo/tarjan_test.go b/graph/topo/tarjan_test.go
index 812b3dec..479c4323 100644
--- a/graph/topo/tarjan_test.go
+++ b/graph/topo/tarjan_test.go
@@ -10,8 +10,8 @@ import (
 	"testing"
 
 	"gonum.org/v1/gonum/graph"
-	"gonum.org/v1/gonum/graph/internal/ordered"
 	"gonum.org/v1/gonum/graph/simple"
+	"gonum.org/v1/gonum/internal/order"
 )
 
 type interval struct{ start, end int }
@@ -182,8 +182,8 @@ func TestTarjanSCC(t *testing.T) {
 			slices.Sort(gotIDs[i])
 		}
 		for _, iv := range test.ambiguousOrder {
-			ordered.BySliceValues(test.want[iv.start:iv.end])
-			ordered.BySliceValues(gotIDs[iv.start:iv.end])
+			order.BySliceValues(test.want[iv.start:iv.end])
+			order.BySliceValues(gotIDs[iv.start:iv.end])
 		}
 		if !reflect.DeepEqual(gotIDs, test.want) {
 			t.Errorf("unexpected Tarjan scc result for %d:\n\tgot:%v\n\twant:%v", i, gotIDs, test.want)
diff --git a/graph/topo/topo_test.go b/graph/topo/topo_test.go
index d60c9cd8..d8222388 100644
--- a/graph/topo/topo_test.go
+++ b/graph/topo/topo_test.go
@@ -10,8 +10,8 @@ import (
 	"testing"
 
 	"gonum.org/v1/gonum/graph"
-	"gonum.org/v1/gonum/graph/internal/ordered"
 	"gonum.org/v1/gonum/graph/simple"
+	"gonum.org/v1/gonum/internal/order"
 )
 
 func TestIsPath(t *testing.T) {
@@ -167,7 +167,7 @@ func TestConnectedComponents(t *testing.T) {
 			slices.Sort(ids)
 			got[j] = ids
 		}
-		ordered.BySliceValues(got)
+		order.BySliceValues(got)
 		if !reflect.DeepEqual(got, test.want) {
 			t.Errorf("unexpected connected components for test %d %T:\ngot: %v\nwant:%v", i, g, got, test.want)
 		}
diff --git a/graph/traverse/traverse_test.go b/graph/traverse/traverse_test.go
index 8c8e14b0..d3c21273 100644
--- a/graph/traverse/traverse_test.go
+++ b/graph/traverse/traverse_test.go
@@ -13,8 +13,8 @@ import (
 
 	"gonum.org/v1/gonum/graph"
 	"gonum.org/v1/gonum/graph/graphs/gen"
-	"gonum.org/v1/gonum/graph/internal/ordered"
 	"gonum.org/v1/gonum/graph/simple"
+	"gonum.org/v1/gonum/internal/order"
 )
 
 var (
@@ -375,7 +375,7 @@ func TestWalkAll(t *testing.T) {
 				slices.Sort(ids)
 				got[j] = ids
 			}
-			ordered.BySliceValues(got)
+			order.BySliceValues(got)
 			if !reflect.DeepEqual(got, test.want) {
 				t.Errorf("unexpected connected components for test %d using %T:\ngot: %v\nwant:%v", i, w, got, test.want)
 			}
diff --git a/spatial/vptree/vptree_test.go b/spatial/vptree/vptree_test.go
index 3544d6b9..f20e7d2a 100644
--- a/spatial/vptree/vptree_test.go
+++ b/spatial/vptree/vptree_test.go
@@ -17,6 +17,8 @@ import (
 	"unsafe"
 
 	"golang.org/x/exp/rand"
+
+	"gonum.org/v1/gonum/internal/order"
 )
 
 var (
@@ -344,8 +346,8 @@ func TestDo(t *testing.T) {
 	for i, p := range wpData {
 		want[i] = p.(Point)
 	}
-	sort.Sort(lexical(got))
-	sort.Sort(lexical(want))
+	order.BySliceValues(got)
+	order.BySliceValues(want)
 
 	if !reflect.DeepEqual(got, want) {
 		t.Errorf("unexpected result from tree iteration: got:%v want:%v", got, want)
@@ -355,27 +357,6 @@ func TestDo(t *testing.T) {
 	}
 }
 
-type lexical []Point
-
-func (c lexical) Len() int { return len(c) }
-func (c lexical) Less(i, j int) bool {
-	a, b := c[i], c[j]
-	l := len(a)
-	if len(b) < l {
-		l = len(b)
-	}
-	for k, v := range a[:l] {
-		if v < b[k] {
-			return true
-		}
-		if v > b[k] {
-			return false
-		}
-	}
-	return len(a) < len(b)
-}
-func (c lexical) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
-
 func BenchmarkNew(b *testing.B) {
 	for _, effort := range []int{0, 10, 100} {
 		b.Run(fmt.Sprintf("New:%d", effort), func(b *testing.B) {
diff --git a/stat/moments_bench_test.go b/stat/moments_bench_test.go
index 28bcf20b..8ccea8e0 100644
--- a/stat/moments_bench_test.go
+++ b/stat/moments_bench_test.go
@@ -18,10 +18,11 @@ import (
 )
 
 const (
-	small  = 10
-	medium = 1000
-	large  = 100000
-	huge   = 10000000
+	empty  = 0
+	small  = 1e1
+	medium = 1e3
+	large  = 1e5
+	huge   = 1e7
 )
 
 // tests for unweighted versions
diff --git a/stat/roc_test.go b/stat/roc_test.go
index e0adea3a..b48e8ce9 100644
--- a/stat/roc_test.go
+++ b/stat/roc_test.go
@@ -5,9 +5,13 @@
 package stat
 
 import (
+	"fmt"
 	"math"
+	"slices"
 	"testing"
 
+	"golang.org/x/exp/rand"
+
 	"gonum.org/v1/gonum/floats"
 )
 
@@ -324,3 +328,61 @@ func TestTOC(t *testing.T) {
 		}
 	}
 }
+
+func BenchmarkROC(b *testing.B) {
+	sizes := []int{empty, small, medium, large}
+	for _, cutoffsSize := range sizes {
+		for _, ySize := range sizes {
+			classesSize := ySize
+			for _, weightsSize := range slices.Compact([]int{empty, ySize}) {
+				benchmarkROC(b, cutoffsSize, ySize, classesSize, weightsSize)
+			}
+		}
+	}
+}
+
+func benchmarkROC(b *testing.B, cutoffsSize int, ySize int, classesSize int, weightsSize int) bool {
+	return b.Run(
+		fmt.Sprintf(
+			"cutoffs=%d,y=%d,classes=%d,weights=%d",
+			cutoffsSize, ySize, classesSize, weightsSize),
+		func(b *testing.B) {
+			src := rand.NewSource(1)
+
+			cutoffs := randomFloats(cutoffsSize, src)
+			slices.Sort(cutoffs)
+
+			y := randomFloats(ySize, src)
+			slices.Sort(y)
+
+			classes := randomBools(classesSize, src)
+
+			var weights []float64
+			if weightsSize != empty {
+				weights = randomFloats(weightsSize, src)
+			}
+
+			b.ResetTimer()
+			for i := 0; i < b.N; i++ {
+				ROC(cutoffs, y, classes, weights)
+			}
+		})
+}
+
+func randomFloats(l int, src rand.Source) []float64 {
+	rnd := rand.New(src)
+	s := make([]float64, l)
+	for i := range s {
+		s[i] = rnd.Float64()
+	}
+	return s
+}
+
+func randomBools(l int, src rand.Source) []bool {
+	rnd := rand.New(src)
+	s := make([]bool, l)
+	for i := range s {
+		s[i] = rnd.Int31n(2) == 1
+	}
+	return s
+}