structstd.enums[src]

This module contains utilities and data structures for working with enums.

Types

Type FunctionEnumFieldStruct[src]

Returns a struct with a field matching each unique named enum element. If the enum is extern and has multiple names for the same value, only the first name is used. Each field is of type Data and has the provided default, which may be undefined.

Parameters

E: type
Data: type
field_default: ?Data

Source Code

Source code
pub fn EnumFieldStruct(comptime E: type, comptime Data: type, comptime field_default: ?Data) type {
    @setEvalBranchQuota(@typeInfo(E).@"enum".fields.len + eval_branch_quota_cushion);
    var struct_fields: [@typeInfo(E).@"enum".fields.len]std.builtin.Type.StructField = undefined;
    for (&struct_fields, @typeInfo(E).@"enum".fields) |*struct_field, enum_field| {
        struct_field.* = .{
            .name = enum_field.name ++ "",
            .type = Data,
            .default_value_ptr = if (field_default) |d| @as(?*const anyopaque, @ptrCast(&d)) else null,
            .is_comptime = false,
            .alignment = if (@sizeOf(Data) > 0) @alignOf(Data) else 0,
        };
    }
    return @Type(.{ .@"struct" = .{
        .layout = .auto,
        .fields = &struct_fields,
        .decls = &.{},
        .is_tuple = false,
    } });
}

Type FunctionEnumSet[src]

A set of enum elements, backed by a bitfield. If the enum is exhaustive but 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.

Parameters

E: type

Types

TypeIndexer[src]

The indexing rules for converting between keys and indices.

Source Code

Source code
pub const Indexer = EnumIndexer(E)

Fields

Values

ConstantKey[src]

The element type for this set.

Source Code

Source code
pub const Key = Indexer.Key

Constantlen[src]

The maximum number of items in this set.

Source Code

Source code
pub const len = Indexer.count

Functions

Functioninit[src]

pub fn init(init_values: EnumFieldStruct(E, bool, false)) Self

Initializes the set using a struct of bools

Parameters

init_values: EnumFieldStruct(E, bool, false)

Source Code

Source code
pub fn init(init_values: EnumFieldStruct(E, bool, false)) Self {
    @setEvalBranchQuota(2 * @typeInfo(E).@"enum".fields.len);
    var result: Self = .{};
    if (@typeInfo(E).@"enum".is_exhaustive) {
        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);
            }
        }
    } else {
        inline for (std.meta.fields(E)) |field| {
            const key = @field(E, field.name);
            if (@field(init_values, field.name)) {
                const i = comptime Indexer.indexOf(key);
                result.bits.set(i);
            }
        }
    }
    return result;
}

FunctioninitEmpty[src]

pub fn initEmpty() Self

Returns a set containing no keys.

Source Code

Source code
pub fn initEmpty() Self {
    return .{ .bits = BitSet.initEmpty() };
}

FunctioninitFull[src]

pub fn initFull() Self

Returns a set containing all possible keys.

Source Code

Source code
pub fn initFull() Self {
    return .{ .bits = BitSet.initFull() };
}

FunctioninitMany[src]

pub fn initMany(keys: []const Key) Self

Returns a set containing multiple keys.

Parameters

keys: []const Key

Source Code

Source code
pub fn initMany(keys: []const Key) Self {
    var set = initEmpty();
    for (keys) |key| set.insert(key);
    return set;
}

FunctioninitOne[src]

pub fn initOne(key: Key) Self

Returns a set containing a single key.

Parameters

key: Key

Source Code

Source code
pub fn initOne(key: Key) Self {
    return initMany(&[_]Key{key});
}

Functioncount[src]

pub fn count(self: Self) usize

Returns the number of keys in the set.

Parameters

self: Self

Source Code

Source code
pub fn count(self: Self) usize {
    return self.bits.count();
}

Functioncontains[src]

pub fn contains(self: Self, key: Key) bool

Checks if a key is in the set.

Parameters

self: Self
key: Key

Source Code

Source code
pub fn contains(self: Self, key: Key) bool {
    return self.bits.isSet(Indexer.indexOf(key));
}

Functioninsert[src]

pub fn insert(self: *Self, key: Key) void

Puts a key in the set.

Parameters

self: *Self
key: Key

Source Code

Source code
pub fn insert(self: *Self, key: Key) void {
    self.bits.set(Indexer.indexOf(key));
}

Functionremove[src]

pub fn remove(self: *Self, key: Key) void

Removes a key from the set.

Parameters

self: *Self
key: Key

Source Code

Source code
pub fn remove(self: *Self, key: Key) void {
    self.bits.unset(Indexer.indexOf(key));
}

FunctionsetPresent[src]

pub fn setPresent(self: *Self, key: Key, present: bool) void

Changes the presence of a key in the set to match the passed bool.

Parameters

self: *Self
key: Key
present: bool

Source Code

Source code
pub fn setPresent(self: *Self, key: Key, present: bool) void {
    self.bits.setValue(Indexer.indexOf(key), present);
}

Functiontoggle[src]

pub fn toggle(self: *Self, key: Key) void

Toggles the presence of a key in the set. If the key is in the set, removes it. Otherwise adds it.

Parameters

self: *Self
key: Key

Source Code

Source code
pub fn toggle(self: *Self, key: Key) void {
    self.bits.toggle(Indexer.indexOf(key));
}

FunctiontoggleSet[src]

pub fn toggleSet(self: *Self, other: Self) void

Toggles the presence of all keys in the passed set.

Parameters

self: *Self
other: Self

Source Code

Source code
pub fn toggleSet(self: *Self, other: Self) void {
    self.bits.toggleSet(other.bits);
}

FunctiontoggleAll[src]

pub fn toggleAll(self: *Self) void

Toggles all possible keys in the set.

Parameters

self: *Self

Source Code

Source code
pub fn toggleAll(self: *Self) void {
    self.bits.toggleAll();
}

FunctionsetUnion[src]

pub fn setUnion(self: *Self, other: Self) void

Adds all keys in the passed set to this set.

Parameters

self: *Self
other: Self

Source Code

Source code
pub fn setUnion(self: *Self, other: Self) void {
    self.bits.setUnion(other.bits);
}

FunctionsetIntersection[src]

pub fn setIntersection(self: *Self, other: Self) void

Removes all keys which are not in the passed set.

Parameters

self: *Self
other: Self

Source Code

Source code
pub fn setIntersection(self: *Self, other: Self) void {
    self.bits.setIntersection(other.bits);
}

Functioneql[src]

pub fn eql(self: Self, other: Self) bool

Returns true iff both sets have the same keys.

Parameters

self: Self
other: Self

Source Code

Source code
pub fn eql(self: Self, other: Self) bool {
    return self.bits.eql(other.bits);
}

FunctionsubsetOf[src]

pub fn subsetOf(self: Self, other: Self) bool

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.

Parameters

self: Self
other: Self

Source Code

Source code
pub fn subsetOf(self: Self, other: Self) bool {
    return self.bits.subsetOf(other.bits);
}

FunctionsupersetOf[src]

pub fn supersetOf(self: Self, other: Self) bool

Returns true iff this set contains all the keys in the other set. This set may have keys not found in the other set.

Parameters

self: Self
other: Self

Source Code

Source code
pub fn supersetOf(self: Self, other: Self) bool {
    return self.bits.supersetOf(other.bits);
}

Functioncomplement[src]

pub fn complement(self: Self) Self

Returns a set with all the keys not in this set.

Parameters

self: Self

Source Code

Source code
pub fn complement(self: Self) Self {
    return .{ .bits = self.bits.complement() };
}

FunctionunionWith[src]

pub fn unionWith(self: Self, other: Self) Self

Returns a set with keys that are in either this set or the other set.

Parameters

self: Self
other: Self

Source Code

Source code
pub fn unionWith(self: Self, other: Self) Self {
    return .{ .bits = self.bits.unionWith(other.bits) };
}

FunctionintersectWith[src]

pub fn intersectWith(self: Self, other: Self) Self

Returns a set with keys that are in both this set and the other set.

Parameters

self: Self
other: Self

Source Code

Source code
pub fn intersectWith(self: Self, other: Self) Self {
    return .{ .bits = self.bits.intersectWith(other.bits) };
}

FunctionxorWith[src]

pub fn xorWith(self: Self, other: Self) Self

Returns a set with keys that are in either this set or the other set, but not both.

Parameters

self: Self
other: Self

Source Code

Source code
pub fn xorWith(self: Self, other: Self) Self {
    return .{ .bits = self.bits.xorWith(other.bits) };
}

FunctiondifferenceWith[src]

pub fn differenceWith(self: Self, other: Self) Self

Returns a set with keys that are in this set except for keys in the other set.

Parameters

self: Self
other: Self

Source Code

Source code
pub fn differenceWith(self: Self, other: Self) Self {
    return .{ .bits = self.bits.differenceWith(other.bits) };
}

Functioniterator[src]

pub fn iterator(self: *const Self) Iterator

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.

Parameters

self: *const Self

Source Code

Source code
pub fn iterator(self: *const Self) Iterator {
    return .{ .inner = self.bits.iterator(.{}) };
}

Source Code

Source code
pub fn EnumSet(comptime E: type) type {
    return struct {
        const Self = @This();

        /// 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;

        const BitSet = std.StaticBitSet(Indexer.count);

        /// The maximum number of items in this set.
        pub const len = Indexer.count;

        bits: BitSet = BitSet.initEmpty(),

        /// Initializes the set using a struct of bools
        pub fn init(init_values: EnumFieldStruct(E, bool, false)) Self {
            @setEvalBranchQuota(2 * @typeInfo(E).@"enum".fields.len);
            var result: Self = .{};
            if (@typeInfo(E).@"enum".is_exhaustive) {
                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);
                    }
                }
            } else {
                inline for (std.meta.fields(E)) |field| {
                    const key = @field(E, field.name);
                    if (@field(init_values, field.name)) {
                        const i = comptime Indexer.indexOf(key);
                        result.bits.set(i);
                    }
                }
            }
            return result;
        }

        /// Returns a set containing no keys.
        pub fn initEmpty() Self {
            return .{ .bits = BitSet.initEmpty() };
        }

        /// Returns a set containing all possible keys.
        pub fn initFull() Self {
            return .{ .bits = BitSet.initFull() };
        }

        /// Returns a set containing multiple keys.
        pub fn initMany(keys: []const Key) Self {
            var set = initEmpty();
            for (keys) |key| set.insert(key);
            return set;
        }

        /// Returns a set containing a single key.
        pub fn initOne(key: Key) Self {
            return initMany(&[_]Key{key});
        }

        /// Returns the number of keys in the set.
        pub fn count(self: Self) usize {
            return self.bits.count();
        }

        /// Checks if a key is in the set.
        pub fn contains(self: Self, key: Key) bool {
            return self.bits.isSet(Indexer.indexOf(key));
        }

        /// Puts a key in the set.
        pub fn insert(self: *Self, key: Key) void {
            self.bits.set(Indexer.indexOf(key));
        }

        /// Removes a key from the set.
        pub fn remove(self: *Self, key: Key) void {
            self.bits.unset(Indexer.indexOf(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);
        }

        /// 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));
        }

        /// Toggles the presence of all keys in the passed set.
        pub fn toggleSet(self: *Self, other: Self) void {
            self.bits.toggleSet(other.bits);
        }

        /// Toggles all possible keys in the set.
        pub fn toggleAll(self: *Self) void {
            self.bits.toggleAll();
        }

        /// Adds all keys in the passed set to this set.
        pub fn setUnion(self: *Self, other: Self) void {
            self.bits.setUnion(other.bits);
        }

        /// 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 {
            return self.bits.eql(other.bits);
        }

        /// 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 {
            return self.bits.subsetOf(other.bits);
        }

        /// 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 {
            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) };
        }

        /// 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 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 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(.{}) };
        }

        pub const Iterator = struct {
            inner: BitSet.Iterator(.{}),

            pub fn next(self: *Iterator) ?Key {
                return if (self.inner.next()) |index|
                    Indexer.keyForIndex(index)
                else
                    null;
            }
        };
    };
}

