/*
 * BackboneLength.h
 *
 *  Created on: 17. 5. 2017
 *      Author: Jan Travnicek
 */

#ifndef BACKBONE_LENGTH_H
#define BACKBONE_LENGTH_H

#include <automaton/FSM/DFA.h>
#include <queue>
#include <alib/algorithm>
#include <alib/utility>

namespace stringology {

namespace properties {

class BackboneLength {
public:
	/**
	 * Computes length of backbone for a suffix/factor/oracle automaton
	 * @param automaton the suffix/factor/oracle automaton
	 * @return backbone length
	 */
	template < class SymbolType, class StateType >
	static unsigned length ( const automaton::DFA < SymbolType, StateType > & automaton );

	template < class StateType >
	class BackboneLengthLess {
	public:
		bool operator ( ) ( const std::pair < StateType, unsigned > & first, const std::pair < StateType, unsigned > & second ) {
			return first.second < second.second;
		}
	};

};

template < class SymbolType, class StateType >
unsigned BackboneLength::length ( const automaton::DFA < SymbolType, StateType > & automaton ) {
	std::priority_queue < std::pair < StateType, unsigned >, ext::vector < std::pair < StateType, unsigned > >, BackboneLengthLess < StateType > > open;
	ext::map < StateType, unsigned > closed;

	unsigned max = 0;
	open.push ( std::make_pair ( automaton.getInitialState ( ), max ) );

	while ( ! open.empty ( ) ) {
		std::pair < StateType, unsigned > current = std::move ( open.top ( ) );
		open.pop ( );
		unsigned & dist = closed [ current.first ];

		if ( dist > current.second )
			continue;

		dist = current.second;
		max = std::max ( max, current.second );

		for ( const std::pair < const ext::pair < StateType, SymbolType >, StateType > & target : automaton.getTransitionsFromState ( current.first ) )
			open.push ( std::make_pair ( target.second, current.second + 1 ) );
	}

	return max;
}

} /* namespace properties */

} /* namespace stringology */

#endif /* BACKBONE_LENGTH_H */
