| ... | ... | @@ -304,6 +304,346 @@ pub fn EnumMap(comptime E: type, comptime V: type) type { |
| 304 | 304 | return IndexedMap(EnumIndexer(E), V, mixin.EnumMapExt); |
| 305 | 305 | } |
| 306 | 306 | |
| 307 | /// A multiset of enum elements up to a count of usize. Backed |
| 308 | /// by an EnumArray. This type does no dynamic allocation and can |
| 309 | /// be copied by value. |
| 310 | pub fn EnumMultiset(comptime E: type) type { |
| 311 | return BoundedEnumMultiset(E, usize); |
| 312 | } |
| 313 | |
| 314 | /// A multiset of enum elements up to CountSize. Backed by an |
| 315 | /// EnumArray. This type does no dynamic allocation and can be |
| 316 | /// copied by value. |
| 317 | pub fn BoundedEnumMultiset(comptime E: type, comptime CountSize: type) type { |
| 318 | return struct { |
| 319 | const Self = @This(); |
| 320 | |
| 321 | counts: EnumArray(E, CountSize), |
| 322 | |
| 323 | /// Initializes the multiset using a struct of counts. |
| 324 | pub fn init(init_counts: EnumFieldStruct(E, CountSize, 0)) Self { |
| 325 | var self = initWithCount(0); |
| 326 | inline for (@typeInfo(E).Enum.fields) |field| { |
| 327 | const c = @field(init_counts, field.name); |
| 328 | const key = @intToEnum(E, field.value); |
| 329 | self.counts.set(key, c); |
| 330 | } |
| 331 | return self; |
| 332 | } |
| 333 | |
| 334 | /// Initializes the multiset with a count of zero. |
| 335 | pub fn initEmpty() Self { |
| 336 | return initWithCount(0); |
| 337 | } |
| 338 | |
| 339 | /// Initializes the multiset with all keys at the |
| 340 | /// same count. |
| 341 | pub fn initWithCount(comptime c: CountSize) Self { |
| 342 | return .{ |
| 343 | .counts = EnumArray(E, CountSize).initDefault(c, .{}), |
| 344 | }; |
| 345 | } |
| 346 | |
| 347 | /// Returns the total number of key counts in the multiset. |
| 348 | pub fn count(self: Self) usize { |
| 349 | var sum: usize = 0; |
| 350 | for (self.counts.values) |c| { |
| 351 | sum += c; |
| 352 | } |
| 353 | return sum; |
| 354 | } |
| 355 | |
| 356 | /// Checks if at least one key in multiset. |
| 357 | pub fn contains(self: Self, key: E) bool { |
| 358 | return self.counts.get(key) > 0; |
| 359 | } |
| 360 | |
| 361 | /// Removes all instance of a key from multiset. Same as |
| 362 | /// setCount(key, 0). |
| 363 | pub fn removeAll(self: *Self, key: E) void { |
| 364 | return self.counts.set(key, 0); |
| 365 | } |
| 366 | |
| 367 | /// Increases the key count by given amount. Caller asserts |
| 368 | /// operation will not overflow. |
| 369 | pub fn addAssertSafe(self: *Self, key: E, c: CountSize) void { |
| 370 | self.counts.getPtr(key).* += c; |
| 371 | } |
| 372 | |
| 373 | /// Increases the key count by given amount. |
| 374 | pub fn add(self: *Self, key: E, c: CountSize) error{Overflow}!void { |
| 375 | self.counts.set(key, try std.math.add(CountSize, self.counts.get(key), c)); |
| 376 | } |
| 377 | |
| 378 | /// Decreases the key count by given amount. If amount is |
| 379 | /// greater than the number of keys in multset, then key count |
| 380 | /// will be set to zero. |
| 381 | pub fn remove(self: *Self, key: E, c: CountSize) void { |
| 382 | self.counts.getPtr(key).* -= @min(self.getCount(key), c); |
| 383 | } |
| 384 | |
| 385 | /// Returns the count for a key. |
| 386 | pub fn getCount(self: Self, key: E) CountSize { |
| 387 | return self.counts.get(key); |
| 388 | } |
| 389 | |
| 390 | /// Set the count for a key. |
| 391 | pub fn setCount(self: *Self, key: E, c: CountSize) void { |
| 392 | self.counts.set(key, c); |
| 393 | } |
| 394 | |
| 395 | /// Increases the all key counts by given multiset. Caller |
| 396 | /// asserts operation will not overflow any key. |
| 397 | pub fn addSetAssertSafe(self: *Self, other: Self) void { |
| 398 | inline for (@typeInfo(E).Enum.fields) |field| { |
| 399 | const key = @intToEnum(E, field.value); |
| 400 | self.addAssertSafe(key, other.getCount(key)); |
| 401 | } |
| 402 | } |
| 403 | |
| 404 | /// Increases the all key counts by given multiset. |
| 405 | pub fn addSet(self: *Self, other: Self) error{Overflow}!void { |
| 406 | inline for (@typeInfo(E).Enum.fields) |field| { |
| 407 | const key = @intToEnum(E, field.value); |
| 408 | try self.add(key, other.getCount(key)); |
| 409 | } |
| 410 | } |
| 411 | |
| 412 | /// Deccreases the all key counts by given multiset. If |
| 413 | /// the given multiset has more key counts than this, |
| 414 | /// then that key will have a key count of zero. |
| 415 | pub fn removeSet(self: *Self, other: Self) void { |
| 416 | inline for (@typeInfo(E).Enum.fields) |field| { |
| 417 | const key = @intToEnum(E, field.value); |
| 418 | self.remove(key, other.getCount(key)); |
| 419 | } |
| 420 | } |
| 421 | |
| 422 | /// Returns true iff all key counts are the same as |
| 423 | /// given multiset. |
| 424 | pub fn eql(self: Self, other: Self) bool { |
| 425 | inline for (@typeInfo(E).Enum.fields) |field| { |
| 426 | const key = @intToEnum(E, field.value); |
| 427 | if (self.getCount(key) != other.getCount(key)) { |
| 428 | return false; |
| 429 | } |
| 430 | } |
| 431 | return true; |
| 432 | } |
| 433 | |
| 434 | /// Returns a multiset with the total key count of this |
| 435 | /// multiset and the other multiset. Caller asserts |
| 436 | /// operation will not overflow any key. |
| 437 | pub fn plusAssertSafe(self: Self, other: Self) Self { |
| 438 | var result = self; |
| 439 | result.addSetAssertSafe(other); |
| 440 | return result; |
| 441 | } |
| 442 | |
| 443 | /// Returns a multiset with the total key count of this |
| 444 | /// multiset and the other multiset. |
| 445 | pub fn plus(self: Self, other: Self) error{Overflow}!Self { |
| 446 | var result = self; |
| 447 | try result.addSet(other); |
| 448 | return result; |
| 449 | } |
| 450 | |
| 451 | /// Returns a multiset with the key count of this |
| 452 | /// multiset minus the corresponding key count in the |
| 453 | /// other multiset. If the other multiset contains |
| 454 | /// more key count than this set, that key will have |
| 455 | /// a count of zero. |
| 456 | pub fn minus(self: Self, other: Self) Self { |
| 457 | var result = self; |
| 458 | result.removeSet(other); |
| 459 | return result; |
| 460 | } |
| 461 | |
| 462 | pub const Entry = EnumArray(E, CountSize).Entry; |
| 463 | pub const Iterator = EnumArray(E, CountSize).Iterator; |
| 464 | |
| 465 | /// Returns an iterator over this multiset. Keys with zero |
| 466 | /// counts are included. Modifications to the set during |
| 467 | /// iteration may or may not be observed by the iterator, |
| 468 | /// but will not invalidate it. |
| 469 | pub fn iterator(self: *Self) Iterator { |
| 470 | return self.counts.iterator(); |
| 471 | } |
| 472 | }; |
| 473 | } |
| 474 | |
| 475 | test "EnumMultiset" { |
| 476 | const Ball = enum { red, green, blue }; |
| 477 | |
| 478 | const empty = EnumMultiset(Ball).initEmpty(); |
| 479 | const r0_g1_b2 = EnumMultiset(Ball).init(.{ |
| 480 | .red = 0, |
| 481 | .green = 1, |
| 482 | .blue = 2, |
| 483 | }); |
| 484 | const ten_of_each = EnumMultiset(Ball).initWithCount(10); |
| 485 | |
| 486 | try testing.expectEqual(empty.count(), 0); |
| 487 | try testing.expectEqual(r0_g1_b2.count(), 3); |
| 488 | try testing.expectEqual(ten_of_each.count(), 30); |
| 489 | |
| 490 | try testing.expect(!empty.contains(.red)); |
| 491 | try testing.expect(!empty.contains(.green)); |
| 492 | try testing.expect(!empty.contains(.blue)); |
| 493 | |
| 494 | try testing.expect(!r0_g1_b2.contains(.red)); |
| 495 | try testing.expect(r0_g1_b2.contains(.green)); |
| 496 | try testing.expect(r0_g1_b2.contains(.blue)); |
| 497 | |
| 498 | try testing.expect(ten_of_each.contains(.red)); |
| 499 | try testing.expect(ten_of_each.contains(.green)); |
| 500 | try testing.expect(ten_of_each.contains(.blue)); |
| 501 | |
| 502 | { |
| 503 | var copy = ten_of_each; |
| 504 | copy.removeAll(.red); |
| 505 | try testing.expect(!copy.contains(.red)); |
| 506 | |
| 507 | // removeAll second time does nothing |
| 508 | copy.removeAll(.red); |
| 509 | try testing.expect(!copy.contains(.red)); |
| 510 | } |
| 511 | |
| 512 | { |
| 513 | var copy = ten_of_each; |
| 514 | copy.addAssertSafe(.red, 6); |
| 515 | try testing.expectEqual(copy.getCount(.red), 16); |
| 516 | } |
| 517 | |
| 518 | { |
| 519 | var copy = ten_of_each; |
| 520 | try copy.add(.red, 6); |
| 521 | try testing.expectEqual(copy.getCount(.red), 16); |
| 522 | |
| 523 | try testing.expectError(error.Overflow, copy.add(.red, std.math.maxInt(usize))); |
| 524 | } |
| 525 | |
| 526 | { |
| 527 | var copy = ten_of_each; |
| 528 | copy.remove(.red, 4); |
| 529 | try testing.expectEqual(copy.getCount(.red), 6); |
| 530 | |
| 531 | // subtracting more it contains does not underflow |
| 532 | copy.remove(.green, 14); |
| 533 | try testing.expectEqual(copy.getCount(.green), 0); |
| 534 | } |
| 535 | |
| 536 | try testing.expectEqual(empty.getCount(.green), 0); |
| 537 | try testing.expectEqual(r0_g1_b2.getCount(.green), 1); |
| 538 | try testing.expectEqual(ten_of_each.getCount(.green), 10); |
| 539 | |
| 540 | { |
| 541 | var copy = empty; |
| 542 | copy.setCount(.red, 6); |
| 543 | try testing.expectEqual(copy.getCount(.red), 6); |
| 544 | } |
| 545 | |
| 546 | { |
| 547 | var copy = r0_g1_b2; |
| 548 | copy.addSetAssertSafe(ten_of_each); |
| 549 | try testing.expectEqual(copy.getCount(.red), 10); |
| 550 | try testing.expectEqual(copy.getCount(.green), 11); |
| 551 | try testing.expectEqual(copy.getCount(.blue), 12); |
| 552 | } |
| 553 | |
| 554 | { |
| 555 | var copy = r0_g1_b2; |
| 556 | try copy.addSet(ten_of_each); |
| 557 | try testing.expectEqual(copy.getCount(.red), 10); |
| 558 | try testing.expectEqual(copy.getCount(.green), 11); |
| 559 | try testing.expectEqual(copy.getCount(.blue), 12); |
| 560 | |
| 561 | const full = EnumMultiset(Ball).initWithCount(std.math.maxInt(usize)); |
| 562 | try testing.expectError(error.Overflow, copy.addSet(full)); |
| 563 | } |
| 564 | |
| 565 | { |
| 566 | var copy = ten_of_each; |
| 567 | copy.removeSet(r0_g1_b2); |
| 568 | try testing.expectEqual(copy.getCount(.red), 10); |
| 569 | try testing.expectEqual(copy.getCount(.green), 9); |
| 570 | try testing.expectEqual(copy.getCount(.blue), 8); |
| 571 | |
| 572 | copy.removeSet(ten_of_each); |
| 573 | try testing.expectEqual(copy.getCount(.red), 0); |
| 574 | try testing.expectEqual(copy.getCount(.green), 0); |
| 575 | try testing.expectEqual(copy.getCount(.blue), 0); |
| 576 | } |
| 577 | |
| 578 | try testing.expect(empty.eql(empty)); |
| 579 | try testing.expect(r0_g1_b2.eql(r0_g1_b2)); |
| 580 | try testing.expect(ten_of_each.eql(ten_of_each)); |
| 581 | try testing.expect(!empty.eql(r0_g1_b2)); |
| 582 | try testing.expect(!r0_g1_b2.eql(ten_of_each)); |
| 583 | try testing.expect(!ten_of_each.eql(empty)); |
| 584 | |
| 585 | { |
| 586 | const result = r0_g1_b2.plusAssertSafe(ten_of_each); |
| 587 | try testing.expectEqual(result.getCount(.red), 10); |
| 588 | try testing.expectEqual(result.getCount(.green), 11); |
| 589 | try testing.expectEqual(result.getCount(.blue), 12); |
| 590 | } |
| 591 | |
| 592 | { |
| 593 | const result = try r0_g1_b2.plus(ten_of_each); |
| 594 | try testing.expectEqual(result.getCount(.red), 10); |
| 595 | try testing.expectEqual(result.getCount(.green), 11); |
| 596 | try testing.expectEqual(result.getCount(.blue), 12); |
| 597 | |
| 598 | const full = EnumMultiset(Ball).initWithCount(std.math.maxInt(usize)); |
| 599 | try testing.expectError(error.Overflow, result.plus(full)); |
| 600 | } |
| 601 | |
| 602 | { |
| 603 | const result = ten_of_each.minus(r0_g1_b2); |
| 604 | try testing.expectEqual(result.getCount(.red), 10); |
| 605 | try testing.expectEqual(result.getCount(.green), 9); |
| 606 | try testing.expectEqual(result.getCount(.blue), 8); |
| 607 | } |
| 608 | |
| 609 | { |
| 610 | const result = ten_of_each.minus(r0_g1_b2).minus(ten_of_each); |
| 611 | try testing.expectEqual(result.getCount(.red), 0); |
| 612 | try testing.expectEqual(result.getCount(.green), 0); |
| 613 | try testing.expectEqual(result.getCount(.blue), 0); |
| 614 | } |
| 615 | |
| 616 | { |
| 617 | var copy = empty; |
| 618 | var it = copy.iterator(); |
| 619 | var entry = it.next().?; |
| 620 | try testing.expectEqual(entry.key, .red); |
| 621 | try testing.expectEqual(entry.value.*, 0); |
| 622 | entry = it.next().?; |
| 623 | try testing.expectEqual(entry.key, .green); |
| 624 | try testing.expectEqual(entry.value.*, 0); |
| 625 | entry = it.next().?; |
| 626 | try testing.expectEqual(entry.key, .blue); |
| 627 | try testing.expectEqual(entry.value.*, 0); |
| 628 | try testing.expectEqual(it.next(), null); |
| 629 | } |
| 630 | |
| 631 | { |
| 632 | var copy = r0_g1_b2; |
| 633 | var it = copy.iterator(); |
| 634 | var entry = it.next().?; |
| 635 | try testing.expectEqual(entry.key, .red); |
| 636 | try testing.expectEqual(entry.value.*, 0); |
| 637 | entry = it.next().?; |
| 638 | try testing.expectEqual(entry.key, .green); |
| 639 | try testing.expectEqual(entry.value.*, 1); |
| 640 | entry = it.next().?; |
| 641 | try testing.expectEqual(entry.key, .blue); |
| 642 | try testing.expectEqual(entry.value.*, 2); |
| 643 | try testing.expectEqual(it.next(), null); |
| 644 | } |
| 645 | } |
| 646 | |
| 307 | 647 | /// An array keyed by an enum, backed by a dense array. |
| 308 | 648 | /// If the enum is not dense, a mapping will be constructed from |
| 309 | 649 | /// enum values to dense indices. This type does no dynamic |