Type FunctionEnumMap[src]

A map keyed by an enum, backed by a bitfield and a dense array. If the enum is exhaustive but 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.

Parameters

E: type
V: type

Types

TypeIndexer[src]

The index mapping for this map

Source Code

Source code
pub const Indexer = EnumIndexer(E)

Fields

bits: BitSet = BitSet.initEmpty()

Bits determining whether items are in the map

values: [Indexer.count]Value = undefined

Values of items in the map. If the associated bit is zero, the value is undefined.

Values

ConstantKey[src]

The key type used to index this map

Source Code

Source code
pub const Key = Indexer.Key

ConstantValue[src]

The value type stored in this map

Source Code

Source code
pub const Value = V

Constantlen[src]

The number of possible keys in the map

Source Code

Source code
pub const len = Indexer.count

Functions

Functioninit[src]

pub fn init(init_values: EnumFieldStruct(E, ?Value, @as(?Value, null))) Self

Initializes the map using a sparse struct of optionals

Parameters

init_values: EnumFieldStruct(E, ?Value, @as(?Value, null))

Source Code

Source code
pub fn init(init_values: EnumFieldStruct(E, ?Value, @as(?Value, null))) Self {
    @setEvalBranchQuota(2 * @typeInfo(E).@"enum".fields.len);
    var result: Self = .{};
    if (@typeInfo(E).@"enum".is_exhaustive) {
        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.*;
            }
        }
    } else {
        inline for (std.meta.fields(E)) |field| {
            const key = @field(E, field.name);
            if (@field(init_values, field.name)) |*v| {
                const i = comptime Indexer.indexOf(key);
                result.bits.set(i);
                result.values[i] = v.*;
            }
        }
    }
    return result;
}

FunctioninitFull[src]

pub fn initFull(value: Value) Self

Initializes a full mapping with all keys set to value. Consider using EnumArray instead if the map will remain full.

Parameters

value: Value

Source Code

Source code
pub fn initFull(value: Value) Self {
    var result: Self = .{
        .bits = Self.BitSet.initFull(),
        .values = undefined,
    };
    @memset(&result.values, value);
    return result;
}

FunctioninitFullWith[src]

pub fn initFullWith(init_values: EnumFieldStruct(E, Value, null)) Self

Initializes a full mapping with supplied values. Consider using EnumArray instead if the map will remain full.

Parameters

init_values: EnumFieldStruct(E, Value, null)

Source Code

Source code
pub fn initFullWith(init_values: EnumFieldStruct(E, Value, null)) Self {
    return initFullWithDefault(null, init_values);
}

FunctioninitFullWithDefault[src]

pub fn initFullWithDefault(comptime default: ?Value, init_values: EnumFieldStruct(E, Value, default)) Self

Initializes a full mapping with a provided default. Consider using EnumArray instead if the map will remain full.

Parameters

default: ?Value
init_values: EnumFieldStruct(E, Value, default)

Source Code

Source code
pub fn initFullWithDefault(comptime default: ?Value, init_values: EnumFieldStruct(E, Value, default)) Self {
    @setEvalBranchQuota(2 * @typeInfo(E).@"enum".fields.len);
    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;
}

Functioncount[src]

pub fn count(self: Self) usize

The number of items in the map.

Parameters

self: Self

Source Code

Source code
pub fn count(self: Self) usize {
    return self.bits.count();
}

Functioncontains[src]

pub fn contains(self: Self, key: Key) bool

Checks if the map contains an item.

Parameters

self: Self
key: Key

Source Code

Source code
pub fn contains(self: Self, key: Key) bool {
    return self.bits.isSet(Indexer.indexOf(key));
}

Functionget[src]

pub fn get(self: Self, key: Key) ?Value

Gets the value associated with a key. If the key is not in the map, returns null.

Parameters

self: Self
key: Key

Source Code

Source code
pub fn get(self: Self, key: Key) ?Value {
    const index = Indexer.indexOf(key);
    return if (self.bits.isSet(index)) self.values[index] else null;
}

FunctiongetAssertContains[src]

pub fn getAssertContains(self: Self, key: Key) Value

Gets the value associated with a key, which must exist in the map.

Parameters

self: Self
key: Key

Source Code

Source code
pub fn getAssertContains(self: Self, key: Key) Value {
    const index = Indexer.indexOf(key);
    assert(self.bits.isSet(index));
    return self.values[index];
}

FunctiongetPtr[src]

pub fn getPtr(self: *Self, key: Key) ?*Value

Gets the address of the value associated with a key. If the key is not in the map, returns null.

Parameters

self: *Self
key: Key

Source Code

Source code
pub fn getPtr(self: *Self, key: Key) ?*Value {
    const index = Indexer.indexOf(key);
    return if (self.bits.isSet(index)) &self.values[index] else null;
}

FunctiongetPtrConst[src]

pub fn getPtrConst(self: *const Self, key: Key) ?*const Value

Gets the address of the const value associated with a key. If the key is not in the map, returns null.

Parameters

self: *const Self
key: Key

Source Code

Source code
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;
}

FunctiongetPtrAssertContains[src]

pub fn getPtrAssertContains(self: *Self, key: Key) *Value

Gets the address of the value associated with a key. The key must be present in the map.

Parameters

self: *Self
key: Key

Source Code

Source code
pub fn getPtrAssertContains(self: *Self, key: Key) *Value {
    const index = Indexer.indexOf(key);
    assert(self.bits.isSet(index));
    return &self.values[index];
}

FunctiongetPtrConstAssertContains[src]

pub fn getPtrConstAssertContains(self: *const Self, key: Key) *const Value

Gets the address of the const value associated with a key. The key must be present in the map.

Parameters

self: *const Self
key: Key

Source Code

Source code
pub fn getPtrConstAssertContains(self: *const Self, key: Key) *const Value {
    const index = Indexer.indexOf(key);
    assert(self.bits.isSet(index));
    return &self.values[index];
}

Functionput[src]

pub fn put(self: *Self, key: Key, value: Value) void

Adds the key to the map with the supplied value. If the key is already in the map, overwrites the value.

Parameters

self: *Self
key: Key
value: Value

Source Code

Source code
pub fn put(self: *Self, key: Key, value: Value) void {
    const index = Indexer.indexOf(key);
    self.bits.set(index);
    self.values[index] = value;
}

FunctionputUninitialized[src]

pub fn putUninitialized(self: *Self, key: Key) *Value

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.

Parameters

self: *Self
key: Key

Source Code

Source code
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];
}

FunctionfetchPut[src]

pub fn fetchPut(self: *Self, key: Key, value: Value) ?Value

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.

Parameters

self: *Self
key: Key
value: Value

Source Code

Source code
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;
}

Functionremove[src]

pub fn remove(self: *Self, key: Key) void

Removes a key from the map. If the key was not in the map, does nothing.

Parameters

self: *Self
key: Key

Source Code

Source code
pub fn remove(self: *Self, key: Key) void {
    const index = Indexer.indexOf(key);
    self.bits.unset(index);
    self.values[index] = undefined;
}

FunctionfetchRemove[src]

pub fn fetchRemove(self: *Self, key: Key) ?Value

Removes a key from the map, and returns the old value. If the key was not in the map, returns null.

Parameters

self: *Self
key: Key

Source Code

Source code
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;
}

Functioniterator[src]

pub fn iterator(self: *Self) Iterator

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.

Parameters

self: *Self

Source Code

Source code
pub fn iterator(self: *Self) Iterator {
    return .{
        .inner = self.bits.iterator(.{}),
        .values = &self.values,
    };
}

Example Usage

test EnumMap {
    const Ball = enum { red, green, blue };

    const some = EnumMap(Ball, u8).init(.{
        .green = 0xff,
        .blue = 0x80,
    });
    try testing.expectEqual(2, some.count());
    try testing.expectEqual(null, some.get(.red));
    try testing.expectEqual(0xff, some.get(.green));
    try testing.expectEqual(0x80, some.get(.blue));
}

Source Code

Source code
pub fn EnumMap(comptime E: type, comptime V: type) type {
    return struct {
        const Self = @This();

        /// 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;

        const BitSet = std.StaticBitSet(Indexer.count);

        /// 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,

        /// Initializes the map using a sparse struct of optionals
        pub fn init(init_values: EnumFieldStruct(E, ?Value, @as(?Value, null))) Self {
            @setEvalBranchQuota(2 * @typeInfo(E).@"enum".fields.len);
            var result: Self = .{};
            if (@typeInfo(E).@"enum".is_exhaustive) {
                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.*;
                    }
                }
            } else {
                inline for (std.meta.fields(E)) |field| {
                    const key = @field(E, field.name);
                    if (@field(init_values, field.name)) |*v| {
                        const i = comptime Indexer.indexOf(key);
                        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: Value) 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, Value, null)) Self {
            return initFullWithDefault(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: ?Value, init_values: EnumFieldStruct(E, Value, default)) Self {
            @setEvalBranchQuota(2 * @typeInfo(E).@"enum".fields.len);
            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;
        }

        /// The number of items in the map.
        pub fn count(self: Self) usize {
            return self.bits.count();
        }

        /// Checks if the map contains an item.
        pub fn contains(self: Self, key: Key) bool {
            return self.bits.isSet(Indexer.indexOf(key));
        }

        /// 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;
        }

        /// 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];
        }

        /// 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;
        }

        /// 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;
        }

        /// 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];
        }

        /// 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];
        }

        /// 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;
        }

        /// 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];
        }

        /// 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;
        }

        /// 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;
        }

        /// 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;
        }

        /// 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,
            };
        }

        /// 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,

            /// 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,

            pub fn next(self: *Iterator) ?Entry {
                return if (self.inner.next()) |index|
                    Entry{
                        .key = Indexer.keyForIndex(index),
                        .value = &self.values[index],
                    }
                else
                    null;
            }
        };
    };
}

Type FunctionEnumMultiset[src]

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.

Parameters

E: type

Fields

counts: EnumArray(E, CountSize)

Values

ConstantEntry[src]

Source Code

Source code
pub const Entry = EnumArray(E, CountSize).Entry

ConstantIterator[src]

Source Code

Source code
pub const Iterator = EnumArray(E, CountSize).Iterator

Functions

Functioninit[src]

pub fn init(init_counts: EnumFieldStruct(E, CountSize, 0)) Self

Initializes the multiset using a struct of counts.

Parameters

init_counts: EnumFieldStruct(E, CountSize, 0)

Source Code

Source code
pub fn init(init_counts: EnumFieldStruct(E, CountSize, 0)) Self {
    @setEvalBranchQuota(2 * @typeInfo(E).@"enum".fields.len);
    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;
}

FunctioninitEmpty[src]

