#ifndef OSRM_ENGINE_ROUTING_BASE_HPP
#define OSRM_ENGINE_ROUTING_BASE_HPP

#include "guidance/turn_bearing.hpp"
#include "guidance/turn_instruction.hpp"

#include "engine/algorithm.hpp"
#include "engine/datafacade.hpp"
#include "engine/internal_route_result.hpp"
#include "engine/phantom_node.hpp"
#include "engine/search_engine_data.hpp"

#include "util/coordinate_calculation.hpp"
#include "util/typedefs.hpp"

#include <boost/assert.hpp>

#include <cstddef>
#include <cstdint>

#include <algorithm>
#include <functional>
#include <iterator>
#include <memory>
#include <numeric>
#include <stack>
#include <utility>
#include <vector>

namespace osrm
{
namespace engine
{

namespace routing_algorithms
{

namespace details
{
template <typename Heap>
void insertSourceInForwardHeap(Heap &forward_heap, const PhantomNode &source)
{
    if (source.IsValidForwardSource())
    {
        forward_heap.Insert(source.forward_segment_id.id,
                            -source.GetForwardWeightPlusOffset(),
                            source.forward_segment_id.id);
    }

    if (source.IsValidReverseSource())
    {
        forward_heap.Insert(source.reverse_segment_id.id,
                            -source.GetReverseWeightPlusOffset(),
                            source.reverse_segment_id.id);
    }
}

template <typename Heap>
void insertTargetInReverseHeap(Heap &reverse_heap, const PhantomNode &target)
{
    if (target.IsValidForwardTarget())
    {
        reverse_heap.Insert(target.forward_segment_id.id,
                            target.GetForwardWeightPlusOffset(),
                            target.forward_segment_id.id);
    }

    if (target.IsValidReverseTarget())
    {
        reverse_heap.Insert(target.reverse_segment_id.id,
                            target.GetReverseWeightPlusOffset(),
                            target.reverse_segment_id.id);
    }
}
} // namespace details
static constexpr bool FORWARD_DIRECTION = true;
static constexpr bool REVERSE_DIRECTION = false;

// Identify nodes in the forward(reverse) search direction that will require loop forcing
// e.g. if source and destination nodes are on the same segment.
std::vector<NodeID> getForwardLoopNodes(const PhantomEndpointCandidates &candidates);
std::vector<NodeID> getForwardLoopNodes(const PhantomCandidatesToTarget &candidates);
std::vector<NodeID> getBackwardLoopNodes(const PhantomEndpointCandidates &candidates);
std::vector<NodeID> getBackwardLoopNodes(const PhantomCandidatesToTarget &candidates);

// Find the specific phantom node endpoints for a given path from a list of candidates.
PhantomEndpoints endpointsFromCandidates(const PhantomEndpointCandidates &candidates,
                                         const std::vector<NodeID> &path);

template <typename HeapNodeT>
inline bool force_loop(const std::vector<NodeID> &force_nodes, const HeapNodeT &heap_node)
{
    // if loops are forced, they are so at the source
    return !force_nodes.empty() &&
           std::find(force_nodes.begin(), force_nodes.end(), heap_node.node) != force_nodes.end() &&
           heap_node.data.parent == heap_node.node;
}

template <typename Heap>
void insertNodesInHeaps(Heap &forward_heap, Heap &reverse_heap, const PhantomEndpoints &endpoints)
{
    details::insertSourceInForwardHeap(forward_heap, endpoints.source_phantom);
    details::insertTargetInReverseHeap(reverse_heap, endpoints.target_phantom);
}

template <typename Heap>
void insertNodesInHeaps(Heap &forward_heap,
                        Heap &reverse_heap,
                        const PhantomEndpointCandidates &endpoint_candidates)
{
    for (const auto &source : endpoint_candidates.source_phantoms)
    {
        details::insertSourceInForwardHeap(forward_heap, source);
    }

    for (const auto &target : endpoint_candidates.target_phantoms)
    {
        details::insertTargetInReverseHeap(reverse_heap, target);
    }
}

template <typename ManyToManyQueryHeap>
void insertSourceInHeap(ManyToManyQueryHeap &heap, const PhantomNodeCandidates &source_candidates)
{
    for (const auto &phantom_node : source_candidates)
    {
        if (phantom_node.IsValidForwardSource())
        {
            heap.Insert(phantom_node.forward_segment_id.id,
                        -phantom_node.GetForwardWeightPlusOffset(),
                        {phantom_node.forward_segment_id.id,
                         -phantom_node.GetForwardDuration(),
                         -phantom_node.GetForwardDistance()});
        }
        if (phantom_node.IsValidReverseSource())
        {
            heap.Insert(phantom_node.reverse_segment_id.id,
                        -phantom_node.GetReverseWeightPlusOffset(),
                        {phantom_node.reverse_segment_id.id,
                         -phantom_node.GetReverseDuration(),
                         -phantom_node.GetReverseDistance()});
        }
    }
}

template <typename ManyToManyQueryHeap>
void insertTargetInHeap(ManyToManyQueryHeap &heap, const PhantomNodeCandidates &target_candidates)
{
    for (const auto &phantom_node : target_candidates)
    {
        if (phantom_node.IsValidForwardTarget())
        {
            heap.Insert(phantom_node.forward_segment_id.id,
                        phantom_node.GetForwardWeightPlusOffset(),
                        {phantom_node.forward_segment_id.id,
                         phantom_node.GetForwardDuration(),
                         phantom_node.GetForwardDistance()});
        }
        if (phantom_node.IsValidReverseTarget())
        {
            heap.Insert(phantom_node.reverse_segment_id.id,
                        phantom_node.GetReverseWeightPlusOffset(),
                        {phantom_node.reverse_segment_id.id,
                         phantom_node.GetReverseDuration(),
                         phantom_node.GetReverseDistance()});
        }
    }
}

template <typename FacadeT>
void annotatePath(const FacadeT &facade,
                  const PhantomEndpoints &endpoints,
                  const std::vector<NodeID> &unpacked_nodes,
                  const std::vector<EdgeID> &unpacked_edges,
                  std::vector<PathData> &unpacked_path)
{
    BOOST_ASSERT(!unpacked_nodes.empty());
    BOOST_ASSERT(unpacked_nodes.size() == unpacked_edges.size() + 1);

    const auto source_node_id = unpacked_nodes.front();
    const auto target_node_id = unpacked_nodes.back();
    const bool start_traversed_in_reverse =
        endpoints.source_phantom.forward_segment_id.id != source_node_id;
    const bool target_traversed_in_reverse =
        endpoints.target_phantom.forward_segment_id.id != target_node_id;

    BOOST_ASSERT(endpoints.source_phantom.forward_segment_id.id == source_node_id ||
                 endpoints.source_phantom.reverse_segment_id.id == source_node_id);
    BOOST_ASSERT(endpoints.target_phantom.forward_segment_id.id == target_node_id ||
                 endpoints.target_phantom.reverse_segment_id.id == target_node_id);

    // datastructures to hold extracted data from geometry
    std::vector<NodeID> id_vector;
    std::vector<SegmentWeight> weight_vector;
    std::vector<SegmentDuration> duration_vector;
    std::vector<DatasourceID> datasource_vector;

    const auto get_segment_geometry = [&](const auto geometry_index) {
        const auto copy = [](auto &vector, const auto range) {
            vector.resize(range.size());
            std::copy(range.begin(), range.end(), vector.begin());
        };

        if (geometry_index.forward)
        {
            copy(id_vector, facade.GetUncompressedForwardGeometry(geometry_index.id));
            copy(weight_vector, facade.GetUncompressedForwardWeights(geometry_index.id));
            copy(duration_vector, facade.GetUncompressedForwardDurations(geometry_index.id));
            copy(datasource_vector, facade.GetUncompressedForwardDatasources(geometry_index.id));
        }
        else
        {
            copy(id_vector, facade.GetUncompressedReverseGeometry(geometry_index.id));
            copy(weight_vector, facade.GetUncompressedReverseWeights(geometry_index.id));
            copy(duration_vector, facade.GetUncompressedReverseDurations(geometry_index.id));
            copy(datasource_vector, facade.GetUncompressedReverseDatasources(geometry_index.id));
        }
    };

    auto node_from = unpacked_nodes.begin(), node_last = std::prev(unpacked_nodes.end());
    for (auto edge = unpacked_edges.begin(); node_from != node_last; ++node_from, ++edge)
    {
        const auto &edge_data = facade.GetEdgeData(*edge);
        const auto turn_id = edge_data.turn_id; // edge-based graph edge index
        const auto node_id = *node_from;        // edge-based graph node index

        const auto geometry_index = facade.GetGeometryIndex(node_id);
        get_segment_geometry(geometry_index);

        BOOST_ASSERT(!id_vector.empty());
        BOOST_ASSERT(!datasource_vector.empty());
        BOOST_ASSERT(weight_vector.size() + 1 == id_vector.size());
        BOOST_ASSERT(duration_vector.size() + 1 == id_vector.size());

        const bool is_first_segment = unpacked_path.empty();

        std::size_t start_index = 0;
        if (is_first_segment)
        {
            unsigned short segment_position = endpoints.source_phantom.fwd_segment_position;
            if (start_traversed_in_reverse)
            {
                segment_position =
                    weight_vector.size() - endpoints.source_phantom.fwd_segment_position - 1;
            }
            BOOST_ASSERT(segment_position >= 0);
            start_index = static_cast<std::size_t>(segment_position);
        }
        const std::size_t end_index = weight_vector.size();

        BOOST_ASSERT(start_index < end_index);
        for (std::size_t segment_idx = start_index; segment_idx < end_index; ++segment_idx)
        {
            unpacked_path.push_back(
                PathData{node_id,
                         id_vector[segment_idx + 1],
                         static_cast<EdgeWeight>(weight_vector[segment_idx]),
                         0,
                         static_cast<EdgeDuration>(duration_vector[segment_idx]),
                         0,
                         datasource_vector[segment_idx],
                         boost::none});
        }
        BOOST_ASSERT(unpacked_path.size() > 0);

        const auto turn_duration = facade.GetDurationPenaltyForEdgeID(turn_id);
        const auto turn_weight = facade.GetWeightPenaltyForEdgeID(turn_id);

        unpacked_path.back().duration_until_turn += turn_duration;
        unpacked_path.back().duration_of_turn = turn_duration;
        unpacked_path.back().weight_until_turn += turn_weight;
        unpacked_path.back().weight_of_turn = turn_weight;
        unpacked_path.back().turn_edge = turn_id;
    }

    std::size_t start_index = 0, end_index = 0;
    const auto source_geometry_id = facade.GetGeometryIndex(source_node_id).id;
    const auto target_geometry = facade.GetGeometryIndex(target_node_id);
    const auto is_local_path = source_geometry_id == target_geometry.id && unpacked_path.empty();

    get_segment_geometry(target_geometry);

    if (target_traversed_in_reverse)
    {
        if (is_local_path)
        {
            start_index = weight_vector.size() - endpoints.source_phantom.fwd_segment_position - 1;
        }
        end_index = weight_vector.size() - endpoints.target_phantom.fwd_segment_position - 1;
    }
    else
    {
        if (is_local_path)
        {
            start_index = endpoints.source_phantom.fwd_segment_position;
        }
        end_index = endpoints.target_phantom.fwd_segment_position;
    }

    // Given the following compressed geometry:
    // U---v---w---x---y---Z
    //    s           t
    // s: fwd_segment 0
    // t: fwd_segment 3
    // -> (U, v), (v, w), (w, x)
    // note that (x, t) is _not_ included but needs to be added later.
    for (std::size_t segment_idx = start_index; segment_idx != end_index;
         (start_index < end_index ? ++segment_idx : --segment_idx))
    {
        BOOST_ASSERT(segment_idx < static_cast<std::size_t>(id_vector.size() - 1));
        unpacked_path.push_back(
            PathData{target_node_id,
                     id_vector[start_index < end_index ? segment_idx + 1 : segment_idx - 1],
                     static_cast<EdgeWeight>(weight_vector[segment_idx]),
                     0,
                     static_cast<EdgeDuration>(duration_vector[segment_idx]),
                     0,
                     datasource_vector[segment_idx],
                     boost::none});
    }

    if (!unpacked_path.empty())
    {
        const auto source_weight = start_traversed_in_reverse
                                       ? endpoints.source_phantom.reverse_weight
                                       : endpoints.source_phantom.forward_weight;
        const auto source_duration = start_traversed_in_reverse
                                         ? endpoints.source_phantom.reverse_duration
                                         : endpoints.source_phantom.forward_duration;
        // The above code will create segments for (v, w), (w,x), (x, y) and (y, Z).
        // However the first segment duration needs to be adjusted to the fact that the source
        // phantom is in the middle of the segment. We do this by subtracting v--s from the
        // duration.

        // Since it's possible duration_until_turn can be less than source_weight here if
        // a negative enough turn penalty is used to modify this edge weight during
        // osrm-contract, we clamp to 0 here so as not to return a negative duration
        // for this segment.

        // TODO this creates a scenario where it's possible the duration from a phantom
        // node to the first turn would be the same as from end to end of a segment,
        // which is obviously incorrect and not ideal...
        unpacked_path.front().weight_until_turn =
            std::max(unpacked_path.front().weight_until_turn - source_weight, 0);
        unpacked_path.front().duration_until_turn =
            std::max(unpacked_path.front().duration_until_turn - source_duration, 0);
    }
}

template <typename Algorithm>
double getPathDistance(const DataFacade<Algorithm> &facade,
                       const std::vector<PathData> &unpacked_path,
                       const PhantomNode &source_phantom,
                       const PhantomNode &target_phantom)
{
    using util::coordinate_calculation::detail::DEGREE_TO_RAD;
    using util::coordinate_calculation::detail::EARTH_RADIUS;

    double distance = 0;
    double prev_lat =
        static_cast<double>(util::toFloating(source_phantom.location.lat)) * DEGREE_TO_RAD;
    double prev_lon =
        static_cast<double>(util::toFloating(source_phantom.location.lon)) * DEGREE_TO_RAD;
    double prev_cos = std::cos(prev_lat);
    for (const auto &p : unpacked_path)
    {
        const auto current_coordinate = facade.GetCoordinateOfNode(p.turn_via_node);

        const double current_lat =
            static_cast<double>(util::toFloating(current_coordinate.lat)) * DEGREE_TO_RAD;
        const double current_lon =
            static_cast<double>(util::toFloating(current_coordinate.lon)) * DEGREE_TO_RAD;
        const double current_cos = std::cos(current_lat);

        const double sin_dlon = std::sin((prev_lon - current_lon) / 2.0);
        const double sin_dlat = std::sin((prev_lat - current_lat) / 2.0);

        const double aharv = sin_dlat * sin_dlat + prev_cos * current_cos * sin_dlon * sin_dlon;
        const double charv = 2. * std::atan2(std::sqrt(aharv), std::sqrt(1.0 - aharv));
        distance += EARTH_RADIUS * charv;

        prev_lat = current_lat;
        prev_lon = current_lon;
        prev_cos = current_cos;
    }

    const double current_lat =
        static_cast<double>(util::toFloating(target_phantom.location.lat)) * DEGREE_TO_RAD;
    const double current_lon =
        static_cast<double>(util::toFloating(target_phantom.location.lon)) * DEGREE_TO_RAD;
    const double current_cos = std::cos(current_lat);

    const double sin_dlon = std::sin((prev_lon - current_lon) / 2.0);
    const double sin_dlat = std::sin((prev_lat - current_lat) / 2.0);

    const double aharv = sin_dlat * sin_dlat + prev_cos * current_cos * sin_dlon * sin_dlon;
    const double charv = 2. * std::atan2(std::sqrt(aharv), std::sqrt(1.0 - aharv));
    distance += EARTH_RADIUS * charv;

    return distance;
}

template <typename AlgorithmT>
InternalRouteResult extractRoute(const DataFacade<AlgorithmT> &facade,
                                 const EdgeWeight weight,
                                 const PhantomEndpointCandidates &endpoint_candidates,
                                 const std::vector<NodeID> &unpacked_nodes,
                                 const std::vector<EdgeID> &unpacked_edges)
{
    InternalRouteResult raw_route_data;

    // No path found for both target nodes?
    if (INVALID_EDGE_WEIGHT == weight)
    {
        return raw_route_data;
    }

    auto phantom_endpoints = endpointsFromCandidates(endpoint_candidates, unpacked_nodes);
    raw_route_data.leg_endpoints = {phantom_endpoints};

    raw_route_data.shortest_path_weight = weight;
    raw_route_data.unpacked_path_segments.resize(1);
    raw_route_data.source_traversed_in_reverse.push_back(
        (unpacked_nodes.front() != phantom_endpoints.source_phantom.forward_segment_id.id));
    raw_route_data.target_traversed_in_reverse.push_back(
        (unpacked_nodes.back() != phantom_endpoints.target_phantom.forward_segment_id.id));

    annotatePath(facade,
                 phantom_endpoints,
                 unpacked_nodes,
                 unpacked_edges,
                 raw_route_data.unpacked_path_segments.front());

    return raw_route_data;
}

template <typename FacadeT> EdgeDistance computeEdgeDistance(const FacadeT &facade, NodeID node_id)
{
    const auto geometry_index = facade.GetGeometryIndex(node_id);

    EdgeDistance total_distance = 0.0;

    auto geometry_range = facade.GetUncompressedForwardGeometry(geometry_index.id);
    for (auto current = geometry_range.begin(); current < geometry_range.end() - 1; ++current)
    {
        total_distance += util::coordinate_calculation::fccApproximateDistance(
            facade.GetCoordinateOfNode(*current), facade.GetCoordinateOfNode(*std::next(current)));
    }

    return total_distance;
}

} // namespace routing_algorithms
} // namespace engine
} // namespace osrm

