Performance and scale.

Defer edge creation.

Don't create edges until the count is known to avoid repeated allocate+
copy operatios.

Limit resolutions.

Allow only a single resolution condition set per target, and overwrite
intermediate results. Reduces memory and obviates allocations.

Propagate fewer conditions.

Instead of propagating notice conditions to parents in graph during
initial resolve, leave them on leaf node, and attach to ancestors in
the final walk. Reduces copies.

Parallelize resolutions.

Use goroutines, mutexes, and waitgroups to resolve branches of the
graph in parallel. Makes better use of available cores.

Don't accumulate resolutions inside non-containers.

During the final resolution walk, only attach actions to ancestors from
the root down until the 1st non-aggregate. Prevents an explosion of
copies in the lower levels of the graph.

Drop origin for scale.

Tracking the origin of every potential origin for every restricted
condition does not scale. By dropping origin, propagating from top
to bottom can prune many redundant paths avoiding an exponential
explosion.

Conditions as bitmask.

Use bit masks for license conditions and condition sets. Reduces maps
and allocations.

Bug: 68860345
Bug: 151177513
Bug: 151953481

Test: m all
Test: m systemlicense
Test: m listshare; out/soong/host/linux-x86/bin/listshare ...
Test: m checkshare; out/soong/host/linux-x86/bin/checkshare ...
Test: m dumpgraph; out/soong/host/linux-x86/dumpgraph ...
Test: m dumpresolutions; out/soong/host/linux-x86/dumpresolutions ...

where ... is the path to the .meta_lic file for the system image. In my
case if

$ export PRODUCT=$(realpath $ANDROID_PRODUCT_OUT --relative-to=$PWD)

... can be expressed as:

${PRODUCT}/gen/META/lic_intermediates/${PRODUCT}/system.img.meta_lic

Change-Id: Ia2ec1b818de6122c239fbd0824754f1d65daffd3
diff --git a/tools/compliance/conditionset_test.go b/tools/compliance/conditionset_test.go
index eac0680..c7306e7 100644
--- a/tools/compliance/conditionset_test.go
+++ b/tools/compliance/conditionset_test.go
@@ -15,576 +15,643 @@
 package compliance
 
 import (
-	"sort"
 	"strings"
 	"testing"
 )
 