pub fn initEmpty() Self

Initializes the multiset with a count of zero.

Source Code

Source code
pub fn initEmpty() Self {
    return initWithCount(0);
}

FunctioninitWithCount[src]

pub fn initWithCount(comptime c: CountSize) Self

Initializes the multiset with all keys at the same count.

Parameters

c: CountSize

Source Code

Source code
pub fn initWithCount(comptime c: CountSize) Self {
    return .{
        .counts = EnumArray(E, CountSize).initDefault(c, .{}),
    };
}

Functioncount[src]

pub fn count(self: Self) usize

Returns the total number of key counts in the multiset.

Parameters

self: Self

Source Code

Source code
pub fn count(self: Self) usize {
    var sum: usize = 0;
    for (self.counts.values) |c| {
        sum += c;
    }
    return sum;
}

Functioncontains[src]

pub fn contains(self: Self, key: E) bool

Checks if at least one key in multiset.

Parameters

self: Self
key: E

Source Code

Source code
pub fn contains(self: Self, key: E) bool {
    return self.counts.get(key) > 0;
}

FunctionremoveAll[src]

pub fn removeAll(self: *Self, key: E) void

Removes all instance of a key from multiset. Same as setCount(key, 0).

Parameters

self: *Self
key: E

Source Code

Source code
pub fn removeAll(self: *Self, key: E) void {
    return self.counts.set(key, 0);
}

FunctionaddAssertSafe[src]

pub fn addAssertSafe(self: *Self, key: E, c: CountSize) void

Increases the key count by given amount. Caller asserts operation will not overflow.

Parameters

self: *Self
key: E
c: CountSize

Source Code

Source code
pub fn addAssertSafe(self: *Self, key: E, c: CountSize) void {
    self.counts.getPtr(key).* += c;
}

Functionadd[src]

pub fn add(self: *Self, key: E, c: CountSize) error{Overflow}!void

Increases the key count by given amount.

Parameters

self: *Self
key: E
c: CountSize

Source Code

Source code
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));
}

Functionremove[src]

pub fn remove(self: *Self, key: E, c: CountSize) void

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.

Parameters

self: *Self
key: E
c: CountSize

Source Code

Source code
pub fn remove(self: *Self, key: E, c: CountSize) void {
    self.counts.getPtr(key).* -= @min(self.getCount(key), c);
}

FunctiongetCount[src]

pub fn getCount(self: Self, key: E) CountSize

Returns the count for a key.

Parameters

self: Self
key: E

Source Code

Source code
pub fn getCount(self: Self, key: E) CountSize {
    return self.counts.get(key);
}

FunctionsetCount[src]

pub fn setCount(self: *Self, key: E, c: CountSize) void

Set the count for a key.

Parameters

self: *Self
key: E
c: CountSize

Source Code

Source code
pub fn setCount(self: *Self, key: E, c: CountSize) void {
    self.counts.set(key, c);
}

FunctionaddSetAssertSafe[src]

pub fn addSetAssertSafe(self: *Self, other: Self) void

Increases the all key counts by given multiset. Caller asserts operation will not overflow any key.

Parameters

self: *Self
other: Self

Source Code

Source code
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));
    }
}

FunctionaddSet[src]

pub fn addSet(self: *Self, other: Self) error{Overflow}!void

Increases the all key counts by given multiset.

Parameters

self: *Self
other: Self

Source Code

Source code
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));
    }
}

FunctionremoveSet[src]

pub fn removeSet(self: *Self, other: Self) void

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.

Parameters

self: *Self
other: Self

Source Code

Source code
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));
    }
}

Functioneql[src]

pub fn eql(self: Self, other: Self) bool

Returns true iff all key counts are the same as given multiset.

Parameters

self: Self
other: Self

Source Code

Source code
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;
}

FunctionsubsetOf[src]

pub fn subsetOf(self: Self, other: Self) bool

Returns true iff all key counts less than or equal to the given multiset.

Parameters

self: Self
other: Self

Source Code

Source code
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;
}

FunctionsupersetOf[src]

pub fn supersetOf(self: Self, other: Self) bool

Returns true iff all key counts greater than or equal to the given multiset.

Parameters

self: Self
other: Self

Source Code

Source code
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;
}

FunctionplusAssertSafe[src]

pub fn plusAssertSafe(self: Self, other: Self) Self

Returns a multiset with the total key count of this multiset and the other multiset. Caller asserts operation will not overflow any key.

Parameters

self: Self
other: Self

Source Code

Source code
pub fn plusAssertSafe(self: Self, other: Self) Self {
    var result = self;
    result.addSetAssertSafe(other);
    return result;
}

Functionplus[src]

pub fn plus(self: Self, other: Self) error{Overflow}!Self

Returns a multiset with the total key count of this multiset and the other multiset.

Parameters

self: Self
other: Self

Source Code

Source code
pub fn plus(self: Self, other: Self) error{Overflow}!Self {
    var result = self;
    try result.addSet(other);
    return result;
}

Functionminus[src]

pub fn minus(self: Self, other: Self) Self

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.

Parameters

self: Self
other: Self

Source Code

Source code
pub fn minus(self: Self, other: Self) Self {
    var result = self;
    result.removeSet(other);
    return result;
}

Functioniterator[src]

pub fn iterator(self: *Self) 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.

Parameters

self: *Self

Source Code

Source code
pub fn iterator(self: *Self) Iterator {
    return self.counts.iterator();
}

Example Usage

test EnumMultiset {
    const Ball = enum { red, green, blue };

    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));
    }

    {
        var copy = ten_of_each;
        copy.addAssertSafe(.red, 6);
        try testing.expectEqual(copy.getCount(.red), 16);
    }

    {
        var copy = ten_of_each;
        try copy.add(.red, 6);
        try testing.expectEqual(copy.getCount(.red), 16);

        try testing.expectError(error.Overflow, copy.add(.red, std.math.maxInt(usize)));
    }

    {
        var copy = ten_of_each;
        copy.remove(.red, 4);
        try testing.expectEqual(copy.getCount(.red), 6);

        // subtracting more it contains does not underflow
        copy.remove(.green, 14);
        try testing.expectEqual(copy.getCount(.green), 0);
    }

    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);

    {
        var copy = empty;
        copy.setCount(.red, 6);
        try testing.expectEqual(copy.getCount(.red), 6);
    }

    {
        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);
    }

    {
        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);

        const full = EnumMultiset(Ball).initWithCount(std.math.maxInt(usize));
        try testing.expectError(error.Overflow, copy.addSet(full));
    }

    {
        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);

        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);
    }

    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));

    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));

    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));

    {
        // with multisets it could be the case where two
        // multisets are neither subset nor superset of each
        // other.

        const r10 = EnumMultiset(Ball).init(.{
            .red = 10,
        });
        const b10 = EnumMultiset(Ball).init(.{
            .blue = 10,
        });

        try testing.expect(!r10.subsetOf(b10));
        try testing.expect(!b10.subsetOf(r10));
        try testing.expect(!r10.supersetOf(b10));
        try testing.expect(!b10.supersetOf(r10));
    }

    {
        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);
    }

    {
        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);

        const full = EnumMultiset(Ball).initWithCount(std.math.maxInt(usize));
        try testing.expectError(error.Overflow, result.plus(full));
    }

    {
        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);
    }

    {
        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);
    }

    {
        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);
    }

    {
        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);
    }
}

Source Code

Source code
pub fn EnumMultiset(comptime E: type) type {
    return BoundedEnumMultiset(E, usize);
}

Type FunctionBoundedEnumMultiset[src]

A multiset of enum elements up to CountSize. Backed by an EnumArray. This type does no dynamic allocation and can be copied by value.

Parameters

E: type
CountSize: type

Fields

counts: EnumArray(E, CountSize)

Values

ConstantEntry[src]

Source Code

Source code
pub const Entry = EnumArray(E, CountSize).Entry

ConstantIterator[src]

Source Code

Source code
pub const Iterator = EnumArray(E, CountSize).Iterator

Functions

Functioninit[src]

pub fn init(init_counts: EnumFieldStruct(E, CountSize, 0)) Self

Initializes the multiset using a struct of counts.

Parameters

init_counts: EnumFieldStruct(E, CountSize, 0)

Source Code

Source code
pub fn init(init_counts: EnumFieldStruct(E, CountSize, 0)) Self {
    @setEvalBranchQuota(2 * @typeInfo(E).@"enum".fields.len);
    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;
}

FunctioninitEmpty[src]

pub fn initEmpty() Self

Initializes the multiset with a count of zero.

Source Code

Source code
pub fn initEmpty() Self {
    return initWithCount(0);
}

FunctioninitWithCount[src]

pub fn initWithCount(comptime c: CountSize) Self

Initializes the multiset with all keys at the same count.

Parameters

c: CountSize

Source Code

Source code
pub fn initWithCount(comptime c: CountSize) Self {
    return .{
        .counts = EnumArray(E, CountSize).initDefault(c, .{}),
    };
}

Functioncount[src]

pub fn count(self: Self) usize

Returns the total number of key counts in the multiset.

Parameters

self: Self

Source Code

Source code
pub fn count(self: Self) usize {
    var sum: usize = 0;
    for (self.counts.values) |c| {
        sum += c;
    }
    return sum;
}

Functioncontains[src]

pub fn contains(self: Self, key: E) bool

Checks if at least one key in multiset.

Parameters

self: Self
key: E

Source Code

Source code
pub fn contains(self: Self, key: E) bool {
    return self.counts.get(key) > 0;
}

FunctionremoveAll[src]

pub fn removeAll(self: *Self, key: E) void

Removes all instance of a key from multiset. Same as setCount(key, 0).

Parameters

self: *Self
key: E

Source Code

Source code
pub fn removeAll(self: *Self, key: E) void {
    return self.counts.set(key, 0);
}

FunctionaddAssertSafe[src]

pub fn addAssertSafe(self: *Self, key: E, c: CountSize) void

Increases the key count by given amount. Caller asserts operation will not overflow.

Parameters

self: *Self
key: E
c: CountSize

Source Code

Source code
pub fn addAssertSafe(self: *Self, key: E, c: CountSize) void {
    self.counts.getPtr(key).* += c;
}

Functionadd[src]

pub fn add(self: *Self, key: E, c: CountSize) error{Overflow}!void

Increases the key count by given amount.

Parameters

self: *Self
key: E
c: CountSize

Source Code

Source code
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));
}

Functionremove[src]

pub fn remove(self: *Self, key: E, c: CountSize) void

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.

Parameters

self: *Self
key: E
c: CountSize

Source Code

Source code
pub fn remove(self: *Self, key: E, c: CountSize) void {
    self.counts.getPtr(key).* -= @min(self.getCount(key), c);
}

FunctiongetCount[src]

pub fn getCount(self: Self, key: E) CountSize

Returns the count for a key.

Parameters

self: Self
key: E

Source Code

Source code
pub fn getCount(self: Self, key: E) CountSize {
    return self.counts.get(key);
}

FunctionsetCount[src]

pub fn setCount(self: *Self, key: E, c: CountSize) void

Set the count for a key.

Parameters

self: *Self
key: E
c: CountSize

Source Code

Source code
pub fn setCount(self: *Self, key: E, c: CountSize) void {
    self.counts.set(key, c);
}

FunctionaddSetAssertSafe[src]

pub fn addSetAssertSafe(self: *Self, other: Self) void

