yuutetu/abst_int

View on GitHub
lib/abst_int/calculus_model/dfa.rb

Summary

Maintainability
C
1 day
Test Coverage
require "set"
require "abst_int/or_set"

class Object
  def try key, *args
    send key, *args
  end
end

class NilClass
  def try key, *args
    nil
  end

  def [] some
    return some
  end

  def + some
    return some
  end

  def | some
    return some
  end
end

module AbstInt::CalculusModel
  class Dfa
    def initialize
      @states = Set.new
      @initial_state = nil
      @final_states = Set.new
      @inputs = Set.new
      @transition = {}
    end

    def set_initial state
      @states << state
      @initial_state = state
    end

    def add_final state
      @states << state
      @final_states << state
    end

    # [state] 1,2,3
    # [input] 'a', 'b', 'c'
    def add_trans state1, input, state2
      @states << state1 << state2
      @inputs << input
      @transition[state1] ||= {}
      @transition[state1][input] = state2
    end

    def & another_dfa
      dfa = AbstInt::CalculusModel::Dfa.new
      dfa.set_initial [self.initial_state, another_dfa.initial_state]
      dfa = generate_inter_dfa another_dfa, dfa, [self.initial_state, another_dfa.initial_state], Set.new
      dfa.each_states do |states|
        dfa.add_final states if self.final?(states[0]) && another_dfa.final?(states[1])
      end
      return dfa
    end

    def not
      cloned_self = self.perfect
      cloned_self.final_states = cloned_self.states - cloned_self.final_states
      return cloned_self
    end

    def final? state
      @final_states.include? state
    end

    def each_states &block
      @states.each &block
    end

    def to_orset
      return generate_orset
    end

    def equal_transition? dfa
      @transition == dfa.transition
    end

    def self.filter_dfa
      dfa = AbstInt::CalculusModel::Dfa.new
      dfa.set_initial 'A'
      dfa.add_trans 'A', 'a', 'A'
      dfa.add_trans 'A', 'b', 'B'
      dfa.add_trans 'B', 'b', 'B'
      dfa.add_final 'A'
      dfa.add_final 'B'
      return dfa
    end

    def accept? str
      result = rec_accept? str, @initial_state
      return result
    end

    protected
    def rec_accept? str, state
      if str.length == 0
        return @final_states.include?(state)
      else
        head = str[0]
        rest = str[1..-1]
        return nil if @transition.try(:[], state).try(:[], head).nil?
        return self.rec_accept?(rest, @transition.try(:[], state).try(:[], head))
      end
    end

    def transition
      @transition
    end

    def initial_state
      @initial_state
    end

    def final_states
      @final_states
    end

    def final_states= final_states
      @final_states = final_states
    end

    def states
      @states
    end

    def perfect
      cloned_self = AbstInt::CalculusModel::Dfa.new
      cloned_self.set_initial self.initial_state
      self.final_states.each do |state|
        cloned_self.add_final state
      end
      @states.each do |state|
        @inputs.each do |input|
          if @transition.try(:[], state).try(:[], input).nil?
            cloned_self.add_trans state, input, :reject
          else
            cloned_self.add_trans state, input, @transition[state][input]
          end
        end
      end
      return cloned_self
    end

    private
    def generate_inter_dfa another_dfa, dfa, state, state_set
      return dfa if state_set.include? state
      @inputs.each do |input|
        return dfa if self.transition.try(:[], state[0]).try(:[], input).nil?
        return dfa if another_dfa.transition.try(:[], state[1]).try(:[], input).nil?
        next_state = [self.transition[state[0]][input], another_dfa.transition[state[1]][input]]
        dfa.add_trans state, input, next_state
        dfa = generate_inter_dfa another_dfa, dfa, next_state, (state_set << state)
      end
      return dfa
    end

    def generate_orset

      # アルファベットを数字に変更
      current_dfa = AbstInt::CalculusModel::Dfa.new
      current_dfa.set_initial @initial_state
      @final_states.each do |state|
        current_dfa.add_final state
      end
      @transition.each do |state1, value|
        value.each do |input, state2|
          case input
          when 'a'
            current_dfa.add_trans state1, AbstInt::OrSet.new(1), state2
          when 'b'
            current_dfa.add_trans state1, AbstInt::OrSet.new(-1), state2
          end
        end
      end

      remove_states = @states - ([@initial_state] + @final_states.to_a)

      remove_states.each do |state|
        current_dfa = self_remain_state_for_ofset_dfa! state, current_dfa
      end

      removed_dfa_without_final = current_dfa
      current_dfa = nil
      current_orset = nil

      @final_states.each do |final|
        current_dfa = removed_dfa_without_final
        remove_states = current_dfa.states - [@initial_state, final]
        remove_states.each do |state|
          current_dfa = self_remain_state_for_ofset_dfa! state, current_dfa
        end
        if @initial_state == final then
          current_dfa.transition[@initial_state].each do |input, state2|
            if state2 == final
              current_orset = current_orset | input.star
            end
          end
        else
          trans_cache = {}
          current_dfa.transition.each do |state1, value|
            value.each do |input, state2|
              trans_cache[state1] ||= {}
              trans_cache[state1][state2] = input
            end
          end
          match_orset = (trans_cache[@initial_state][@initial_state] | (trans_cache[@initial_state][final] + trans_cache[final][final].try(:star) + trans_cache[final][@initial_state])).try(:star) + trans_cache[@initial_state][final] + trans_cache[final][final].try(:star)
          current_orset = current_orset | match_orset
        end
      end

      return current_orset
    end

    def cache_check start, goal, pass_states
      return @cache.try(:[], start).try(:[], goal).try(:[], pass_states)
    end

    def save_cache start, goal, pass_states, result
      @cache ||= {}
      @cache[start] ||= {}
      @cache[start][goal] ||= {}
      @cache[start][goal][pass_states] = result
      return nil
    end

    protected
    def self_remain_state_for_ofset_dfa! state, current_dfa
      new_dfa = AbstInt::CalculusModel::Dfa.new
      in_states, out_states = Set.new, Set.new
      current_dfa.transition.each do |state1, value|
        value.each do |input, state2|
          (out_states << state2; next) if state1 == state && state2 != state
          (in_states  << state1; next) if state1 != state && state2 == state
          new_dfa.add_trans state1, input, state2
        end
      end

      trans_cache = {}
      current_dfa.transition.each do |state1, value|
        value.each do |input, state2|
          if in_states.include?(state1) ||
             out_states.include?(state2) ||
             state == state1 ||
             state == state2 then
            trans_cache[state1] ||= {}
            trans_cache[state1][state2] = input
          end
        end
      end

      in_states.each do |in_state|
        out_states.each do |out_state|
          right_orset = nil
          if trans_cache.try(:[], in_state).try(:[], state) && trans_cache.try(:[], state).try(:[], out_state) then
            if trans_cache.try(:[], state).try(:[], state) then
              right_orset = trans_cache[in_state][state] + trans_cache[state][out_state].star + trans_cache[state][out_state]
            else
              right_orset = trans_cache[in_state][state] + trans_cache[state][out_state]
            end
          end
          if trans_cache.try(:[], in_state).try(:[], out_state) && right_orset then
            new_orset = trans_cache[in_state][out_state] | right_orset
          elsif right_orset then
            new_orset = right_orset
          else
            new_orset = trans_cache[in_state][out_state]
          end

          new_dfa.add_trans in_state, new_orset, out_state if new_orset
        end
      end

      return new_dfa
    end
  end
end