community: drop dependency on intsets
MODIFY
graph/community/louvain_common.go
graph/community/louvain_common.go
@@ -8,8 +8,10 @@ package community // import "gonum.org/v1/gonum/graph/community"
import (
"fmt"
"math/rand"
+ "sort"
"gonum.org/v1/gonum/graph"
+ "gonum.org/v1/gonum/graph/internal/set"
)
// Q returns the modularity Q score of the graph g subdivided into the
@@ -306,6 +308,36 @@ func (d *dense) TakeMin(p *int) bool {
return true
}
+// slice is a sparse integer set iterator.
+type slice struct {
+ pos int
+ elems []int
+}
+
+// newSlice returns a new slice of elements from s, sorted ascending.
+func newSlice(s set.Ints) *slice {
+ elems := make([]int, 0, len(s))
+ for i := range s {
+ elems = append(elems, i)
+ }
+ sort.Ints(elems)
+ return &slice{elems: elems}
+}
+
+// TakeMin mimics intsets.Sparse TakeMin for a sorted set. If the set
+// iterator position is less than the iterator size, TakeMin sets *p
+// to the the iterator position's element and increments the position
+// and returns true.
+// Otherwise, it returns false and *p is undefined.
+func (s *slice) TakeMin(p *int) bool {
+ if s.pos >= len(s.elems) {
+ return false
+ }
+ *p = s.elems[s.pos]
+ s.pos++
+ return true
+}
+
const (
negativeWeight = "community: unexpected negative edge weight"
positiveWeight = "community: unexpected positive edge weight"

MODIFY
graph/community/louvain_directed_multiplex.go
graph/community/louvain_directed_multiplex.go
@@ -10,10 +10,9 @@ import (
"math/rand"
"sort"
- "golang.org/x/tools/container/intsets"
-
"gonum.org/v1/gonum/graph"
"gonum.org/v1/gonum/graph/internal/ordered"
+ "gonum.org/v1/gonum/graph/internal/set"
)
// DirectedMultiplex is a directed multiplex graph.
@@ -125,16 +124,16 @@ func NewDirectedLayers(layers ...graph.Directed) (DirectedLayers, error) {
if len(layers) == 0 {
return nil, nil
}
- var base, next intsets.Sparse
+ base := make(set.Ints)
for _, n := range layers[0].Nodes() {
- base.Insert(n.ID())
+ base.Add(n.ID())
}
for i, l := range layers[1:] {
- next.Clear()
+ next := make(set.Ints)
for _, n := range l.Nodes() {
- next.Insert(n.ID())
+ next.Add(n.ID())
}
- if !next.Equals(&base) {
+ if !set.IntsEqual(base, next) {
return nil, fmt.Errorf("community: layer ID mismatch between layers: %d", i+1)
}
}
@@ -760,15 +759,15 @@ func (l *directedMultiplexLocalMover) deltaQ(n graph.Node) (deltaQ float64, dst
iterator = &dense{n: len(l.communities)}
} else {
// Find communities connected to n.
- var connected intsets.Sparse
+ connected := make(set.Ints)
// The following for loop is equivalent to:
//
// for i := 0; i < l.g.Depth(); i++ {
// for _, v := range l.g.Layer(i).From(n) {
- // connected.Insert(l.memberships[v.ID()])
+ // connected.Add(l.memberships[v.ID()])
// }
// for _, v := range l.g.Layer(i).To(n) {
- // connected.Insert(l.memberships[v.ID()])
+ // connected.Add(l.memberships[v.ID()])
// }
// }
//
@@ -776,15 +775,15 @@ func (l *directedMultiplexLocalMover) deltaQ(n graph.Node) (deltaQ float64, dst
// each layer.
for _, layer := range l.g.layers {
for _, vid := range layer.edgesFrom[id] {
- connected.Insert(l.memberships[vid])
+ connected.Add(l.memberships[vid])
}
for _, vid := range layer.edgesTo[id] {
- connected.Insert(l.memberships[vid])
+ connected.Add(l.memberships[vid])
}
}
// Insert the node's own community.
- connected.Insert(l.memberships[id])
- iterator = &connected
+ connected.Add(l.memberships[id])
+ iterator = newSlice(connected)
}
// Calculate the highest modularity gain

MODIFY
graph/community/louvain_directed_multiplex_test.go
graph/community/louvain_directed_multiplex_test.go
@@ -30,7 +30,7 @@ var communityDirectedMultiplexQTests = []struct {
structures: []structure{
{
resolution: 1,
- memberships: []set{
+ memberships: []intset{
0: linksTo(0),
1: linksTo(1),
2: linksTo(2),
@@ -63,7 +63,7 @@ var communityDirectedMultiplexQTests = []struct {
structures: []structure{
{
resolution: 1,
- memberships: []set{
+ memberships: []intset{
0: linksTo(0, 1),
1: linksTo(2, 3, 4),
},
@@ -102,7 +102,7 @@ var communityDirectedMultiplexQTests = []struct {
structures: []structure{
{
resolution: 1,
- memberships: []set{
+ memberships: []intset{
0: linksTo(0, 1),
1: linksTo(2, 3, 4),
},
@@ -139,7 +139,7 @@ var communityDirectedMultiplexQTests = []struct {
structures: []structure{
{
resolution: 1,
- memberships: []set{
+ memberships: []intset{
0: linksTo(0, 1, 2),
1: linksTo(3, 4, 5),
},
@@ -147,7 +147,7 @@ var communityDirectedMultiplexQTests = []struct {
},
{
resolution: 1,
- memberships: []set{
+ memberships: []intset{
0: linksTo(0, 1, 2, 3, 4, 5),
},
want: 0, tol: 1e-14,
@@ -180,7 +180,7 @@ var communityDirectedMultiplexQTests = []struct {
structures: []structure{
{
resolution: 1,
- memberships: []set{
+ memberships: []intset{
0: linksTo(0, 1, 2),
1: linksTo(3, 4, 5),
},
@@ -188,7 +188,7 @@ var communityDirectedMultiplexQTests = []struct {
},
{
resolution: 1,
- memberships: []set{
+ memberships: []intset{
0: linksTo(0),
1: linksTo(1),
2: linksTo(2),
@@ -229,7 +229,7 @@ var communityDirectedMultiplexQTests = []struct {
structures: []structure{
{
resolution: 1,
- memberships: []set{
+ memberships: []intset{
0: linksTo(0, 6),
1: linksTo(1, 7, 9, 12),
2: linksTo(2, 8, 11),
@@ -239,7 +239,7 @@ var communityDirectedMultiplexQTests = []struct {
},
{
resolution: 1,
- memberships: []set{
+ memberships: []intset{
0: linksTo(0, 2, 3, 4, 5, 10),
1: linksTo(1, 7, 9, 12),
2: linksTo(6),
@@ -249,7 +249,7 @@ var communityDirectedMultiplexQTests = []struct {
},
{
resolution: 1,
- memberships: []set{
+ memberships: []intset{
0: linksTo(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12),
},
want: 0, tol: 1e-14,
@@ -483,7 +483,7 @@ var localDirectedMultiplexMoveTests = []struct {
layers: []layer{{g: blondel, weight: 1}, {g: blondel, weight: 0.5}},
structures: []moveStructures{
{
- memberships: []set{
+ memberships: []intset{
0: linksTo(0, 1, 2, 4, 5),
1: linksTo(3, 6, 7),
2: linksTo(8, 9, 10, 12, 14, 15),
@@ -494,7 +494,7 @@ var localDirectedMultiplexMoveTests = []struct {
tol: 1e-14,
},
{
- memberships: []set{
+ memberships: []intset{
0: linksTo(0, 1, 2, 4, 5),
1: linksTo(3, 6, 7),
2: linksTo(8, 9, 10, 12, 14, 15),
@@ -505,7 +505,7 @@ var localDirectedMultiplexMoveTests = []struct {
tol: 1e-14,
},
{
- memberships: []set{
+ memberships: []intset{
0: linksTo(0, 1, 2, 4, 5),
1: linksTo(3, 6, 7),
2: linksTo(8, 9, 10, 12, 14, 15),

MODIFY
graph/community/louvain_directed_test.go
graph/community/louvain_directed_test.go
@@ -19,7 +19,7 @@ import (
var communityDirectedQTests = []struct {
name string
- g []set
+ g []intset
structures []structure
wantLevels []level
@@ -32,7 +32,7 @@ var communityDirectedQTests = []struct {
structures: []structure{
{
resolution: 1,
- memberships: []set{
+ memberships: []intset{
0: linksTo(0, 1),
1: linksTo(2, 3, 4),
},
@@ -68,7 +68,7 @@ var communityDirectedQTests = []struct {
structures: []structure{
{
resolution: 1,
- memberships: []set{
+ memberships: []intset{
0: linksTo(0, 1, 2, 3, 7, 11, 12, 13, 17, 19, 21),
1: linksTo(4, 5, 6, 10, 16),
2: linksTo(8, 9, 14, 15, 18, 20, 22, 26, 29, 30, 32, 33),
@@ -148,7 +148,7 @@ var communityDirectedQTests = []struct {
structures: []structure{
{
resolution: 1,
- memberships: []set{
+ memberships: []intset{
0: linksTo(0, 1, 2, 3, 4, 5, 6, 7),
1: linksTo(8, 9, 10, 11, 12, 13, 14, 15),
},
@@ -382,7 +382,7 @@ tests:
var localDirectedMoveTests = []struct {
name string
- g []set
+ g []intset
structures []moveStructures
}{
{
@@ -390,7 +390,7 @@ var localDirectedMoveTests = []struct {
g: blondel,
structures: []moveStructures{
{
- memberships: []set{
+ memberships: []intset{
0: linksTo(0, 1, 2, 4, 5),
1: linksTo(3, 6, 7),
2: linksTo(8, 9, 10, 12, 14, 15),
@@ -401,7 +401,7 @@ var localDirectedMoveTests = []struct {
tol: 1e-14,
},
{
- memberships: []set{
+ memberships: []intset{
0: linksTo(0, 1, 2, 4, 5),
1: linksTo(3, 6, 7),
2: linksTo(8, 9, 10, 12, 14, 15),
@@ -412,7 +412,7 @@ var localDirectedMoveTests = []struct {
tol: 1e-14,
},
{
- memberships: []set{
+ memberships: []intset{
0: linksTo(0, 1, 2, 4, 5),
1: linksTo(3, 6, 7),
2: linksTo(8, 9, 10, 12, 14, 15),

MODIFY
graph/community/louvain_test.go
graph/community/louvain_test.go
@@ -13,14 +13,14 @@ import (
"gonum.org/v1/gonum/graph/simple"
)
-// set is an integer set.
-type set map[int]struct{}
+// intset is an integer set.
+type intset map[int]struct{}
-func linksTo(i ...int) set {
+func linksTo(i ...int) intset {
if len(i) == 0 {
return nil
}
- s := make(set)
+ s := make(intset)
for _, v := range i {
s[v] = struct{}{}
}
@@ -28,15 +28,15 @@ func linksTo(i ...int) set {
}
type layer struct {
- g []set
+ g []intset
edgeWeight float64 // Zero edge weight is interpreted as 1.0.
weight float64
}
var (
- unconnected = []set{ /* Nodes 0-4 are implicit .*/ 5: nil}
+ unconnected = []intset{ /* Nodes 0-4 are implicit .*/ 5: nil}
- smallDumbell = []set{
+ smallDumbell = []intset{
0: linksTo(1, 2),
1: linksTo(2),
2: linksTo(3),
@@ -44,7 +44,7 @@ var (
4: linksTo(5),
5: nil,
}
- dumbellRepulsion = []set{
+ dumbellRepulsion = []intset{
0: linksTo(4),
1: linksTo(5),
2: nil,
@@ -53,7 +53,7 @@ var (
5: nil,
}
- repulsion = []set{
+ repulsion = []intset{
0: linksTo(3, 4, 5),
1: linksTo(3, 4, 5),
2: linksTo(3, 4, 5),
@@ -62,7 +62,7 @@ var (
5: linksTo(0, 1, 2),
}
- simpleDirected = []set{
+ simpleDirected = []intset{
0: linksTo(1),
1: linksTo(0, 4),
2: linksTo(1),
@@ -71,9 +71,9 @@ var (
}
// http://www.slate.com/blogs/the_world_/2014/07/17/the_middle_east_friendship_chart.html
- middleEast = struct{ friends, complicated, enemies []set }{
+ middleEast = struct{ friends, complicated, enemies []intset }{
// green cells
- friends: []set{
+ friends: []intset{
0: nil,
1: linksTo(5, 7, 9, 12),
2: linksTo(11),
@@ -90,7 +90,7 @@ var (
},
// yellow cells
- complicated: []set{
+ complicated: []intset{
0: linksTo(2, 4),
1: linksTo(4, 8),
2: linksTo(0, 3, 4, 5, 8, 9),
@@ -107,7 +107,7 @@ var (
},
// red cells
- enemies: []set{
+ enemies: []intset{
0: linksTo(1, 3, 5, 6, 7, 8, 9, 10, 11, 12),
1: linksTo(0, 2, 3, 6, 10, 11),
2: linksTo(1, 6, 7, 10, 12),
@@ -131,7 +131,7 @@ var (
// head from a node with lower Page Rank to a node with higher Page
// Rank. This has no impact on undirected tests, but allows a sensible
// view for directed tests.
- zachary = []set{
+ zachary = []intset{
0: nil, // rank=0.097
1: linksTo(0, 2), // rank=0.05288
2: linksTo(0, 32), // rank=0.05708
@@ -174,7 +174,7 @@ var (
// head from a node with lower Page Rank to a node with higher Page
// Rank. This has no impact on undirected tests, but allows a sensible
// view for directed tests.
- blondel = []set{
+ blondel = []intset{
0: linksTo(2), // rank=0.06858
1: linksTo(2, 4, 7), // rank=0.05264
2: nil, // rank=0.08249
@@ -196,7 +196,7 @@ var (
type structure struct {
resolution float64
- memberships []set
+ memberships []intset
want, tol float64
}
@@ -206,7 +206,7 @@ type level struct {
}
type moveStructures struct {
- memberships []set
+ memberships []intset
targetNodes []graph.Node
resolution float64
@@ -255,11 +255,11 @@ func init() {
// This init function checks the Middle East relationship data.
func init() {
- world := make([]set, len(middleEast.friends))
+ world := make([]intset, len(middleEast.friends))
for i := range world {
- world[i] = make(set)
+ world[i] = make(intset)
}
- for _, relationships := range [][]set{middleEast.friends, middleEast.complicated, middleEast.enemies} {
+ for _, relationships := range [][]intset{middleEast.friends, middleEast.complicated, middleEast.enemies} {
for i, rel := range relationships {
for inter := range rel {
if _, ok := world[i][inter]; ok {

MODIFY
graph/community/louvain_undirected_multiplex.go
graph/community/louvain_undirected_multiplex.go
@@ -10,10 +10,9 @@ import (
"math/rand"
"sort"
- "golang.org/x/tools/container/intsets"
-
"gonum.org/v1/gonum/graph"
"gonum.org/v1/gonum/graph/internal/ordered"
+ "gonum.org/v1/gonum/graph/internal/set"
)
// UndirectedMultiplex is an undirected multiplex graph.
@@ -124,16 +123,16 @@ func NewUndirectedLayers(layers ...graph.Undirected) (UndirectedLayers, error) {
if len(layers) == 0 {
return nil, nil
}
- var base, next intsets.Sparse
+ base := make(set.Ints)
for _, n := range layers[0].Nodes() {
- base.Insert(n.ID())
+ base.Add(n.ID())
}
for i, l := range layers[1:] {
- next.Clear()
+ next := make(set.Ints)
for _, n := range l.Nodes() {
- next.Insert(n.ID())
+ next.Add(n.ID())
}
- if !next.Equals(&base) {
+ if !set.IntsEqual(next, base) {
return nil, fmt.Errorf("community: layer ID mismatch between layers: %d", i+1)
}
}
@@ -702,12 +701,12 @@ func (l *undirectedMultiplexLocalMover) deltaQ(n graph.Node) (deltaQ float64, ds
iterator = &dense{n: len(l.communities)}
} else {
// Find communities connected to n.
- var connected intsets.Sparse
+ connected := make(set.Ints)
// The following for loop is equivalent to:
//
// for i := 0; i < l.g.Depth(); i++ {
// for _, v := range l.g.Layer(i).From(n) {
- // connected.Insert(l.memberships[v.ID()])
+ // connected.Add(l.memberships[v.ID()])
// }
// }
//
@@ -715,12 +714,12 @@ func (l *undirectedMultiplexLocalMover) deltaQ(n graph.Node) (deltaQ float64, ds
// each layer.
for _, layer := range l.g.layers {
for _, vid := range layer.edges[id] {
- connected.Insert(l.memberships[vid])
+ connected.Add(l.memberships[vid])
}
}
// Insert the node's own community.
- connected.Insert(l.memberships[id])
- iterator = &connected
+ connected.Add(l.memberships[id])
+ iterator = newSlice(connected)
}
// Calculate the highest modularity gain

MODIFY
graph/community/louvain_undirected_multiplex_test.go
graph/community/louvain_undirected_multiplex_test.go
@@ -30,7 +30,7 @@ var communityUndirectedMultiplexQTests = []struct {
structures: []structure{
{
resolution: 1,
- memberships: []set{
+ memberships: []intset{
0: linksTo(0),
1: linksTo(1),
2: linksTo(2),
@@ -64,7 +64,7 @@ var communityUndirectedMultiplexQTests = []struct {
structures: []structure{
{
resolution: 1,
- memberships: []set{
+ memberships: []intset{
0: linksTo(0, 1, 2),
1: linksTo(3, 4, 5),
},
@@ -72,7 +72,7 @@ var communityUndirectedMultiplexQTests = []struct {
},
{
resolution: 1,
- memberships: []set{
+ memberships: []intset{
0: linksTo(0, 1, 2, 3, 4, 5),
},
want: 0, tol: 1e-14,
@@ -108,7 +108,7 @@ var communityUndirectedMultiplexQTests = []struct {
structures: []structure{
{
resolution: 1,
- memberships: []set{
+ memberships: []intset{
0: linksTo(0, 1, 2),
1: linksTo(3, 4, 5),
},
@@ -116,7 +116,7 @@ var communityUndirectedMultiplexQTests = []struct {
},
{
resolution: 1,
- memberships: []set{
+ memberships: []intset{
0: linksTo(0, 1, 2, 3, 4, 5),
},
want: 0, tol: 1e-14,
@@ -149,7 +149,7 @@ var communityUndirectedMultiplexQTests = []struct {
structures: []structure{
{
resolution: 1,
- memberships: []set{
+ memberships: []intset{
0: linksTo(0, 1, 2),
1: linksTo(3, 4, 5),
},
@@ -157,7 +157,7 @@ var communityUndirectedMultiplexQTests = []struct {
},
{
resolution: 1,
- memberships: []set{
+ memberships: []intset{
0: linksTo(0),
1: linksTo(1),
2: linksTo(2),
@@ -198,7 +198,7 @@ var communityUndirectedMultiplexQTests = []struct {
structures: []structure{
{
resolution: 1,
- memberships: []set{
+ memberships: []intset{
0: linksTo(0, 6),
1: linksTo(1, 7, 9, 12),
2: linksTo(2, 8, 11),
@@ -208,7 +208,7 @@ var communityUndirectedMultiplexQTests = []struct {
},
{
resolution: 1,
- memberships: []set{
+ memberships: []intset{
0: linksTo(0, 2, 3, 4, 5, 10),
1: linksTo(1, 7, 9, 12),
2: linksTo(6),
@@ -218,7 +218,7 @@ var communityUndirectedMultiplexQTests = []struct {
},
{
resolution: 1,
- memberships: []set{
+ memberships: []intset{
0: linksTo(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12),
},
want: 0, tol: 1e-14,
@@ -452,7 +452,7 @@ var localUndirectedMultiplexMoveTests = []struct {
layers: []layer{{g: blondel, weight: 1}, {g: blondel, weight: 0.5}},
structures: []moveStructures{
{
- memberships: []set{
+ memberships: []intset{
0: linksTo(0, 1, 2, 4, 5),
1: linksTo(3, 6, 7),
2: linksTo(8, 9, 10, 12, 14, 15),
@@ -463,7 +463,7 @@ var localUndirectedMultiplexMoveTests = []struct {
tol: 1e-14,
},
{
- memberships: []set{
+ memberships: []intset{
0: linksTo(0, 1, 2, 4, 5),
1: linksTo(3, 6, 7),
2: linksTo(8, 9, 10, 12, 14, 15),
@@ -474,7 +474,7 @@ var localUndirectedMultiplexMoveTests = []struct {
tol: 1e-14,
},
{
- memberships: []set{
+ memberships: []intset{
0: linksTo(0, 1, 2, 4, 5),
1: linksTo(3, 6, 7),
2: linksTo(8, 9, 10, 12, 14, 15),

MODIFY
graph/community/louvain_undirected_test.go
graph/community/louvain_undirected_test.go
@@ -19,7 +19,7 @@ import (
var communityUndirectedQTests = []struct {
name string
- g []set
+ g []intset
structures []structure
wantLevels []level
@@ -31,7 +31,7 @@ var communityUndirectedQTests = []struct {
structures: []structure{
{
resolution: 1,
- memberships: []set{
+ memberships: []intset{
0: linksTo(0),
1: linksTo(1),
2: linksTo(2),
@@ -63,7 +63,7 @@ var communityUndirectedQTests = []struct {
{
resolution: 1,
// community structure and modularity calculated by java reference implementation.
- memberships: []set{
+ memberships: []intset{
0: linksTo(0, 1, 2),
1: linksTo(3, 4, 5),
},
@@ -71,7 +71,7 @@ var communityUndirectedQTests = []struct {
},
{
resolution: 1,
- memberships: []set{
+ memberships: []intset{
0: linksTo(0, 1, 2, 3, 4, 5),
},
// theoretical expectation.
@@ -106,7 +106,7 @@ var communityUndirectedQTests = []struct {
{
resolution: 1,
// community structure and modularity from doi: 10.1140/epjb/e2013-40829-0
- memberships: []set{
+ memberships: []intset{
0: linksTo(0, 1, 2, 3, 7, 11, 12, 13, 17, 19, 21),
1: linksTo(4, 5, 6, 10, 16),
2: linksTo(8, 9, 14, 15, 18, 20, 22, 26, 29, 30, 32, 33),
@@ -118,7 +118,7 @@ var communityUndirectedQTests = []struct {
{
resolution: 0.5,
// community structure and modularity calculated by java reference implementation.
- memberships: []set{
+ memberships: []intset{
0: linksTo(0, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 16, 17, 19, 21),
1: linksTo(8, 14, 15, 18, 20, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33),
},
@@ -127,7 +127,7 @@ var communityUndirectedQTests = []struct {
{
resolution: 2,
// community structure and modularity calculated by java reference implementation.
- memberships: []set{
+ memberships: []intset{
0: linksTo(14, 18, 20, 22, 32, 33, 15),
1: linksTo(0, 1, 11, 17, 19, 21),
2: linksTo(2, 3, 7, 9, 12, 13),
@@ -207,7 +207,7 @@ var communityUndirectedQTests = []struct {
{
resolution: 1,
// community structure and modularity calculated by java reference implementation.
- memberships: []set{
+ memberships: []intset{
0: linksTo(0, 1, 2, 3, 4, 5, 6, 7),
1: linksTo(8, 9, 10, 11, 12, 13, 14, 15),
},
@@ -441,7 +441,7 @@ tests:
var localUndirectedMoveTests = []struct {
name string
- g []set
+ g []intset
structures []moveStructures
}{
{
@@ -449,7 +449,7 @@ var localUndirectedMoveTests = []struct {
g: blondel,
structures: []moveStructures{
{
- memberships: []set{
+ memberships: []intset{
0: linksTo(0, 1, 2, 4, 5),
1: linksTo(3, 6, 7),
2: linksTo(8, 9, 10, 12, 14, 15),
@@ -460,7 +460,7 @@ var localUndirectedMoveTests = []struct {
tol: 1e-14,
},
{
- memberships: []set{
+ memberships: []intset{
0: linksTo(0, 1, 2, 4, 5),
1: linksTo(3, 6, 7),
2: linksTo(8, 9, 10, 12, 14, 15),
@@ -471,7 +471,7 @@ var localUndirectedMoveTests = []struct {
tol: 1e-14,
},
{
- memberships: []set{
+ memberships: []intset{
0: linksTo(0, 1, 2, 4, 5),
1: linksTo(3, 6, 7),
2: linksTo(8, 9, 10, 12, 14, 15),

community: drop dependency on intsets