Increases the all key counts by given multiset. Caller asserts operation will not overflow any key.

Parameters

self: *Self
other: Self

Source Code

Source code
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));
    }
}

FunctionaddSet[src]

pub fn addSet(self: *Self, other: Self) error{Overflow}!void

Increases the all key counts by given multiset.

Parameters

self: *Self
other: Self

Source Code

Source code
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));
    }
}

FunctionremoveSet[src]

pub fn removeSet(self: *Self, other: Self) void

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.

Parameters

self: *Self
other: Self

Source Code

Source code
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));
    }
}

Functioneql[src]

pub fn eql(self: Self, other: Self) bool

Returns true iff all key counts are the same as given multiset.

Parameters

self: Self
other: Self

Source Code

Source code
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;
}

FunctionsubsetOf[src]

pub fn subsetOf(self: Self, other: Self) bool

Returns true iff all key counts less than or equal to the given multiset.

Parameters

self: Self
other: Self

Source Code

Source code
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;
}

FunctionsupersetOf[src]

pub fn supersetOf(self: Self, other: Self) bool

Returns true iff all key counts greater than or equal to the given multiset.

Parameters

self: Self
other: Self

Source Code

Source code
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;
}

FunctionplusAssertSafe[src]

pub fn plusAssertSafe(self: Self, other: Self) Self

Returns a multiset with the total key count of this multiset and the other multiset. Caller asserts operation will not overflow any key.

Parameters

self: Self
other: Self

Source Code

Source code
pub fn plusAssertSafe(self: Self, other: Self) Self {
    var result = self;
    result.addSetAssertSafe(other);
    return result;
}

Functionplus[src]

pub fn plus(self: Self, other: Self) error{Overflow}!Self

Returns a multiset with the total key count of this multiset and the other multiset.

Parameters

self: Self
other: Self

Source Code

Source code
pub fn plus(self: Self, other: Self) error{Overflow}!Self {
    var result = self;
    try result.addSet(other);
    return result;
}

Functionminus[src]

pub fn minus(self: Self, other: Self) Self

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.

Parameters

self: Self
other: Self

Source Code

Source code
pub fn minus(self: Self, other: Self) Self {
    var result = self;
    result.removeSet(other);
    return result;
}

Functioniterator[src]

pub fn iterator(self: *Self) 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.

Parameters

self: *Self

Source Code

Source code
pub fn iterator(self: *Self) Iterator {
    return self.counts.iterator();
}

Source Code

Source code
pub fn BoundedEnumMultiset(comptime E: type, comptime CountSize: type) type {
    return struct {
        const Self = @This();

        counts: EnumArray(E, CountSize),

        /// Initializes the multiset using a struct of counts.
        pub fn init(init_counts: EnumFieldStruct(E, CountSize, 0)) Self {
            @setEvalBranchQuota(2 * @typeInfo(E).@"enum".fields.len);
            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;
        }

        /// Initializes the multiset with a count of zero.
        pub fn initEmpty() Self {
            return initWithCount(0);
        }

        /// 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 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;
        }

        /// Checks if at least one key in multiset.
        pub fn contains(self: Self, key: E) bool {
            return self.counts.get(key) > 0;
        }

        /// 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);
        }

        /// 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;
        }

        /// 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));
        }

        /// 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);
        }

        /// Returns the count for a key.
        pub fn getCount(self: Self, key: E) CountSize {
            return self.counts.get(key);
        }

        /// Set the count for a key.
        pub fn setCount(self: *Self, key: E, c: CountSize) void {
            self.counts.set(key, c);
        }

        /// 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));
            }
        }

        /// 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));
            }
        }

        /// 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 all key counts are the same as
        /// given multiset.
        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;
        }

        /// Returns true iff all key counts less than or
        /// equal to the given multiset.
        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;
        }

        /// Returns true iff all key counts greater than or
        /// equal to the given multiset.
        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;
        }

        /// 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 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 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 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();
        }
    };
}

Type FunctionEnumArray[src]

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.

Parameters

E: type
V: type

Types

TypeIndexer[src]

The index mapping for this map

Source Code

Source code
pub const Indexer = EnumIndexer(E)

Fields

Values

ConstantKey[src]

The key type used to index this map

Source Code

Source code
pub const Key = Indexer.Key

ConstantValue[src]

The value type stored in this map

Source Code

Source code
pub const Value = V

Constantlen[src]

The number of possible keys in the map

Source Code

Source code
pub const len = Indexer.count

Functions

Functioninit[src]

pub fn init(init_values: EnumFieldStruct(E, Value, null)) Self

Parameters

init_values: EnumFieldStruct(E, Value, null)

Source Code

Source code
pub fn init(init_values: EnumFieldStruct(E, Value, null)) Self {
    return initDefault(null, init_values);
}

FunctioninitDefault[src]

pub fn initDefault(comptime default: ?Value, init_values: EnumFieldStruct(E, Value, default)) Self

Initializes values in the enum array, with the specified default.

Parameters

default: ?Value
init_values: EnumFieldStruct(E, Value, default)

Source Code

Source code
pub fn initDefault(comptime default: ?Value, init_values: EnumFieldStruct(E, Value, default)) Self {
    @setEvalBranchQuota(2 * @typeInfo(E).@"enum".fields.len);
    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;
}

FunctioninitUndefined[src]

pub fn initUndefined() Self

Source Code

Source code
pub fn initUndefined() Self {
    return Self{ .values = undefined };
}

FunctioninitFill[src]

pub fn initFill(v: Value) Self

Parameters

Source Code

Source code
pub fn initFill(v: Value) Self {
    var self: Self = undefined;
    @memset(&self.values, v);
    return self;
}

Functionget[src]

pub fn get(self: Self, key: Key) Value

Returns the value in the array associated with a key.

Parameters

self: Self
key: Key

Source Code

Source code
pub fn get(self: Self, key: Key) Value {
    return self.values[Indexer.indexOf(key)];
}

FunctiongetPtr[src]

pub fn getPtr(self: *Self, key: Key) *Value

Returns a pointer to the slot in the array associated with a key.

Parameters

self: *Self
key: Key

Source Code

Source code
pub fn getPtr(self: *Self, key: Key) *Value {
    return &self.values[Indexer.indexOf(key)];
}

FunctiongetPtrConst[src]

pub fn getPtrConst(self: *const Self, key: Key) *const Value

Returns a const pointer to the slot in the array associated with a key.

Parameters

self: *const Self
key: Key

Source Code

Source code
pub fn getPtrConst(self: *const Self, key: Key) *const Value {
    return &self.values[Indexer.indexOf(key)];
}

Functionset[src]

pub fn set(self: *Self, key: Key, value: Value) void

Sets the value in the slot associated with a key.

Parameters

self: *Self
key: Key
value: Value

Source Code

Source code
pub fn set(self: *Self, key: Key, value: Value) void {
    self.values[Indexer.indexOf(key)] = value;
}

Functioniterator[src]

pub fn iterator(self: *Self) Iterator

Iterates over the items in the array, in index order.

Parameters

self: *Self

Source Code

Source code
pub fn iterator(self: *Self) Iterator {
    return .{
        .values = &self.values,
    };
}

Source Code

Source code
pub fn EnumArray(comptime E: type, comptime V: type) type {
    return struct {
        const Self = @This();

        /// 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;

        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 {
            @setEvalBranchQuota(2 * @typeInfo(E).@"enum".fields.len);
            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 };
        }

        pub fn initFill(v: Value) Self {
            var self: Self = undefined;
            @memset(&self.values, v);
            return self;
        }

        /// Returns the value in the array associated with a key.
        pub fn get(self: Self, key: Key) Value {
            return self.values[Indexer.indexOf(key)];
        }

        /// Returns a pointer to the slot in the array associated with a key.
        pub fn getPtr(self: *Self, key: Key) *Value {
            return &self.values[Indexer.indexOf(key)];
        }

        /// Returns a const pointer to the slot in the array associated with a key.
        pub fn getPtrConst(self: *const Self, key: Key) *const Value {
            return &self.values[Indexer.indexOf(key)];
        }

        /// Sets the value in the slot associated with a key.
        pub fn set(self: *Self, key: Key, value: Value) void {
            self.values[Indexer.indexOf(key)] = value;
        }

        /// Iterates over the items in the array, in index order.
        pub fn iterator(self: *Self) Iterator {
            return .{
                .values = &self.values,
            };
        }

        /// An entry in the array.
        pub const Entry = struct {
            /// The key associated with this entry.
            /// Modifying this key will not change the array.
            key: Key,

            /// A pointer to the value in the array associated
            /// with this key.  Modifications through this
            /// pointer will modify the underlying data.
            value: *Value,
        };

        pub const Iterator = struct {
            index: usize = 0,
            values: *[Indexer.count]Value,

            pub fn next(self: *Iterator) ?Entry {
                const index = self.index;
                if (index < Indexer.count) {
                    self.index += 1;
                    return Entry{
                        .key = Indexer.keyForIndex(index),
                        .value = &self.values[index],
                    };
                }
                return null;
            }
        };
    };
}

Type FunctionEnumIndexer[src]

Parameters

E: type

Values

ConstantKey[src]

Source Code

Source code
pub const Key: type = E

Constantcount[src]

Source Code

Source code
pub const count: comptime_int = std.math.maxInt(RangeType) + 1

ConstantKey[src]

Source Code

Source code
pub const Key = E

Constantcount[src]

Source Code

Source code
pub const count: comptime_int = 0

ConstantKey[src]

Source Code

Source code
pub const Key = E

Constantcount[src]

Source Code

Source code
pub const count: comptime_int = fields_len

ConstantKey[src]

Source Code

Source code
pub const Key = E

Constantcount[src]

Source Code

Source code
pub const count: comptime_int = fields_len

Functions

FunctionindexOf[src]

pub fn indexOf(e: E) usize

Parameters

e: E

Source Code

Source code
pub fn indexOf(e: E) usize {
    if (backing_int_sign == .unsigned)
        return @intFromEnum(e);

    return if (@intFromEnum(e) < 0)
        @intCast(@intFromEnum(e) - min_value)
    else
        @as(RangeType, -min_value) + @as(RangeType, @intCast(@intFromEnum(e)));
}

FunctionkeyForIndex[src]

pub fn keyForIndex(i: usize) E

Parameters

i: usize

Source Code

Source code
pub fn keyForIndex(i: usize) E {
    if (backing_int_sign == .unsigned)
        return @enumFromInt(i);

    return @enumFromInt(@as(std.meta.Int(.signed, @bitSizeOf(RangeType) + 1), @intCast(i)) + min_value);
}

FunctionindexOf[src]

pub fn indexOf(e: E) usize

Parameters

e: E

Source Code

Source code
pub fn indexOf(e: E) usize {
    _ = e;
    unreachable;
}

FunctionkeyForIndex[src]

pub fn keyForIndex(i: usize) E

Parameters

i: usize

Source Code

Source code
pub fn keyForIndex(i: usize) E {
    _ = i;
    unreachable;
}

FunctionlessThan[src]

pub fn lessThan(comptime ctx: @This(), comptime a: usize, comptime b: usize) bool

Parameters

ctx: @This()
a: usize
b: usize

Source Code

Source code
pub fn lessThan(comptime ctx: @This(), comptime a: usize, comptime b: usize) bool {
    return ctx.fields[a].value < ctx.fields[b].value;
}