#endif // OSRM_ENGINE_ROUTING_BASE_HPP


#ifndef STREAM_LINE_HPP
#define STREAM_LINE_HPP
#include <ctime>
#include <random>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/for_each.hpp>
#include <deque>
#include <vector>
#include "image/image.hpp"
#include "interpolation_process.hpp"
#include "basic_process.hpp"
#include "roi.hpp"
#include "fib_data.hpp"

typedef boost::mpl::vector<
            EstimateNextDirection,
            SmoothDir,
            MoveTrack
> streamline_method_process;

typedef boost::mpl::vector<
            LocateVoxel
> voxel_tracking;

typedef boost::mpl::vector<
            EstimateNextDirectionRungeKutta4,
            MoveTrack
> streamline_runge_kutta_4_method_process;

struct TrackingParam
{
    float threshold,otsu_threshold;
    float cull_cos_angle;
    float step_size;
    float smooth_fraction;
    float min_length;
    float max_length;

};

class TrackingMethod{
private:
    std::auto_ptr<basic_interpolation> interpolation;
public:// Parameters
    image::vector<3,float> position;
    image::vector<3,float> dir;
    image::vector<3,float> next_dir;
    bool terminated;
    bool forward;
public:
    const tracking_data& trk;
    const TrackingParam& param;
    float current_fa_threshold;
    float current_tracking_angle;
    float current_tracking_smoothing;
    float current_step_size_in_voxel[3];
    int current_min_steps3;
    int current_max_steps3;
    void scaling_in_voxel(image::vector<3,float>& dir) const
    {
        dir[0] *= current_step_size_in_voxel[0];
        dir[1] *= current_step_size_in_voxel[1];
        dir[2] *= current_step_size_in_voxel[2];
    }
private:
    const RoiMgr& roi_mgr;
	std::vector<float> track_buffer;
	mutable std::vector<float> reverse_buffer;
    unsigned int buffer_front_pos;
    unsigned int buffer_back_pos;

private:
    unsigned int init_fib_index;
public:
    unsigned int get_buffer_size(void) const
	{
		return buffer_back_pos-buffer_front_pos;
	}
    unsigned int get_point_count(void) const
	{
		return (buffer_back_pos-buffer_front_pos)/3;
	}
    bool get_dir(const image::vector<3,float>& position,
                      const image::vector<3,float>& ref_dir,
                      image::vector<3,float>& result_dir)
    {
        return interpolation->evaluate(trk,position,ref_dir,result_dir,current_fa_threshold,current_tracking_angle);
    }
public:
    TrackingMethod(const tracking_data& trk_,basic_interpolation* interpolation_,
                   const RoiMgr& roi_mgr_,const TrackingParam& param_):
        trk(trk_),interpolation(interpolation_),roi_mgr(roi_mgr_),param(param_),init_fib_index(0)
	{

	}
public:
	template<class Process>
    void operator()(Process)
    {
        Process()(*this);
    }
	template<class ProcessList>
    void tracking(ProcessList)
    {
        boost::mpl::for_each<ProcessList>(boost::ref(*this));
    }
public:

