This documentation is automatically generated by online-judge-tools/verification-helper

:warning: src/datastructure/multi_set.cr

Required by

Code

class MultiSet(T)
  include Iterable(T)
  include Enumerable(T)

  @count = Hash(T, Int32).new(0)
  @size = 0

  # Returns the additive identity of this type.
  #
  # This is an empty multiset
  def self.additive_identify : self
    new
  end

  # Creates a new, empty multiset.
  def initialize(initial_capacity = nil)
    @count = Hash(T, Int32).new(0, initial_capacity: initial_capacity)
  end

  # Creates a new multiset from the elements in *elements*.
  def initialize(elements : Enumerable(T))
    @count = Hash(T, Int32).new(0)
    concat elements
  end

  # Creates a new multiset from the enumerable of {element, count}.
  def self.from_counts(counts : Enumerable({T, Int32})) : MultiSet(T)
    counts.each_with_object(MultiSet(T).new) do |(elem, cnt), set|
      raise ArgumentError.new "Duplicate element: #{elem}" if set.includes?(elem)
      raise ArgumentError.new "Negative count: #{cnt}" if cnt < 0
      set.@count[elem] += cnt
    end
  end

  protected def initialize(*, @count : Hash(T, Int32), @size : Int32)
  end

  # Returns the number of elements in the set.
  def size : Int32
    @size
  end

  # Returns the number of kinds in the multiset.
  def kind_count : Int32
    @count.size
  end

  # Returns `true` if the multiset is empty.
  def empty? : Bool
    size == 0
  end

  # Compares with *other*.
  def ==(other : MultiSet) : Bool
    @count == other.@count
  end

  # Returns `true` if *object* exists in the multiset.
  def includes?(object : T) : Bool
    @count[object] > 0
  end

  # :ditto:
  def ===(object : T) : Bool
    includes?(object)
  end

  # Returns the number of times that the *object* is present in the multiset.
  def count(object : T)
    @count[object]
  end

  # Adds *object* to the multiset and returns `self`.
  def add(object : T) : self
    @count[object] += 1
    @size += 1
    self
  end

  # :ditto:
  def <<(object : T) : self
    add object
  end

  # Adds *object* to the multiset *count* times and returns `self`.
  def add(object : T, count : Int32) : self
    raise ArgumentError.new unless count >= 0
    @count[object] += count
    @size += count
    self
  end

  # Removes the *object* from the multiset and returns `true` if it was present, otherwise returns `false`.
  def delete(object : T) : Bool
    if flag = @count[object] > 0
      @count[object] -= 1
      @count.delete(object) if @count[object] == 0
    end
    flag
  end

  # Removes the *object* from the multiset at most *count* times and returns `true`
  # if it was present, otherwise returns `false`.
  def delete(object : T, count : Int32) : Bool
    raise ArgumentError.new unless count >= 0
    if flag = @count[object] > 0
      @count[object] = {0, @count[object] - count}.max
      @count.delete(object) if @count[object] == 0
    end
    flag
  end

  # Adds `each` element of *elems* to the multisetset and returns `self`.
  def concat(elems) : self
    elems.each { |elem| self << elem }
    self
  end

  # Removes all elements in the multiset and returns `self`.
  def clear : self
    @count.clear
    @size = 0
    self
  end

  private class MultiSetIterator(T)
    include Iterator(T)
    include IteratorWrapper

    @iterator : Iterator({T, Int32})
    @value : Tuple(T?, Int32)

    def initialize(count : Hash(T, Int32))
      @iterator = count.each
      @value = {nil, 0}
    end

    def next
      until @value[1] > 0
        @value = wrapped_next
      end
      @value = {@value[0], @value[1] - 1}
      @value[0].not_nil!
    end
  end

  # Returns an iterator for each element of the multiset.
  def each
    MultiSetIterator(T).new(@count)
  end

  # Yields each element of the multiset, and returns `nil`.
  def each(&) : Nil
    @count.each do |(elem, cnt)|
      cnt.times { yield elem }
    end
  end

  # Returns `true` if the multiset and the given multiset have at least one element in common.
  def intersects?(other : self) : Bool
    if kind_count < other.kind_count
      any? { |o| other.includes?(o) }
    else
      other.any? { |o| includes?(o) }
    end
  end

  # Returns `true` if the multiset is a subset of the given multiset.
  def subset_of?(other : self) : Bool
    return false if other.size < size
    all? { |o| other.includes?(o) }
  end

  # Returns true if the multiset is a superset of the given multiset.
  def superset_of?(other : self) : Bool
    other.subset_of?(self)
  end

  # Returns an iterator for each tuple of element and count of the multiset
  def each_count
    @count.each
  end

  # Yields each pair of element and count of the multiset, and returns `nil`.
  def each_count(&) : Nil
    @count.each do |(elem, cnt)|
      yield(elem, cnt)
    end
  end

  # Intersection.
  #
  # ```
  # MultiSet{1, 2, 2, 3} & MultiSet{2, 3, 3, 4} # => MultiSet{2, 3}
  # ```
  def &(other : MultiSet(T)) : self
    small, large = self, other
    if large.kind_count < small.kind_count
      small, large = large, small
    end

    result = MultiSet(T).new
    small.each_count do |elem, small_cnt|
      large_cnt = large.count(elem)
      result.add elem, Math.min(small_cnt, large_cnt) if large_cnt > 0
    end
    result
  end

  # Union.
  #
  # ```
  # MultiSet{1, 2, 2} | MultiSet{2, 3} # => MultiSet{1, 2, 2, 3}
  # ```
  def |(other : MultiSet(U)) : MultiSet(T | U) forall U
    result = MultiSet(T | U).new
    each_count { |elem, cnt| result.add elem, cnt }
    other.each_count { |elem, cnt| result.add elem, cnt }
    result
  end

  # Addition. Same as `#|`.
  def +(other : MultiSet(U)) : MultiSet(T | U) forall U
    self | other
  end

  # Difference.
  #
  # ```
  # MultiSet{1, 2, 2, 3} - MultiSet{1, 2} # => MultiSet{2, 3}
  # MultiSet{1, 2, 2, 3} - [1, 2]         # => MultiSet{2, 3}
  # ```
  def -(other : MultiSet) : self
    dup.subtract other
  end

  # :ditto:
  def -(other : Enumerable) : self
    dup.subtract other
  end

  # Repetition
  #
  # ```
  # MultiSet{1, 2, 2} * 2 # => MultiSet{1, 1, 2, 2, 2, 2}
  # ```
  def *(times : Int) : self
    if times == 0 || empty?
      MultiSet(T).new
    elsif times == 1
      dup
    else
      set = MultiSet(T).new(@count.size)
      each_count do |elem, cnt|
        set.add elem, cnt * times
      end
      set
    end
  end

  # Returns `self` after removing *other* elements.
  #
  # ```
  # MultiSet{1, 2, 2, 3}.subtract MultiSet{1, 2} # => MultiSet{2, 3}
  # MultiSet{1, 2, 2, 3}.subtract [1, 2]         # => MultiSet{2, 3}
  # ```
  def subtract(other : MultiSet(U)) : self forall U
    other.each_count do |elem, cnt|
      delete elem, cnt
    end
    self
  end

  # :ditto:
  def subtract(other : Enumerable) : self forall U
    other.each do |elem|
      delete elem
    end
    self
  end

  # Returns a new multiset with all of the same elements.
  def dup : self
    MultiSet(T).new count: @count.dup, size: @size
  end

  # Returns a new multiset with all of the elements cloned.
  def clone : self
    set = MultiSet(T).new(@count.size)
    each_count do |elem, cnt|
      set.add elem.clone, cnt
    end
    set
  end

  # Writes a string representation of the multiset to *io*.
  #
  # ```
  # set = MultiSet{3, 1, 4, 1, 5}
  # set.to_s # => "MultiSet{3, 1, 1, 4, 5}"
  # ```
  def to_s(io : IO) : Nil
    io << "MultiSet{"
    each.join(", ", io)
    io << '}'
  end

  # Writes a string representation of the multiset to *io*.
  #
  # ```
  # set = MultiSet{3, 1, 4, 1, 5}
  # set.inspect # => "{3(1), 1(2), 4(1), 5(1)}"
  # ```
  def inspect(io : IO) : Nil
    io << '{'
    each_count.join(", ", io) do |(elem, count), io|
      io << elem << '(' << count << ')'
    end
    io << '}'
  end