Functionswap[src]

pub fn swap(comptime ctx: @This(), comptime a: usize, comptime b: usize) void

Parameters

ctx: @This()
a: usize
b: usize

Source Code

Source code
pub fn swap(comptime ctx: @This(), comptime a: usize, comptime b: usize) void {
    return std.mem.swap(EnumField, &ctx.fields[a], &ctx.fields[b]);
}

FunctionindexOf[src]

pub fn indexOf(e: E) usize

Parameters

e: E

Source Code

Source code
pub fn indexOf(e: E) usize {
    return @as(usize, @intCast(@intFromEnum(e) - min));
}

FunctionkeyForIndex[src]

pub fn keyForIndex(i: usize) E

Parameters

i: usize

Source Code

Source code
pub fn keyForIndex(i: usize) E {
    // TODO fix addition semantics.  This calculation
    // gives up some safety to avoid artificially limiting
    // the range of signed enum values to max_isize.
    const enum_value = if (min < 0) @as(isize, @bitCast(i)) +% min else i + min;
    return @as(E, @enumFromInt(@as(@typeInfo(E).@"enum".tag_type, @intCast(enum_value))));
}

FunctionindexOf[src]

pub fn indexOf(e: E) usize

Parameters

e: E

Source Code

Source code
pub fn indexOf(e: E) usize {
    for (keys, 0..) |k, i| {
        if (k == e) return i;
    }
    unreachable;
}

FunctionkeyForIndex[src]

pub fn keyForIndex(i: usize) E

Parameters

i: usize

Source Code

Source code
pub fn keyForIndex(i: usize) E {
    return keys[i];
}

Source Code

Source code
pub fn EnumIndexer(comptime E: type) type {
    // Assumes that the enum fields are sorted in ascending order (optimistic).
    // Unsorted enums may require the user to manually increase the quota.
    @setEvalBranchQuota(3 * @typeInfo(E).@"enum".fields.len + eval_branch_quota_cushion);

    if (!@typeInfo(E).@"enum".is_exhaustive) {
        const BackingInt = @typeInfo(E).@"enum".tag_type;
        if (@bitSizeOf(BackingInt) > @bitSizeOf(usize))
            @compileError("Cannot create an enum indexer for a given non-exhaustive enum, tag_type is larger than usize.");

        return struct {
            pub const Key: type = E;

            const backing_int_sign = @typeInfo(BackingInt).int.signedness;
            const min_value = std.math.minInt(BackingInt);
            const max_value = std.math.maxInt(BackingInt);

            const RangeType = std.meta.Int(.unsigned, @bitSizeOf(BackingInt));
            pub const count: comptime_int = std.math.maxInt(RangeType) + 1;

            pub fn indexOf(e: E) usize {
                if (backing_int_sign == .unsigned)
                    return @intFromEnum(e);

                return if (@intFromEnum(e) < 0)
                    @intCast(@intFromEnum(e) - min_value)
                else
                    @as(RangeType, -min_value) + @as(RangeType, @intCast(@intFromEnum(e)));
            }
            pub fn keyForIndex(i: usize) E {
                if (backing_int_sign == .unsigned)
                    return @enumFromInt(i);

                return @enumFromInt(@as(std.meta.Int(.signed, @bitSizeOf(RangeType) + 1), @intCast(i)) + min_value);
            }
        };
    }

    const const_fields = @typeInfo(E).@"enum".fields;
    var fields = const_fields[0..const_fields.len].*;
    const fields_len = fields.len;

    if (fields_len == 0) {
        return struct {
            pub const Key = E;
            pub const count: comptime_int = 0;
            pub fn indexOf(e: E) usize {
                _ = e;
                unreachable;
            }
            pub fn keyForIndex(i: usize) E {
                _ = i;
                unreachable;
            }
        };
    }

    const min = fields[0].value;
    const max = fields[fields.len - 1].value;

    const SortContext = struct {
        fields: []EnumField,

        pub fn lessThan(comptime ctx: @This(), comptime a: usize, comptime b: usize) bool {
            return ctx.fields[a].value < ctx.fields[b].value;
        }

        pub fn swap(comptime ctx: @This(), comptime a: usize, comptime b: usize) void {
            return std.mem.swap(EnumField, &ctx.fields[a], &ctx.fields[b]);
        }
    };
    std.sort.insertionContext(0, fields_len, SortContext{ .fields = &fields });

    if (max - min == fields.len - 1) {
        return struct {
            pub const Key = E;
            pub const count: comptime_int = fields_len;
            pub fn indexOf(e: E) usize {
                return @as(usize, @intCast(@intFromEnum(e) - min));
            }
            pub fn keyForIndex(i: usize) E {
                // TODO fix addition semantics.  This calculation
                // gives up some safety to avoid artificially limiting
                // the range of signed enum values to max_isize.
                const enum_value = if (min < 0) @as(isize, @bitCast(i)) +% min else i + min;
                return @as(E, @enumFromInt(@as(@typeInfo(E).@"enum".tag_type, @intCast(enum_value))));
            }
        };
    }

    const keys = valuesFromFields(E, &fields);

    return struct {
        pub const Key = E;
        pub const count: comptime_int = fields_len;
        pub fn indexOf(e: E) usize {
            for (keys, 0..) |k, i| {
                if (k == e) return i;
            }
            unreachable;
        }
        pub fn keyForIndex(i: usize) E {
            return keys[i];
        }
    };
}

Functions

FunctionvaluesFromFields[src]

pub inline fn valuesFromFields(comptime E: type, comptime fields: []const EnumField) []const E

Looks up the supplied fields in the given enum type. Uses only the field names, field values are ignored. The result array is in the same order as the input.

Parameters

E: type
fields: []const EnumField

Source Code

Source code
pub inline fn valuesFromFields(comptime E: type, comptime fields: []const EnumField) []const E {
    comptime {
        var result: [fields.len]E = undefined;
        for (&result, fields) |*r, f| {
            r.* = @enumFromInt(f.value);
        }
        const final = result;
        return &final;
    }
}

Functionvalues[src]

pub fn values(comptime E: type) []const E

Returns the set of all named values in the given enum, in declaration order.

Parameters

E: type

Example Usage

test values {
    const E = enum {
        X,
        Y,
        Z,
        const A = 1;
    };
    try testing.expectEqualSlices(E, &.{ .X, .Y, .Z }, values(E));
}

Source Code

Source code
pub fn values(comptime E: type) []const E {
    return comptime valuesFromFields(E, @typeInfo(E).@"enum".fields);
}

FunctiontagName[src]

pub fn tagName(comptime E: type, e: E) ?[]const u8

A safe alternative to @tagName() for non-exhaustive enums that doesn't panic when e has no tagged value. Returns the tag name for e or null if no tag exists.

Parameters

E: type
e: E

Example Usage

test tagName {
    const E = enum(u8) { a, b, _ };
    try testing.expect(tagName(E, .a) != null);
    try testing.expectEqualStrings("a", tagName(E, .a).?);
    try testing.expect(tagName(E, @as(E, @enumFromInt(42))) == null);
}

Source Code

Source code
pub fn tagName(comptime E: type, e: E) ?[]const u8 {
    return inline for (@typeInfo(E).@"enum".fields) |f| {
        if (@intFromEnum(e) == f.value) break f.name;
    } else null;
}

FunctiondirectEnumArrayLen[src]

pub fn directEnumArrayLen(comptime E: type, comptime max_unused_slots: comptime_int) comptime_int

Determines the length of a direct-mapped enum array, indexed by @intCast(usize, @intFromEnum(enum_value)). If the enum is non-exhaustive, the resulting length will only be enough to hold all explicit fields. If the enum contains any fields with values that cannot be represented by usize, a compile error is issued. The max_unused_slots parameter limits the total number of items which have no matching enum key (holes in the enum numbering). So for example, if an enum has values 1, 2, 5, and 6, max_unused_slots must be at least 3, to allow unused slots 0, 3, and 4.

Parameters

E: type
max_unused_slots: comptime_int

Source Code

Source code
pub fn directEnumArrayLen(comptime E: type, comptime max_unused_slots: comptime_int) comptime_int {
    var max_value: comptime_int = -1;
    const max_usize: comptime_int = ~@as(usize, 0);
    const fields = @typeInfo(E).@"enum".fields;
    for (fields) |f| {
        if (f.value < 0) {
            @compileError("Cannot create a direct enum array for " ++ @typeName(E) ++ ", field ." ++ f.name ++ " has a negative value.");
        }
        if (f.value > max_value) {
            if (f.value > max_usize) {
                @compileError("Cannot create a direct enum array for " ++ @typeName(E) ++ ", field ." ++ f.name ++ " is larger than the max value of usize.");
            }
            max_value = f.value;
        }
    }

    const unused_slots = max_value + 1 - fields.len;
    if (unused_slots > max_unused_slots) {
        const unused_str = std.fmt.comptimePrint("{d}", .{unused_slots});
        const allowed_str = std.fmt.comptimePrint("{d}", .{max_unused_slots});
        @compileError("Cannot create a direct enum array for " ++ @typeName(E) ++ ". It would have " ++ unused_str ++ " unused slots, but only " ++ allowed_str ++ " are allowed.");
    }

    return max_value + 1;
}

FunctiondirectEnumArray[src]

pub fn directEnumArray( comptime E: type, comptime Data: type, comptime max_unused_slots: comptime_int, init_values: EnumFieldStruct(E, Data, null), ) [directEnumArrayLen(E, max_unused_slots)]Data

Initializes an array of Data which can be indexed by @intCast(usize, @intFromEnum(enum_value)). If the enum is non-exhaustive, the resulting array will only be large enough to hold all explicit fields. If the enum contains any fields with values that cannot be represented by usize, a compile error is issued. The max_unused_slots parameter limits the total number of items which have no matching enum key (holes in the enum numbering). So for example, if an enum has values 1, 2, 5, and 6, max_unused_slots must be at least 3, to allow unused slots 0, 3, and 4. The init_values parameter must be a struct with field names that match the enum values. If the enum has multiple fields with the same value, the name of the first one must be used.

Parameters

E: type
Data: type
max_unused_slots: comptime_int
init_values: EnumFieldStruct(E, Data, null)

Example Usage

test directEnumArray {
    const E = enum(i4) { a = 4, b = 6, c = 2 };
    var runtime_false: bool = false;
    _ = &runtime_false;
    const array = directEnumArray(E, bool, 4, .{
        .a = true,
        .b = runtime_false,
        .c = true,
    });

    try testing.expectEqual([7]bool, @TypeOf(array));
    try testing.expectEqual(true, array[4]);
    try testing.expectEqual(false, array[6]);
    try testing.expectEqual(true, array[2]);
}

Source Code

Source code
pub fn directEnumArray(
    comptime E: type,
    comptime Data: type,
    comptime max_unused_slots: comptime_int,
    init_values: EnumFieldStruct(E, Data, null),
) [directEnumArrayLen(E, max_unused_slots)]Data {
    return directEnumArrayDefault(E, Data, null, max_unused_slots, init_values);
}

FunctiondirectEnumArrayDefault[src]

pub fn directEnumArrayDefault( comptime E: type, comptime Data: type, comptime default: ?Data, comptime max_unused_slots: comptime_int, init_values: EnumFieldStruct(E, Data, default), ) [directEnumArrayLen(E, max_unused_slots)]Data