	std::vector<float>& get_track_buffer(void){return track_buffer;}
	std::vector<float>& get_reverse_buffer(void){return reverse_buffer;}

	template<class ProcessList>
    bool start_tracking(bool smoothing)
    {
        image::vector<3,float> seed_pos(position);
        image::vector<3,float> begin_dir(dir);
        // floatd for full backward or full forward
        track_buffer.resize(current_max_steps3 << 1);
        reverse_buffer.resize(current_max_steps3 << 1);
        buffer_front_pos = current_max_steps3;
        buffer_back_pos = current_max_steps3;
        image::vector<3,float> end_point1;
        terminated = false;
		do
		{
            if(get_buffer_size() > current_max_steps3 || buffer_back_pos + 3 >= track_buffer.size())
				return false;
            if(roi_mgr.is_excluded_point(position))
				return false;
            track_buffer[buffer_back_pos] = position[0];
            track_buffer[buffer_back_pos+1] = position[1];
            track_buffer[buffer_back_pos+2] = position[2];
            buffer_back_pos += 3;
            if(roi_mgr.is_terminate_point(position))
                break;
            tracking(ProcessList());
			// make sure that the length won't overflow

		}
        while(!terminated);

        end_point1 = position;
        terminated = false;
        position = seed_pos;
        dir = -begin_dir;
        forward = false;
		do
		{
            tracking(ProcessList());
			// make sure that the length won't overflow
            if(get_buffer_size() > current_max_steps3 || buffer_front_pos < 3)
				return false;
            if(terminated)
				break;
			buffer_front_pos -= 3;
            if(roi_mgr.is_excluded_point(position))
				return false;
            track_buffer[buffer_front_pos] = position[0];
            track_buffer[buffer_front_pos+1] = position[1];
            track_buffer[buffer_front_pos+2] = position[2];
        }
        while(!roi_mgr.is_terminate_point(position));

        if(smoothing)
        {
            std::vector<float> smoothed(track_buffer.size());
            float w[5] = {1.0,2.0,4.0,2.0,1.0};
            int dis[5] = {-6, -3, 0, 3, 6};
            for(int index = buffer_front_pos;index < buffer_back_pos;++index)
            {
                float sum_w = 0.0;
                float sum = 0.0;
                for(char i = 0;i < 5;++i)
                {
                    int cur_index = index + dis[i];
                    if(cur_index < buffer_front_pos || cur_index >= buffer_back_pos)
                        continue;
                    sum += w[i]*track_buffer[cur_index];
                    sum_w += w[i];
                }
                if(sum_w != 0.0)
                    smoothed[index] = sum/sum_w;
            }
            smoothed.swap(track_buffer);
        }

        return get_buffer_size() > current_min_steps3 &&
               roi_mgr.have_include(get_result(),get_buffer_size()) &&
               roi_mgr.fulfill_end_point(position,end_point1);

	}
        bool init(unsigned char initial_direction,
                  const image::vector<3,float>& position_,
                  std::mt19937& seed)
        {
            std::uniform_real_distribution<float> gen(0,1);
            position = position_;
            terminated = false;
            forward = true;
            image::pixel_index<3> pindex(std::round(position[0]),
                                    std::round(position[1]),
                                    std::round(position[2]),trk.dim);
            if (!trk.dim.is_valid(pindex))
                return false;

            switch (initial_direction)
            {
            case 0:// main direction
                {
                    if(trk.fa[0][pindex.index()] < current_fa_threshold)
                        return false;
                    dir = trk.get_dir(pindex.index(),0);
                }
                return true;
            case 1:// random direction
                for (unsigned int index = 0;index < 10;++index)
                {
                    float txy = gen(seed);
                    float tz = gen(seed)/2.0;
                    float x = std::sin(txy)*std::sin(tz);
                    float y = std::cos(txy)*std::sin(tz);
                    float z = std::cos(tz);
                    if (get_dir(position,image::vector<3,float>(x,y,z),dir))
                        return true;
                }
                return false;
            case 2:// all direction
                {
                    if (init_fib_index >= trk.fib_num ||
                        trk.fa[init_fib_index][pindex.index()] < current_fa_threshold)
                    {
                        init_fib_index = 0;
                        return false;
                    }
                    else
                        dir = trk.get_dir(pindex.index(),init_fib_index);
                    ++init_fib_index;
                }
                return true;
            }
            return false;
        }

