newmen/versatile-diamond

View on GitHub
analyzer/lib/concepts/there.rb

Summary

Maintainability
A
0 mins
Test Coverage
module VersatileDiamond
  module Concepts

    # Implementation which know about environment specs and has info about
    # positions between reactants and environment specs
    class There
      include Modules::GraphDupper
      include Modules::ListsComparer
      include Modules::SpecAtomSwapper
      include PositionsComparer
      extend Forwardable

      def_delegators :where, :description, :swap_source
      attr_reader :where # for graphs generators

      # Raises when target atom of sidepiece there object haven't lattice
      class ReversingError < Errors::Base
        attr_reader :spec
        def initialize(spec, atom)
          @spec, @atom = spec, atom
        end

        # Gets the keyname of invalid atom
        # @return [Symbol] the keyname of atom
        def keyname
          @spec.keyname(@atom)
        end
      end

      # Initialize a new instance of there object
      # @param [Where] where the basic where object
      # @param [Hash] target_refs the hash of references from target name to
      #   real reactant and it atom
      def initialize(where, target_refs)
        @where, @target_refs = where, target_refs
      end

      # Makes a duplicate of there object
      # @param [There] other the there object which will be duplicated
      def initialize_copy(other)
        @where = other.where.dup
        @target_refs = Hash[other.target_refs.map { |nm, sa| [nm, sa.dup] }]
      end

      # Gets the positions graph
      # @return [Hash] the graph of positions between target atoms of specs and
      #   environment atoms of specs
      def links
        transform_where_links(:total_links)
      end

      # Gets possible cutten positions graph which uses just own links of where object
      # @return [Hash] the possible cutten graph of positions between target atoms of
      #   specs and environment atoms of specs
      def own_links
        transform_where_links(:links)
      end

      # Makes reversed there object
      # @param [Mcs::MappingResult] mapping by which the other side spec and atom will
      #   be gotten
      # @return [There] the reversed there object
      def reverse(mapping)
        reversed_refs = {}
        target_refs.each do |target, (spec, atom)|
          other_side_spec_atom = mapping.other_side(spec, atom)
          if other_side_spec_atom.last.lattice
            reversed_refs[target] = other_side_spec_atom
          else
            raise ReversingError.new(*other_side_spec_atom)
          end
        end

        # TODO: simplify relations to targets if can
        self.class.new(where.dup, reversed_refs)
      end

      # Provides environment species
      # @return [Array] all species stored in used where and in their parents
      def env_specs
        where.all_specs.uniq
      end

      # Checks that passed spec is used in internal where object
      # @param [Symbol] target the type of swapping species
      # @param [SpecificSpec] spec which will be checked
      # @return [Boolan] is used similar spec or not
      def use_similar?(target, spec)
        target == :source &&
          target_refs.values.any? { |(s, _)| s == spec } ||
            where.total_links.any? do |_, rels|
              rels.any? { |(s, _), _| s == spec }
            end
      end

      # Provides target species
      # @return [Array] the array of target species
      def target_specs
        target_refs.values.map(&:first).uniq
      end

      # Swaps target spec from some to some
      # @param [SpecificSpec] from the spec from which need to swap
      # @param [SpecificSpec] to the spec to which need to swap
      def swap_target(from, to)
        @target_refs = Hash[@target_refs.map { |nm, sa| [nm, swap(sa, from, to)] }]
      end

      # Swaps atoms which uses as target
      # @param [SpecificSpec] spec the specific spec the atom of which will be swapped
      # @param [Atom] from the used atom
      # @param [Atom] to the new atom
      def swap_target_atom(spec, from, to)
        new_refs = @target_refs.map do |nm, sa|
          [nm, swap_only_atoms(sa, spec, from, to)]
        end
        @target_refs = Hash[new_refs]
      end

      # Swaps atoms in environment
      # @param [SpecificSpec] spec the specific spec the atom of which will be swapped
      # @param [Atom] from the used atom
      # @param [Atom] to the new atom
      def swap_env_atom(spec, from, to)
        where.swap_atom(spec, from, to)
      end

      # Gets atoms of passed spec which used in positions
      # @param [Spec | SpecificSpec] spec by which the atoms will be collected
      # @return [Array] the array of using atoms
      def used_atoms_of(spec)
        atoms = target_refs.each_with_object([]) do |(_, (s, a)), acc|
          acc << a if s == spec
        end

        (atoms + where.used_atoms_of(spec)).uniq
      end

      # Compares two there objects
      # @param [There] other with which comparison
      # @return [Boolean] are their wheres equal
      def same?(other)
        self == other ||
          (target_specs.size == other.target_specs.size &&
            env_specs.size == other.env_specs.size && same_positions?(other))
      end

      # Compares own positions between self and other there objects
      # @param [There] other there object which own positions will be checked
      # @return [Boolean] are same own positions or not
      def same_own_positions?(other)
        same_by_method?(:own_positions, other)
      end

      def to_s
        env_specs.map(&:name).join(' & ')
      end

      def inspect
        '/ ' + env_specs.map(&:inspect).join(' & ') + ' /'
      end

    protected

      attr_reader :target_refs

      # Gets the list of own position tuples
      # @return [Array] the list of own position tuples
      def own_positions
        make_positions(own_links)
      end

    private

      # Gets the transformed links of where object where target symbols is replaced to
      # correspond spec-atom pairs of reactants
      #
      # @param [Symbol] links_method which will be called from where object
      # @return [Hash] the transformed links graph
      def transform_where_links(links_method)
        dup_graph(where.public_send(links_method)) do |v|
          v.is_a?(Symbol) ? target_refs[v] : v
        end
      end
    end

  end
end