Initializes an array of Data which can be indexed by @intCast(usize, @intFromEnum(enum_value)). The enum must be exhaustive. If the enum contains any fields with values that cannot be represented by usize, a compile error is issued. The max_unused_slots parameter limits the total number of items which have no matching enum key (holes in the enum numbering). So for example, if an enum has values 1, 2, 5, and 6, max_unused_slots must be at least 3, to allow unused slots 0, 3, and 4. The init_values parameter must be a struct with field names that match the enum values. If the enum has multiple fields with the same value, the name of the first one must be used.

Parameters

E: type
Data: type
default: ?Data
max_unused_slots: comptime_int
init_values: EnumFieldStruct(E, Data, default)

Example Usage

test directEnumArrayDefault {
    const E = enum(i4) { a = 4, b = 6, c = 2 };
    var runtime_false: bool = false;
    _ = &runtime_false;
    const array = directEnumArrayDefault(E, bool, false, 4, .{
        .a = true,
        .b = runtime_false,
    });

    try testing.expectEqual([7]bool, @TypeOf(array));
    try testing.expectEqual(true, array[4]);
    try testing.expectEqual(false, array[6]);
    try testing.expectEqual(false, array[2]);
}

Source Code

Source code
pub fn directEnumArrayDefault(
    comptime E: type,
    comptime Data: type,
    comptime default: ?Data,
    comptime max_unused_slots: comptime_int,
    init_values: EnumFieldStruct(E, Data, default),
) [directEnumArrayLen(E, max_unused_slots)]Data {
    const len = comptime directEnumArrayLen(E, max_unused_slots);
    var result: [len]Data = if (default) |d| [_]Data{d} ** len else undefined;
    inline for (@typeInfo(@TypeOf(init_values)).@"struct".fields) |f| {
        const enum_value = @field(E, f.name);
        const index = @as(usize, @intCast(@intFromEnum(enum_value)));
        result[index] = @field(init_values, f.name);
    }
    return result;
}

FunctionnameCast[src]

pub fn nameCast(comptime E: type, comptime value: anytype) E

Cast an enum literal, value, or string to the enum value of type E with the same name.

Parameters

E: type

Example Usage

test nameCast {
    const A = enum(u1) { a = 0, b = 1 };
    const B = enum(u1) { a = 1, b = 0 };
    try testing.expectEqual(A.a, nameCast(A, .a));
    try testing.expectEqual(A.a, nameCast(A, A.a));
    try testing.expectEqual(A.a, nameCast(A, B.a));
    try testing.expectEqual(A.a, nameCast(A, "a"));
    try testing.expectEqual(A.a, nameCast(A, @as(*const [1]u8, "a")));
    try testing.expectEqual(A.a, nameCast(A, @as([:0]const u8, "a")));
    try testing.expectEqual(A.a, nameCast(A, @as([]const u8, "a")));

    try testing.expectEqual(B.a, nameCast(B, .a));
    try testing.expectEqual(B.a, nameCast(B, A.a));
    try testing.expectEqual(B.a, nameCast(B, B.a));
    try testing.expectEqual(B.a, nameCast(B, "a"));

    try testing.expectEqual(B.b, nameCast(B, .b));
    try testing.expectEqual(B.b, nameCast(B, A.b));
    try testing.expectEqual(B.b, nameCast(B, B.b));
    try testing.expectEqual(B.b, nameCast(B, "b"));
}

Source Code

Source code
pub fn nameCast(comptime E: type, comptime value: anytype) E {
    return comptime blk: {
        const V = @TypeOf(value);
        if (V == E) break :blk value;
        const name: ?[]const u8 = switch (@typeInfo(V)) {
            .enum_literal, .@"enum" => @tagName(value),
            .pointer => value,
            else => null,
        };
        if (name) |n| {
            if (@hasField(E, n)) {
                break :blk @field(E, n);
            }
            @compileError("Enum " ++ @typeName(E) ++ " has no field named " ++ n);
        }
        @compileError("Cannot cast from " ++ @typeName(@TypeOf(value)) ++ " to " ++ @typeName(E));
    };
}

Source Code

Source code
//! This module contains utilities and data structures for working with enums.

const std = @import("std");
const assert = std.debug.assert;
const testing = std.testing;
const EnumField = std.builtin.Type.EnumField;

/// Increment this value when adding APIs that add single backwards branches.
const eval_branch_quota_cushion = 10;

/// Returns a struct with a field matching each unique named enum element.
/// If the enum is extern and has multiple names for the same value, only
/// the first name is used.  Each field is of type Data and has the provided
/// default, which may be undefined.
pub fn EnumFieldStruct(comptime E: type, comptime Data: type, comptime field_default: ?Data) type {
    @setEvalBranchQuota(@typeInfo(E).@"enum".fields.len + eval_branch_quota_cushion);
    var struct_fields: [@typeInfo(E).@"enum".fields.len]std.builtin.Type.StructField = undefined;
    for (&struct_fields, @typeInfo(E).@"enum".fields) |*struct_field, enum_field| {
        struct_field.* = .{
            .name = enum_field.name ++ "",
            .type = Data,
            .default_value_ptr = if (field_default) |d| @as(?*const anyopaque, @ptrCast(&d)) else null,
            .is_comptime = false,
            .alignment = if (@sizeOf(Data) > 0) @alignOf(Data) else 0,
        };
    }
    return @Type(.{ .@"struct" = .{
        .layout = .auto,
        .fields = &struct_fields,
        .decls = &.{},
        .is_tuple = false,
    } });
}

/// Looks up the supplied fields in the given enum type.
/// Uses only the field names, field values are ignored.
/// The result array is in the same order as the input.
pub inline fn valuesFromFields(comptime E: type, comptime fields: []const EnumField) []const E {
    comptime {
        var result: [fields.len]E = undefined;
        for (&result, fields) |*r, f| {
            r.* = @enumFromInt(f.value);
        }
        const final = result;
        return &final;
    }
}

/// Returns the set of all named values in the given enum, in
/// declaration order.
pub fn values(comptime E: type) []const E {
    return comptime valuesFromFields(E, @typeInfo(E).@"enum".fields);
}

/// A safe alternative to @tagName() for non-exhaustive enums that doesn't
/// panic when `e` has no tagged value.
/// Returns the tag name for `e` or null if no tag exists.
pub fn tagName(comptime E: type, e: E) ?[]const u8 {
    return inline for (@typeInfo(E).@"enum".fields) |f| {
        if (@intFromEnum(e) == f.value) break f.name;
    } else null;
}

test tagName {
    const E = enum(u8) { a, b, _ };
    try testing.expect(tagName(E, .a) != null);
    try testing.expectEqualStrings("a", tagName(E, .a).?);
    try testing.expect(tagName(E, @as(E, @enumFromInt(42))) == null);
}

/// Determines the length of a direct-mapped enum array, indexed by
/// @intCast(usize, @intFromEnum(enum_value)).
/// If the enum is non-exhaustive, the resulting length will only be enough
/// to hold all explicit fields.
/// If the enum contains any fields with values that cannot be represented
/// by usize, a compile error is issued.  The max_unused_slots parameter limits
/// the total number of items which have no matching enum key (holes in the enum
/// numbering).  So for example, if an enum has values 1, 2, 5, and 6, max_unused_slots
/// must be at least 3, to allow unused slots 0, 3, and 4.
pub fn directEnumArrayLen(comptime E: type, comptime max_unused_slots: comptime_int) comptime_int {
    var max_value: comptime_int = -1;
    const max_usize: comptime_int = ~@as(usize, 0);
    const fields = @typeInfo(E).@"enum".fields;
    for (fields) |f| {
        if (f.value < 0) {
            @compileError("Cannot create a direct enum array for " ++ @typeName(E) ++ ", field ." ++ f.name ++ " has a negative value.");
        }
        if (f.value > max_value) {
            if (f.value > max_usize) {
                @compileError("Cannot create a direct enum array for " ++ @typeName(E) ++ ", field ." ++ f.name ++ " is larger than the max value of usize.");
            }
            max_value = f.value;
        }
    }

    const unused_slots = max_value + 1 - fields.len;
    if (unused_slots > max_unused_slots) {
        const unused_str = std.fmt.comptimePrint("{d}", .{unused_slots});
        const allowed_str = std.fmt.comptimePrint("{d}", .{max_unused_slots});
        @compileError("Cannot create a direct enum array for " ++ @typeName(E) ++ ". It would have " ++ unused_str ++ " unused slots, but only " ++ allowed_str ++ " are allowed.");
    }

    return max_value + 1;
}

/// Initializes an array of Data which can be indexed by
/// @intCast(usize, @intFromEnum(enum_value)).
/// If the enum is non-exhaustive, the resulting array will only be large enough
/// to hold all explicit fields.
/// If the enum contains any fields with values that cannot be represented
/// by usize, a compile error is issued.  The max_unused_slots parameter limits
/// the total number of items which have no matching enum key (holes in the enum
/// numbering).  So for example, if an enum has values 1, 2, 5, and 6, max_unused_slots
/// must be at least 3, to allow unused slots 0, 3, and 4.
/// The init_values parameter must be a struct with field names that match the enum values.
/// If the enum has multiple fields with the same value, the name of the first one must
/// be used.
pub fn directEnumArray(
    comptime E: type,
    comptime Data: type,
    comptime max_unused_slots: comptime_int,
    init_values: EnumFieldStruct(E, Data, null),
) [directEnumArrayLen(E, max_unused_slots)]Data {
    return directEnumArrayDefault(E, Data, null, max_unused_slots, init_values);
}

test directEnumArray {
    const E = enum(i4) { a = 4, b = 6, c = 2 };
    var runtime_false: bool = false;
    _ = &runtime_false;
    const array = directEnumArray(E, bool, 4, .{
        .a = true,
        .b = runtime_false,
        .c = true,
    });

    try testing.expectEqual([7]bool, @TypeOf(array));
    try testing.expectEqual(true, array[4]);
    try testing.expectEqual(false, array[6]);
    try testing.expectEqual(true, array[2]);
}

/// Initializes an array of Data which can be indexed by
/// @intCast(usize, @intFromEnum(enum_value)).  The enum must be exhaustive.
/// If the enum contains any fields with values that cannot be represented
/// by usize, a compile error is issued.  The max_unused_slots parameter limits
/// the total number of items which have no matching enum key (holes in the enum
/// numbering).  So for example, if an enum has values 1, 2, 5, and 6, max_unused_slots
/// must be at least 3, to allow unused slots 0, 3, and 4.
/// The init_values parameter must be a struct with field names that match the enum values.
/// If the enum has multiple fields with the same value, the name of the first one must
/// be used.
pub fn directEnumArrayDefault(
    comptime E: type,
    comptime Data: type,
    comptime default: ?Data,
    comptime max_unused_slots: comptime_int,
    init_values: EnumFieldStruct(E, Data, default),
) [directEnumArrayLen(E, max_unused_slots)]Data {
    const len = comptime directEnumArrayLen(E, max_unused_slots);
    var result: [len]Data = if (default) |d| [_]Data{d} ** len else undefined;
    inline for (@typeInfo(@TypeOf(init_values)).@"struct".fields) |f| {
        const enum_value = @field(E, f.name);
        const index = @as(usize, @intCast(@intFromEnum(enum_value)));
        result[index] = @field(init_values, f.name);
    }
    return result;
}