        const float* tracking(unsigned char tracking_method,unsigned int& point_count)
        {
            point_count = 0;
            switch (tracking_method)
            {
            case 0:
                if (!start_tracking<streamline_method_process>(false))
                    return 0;
                break;
            case 1:
                if (!start_tracking<streamline_runge_kutta_4_method_process>(false))
                    return 0;
                break;
            case 2:
                position[0] = std::round(position[0]);
                position[1] = std::round(position[1]);
                position[2] = std::round(position[2]);
                if (!start_tracking<voxel_tracking>(true))
                    return 0;
                break;
            default:
                return 0;
            }
            point_count = get_point_count();
            return get_result();
        }

	const float* get_result(void) const
	{
                image::vector<3,float> head(&*(track_buffer.begin() + buffer_front_pos));
                image::vector<3,float> tail(&*(track_buffer.begin() + buffer_back_pos-3));
		tail -= head;
                image::vector<3,float> abs_dis(std::abs(tail[0]),std::abs(tail[1]),std::abs(tail[2]));

		if((abs_dis[0] > abs_dis[1] && abs_dis[0] > abs_dis[2] && tail[0] < 0) ||
		   (abs_dis[1] > abs_dis[0] && abs_dis[1] > abs_dis[2] && tail[1] < 0) ||
		   (abs_dis[2] > abs_dis[1] && abs_dis[2] > abs_dis[0] && tail[2] < 0))
		{
			std::vector<float>::const_iterator src = track_buffer.begin() + buffer_back_pos-3;
			std::vector<float>::iterator iter = reverse_buffer.begin();
			std::vector<float>::iterator end = reverse_buffer.begin()+buffer_back_pos-buffer_front_pos;
			for(;iter < end;iter += 3,src -= 3)
				std::copy(src,src+3,iter);
			return &*reverse_buffer.begin();
		}

		return &*(track_buffer.begin() + buffer_front_pos);
	}
};

#endif//STREAM_LINE_HPP