Commit 17f83ace03
Changed files (1)
lib
std
lib/std/enums.zig
@@ -241,957 +241,794 @@ test nameCast {
/// to dense indices. This type does no dynamic allocation and
/// can be copied by value.
pub fn EnumSet(comptime E: type) type {
- const mixin = struct {
- fn EnumSetExt(comptime Self: type) type {
- const Indexer = Self.Indexer;
- return struct {
- /// Initializes the set using a struct of bools
- pub fn init(init_values: EnumFieldStruct(E, bool, false)) Self {
- var result = Self{};
- comptime var i: usize = 0;
- inline while (i < Self.len) : (i += 1) {
- const key = comptime Indexer.keyForIndex(i);
- const tag = comptime @tagName(key);
- if (@field(init_values, tag)) {
- result.bits.set(i);
- }
- }
- return result;
- }
- };
- }
- };
- return IndexedSet(EnumIndexer(E), mixin.EnumSetExt);
-}
+ return struct {
+ const Self = @This();
-/// A map keyed by an enum, backed by a bitfield and a dense array.
-/// If the enum is not dense, a mapping will be constructed from
-/// enum values to dense indices. This type does no dynamic
-/// allocation and can be copied by value.
-pub fn EnumMap(comptime E: type, comptime V: type) type {
- const mixin = struct {
- fn EnumMapExt(comptime Self: type) type {
- const Indexer = Self.Indexer;
- return struct {
- /// Initializes the map using a sparse struct of optionals
- pub fn init(init_values: EnumFieldStruct(E, ?V, @as(?V, null))) Self {
- var result = Self{};
- comptime var i: usize = 0;
- inline while (i < Self.len) : (i += 1) {
- const key = comptime Indexer.keyForIndex(i);
- const tag = comptime @tagName(key);
- if (@field(init_values, tag)) |*v| {
- result.bits.set(i);
- result.values[i] = v.*;
- }
- }
- return result;
- }
- /// Initializes a full mapping with all keys set to value.
- /// Consider using EnumArray instead if the map will remain full.
- pub fn initFull(value: V) Self {
- var result = Self{
- .bits = Self.BitSet.initFull(),
- .values = undefined,
- };
- @memset(&result.values, value);
- return result;
- }
- /// Initializes a full mapping with supplied values.
- /// Consider using EnumArray instead if the map will remain full.
- pub fn initFullWith(init_values: EnumFieldStruct(E, V, @as(?V, null))) Self {
- return initFullWithDefault(@as(?V, null), init_values);
- }
- /// Initializes a full mapping with a provided default.
- /// Consider using EnumArray instead if the map will remain full.
- pub fn initFullWithDefault(comptime default: ?V, init_values: EnumFieldStruct(E, V, default)) Self {
- var result = Self{
- .bits = Self.BitSet.initFull(),
- .values = undefined,
- };
- comptime var i: usize = 0;
- inline while (i < Self.len) : (i += 1) {
- const key = comptime Indexer.keyForIndex(i);
- const tag = comptime @tagName(key);
- result.values[i] = @field(init_values, tag);
- }
- return result;
- }
- };
- }
- };
- return IndexedMap(EnumIndexer(E), V, mixin.EnumMapExt);
-}
+ /// The indexing rules for converting between keys and indices.
+ pub const Indexer = EnumIndexer(E);
+ /// The element type for this set.
+ pub const Key = Indexer.Key;
-/// A multiset of enum elements up to a count of usize. Backed
-/// by an EnumArray. This type does no dynamic allocation and can
-/// be copied by value.
-pub fn EnumMultiset(comptime E: type) type {
- return BoundedEnumMultiset(E, usize);
-}
+ const BitSet = std.StaticBitSet(Indexer.count);
-/// A multiset of enum elements up to CountSize. Backed by an
-/// EnumArray. This type does no dynamic allocation and can be
-/// copied by value.
-pub fn BoundedEnumMultiset(comptime E: type, comptime CountSize: type) type {
- return struct {
- const Self = @This();
+ /// The maximum number of items in this set.
+ pub const len = Indexer.count;
- counts: EnumArray(E, CountSize),
+ bits: BitSet = BitSet.initEmpty(),
- /// Initializes the multiset using a struct of counts.
- pub fn init(init_counts: EnumFieldStruct(E, CountSize, 0)) Self {
- var self = initWithCount(0);
- inline for (@typeInfo(E).Enum.fields) |field| {
- const c = @field(init_counts, field.name);
- const key = @as(E, @enumFromInt(field.value));
- self.counts.set(key, c);
+ /// Initializes the set using a struct of bools
+ pub fn init(init_values: EnumFieldStruct(E, bool, false)) Self {
+ var result: Self = .{};
+ inline for (0..Self.len) |i| {
+ const key = comptime Indexer.keyForIndex(i);
+ const tag = @tagName(key);
+ if (@field(init_values, tag)) {
+ result.bits.set(i);
+ }
}
- return self;
+ return result;
}
- /// Initializes the multiset with a count of zero.
+ /// Returns a set containing no keys.
pub fn initEmpty() Self {
- return initWithCount(0);
+ return .{ .bits = BitSet.initEmpty() };
}
- /// Initializes the multiset with all keys at the
- /// same count.
- pub fn initWithCount(comptime c: CountSize) Self {
- return .{
- .counts = EnumArray(E, CountSize).initDefault(c, .{}),
- };
+ /// Returns a set containing all possible keys.
+ pub fn initFull() Self {
+ return .{ .bits = BitSet.initFull() };
}
- /// Returns the total number of key counts in the multiset.
- pub fn count(self: Self) usize {
- var sum: usize = 0;
- for (self.counts.values) |c| {
- sum += c;
- }
- return sum;
+ /// Returns a set containing multiple keys.
+ pub fn initMany(keys: []const Key) Self {
+ var set = initEmpty();
+ for (keys) |key| set.insert(key);
+ return set;
}
- /// Checks if at least one key in multiset.
- pub fn contains(self: Self, key: E) bool {
- return self.counts.get(key) > 0;
+ /// Returns a set containing a single key.
+ pub fn initOne(key: Key) Self {
+ return initMany(&[_]Key{key});
}
- /// Removes all instance of a key from multiset. Same as
- /// setCount(key, 0).
- pub fn removeAll(self: *Self, key: E) void {
- return self.counts.set(key, 0);
+ /// Returns the number of keys in the set.
+ pub fn count(self: Self) usize {
+ return self.bits.count();
}
- /// Increases the key count by given amount. Caller asserts
- /// operation will not overflow.
- pub fn addAssertSafe(self: *Self, key: E, c: CountSize) void {
- self.counts.getPtr(key).* += c;
+ /// Checks if a key is in the set.
+ pub fn contains(self: Self, key: Key) bool {
+ return self.bits.isSet(Indexer.indexOf(key));
}
- /// Increases the key count by given amount.
- pub fn add(self: *Self, key: E, c: CountSize) error{Overflow}!void {
- self.counts.set(key, try std.math.add(CountSize, self.counts.get(key), c));
+ /// Puts a key in the set.
+ pub fn insert(self: *Self, key: Key) void {
+ self.bits.set(Indexer.indexOf(key));
}
- /// Decreases the key count by given amount. If amount is
- /// greater than the number of keys in multset, then key count
- /// will be set to zero.
- pub fn remove(self: *Self, key: E, c: CountSize) void {
- self.counts.getPtr(key).* -= @min(self.getCount(key), c);
+ /// Removes a key from the set.
+ pub fn remove(self: *Self, key: Key) void {
+ self.bits.unset(Indexer.indexOf(key));
}
- /// Returns the count for a key.
- pub fn getCount(self: Self, key: E) CountSize {
- return self.counts.get(key);
+ /// Changes the presence of a key in the set to match the passed bool.
+ pub fn setPresent(self: *Self, key: Key, present: bool) void {
+ self.bits.setValue(Indexer.indexOf(key), present);
}
- /// Set the count for a key.
- pub fn setCount(self: *Self, key: E, c: CountSize) void {
- self.counts.set(key, c);
+ /// Toggles the presence of a key in the set. If the key is in
+ /// the set, removes it. Otherwise adds it.
+ pub fn toggle(self: *Self, key: Key) void {
+ self.bits.toggle(Indexer.indexOf(key));
}
- /// Increases the all key counts by given multiset. Caller
- /// asserts operation will not overflow any key.
- pub fn addSetAssertSafe(self: *Self, other: Self) void {
- inline for (@typeInfo(E).Enum.fields) |field| {
- const key = @as(E, @enumFromInt(field.value));
- self.addAssertSafe(key, other.getCount(key));
- }
+ /// Toggles the presence of all keys in the passed set.
+ pub fn toggleSet(self: *Self, other: Self) void {
+ self.bits.toggleSet(other.bits);
}
- /// Increases the all key counts by given multiset.
- pub fn addSet(self: *Self, other: Self) error{Overflow}!void {
- inline for (@typeInfo(E).Enum.fields) |field| {
- const key = @as(E, @enumFromInt(field.value));
- try self.add(key, other.getCount(key));
- }
+ /// Toggles all possible keys in the set.
+ pub fn toggleAll(self: *Self) void {
+ self.bits.toggleAll();
}
- /// Decreases the all key counts by given multiset. If
- /// the given multiset has more key counts than this,
- /// then that key will have a key count of zero.
- pub fn removeSet(self: *Self, other: Self) void {
- inline for (@typeInfo(E).Enum.fields) |field| {
- const key = @as(E, @enumFromInt(field.value));
- self.remove(key, other.getCount(key));
- }
+ /// Adds all keys in the passed set to this set.
+ pub fn setUnion(self: *Self, other: Self) void {
+ self.bits.setUnion(other.bits);
}
- /// Returns true iff all key counts are the same as
- /// given multiset.
+ /// Removes all keys which are not in the passed set.
+ pub fn setIntersection(self: *Self, other: Self) void {
+ self.bits.setIntersection(other.bits);
+ }
+
+ /// Returns true iff both sets have the same keys.
pub fn eql(self: Self, other: Self) bool {
- inline for (@typeInfo(E).Enum.fields) |field| {
- const key = @as(E, @enumFromInt(field.value));
- if (self.getCount(key) != other.getCount(key)) {
- return false;
- }
- }
- return true;
+ return self.bits.eql(other.bits);
}
- /// Returns true iff all key counts less than or
- /// equal to the given multiset.
+ /// Returns true iff all the keys in this set are
+ /// in the other set. The other set may have keys
+ /// not found in this set.
pub fn subsetOf(self: Self, other: Self) bool {
- inline for (@typeInfo(E).Enum.fields) |field| {
- const key = @as(E, @enumFromInt(field.value));
- if (self.getCount(key) > other.getCount(key)) {
- return false;
- }
- }
- return true;
+ return self.bits.subsetOf(other.bits);
}
- /// Returns true iff all key counts greater than or
- /// equal to the given multiset.
+ /// Returns true iff this set contains all the keys
+ /// in the other set. This set may have keys not
+ /// found in the other set.
pub fn supersetOf(self: Self, other: Self) bool {
- inline for (@typeInfo(E).Enum.fields) |field| {
- const key = @as(E, @enumFromInt(field.value));
- if (self.getCount(key) < other.getCount(key)) {
- return false;
- }
- }
- return true;
+ return self.bits.supersetOf(other.bits);
}
- /// Returns a multiset with the total key count of this
- /// multiset and the other multiset. Caller asserts
- /// operation will not overflow any key.
- pub fn plusAssertSafe(self: Self, other: Self) Self {
- var result = self;
- result.addSetAssertSafe(other);
- return result;
+ /// Returns a set with all the keys not in this set.
+ pub fn complement(self: Self) Self {
+ return .{ .bits = self.bits.complement() };
}
- /// Returns a multiset with the total key count of this
- /// multiset and the other multiset.
- pub fn plus(self: Self, other: Self) error{Overflow}!Self {
- var result = self;
- try result.addSet(other);
- return result;
+ /// Returns a set with keys that are in either this
+ /// set or the other set.
+ pub fn unionWith(self: Self, other: Self) Self {
+ return .{ .bits = self.bits.unionWith(other.bits) };
}
- /// Returns a multiset with the key count of this
- /// multiset minus the corresponding key count in the
- /// other multiset. If the other multiset contains
- /// more key count than this set, that key will have
- /// a count of zero.
- pub fn minus(self: Self, other: Self) Self {
- var result = self;
- result.removeSet(other);
- return result;
+ /// Returns a set with keys that are in both this
+ /// set and the other set.
+ pub fn intersectWith(self: Self, other: Self) Self {
+ return .{ .bits = self.bits.intersectWith(other.bits) };
}
- pub const Entry = EnumArray(E, CountSize).Entry;
- pub const Iterator = EnumArray(E, CountSize).Iterator;
-
- /// Returns an iterator over this multiset. Keys with zero
- /// counts are included. Modifications to the set during
- /// iteration may or may not be observed by the iterator,
- /// but will not invalidate it.
- pub fn iterator(self: *Self) Iterator {
- return self.counts.iterator();
+ /// Returns a set with keys that are in either this
+ /// set or the other set, but not both.
+ pub fn xorWith(self: Self, other: Self) Self {
+ return .{ .bits = self.bits.xorWith(other.bits) };
}
- };
-}
-test EnumMultiset {
- const Ball = enum { red, green, blue };
+ /// Returns a set with keys that are in this set
+ /// except for keys in the other set.
+ pub fn differenceWith(self: Self, other: Self) Self {
+ return .{ .bits = self.bits.differenceWith(other.bits) };
+ }
- const empty = EnumMultiset(Ball).initEmpty();
- const r0_g1_b2 = EnumMultiset(Ball).init(.{
- .red = 0,
- .green = 1,
- .blue = 2,
- });
- const ten_of_each = EnumMultiset(Ball).initWithCount(10);
-
- try testing.expectEqual(empty.count(), 0);
- try testing.expectEqual(r0_g1_b2.count(), 3);
- try testing.expectEqual(ten_of_each.count(), 30);
-
- try testing.expect(!empty.contains(.red));
- try testing.expect(!empty.contains(.green));
- try testing.expect(!empty.contains(.blue));
-
- try testing.expect(!r0_g1_b2.contains(.red));
- try testing.expect(r0_g1_b2.contains(.green));
- try testing.expect(r0_g1_b2.contains(.blue));
-
- try testing.expect(ten_of_each.contains(.red));
- try testing.expect(ten_of_each.contains(.green));
- try testing.expect(ten_of_each.contains(.blue));
-
- {
- var copy = ten_of_each;
- copy.removeAll(.red);
- try testing.expect(!copy.contains(.red));
-
- // removeAll second time does nothing
- copy.removeAll(.red);
- try testing.expect(!copy.contains(.red));
- }
+ /// Returns an iterator over this set, which iterates in
+ /// index order. Modifications to the set during iteration
+ /// may or may not be observed by the iterator, but will
+ /// not invalidate it.
+ pub fn iterator(self: *const Self) Iterator {
+ return .{ .inner = self.bits.iterator(.{}) };
+ }
- {
- var copy = ten_of_each;
- copy.addAssertSafe(.red, 6);
- try testing.expectEqual(copy.getCount(.red), 16);
- }
+ pub const Iterator = struct {
+ inner: BitSet.Iterator(.{}),
- {
- var copy = ten_of_each;
- try copy.add(.red, 6);
- try testing.expectEqual(copy.getCount(.red), 16);
+ pub fn next(self: *Iterator) ?Key {
+ return if (self.inner.next()) |index|
+ Indexer.keyForIndex(index)
+ else
+ null;
+ }
+ };
+ };
+}
- try testing.expectError(error.Overflow, copy.add(.red, std.math.maxInt(usize)));
- }
+/// A map keyed by an enum, backed by a bitfield and a dense array.
+/// If the enum is not dense, a mapping will be constructed from
+/// enum values to dense indices. This type does no dynamic
+/// allocation and can be copied by value.
+pub fn EnumMap(comptime E: type, comptime V: type) type {
+ return struct {
+ const Self = @This();
- {
- var copy = ten_of_each;
- copy.remove(.red, 4);
- try testing.expectEqual(copy.getCount(.red), 6);
+ /// The index mapping for this map
+ pub const Indexer = EnumIndexer(E);
+ /// The key type used to index this map
+ pub const Key = Indexer.Key;
+ /// The value type stored in this map
+ pub const Value = V;
+ /// The number of possible keys in the map
+ pub const len = Indexer.count;
- // subtracting more it contains does not underflow
- copy.remove(.green, 14);
- try testing.expectEqual(copy.getCount(.green), 0);
- }
+ const BitSet = std.StaticBitSet(Indexer.count);
- try testing.expectEqual(empty.getCount(.green), 0);
- try testing.expectEqual(r0_g1_b2.getCount(.green), 1);
- try testing.expectEqual(ten_of_each.getCount(.green), 10);
+ /// Bits determining whether items are in the map
+ bits: BitSet = BitSet.initEmpty(),
+ /// Values of items in the map. If the associated
+ /// bit is zero, the value is undefined.
+ values: [Indexer.count]Value = undefined,
- {
- var copy = empty;
- copy.setCount(.red, 6);
- try testing.expectEqual(copy.getCount(.red), 6);
- }
+ /// Initializes the map using a sparse struct of optionals
+ pub fn init(init_values: EnumFieldStruct(E, ?Value, null)) Self {
+ var result: Self = .{};
+ inline for (0..Self.len) |i| {
+ const key = comptime Indexer.keyForIndex(i);
+ const tag = @tagName(key);
+ if (@field(init_values, tag)) |*v| {
+ result.bits.set(i);
+ result.values[i] = v.*;
+ }
+ }
+ }
- {
- var copy = r0_g1_b2;
- copy.addSetAssertSafe(ten_of_each);
- try testing.expectEqual(copy.getCount(.red), 10);
- try testing.expectEqual(copy.getCount(.green), 11);
- try testing.expectEqual(copy.getCount(.blue), 12);
- }
+ /// Initializes a full mapping with all keys set to value.
+ /// Consider using EnumArray instead if the map will remain full.
+ pub fn initFull(value: Value) Self {
+ var result: Self = .{
+ .bits = Self.BitSet.initFull(),
+ .values = undefined,
+ };
+ @memset(&result.values, value);
+ return result;
+ }
- {
- var copy = r0_g1_b2;
- try copy.addSet(ten_of_each);
- try testing.expectEqual(copy.getCount(.red), 10);
- try testing.expectEqual(copy.getCount(.green), 11);
- try testing.expectEqual(copy.getCount(.blue), 12);
+ /// Initializes a full mapping with supplied values.
+ /// Consider using EnumArray instead if the map will remain full.
+ pub fn initFullWith(init_values: EnumFieldStruct(E, Value, null)) Self {
+ return initFullWithDefault(null, init_values);
+ }
- const full = EnumMultiset(Ball).initWithCount(std.math.maxInt(usize));
- try testing.expectError(error.Overflow, copy.addSet(full));
- }
+ /// Initializes a full mapping with a provided default.
+ /// Consider using EnumArray instead if the map will remain full.
+ pub fn initFullWithDefault(comptime default: ?Value, init_values: EnumFieldStruct(E, Value, default)) Self {
+ var result: Self = .{
+ .bits = Self.BitSet.initFull(),
+ .values = undefined,
+ };
+ inline for (0..Self.len) |i| {
+ const key = comptime Indexer.keyForIndex(i);
+ const tag = @tagName(key);
+ result.values[i] = @field(init_values, tag);
+ }
+ return result;
+ }
- {
- var copy = ten_of_each;
- copy.removeSet(r0_g1_b2);
- try testing.expectEqual(copy.getCount(.red), 10);
- try testing.expectEqual(copy.getCount(.green), 9);
- try testing.expectEqual(copy.getCount(.blue), 8);
+ /// The number of items in the map.
+ pub fn count(self: Self) usize {
+ return self.bits.count();
+ }
- copy.removeSet(ten_of_each);
- try testing.expectEqual(copy.getCount(.red), 0);
- try testing.expectEqual(copy.getCount(.green), 0);
- try testing.expectEqual(copy.getCount(.blue), 0);
- }
+ /// Checks if the map contains an item.
+ pub fn contains(self: Self, key: Key) bool {
+ return self.bits.isSet(Indexer.indexOf(key));
+ }
- try testing.expect(empty.eql(empty));
- try testing.expect(r0_g1_b2.eql(r0_g1_b2));
- try testing.expect(ten_of_each.eql(ten_of_each));
- try testing.expect(!empty.eql(r0_g1_b2));
- try testing.expect(!r0_g1_b2.eql(ten_of_each));
- try testing.expect(!ten_of_each.eql(empty));
+ /// Gets the value associated with a key.
+ /// If the key is not in the map, returns null.
+ pub fn get(self: Self, key: Key) ?Value {
+ const index = Indexer.indexOf(key);
+ return if (self.bits.isSet(index)) self.values[index] else null;
+ }
- try testing.expect(empty.subsetOf(empty));
- try testing.expect(r0_g1_b2.subsetOf(r0_g1_b2));
- try testing.expect(empty.subsetOf(r0_g1_b2));
- try testing.expect(r0_g1_b2.subsetOf(ten_of_each));
- try testing.expect(!ten_of_each.subsetOf(r0_g1_b2));
- try testing.expect(!r0_g1_b2.subsetOf(empty));
+ /// Gets the value associated with a key, which must
+ /// exist in the map.
+ pub fn getAssertContains(self: Self, key: Key) Value {
+ const index = Indexer.indexOf(key);
+ assert(self.bits.isSet(index));
+ return self.values[index];
+ }
- try testing.expect(empty.supersetOf(empty));
- try testing.expect(r0_g1_b2.supersetOf(r0_g1_b2));
- try testing.expect(r0_g1_b2.supersetOf(empty));
- try testing.expect(ten_of_each.supersetOf(r0_g1_b2));
- try testing.expect(!r0_g1_b2.supersetOf(ten_of_each));
- try testing.expect(!empty.supersetOf(r0_g1_b2));
+ /// Gets the address of the value associated with a key.
+ /// If the key is not in the map, returns null.
+ pub fn getPtr(self: *Self, key: Key) ?*Value {
+ const index = Indexer.indexOf(key);
+ return if (self.bits.isSet(index)) &self.values[index] else null;
+ }
- {
- // with multisets it could be the case where two
- // multisets are neither subset nor superset of each
- // other.
+ /// Gets the address of the const value associated with a key.
+ /// If the key is not in the map, returns null.
+ pub fn getPtrConst(self: *const Self, key: Key) ?*const Value {
+ const index = Indexer.indexOf(key);
+ return if (self.bits.isSet(index)) &self.values[index] else null;
+ }
- const r10 = EnumMultiset(Ball).init(.{
- .red = 10,
- });
- const b10 = EnumMultiset(Ball).init(.{
- .blue = 10,
- });
+ /// Gets the address of the value associated with a key.
+ /// The key must be present in the map.
+ pub fn getPtrAssertContains(self: *Self, key: Key) *Value {
+ const index = Indexer.indexOf(key);
+ assert(self.bits.isSet(index));
+ return &self.values[index];
+ }
- try testing.expect(!r10.subsetOf(b10));
- try testing.expect(!b10.subsetOf(r10));
- try testing.expect(!r10.supersetOf(b10));
- try testing.expect(!b10.supersetOf(r10));
- }
+ /// Gets the address of the const value associated with a key.
+ /// The key must be present in the map.
+ pub fn getPtrConstAssertContains(self: *const Self, key: Key) *const Value {
+ const index = Indexer.indexOf(key);
+ assert(self.bits.isSet(index));
+ return &self.values[index];
+ }
- {
- const result = r0_g1_b2.plusAssertSafe(ten_of_each);
- try testing.expectEqual(result.getCount(.red), 10);
- try testing.expectEqual(result.getCount(.green), 11);
- try testing.expectEqual(result.getCount(.blue), 12);
- }
+ /// Adds the key to the map with the supplied value.
+ /// If the key is already in the map, overwrites the value.
+ pub fn put(self: *Self, key: Key, value: Value) void {
+ const index = Indexer.indexOf(key);
+ self.bits.set(index);
+ self.values[index] = value;
+ }
- {
- const result = try r0_g1_b2.plus(ten_of_each);
- try testing.expectEqual(result.getCount(.red), 10);
- try testing.expectEqual(result.getCount(.green), 11);
- try testing.expectEqual(result.getCount(.blue), 12);
+ /// Adds the key to the map with an undefined value.
+ /// If the key is already in the map, the value becomes undefined.
+ /// A pointer to the value is returned, which should be
+ /// used to initialize the value.
+ pub fn putUninitialized(self: *Self, key: Key) *Value {
+ const index = Indexer.indexOf(key);
+ self.bits.set(index);
+ self.values[index] = undefined;
+ return &self.values[index];
+ }
- const full = EnumMultiset(Ball).initWithCount(std.math.maxInt(usize));
- try testing.expectError(error.Overflow, result.plus(full));
- }
+ /// Sets the value associated with the key in the map,
+ /// and returns the old value. If the key was not in
+ /// the map, returns null.
+ pub fn fetchPut(self: *Self, key: Key, value: Value) ?Value {
+ const index = Indexer.indexOf(key);
+ const result: ?Value = if (self.bits.isSet(index)) self.values[index] else null;
+ self.bits.set(index);
+ self.values[index] = value;
+ return result;
+ }
- {
- const result = ten_of_each.minus(r0_g1_b2);
- try testing.expectEqual(result.getCount(.red), 10);
- try testing.expectEqual(result.getCount(.green), 9);
- try testing.expectEqual(result.getCount(.blue), 8);
- }
+ /// Removes a key from the map. If the key was not in the map,
+ /// does nothing.
+ pub fn remove(self: *Self, key: Key) void {
+ const index = Indexer.indexOf(key);
+ self.bits.unset(index);
+ self.values[index] = undefined;
+ }
- {
- const result = ten_of_each.minus(r0_g1_b2).minus(ten_of_each);
- try testing.expectEqual(result.getCount(.red), 0);
- try testing.expectEqual(result.getCount(.green), 0);
- try testing.expectEqual(result.getCount(.blue), 0);
- }
+ /// Removes a key from the map, and returns the old value.
+ /// If the key was not in the map, returns null.
+ pub fn fetchRemove(self: *Self, key: Key) ?Value {
+ const index = Indexer.indexOf(key);
+ const result: ?Value = if (self.bits.isSet(index)) self.values[index] else null;
+ self.bits.unset(index);
+ self.values[index] = undefined;
+ return result;
+ }
- {
- var copy = empty;
- var it = copy.iterator();
- var entry = it.next().?;
- try testing.expectEqual(entry.key, .red);
- try testing.expectEqual(entry.value.*, 0);
- entry = it.next().?;
- try testing.expectEqual(entry.key, .green);
- try testing.expectEqual(entry.value.*, 0);
- entry = it.next().?;
- try testing.expectEqual(entry.key, .blue);
- try testing.expectEqual(entry.value.*, 0);
- try testing.expectEqual(it.next(), null);
- }
+ /// Returns an iterator over the map, which visits items in index order.
+ /// Modifications to the underlying map may or may not be observed by
+ /// the iterator, but will not invalidate it.
+ pub fn iterator(self: *Self) Iterator {
+ return .{
+ .inner = self.bits.iterator(.{}),
+ .values = &self.values,
+ };
+ }
- {
- var copy = r0_g1_b2;
- var it = copy.iterator();
- var entry = it.next().?;
- try testing.expectEqual(entry.key, .red);
- try testing.expectEqual(entry.value.*, 0);
- entry = it.next().?;
- try testing.expectEqual(entry.key, .green);
- try testing.expectEqual(entry.value.*, 1);
- entry = it.next().?;
- try testing.expectEqual(entry.key, .blue);
- try testing.expectEqual(entry.value.*, 2);
- try testing.expectEqual(it.next(), null);
- }
-}
+ /// An entry in the map.
+ pub const Entry = struct {
+ /// The key associated with this entry.
+ /// Modifying this key will not change the map.
+ key: Key,
-/// An array keyed by an enum, backed by a dense array.
-/// If the enum is not dense, a mapping will be constructed from
-/// enum values to dense indices. This type does no dynamic
-/// allocation and can be copied by value.
-pub fn EnumArray(comptime E: type, comptime V: type) type {
- const mixin = struct {
- fn EnumArrayExt(comptime Self: type) type {
- const Indexer = Self.Indexer;
- return struct {
- /// Initializes all values in the enum array
- pub fn init(init_values: EnumFieldStruct(E, V, @as(?V, null))) Self {
- return initDefault(@as(?V, null), init_values);
- }
+ /// A pointer to the value in the map associated
+ /// with this key. Modifications through this
+ /// pointer will modify the underlying data.
+ value: *Value,
+ };
+
+ pub const Iterator = struct {
+ inner: BitSet.Iterator(.{}),
+ values: *[Indexer.count]Value,
- /// Initializes values in the enum array, with the specified default.
- pub fn initDefault(comptime default: ?V, init_values: EnumFieldStruct(E, V, default)) Self {
- var result = Self{ .values = undefined };
- comptime var i: usize = 0;
- inline while (i < Self.len) : (i += 1) {
- const key = comptime Indexer.keyForIndex(i);
- const tag = @tagName(key);
- result.values[i] = @field(init_values, tag);
+ pub fn next(self: *Iterator) ?Entry {
+ return if (self.inner.next()) |index|
+ Entry{
+ .key = Indexer.keyForIndex(index),
+ .value = &self.values[index],
}
- return result;
- }
- };
- }
+ else
+ null;
+ }
+ };
};
- return IndexedArray(EnumIndexer(E), V, mixin.EnumArrayExt);
}
-fn NoExtension(comptime Self: type) type {
- _ = Self;
- return NoExt;
+/// A multiset of enum elements up to a count of usize. Backed
+/// by an EnumArray. This type does no dynamic allocation and can
+/// be copied by value.
+pub fn EnumMultiset(comptime E: type) type {
+ return BoundedEnumMultiset(E, usize);
}
-const NoExt = struct {};
-/// A set type with an Indexer mapping from keys to indices.
-/// Presence or absence is stored as a dense bitfield. This
-/// type does no allocation and can be copied by value.
-pub fn IndexedSet(comptime I: type, comptime Ext: ?fn (type) type) type {
- comptime ensureIndexer(I);
+/// A multiset of enum elements up to CountSize. Backed by an
+/// EnumArray. This type does no dynamic allocation and can be
+/// copied by value.
+pub fn BoundedEnumMultiset(comptime E: type, comptime CountSize: type) type {
return struct {
const Self = @This();
- pub usingnamespace (Ext orelse NoExtension)(Self);
-
- /// The indexing rules for converting between keys and indices.
- pub const Indexer = I;
- /// The element type for this set.
- pub const Key = Indexer.Key;
-
- const BitSet = std.StaticBitSet(Indexer.count);
-
- /// The maximum number of items in this set.
- pub const len = Indexer.count;
-
- bits: BitSet = BitSet.initEmpty(),
+ counts: EnumArray(E, CountSize),
- /// Returns a set containing no keys.
- pub fn initEmpty() Self {
- return .{ .bits = BitSet.initEmpty() };
+ /// Initializes the multiset using a struct of counts.
+ pub fn init(init_counts: EnumFieldStruct(E, CountSize, 0)) Self {
+ var self = initWithCount(0);
+ inline for (@typeInfo(E).Enum.fields) |field| {
+ const c = @field(init_counts, field.name);
+ const key = @as(E, @enumFromInt(field.value));
+ self.counts.set(key, c);
+ }
+ return self;
}
- /// Returns a set containing all possible keys.
- pub fn initFull() Self {
- return .{ .bits = BitSet.initFull() };
+ /// Initializes the multiset with a count of zero.
+ pub fn initEmpty() Self {
+ return initWithCount(0);
}
- /// Returns a set containing multiple keys.
- pub fn initMany(keys: []const Key) Self {
- var set = initEmpty();
- for (keys) |key| set.insert(key);
- return set;
+ /// Initializes the multiset with all keys at the
+ /// same count.
+ pub fn initWithCount(comptime c: CountSize) Self {
+ return .{
+ .counts = EnumArray(E, CountSize).initDefault(c, .{}),
+ };
}
- /// Returns a set containing a single key.
- pub fn initOne(key: Key) Self {
- return initMany(&[_]Key{key});
+ /// Returns the total number of key counts in the multiset.
+ pub fn count(self: Self) usize {
+ var sum: usize = 0;
+ for (self.counts.values) |c| {
+ sum += c;
+ }
+ return sum;
}
- /// Returns the number of keys in the set.
- pub fn count(self: Self) usize {
- return self.bits.count();
+ /// Checks if at least one key in multiset.
+ pub fn contains(self: Self, key: E) bool {
+ return self.counts.get(key) > 0;
}
- /// Checks if a key is in the set.
- pub fn contains(self: Self, key: Key) bool {
- return self.bits.isSet(Indexer.indexOf(key));
+ /// Removes all instance of a key from multiset. Same as
+ /// setCount(key, 0).
+ pub fn removeAll(self: *Self, key: E) void {
+ return self.counts.set(key, 0);
}
- /// Puts a key in the set.
- pub fn insert(self: *Self, key: Key) void {
- self.bits.set(Indexer.indexOf(key));
+ /// Increases the key count by given amount. Caller asserts
+ /// operation will not overflow.
+ pub fn addAssertSafe(self: *Self, key: E, c: CountSize) void {
+ self.counts.getPtr(key).* += c;
}
- /// Removes a key from the set.
- pub fn remove(self: *Self, key: Key) void {
- self.bits.unset(Indexer.indexOf(key));
+ /// Increases the key count by given amount.
+ pub fn add(self: *Self, key: E, c: CountSize) error{Overflow}!void {
+ self.counts.set(key, try std.math.add(CountSize, self.counts.get(key), c));
}
- /// Changes the presence of a key in the set to match the passed bool.
- pub fn setPresent(self: *Self, key: Key, present: bool) void {
- self.bits.setValue(Indexer.indexOf(key), present);
+ /// Decreases the key count by given amount. If amount is
+ /// greater than the number of keys in multset, then key count
+ /// will be set to zero.
+ pub fn remove(self: *Self, key: E, c: CountSize) void {
+ self.counts.getPtr(key).* -= @min(self.getCount(key), c);
}
- /// Toggles the presence of a key in the set. If the key is in
- /// the set, removes it. Otherwise adds it.
- pub fn toggle(self: *Self, key: Key) void {
- self.bits.toggle(Indexer.indexOf(key));
+ /// Returns the count for a key.
+ pub fn getCount(self: Self, key: E) CountSize {
+ return self.counts.get(key);
}
- /// Toggles the presence of all keys in the passed set.
- pub fn toggleSet(self: *Self, other: Self) void {
- self.bits.toggleSet(other.bits);
+ /// Set the count for a key.
+ pub fn setCount(self: *Self, key: E, c: CountSize) void {
+ self.counts.set(key, c);
}
- /// Toggles all possible keys in the set.
- pub fn toggleAll(self: *Self) void {
- self.bits.toggleAll();
+ /// Increases the all key counts by given multiset. Caller
+ /// asserts operation will not overflow any key.
+ pub fn addSetAssertSafe(self: *Self, other: Self) void {
+ inline for (@typeInfo(E).Enum.fields) |field| {
+ const key = @as(E, @enumFromInt(field.value));
+ self.addAssertSafe(key, other.getCount(key));
+ }
}
- /// Adds all keys in the passed set to this set.
- pub fn setUnion(self: *Self, other: Self) void {
- self.bits.setUnion(other.bits);
+ /// Increases the all key counts by given multiset.
+ pub fn addSet(self: *Self, other: Self) error{Overflow}!void {
+ inline for (@typeInfo(E).Enum.fields) |field| {
+ const key = @as(E, @enumFromInt(field.value));
+ try self.add(key, other.getCount(key));
+ }
}
- /// Removes all keys which are not in the passed set.
- pub fn setIntersection(self: *Self, other: Self) void {
- self.bits.setIntersection(other.bits);
+ /// Decreases the all key counts by given multiset. If
+ /// the given multiset has more key counts than this,
+ /// then that key will have a key count of zero.
+ pub fn removeSet(self: *Self, other: Self) void {
+ inline for (@typeInfo(E).Enum.fields) |field| {
+ const key = @as(E, @enumFromInt(field.value));
+ self.remove(key, other.getCount(key));
+ }
}
- /// Returns true iff both sets have the same keys.
+ /// Returns true iff all key counts are the same as
+ /// given multiset.
pub fn eql(self: Self, other: Self) bool {
- return self.bits.eql(other.bits);
+ inline for (@typeInfo(E).Enum.fields) |field| {
+ const key = @as(E, @enumFromInt(field.value));
+ if (self.getCount(key) != other.getCount(key)) {
+ return false;
+ }
+ }
+ return true;
}
- /// Returns true iff all the keys in this set are
- /// in the other set. The other set may have keys
- /// not found in this set.
+ /// Returns true iff all key counts less than or
+ /// equal to the given multiset.
pub fn subsetOf(self: Self, other: Self) bool {
- return self.bits.subsetOf(other.bits);
+ inline for (@typeInfo(E).Enum.fields) |field| {
+ const key = @as(E, @enumFromInt(field.value));
+ if (self.getCount(key) > other.getCount(key)) {
+ return false;
+ }
+ }
+ return true;
}
- /// Returns true iff this set contains all the keys
- /// in the other set. This set may have keys not
- /// found in the other set.
+ /// Returns true iff all key counts greater than or
+ /// equal to the given multiset.
pub fn supersetOf(self: Self, other: Self) bool {
- return self.bits.supersetOf(other.bits);
- }
-
- /// Returns a set with all the keys not in this set.
- pub fn complement(self: Self) Self {
- return .{ .bits = self.bits.complement() };
- }
-
- /// Returns a set with keys that are in either this
- /// set or the other set.
- pub fn unionWith(self: Self, other: Self) Self {
- return .{ .bits = self.bits.unionWith(other.bits) };
- }
-
- /// Returns a set with keys that are in both this
- /// set and the other set.
- pub fn intersectWith(self: Self, other: Self) Self {
- return .{ .bits = self.bits.intersectWith(other.bits) };
+ inline for (@typeInfo(E).Enum.fields) |field| {
+ const key = @as(E, @enumFromInt(field.value));
+ if (self.getCount(key) < other.getCount(key)) {
+ return false;
+ }
+ }
+ return true;
}
- /// Returns a set with keys that are in either this
- /// set or the other set, but not both.
- pub fn xorWith(self: Self, other: Self) Self {
- return .{ .bits = self.bits.xorWith(other.bits) };
+ /// Returns a multiset with the total key count of this
+ /// multiset and the other multiset. Caller asserts
+ /// operation will not overflow any key.
+ pub fn plusAssertSafe(self: Self, other: Self) Self {
+ var result = self;
+ result.addSetAssertSafe(other);
+ return result;
}
- /// Returns a set with keys that are in this set
- /// except for keys in the other set.
- pub fn differenceWith(self: Self, other: Self) Self {
- return .{ .bits = self.bits.differenceWith(other.bits) };
+ /// Returns a multiset with the total key count of this
+ /// multiset and the other multiset.
+ pub fn plus(self: Self, other: Self) error{Overflow}!Self {
+ var result = self;
+ try result.addSet(other);
+ return result;
}
- /// Returns an iterator over this set, which iterates in
- /// index order. Modifications to the set during iteration
- /// may or may not be observed by the iterator, but will
- /// not invalidate it.
- pub fn iterator(self: *const Self) Iterator {
- return .{ .inner = self.bits.iterator(.{}) };
+ /// Returns a multiset with the key count of this
+ /// multiset minus the corresponding key count in the
+ /// other multiset. If the other multiset contains
+ /// more key count than this set, that key will have
+ /// a count of zero.
+ pub fn minus(self: Self, other: Self) Self {
+ var result = self;
+ result.removeSet(other);
+ return result;
}
- pub const Iterator = struct {
- inner: BitSet.Iterator(.{}),
+ pub const Entry = EnumArray(E, CountSize).Entry;
+ pub const Iterator = EnumArray(E, CountSize).Iterator;
- pub fn next(self: *Iterator) ?Key {
- return if (self.inner.next()) |index|
- Indexer.keyForIndex(index)
- else
- null;
- }
- };
+ /// Returns an iterator over this multiset. Keys with zero
+ /// counts are included. Modifications to the set during
+ /// iteration may or may not be observed by the iterator,
+ /// but will not invalidate it.
+ pub fn iterator(self: *Self) Iterator {
+ return self.counts.iterator();
+ }
};
}
-test "pure EnumSet fns" {
- const Suit = enum { spades, hearts, clubs, diamonds };
-
- const empty = EnumSet(Suit).initEmpty();
- const full = EnumSet(Suit).initFull();
- const black = EnumSet(Suit).initMany(&[_]Suit{ .spades, .clubs });
- const red = EnumSet(Suit).initMany(&[_]Suit{ .hearts, .diamonds });
-
- try testing.expect(empty.eql(empty));
- try testing.expect(full.eql(full));
- try testing.expect(!empty.eql(full));
- try testing.expect(!full.eql(empty));
- try testing.expect(!empty.eql(black));
- try testing.expect(!full.eql(red));
- try testing.expect(!red.eql(empty));
- try testing.expect(!black.eql(full));
-
- try testing.expect(empty.subsetOf(empty));
- try testing.expect(empty.subsetOf(full));
- try testing.expect(full.subsetOf(full));
- try testing.expect(!black.subsetOf(red));
- try testing.expect(!red.subsetOf(black));
-
- try testing.expect(full.supersetOf(full));
- try testing.expect(full.supersetOf(empty));
- try testing.expect(empty.supersetOf(empty));
- try testing.expect(!black.supersetOf(red));
- try testing.expect(!red.supersetOf(black));
-
- try testing.expect(empty.complement().eql(full));
- try testing.expect(full.complement().eql(empty));
- try testing.expect(black.complement().eql(red));
- try testing.expect(red.complement().eql(black));
+test EnumMultiset {
+ const Ball = enum { red, green, blue };
- try testing.expect(empty.unionWith(empty).eql(empty));
- try testing.expect(empty.unionWith(full).eql(full));
- try testing.expect(full.unionWith(full).eql(full));
- try testing.expect(full.unionWith(empty).eql(full));
- try testing.expect(black.unionWith(red).eql(full));
- try testing.expect(red.unionWith(black).eql(full));
+ const empty = EnumMultiset(Ball).initEmpty();
+ const r0_g1_b2 = EnumMultiset(Ball).init(.{
+ .red = 0,
+ .green = 1,
+ .blue = 2,
+ });
+ const ten_of_each = EnumMultiset(Ball).initWithCount(10);
- try testing.expect(empty.intersectWith(empty).eql(empty));
- try testing.expect(empty.intersectWith(full).eql(empty));
- try testing.expect(full.intersectWith(full).eql(full));
- try testing.expect(full.intersectWith(empty).eql(empty));
- try testing.expect(black.intersectWith(red).eql(empty));
- try testing.expect(red.intersectWith(black).eql(empty));
+ try testing.expectEqual(empty.count(), 0);
+ try testing.expectEqual(r0_g1_b2.count(), 3);
+ try testing.expectEqual(ten_of_each.count(), 30);
- try testing.expect(empty.xorWith(empty).eql(empty));
- try testing.expect(empty.xorWith(full).eql(full));
- try testing.expect(full.xorWith(full).eql(empty));
- try testing.expect(full.xorWith(empty).eql(full));
- try testing.expect(black.xorWith(red).eql(full));
- try testing.expect(red.xorWith(black).eql(full));
+ try testing.expect(!empty.contains(.red));
+ try testing.expect(!empty.contains(.green));
+ try testing.expect(!empty.contains(.blue));
- try testing.expect(empty.differenceWith(empty).eql(empty));
- try testing.expect(empty.differenceWith(full).eql(empty));
- try testing.expect(full.differenceWith(full).eql(empty));
- try testing.expect(full.differenceWith(empty).eql(full));
- try testing.expect(full.differenceWith(red).eql(black));
- try testing.expect(full.differenceWith(black).eql(red));
-}
+ try testing.expect(!r0_g1_b2.contains(.red));
+ try testing.expect(r0_g1_b2.contains(.green));
+ try testing.expect(r0_g1_b2.contains(.blue));
-test "EnumSet empty" {
- const E = enum {};
- const empty = EnumSet(E).initEmpty();
- const full = EnumSet(E).initFull();
+ try testing.expect(ten_of_each.contains(.red));
+ try testing.expect(ten_of_each.contains(.green));
+ try testing.expect(ten_of_each.contains(.blue));
- try std.testing.expect(empty.eql(full));
- try std.testing.expect(empty.complement().eql(full));
- try std.testing.expect(empty.complement().eql(full.complement()));
- try std.testing.expect(empty.eql(full.complement()));
-}
+ {
+ var copy = ten_of_each;
+ copy.removeAll(.red);
+ try testing.expect(!copy.contains(.red));
-test "EnumSet const iterator" {
- const Direction = enum { up, down, left, right };
- const diag_move = init: {
- var move = EnumSet(Direction).initEmpty();
- move.insert(.right);
- move.insert(.up);
- break :init move;
- };
+ // removeAll second time does nothing
+ copy.removeAll(.red);
+ try testing.expect(!copy.contains(.red));
+ }
- var result = EnumSet(Direction).initEmpty();
- var it = diag_move.iterator();
- while (it.next()) |dir| {
- result.insert(dir);
+ {
+ var copy = ten_of_each;
+ copy.addAssertSafe(.red, 6);
+ try testing.expectEqual(copy.getCount(.red), 16);
}
- try testing.expect(result.eql(diag_move));
-}
+ {
+ var copy = ten_of_each;
+ try copy.add(.red, 6);
+ try testing.expectEqual(copy.getCount(.red), 16);
-/// A map from keys to values, using an index lookup. Uses a
-/// bitfield to track presence and a dense array of values.
-/// This type does no allocation and can be copied by value.
-pub fn IndexedMap(comptime I: type, comptime V: type, comptime Ext: ?fn (type) type) type {
- comptime ensureIndexer(I);
- return struct {
- const Self = @This();
+ try testing.expectError(error.Overflow, copy.add(.red, std.math.maxInt(usize)));
+ }
- pub usingnamespace (Ext orelse NoExtension)(Self);
+ {
+ var copy = ten_of_each;
+ copy.remove(.red, 4);
+ try testing.expectEqual(copy.getCount(.red), 6);
- /// The index mapping for this map
- pub const Indexer = I;
- /// The key type used to index this map
- pub const Key = Indexer.Key;
- /// The value type stored in this map
- pub const Value = V;
- /// The number of possible keys in the map
- pub const len = Indexer.count;
+ // subtracting more it contains does not underflow
+ copy.remove(.green, 14);
+ try testing.expectEqual(copy.getCount(.green), 0);
+ }
- const BitSet = std.StaticBitSet(Indexer.count);
+ try testing.expectEqual(empty.getCount(.green), 0);
+ try testing.expectEqual(r0_g1_b2.getCount(.green), 1);
+ try testing.expectEqual(ten_of_each.getCount(.green), 10);
- /// Bits determining whether items are in the map
- bits: BitSet = BitSet.initEmpty(),
- /// Values of items in the map. If the associated
- /// bit is zero, the value is undefined.
- values: [Indexer.count]Value = undefined,
+ {
+ var copy = empty;
+ copy.setCount(.red, 6);
+ try testing.expectEqual(copy.getCount(.red), 6);
+ }
- /// The number of items in the map.
- pub fn count(self: Self) usize {
- return self.bits.count();
- }
+ {
+ var copy = r0_g1_b2;
+ copy.addSetAssertSafe(ten_of_each);
+ try testing.expectEqual(copy.getCount(.red), 10);
+ try testing.expectEqual(copy.getCount(.green), 11);
+ try testing.expectEqual(copy.getCount(.blue), 12);
+ }
- /// Checks if the map contains an item.
- pub fn contains(self: Self, key: Key) bool {
- return self.bits.isSet(Indexer.indexOf(key));
- }
+ {
+ var copy = r0_g1_b2;
+ try copy.addSet(ten_of_each);
+ try testing.expectEqual(copy.getCount(.red), 10);
+ try testing.expectEqual(copy.getCount(.green), 11);
+ try testing.expectEqual(copy.getCount(.blue), 12);
- /// Gets the value associated with a key.
- /// If the key is not in the map, returns null.
- pub fn get(self: Self, key: Key) ?Value {
- const index = Indexer.indexOf(key);
- return if (self.bits.isSet(index)) self.values[index] else null;
- }
+ const full = EnumMultiset(Ball).initWithCount(std.math.maxInt(usize));
+ try testing.expectError(error.Overflow, copy.addSet(full));
+ }
- /// Gets the value associated with a key, which must
- /// exist in the map.
- pub fn getAssertContains(self: Self, key: Key) Value {
- const index = Indexer.indexOf(key);
- assert(self.bits.isSet(index));
- return self.values[index];
- }
+ {
+ var copy = ten_of_each;
+ copy.removeSet(r0_g1_b2);
+ try testing.expectEqual(copy.getCount(.red), 10);
+ try testing.expectEqual(copy.getCount(.green), 9);
+ try testing.expectEqual(copy.getCount(.blue), 8);
- /// Gets the address of the value associated with a key.
- /// If the key is not in the map, returns null.
- pub fn getPtr(self: *Self, key: Key) ?*Value {
- const index = Indexer.indexOf(key);
- return if (self.bits.isSet(index)) &self.values[index] else null;
- }
+ copy.removeSet(ten_of_each);
+ try testing.expectEqual(copy.getCount(.red), 0);
+ try testing.expectEqual(copy.getCount(.green), 0);
+ try testing.expectEqual(copy.getCount(.blue), 0);
+ }
- /// Gets the address of the const value associated with a key.
- /// If the key is not in the map, returns null.
- pub fn getPtrConst(self: *const Self, key: Key) ?*const Value {
- const index = Indexer.indexOf(key);
- return if (self.bits.isSet(index)) &self.values[index] else null;
- }
+ try testing.expect(empty.eql(empty));
+ try testing.expect(r0_g1_b2.eql(r0_g1_b2));
+ try testing.expect(ten_of_each.eql(ten_of_each));
+ try testing.expect(!empty.eql(r0_g1_b2));
+ try testing.expect(!r0_g1_b2.eql(ten_of_each));
+ try testing.expect(!ten_of_each.eql(empty));
- /// Gets the address of the value associated with a key.
- /// The key must be present in the map.
- pub fn getPtrAssertContains(self: *Self, key: Key) *Value {
- const index = Indexer.indexOf(key);
- assert(self.bits.isSet(index));
- return &self.values[index];
- }
+ try testing.expect(empty.subsetOf(empty));
+ try testing.expect(r0_g1_b2.subsetOf(r0_g1_b2));
+ try testing.expect(empty.subsetOf(r0_g1_b2));
+ try testing.expect(r0_g1_b2.subsetOf(ten_of_each));
+ try testing.expect(!ten_of_each.subsetOf(r0_g1_b2));
+ try testing.expect(!r0_g1_b2.subsetOf(empty));
- /// Gets the address of the const value associated with a key.
- /// The key must be present in the map.
- pub fn getPtrConstAssertContains(self: *const Self, key: Key) *const Value {
- const index = Indexer.indexOf(key);
- assert(self.bits.isSet(index));
- return &self.values[index];
- }
+ try testing.expect(empty.supersetOf(empty));
+ try testing.expect(r0_g1_b2.supersetOf(r0_g1_b2));
+ try testing.expect(r0_g1_b2.supersetOf(empty));
+ try testing.expect(ten_of_each.supersetOf(r0_g1_b2));
+ try testing.expect(!r0_g1_b2.supersetOf(ten_of_each));
+ try testing.expect(!empty.supersetOf(r0_g1_b2));
- /// Adds the key to the map with the supplied value.
- /// If the key is already in the map, overwrites the value.
- pub fn put(self: *Self, key: Key, value: Value) void {
- const index = Indexer.indexOf(key);
- self.bits.set(index);
- self.values[index] = value;
- }
+ {
+ // with multisets it could be the case where two
+ // multisets are neither subset nor superset of each
+ // other.
- /// Adds the key to the map with an undefined value.
- /// If the key is already in the map, the value becomes undefined.
- /// A pointer to the value is returned, which should be
- /// used to initialize the value.
- pub fn putUninitialized(self: *Self, key: Key) *Value {
- const index = Indexer.indexOf(key);
- self.bits.set(index);
- self.values[index] = undefined;
- return &self.values[index];
- }
+ const r10 = EnumMultiset(Ball).init(.{
+ .red = 10,
+ });
+ const b10 = EnumMultiset(Ball).init(.{
+ .blue = 10,
+ });
- /// Sets the value associated with the key in the map,
- /// and returns the old value. If the key was not in
- /// the map, returns null.
- pub fn fetchPut(self: *Self, key: Key, value: Value) ?Value {
- const index = Indexer.indexOf(key);
- const result: ?Value = if (self.bits.isSet(index)) self.values[index] else null;
- self.bits.set(index);
- self.values[index] = value;
- return result;
- }
+ try testing.expect(!r10.subsetOf(b10));
+ try testing.expect(!b10.subsetOf(r10));
+ try testing.expect(!r10.supersetOf(b10));
+ try testing.expect(!b10.supersetOf(r10));
+ }
- /// Removes a key from the map. If the key was not in the map,
- /// does nothing.
- pub fn remove(self: *Self, key: Key) void {
- const index = Indexer.indexOf(key);
- self.bits.unset(index);
- self.values[index] = undefined;
- }
+ {
+ const result = r0_g1_b2.plusAssertSafe(ten_of_each);
+ try testing.expectEqual(result.getCount(.red), 10);
+ try testing.expectEqual(result.getCount(.green), 11);
+ try testing.expectEqual(result.getCount(.blue), 12);
+ }
- /// Removes a key from the map, and returns the old value.
- /// If the key was not in the map, returns null.
- pub fn fetchRemove(self: *Self, key: Key) ?Value {
- const index = Indexer.indexOf(key);
- const result: ?Value = if (self.bits.isSet(index)) self.values[index] else null;
- self.bits.unset(index);
- self.values[index] = undefined;
- return result;
- }
+ {
+ const result = try r0_g1_b2.plus(ten_of_each);
+ try testing.expectEqual(result.getCount(.red), 10);
+ try testing.expectEqual(result.getCount(.green), 11);
+ try testing.expectEqual(result.getCount(.blue), 12);
- /// Returns an iterator over the map, which visits items in index order.
- /// Modifications to the underlying map may or may not be observed by
- /// the iterator, but will not invalidate it.
- pub fn iterator(self: *Self) Iterator {
- return .{
- .inner = self.bits.iterator(.{}),
- .values = &self.values,
- };
- }
+ const full = EnumMultiset(Ball).initWithCount(std.math.maxInt(usize));
+ try testing.expectError(error.Overflow, result.plus(full));
+ }
- /// An entry in the map.
- pub const Entry = struct {
- /// The key associated with this entry.
- /// Modifying this key will not change the map.
- key: Key,
+ {
+ const result = ten_of_each.minus(r0_g1_b2);
+ try testing.expectEqual(result.getCount(.red), 10);
+ try testing.expectEqual(result.getCount(.green), 9);
+ try testing.expectEqual(result.getCount(.blue), 8);
+ }
- /// A pointer to the value in the map associated
- /// with this key. Modifications through this
- /// pointer will modify the underlying data.
- value: *Value,
- };
+ {
+ const result = ten_of_each.minus(r0_g1_b2).minus(ten_of_each);
+ try testing.expectEqual(result.getCount(.red), 0);
+ try testing.expectEqual(result.getCount(.green), 0);
+ try testing.expectEqual(result.getCount(.blue), 0);
+ }
- pub const Iterator = struct {
- inner: BitSet.Iterator(.{}),
- values: *[Indexer.count]Value,
+ {
+ var copy = empty;
+ var it = copy.iterator();
+ var entry = it.next().?;
+ try testing.expectEqual(entry.key, .red);
+ try testing.expectEqual(entry.value.*, 0);
+ entry = it.next().?;
+ try testing.expectEqual(entry.key, .green);
+ try testing.expectEqual(entry.value.*, 0);
+ entry = it.next().?;
+ try testing.expectEqual(entry.key, .blue);
+ try testing.expectEqual(entry.value.*, 0);
+ try testing.expectEqual(it.next(), null);
+ }
- pub fn next(self: *Iterator) ?Entry {
- return if (self.inner.next()) |index|
- Entry{
- .key = Indexer.keyForIndex(index),
- .value = &self.values[index],
- }
- else
- null;
- }
- };
- };
+ {
+ var copy = r0_g1_b2;
+ var it = copy.iterator();
+ var entry = it.next().?;
+ try testing.expectEqual(entry.key, .red);
+ try testing.expectEqual(entry.value.*, 0);
+ entry = it.next().?;
+ try testing.expectEqual(entry.key, .green);
+ try testing.expectEqual(entry.value.*, 1);
+ entry = it.next().?;
+ try testing.expectEqual(entry.key, .blue);
+ try testing.expectEqual(entry.value.*, 2);
+ try testing.expectEqual(it.next(), null);
+ }
}
-/// A dense array of values, using an indexed lookup.
-/// This type does no allocation and can be copied by value.
-pub fn IndexedArray(comptime I: type, comptime V: type, comptime Ext: ?fn (type) type) type {
- comptime ensureIndexer(I);
+/// An array keyed by an enum, backed by a dense array.
+/// If the enum is not dense, a mapping will be constructed from
+/// enum values to dense indices. This type does no dynamic
+/// allocation and can be copied by value.
+pub fn EnumArray(comptime E: type, comptime V: type) type {
return struct {
const Self = @This();
- pub usingnamespace (Ext orelse NoExtension)(Self);
-
/// The index mapping for this map
- pub const Indexer = I;
+ pub const Indexer = EnumIndexer(E);
/// The key type used to index this map
pub const Key = Indexer.Key;
/// The value type stored in this map
@@ -1201,6 +1038,21 @@ pub fn IndexedArray(comptime I: type, comptime V: type, comptime Ext: ?fn (type)
values: [Indexer.count]Value,
+ pub fn init(init_values: EnumFieldStruct(E, Value, null)) Self {
+ return initDefault(null, init_values);
+ }
+
+ /// Initializes values in the enum array, with the specified default.
+ pub fn initDefault(comptime default: ?Value, init_values: EnumFieldStruct(E, Value, default)) Self {
+ var result: Self = .{ .values = undefined };
+ inline for (0..Self.len) |i| {
+ const key = comptime Indexer.keyForIndex(i);
+ const tag = @tagName(key);
+ result.values[i] = @field(init_values, tag);
+ }
+ return result;
+ }
+
pub fn initUndefined() Self {
return Self{ .values = undefined };
}
@@ -1269,46 +1121,96 @@ pub fn IndexedArray(comptime I: type, comptime V: type, comptime Ext: ?fn (type)
};
}
-/// Verifies that a type is a valid Indexer, providing a helpful
-/// compile error if not. An Indexer maps a comptime-known set
-/// of keys to a dense set of zero-based indices.
-/// The indexer interface must look like this:
-/// ```
-/// struct {
-/// /// The key type which this indexer converts to indices
-/// pub const Key: type,
-/// /// The number of indexes in the dense mapping
-/// pub const count: comptime_int,
-/// /// Converts from a key to an index
-/// pub fn indexOf(Key) usize;
-/// /// Converts from an index to a key
-/// pub fn keyForIndex(usize) Key;
-/// }
-/// ```
-pub fn ensureIndexer(comptime T: type) void {
- comptime {
- if (!@hasDecl(T, "Key")) @compileError("Indexer must have decl Key: type.");
- if (@TypeOf(T.Key) != type) @compileError("Indexer.Key must be a type.");
- if (!@hasDecl(T, "count")) @compileError("Indexer must have decl count: comptime_int.");
- if (@TypeOf(T.count) != comptime_int) @compileError("Indexer.count must be a comptime_int.");
- if (!@hasDecl(T, "indexOf")) @compileError("Indexer.indexOf must be a fn (Key) usize.");
- if (@TypeOf(T.indexOf) != fn (T.Key) usize) @compileError("Indexer must have decl indexOf: fn (Key) usize.");
- if (!@hasDecl(T, "keyForIndex")) @compileError("Indexer must have decl keyForIndex: fn (usize) Key.");
- if (@TypeOf(T.keyForIndex) != fn (usize) T.Key) @compileError("Indexer.keyForIndex must be a fn (usize) Key.");
- }
+test "pure EnumSet fns" {
+ const Suit = enum { spades, hearts, clubs, diamonds };
+
+ const empty = EnumSet(Suit).initEmpty();
+ const full = EnumSet(Suit).initFull();
+ const black = EnumSet(Suit).initMany(&[_]Suit{ .spades, .clubs });
+ const red = EnumSet(Suit).initMany(&[_]Suit{ .hearts, .diamonds });
+
+ try testing.expect(empty.eql(empty));
+ try testing.expect(full.eql(full));
+ try testing.expect(!empty.eql(full));
+ try testing.expect(!full.eql(empty));
+ try testing.expect(!empty.eql(black));
+ try testing.expect(!full.eql(red));
+ try testing.expect(!red.eql(empty));
+ try testing.expect(!black.eql(full));
+
+ try testing.expect(empty.subsetOf(empty));
+ try testing.expect(empty.subsetOf(full));
+ try testing.expect(full.subsetOf(full));
+ try testing.expect(!black.subsetOf(red));
+ try testing.expect(!red.subsetOf(black));
+
+ try testing.expect(full.supersetOf(full));
+ try testing.expect(full.supersetOf(empty));
+ try testing.expect(empty.supersetOf(empty));
+ try testing.expect(!black.supersetOf(red));
+ try testing.expect(!red.supersetOf(black));
+
+ try testing.expect(empty.complement().eql(full));
+ try testing.expect(full.complement().eql(empty));
+ try testing.expect(black.complement().eql(red));
+ try testing.expect(red.complement().eql(black));
+
+ try testing.expect(empty.unionWith(empty).eql(empty));
+ try testing.expect(empty.unionWith(full).eql(full));
+ try testing.expect(full.unionWith(full).eql(full));
+ try testing.expect(full.unionWith(empty).eql(full));
+ try testing.expect(black.unionWith(red).eql(full));
+ try testing.expect(red.unionWith(black).eql(full));
+
+ try testing.expect(empty.intersectWith(empty).eql(empty));
+ try testing.expect(empty.intersectWith(full).eql(empty));
+ try testing.expect(full.intersectWith(full).eql(full));
+ try testing.expect(full.intersectWith(empty).eql(empty));
+ try testing.expect(black.intersectWith(red).eql(empty));
+ try testing.expect(red.intersectWith(black).eql(empty));
+
+ try testing.expect(empty.xorWith(empty).eql(empty));
+ try testing.expect(empty.xorWith(full).eql(full));
+ try testing.expect(full.xorWith(full).eql(empty));
+ try testing.expect(full.xorWith(empty).eql(full));
+ try testing.expect(black.xorWith(red).eql(full));
+ try testing.expect(red.xorWith(black).eql(full));
+
+ try testing.expect(empty.differenceWith(empty).eql(empty));
+ try testing.expect(empty.differenceWith(full).eql(empty));
+ try testing.expect(full.differenceWith(full).eql(empty));
+ try testing.expect(full.differenceWith(empty).eql(full));
+ try testing.expect(full.differenceWith(red).eql(black));
+ try testing.expect(full.differenceWith(black).eql(red));
}
-test ensureIndexer {
- ensureIndexer(struct {
- pub const Key = u32;
- pub const count: comptime_int = 8;
- pub fn indexOf(k: Key) usize {
- return @as(usize, @intCast(k));
- }
- pub fn keyForIndex(index: usize) Key {
- return @as(Key, @intCast(index));
- }
- });
+test "EnumSet empty" {
+ const E = enum {};
+ const empty = EnumSet(E).initEmpty();
+ const full = EnumSet(E).initFull();
+
+ try std.testing.expect(empty.eql(full));
+ try std.testing.expect(empty.complement().eql(full));
+ try std.testing.expect(empty.complement().eql(full.complement()));
+ try std.testing.expect(empty.eql(full.complement()));
+}
+
+test "EnumSet const iterator" {
+ const Direction = enum { up, down, left, right };
+ const diag_move = init: {
+ var move = EnumSet(Direction).initEmpty();
+ move.insert(.right);
+ move.insert(.up);
+ break :init move;
+ };
+
+ var result = EnumSet(Direction).initEmpty();
+ var it = diag_move.iterator();
+ while (it.next()) |dir| {
+ result.insert(dir);
+ }
+
+ try testing.expect(result.eql(diag_move));
}
pub fn EnumIndexer(comptime E: type) type {
@@ -1438,7 +1340,6 @@ test "EnumIndexer non-exhaustive" {
_,
};
const Indexer = EnumIndexer(E);
- ensureIndexer(Indexer);
const min_tag: E = @enumFromInt(std.math.minInt(BackingInt));
const max_tag: E = @enumFromInt(std.math.maxInt(BackingInt));
@@ -1466,7 +1367,6 @@ test "EnumIndexer non-exhaustive" {
test "EnumIndexer dense zeroed" {
const E = enum(u2) { b = 1, a = 0, c = 2 };
const Indexer = EnumIndexer(E);
- ensureIndexer(Indexer);
try testing.expectEqual(E, Indexer.Key);
try testing.expectEqual(3, Indexer.count);
@@ -1482,7 +1382,6 @@ test "EnumIndexer dense zeroed" {
test "EnumIndexer dense positive" {
const E = enum(u4) { c = 6, a = 4, b = 5 };
const Indexer = EnumIndexer(E);
- ensureIndexer(Indexer);
try testing.expectEqual(E, Indexer.Key);
try testing.expectEqual(3, Indexer.count);
@@ -1498,7 +1397,6 @@ test "EnumIndexer dense positive" {
test "EnumIndexer dense negative" {
const E = enum(i4) { a = -6, c = -4, b = -5 };
const Indexer = EnumIndexer(E);
- ensureIndexer(Indexer);
try testing.expectEqual(E, Indexer.Key);
try testing.expectEqual(3, Indexer.count);
@@ -1514,7 +1412,6 @@ test "EnumIndexer dense negative" {
test "EnumIndexer sparse" {
const E = enum(i4) { a = -2, c = 6, b = 4 };
const Indexer = EnumIndexer(E);
- ensureIndexer(Indexer);
try testing.expectEqual(E, Indexer.Key);
try testing.expectEqual(3, Indexer.count);
@@ -1530,7 +1427,6 @@ test "EnumIndexer sparse" {
test "EnumIndexer empty" {
const E = enum {};
const Indexer = EnumIndexer(E);
- ensureIndexer(Indexer);
try testing.expectEqual(E, Indexer.Key);
try testing.expectEqual(0, Indexer.count);
}