test directEnumArrayDefault {
    const E = enum(i4) { a = 4, b = 6, c = 2 };
    var runtime_false: bool = false;
    _ = &runtime_false;
    const array = directEnumArrayDefault(E, bool, false, 4, .{
        .a = true,
        .b = runtime_false,
    });

    try testing.expectEqual([7]bool, @TypeOf(array));
    try testing.expectEqual(true, array[4]);
    try testing.expectEqual(false, array[6]);
    try testing.expectEqual(false, array[2]);
}

test "directEnumArrayDefault slice" {
    const E = enum(i4) { a = 4, b = 6, c = 2 };
    var runtime_b = "b";
    _ = &runtime_b;
    const array = directEnumArrayDefault(E, []const u8, "default", 4, .{
        .a = "a",
        .b = runtime_b,
    });

    try testing.expectEqual([7][]const u8, @TypeOf(array));
    try testing.expectEqualSlices(u8, "a", array[4]);
    try testing.expectEqualSlices(u8, "b", array[6]);
    try testing.expectEqualSlices(u8, "default", array[2]);
}

/// Cast an enum literal, value, or string to the enum value of type E
/// with the same name.
pub fn nameCast(comptime E: type, comptime value: anytype) E {
    return comptime blk: {
        const V = @TypeOf(value);
        if (V == E) break :blk value;
        const name: ?[]const u8 = switch (@typeInfo(V)) {
            .enum_literal, .@"enum" => @tagName(value),
            .pointer => value,
            else => null,
        };
        if (name) |n| {
            if (@hasField(E, n)) {
                break :blk @field(E, n);
            }
            @compileError("Enum " ++ @typeName(E) ++ " has no field named " ++ n);
        }
        @compileError("Cannot cast from " ++ @typeName(@TypeOf(value)) ++ " to " ++ @typeName(E));
    };
}

test nameCast {
    const A = enum(u1) { a = 0, b = 1 };
    const B = enum(u1) { a = 1, b = 0 };
    try testing.expectEqual(A.a, nameCast(A, .a));
    try testing.expectEqual(A.a, nameCast(A, A.a));
    try testing.expectEqual(A.a, nameCast(A, B.a));
    try testing.expectEqual(A.a, nameCast(A, "a"));
    try testing.expectEqual(A.a, nameCast(A, @as(*const [1]u8, "a")));
    try testing.expectEqual(A.a, nameCast(A, @as([:0]const u8, "a")));
    try testing.expectEqual(A.a, nameCast(A, @as([]const u8, "a")));

    try testing.expectEqual(B.a, nameCast(B, .a));
    try testing.expectEqual(B.a, nameCast(B, A.a));
    try testing.expectEqual(B.a, nameCast(B, B.a));
    try testing.expectEqual(B.a, nameCast(B, "a"));

    try testing.expectEqual(B.b, nameCast(B, .b));
    try testing.expectEqual(B.b, nameCast(B, A.b));
    try testing.expectEqual(B.b, nameCast(B, B.b));
    try testing.expectEqual(B.b, nameCast(B, "b"));
}

/// A set of enum elements, backed by a bitfield.  If the enum
/// is exhaustive but 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 EnumSet(comptime E: type) type {
    return struct {
        const Self = @This();

        /// 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;

        const BitSet = std.StaticBitSet(Indexer.count);

        /// The maximum number of items in this set.
        pub const len = Indexer.count;

        bits: BitSet = BitSet.initEmpty(),

        /// Initializes the set using a struct of bools
        pub fn init(init_values: EnumFieldStruct(E, bool, false)) Self {
            @setEvalBranchQuota(2 * @typeInfo(E).@"enum".fields.len);
            var result: Self = .{};
            if (@typeInfo(E).@"enum".is_exhaustive) {
                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);
                    }
                }
            } else {
                inline for (std.meta.fields(E)) |field| {
                    const key = @field(E, field.name);
                    if (@field(init_values, field.name)) {
                        const i = comptime Indexer.indexOf(key);
                        result.bits.set(i);
                    }
                }
            }
            return result;
        }

        /// Returns a set containing no keys.
        pub fn initEmpty() Self {
            return .{ .bits = BitSet.initEmpty() };
        }

        /// Returns a set containing all possible keys.
        pub fn initFull() Self {
            return .{ .bits = BitSet.initFull() };
        }

        /// Returns a set containing multiple keys.
        pub fn initMany(keys: []const Key) Self {
            var set = initEmpty();
            for (keys) |key| set.insert(key);
            return set;
        }

        /// Returns a set containing a single key.
        pub fn initOne(key: Key) Self {
            return initMany(&[_]Key{key});
        }

        /// Returns the number of keys in the set.
        pub fn count(self: Self) usize {
            return self.bits.count();
        }

        /// Checks if a key is in the set.
        pub fn contains(self: Self, key: Key) bool {
            return self.bits.isSet(Indexer.indexOf(key));
        }

        /// Puts a key in the set.
        pub fn insert(self: *Self, key: Key) void {
            self.bits.set(Indexer.indexOf(key));
        }

        /// Removes a key from the set.
        pub fn remove(self: *Self, key: Key) void {
            self.bits.unset(Indexer.indexOf(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);
        }

        /// 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));
        }

        /// Toggles the presence of all keys in the passed set.
        pub fn toggleSet(self: *Self, other: Self) void {
            self.bits.toggleSet(other.bits);
        }

        /// Toggles all possible keys in the set.
        pub fn toggleAll(self: *Self) void {
            self.bits.toggleAll();
        }

        /// Adds all keys in the passed set to this set.
        pub fn setUnion(self: *Self, other: Self) void {
            self.bits.setUnion(other.bits);
        }

        /// 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 {
            return self.bits.eql(other.bits);
        }

        /// 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 {
            return self.bits.subsetOf(other.bits);
        }

        /// 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 {
            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) };
        }

        /// 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 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 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(.{}) };
        }

        pub const Iterator = struct {
            inner: BitSet.Iterator(.{}),

            pub fn next(self: *Iterator) ?Key {
                return if (self.inner.next()) |index|
                    Indexer.keyForIndex(index)
                else
                    null;
            }
        };
    };
}

/// A map keyed by an enum, backed by a bitfield and a dense array.
/// If the enum is exhaustive but 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();

        /// 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;

        const BitSet = std.StaticBitSet(Indexer.count);

        /// 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,

        /// Initializes the map using a sparse struct of optionals
        pub fn init(init_values: EnumFieldStruct(E, ?Value, @as(?Value, null))) Self {
            @setEvalBranchQuota(2 * @typeInfo(E).@"enum".fields.len);
            var result: Self = .{};
            if (@typeInfo(E).@"enum".is_exhaustive) {
                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.*;
                    }
                }
            } else {
                inline for (std.meta.fields(E)) |field| {
                    const key = @field(E, field.name);
                    if (@field(init_values, field.name)) |*v| {
                        const i = comptime Indexer.indexOf(key);
                        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: Value) 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, Value, null)) Self {
            return initFullWithDefault(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: ?Value, init_values: EnumFieldStruct(E, Value, default)) Self {
            @setEvalBranchQuota(2 * @typeInfo(E).@"enum".fields.len);
            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;
        }

        /// The number of items in the map.
        pub fn count(self: Self) usize {
            return self.bits.count();
        }

        /// Checks if the map contains an item.
        pub fn contains(self: Self, key: Key) bool {
            return self.bits.isSet(Indexer.indexOf(key));
        }

        /// 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;
        }

        /// 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];
        }

        /// 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;
        }

        /// 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;
        }

        /// 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];
        }

        /// 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];
        }

        /// 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;
        }

        /// 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];
        }

        /// 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;
        }

        /// 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;
        }

        /// 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;
        }

        /// 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,
            };
        }

        /// 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,

            /// 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,

            pub fn next(self: *Iterator) ?Entry {
                return if (self.inner.next()) |index|
                    Entry{
                        .key = Indexer.keyForIndex(index),
                        .value = &self.values[index],
                    }
                else
                    null;
            }
        };
    };
}

test EnumMap {
    const Ball = enum { red, green, blue };

    const some = EnumMap(Ball, u8).init(.{
        .green = 0xff,
        .blue = 0x80,
    });
    try testing.expectEqual(2, some.count());
    try testing.expectEqual(null, some.get(.red));
    try testing.expectEqual(0xff, some.get(.green));
    try testing.expectEqual(0x80, some.get(.blue));
}

/// 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);
}

/// 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();

        counts: EnumArray(E, CountSize),

        /// Initializes the multiset using a struct of counts.
        pub fn init(init_counts: EnumFieldStruct(E, CountSize, 0)) Self {
            @setEvalBranchQuota(2 * @typeInfo(E).@"enum".fields.len);
            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;
        }

        /// Initializes the multiset with a count of zero.
        pub fn initEmpty() Self {
            return initWithCount(0);
        }

        /// 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 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;
        }

        /// Checks if at least one key in multiset.
        pub fn contains(self: Self, key: E) bool {
            return self.counts.get(key) > 0;
        }

        /// 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);
        }

        /// 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;
        }

        /// 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));
        }

        /// 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);
        }

        /// Returns the count for a key.
        pub fn getCount(self: Self, key: E) CountSize {
            return self.counts.get(key);
        }

        /// Set the count for a key.
        pub fn setCount(self: *Self, key: E, c: CountSize) void {
            self.counts.set(key, c);
        }

        /// 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));
            }
        }

        /// 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));
            }
        }

        /// 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 all key counts are the same as
        /// given multiset.
        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;
        }

        /// Returns true iff all key counts less than or
        /// equal to the given multiset.
        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;
        }

        /// Returns true iff all key counts greater than or
        /// equal to the given multiset.
        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;
        }

        /// 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 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 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 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();
        }
    };
}

