diff --git a/src/array_hash_set/managed.zig b/src/array_hash_set/managed.zig index b71bb84..fd43ee6 100644 --- a/src/array_hash_set/managed.zig +++ b/src/array_hash_set/managed.zig @@ -816,6 +816,7 @@ test "benchmark" { const allocator = std.testing.allocator; const Iterations = 10_000; const SetSize = 1000; + const io = std.testing.io; // Setup var base = try ArraySetManaged(u32).initCapacity(allocator, SetSize); @@ -827,37 +828,40 @@ test "benchmark" { for (0..SetSize) |i| _ = other.addAssumeCapacity(@intCast(i + SetSize / 2)); // Benchmark unionOf - var union_timer = try std.time.Timer.start(); + const union_start = std.Io.Timestamp.now(io, .awake); for (0..Iterations) |_| { var result = try base.unionOf(other); defer result.deinit(); } - const union_elapsed = union_timer.read(); + const union_elapsed = union_start.untilNow(io, .awake); + std.debug.print("\nunionOf: {d} ops/sec ({d:.2} ns/op)\n", .{ - Iterations * std.time.ns_per_s / union_elapsed, - @as(f64, @floatFromInt(union_elapsed)) / @as(f64, @floatFromInt(Iterations)), + @as(f64, @floatFromInt(Iterations * std.time.ns_per_s)) / @as(f64, @floatFromInt(union_elapsed.toNanoseconds())), + @as(f64, @floatFromInt(union_elapsed.toNanoseconds())) / @as(f64, @floatFromInt(Iterations)), }); - // Benchmark intersectionOf - var inter_timer = try std.time.Timer.start(); + const inter_start = std.Io.Timestamp.now(io, .awake); for (0..Iterations) |_| { var result = try base.intersectionOf(other); defer result.deinit(); } - const inter_elapsed = inter_timer.read(); - std.debug.print("intersectionOf: {d} ops/sec ({d:.2} ns/op)\n", .{ - Iterations * std.time.ns_per_s / inter_elapsed, - @as(f64, @floatFromInt(inter_elapsed)) / @as(f64, @floatFromInt(Iterations)), + const inter_elapsed = inter_start.untilNow(io, .awake); + + std.debug.print("\nintersectionOf: {d} ops/sec ({d:.2} ns/op)\n", .{ + @as(f64, @floatFromInt(Iterations * std.time.ns_per_s)) / @as(f64, @floatFromInt(inter_elapsed.toNanoseconds())), + @as(f64, @floatFromInt(inter_elapsed.toNanoseconds())) / @as(f64, @floatFromInt(Iterations)), }); - // Benchmark containsAll - var contains_timer = try std.time.Timer.start(); + // Benchmark containsAll + const contains_start = std.Io.Timestamp.now(io, .awake); for (0..Iterations) |_| { _ = base.containsAll(other); } - const contains_elapsed = contains_timer.read(); - std.debug.print("containsAll: {d} ops/sec ({d:.2} ns/op)\n", .{ - Iterations * std.time.ns_per_s / contains_elapsed, - @as(f64, @floatFromInt(contains_elapsed)) / @as(f64, @floatFromInt(Iterations)), + const contains_elapsed = contains_start.untilNow(io, .awake); + + std.debug.print("\ncontainsAll: {d} ops/sec ({d:.2} ns/op)\n", .{ + @as(f64, @floatFromInt(Iterations * std.time.ns_per_s)) / @as(f64, @floatFromInt(contains_elapsed.toNanoseconds())), + @as(f64, @floatFromInt(contains_elapsed.toNanoseconds())) / @as(f64, @floatFromInt(Iterations)), }); + } diff --git a/src/hash_set/unmanaged.zig b/src/hash_set/unmanaged.zig index b9f9d77..5680005 100644 --- a/src/hash_set/unmanaged.zig +++ b/src/hash_set/unmanaged.zig @@ -242,22 +242,12 @@ pub fn HashSetUnmanagedWithContext(comptime E: type, comptime Context: type, com /// differenceUpdate does an in-place mutation of this set /// and other. This set will contain all elements of this set that are not /// also elements of other. - pub fn differenceUpdate(self: *Self, allocator: Allocator, other: Self) Allocator.Error!void { - // In-place mutation invalidates iterators therefore a temp set is needed. - // So instead of a temp set, just invoke the regular full function which - // allocates and returns a set then swap out the map internally. - - // Also, this saves a step of not having to possibly discard many elements - // from the self set. - - // Just get a new set with the normal method. - const diffSet = try self.differenceOf(allocator, other); - - // Destroy the internal map. - self.unmanaged.deinit(allocator); - - // Swap it out with the new set. - self.unmanaged = diffSet.unmanaged; + pub fn differenceUpdate(self: *Self, other: Self) Allocator.Error!void { + var iter = other.iterator(); + + while (iter.next()) |key_ptr| { + _ = self.remove(key_ptr.*); + } } fn dump(self: Self) void { @@ -334,21 +324,19 @@ pub fn HashSetUnmanagedWithContext(comptime E: type, comptime Context: type, com /// to the current set from the other set keeping only /// elements found in this Set and the other Set. pub fn intersectionUpdate(self: *Self, allocator: Allocator, other: Self) Allocator.Error!void { - // In-place mutation invalidates iterators therefore a temp set is needed. - // So instead of a temp set, just invoke the regular full function which - // allocates and returns a set then swap out the map internally. - - // Also, this saves a step of not having to possibly discard many elements - // from the self set. - - // Just get a new set with the normal method. - const interSet = try self.intersectionOf(allocator, other); + var to_remove: std.ArrayList(E) = .empty; + defer to_remove.deinit(allocator); - // Destroy the internal map. - self.unmanaged.deinit(allocator); + var iter = self.iterator(); + while (iter.next()) |key_ptr| { + if (!other.contains(key_ptr.*)) { + try to_remove.append(allocator, key_ptr.*); + } + } - // Swap it out with the new set. - self.unmanaged = interSet.unmanaged; + for (to_remove.items) |item| { + _ = self.remove(item); + } } /// isDisjoint returns true if the intersection between two sets is the null set. @@ -483,21 +471,16 @@ pub fn HashSetUnmanagedWithContext(comptime E: type, comptime Context: type, com /// symmetricDifferenceUpdate does an in-place mutation with all elements /// which are in either this set or the other set but not in both. pub fn symmetricDifferenceUpdate(self: *Self, allocator: Allocator, other: Self) Allocator.Error!void { - // In-place mutation invalidates iterators therefore a temp set is needed. - // So instead of a temp set, just invoke the regular full function which - // allocates and returns a set then swap out the map internally. - - // Also, this saves a step of not having to possibly discard many elements - // from the self set. - - // Just get a new set with the normal method. - const sd = try self.symmetricDifferenceOf(allocator, other); - - // Destroy the internal map. - self.unmanaged.deinit(allocator); + var iter = other.iterator(); + while (iter.next()) |key_ptr| { + const element = key_ptr.*; - // Swap it out with the new set. - self.unmanaged = sd.unmanaged; + if (self.contains(element)) { + _ = self.remove(element); + } else { + _ = try self.add(allocator, element); + } + } } /// union returns a new set with all elements in both sets. @@ -812,7 +795,7 @@ test "in-place methods" { defer f.deinit(testing.allocator); _ = try f.appendSlice(testing.allocator, &.{ 1, 11, 111, 222, 2222, 1111 }); - try e.differenceUpdate(testing.allocator, f); + try e.differenceUpdate(f); try expectEqual(1, e.cardinality()); try expect(e.contains(11111));