/*
 * GraphTransitions.h
 *
 *  Created on: 17.11.2012
 *      Author: Christian Dehnert
 */

#ifndef STORM_MODELS_GRAPHTRANSITIONS_H_
#define STORM_MODELS_GRAPHTRANSITIONS_H_

#include "src/storage/SparseMatrix.h"

#include <algorithm>
#include <memory>

namespace storm {

namespace models {

/*!
 * This class stores the predecessors of all states in a state space of the
 * given size.
 */
template <class T>
class GraphTransitions {

public:
	/*!
	 * Just typedef the iterator as a pointer to the index type.
	 */
	typedef const uint_fast64_t * const statePredecessorIterator;

	//! Constructor
	/*!
	 * Constructs an object representing the graph structure of the given
	 * transition relation, which is given by a sparse matrix.
	 * @param transitionMatrix The (0-based) matrix representing the transition
	 * relation.
	 * @param forward If set to true, this objects will store the graph structure
	 * of the backwards transition relation.
	 */
	GraphTransitions(std::shared_ptr<storm::storage::SparseMatrix<T>> transitionMatrix, bool forward)
			: successorList(nullptr), stateIndications(nullptr), numberOfStates(transitionMatrix->getRowCount()), numberOfNonZeroTransitions(transitionMatrix->getNonZeroEntryCount()) {
		if (forward) {
			this->initializeForward(transitionMatrix);
		} else {
			this->initializeBackward(transitionMatrix);
		}
	}

	//! Destructor
	/*!
	 * Destructor. Frees the internal storage.
	 */
	~GraphTransitions() {
		if (this->successorList != nullptr) {
			delete[] this->successorList;
		}
		if (this->stateIndications != nullptr) {
			delete[] this->stateIndications;
		}
	}

	/*!
	 * Returns an iterator to the successors of the given state.
	 * @param state The state for which to get the successor iterator.
	 * @return An iterator to the predecessors of the given states.
	 */
	statePredecessorIterator beginStateSuccessorsIterator(uint_fast64_t state) const {
		return this->successorList + this->stateIndications[state];
	}

	/*!
	 * Returns an iterator referring to the element after the successors of
	 * the given state.
	 * @param row The state for which to get the iterator.
	 * @return An iterator referring to the element after the successors of
	 * the given state.
	 */
	statePredecessorIterator endStateSuccessorsIterator(uint_fast64_t state) const {
		return this->successorList + this->stateIndications[state + 1];
	}

private:

	/*!
	 * Initializes this graph transitions object using the forward transition
	 * relation given by means of a sparse matrix.
	 */
	void initializeForward(std::shared_ptr<storm::storage::SparseMatrix<T>> transitionMatrix) {
		this->successorList = new uint_fast64_t[numberOfNonZeroTransitions];
		this->stateIndications = new uint_fast64_t[numberOfStates + 1];

		// First, we copy the index list from the sparse matrix as this will
		// stay the same.
		std::copy(transitionMatrix->getRowIndicationsPointer().begin(), transitionMatrix->getRowIndicationsPointer().end(), this->stateIndications);

		// Now we can iterate over all rows of the transition matrix and record
		// the target state.
		for (uint_fast64_t i = 0, currentNonZeroElement = 0; i < numberOfStates; i++) {
			for (auto rowIt = transitionMatrix->beginConstColumnIterator(i); rowIt != transitionMatrix->endConstColumnIterator(i); ++rowIt) {
				this->stateIndications[currentNonZeroElement++] = *rowIt;
			}
		}
	}

	/*!
	 * Initializes this graph transitions object using the backwards transition
	 * relation, whose forward transition relation is given by means of a sparse
	 * matrix.
	 */
	void initializeBackward(std::shared_ptr<storm::storage::SparseMatrix<T>> transitionMatrix) {
		this->successorList = new uint_fast64_t[numberOfNonZeroTransitions];
		this->stateIndications = new uint_fast64_t[numberOfStates + 1];

		// First, we need to count how many backward transitions each state has.
		// NOTE: We disregard the diagonal here, as we only consider "true"
		// predecessors.
		for (uint_fast64_t i = 0; i < numberOfStates; i++) {
			for (auto rowIt = transitionMatrix->beginConstColumnIterator(i); rowIt != transitionMatrix->endConstColumnIterator(i); ++rowIt) {
				this->stateIndications[*rowIt + 1]++;
			}
		}

		// Now compute the accumulated offsets.
		for (uint_fast64_t i = 1; i < numberOfStates; i++) {
			this->stateIndications[i] = this->stateIndications[i - 1] + this->stateIndications[i];
		}

		// Put a sentinel element at the end of the indices list. This way,
		// for each state i the range of indices can be read off between
		// state_indices_list[i] and state_indices_list[i + 1].
		this->stateIndications[numberOfStates] = numberOfNonZeroTransitions;

		// Create an array that stores the next index for each state. Initially
		// this corresponds to the previously computed accumulated offsets.
		uint_fast64_t* nextIndicesList = new uint_fast64_t[numberOfStates];
		std::copy(stateIndications, stateIndications + numberOfStates, nextIndicesList);

		// Now we are ready to actually fill in the list of predecessors for
		// every state. Again, we start by considering all but the last row.
		for (uint_fast64_t i = 0; i < numberOfStates; i++) {
			for (auto rowIt = transitionMatrix->beginConstColumnIterator(i); rowIt != transitionMatrix->endConstColumnIterator(i); ++rowIt) {
				this->successorList[nextIndicesList[*rowIt]++] = i;
			}
		}

		// Now we can dispose of the auxiliary array.
		delete[] nextIndicesList;
	}

	/*! A list of successors for *all* states. */
	uint_fast64_t* successorList;

	/*!
	 * A list of indices indicating at which position in the global array the
	 * successors of a state can be found.
	 */
	uint_fast64_t* stateIndications;

	/*!
	 * Store the number of states to determine the highest index at which the
	 * state_indices_list may be accessed.
	 */
	uint_fast64_t numberOfStates;

	/*!
	 * Store the number of non-zero transition entries to determine the highest
	 * index at which the predecessor_list may be accessed.
	 */
	uint_fast64_t numberOfNonZeroTransitions;
};

} // namespace models

} // namespace storm

#endif /* STORM_MODELS_GRAPHTRANSITIONS_H_ */