<commit_before>module Qernel

##
# A slot combines several input/output links of the same carrier.
# It can either be on the input or output side of a converter. A
# slot waits (see #ready?) until all passive_links have been calculated
# and then calculates the active_links.
#
# Usually we are just interested in the #external_value of a slot,
# which is the amount of energy that goes through. The internal value
# is the external_value * conversion, which in the end equals the converter
# demand. So:
#
#  external_value * conversion = internal_value = converter.demand
#
#
class Slot
  include DatasetAttributes
  include Topology::Slot


  # --------- Accessor ---------------------------------------------------------

  attr_accessor :carrier, 
                :converter, 
                :converter_id, 
                :graph

  attr_reader :direction, 
              :id


  # --------- Dataset ---------------------------------------------------------

  DATASET_ATTRIBUTES = [:conversion, :country_specific]
  dataset_accessors DATASET_ATTRIBUTES

  def self.dataset_group; :graph; end
  
  # --------- Initialize ------------------------------------------------------

  def initialize(id, converter, carrier, direction = :input)
    @id = id.is_a?(Numeric) ? id : Hashpipe.hash(id)

    @converter = converter
    @carrier = carrier
    @direction = direction.to_sym
    self.dataset_key # memoize dataset_key
  end


  # --------- Calculation -----------------------------------------------------

  # @return [Boolean] is Slot ready for calculation?
  #
  def ready?
    passive_links.all?(&:value)
    # 2010-06-07 sb:
    # Theoretically it should be:
    #
    #   passive_links.all?(&:value) and converter.has_demand?
    #
    # As a slot can only calculate if the converter demand is
    # known. But as we control the flow from the converter we
    # can skip this (saves a bit of performance).
  end

  # Calculate the link values
  # @return [Array<Link>]
  #
  def calculate
    # 2010-06-07 sb
    # I don't remember why I don't use active_links here.
    # I assume it must be because of inversed_flexible?
    # and [constant with undefined value].

    if input?
      active_links.select(&:constant?).each(&:calculate)
      active_links.select(&:share?).each(&:calculate)

      # Calculate flexible links with boundaries first. Because
      # without boundaries a link takes everything. 
      flexible_links = active_links.select(&:flexible?)
      flexible_links.select(&:max_boundaries?).sort_by(&:priority).each(&:calculate)
      flexible_links.reject(&:max_boundaries?).sort_by(&:priority).each(&:calculate)
    end
    if output?
      links.select(&:reversed?).each(&:calculate)
      links.select(&:dependent?).each(&:calculate)
    end
  end


  # --------- Slot Types ------------------------------------------------------

  # @return [Boolean] Is Slot an input (on the left side of converter)
  # Links that are calculated by this converter
  #
  def input?
    (direction === :input)
  end

  # @return [Boolean] is it an output (on the left side of converter)
  #
  def output?
    !input?
  end

  def environment?
    converter.environment?
  end

  def loss?
    carrier.loss?
  end


  # --------- Traversal -------------------------------------------------------

  # @return [Array<Link>] Links that are calculated by this Slot
  #
  def active_links
    @active_links ||= if input? 
      links.select(&:calculated_by_left?)
    else
      links.select(&:calculated_by_right?)
    end
  end


  # @return [Array<Link>] Links calculated by the converter on the other end.
  #
  def passive_links
    @passive_links ||= if input? 
      links.select(&:calculated_by_right?) 
    else 
      links.select(&:calculated_by_left?)
    end
  end

  # @return [Array<Link>]
  #
  def links
    # For legacy reasons, we still access links through the converter.
    @links ||= if input? <commit_msg>aliased Slot#input? #output? with lft_of_converter? and rgt_of_converter?<commit_after>module Qernel

##
# A slot combines several input/output links of the same carrier.
# It can either be on the input or output side of a converter. A
# slot waits (see #ready?) until all passive_links have been calculated
# and then calculates the active_links.
#
# Usually we are just interested in the #external_value of a slot,
# which is the amount of energy that goes through. The internal value
# is the external_value * conversion, which in the end equals the converter
# demand. So:
#
#  external_value * conversion = internal_value = converter.demand
#
#
class Slot
  include DatasetAttributes
  include Topology::Slot


  # --------- Accessor ---------------------------------------------------------

  attr_accessor :carrier, 
                :converter, 
                :converter_id, 
                :graph

  attr_reader :direction, 
              :id


  # --------- Dataset ---------------------------------------------------------

  DATASET_ATTRIBUTES = [:conversion, :country_specific]
  dataset_accessors DATASET_ATTRIBUTES

  def self.dataset_group; :graph; end
  
  # --------- Initialize ------------------------------------------------------

  def initialize(id, converter, carrier, direction = :input)
    @id = id.is_a?(Numeric) ? id : Hashpipe.hash(id)

    @converter = converter
    @carrier = carrier
    @direction = direction.to_sym
    self.dataset_key # memoize dataset_key
  end


  # --------- Calculation -----------------------------------------------------

  # @return [Boolean] is Slot ready for calculation?
  #
  def ready?
    passive_links.all?(&:value)
    # 2010-06-07 sb:
    # Theoretically it should be:
    #
    #   passive_links.all?(&:value) and converter.has_demand?
    #
    # As a slot can only calculate if the converter demand is
    # known. But as we control the flow from the converter we
    # can skip this (saves a bit of performance).
  end

  # Calculate the link values
  # @return [Array<Link>]
  #
  def calculate
    # 2010-06-07 sb
    # I don't remember why I don't use active_links here.
    # I assume it must be because of inversed_flexible?
    # and [constant with undefined value].

    if lft_of_converter?
      active_links.select(&:constant?).each(&:calculate)
      active_links.select(&:share?).each(&:calculate)

      # Calculate flexible links with boundaries first. Because
      # without boundaries a link takes everything. 
      flexible_links = active_links.select(&:flexible?)
      flexible_links.select(&:max_boundaries?).sort_by(&:priority).each(&:calculate)
      flexible_links.reject(&:max_boundaries?).sort_by(&:priority).each(&:calculate)
    end
    if rgt_of_converter?
      links.select(&:reversed?).each(&:calculate)
      links.select(&:dependent?).each(&:calculate)
    end
  end


  # --------- Slot Types ------------------------------------------------------

  # @return [Boolean] Is Slot an input (on the left side of converter)
  # Links that are calculated by this converter
  #
  def input?
    (direction === :input)
  end
  alias lft_of_converter? input?


  # @return [Boolean] is it an output (on the left side of converter)
  #
  def output?
    !input?
  end
  alias rgt_of_converter? input?

  def environment?
    converter.environment?
  end

  def loss?
    carrier.loss?
  end


  # --------- Traversal -------------------------------------------------------

  # @return [Array<Link>] Links that are calculated by this Slot
  #
  def active_links
    @active_links ||= if lft_of_converter?
      links.select(&:calculated_by_left?)
    else
      links.select(&:calculated_by_right?)
    end
  end


  # @return [Array<Link>] Links calculated by the converter on the other end.
  #
  def passive_links
    @passive_links ||= if lft_of_converter?
      links.select(&:calculated_by_right?) 
    else 
      links.select(&:calculated_by_left?)
    end
  end

  # @return [Array<Link>]
  #
  def links
    # For legacy reasons, we still access links through the converter.
    @links ||= if lft_of_converter?
{'fraction_non_alphanumeric': 0.17909338617785484, 'fraction_numerical': 0.003963339113202873, 'mean_word_length': 2.1481481481481484, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1971157', 'n_tokens_mistral': 2415, 'n_tokens_neox': 2223, 'n_words': 704}