<commit_before>require_relative 'status'
require_relative 'instance_count'
require_relative 'condition'

module TidBits
module Facts


class Fact

include Options::Configurable, Status, InstanceCount


# Yaml can't have symbols as rvalues
#
def self.sanitize key, value

	if [ :params, :metas ].include? key

		value = value.map!( &:to_sym )

	end

	return key, value

end



attr_reader :depend, :state, :status, :params, :stateMachine, :address, :conditions, :operation



def initialize( **opts )

	super

	init

	# This must survive reset, because we call reset if we need to re-analyze realite,
	# but then we won't know whether we fixed things or not unless we store it here.
	#
	@fixedAny = false

end



def init

	@mustDepend    = Array.eat( options.mustDepend   )
	@depend        = Array.eat( options.dependOn     )
	@metas         = Array.eat( options.metas        )
	@factPool      = Array.eat( options.factPool   )


	@log           = Logger.new(STDERR)
	@log.progname  = self.class.name

	@indexKeys     = Array.eat( options.indexKeys )

	@sm            = options.stateMachine || StateMachine.new
	@address       = createAddress
	@params        = createParams
	@state         = createState
	@desire        = @sm.desire!( @address )
	@conditions    = @sm.conditions.dig!( *@address )
	@facts         = @sm.facts

	@facts.set @address, self

	createDesire
	createConditions

end


def createAddress

	@indexKeys.map { |key| options[ key ] }.unshift self.class.name

end


# create a state object from options, only setting expect
#
def createState( opts = options )

	state = TidBits::Options::Settings.new

	# Only take actual tests
	#
	opts.

		select { |opt| ! options.metas .include? opt }.
	   select { |opt| ! options.params.include? opt }.

	   each do | key, value |

			state[ key ] = { expect: value }

	   end

	state

end


# put the state on the stateMachine
#
def createDesire

	@state.each do | key, value |

		if @desire[ key ] && desire[ key ] != value.expect

			raise "Conflicting wanted states for: #{@address[ key ].ai}
			       desire: #{ value.expect }, but value already present is:
			       desire: #{ @desire[ key ]}.
					"

		end

		@desire[ key ] = value.expect

	end

end



def createConditions

	@state.each do | key, value |

		klass = Conditions.const_get( self.class.lastname ).const_get( key.to_s.capitalize! )
		address = @address.dup << key

		@conditions[ key ] and  raise "The condition already exists: #{address.ai}"

		@conditions[ key ] = klass.new( **options, address: address, stateMachine: @sm)

	end

	@conditions

end



def fixedAny?

	@fixedAny and return true

	@depend.any? { |dep| dep.fixedAny? }

end




def analyze

	checkDepends  or  ( analyzeFailed; return false )

	@operation ||= :analyze

	analyzePassed?  and  return @status
	states = @conditions.map { |key, cond| cond.analyze }

	states.all? { |state| state.include? :analyzePassed }  ?  analyzePassed : analyzeFailed

	@operation == :analyze and @operation = nil
	return analyzePassed? ? true : false

end


def check

	checkDepends  or  ( checkFailed; return false )

	@operation ||= :check

	analyzed? or analyze
	states = @conditions.map { |key, cond| cond.check }

	states.all? { |state| state.include? :checkPassed }  ?  checkPassed : checkFailed

	@operation == :check and @operation = nil
	return checkPassed? ? true : false

end


def fix

	fixDepends  or  ( fixFailed; return false )

	@operation ||= :fix<commit_msg>Optimize loops in fact<commit_after>require_relative 'status'
require_relative 'instance_count'
require_relative 'condition'

module TidBits
module Facts


class Fact

include Options::Configurable, Status, InstanceCount


# Yaml can't have symbols as rvalues
#
def self.sanitize key, value

	if [ :params, :metas ].include? key

		value = value.map!( &:to_sym )

	end

	return key, value

end



attr_reader :depend, :state, :status, :params, :stateMachine, :address, :conditions, :operation



def initialize( **opts )

	super

	init

	# This must survive reset, because we call reset if we need to re-analyze realite,
	# but then we won't know whether we fixed things or not unless we store it here.
	#
	@fixedAny = false

end



def init

	@mustDepend    = Array.eat( options.mustDepend   )
	@depend        = Array.eat( options.dependOn     )
	@metas         = Array.eat( options.metas        )
	@factPool      = Array.eat( options.factPool   )


	@log           = Logger.new(STDERR)
	@log.progname  = self.class.name

	@indexKeys     = Array.eat( options.indexKeys )

	@sm            = options.stateMachine || StateMachine.new
	@address       = createAddress
	@params        = createParams
	@state         = createState
	@desire        = @sm.desire!( @address )
	@conditions    = @sm.conditions.dig!( *@address )
	@facts         = @sm.facts

	@facts.set @address, self

	createDesire
	createConditions

end


def createAddress

	@indexKeys.map { |key| options[ key ] }.unshift self.class.name

end


# create a state object from options, only setting expect
#
def createState( opts = options )

	state = TidBits::Options::Settings.new

	# Only take actual tests
	#
	opts.

		select { |opt| ! options.metas .include? opt }.
	   select { |opt| ! options.params.include? opt }.

	   each do | key, value |

			state[ key ] = { expect: value }

	   end

	state

end


# put the state on the stateMachine
#
def createDesire

	@state.each do | key, value |

		if @desire[ key ] && desire[ key ] != value.expect

			raise "Conflicting wanted states for: #{@address[ key ].ai}
			       desire: #{ value.expect }, but value already present is:
			       desire: #{ @desire[ key ]}.
					"

		end

		@desire[ key ] = value.expect

	end

end



def createConditions

	@state.each do | key, value |

		klass = Conditions.const_get( self.class.lastname ).const_get( key.to_s.capitalize! )
		address = @address.dup << key

		@conditions[ key ] and  raise "The condition already exists: #{address.ai}"

		@conditions[ key ] = klass.new( **options, address: address, stateMachine: @sm)

	end

	@conditions

end



def fixedAny?

	@fixedAny and return true

	@depend.any? { |dep| dep.fixedAny? }

end




def analyze

	checkDepends  or  ( analyzeFailed; return false )

	@operation ||= :analyze

	analyzePassed?  and  return @status
	states = @conditions.map { |key, cond| cond.analyze }

	states.none? { |state| state.include? :analyzeFailed }  ?  analyzePassed : analyzeFailed

	@operation == :analyze and @operation = nil
	return analyzePassed? ? true : false

end


def check

	checkDepends  or  ( checkFailed; return false )

	@operation ||= :check

	analyzed? or analyze
	states = @conditions.map { |key, cond| cond.check }

	states.none? { |state| state.include? :checkFailed }  ?  checkPassed : checkFailed

	@operation == :check and @operation = nil
	return checkPassed? ? true : false

end


def fix

	fixDepends  or  ( fixFailed; return false )

	@operation ||= :fix
{'fraction_non_alphanumeric': 0.11067764773876608, 'fraction_numerical': 0.0, 'mean_word_length': 3.1153388822829964, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 3, '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': '719151', 'n_tokens_mistral': 2428, 'n_tokens_neox': 2220, 'n_words': 684}