end
class MultiSet(T)
  include Iterable(T)
  include Enumerable(T)

  @count = Hash(T, Int32).new(0)
  @size = 0

  # Returns the additive identity of this type.
  #
  # This is an empty multiset
  def self.additive_identify : self
    new
  end

  # Creates a new, empty multiset.
  def initialize(initial_capacity = nil)
    @count = Hash(T, Int32).new(0, initial_capacity: initial_capacity)
  end

  # Creates a new multiset from the elements in *elements*.
  def initialize(elements : Enumerable(T))
    @count = Hash(T, Int32).new(0)
    concat elements
  end

  # Creates a new multiset from the enumerable of {element, count}.
  def self.from_counts(counts : Enumerable({T, Int32})) : MultiSet(T)
    counts.each_with_object(MultiSet(T).new) do |(elem, cnt), set|
      raise ArgumentError.new "Duplicate element: #{elem}" if set.includes?(elem)
      raise ArgumentError.new "Negative count: #{cnt}" if cnt < 0
      set.@count[elem] += cnt
    end
  end

  protected def initialize(*, @count : Hash(T, Int32), @size : Int32)
  end

  # Returns the number of elements in the set.
  def size : Int32
    @size
  end

  # Returns the number of kinds in the multiset.
  def kind_count : Int32
    @count.size
  end

  # Returns `true` if the multiset is empty.
  def empty? : Bool
    size == 0
  end

  # Compares with *other*.
  def ==(other : MultiSet) : Bool
    @count == other.@count
  end

  # Returns `true` if *object* exists in the multiset.
  def includes?(object : T) : Bool
    @count[object] > 0
  end

  # :ditto:
  def ===(object : T) : Bool
    includes?(object)
  end

  # Returns the number of times that the *object* is present in the multiset.
  def count(object : T)
    @count[object]
  end

  # Adds *object* to the multiset and returns `self`.
  def add(object : T) : self
    @count[object] += 1
    @size += 1
    self
  end

  # :ditto:
  def <<(object : T) : self
    add object
  end

  # Adds *object* to the multiset *count* times and returns `self`.
  def add(object : T, count : Int32) : self
    raise ArgumentError.new unless count >= 0
    @count[object] += count
    @size += count
    self
  end

  # Removes the *object* from the multiset and returns `true` if it was present, otherwise returns `false`.
  def delete(object : T) : Bool
    if flag = @count[object] > 0
      @count[object] -= 1
      @count.delete(object) if @count[object] == 0
    end
    flag
  end

  # Removes the *object* from the multiset at most *count* times and returns `true`
  # if it was present, otherwise returns `false`.
  def delete(object : T, count : Int32) : Bool
    raise ArgumentError.new unless count >= 0
    if flag = @count[object] > 0
      @count[object] = {0, @count[object] - count}.max
      @count.delete(object) if @count[object] == 0
    end
    flag
  end

  # Adds `each` element of *elems* to the multisetset and returns `self`.
  def concat(elems) : self
    elems.each { |elem| self << elem }
    self
  end

  # Removes all elements in the multiset and returns `self`.
  def clear : self
    @count.clear
    @size = 0
    self
  end

  private class MultiSetIterator(T)
    include Iterator(T)
    include IteratorWrapper

    @iterator : Iterator({T, Int32})
    @value : Tuple(T?, Int32)

    def initialize(count : Hash(T, Int32))
      @iterator = count.each
      @value = {nil, 0}
    end

    def next
      until @value[1] > 0
        @value = wrapped_next
      end
      @value = {@value[0], @value[1] - 1}
      @value[0].not_nil!
    end
  end

  # Returns an iterator for each element of the multiset.
  def each
    MultiSetIterator(T).new(@count)
  end

  # Yields each element of the multiset, and returns `nil`.
  def each(&) : Nil
    @count.each do |(elem, cnt)|
      cnt.times { yield elem }
    end
  end

  # Returns `true` if the multiset and the given multiset have at least one element in common.
  def intersects?(other : self) : Bool
    if kind_count < other.kind_count
      any? { |o| other.includes?(o) }
    else
      other.any? { |o| includes?(o) }
    end
  end

  # Returns `true` if the multiset is a subset of the given multiset.
  def subset_of?(other : self) : Bool
    return false if other.size < size
    all? { |o| other.includes?(o) }
  end

  # Returns true if the multiset is a superset of the given multiset.
  def superset_of?(other : self) : Bool
    other.subset_of?(self)
  end

  # Returns an iterator for each tuple of element and count of the multiset
  def each_count
    @count.each
  end

  # Yields each pair of element and count of the multiset, and returns `nil`.
  def each_count(&) : Nil
    @count.each do |(elem, cnt)|
      yield(elem, cnt)
    end
  end

  # Intersection.
  #
  # ```
  # MultiSet{1, 2, 2, 3} & MultiSet{2, 3, 3, 4} # => MultiSet{2, 3}
  # ```
  def &(other : MultiSet(T)) : self
    small, large = self, other
    if large.kind_count < small.kind_count
      small, large = large, small
    end

    result = MultiSet(T).new
    small.each_count do |elem, small_cnt|
      large_cnt = large.count(elem)
      result.add elem, Math.min(small_cnt, large_cnt) if large_cnt > 0
    end
    result
  end

  # Union.
  #
  # ```
  # MultiSet{1, 2, 2} | MultiSet{2, 3} # => MultiSet{1, 2, 2, 3}
  # ```
  def |(other : MultiSet(U)) : MultiSet(T | U) forall U
    result = MultiSet(T | U).new
    each_count { |elem, cnt| result.add elem, cnt }
    other.each_count { |elem, cnt| result.add elem, cnt }
    result
  end

  # Addition. Same as `#|`.
  def +(other : MultiSet(U)) : MultiSet(T | U) forall U
    self | other
  end

  # Difference.
  #
  # ```
  # MultiSet{1, 2, 2, 3} - MultiSet{1, 2} # => MultiSet{2, 3}
  # MultiSet{1, 2, 2, 3} - [1, 2]         # => MultiSet{2, 3}
  # ```
  def -(other : MultiSet) : self
    dup.subtract other
  end

  # :ditto:
  def -(other : Enumerable) : self
    dup.subtract other
  end

  # Repetition
  #
  # ```
  # MultiSet{1, 2, 2} * 2 # => MultiSet{1, 1, 2, 2, 2, 2}
  # ```
  def *(times : Int) : self
    if times == 0 || empty?
      MultiSet(T).new
    elsif times == 1
      dup
    else
      set = MultiSet(T).new(@count.size)
      each_count do |elem, cnt|
        set.add elem, cnt * times
      end
      set
    end
  end

  # Returns `self` after removing *other* elements.
  #
  # ```
  # MultiSet{1, 2, 2, 3}.subtract MultiSet{1, 2} # => MultiSet{2, 3}
  # MultiSet{1, 2, 2, 3}.subtract [1, 2]         # => MultiSet{2, 3}
  # ```
  def subtract(other : MultiSet(U)) : self forall U
    other.each_count do |elem, cnt|
      delete elem, cnt
    end
    self
  end

  # :ditto:
  def subtract(other : Enumerable) : self forall U
    other.each do |elem|
      delete elem
    end
    self
  end

  # Returns a new multiset with all of the same elements.
  def dup : self
    MultiSet(T).new count: @count.dup, size: @size
  end

  # Returns a new multiset with all of the elements cloned.
  def clone : self
    set = MultiSet(T).new(@count.size)
    each_count do |elem, cnt|
      set.add elem.clone, cnt
    end
    set
  end

  # Writes a string representation of the multiset to *io*.
  #
  # ```
  # set = MultiSet{3, 1, 4, 1, 5}
  # set.to_s # => "MultiSet{3, 1, 1, 4, 5}"
  # ```
  def to_s(io : IO) : Nil
    io << "MultiSet{"
    each.join(", ", io)
    io << '}'
  end

  # Writes a string representation of the multiset to *io*.
  #
  # ```
  # set = MultiSet{3, 1, 4, 1, 5}
  # set.inspect # => "{3(1), 1(2), 4(1), 5(1)}"
  # ```
  def inspect(io : IO) : Nil
    io << '{'
    each_count.join(", ", io) do |(elem, count), io|
      io << elem << '(' << count << ')'
    end
    io << '}'
  end
end
Back to top page