test EnumMultiset {
    const Ball = enum { red, green, blue };

    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));
    }

    {
        var copy = ten_of_each;
        copy.addAssertSafe(.red, 6);
        try testing.expectEqual(copy.getCount(.red), 16);
    }

    {
        var copy = ten_of_each;
        try copy.add(.red, 6);
        try testing.expectEqual(copy.getCount(.red), 16);

        try testing.expectError(error.Overflow, copy.add(.red, std.math.maxInt(usize)));
    }

    {
        var copy = ten_of_each;
        copy.remove(.red, 4);
        try testing.expectEqual(copy.getCount(.red), 6);

        // subtracting more it contains does not underflow
        copy.remove(.green, 14);
        try testing.expectEqual(copy.getCount(.green), 0);
    }

    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);

    {
        var copy = empty;
        copy.setCount(.red, 6);
        try testing.expectEqual(copy.getCount(.red), 6);
    }

    {
        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);
    }

    {
        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);

        const full = EnumMultiset(Ball).initWithCount(std.math.maxInt(usize));
        try testing.expectError(error.Overflow, copy.addSet(full));
    }

    {
        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);

        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);
    }

    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));

    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));

    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));

    {
        // with multisets it could be the case where two
        // multisets are neither subset nor superset of each
        // other.

        const r10 = EnumMultiset(Ball).init(.{
            .red = 10,
        });
        const b10 = EnumMultiset(Ball).init(.{
            .blue = 10,
        });

        try testing.expect(!r10.subsetOf(b10));
        try testing.expect(!b10.subsetOf(r10));
        try testing.expect(!r10.supersetOf(b10));
        try testing.expect(!b10.supersetOf(r10));
    }

    {
        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);
    }

    {
        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);

        const full = EnumMultiset(Ball).initWithCount(std.math.maxInt(usize));
        try testing.expectError(error.Overflow, result.plus(full));
    }

    {
        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);
    }

    {
        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);
    }

    {
        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);
    }

    {
        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 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();

        /// 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;

        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 {
            @setEvalBranchQuota(2 * @typeInfo(E).@"enum".fields.len);
            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 };
        }

        pub fn initFill(v: Value) Self {
            var self: Self = undefined;
            @memset(&self.values, v);
            return self;
        }

        /// Returns the value in the array associated with a key.
        pub fn get(self: Self, key: Key) Value {
            return self.values[Indexer.indexOf(key)];
        }

        /// Returns a pointer to the slot in the array associated with a key.
        pub fn getPtr(self: *Self, key: Key) *Value {
            return &self.values[Indexer.indexOf(key)];
        }

        /// Returns a const pointer to the slot in the array associated with a key.
        pub fn getPtrConst(self: *const Self, key: Key) *const Value {
            return &self.values[Indexer.indexOf(key)];
        }

        /// Sets the value in the slot associated with a key.
        pub fn set(self: *Self, key: Key, value: Value) void {
            self.values[Indexer.indexOf(key)] = value;
        }

        /// Iterates over the items in the array, in index order.
        pub fn iterator(self: *Self) Iterator {
            return .{
                .values = &self.values,
            };
        }

        /// An entry in the array.
        pub const Entry = struct {
            /// The key associated with this entry.
            /// Modifying this key will not change the array.
            key: Key,

            /// A pointer to the value in the array associated
            /// with this key.  Modifications through this
            /// pointer will modify the underlying data.
            value: *Value,
        };

        pub const Iterator = struct {
            index: usize = 0,
            values: *[Indexer.count]Value,

            pub fn next(self: *Iterator) ?Entry {
                const index = self.index;
                if (index < Indexer.count) {
                    self.index += 1;
                    return Entry{
                        .key = Indexer.keyForIndex(index),
                        .value = &self.values[index],
                    };
                }
                return null;
            }
        };
    };
}

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 "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));
}

test "EnumSet non-exhaustive" {
    const BitIndices = enum(u4) {
        a = 0,
        b = 1,
        c = 4,
        _,
    };
    const BitField = EnumSet(BitIndices);

    var flags = BitField.init(.{ .a = true, .b = true });
    flags.insert(.c);
    flags.remove(.a);
    try testing.expect(!flags.contains(.a));
    try testing.expect(flags.contains(.b));
    try testing.expect(flags.contains(.c));
}

pub fn EnumIndexer(comptime E: type) type {
    // Assumes that the enum fields are sorted in ascending order (optimistic).
    // Unsorted enums may require the user to manually increase the quota.
    @setEvalBranchQuota(3 * @typeInfo(E).@"enum".fields.len + eval_branch_quota_cushion);

    if (!@typeInfo(E).@"enum".is_exhaustive) {
        const BackingInt = @typeInfo(E).@"enum".tag_type;
        if (@bitSizeOf(BackingInt) > @bitSizeOf(usize))
            @compileError("Cannot create an enum indexer for a given non-exhaustive enum, tag_type is larger than usize.");

        return struct {
            pub const Key: type = E;

            const backing_int_sign = @typeInfo(BackingInt).int.signedness;
            const min_value = std.math.minInt(BackingInt);
            const max_value = std.math.maxInt(BackingInt);

            const RangeType = std.meta.Int(.unsigned, @bitSizeOf(BackingInt));
            pub const count: comptime_int = std.math.maxInt(RangeType) + 1;

            pub fn indexOf(e: E) usize {
                if (backing_int_sign == .unsigned)
                    return @intFromEnum(e);

                return if (@intFromEnum(e) < 0)
                    @intCast(@intFromEnum(e) - min_value)
                else
                    @as(RangeType, -min_value) + @as(RangeType, @intCast(@intFromEnum(e)));
            }
            pub fn keyForIndex(i: usize) E {
                if (backing_int_sign == .unsigned)
                    return @enumFromInt(i);

                return @enumFromInt(@as(std.meta.Int(.signed, @bitSizeOf(RangeType) + 1), @intCast(i)) + min_value);
            }
        };
    }

    const const_fields = @typeInfo(E).@"enum".fields;
    var fields = const_fields[0..const_fields.len].*;
    const fields_len = fields.len;

    if (fields_len == 0) {
        return struct {
            pub const Key = E;
            pub const count: comptime_int = 0;
            pub fn indexOf(e: E) usize {
                _ = e;
                unreachable;
            }
            pub fn keyForIndex(i: usize) E {
                _ = i;
                unreachable;
            }
        };
    }

    const min = fields[0].value;
    const max = fields[fields.len - 1].value;

    const SortContext = struct {
        fields: []EnumField,

        pub fn lessThan(comptime ctx: @This(), comptime a: usize, comptime b: usize) bool {
            return ctx.fields[a].value < ctx.fields[b].value;
        }

        pub fn swap(comptime ctx: @This(), comptime a: usize, comptime b: usize) void {
            return std.mem.swap(EnumField, &ctx.fields[a], &ctx.fields[b]);
        }
    };
    std.sort.insertionContext(0, fields_len, SortContext{ .fields = &fields });

    if (max - min == fields.len - 1) {
        return struct {
            pub const Key = E;
            pub const count: comptime_int = fields_len;
            pub fn indexOf(e: E) usize {
                return @as(usize, @intCast(@intFromEnum(e) - min));
            }
            pub fn keyForIndex(i: usize) E {
                // TODO fix addition semantics.  This calculation
                // gives up some safety to avoid artificially limiting
                // the range of signed enum values to max_isize.
                const enum_value = if (min < 0) @as(isize, @bitCast(i)) +% min else i + min;
                return @as(E, @enumFromInt(@as(@typeInfo(E).@"enum".tag_type, @intCast(enum_value))));
            }
        };
    }

    const keys = valuesFromFields(E, &fields);

    return struct {
        pub const Key = E;
        pub const count: comptime_int = fields_len;
        pub fn indexOf(e: E) usize {
            for (keys, 0..) |k, i| {
                if (k == e) return i;
            }
            unreachable;
        }
        pub fn keyForIndex(i: usize) E {
            return keys[i];
        }
    };
}

test "EnumIndexer non-exhaustive" {
    const backing_ints = [_]type{
        i1,
        i2,
        i3,
        i4,
        i8,
        i16,
        std.meta.Int(.signed, @bitSizeOf(isize) - 1),
        isize,
        u1,
        u2,
        u3,
        u4,
        u16,
        std.meta.Int(.unsigned, @bitSizeOf(usize) - 1),
        usize,
    };
    inline for (backing_ints) |BackingInt| {
        const E = enum(BackingInt) {
            number_zero_tag = 0,
            _,
        };
        const Indexer = EnumIndexer(E);

        const min_tag: E = @enumFromInt(std.math.minInt(BackingInt));
        const max_tag: E = @enumFromInt(std.math.maxInt(BackingInt));

        const RangedType = std.meta.Int(.unsigned, @bitSizeOf(BackingInt));
        const max_index: comptime_int = std.math.maxInt(RangedType);
        const number_zero_tag_index: usize = switch (@typeInfo(BackingInt).int.signedness) {
            .unsigned => 0,
            .signed => std.math.divCeil(comptime_int, max_index, 2) catch unreachable,
        };

        try testing.expectEqual(E, Indexer.Key);
        try testing.expectEqual(max_index + 1, Indexer.count);

        try testing.expectEqual(@as(usize, 0), Indexer.indexOf(min_tag));
        try testing.expectEqual(number_zero_tag_index, Indexer.indexOf(E.number_zero_tag));
        try testing.expectEqual(@as(usize, max_index), Indexer.indexOf(max_tag));

        try testing.expectEqual(min_tag, Indexer.keyForIndex(0));
        try testing.expectEqual(E.number_zero_tag, Indexer.keyForIndex(number_zero_tag_index));
        try testing.expectEqual(max_tag, Indexer.keyForIndex(max_index));
    }
}

test "EnumIndexer dense zeroed" {
    const E = enum(u2) { b = 1, a = 0, c = 2 };
    const Indexer = EnumIndexer(E);
    try testing.expectEqual(E, Indexer.Key);
    try testing.expectEqual(3, Indexer.count);

    try testing.expectEqual(@as(usize, 0), Indexer.indexOf(.a));
    try testing.expectEqual(@as(usize, 1), Indexer.indexOf(.b));
    try testing.expectEqual(@as(usize, 2), Indexer.indexOf(.c));

    try testing.expectEqual(E.a, Indexer.keyForIndex(0));
    try testing.expectEqual(E.b, Indexer.keyForIndex(1));
    try testing.expectEqual(E.c, Indexer.keyForIndex(2));
}

test "EnumIndexer dense positive" {
    const E = enum(u4) { c = 6, a = 4, b = 5 };
    const Indexer = EnumIndexer(E);
    try testing.expectEqual(E, Indexer.Key);
    try testing.expectEqual(3, Indexer.count);

    try testing.expectEqual(@as(usize, 0), Indexer.indexOf(.a));
    try testing.expectEqual(@as(usize, 1), Indexer.indexOf(.b));
    try testing.expectEqual(@as(usize, 2), Indexer.indexOf(.c));

    try testing.expectEqual(E.a, Indexer.keyForIndex(0));
    try testing.expectEqual(E.b, Indexer.keyForIndex(1));
    try testing.expectEqual(E.c, Indexer.keyForIndex(2));
}

test "EnumIndexer dense negative" {
    const E = enum(i4) { a = -6, c = -4, b = -5 };
    const Indexer = EnumIndexer(E);
    try testing.expectEqual(E, Indexer.Key);
    try testing.expectEqual(3, Indexer.count);

    try testing.expectEqual(@as(usize, 0), Indexer.indexOf(.a));
    try testing.expectEqual(@as(usize, 1), Indexer.indexOf(.b));
    try testing.expectEqual(@as(usize, 2), Indexer.indexOf(.c));

    try testing.expectEqual(E.a, Indexer.keyForIndex(0));
    try testing.expectEqual(E.b, Indexer.keyForIndex(1));
    try testing.expectEqual(E.c, Indexer.keyForIndex(2));
}

test "EnumIndexer sparse" {
    const E = enum(i4) { a = -2, c = 6, b = 4 };
    const Indexer = EnumIndexer(E);
    try testing.expectEqual(E, Indexer.Key);
    try testing.expectEqual(3, Indexer.count);

    try testing.expectEqual(@as(usize, 0), Indexer.indexOf(.a));
    try testing.expectEqual(@as(usize, 1), Indexer.indexOf(.b));
    try testing.expectEqual(@as(usize, 2), Indexer.indexOf(.c));

    try testing.expectEqual(E.a, Indexer.keyForIndex(0));
    try testing.expectEqual(E.b, Indexer.keyForIndex(1));
    try testing.expectEqual(E.c, Indexer.keyForIndex(2));
}

test "EnumIndexer empty" {
    const E = enum {};
    const Indexer = EnumIndexer(E);
    try testing.expectEqual(E, Indexer.Key);
    try testing.expectEqual(0, Indexer.count);
}

test values {
    const E = enum {
        X,
        Y,
        Z,
        const A = 1;
    };
    try testing.expectEqualSlices(E, &.{ .X, .Y, .Z }, values(E));
}