-type byName map[string][]string
-
-func (bn byName) checkPublic(ls *LicenseConditionSet, t *testing.T) {
-	for names, expected := range bn {
-		name := ConditionNames(strings.Split(names, ":"))
-		if ls.HasAnyByName(name) {
-			if len(expected) == 0 {
-				t.Errorf("unexpected LicenseConditionSet.HasAnyByName(%q): got true, want false", name)
-			}
-		} else {
-			if len(expected) != 0 {
-				t.Errorf("unexpected LicenseConditionSet.HasAnyByName(%q): got false, want true", name)
-			}
-		}
-		if len(expected) != ls.CountByName(name) {
-			t.Errorf("unexpected LicenseConditionSet.CountByName(%q): got %d, want %d", name, ls.CountByName(name), len(expected))
-		}
-		byName := ls.ByName(name).AsList()
-		if len(expected) != len(byName) {
-			t.Errorf("unexpected LicenseConditionSet.ByName(%q): got %v, want %v", name, byName, expected)
-		} else {
-			sort.Strings(expected)
-			actual := make([]string, 0, len(byName))
-			for _, lc := range byName {
-				actual = append(actual, lc.Origin().Name())
-			}
-			sort.Strings(actual)
-			for i := 0; i < len(expected); i++ {
-				if expected[i] != actual[i] {
-					t.Errorf("unexpected LicenseConditionSet.ByName(%q) index %d in %v: got %s, want %s", name, i, actual, actual[i], expected[i])
-				}
-			}
-		}
-	}
-}
-
-type byOrigin map[string][]string
-
-func (bo byOrigin) checkPublic(lg *LicenseGraph, ls *LicenseConditionSet, t *testing.T) {
-	expectedCount := 0
-	for origin, expected := range bo {
-		expectedCount += len(expected)
-		onode := newTestNode(lg, origin)
-		if ls.HasAnyByOrigin(onode) {
-			if len(expected) == 0 {
-				t.Errorf("unexpected LicenseConditionSet.HasAnyByOrigin(%q): got true, want false", origin)
-			}
-		} else {
-			if len(expected) != 0 {
-				t.Errorf("unexpected LicenseConditionSet.HasAnyByOrigin(%q): got false, want true", origin)
-			}
-		}
-		if len(expected) != ls.CountByOrigin(onode) {
-			t.Errorf("unexpected LicenseConditionSet.CountByOrigin(%q): got %d, want %d", origin, ls.CountByOrigin(onode), len(expected))
-		}
-		byOrigin := ls.ByOrigin(onode).AsList()
-		if len(expected) != len(byOrigin) {
-			t.Errorf("unexpected LicenseConditionSet.ByOrigin(%q): got %v, want %v", origin, byOrigin, expected)
-		} else {
-			sort.Strings(expected)
-			actual := make([]string, 0, len(byOrigin))
-			for _, lc := range byOrigin {
-				actual = append(actual, lc.Name())
-			}
-			sort.Strings(actual)
-			for i := 0; i < len(expected); i++ {
-				if expected[i] != actual[i] {
-					t.Errorf("unexpected LicenseConditionSet.ByOrigin(%q) index %d in %v: got %s, want %s", origin, i, actual, actual[i], expected[i])
-				}
-			}
-		}
-	}
-	if expectedCount != ls.Count() {
-		t.Errorf("unexpected LicenseConditionSet.Count(): got %d, want %d", ls.Count(), expectedCount)
-	}
-	if ls.IsEmpty() {
-		if expectedCount != 0 {
-			t.Errorf("unexpected LicenseConditionSet.IsEmpty(): got true, want false")
-		}
-	} else {
-		if expectedCount == 0 {
-			t.Errorf("unexpected LicenseConditionSet.IsEmpty(): got false, want true")
-		}
-	}
-}
-
 func TestConditionSet(t *testing.T) {
 	tests := []struct {
-		name       string
-		conditions map[string][]string
-		add        map[string][]string
-		byName     map[string][]string
-		byOrigin   map[string][]string
+		name        string
+		conditions  []string
+		plus        *[]string
+		minus       *[]string
+		matchingAny map[string][]string
+		expected    []string
 	}{
 		{
 			name:       "empty",
-			conditions: map[string][]string{},
-			add:        map[string][]string{},
-			byName: map[string][]string{
+			conditions: []string{},
+			plus:       &[]string{},
+			matchingAny: map[string][]string{
 				"notice":     []string{},
 				"restricted": []string{},
+				"restricted|reciprocal": []string{},
 			},
-			byOrigin: map[string][]string{
-				"bin1": []string{},
-				"lib1": []string{},
-				"bin2": []string{},
-				"lib2": []string{},
+			expected:   []string{},
+		},
+		{
+			name:       "emptyminusnothing",
+			conditions: []string{},
+			minus:      &[]string{},
+			matchingAny: map[string][]string{
+				"notice":     []string{},
+				"restricted": []string{},
+				"restricted|reciprocal": []string{},
 			},
+			expected:   []string{},
+		},
+		{
+			name:       "emptyminusnotice",
+			conditions: []string{},
+			minus:      &[]string{"notice"},
+			matchingAny: map[string][]string{
+				"notice":     []string{},
+				"restricted": []string{},
+				"restricted|reciprocal": []string{},
+			},
+			expected:   []string{},
 		},
 		{
 			name: "noticeonly",
-			conditions: map[string][]string{
-				"notice": []string{"bin1", "lib1"},
-			},
-			byName: map[string][]string{
-				"notice":     []string{"bin1", "lib1"},
+			conditions: []string{"notice"},
+			matchingAny: map[string][]string{
+				"notice":     []string{"notice"},
+				"notice|proprietary":     []string{"notice"},
 				"restricted": []string{},
 			},
-			byOrigin: map[string][]string{
-				"bin1": []string{"notice"},
-				"lib1": []string{"notice"},
-				"bin2": []string{},
-				"lib2": []string{},
-			},
+			expected: []string{"notice"},
 		},
 		{
-			name: "noticeonlyadded",
-			conditions: map[string][]string{
-				"notice": []string{"bin1", "lib1"},
-			},
-			add: map[string][]string{
-				"notice": []string{"bin1", "bin2"},
-			},
-			byName: map[string][]string{
-				"notice":     []string{"bin1", "bin2", "lib1"},
+			name: "allnoticeonly",
+			conditions: []string{"notice"},
+			plus: &[]string{"notice"},
+			matchingAny: map[string][]string{
+				"notice":     []string{"notice"},
+				"notice|proprietary":     []string{"notice"},
 				"restricted": []string{},
 			},
-			byOrigin: map[string][]string{
-				"bin1": []string{"notice"},
-				"lib1": []string{"notice"},
-				"bin2": []string{"notice"},
-				"lib2": []string{},
+			expected: []string{"notice"},
+		},
+		{
+			name: "emptyplusnotice",
+			conditions: []string{},
+			plus: &[]string{"notice"},
+			matchingAny: map[string][]string{
+				"notice":     []string{"notice"},
+				"notice|proprietary":     []string{"notice"},
+				"restricted": []string{},
 			},
+			expected: []string{"notice"},
 		},
 		{
 			name: "everything",
-			conditions: map[string][]string{
-				"notice":            []string{"bin1", "bin2", "lib1", "lib2"},
-				"reciprocal":        []string{"bin1", "bin2", "lib1", "lib2"},
-				"restricted":        []string{"bin1", "bin2", "lib1", "lib2"},
-				"by_exception_only": []string{"bin1", "bin2", "lib1", "lib2"},
+			conditions: []string{"unencumbered", "permissive", "notice", "reciprocal", "restricted", "proprietary"},
+			plus: &[]string{"restricted_with_classpath_exception", "restricted_allows_dynamic_linking", "by_exception_only", "not_allowed"},
+			matchingAny: map[string][]string{
+				"unencumbered": []string{"unencumbered"},
+				"permissive":       []string{"permissive"},
+				"notice":     []string{"notice"},
+				"reciprocal":     []string{"reciprocal"},
+				"restricted":     []string{"restricted"},
+				"restricted_with_classpath_exception":     []string{"restricted_with_classpath_exception"},
+				"restricted_allows_dynamic_linking":     []string{"restricted_allows_dynamic_linking"},
+				"proprietary":     []string{"proprietary"},
+				"by_exception_only":     []string{"by_exception_only"},
+				"not_allowed":     []string{"not_allowed"},
+				"notice|proprietary":     []string{"notice", "proprietary"},
 			},
-			add: map[string][]string{
-				"notice":            []string{"bin1", "bin2", "lib1", "lib2"},
-				"reciprocal":        []string{"bin1", "bin2", "lib1", "lib2"},
-				"restricted":        []string{"bin1", "bin2", "lib1", "lib2"},
-				"by_exception_only": []string{"bin1", "bin2", "lib1", "lib2"},
-			},
-			byName: map[string][]string{
-				"permissive":        []string{},
-				"notice":            []string{"bin1", "bin2", "lib1", "lib2"},
-				"reciprocal":        []string{"bin1", "bin2", "lib1", "lib2"},
-				"restricted":        []string{"bin1", "bin2", "lib1", "lib2"},
-				"by_exception_only": []string{"bin1", "bin2", "lib1", "lib2"},
-			},
-			byOrigin: map[string][]string{
-				"bin1":  []string{"notice", "reciprocal", "restricted", "by_exception_only"},
-				"bin2":  []string{"notice", "reciprocal", "restricted", "by_exception_only"},
-				"lib1":  []string{"notice", "reciprocal", "restricted", "by_exception_only"},
-				"lib2":  []string{"notice", "reciprocal", "restricted", "by_exception_only"},
-				"other": []string{},
+			expected: []string{
+				"unencumbered",
+				"permissive",
+				"notice",
+				"reciprocal",
+				"restricted",
+				"restricted_with_classpath_exception",
+				"restricted_allows_dynamic_linking",
+				"proprietary",
+				"by_exception_only",
+				"not_allowed",
 			},
 		},
 		{
-			name: "allbutoneeach",
-			conditions: map[string][]string{
-				"notice":            []string{"bin2", "lib1", "lib2"},
-				"reciprocal":        []string{"bin1", "lib1", "lib2"},
-				"restricted":        []string{"bin1", "bin2", "lib2"},
-				"by_exception_only": []string{"bin1", "bin2", "lib1"},
+			name: "everythingplusminusnothing",
+			conditions: []string{
+				"unencumbered",
+				"permissive",
+				"notice",
+				"reciprocal",
+				"restricted",
+				"restricted_with_classpath_exception",
+				"restricted_allows_dynamic_linking",
+				"proprietary",
+				"by_exception_only",
+				"not_allowed",
 			},
-			byName: map[string][]string{
-				"permissive":        []string{},
-				"notice":            []string{"bin2", "lib1", "lib2"},
-				"reciprocal":        []string{"bin1", "lib1", "lib2"},
-				"restricted":        []string{"bin1", "bin2", "lib2"},
-				"by_exception_only": []string{"bin1", "bin2", "lib1"},
+			plus: &[]string{},
+			minus: &[]string{},
+			matchingAny: map[string][]string{
+				"unencumbered|permissive|notice": []string{"unencumbered", "permissive", "notice"},
+				"restricted|reciprocal":     []string{"reciprocal", "restricted"},
+				"proprietary|by_exception_only":     []string{"proprietary", "by_exception_only"},
+				"not_allowed":     []string{"not_allowed"},
 			},
-			byOrigin: map[string][]string{
-				"bin1":  []string{"reciprocal", "restricted", "by_exception_only"},
-				"bin2":  []string{"notice", "restricted", "by_exception_only"},
-				"lib1":  []string{"notice", "reciprocal", "by_exception_only"},
-				"lib2":  []string{"notice", "reciprocal", "restricted"},
-				"other": []string{},
+			expected: []string{
+				"unencumbered",
+				"permissive",
+				"notice",
+				"reciprocal",
+				"restricted",
+				"restricted_with_classpath_exception",
+				"restricted_allows_dynamic_linking",
+				"proprietary",
+				"by_exception_only",
+				"not_allowed",
 			},
 		},
 		{
-			name: "allbutoneeachadded",
-			conditions: map[string][]string{
-				"notice":            []string{"bin2", "lib1", "lib2"},
-				"reciprocal":        []string{"bin1", "lib1", "lib2"},
-				"restricted":        []string{"bin1", "bin2", "lib2"},
-				"by_exception_only": []string{"bin1", "bin2", "lib1"},
+			name: "allbutone",
+			conditions: []string{"unencumbered", "permissive", "notice", "reciprocal", "restricted", "proprietary"},
+			plus: &[]string{"restricted_allows_dynamic_linking", "by_exception_only", "not_allowed"},
+			matchingAny: map[string][]string{
+				"unencumbered": []string{"unencumbered"},
+				"permissive":       []string{"permissive"},
+				"notice":     []string{"notice"},
+				"reciprocal":     []string{"reciprocal"},
+				"restricted":     []string{"restricted"},
+				"restricted_with_classpath_exception":     []string{},
+				"restricted_allows_dynamic_linking":     []string{"restricted_allows_dynamic_linking"},
+				"proprietary":     []string{"proprietary"},
+				"by_exception_only":     []string{"by_exception_only"},
+				"not_allowed":     []string{"not_allowed"},
+				"notice|proprietary":     []string{"notice", "proprietary"},
 			},
-			add: map[string][]string{
-				"notice":            []string{"bin2", "lib1", "lib2"},
-				"reciprocal":        []string{"bin1", "lib1", "lib2"},
-				"restricted":        []string{"bin1", "bin2", "lib2"},
-				"by_exception_only": []string{"bin1", "bin2", "lib1"},
-			},
-			byName: map[string][]string{
-				"permissive":        []string{},
-				"notice":            []string{"bin2", "lib1", "lib2"},
-				"reciprocal":        []string{"bin1", "lib1", "lib2"},
-				"restricted":        []string{"bin1", "bin2", "lib2"},
-				"by_exception_only": []string{"bin1", "bin2", "lib1"},
-			},
-			byOrigin: map[string][]string{
-				"bin1":  []string{"reciprocal", "restricted", "by_exception_only"},
-				"bin2":  []string{"notice", "restricted", "by_exception_only"},
-				"lib1":  []string{"notice", "reciprocal", "by_exception_only"},
-				"lib2":  []string{"notice", "reciprocal", "restricted"},
-				"other": []string{},
+			expected: []string{
+				"unencumbered",
+				"permissive",
+				"notice",
+				"reciprocal",
+				"restricted",
+				"restricted_allows_dynamic_linking",
+				"proprietary",
+				"by_exception_only",
+				"not_allowed",
 			},
 		},
 		{
-			name: "allbutoneeachfilled",
-			conditions: map[string][]string{
-				"notice":            []string{"bin2", "lib1", "lib2"},
-				"reciprocal":        []string{"bin1", "lib1", "lib2"},
-				"restricted":        []string{"bin1", "bin2", "lib2"},
-				"by_exception_only": []string{"bin1", "bin2", "lib1"},
+			name: "everythingminusone",
+			conditions: []string{
+				"unencumbered",
+				"permissive",
+				"notice",
+				"reciprocal",
+				"restricted",
+				"restricted_with_classpath_exception",
+				"restricted_allows_dynamic_linking",
+				"proprietary",
+				"by_exception_only",
+				"not_allowed",
 			},
-			add: map[string][]string{
-				"notice":            []string{"bin1", "bin2", "lib1"},
-				"reciprocal":        []string{"bin1", "bin2", "lib2"},
-				"restricted":        []string{"bin1", "lib1", "lib2"},
-				"by_exception_only": []string{"bin2", "lib1", "lib2"},
+			minus: &[]string{"restricted_allows_dynamic_linking"},
+			matchingAny: map[string][]string{
+				"unencumbered": []string{"unencumbered"},
+				"permissive":       []string{"permissive"},
+				"notice":     []string{"notice"},
+				"reciprocal":     []string{"reciprocal"},
+				"restricted":     []string{"restricted"},
+				"restricted_with_classpath_exception":     []string{"restricted_with_classpath_exception"},
+				"restricted_allows_dynamic_linking":     []string{},
+				"proprietary":     []string{"proprietary"},
+				"by_exception_only":     []string{"by_exception_only"},
+				"not_allowed":     []string{"not_allowed"},
+				"restricted|proprietary":     []string{"restricted", "proprietary"},
 			},
-			byName: map[string][]string{
-				"permissive":        []string{},
-				"notice":            []string{"bin1", "bin2", "lib1", "lib2"},
-				"reciprocal":        []string{"bin1", "bin2", "lib1", "lib2"},
-				"restricted":        []string{"bin1", "bin2", "lib1", "lib2"},
-				"by_exception_only": []string{"bin1", "bin2", "lib1", "lib2"},
-			},
-			byOrigin: map[string][]string{
-				"bin1":  []string{"notice", "reciprocal", "restricted", "by_exception_only"},
-				"bin2":  []string{"notice", "reciprocal", "restricted", "by_exception_only"},
-				"lib1":  []string{"notice", "reciprocal", "restricted", "by_exception_only"},
-				"lib2":  []string{"notice", "reciprocal", "restricted", "by_exception_only"},
-				"other": []string{},
+			expected: []string{
+				"unencumbered",
+				"permissive",
+				"notice",
+				"reciprocal",
+				"restricted",
+				"restricted_with_classpath_exception",
+				"proprietary",
+				"by_exception_only",
+				"not_allowed",
 			},
 		},
 		{
-			name: "oneeach",
-			conditions: map[string][]string{
-				"notice":            []string{"bin1"},
-				"reciprocal":        []string{"bin2"},
-				"restricted":        []string{"lib1"},
-				"by_exception_only": []string{"lib2"},
+			name: "everythingminuseverything",
+			conditions: []string{
+				"unencumbered",
+				"permissive",
+				"notice",
+				"reciprocal",
+				"restricted",
+				"restricted_with_classpath_exception",
+				"restricted_allows_dynamic_linking",
+				"proprietary",
+				"by_exception_only",
+				"not_allowed",
 			},
-			byName: map[string][]string{
-				"permissive":        []string{},
-				"notice":            []string{"bin1"},
-				"reciprocal":        []string{"bin2"},
-				"restricted":        []string{"lib1"},
-				"by_exception_only": []string{"lib2"},
+			minus: &[]string{
+				"unencumbered",
+				"permissive",
+				"notice",
+				"reciprocal",
+				"restricted",
+				"restricted_with_classpath_exception",
+				"restricted_allows_dynamic_linking",
+				"proprietary",
+				"by_exception_only",
+				"not_allowed",
 			},
-			byOrigin: map[string][]string{
-				"bin1":  []string{"notice"},
-				"bin2":  []string{"reciprocal"},
-				"lib1":  []string{"restricted"},
-				"lib2":  []string{"by_exception_only"},
-				"other": []string{},
+			matchingAny: map[string][]string{
+				"unencumbered": []string{},
+				"permissive":       []string{},
+				"notice":     []string{},
+				"reciprocal":     []string{},
+				"restricted":     []string{},
+				"restricted_with_classpath_exception":     []string{},
+				"restricted_allows_dynamic_linking":     []string{},
+				"proprietary":     []string{},
+				"by_exception_only":     []string{},
+				"not_allowed":     []string{},
+				"restricted|proprietary":     []string{},
 			},
+			expected: []string{},
 		},
 		{
-			name: "oneeachoverlap",
-			conditions: map[string][]string{
-				"notice":            []string{"bin1"},
-				"reciprocal":        []string{"bin2"},
-				"restricted":        []string{"lib1"},
-				"by_exception_only": []string{"lib2"},
+			name: "restrictedplus",
+			conditions: []string{"restricted", "restricted_with_classpath_exception", "restricted_allows_dynamic_linking"},
+			plus: &[]string{"permissive", "notice", "restricted", "proprietary"},
+			matchingAny: map[string][]string{
+				"unencumbered":     []string{},
+				"permissive":     []string{"permissive"},
+				"notice":     []string{"notice"},
+				"restricted":     []string{"restricted"},
+				"restricted_with_classpath_exception":     []string{"restricted_with_classpath_exception"},
+				"restricted_allows_dynamic_linking":     []string{"restricted_allows_dynamic_linking"},
+				"proprietary":     []string{"proprietary"},
+				"restricted|proprietary":     []string{"restricted", "proprietary"},
+				"by_exception_only": []string{},
+				"proprietary|by_exception_only":     []string{"proprietary"},
 			},
-			add: map[string][]string{
-				"notice":            []string{"lib2"},
-				"reciprocal":        []string{"lib1"},
-				"restricted":        []string{"bin2"},
-				"by_exception_only": []string{"bin1"},
-			},
-			byName: map[string][]string{
-				"permissive":        []string{},
-				"notice":            []string{"bin1", "lib2"},
-				"reciprocal":        []string{"bin2", "lib1"},
-				"restricted":        []string{"bin2", "lib1"},
-				"by_exception_only": []string{"bin1", "lib2"},
-			},
-			byOrigin: map[string][]string{
-				"bin1":  []string{"by_exception_only", "notice"},
-				"bin2":  []string{"reciprocal", "restricted"},
-				"lib1":  []string{"reciprocal", "restricted"},
-				"lib2":  []string{"by_exception_only", "notice"},
-				"other": []string{},
-			},
-		},
-		{
-			name: "oneeachadded",
-			conditions: map[string][]string{
-				"notice":            []string{"bin1"},
-				"reciprocal":        []string{"bin2"},
-				"restricted":        []string{"lib1"},
-				"by_exception_only": []string{"lib2"},
-			},
-			add: map[string][]string{
-				"notice":            []string{"bin1"},
-				"reciprocal":        []string{"bin2"},
-				"restricted":        []string{"lib1"},
-				"by_exception_only": []string{"lib2"},
-			},
-			byName: map[string][]string{
-				"permissive":        []string{},
-				"notice":            []string{"bin1"},
-				"reciprocal":        []string{"bin2"},
-				"restricted":        []string{"lib1"},
-				"by_exception_only": []string{"lib2"},
-			},
-			byOrigin: map[string][]string{
-				"bin1":  []string{"notice"},
-				"bin2":  []string{"reciprocal"},
-				"lib1":  []string{"restricted"},
-				"lib2":  []string{"by_exception_only"},
-				"other": []string{},
-			},
+			expected: []string{"permissive", "notice", "restricted", "restricted_with_classpath_exception", "restricted_allows_dynamic_linking", "proprietary"},
 		},
 	}
 	for _, tt := range tests {
-		testPublicInterface := func(lg *LicenseGraph, cs *LicenseConditionSet, t *testing.T) {
-			byName(tt.byName).checkPublic(cs, t)
-			byOrigin(tt.byOrigin).checkPublic(lg, cs, t)
+		toConditions := func(names []string) []LicenseCondition {
+			result := make([]LicenseCondition, 0, len(names))
+			for _, name := range names {
+				result = append(result, RecognizedConditionNames[name])
+			}
+			return result
 		}
-		t.Run(tt.name+"_public_interface", func(t *testing.T) {
-			lg := newLicenseGraph()
-			cs := NewLicenseConditionSet(toConditionList(lg, tt.conditions)...)
-			if tt.add != nil {
-				cs.Add(toConditionList(lg, tt.add)...)
+		populate := func() LicenseConditionSet {
+			testSet := NewLicenseConditionSet(toConditions(tt.conditions)...)
+			if tt.plus != nil {
+				testSet = testSet.Plus(toConditions(*tt.plus)...)
 			}
-			testPublicInterface(lg, cs, t)
-		})
-
-		t.Run("Copy() of "+tt.name+"_public_interface", func(t *testing.T) {
-			lg := newLicenseGraph()
-			cs := NewLicenseConditionSet(toConditionList(lg, tt.conditions)...)
-			if tt.add != nil {
-				cs.Add(toConditionList(lg, tt.add)...)
+			if tt.minus != nil {
+				testSet = testSet.Minus(toConditions(*tt.minus)...)
 			}
-			testPublicInterface(lg, cs.Copy(), t)
-		})
+			return testSet
+		}
+		populateSet := func() LicenseConditionSet {
+			testSet := NewLicenseConditionSet(toConditions(tt.conditions)...)
+			if tt.plus != nil {
+				testSet = testSet.Union(NewLicenseConditionSet(toConditions(*tt.plus)...))
+			}
+			if tt.minus != nil {
+				testSet = testSet.Difference(NewLicenseConditionSet(toConditions(*tt.minus)...))
+			}
+			return testSet
+		}
+		populatePlusSet := func() LicenseConditionSet {
+			testSet := NewLicenseConditionSet(toConditions(tt.conditions)...)
+			if tt.plus != nil {
+				testSet = testSet.Union(NewLicenseConditionSet(toConditions(*tt.plus)...))
+			}
+			if tt.minus != nil {
+				testSet = testSet.Minus(toConditions(*tt.minus)...)
+			}
+			return testSet
+		}
+		populateMinusSet := func() LicenseConditionSet {
+			testSet := NewLicenseConditionSet(toConditions(tt.conditions)...)
+			if tt.plus != nil {
+				testSet = testSet.Plus(toConditions(*tt.plus)...)
+			}
+			if tt.minus != nil {
+				testSet = testSet.Difference(NewLicenseConditionSet(toConditions(*tt.minus)...))
+			}
+			return testSet
+		}
+		checkMatching := func(cs LicenseConditionSet, t *testing.T) {
+			for data, expectedNames := range tt.matchingAny {
+				expectedConditions := toConditions(expectedNames)
+				expected := NewLicenseConditionSet(expectedConditions...)
+				actual := cs.MatchingAny(toConditions(strings.Split(data, "|"))...)
+				actualNames := actual.Names()
 
-		testPrivateInterface := func(lg *LicenseGraph, cs *LicenseConditionSet, t *testing.T) {
-			slist := make([]string, 0, cs.Count())
-			for origin, expected := range tt.byOrigin {
-				for _, name := range expected {
-					slist = append(slist, origin+";"+name)
+				t.Logf("MatchingAny(%s): actual set %04x %s", data, actual, actual.String())
+				t.Logf("MatchingAny(%s): expected set %04x %s", data, expected, expected.String())
+
+				if actual != expected {
+					t.Errorf("MatchingAny(%s): got %04x, want %04x", data, actual, expected)
+					continue
+				}
+				if len(actualNames) != len(expectedNames) {
+					t.Errorf("len(MatchinAny(%s).Names()): got %d, want %d",
+						data, len(actualNames), len(expectedNames))
+				} else {
+					for i := 0; i < len(actualNames); i++ {
+						if actualNames[i] != expectedNames[i] {
+							t.Errorf("MatchingAny(%s).Names()[%d]: got %s, want %s",
+								data, i, actualNames[i], expectedNames[i])
+							break
+						}
+					}
+				}
+				actualConditions := actual.AsList()
+				if len(actualConditions) != len(expectedConditions) {
+					t.Errorf("len(MatchingAny(%d).AsList()):  got %d, want %d",
+						data, len(actualNames), len(expectedNames))
+				} else {
+					for i := 0; i < len(actualNames); i++ {
+						if actualNames[i] != expectedNames[i] {
+							t.Errorf("MatchingAny(%s).AsList()[%d]: got %s, want %s",
+								data, i, actualNames[i], expectedNames[i])
+							break
+						}
+					}
 				}
 			}
-			actualSlist := cs.asStringList(";")
-			if len(slist) != len(actualSlist) {
-				t.Errorf("unexpected LicenseConditionSet.asStringList(\";\"): got %v, want %v", actualSlist, slist)
+		}
+		checkMatchingSet := func(cs LicenseConditionSet, t *testing.T) {
+			for data, expectedNames := range tt.matchingAny {
+				expected := NewLicenseConditionSet(toConditions(expectedNames)...)
+				actual := cs.MatchingAnySet(NewLicenseConditionSet(toConditions(strings.Split(data, "|"))...))
+				actualNames := actual.Names()
+
+				t.Logf("MatchingAnySet(%s): actual set %04x %s", data, actual, actual.String())
+				t.Logf("MatchingAnySet(%s): expected set %04x %s", data, expected, expected.String())
+
+				if actual != expected {
+					t.Errorf("MatchingAnySet(%s): got %04x, want %04x", data, actual, expected)
+					continue
+				}
+				if len(actualNames) != len(expectedNames) {
+					t.Errorf("len(MatchingAnySet(%s).Names()): got %d, want %d",
+						data, len(actualNames), len(expectedNames))
+				} else {
+					for i := 0; i < len(actualNames); i++ {
+						if actualNames[i] != expectedNames[i] {
+							t.Errorf("MatchingAnySet(%s).Names()[%d]: got %s, want %s",
+								data, i, actualNames[i], expectedNames[i])
+							break
+						}
+					}
+				}
+				expectedConditions := toConditions(expectedNames)
+				actualConditions := actual.AsList()
+				if len(actualConditions) != len(expectedConditions) {
+					t.Errorf("len(MatchingAnySet(%s).AsList()): got %d, want %d",
+						data, len(actualNames), len(expectedNames))
+				} else {
+					for i := 0; i < len(actualNames); i++ {
+						if actualNames[i] != expectedNames[i] {
+							t.Errorf("MatchingAnySet(%s).AsList()[%d]: got %s, want %s",
+								data, i, actualNames[i], expectedNames[i])
+							break
+						}
+					}
+				}
+			}
+		}
+
+		checkExpected := func(actual LicenseConditionSet, t *testing.T) bool {
+			t.Logf("checkExpected{%s}", strings.Join(tt.expected, ", "))
+
+			expectedConditions := toConditions(tt.expected)
+			expected := NewLicenseConditionSet(expectedConditions...)
+
+			actualNames := actual.Names()
+
+			t.Logf("actual license condition set: %04x %s", actual, actual.String())
+			t.Logf("expected license condition set: %04x %s", expected, expected.String())
+
+			if actual != expected {
+				t.Errorf("checkExpected: got %04x, want %04x", actual, expected)
+				return false
+			}
+
+			if len(actualNames) != len(tt.expected) {
+				t.Errorf("len(actual.Names()): got %d, want %d", len(actualNames), len(tt.expected))
 			} else {
-				sort.Strings(slist)
-				sort.Strings(actualSlist)
-				for i := 0; i < len(slist); i++ {
-					if slist[i] != actualSlist[i] {
-						t.Errorf("unexpected LicenseConditionSet.asStringList(\";\") index %d in %v: got %s, want %s", i, actualSlist, actualSlist[i], slist[i])
+				for i := 0; i < len(actualNames); i++ {
+					if actualNames[i] != tt.expected[i] {
+						t.Errorf("actual.Names()[%d]: got %s, want %s", i, actualNames[i], tt.expected[i])
+						break
 					}
 				}
 			}
+
+			actualConditions := actual.AsList()
+			if len(actualConditions) != len(expectedConditions) {
+				t.Errorf("len(actual.AsList()): got %d, want %d", len(actualConditions), len(expectedConditions))
+			} else {
+				for i := 0; i < len(actualConditions); i++ {
+					if actualConditions[i] != expectedConditions[i] {
+						t.Errorf("actual.AsList()[%d]: got %s, want %s",
+							i, actualConditions[i], expectedConditions[i])
+						break
+					}
+				}
+			}
+
+			if len(tt.expected) == 0 {
+				if !actual.IsEmpty() {
+					t.Errorf("actual.IsEmpty(): got false, want true")
+				}
+				if actual.HasAny(expectedConditions...) {
+					t.Errorf("actual.HasAny(): got true, want false")
+				}
+			} else {
+				if actual.IsEmpty() {
+					t.Errorf("actual.IsEmpty(): got true, want false")
+				}
+				if !actual.HasAny(expectedConditions...) {
+					t.Errorf("actual.HasAny(all expected): got false, want true")
+				}
+			}
+			if !actual.HasAll(expectedConditions...) {
+				t.Errorf("actual.Hasll(all expected): want true, got false")
+			}
+			for _, expectedCondition := range expectedConditions {
+				if !actual.HasAny(expectedCondition) {
+					t.Errorf("actual.HasAny(%q): got false, want true", expectedCondition.Name())
+				}
+				if !actual.HasAll(expectedCondition) {
+					t.Errorf("actual.HasAll(%q): got false, want true", expectedCondition.Name())
+				}
+			}
+
+			notExpected := (AllLicenseConditions &^ expected)
+			notExpectedList := notExpected.AsList()
+			t.Logf("not expected license condition set: %04x %s", notExpected, notExpected.String())
+
+			if len(tt.expected) == 0 {
+				if actual.HasAny(append(expectedConditions, notExpectedList...)...) {
+					t.Errorf("actual.HasAny(all conditions): want false, got true")
+				}
+			} else {
+				if !actual.HasAny(append(expectedConditions, notExpectedList...)...) {
+					t.Errorf("actual.HasAny(all conditions): want true, got false")
+				}
+			}
+			if len(notExpectedList) == 0 {
+				if !actual.HasAll(append(expectedConditions, notExpectedList...)...) {
+					t.Errorf("actual.HasAll(all conditions): want true, got false")
+				}
+			} else {
+				if actual.HasAll(append(expectedConditions, notExpectedList...)...) {
+					t.Errorf("actual.HasAll(all conditions): want false, got true")
+				}
+			}
+			for _, unexpectedCondition := range notExpectedList {
+				if actual.HasAny(unexpectedCondition) {
+					t.Errorf("actual.HasAny(%q): got true, want false", unexpectedCondition.Name())
+				}
+				if actual.HasAll(unexpectedCondition) {
+					t.Errorf("actual.HasAll(%q): got true, want false", unexpectedCondition.Name())
+				}
+			}
+			return true
 		}
 
-		t.Run(tt.name+"_private_list_interface", func(t *testing.T) {
-			lg := newLicenseGraph()
-			cs := newLicenseConditionSet()
-			for name, origins := range tt.conditions {
-				for _, origin := range origins {
-					cs.add(newTestNode(lg, origin), name)
-				}
-			}
-			if tt.add != nil {
-				cs.Add(toConditionList(lg, tt.add)...)
-			}
-			testPrivateInterface(lg, cs, t)
-		})
+		checkExpectedSet := func(actual LicenseConditionSet, t *testing.T) bool {
+			t.Logf("checkExpectedSet{%s}", strings.Join(tt.expected, ", "))
 
-		t.Run(tt.name+"_private_set_interface", func(t *testing.T) {
-			lg := newLicenseGraph()
-			cs := newLicenseConditionSet()
-			for name, origins := range tt.conditions {
-				for _, origin := range origins {
-					cs.add(newTestNode(lg, origin), name)
-				}
+			expectedConditions := toConditions(tt.expected)
+			expected := NewLicenseConditionSet(expectedConditions...)
+
+			actualNames := actual.Names()
+
+			t.Logf("actual license condition set: %04x %s", actual, actual.String())
+			t.Logf("expected license condition set: %04x %s", expected, expected.String())
+
+			if actual != expected {
+				t.Errorf("checkExpectedSet: got %04x, want %04x", actual, expected)
+				return false
 			}
-			if tt.add != nil {
-				other := newLicenseConditionSet()
-				for name, origins := range tt.add {
-					for _, origin := range origins {
-						other.add(newTestNode(lg, origin), name)
+
+			if len(actualNames) != len(tt.expected) {
+				t.Errorf("len(actual.Names()): got %d, want %d", len(actualNames), len(tt.expected))
+			} else {
+				for i := 0; i < len(actualNames); i++ {
+					if actualNames[i] != tt.expected[i] {
+						t.Errorf("actual.Names()[%d]: got %s, want %s", i, actualNames[i], tt.expected[i])
+						break
 					}
 				}
-				cs.AddSet(other)
 			}
-			testPrivateInterface(lg, cs, t)
-		})
-	}
-}
 
-func TestConditionSet_Removals(t *testing.T) {
-	tests := []struct {
-		name         string
-		conditions   map[string][]string
-		removeByName []ConditionNames
-		removeSet    map[string][]string
-		byName       map[string][]string
-		byOrigin     map[string][]string
-	}{
-		{
-			name:         "emptybyname",
-			conditions:   map[string][]string{},
-			removeByName: []ConditionNames{{"reciprocal", "restricted"}},
-			byName: map[string][]string{
-				"notice":     []string{},
-				"restricted": []string{},
-			},
-			byOrigin: map[string][]string{
-				"bin1": []string{},
-				"lib1": []string{},
-				"bin2": []string{},
-				"lib2": []string{},
-			},
-		},
-		{
-			name:       "emptybyset",
-			conditions: map[string][]string{},
-			removeSet: map[string][]string{
-				"notice":     []string{"bin1", "bin2"},
-				"restricted": []string{"lib1", "lib2"},
-			},
-			byName: map[string][]string{
-				"notice":     []string{},
-				"restricted": []string{},
-			},
-			byOrigin: map[string][]string{
-				"bin1": []string{},
-				"lib1": []string{},
-				"bin2": []string{},
-				"lib2": []string{},
-			},
-		},
-		{
-			name: "everythingremovenone",
-			conditions: map[string][]string{
-				"notice":            []string{"bin1", "bin2", "lib1", "lib2"},
-				"reciprocal":        []string{"bin1", "bin2", "lib1", "lib2"},
-				"restricted":        []string{"bin1", "bin2", "lib1", "lib2"},
-				"by_exception_only": []string{"bin1", "bin2", "lib1", "lib2"},
-			},
-			removeByName: []ConditionNames{{"permissive", "unencumbered"}},
-			removeSet: map[string][]string{
-				"notice": []string{"apk1"},
-			},
-			byName: map[string][]string{
-				"permissive":        []string{},
-				"notice":            []string{"bin1", "bin2", "lib1", "lib2"},
-				"reciprocal":        []string{"bin1", "bin2", "lib1", "lib2"},
-				"restricted":        []string{"bin1", "bin2", "lib1", "lib2"},
-				"by_exception_only": []string{"bin1", "bin2", "lib1", "lib2"},
-			},
-			byOrigin: map[string][]string{
-				"bin1":  []string{"notice", "reciprocal", "restricted", "by_exception_only"},
-				"bin2":  []string{"notice", "reciprocal", "restricted", "by_exception_only"},
-				"lib1":  []string{"notice", "reciprocal", "restricted", "by_exception_only"},
-				"lib2":  []string{"notice", "reciprocal", "restricted", "by_exception_only"},
-				"other": []string{},
-			},
-		},
-		{
-			name: "everythingremovesome",
-			conditions: map[string][]string{
-				"notice":            []string{"bin1", "bin2", "lib1", "lib2"},
-				"reciprocal":        []string{"bin1", "bin2", "lib1", "lib2"},
-				"restricted":        []string{"bin1", "bin2", "lib1", "lib2"},
-				"by_exception_only": []string{"bin1", "bin2", "lib1", "lib2"},
-			},
-			removeByName: []ConditionNames{{"restricted", "by_exception_only"}},
-			removeSet: map[string][]string{
-				"notice": []string{"lib1"},
-			},
-			byName: map[string][]string{
-				"permissive":        []string{},
-				"notice":            []string{"bin1", "bin2", "lib2"},
-				"reciprocal":        []string{"bin1", "bin2", "lib1", "lib2"},
-				"restricted":        []string{},
-				"by_exception_only": []string{},
-			},
-			byOrigin: map[string][]string{
-				"bin1":  []string{"notice", "reciprocal"},
-				"bin2":  []string{"notice", "reciprocal"},
-				"lib1":  []string{"reciprocal"},
-				"lib2":  []string{"notice", "reciprocal"},
-				"other": []string{},
-			},
-		},
-		{
-			name: "everythingremoveall",
-			conditions: map[string][]string{
-				"notice":            []string{"bin1", "bin2", "lib1", "lib2"},
-				"reciprocal":        []string{"bin1", "bin2", "lib1", "lib2"},
-				"restricted":        []string{"bin1", "bin2", "lib1", "lib2"},
-				"by_exception_only": []string{"bin1", "bin2", "lib1", "lib2"},
-			},
-			removeByName: []ConditionNames{{"restricted", "by_exception_only"}},
-			removeSet: map[string][]string{
-				"notice":     []string{"bin1", "bin2", "lib1", "lib2"},
-				"reciprocal": []string{"bin1", "bin2", "lib1", "lib2"},
-				"restricted": []string{"bin1"},
-			},
-			byName: map[string][]string{
-				"permissive":        []string{},
-				"notice":            []string{},
-				"reciprocal":        []string{},
-				"restricted":        []string{},
-				"by_exception_only": []string{},
-			},
-			byOrigin: map[string][]string{
-				"bin1":  []string{},
-				"bin2":  []string{},
-				"lib1":  []string{},
-				"lib2":  []string{},
-				"other": []string{},
-			},
-		},
-	}
-	for _, tt := range tests {
+			actualConditions := actual.AsList()
+			if len(actualConditions) != len(expectedConditions) {
+				t.Errorf("len(actual.AsList()): got %d, want %d", len(actualConditions), len(expectedConditions))
+			} else {
+				for i := 0; i < len(actualConditions); i++ {
+					if actualConditions[i] != expectedConditions[i] {
+						t.Errorf("actual.AsList()[%d}: got %s, want %s",
+							i, actualConditions[i], expectedConditions[i])
+						break
+					}
+				}
+			}
+
+			if len(tt.expected) == 0 {
+				if !actual.IsEmpty() {
+					t.Errorf("actual.IsEmpty(): got false, want true")
+				}
+				if actual.MatchesAnySet(expected) {
+					t.Errorf("actual.MatchesAnySet({}): got true, want false")
+				}
+				if actual.MatchesEverySet(expected, expected) {
+					t.Errorf("actual.MatchesEverySet({}, {}): want false, got true")
+				}
+			} else {
+				if actual.IsEmpty() {
+					t.Errorf("actual.IsEmpty(): got true, want false")
+				}
+				if !actual.MatchesAnySet(expected) {
+					t.Errorf("actual.MatchesAnySet({all expected}): want true, got false")
+				}
+				if !actual.MatchesEverySet(expected, expected) {
+					t.Errorf("actual.MatchesEverySet({all expected}, {all expected}): want true, got false")
+				}
+			}
+
+			notExpected := (AllLicenseConditions &^ expected)
+			t.Logf("not expected license condition set: %04x %s", notExpected, notExpected.String())
+
+			if len(tt.expected) == 0 {
+				if actual.MatchesAnySet(expected, notExpected) {
+					t.Errorf("empty actual.MatchesAnySet({expected}, {not expected}): want false, got true")
+				}
+			} else {
+				if !actual.MatchesAnySet(expected, notExpected) {
+					t.Errorf("actual.MatchesAnySet({expected}, {not expected}): want true, got false")
+				}
+			}
+			if actual.MatchesAnySet(notExpected) {
+				t.Errorf("actual.MatchesAnySet({not expected}): want false, got true")
+			}
+			if actual.MatchesEverySet(notExpected) {
+				t.Errorf("actual.MatchesEverySet({not expected}): want false, got true")
+			}
+			if actual.MatchesEverySet(expected, notExpected) {
+				t.Errorf("actual.MatchesEverySet({expected}, {not expected}): want false, got true")
+			}
+
+			if !actual.Difference(expected).IsEmpty() {
+				t.Errorf("actual.Difference({expected}).IsEmpty(): want true, got false")
+			}
+			if expected != actual.Intersection(expected) {
+				t.Errorf("expected == actual.Intersection({expected}): want true, got false (%04x != %04x)", expected, actual.Intersection(expected))
+			}
+			if actual != actual.Intersection(expected) {
+				t.Errorf("actual == actual.Intersection({expected}): want true, got false (%04x != %04x)", actual, actual.Intersection(expected))
+			}
+			return true
+		}
+
 		t.Run(tt.name, func(t *testing.T) {
-			lg := newLicenseGraph()
-			cs := newLicenseConditionSet()
-			for name, origins := range tt.conditions {
-				for _, origin := range origins {
-					cs.add(newTestNode(lg, origin), name)
-				}
+			cs := populate()
+			if checkExpected(cs, t) {
+				checkMatching(cs, t)
 			}
-			if tt.removeByName != nil {
-				cs.RemoveAllByName(tt.removeByName...)
+			if checkExpectedSet(cs, t) {
+				checkMatchingSet(cs, t)
 			}
-			if tt.removeSet != nil {
-				other := newLicenseConditionSet()
-				for name, origins := range tt.removeSet {
-					for _, origin := range origins {
-						other.add(newTestNode(lg, origin), name)
-					}
-				}
-				cs.RemoveSet(other)
+		})
+
+		t.Run(tt.name+"_sets", func(t *testing.T) {
+			cs := populateSet()
+			if checkExpected(cs, t) {
+				checkMatching(cs, t)
 			}
-			byName(tt.byName).checkPublic(cs, t)
-			byOrigin(tt.byOrigin).checkPublic(lg, cs, t)
+			if checkExpectedSet(cs, t){
+				checkMatchingSet(cs, t)
+			}
+		})
+
+		t.Run(tt.name+"_plusset", func(t *testing.T) {
+			cs := populatePlusSet()
+			if checkExpected(cs, t) {
+				checkMatching(cs, t)
+			}
+			if checkExpectedSet(cs, t){
+				checkMatchingSet(cs, t)
+			}
+		})
+
+		t.Run(tt.name+"_minusset", func(t *testing.T) {
+			cs := populateMinusSet()
+			if checkExpected(cs, t) {
+				checkMatching(cs, t)
+			}
+			if checkExpectedSet(cs, t){
+				checkMatchingSet(cs, t)
+			}
 		})
 	}
 }