├── .gitignore ├── README.md ├── makefile ├── main.cpp ├── MDPoint.h ├── MDPoint.cpp ├── mainpage.dox ├── Param.h ├── TrajData.h ├── Outlier.h ├── Outlier.cpp ├── Trajectory.cpp ├── OutlierDetector.h ├── ConvertData.cpp ├── DistanceOutlier.h ├── Trajectory.h ├── TrajData.cpp ├── DistanceOutlier.cpp ├── Measure.cpp ├── csv_parser.cpp ├── csv_parser.hpp ├── OutlierDetector.cpp └── hurricane2000_2006.tra /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.o 3 | main 4 | convertData 5 | html/ 6 | latex/ 7 | .cproject 8 | .project 9 | sampleOut.eps 10 | sampleOut.png 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is a library for trajectory outlier detection based on code provided publicly by Jae-Gil Lee at http://dm.kaist.ac.kr/jaegil/. 2 | 3 | The algorithm that this code executes is from a paper entitled "Trajectory Outlier Detection: A Partition-and-Detect framework", 4 | and can be found [here](http://dm.kaist.ac.kr/jaegil/papers/icde08.pdf). 5 | 6 | Sample code using the library is provided in main.cpp and can be compiled with the command "make". A C++11 compatible compiler and gnuplot are required. 7 | 8 | For more details see the [project page](http://hansenrl.github.io/trajectory/index.html). 9 | 10 | 11 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | CC = g++ 2 | CFLAGS = -std=c++11 -g 3 | LFLAGS = -lboost_iostreams -lboost_system -lboost_filesystem 4 | DEPS = DistanceOutlier.h MDPoint.h Outlier.h OutlierDetector.h TrajData.h Trajectory.h Param.h 5 | 6 | main: main.o Trajectory.o MDPoint.o OutlierDetector.o DistanceOutlier.o Measure.o Outlier.o TrajData.o 7 | $(CC) $(CFLAGS) -o $@ $^ $(LFLAGS) 8 | 9 | all: main convertData 10 | 11 | clean: 12 | -@rm *.o main convertData 13 | 14 | convertData: ConvertData.cpp csv_parser.cpp csv_parser.hpp 15 | $(CC) $(CFLAGS) -o $@ ConvertData.cpp csv_parser.cpp 16 | 17 | %.o: %.cpp $(DEPS) 18 | $(CC) $(CFLAGS) -c -o $@ $< 19 | 20 | 21 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "TrajData.h" 5 | #include "OutlierDetector.h" 6 | 7 | using namespace std; 8 | 9 | int main () { 10 | // TrajData reads in data and holds trajectory information 11 | TrajData data; 12 | data.readFile("hurricane2000_2006.tra"); 13 | 14 | // COutlierDetector will find outliers in the provided dataset 15 | COutlierDetector outlierDetector(&data); 16 | outlierDetector.PartitionTrajectory(); 17 | outlierDetector.DetectOutlier(); 18 | cout << "Size of trajectory outlierList: " << data.m_outlierList.size() << "\n"; 19 | cout << "Finished partitioning and finding outliers!\n"; 20 | 21 | // Output plot of trajectories and outliers 22 | data.OutputTrajectoryPlot("sampleOut.eps"); 23 | 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /MDPoint.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // CMDPoint command target 4 | 5 | /** 6 | * \brief A simple point class 7 | * 8 | * Has support for a variable number of dimensions. 9 | */ 10 | class CMDPoint 11 | { 12 | public: 13 | CMDPoint(); 14 | CMDPoint(int nDimensions); 15 | virtual ~CMDPoint(); 16 | private: 17 | int m_nDimensions; // the number of dimensions of a point 18 | float* m_coordinate; // the coordinate of a point 19 | public: 20 | const int GetNDimensions() const { return m_nDimensions; } 21 | ///< Getter for the number of dimensions 22 | const float GetCoordinate(int nth) const { return m_coordinate[nth]; } 23 | ///< Getter for the value of the nth dimension of the point 24 | void SetCoordinate(int nth, float value) { m_coordinate[nth] = value; } 25 | ///< Setter for the value of the nth dimension of the point 26 | }; 27 | -------------------------------------------------------------------------------- /MDPoint.cpp: -------------------------------------------------------------------------------- 1 | // MDPoint.cpp : implementation file 2 | // 3 | 4 | //#include "stdafx.h" 5 | #include "MDPoint.h" 6 | 7 | 8 | // CMDPoint 9 | 10 | /** 11 | * \brief Default constructor 12 | * 13 | * Constructs a point with dimension 2 and both dimension values equal to 0.0. 14 | */ 15 | CMDPoint::CMDPoint() 16 | { 17 | m_nDimensions = 2; 18 | m_coordinate = new float [m_nDimensions]; 19 | m_coordinate[0] = m_coordinate[1] = 0.0; 20 | } 21 | 22 | /** 23 | * \brief Constructor for a point with custom dimension 24 | * 25 | * @param [in] nDimensions the number of dimensions the point exists in 26 | */ 27 | CMDPoint::CMDPoint(int nDimensions) 28 | { 29 | m_nDimensions = nDimensions; 30 | m_coordinate = new float [m_nDimensions]; 31 | for (int i = 0; i < m_nDimensions; i++) m_coordinate[i] = 0.0; 32 | } 33 | 34 | CMDPoint::~CMDPoint() 35 | { 36 | } 37 | -------------------------------------------------------------------------------- /mainpage.dox: -------------------------------------------------------------------------------- 1 | /** 2 | \mainpage Trajectory Outlier Detection 3 | 4 | Introduction 5 | === 6 | 7 | This is a library for trajectory outlier detection that uses the algorithm presented by 8 | Jae-Gil Lee and others in their paper [Trajectory Outlier Detection: A Partition-and-Detect Framework][1] \[1\]. 9 | 10 | 11 | This code is based heavily on the source code made available on Jae-Gil Lee's [webpage][2]. The code on 12 | on his website is a complete implementation of their algorithm, but like any good research prototype 13 | includes little documentation. Additionally, it was written with Microsoft Visual Studio - 14 | it uses Windows-specific visualization and proprietary Microsoft C++ structures. 15 | Therefore, the goal of this project was to take that implementation and rewrite it to be a cross-platform library. 16 | 17 | ![Alt Image](../example.png) 18 | 19 | [1]: http://dm.kaist.ac.kr/jaegil/papers/icde08.pdf "Trajectory Outlier Detection Paper" 20 | [2]: http://dm.kaist.ac.kr/jaegil/ "Source Code" 21 | \[1\] Jae-Gil Lee, Jiawei Han, and Xiaolei Li. 2008. Trajectory Outlier Detection: 22 | A Partition-and-Detect Framework. In Proceedings of the 2008 IEEE 24th International 23 | Conference on Data Engineering (ICDE '08). IEEE Computer Society, Washington, DC, USA, 140-149. 24 | 25 | */ 26 | -------------------------------------------------------------------------------- /Param.h: -------------------------------------------------------------------------------- 1 | #ifndef __PARAM_H__ 2 | #define __PARAM_H__ 3 | 4 | #define __COMBO_II__ 5 | 6 | #if defined(__COMBO_I__) 7 | #undef __USE_CONVENTIONAL_PARTITONING__ 8 | #define __USE_NO_PARTITIONING__ 9 | #undef __PARTITION_PRUNING_OPTIMIZATION__ 10 | #elif defined(__COMBO_II__) 11 | #define __USE_CONVENTIONAL_PARTITONING__ 12 | #undef __USE_NO_PARTITIONING__ 13 | #define __PARTITION_PRUNING_OPTIMIZATION__ 14 | #endif 15 | #undef __SHOW_TRAJECTORY_PARTITION__ 16 | #define __INCORPORATE_DENSITY__ 17 | #define __PRECOMPUTE_DENSITY__ 18 | #undef __VISUALIZE_DEBUG_INFO__ 19 | 20 | // This header file contains all the tuning parameters for the outlier detection algorithm 21 | 22 | const float g_FRACTION_PARAMETER = (float)0.95; 23 | const float g_DISTANCE_PARAMETER = (float)82.0; // (float)80.0; 24 | const float g_MINIMUM_OUTLYING_PROPORTION = (float)0.50; // (float)0.10; 25 | 26 | const int MDL_COST_ADVANTAGE = 20; 27 | const float MIN_LINESEGMENT_LENGTH = 1.0; 28 | const float MAX_LINESEGMENT_LENGTH = 10000.0; // 100.0 only for deer 29 | 30 | #define WEIGHTED_DISTANCE(_x,_y,_z) ((float)1.0 * (_x) + (float)1.0 * (_y) + (float)10.0 * (_z)) 31 | // #define WEIGHTED_DISTANCE(_x,_y,_z) ((float)1.0 * (_x) + (float)1.0 * (_y) + (float)5.0 * (_z)) 32 | 33 | #define RESULT_FILE "C:\\experiments\\trajectory outlier\\result\\result.txt" 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /TrajData.h: -------------------------------------------------------------------------------- 1 | /* 2 | * TrajData.h 3 | * 4 | * Created on: Oct 16, 2014 5 | * Author: ross 6 | */ 7 | 8 | #ifndef TRAJDATA_H_ 9 | #define TRAJDATA_H_ 10 | 11 | #include 12 | #include 13 | #include "Outlier.h" 14 | #include "Trajectory.h" 15 | 16 | /** 17 | * \brief The main class, and handler for all of the trajectory data 18 | * 19 | * The main purpose of this class is to hold the trajectory data. Contains functions to read in trajectory data and output 20 | * images. After outlier detection is run this class also holds information on the outlying trajectories and outlying partitions. 21 | */ 22 | class TrajData 23 | { 24 | public: 25 | TrajData(); 26 | ~TrajData(); 27 | std::string m_inputFilePath; ///< The path of the input file 28 | int m_nDimensions; ///< The dimensionality of the dataset (typically 2) 29 | int m_nTrajectories; ///< The number of trajectories in the dataset 30 | int m_nOutliers; ///< The number of outliers found 31 | int m_nOutlyingPartitions; ///< The outlying partitions found 32 | 33 | std::vector m_trajectoryList; ///< The list of trajectories 34 | std::list m_outlierList; ///< The list of outliers 35 | float m_paramFraction; ///< The parameter p (fraction of trajectories that must be not close to be an outlier) 36 | float m_paramDistance; ///< The distance parameter D 37 | int m_nLineSegments; ///< The number of line segments in the original data 38 | int m_nTrajectoryPartitions; ///< The number of trajectory partitions 39 | 40 | bool readFile(string); 41 | void OutputTrajectoryPlot(string); 42 | void OutputTrajectoryPlotPNG(string filePath); 43 | void OutputTrajectoryPlot(string filePath, string termSettings, string oTrajColor, int oTrajWidth, string oPartColor, int oPartWidth); 44 | 45 | private: 46 | int m_maxNPoints; ///< The maximum number of points to read in 47 | }; 48 | 49 | 50 | #endif /* TRAJDATA_H_ */ 51 | -------------------------------------------------------------------------------- /Outlier.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // COutlier command target 4 | 5 | #include "MDPoint.h" 6 | #include "Trajectory.h" 7 | #include 8 | 9 | #include 10 | using namespace std; 11 | 12 | 13 | typedef pair LineSegment; 14 | 15 | /** 16 | * \brief A trajectory outlier 17 | * 18 | * Holds information useful for working with a trajectory that is an outlier - such as the outlying partitions 19 | * that are part of this trajectory 20 | */ 21 | class COutlier 22 | { 23 | friend class COutlierDetector; 24 | public: 25 | COutlier(); 26 | COutlier(int id, int trajectoryId, int nDimensions); 27 | virtual ~COutlier(); 28 | private: 29 | int m_outlierId; // the identifier of this outlier 30 | int m_nDimensions; // the dimensionality of this outlier 31 | int m_trajectoryId; // the identifier of the trajectory that this outlier belongs to 32 | int m_nOutlyingPartitions; // the number of outlying trajectory partitions in this outlier 33 | vector m_outlyingPartitionArray; // the array of outlying trajectory partitions 34 | float m_outlyingRatio; // the ratio of the outlying length to the entire length 35 | int m_nPenWidth; // the width of the pen for drawing a cluster 36 | public: 37 | void SetId(int id) { m_outlierId = id; } 38 | ///< Setter for the outlier ID 39 | const int GetId() const { return m_outlierId; } 40 | ///< Getter for the outlier ID 41 | const int GetTrajectoryId() const { return m_trajectoryId; } 42 | ///< Getter for the trajectory ID 43 | const int GetNOutlyingPartitions() const { return m_nOutlyingPartitions; } 44 | ///< Getter for the number of outlying partitions in the outlying trajectory 45 | const vector GetOutlyingPartitionArray() const { return m_outlyingPartitionArray; } 46 | ///< Getter for the array of outlying partitions 47 | const float GetOutlyingRatio() const { return m_outlyingRatio; } 48 | ///< Getter for the ratio of outlying partition length to total length 49 | void SetupInfo(CTrajectory* pTrajectory); 50 | }; 51 | -------------------------------------------------------------------------------- /Outlier.cpp: -------------------------------------------------------------------------------- 1 | // Outlier.cpp : implementation file 2 | // 3 | 4 | #include "Param.h" 5 | #include "Outlier.h" 6 | 7 | 8 | // COutlier 9 | 10 | /** 11 | * \brief Constructor for 2-dimensional data, sets the outlier and trajectory IDs to -1 12 | */ 13 | COutlier::COutlier() 14 | { 15 | m_outlierId = -1; 16 | m_nDimensions = 2; 17 | m_trajectoryId = -1; 18 | m_nOutlyingPartitions = 0; 19 | m_outlyingRatio = 0.0; 20 | m_nPenWidth = 3; 21 | } 22 | 23 | /** 24 | * \brief Constructor 25 | * 26 | * @param [in] id outlier ID 27 | * @param [in] trajectoryId trajectory ID 28 | * @param [in] nDimensions the number of dimensions in the data 29 | */ 30 | COutlier::COutlier(int id, int trajectoryId, int nDimensions) 31 | { 32 | m_outlierId = id; 33 | m_nDimensions = nDimensions; 34 | m_trajectoryId = trajectoryId; 35 | m_nOutlyingPartitions = 0; 36 | m_outlyingRatio = 0.0; 37 | m_nPenWidth = 3; 38 | } 39 | 40 | COutlier::~COutlier() 41 | { 42 | m_nOutlyingPartitions = 0; 43 | } 44 | 45 | 46 | // COutlier member functions 47 | /** 48 | * \brief Pulls the relevant information from the trajectory into this outlier object 49 | * 50 | * @param [in] pTrajectory the trajectory object for the outlier 51 | */ 52 | void COutlier::SetupInfo(CTrajectory* pTrajectory) 53 | { 54 | // setup the number of outlying trajectory partitions 55 | m_nOutlyingPartitions = pTrajectory->GetNumOutlyingPartition(); 56 | 57 | // setup the points of outlying trajectory partitions 58 | for (int i = 0; i < m_nOutlyingPartitions; i++) 59 | { 60 | // get each outlying trajectory partition 61 | pair aPartition = pTrajectory->GetOutlyingPartition(i); 62 | 63 | CMDPoint startPoint, endPoint; 64 | for (int j = 0; j < m_nDimensions; j++) 65 | { 66 | startPoint.SetCoordinate(j, aPartition.first->GetCoordinate(j)); 67 | endPoint.SetCoordinate(j, aPartition.second->GetCoordinate(j)); 68 | } 69 | m_outlyingPartitionArray.push_back(LineSegment(startPoint, endPoint)); 70 | } 71 | 72 | // setup the ratio of outlying trajectory partitions 73 | m_outlyingRatio = pTrajectory->GetOutlyingLength() / pTrajectory->GetLength(); 74 | 75 | return; 76 | } 77 | -------------------------------------------------------------------------------- /Trajectory.cpp: -------------------------------------------------------------------------------- 1 | // Trajectory.cpp : implementation file 2 | // 3 | 4 | #include "Trajectory.h" 5 | #include "Param.h" 6 | 7 | // CTrajectory 8 | 9 | /** 10 | * \brief Default constructor 11 | * 12 | * Sets the trajectory ID to -1 and the number of dimensions to 2. 13 | */ 14 | CTrajectory::CTrajectory() 15 | { 16 | m_trajectoryId = -1; 17 | m_nDimensions = 2; 18 | m_nPoints = 0; 19 | m_nPartitionPoints = 0; 20 | m_totalPartitionLength = 0.0; 21 | m_outlyingPartitionLength = 0.0; 22 | m_nOutlyingPartitions = 0; 23 | m_nPenWidth = 1; // thin width 24 | m_containOutlier = false; 25 | } 26 | 27 | /** 28 | * \brief Constructor with id and dimension parameters 29 | * 30 | * @param [in] id the trajectory ID 31 | * @param [in] nDimensions the number of dimensions for the points in the trajectory (typically 2) 32 | */ 33 | CTrajectory::CTrajectory(int id, int nDimensions) 34 | { 35 | m_trajectoryId = id; 36 | m_nDimensions = nDimensions; 37 | m_nPoints = 0; 38 | m_nPartitionPoints = 0; 39 | m_totalPartitionLength = 0.0; 40 | m_outlyingPartitionLength = 0.0; 41 | m_nOutlyingPartitions = 0; 42 | m_nPenWidth = 1; // thin width 43 | m_containOutlier = false; 44 | } 45 | 46 | CTrajectory::~CTrajectory() 47 | { 48 | m_nPoints = 0; 49 | m_containOutlier = false; 50 | } 51 | 52 | 53 | // CTrajectory member functions 54 | 55 | /** 56 | * \brief Get the nth outlying partition in this trajectory 57 | * 58 | * @param [in] nth Which outlying partition of the trajectory to return 59 | * @return A pair of CMDPoint pointers which define the start and end points of the partition segment 60 | */ 61 | pair CTrajectory::GetOutlyingPartition(int nth) 62 | { 63 | int index = m_outlyingPartitionArray[nth]; 64 | pair lineSegment; 65 | 66 | #if defined(__USE_CONVENTIONAL_PARTITONING__) && !defined(__PARTITION_PRUNING_OPTIMIZATION__) 67 | lineSegment.first = &(m_partitionPointArray[index]); 68 | lineSegment.second = &(m_partitionPointArray[index + 1]); 69 | #elif defined(__USE_NO_PARTITIONING__) || defined(__PARTITION_PRUNING_OPTIMIZATION__) 70 | lineSegment.first = &(m_pointArray[index]); 71 | lineSegment.second = &(m_pointArray[index + 1]); 72 | #endif 73 | 74 | return lineSegment; 75 | } 76 | -------------------------------------------------------------------------------- /OutlierDetector.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "TrajData.h" 4 | #include "Trajectory.h" 5 | #include "Outlier.h" 6 | #include "Param.h" 7 | 8 | #include 9 | #include 10 | #include 11 | using namespace std; 12 | 13 | 14 | // COutlierDetector 15 | 16 | typedef pair IntIntPair; 17 | typedef pair PointPointPair; 18 | typedef vector FloatArray; 19 | 20 | /** 21 | * \brief A high-level class to perform the outlier detection 22 | * 23 | * Mainly does management of the outlier detection (prunining of partitions for optimization, flagging trajectories as outliers, etc.) 24 | * Much of the actual computation for outlier detection occurs in CDistanceOutlier. 25 | */ 26 | class COutlierDetector 27 | { 28 | public: 29 | COutlierDetector(); 30 | COutlierDetector(TrajData* data); 31 | virtual ~COutlierDetector(); 32 | public: 33 | TrajData* m_data; 34 | private: 35 | int m_nTotalLineSegments; 36 | vector m_idArray; 37 | vector m_lineSegmentArray; 38 | vector m_distanceIndex; // an array of the distances between partitions 39 | FloatArray m_lengthArray; 40 | vector m_partitionInfoArray; 41 | // programming trick: avoid frequent execution of the new and delete operations 42 | CMDPoint m_vector1, m_vector2; 43 | CMDPoint m_projectionPoint; 44 | float m_coefficient, m_coefficient1, m_coefficient2; 45 | #if defined(__PARTITION_PRUNING_OPTIMIZATION__) 46 | // added for partition pruning optimization 47 | PartitionInfo m_partitionInfo; 48 | float m_distance1, m_distance2, m_theta, m_cosTheta; 49 | float m_lowerBoundPerp, m_lowerBoundPara, m_lowerBoundAngle; 50 | float m_upperBoundPerp, m_upperBoundPara, m_upperBoundAngle; 51 | map m_closePartitionArrayIndexMap; 52 | vector > m_closePartitionArray; 53 | map m_finePartitionLengthMap; 54 | map m_coarsePartitionIdMap; 55 | #endif /* #if defined(__PARTITION_PRUNING_OPTIMIZATION__) */ 56 | public: 57 | bool ResetOutlierDetector(); 58 | bool PartitionTrajectory(); 59 | bool DetectOutlier(); 60 | private: 61 | bool FindOptimalPartition(CTrajectory* pTrajectory); 62 | bool StoreTrajectoryPartitionIntoIndex(); 63 | bool CheckOutlyingProportion(CTrajectory* pTrajectory, float minProportion); 64 | bool GenerateAndSetupOutlier(CTrajectory* pTrajectory, int outlierId); 65 | private: 66 | int ComputeModelCost(CTrajectory* pTrajectory, int startPIndex, int endPIndex); 67 | int ComputeEncodingCost(CTrajectory* pTrajectory, int startPIndex, int endPIndex); 68 | float MeasurePerpendicularDistance(CMDPoint* s1, CMDPoint* e1, CMDPoint* s2, CMDPoint* e2); 69 | float MeasureDistanceFromPointToLineSegment(CMDPoint* s, CMDPoint* e, CMDPoint* p); 70 | float MeasureDistanceFromPointToPoint(CMDPoint* point1, CMDPoint* point2); 71 | float ComputeVectorLength(CMDPoint* vector); 72 | float ComputeInnerProduct(CMDPoint* vector1, CMDPoint* vector2); 73 | float MeasureAngleDisntance(CMDPoint* s1, CMDPoint* e1, CMDPoint* s2, CMDPoint* e2); 74 | float ComputeDistanceBetweenTwoLineSegments(CMDPoint* startPoint1, CMDPoint* endPoint1, CMDPoint* startPoint2, CMDPoint* endPoint2); 75 | void ComputeDistanceBetweenTwoLineSegments(CMDPoint* startPoint1, CMDPoint* endPoint1, CMDPoint* startPoint2, CMDPoint* endPoint2, float* distanceVector); 76 | void SubComputeDistanceBetweenTwoLineSegments(CMDPoint* startPoint1, CMDPoint* endPoint1, CMDPoint* startPoint2, CMDPoint* endPoint2, float& perpendicularDistance, float& parallelDistance, float& angleDistance); 77 | }; 78 | -------------------------------------------------------------------------------- /ConvertData.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * ConvertData.cpp 3 | * 4 | * Converts data in the format of the WPAFB 2009 dataset to the .traj format expected by TrajData 5 | * See https://www.sdms.afrl.af.mil/index.php?collection=wpafb2009 for details about the WPAFB 2009 dataset 6 | * 7 | * Created on: Oct 31, 2014 8 | * Author: ross 9 | */ 10 | 11 | #include "csv_parser.hpp" 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | int main(void) 21 | { 22 | const string filename("/home/ross/TrackTruth/TRAIN/20091021_truth_rset0_frames0100-0611.csv"); 23 | //const string filename("/home/ross/csv-parser-cplusplus-read-only/examples/example_input.csv"); 24 | const char field_terminator = ','; 25 | const char line_terminator = '\n'; 26 | const double m_per_lon = 85417; 27 | const double m_per_lat = 111030; 28 | 29 | csv_parser file_parser; 30 | 31 | file_parser.set_skip_lines(1); 32 | file_parser.init(filename.c_str()); 33 | file_parser.set_field_term_char(field_terminator); 34 | file_parser.set_line_term_char(line_terminator); 35 | 36 | unsigned int row_count = 1U; 37 | 38 | struct point { 39 | int id; 40 | double lat; 41 | double lon; 42 | int frame; 43 | }; 44 | 45 | double min_lat = 90; 46 | double min_lon = 180; 47 | 48 | map > points; 49 | 50 | while(file_parser.has_more_rows()){ 51 | /*unsigned int i = 0; 52 | 53 | csv_row row = file_parser.get_row(); 54 | 55 | for (i = 0; i < row.size(); i++) 56 | { 57 | printf("COLUMN %02d : %s\n", i + 1U, row[i].c_str()); 58 | } 59 | 60 | printf("====================================================================\n"); 61 | printf("END OF ROW %02d\n", row_count); 62 | printf("====================================================================\n");*/ 63 | 64 | //if(points.size() > 10000) 65 | // break; 66 | 67 | csv_row row = file_parser.get_row(); 68 | 69 | //cout << row[0] << " " << row[1] << " " << row[2] << " " << row[6]; 70 | int id = atoi(row[0].c_str()); 71 | //double lat = atof(row[1].c_str()); 72 | double lat = atof(row[1].c_str()); 73 | //printf("%lf ", lat); 74 | //cout << setprecision(numeric_limits::digits10) << lat << " " << row[1].c_str() << "\n"; 75 | double lon = atof(row[2].c_str()); 76 | int frame = atoi(row[6].c_str()); 77 | 78 | if(frame > 400) 79 | continue; 80 | 81 | if( lat < min_lat ) { min_lat = lat; } 82 | if( lon < min_lon ) { min_lon = lon; } 83 | 84 | point new_point; 85 | new_point.id = id; new_point.lat = lat; new_point.lon = lon; new_point.frame = frame; 86 | 87 | points[id].push_back(new_point); 88 | 89 | 90 | //cout << "\n"; 91 | if(row_count % 10000 == 0){ 92 | cout << "Row " << row_count << "\n"; 93 | } 94 | 95 | row_count++; 96 | } 97 | 98 | // remove short paths 99 | for( auto iter = points.begin(); iter != points.end(); iter++){ 100 | if(iter->second.size() < 20){ 101 | points.erase(iter++); 102 | } 103 | } 104 | 105 | ofstream ofile; 106 | ofile.open("/home/ross/afrl_data.tra"); 107 | ofile << "2\n"; 108 | ofile << points.size() << "\n"; 109 | 110 | for( auto map_pair : points){ 111 | ofile << map_pair.first - 1 << " " << map_pair.second.size(); 112 | for( auto p : map_pair.second){ 113 | ofile << " " << (p.lon - min_lon) * m_per_lon << " " << (p.lat - min_lat) * m_per_lat; 114 | } 115 | ofile << "\n"; 116 | } 117 | ofile.close(); 118 | 119 | return 0; 120 | } 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /DistanceOutlier.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | //#include "TraOutlierDoc.h" 4 | #include "TrajData.h" 5 | #include "Trajectory.h" 6 | #include "Outlier.h" 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | using namespace std; 13 | 14 | 15 | // CDistanceOutlier 16 | 17 | typedef pair IntIntPair; 18 | typedef vector IntIntPairArray; 19 | typedef vector FloatArray; 20 | typedef vector FloatMatrix; 21 | typedef vector> IntIntPairMatrix; 22 | 23 | #define DISTANCE_MATRIX(_x,_y) ((*m_distanceIndex)[_x])[(_y)] 24 | #define LENGTH_ARRAY(_x) ((*m_lengthArray)[_x]) 25 | 26 | /** 27 | * \brief Handles partition outlier computation 28 | * 29 | * Used by OutlierDetector to handle the actual distance comparisions to find outlying partitions. The comparisions and finding of 30 | * partition outliers are done here, while a lot of the management and higher-level functionality (finding and cataloging outlying 31 | * trajectories, pruning optimization, etc.) are handled in COutlierDetector. 32 | */ 33 | class CDistanceOutlier 34 | { 35 | public: 36 | CDistanceOutlier(int nTrajectories, int nLineSegments, IntIntPairArray* idArray, FloatMatrix* distanceIndex); 37 | virtual ~CDistanceOutlier(); 38 | public: 39 | bool DetectOutlyingLineSegment(vector* result); 40 | void SetFractionParameter(float fraction) { m_fractionParam = fraction; } 41 | ///< Setter for the fraction of trajectories that must be far to be called an outlier (in the paper, "p") 42 | void SetDistanceParameter(float distance) { m_distanceParam = distance; } 43 | ///< Setter for the distance parameter D 44 | void SetLengthArray(FloatArray* lengthArray) { m_lengthArray = lengthArray; } 45 | ///< Setter for the array of partition lengths 46 | void SetCloseTrajectoryPartition(map* indexMap, IntIntPairMatrix* partitionMatrix) 47 | { m_closePartitionArrayIndexMap = indexMap; m_closePartitionArray = partitionMatrix; } 48 | ///< Setter for mappings required for partition pruning optimization 49 | void SetFinePartitionLength(map* lengthMap) { m_finePartitionLengthMap = lengthMap; } 50 | ///< Setter for lengths of fine level partitions 51 | void SetCoarsePartitionId(map* idMap) { m_coarsePartitionIdMap = idMap; } 52 | ///< Setter for mapping of coarse partition IDs 53 | void SetDensityFilePath(string filePath) { m_densityFilePath = filePath; } 54 | ///< Set file path for precomputed density file 55 | private: 56 | int GetNumOfNearTrajectories(int index); 57 | bool IsTrajectoryNear(int lineSegmentId, int trajectoryId); 58 | void ConstructRangeOfTrajectory(); 59 | void MeasureDensityOfLineSegment(); 60 | float MeasureDistanceFromPointToPoint(CMDPoint* point1, CMDPoint* point2); 61 | #if defined(__VISUALIZE_DEBUG_INFO__) 62 | void GetLineSegmentPoint(int lineSegmentId, CPoint* start, CPoint* end); 63 | void GetLineSegmentPoint(int trajectoryId, int index, CPoint* start, CPoint *end); 64 | #endif 65 | public: 66 | TrajData* m_data; 67 | private: 68 | int m_nTrajectories; 69 | int m_nLineSegments; 70 | IntIntPairArray* m_idArray; 71 | FloatMatrix* m_distanceIndex; 72 | FloatArray* m_lengthArray; // array of partition lengths 73 | float m_fractionParam; 74 | float m_distanceParam; 75 | int m_nNearTrajectories; 76 | IntIntPairArray m_trajectoryRange; 77 | FloatArray m_densityArray; 78 | string m_densityFilePath; 79 | private: 80 | map* m_closePartitionArrayIndexMap; 81 | IntIntPairMatrix* m_closePartitionArray; 82 | map* m_finePartitionLengthMap; 83 | map* m_coarsePartitionIdMap; 84 | }; 85 | -------------------------------------------------------------------------------- /Trajectory.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // CTrajectory command target 4 | 5 | #include 6 | #include "MDPoint.h" 7 | 8 | #include 9 | using namespace std; 10 | 11 | 12 | // this structure contains the information required to derive the lower and upper bounds 13 | /** 14 | * \brief Information required to derive the lower and upper bounds for optimized partition pruning 15 | */ 16 | typedef struct PartitionInfo { 17 | float maxPerpDist; 18 | float minLength; 19 | float maxLength; 20 | float maxTheta; 21 | } PartitionInfo; 22 | 23 | /** 24 | * \brief The main storage for information about a trajectory 25 | * 26 | * Represents a trajectory in the dataset, including its partitioning. 27 | */ 28 | class CTrajectory 29 | { 30 | friend class COutlierDetector; 31 | friend class CDistanceOutlier; 32 | public: 33 | CTrajectory(); 34 | CTrajectory(int id, int nDimensions); 35 | virtual ~CTrajectory(); 36 | private: 37 | int m_trajectoryId; // the identifier of this trajectory 38 | int m_nDimensions; // the dimensionality of this trajectory 39 | int m_nPoints; // the number of points constituting a trajectory 40 | vector m_pointArray; // the array of the trajectory points 41 | int m_nPartitionPoints; // the number of partition points in a trajectory 42 | vector m_partitionPointArray; // the array of the partition points 43 | vector m_partitionIndexArray; // the array of the partition indexes 44 | vector m_partitionInfoArray; // the array of the partition information 45 | float m_totalPartitionLength; // the total length of all trajectory partitions 46 | float m_outlyingPartitionLength; // the total length of all outlying partitions 47 | int m_nOutlyingPartitions; // the number of outlying trajectory partitions 48 | vector m_outlyingPartitionArray; // the array of the indexes of outlying partitions 49 | int m_nPenWidth; // the width of the pen for drawing a trajectory 50 | bool m_containOutlier; // true if this trajectory contains an outlier; false otherwise 51 | public: 52 | void SetId(int id) { m_trajectoryId = id; } ///< Set the trajectory id of this trajectory 53 | const int GetId() const { return m_trajectoryId; } ///< Return the trajectory id of this trajectory 54 | void AddPointToArray(CMDPoint point) { m_pointArray.push_back(point); m_nPoints++; } ///< Add a CMDPoint point to the array of trajectory points 55 | void AddPartitionPointToArray(CMDPoint point, int index) { 56 | m_partitionPointArray.push_back(point); 57 | m_partitionIndexArray.push_back(index); 58 | m_nPartitionPoints++; 59 | } ///< Add a point to the array of partition points 60 | void StorePartitionInfo(PartitionInfo info) { m_partitionInfoArray.push_back(info); } 61 | ///< Add the PartitionInfo object info to the array of partition information 62 | void SetLength(float length) { m_totalPartitionLength = length; } 63 | ///< Setter for total partition length 64 | const float GetLength() const { return m_totalPartitionLength; } 65 | ///< Getter for total partition length 66 | void SetOutlyingLength(float length) { m_outlyingPartitionLength = length; } 67 | ///< Setter for outlying partition length 68 | const float GetOutlyingLength() const { return m_outlyingPartitionLength; } 69 | ///< Getter for outlying partition length 70 | const int GetNumOutlyingPartition() const { return m_nOutlyingPartitions; } 71 | ///< Getter for number of outlying partitions 72 | const vector GetPointArray() const { return m_pointArray; } 73 | ///< Getter for trajectory point array 74 | const vector GetPartitionPointArray() const { return m_partitionPointArray; } 75 | ///< Getter for partition point array 76 | void AddOutlyingPartition(int index) { m_outlyingPartitionArray.push_back(index); m_nOutlyingPartitions++; } 77 | ///< Add an outlying partition to the array of outlying partitions 78 | 79 | pair GetOutlyingPartition(int nth); 80 | 81 | }; 82 | -------------------------------------------------------------------------------- /TrajData.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * TrajData.cpp 3 | * 4 | * Author: Ross H 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "TrajData.h" 16 | #include "MDPoint.h" 17 | #include "Trajectory.h" 18 | #include "OutlierDetector.h" 19 | #include "Param.h" 20 | #include "gnuplot-iostream.h" 21 | 22 | /** 23 | * \brief Default constructor. Uses the fraction parameter p and the distance parameter D specified in params.h 24 | */ 25 | TrajData::TrajData(){ 26 | m_nTrajectories = 0; 27 | m_nOutliers = 0; 28 | m_nOutlyingPartitions = 0; 29 | m_nLineSegments = 0; 30 | m_nTrajectoryPartitions = 0; 31 | 32 | m_paramFraction = g_FRACTION_PARAMETER; 33 | m_paramDistance = g_DISTANCE_PARAMETER; 34 | } 35 | 36 | TrajData::~TrajData(){ 37 | 38 | } 39 | 40 | /** 41 | * \brief Output an eps image of the trajectories and outliers. 42 | * 43 | * Makes a plot of the trajectories with gnuplot and highlights outlying trajectories and partitions. 44 | * Uses default coloring and line widths: outlier trajectories colored red, line widths 2, 45 | * and outlier partitions in a line width of 6. 46 | * 47 | * The output file is specified with filePath, which should be an .eps file. 48 | * 49 | * @param filePath [in] The desired path and filename of the eps file to output 50 | */ 51 | void TrajData::OutputTrajectoryPlot(string filePath){ 52 | OutputTrajectoryPlot(filePath, "post eps color", "red", 2, "red", 6); 53 | } 54 | 55 | /** 56 | * \brief Output a png image of the trajectories and outliers. 57 | * 58 | * Makes a plot of the trajectories with gnuplot and highlights outlying trajectories and partitions. 59 | * Uses default coloring and line widths: outlier trajectories colored red, line widths 2, 60 | * and outlier partitions in a line width of 6. 61 | * 62 | * The output file is specified with filePath, which should be an .png file. 63 | * 64 | * @param filePath [in] The desired path and filename of the png file to output 65 | */ 66 | void TrajData::OutputTrajectoryPlotPNG(string filePath){ 67 | OutputTrajectoryPlot(filePath, "pngcairo enhanced", "red", 2, "red", 6); 68 | } 69 | 70 | /** 71 | * \brief Output an eps image of the trajectories and outliers. 72 | * 73 | * Makes a plot of the trajectories with gnuplot and highlights outlying trajectories and partitions. 74 | * The output file is specified with filePath, which should be an eps file. 75 | * 76 | * @param filePath [in] The desired path and filename of the eps file to output 77 | * @param termSettings [in] the terminal settings passed to gnuplot to specify the output type and style. For example, for eps files specify "post eps", for png output specify "pngcairo", etc. Options can be specified afterward, such as "post eps color". 78 | * @param oTrajColor [in] The color for outlying trajectories 79 | * @param oTrajWidth [in] The line width for outlying trajectories 80 | * @param oPartColor [in] The color for outlying partitions 81 | * @param oPartWidth [in] The line width for outlying partitions 82 | */ 83 | void TrajData::OutputTrajectoryPlot(string filePath, string termSettings, string oTrajColor, int oTrajWidth, string oPartColor, int oPartWidth){ 84 | set outlier_trajectories; 85 | for (COutlier* outlier_p : m_outlierList) 86 | { 87 | outlier_trajectories.insert((*outlier_p).GetTrajectoryId()); 88 | } 89 | 90 | // Setup terminal 91 | Gnuplot gp; 92 | gp << "set term " << termSettings << "\n"; 93 | gp << "set output '" << filePath << "'\n"; 94 | gp << "unset key\n"; 95 | 96 | // Draw normal trajectories and outlier trajectories 97 | if(outlier_trajectories.find(m_trajectoryList[0]->GetId()) == outlier_trajectories.end()) 98 | { 99 | gp << "plot '-' using 1:2 with lines title '0' linetype -1 lw 1" ; 100 | } else { 101 | gp << ", '' using 1:2 with lines title '0' linetype 1 lc rgb '" << oTrajColor << "' lw " << oTrajWidth; 102 | } 103 | for(int i = 1; i < m_trajectoryList.size(); i++){ 104 | if(outlier_trajectories.find(m_trajectoryList[i]->GetId()) == outlier_trajectories.end()) 105 | { 106 | gp << ", '' using 1:2 with lines title '0' linetype -1 lw 1"; 107 | } else { 108 | gp << ", '' using 1:2 with lines title '0' linetype 1 lc rgb '" << oTrajColor << "' lw " << oTrajWidth; 109 | } 110 | } 111 | 112 | // Find number of outlying partitions and setup plotting for them 113 | int totalOutlyingPartitions = 0; 114 | for( COutlier* outlier : m_outlierList){ 115 | totalOutlyingPartitions += outlier->GetNOutlyingPartitions(); 116 | } 117 | for(int i = 0; i < totalOutlyingPartitions; i++){ 118 | gp << ", '' using 1:2 with lines title '0' linetype 1 lc rgb '" << oPartColor << "' lw " << oPartWidth; 119 | } 120 | gp << "\n"; 121 | 122 | // Draw outlying partitions 123 | for( CTrajectory* traj_p : m_trajectoryList){ 124 | vector > pts; 125 | CTrajectory traj = *traj_p; 126 | vector points = traj.GetPointArray(); 127 | for ( CMDPoint point : points){ 128 | pts.push_back(make_tuple(point.GetCoordinate(0), point.GetCoordinate(1))); 129 | } 130 | 131 | gp.send1d(pts); 132 | } 133 | int i = 0; 134 | for( COutlier* outlier : m_outlierList){ 135 | auto partitions = outlier->GetOutlyingPartitionArray(); 136 | for( auto partition : partitions){ 137 | vector > pts; 138 | pts.push_back(make_tuple(partition.first.GetCoordinate(0),partition.first.GetCoordinate(1))); 139 | pts.push_back(make_tuple(partition.second.GetCoordinate(0),partition.second.GetCoordinate(1))); 140 | gp.send1d(pts); 141 | i++; 142 | if(i == 2) { 143 | //break; 144 | } 145 | } 146 | } 147 | } 148 | 149 | /** 150 | * \brief Read trajectories from an input .traj file 151 | * 152 | * 153 | * @param[in] filePath The path to the input .traj file 154 | * @return True if read is successful 155 | */ 156 | bool TrajData::readFile(string filePath) 157 | { 158 | int nDimensions = 2; // default dimension = 2 159 | int nTrajectories = 0; 160 | int nTotalPoints = 0; 161 | int trajectoryId; 162 | int nPoints; 163 | float value; 164 | char buffer[1024000]; 165 | char tmp_buf1[1024], tmp_buf2[1024]; 166 | int offset = 0; 167 | 168 | ifstream istr(filePath.c_str()); 169 | 170 | 171 | if(!istr) 172 | { 173 | cout << "Could not read file " << filePath << "\n"; 174 | return false; 175 | } 176 | 177 | istr.getline(buffer, sizeof(buffer)); // the number of dimensions 178 | sscanf(buffer, "%d", &nDimensions); 179 | m_nDimensions = nDimensions; 180 | istr.getline(buffer, sizeof(buffer)); // the number of trajectories 181 | sscanf(buffer, "%d", &nTrajectories); 182 | m_nTrajectories = nTrajectories; 183 | 184 | m_maxNPoints = INT_MIN; // initialize for comparison 185 | 186 | // the trajectory Id, the number of points, the coordinate of a point ... 187 | for (int i = 0; i < nTrajectories; i++) 188 | { 189 | offset = 0; 190 | 191 | istr.getline(buffer, sizeof(buffer)); // each trajectory 192 | sscanf(buffer, "%d %d", &trajectoryId, &nPoints); 193 | sscanf(buffer, "%s %s", tmp_buf1, tmp_buf2); 194 | offset += (int)strlen(tmp_buf1) + (int)strlen(tmp_buf2) + 2; 195 | 196 | if (nPoints > m_maxNPoints) m_maxNPoints = nPoints; 197 | m_nLineSegments += (nPoints - 1); 198 | nTotalPoints += nPoints; 199 | 200 | CTrajectory* pTrajectoryItem = new CTrajectory(trajectoryId, nDimensions); 201 | m_trajectoryList.push_back(pTrajectoryItem); 202 | 203 | for (int j = 0; j < nPoints; j++) 204 | { 205 | CMDPoint point(nDimensions); // initialize the CMDPoint class for each point 206 | 207 | for (int k = 0; k < nDimensions; k++) 208 | { 209 | sscanf(buffer + offset, "%f", &value); 210 | sscanf(buffer + offset, "%s", tmp_buf1); // see how long the float is to advance the scan 211 | offset += (int)strlen(tmp_buf1) + 1; 212 | 213 | point.SetCoordinate(k, value); 214 | } 215 | 216 | pTrajectoryItem->AddPointToArray(point); 217 | } 218 | } 219 | 220 | istr.close(); 221 | 222 | return true; 223 | } 224 | -------------------------------------------------------------------------------- /DistanceOutlier.cpp: -------------------------------------------------------------------------------- 1 | // DistanceOutlier.cpp : implementation file 2 | // 3 | 4 | #include "Param.h" 5 | #include "DistanceOutlier.h" 6 | #include 7 | #include 8 | 9 | 10 | // CDistanceOutlier 11 | 12 | /** 13 | * \brief Constructor 14 | * 15 | * @param [in] nTrajectories the number of trajectories in the dataset 16 | * @param [in] nLineSegments the number of line segments (partitions) in the dataset 17 | * @param [in] idArray an array that maps each partition (partition ID as the index of the array) to the ID of the trajectory it belongs to 18 | * @param [in] distanceIndex the matrix of distances between partitions 19 | */ 20 | CDistanceOutlier::CDistanceOutlier(int nTrajectories, int nLineSegments, IntIntPairArray* idArray, FloatMatrix* distanceIndex) 21 | { 22 | m_nTrajectories = nTrajectories; 23 | m_nLineSegments = nLineSegments; 24 | m_idArray = idArray; 25 | m_distanceIndex = distanceIndex; 26 | } 27 | 28 | CDistanceOutlier::~CDistanceOutlier() 29 | { 30 | } 31 | 32 | /** 33 | * \brief Determines if any of the partitions are outlying or not 34 | * 35 | * This method is the real workhorse behind the outlier detection - it performs the actual distance comparisions to check 36 | * if a partition is an outlier or not. 37 | * 38 | * @param [out] result flags for each partition denoting whether they are outlying or not 39 | * @return Successful or not. 40 | */ 41 | bool CDistanceOutlier::DetectOutlyingLineSegment(vector* result) 42 | { 43 | m_nNearTrajectories = (int)ceil((double)m_nTrajectories * (1.0 - (double)m_fractionParam)); 44 | 45 | #ifdef __INCORPORATE_DENSITY__ 46 | MeasureDensityOfLineSegment(); 47 | #endif 48 | 49 | #ifndef __PARTITION_PRUNING_OPTIMIZATION__ 50 | ConstructRangeOfTrajectory(); 51 | 52 | for (int i = 0; i < m_nLineSegments; i++) 53 | { 54 | 55 | #ifdef __INCORPORATE_DENSITY__ 56 | if ((int)ceil(GetNumOfNearTrajectories(i) * m_densityArray[i]) <= m_nNearTrajectories) 57 | #else 58 | if (GetNumOfNearTrajectories(i) <= m_nNearTrajectories) 59 | #endif 60 | result->SetAt(i, true); 61 | else 62 | result->SetAt(i, false); 63 | } 64 | #else /* #ifndef __PARTITION_PRUNING_OPTIMIZATION__ */ 65 | int nCloseTrajectories; 66 | map closeTrajectoryMap; 67 | 68 | map::const_iterator iter; 69 | for (iter = m_closePartitionArrayIndexMap->begin(); iter != m_closePartitionArrayIndexMap->end(); iter++) 70 | { 71 | float length = (*m_finePartitionLengthMap)[iter->first]; 72 | int currIndex = iter->second; 73 | int nClosePartitions = (int)(*m_closePartitionArray)[currIndex].size(); 74 | IntIntPair closePartition; 75 | 76 | nCloseTrajectories = 0; 77 | closeTrajectoryMap.clear(); 78 | 79 | for (int i = 0; i < nClosePartitions; i++) 80 | { 81 | closePartition = (*m_closePartitionArray)[currIndex][i]; 82 | 83 | if (closeTrajectoryMap.find(closePartition.first) != closeTrajectoryMap.end()) 84 | closeTrajectoryMap[closePartition.first] += (*m_finePartitionLengthMap)[closePartition]; 85 | else 86 | closeTrajectoryMap[closePartition.first] = (*m_finePartitionLengthMap)[closePartition]; 87 | } 88 | 89 | map::const_iterator iter_tra; 90 | for (iter_tra = closeTrajectoryMap.begin(); iter_tra != closeTrajectoryMap.end(); iter_tra++) 91 | // NOTE: be careful for this design 92 | if (iter_tra->second >= length) nCloseTrajectories++; 93 | // nCloseTrajectories++; 94 | 95 | #ifdef __INCORPORATE_DENSITY__ 96 | float density = m_densityArray[(*m_coarsePartitionIdMap)[iter->first]]; 97 | if ((int)ceil(nCloseTrajectories * density) <= m_nNearTrajectories) 98 | #else 99 | if (nCloseTrajectories <= m_nNearTrajectories) 100 | #endif 101 | (*result)[currIndex] = true; 102 | else 103 | (*result)[currIndex] = false; 104 | 105 | } 106 | #endif /* #ifndef __PARTITION_PRUNING_OPTIMIZATION__ */ 107 | 108 | return true; 109 | } 110 | 111 | /** 112 | * \brief Returns the number of trajectories that are close to the line segment 113 | * 114 | * @param [in] index the ID of the line segment (partition) 115 | * @return the number of close trajectories 116 | */ 117 | int CDistanceOutlier::GetNumOfNearTrajectories(int index) 118 | { 119 | int currTrajectoryId = (*m_idArray)[index].first; 120 | int nNearTrajectories = 0; 121 | 122 | for (int trajectoryId = 0; trajectoryId < currTrajectoryId; trajectoryId++) 123 | if (IsTrajectoryNear(index, trajectoryId)) 124 | nNearTrajectories++; 125 | 126 | for (int trajectoryId = currTrajectoryId + 1; trajectoryId < m_nTrajectories; trajectoryId++) 127 | if (IsTrajectoryNear(index, trajectoryId)) 128 | nNearTrajectories++; 129 | 130 | return nNearTrajectories; 131 | } 132 | 133 | /** 134 | * \brief Returns whether a trajectory is near a line segment (partition) 135 | * 136 | * @param [in] lineSegmentId the ID of the partition 137 | * @param [in] trajectoryId the ID of the trajectory 138 | * @return true if the trajectory and partition are close, false if they are not 139 | */ 140 | bool CDistanceOutlier::IsTrajectoryNear(int lineSegmentId, int trajectoryId) 141 | { 142 | int startLineSegmentId = m_trajectoryRange[trajectoryId].first; 143 | int endLineSegmentId = m_trajectoryRange[trajectoryId].second; 144 | float totalCloseDistance = 0.0; 145 | 146 | for (int i = startLineSegmentId; i <= endLineSegmentId; i++) 147 | { 148 | if (DISTANCE_MATRIX(lineSegmentId, i) <= m_distanceParam) 149 | { 150 | totalCloseDistance += LENGTH_ARRAY(i); 151 | #ifdef __VISUALIZE_DEBUG_INFO__ 152 | CPoint startPoint, endPoint; 153 | GetLineSegmentPoint(i, &startPoint, &endPoint); 154 | m_document->m_nCloseLineSegments++; 155 | m_document->m_startPointArray.Add(startPoint); 156 | m_document->m_endPointArray.Add(endPoint); 157 | #endif 158 | } 159 | } 160 | 161 | // NOTE: be careful for this design 162 | if (totalCloseDistance >= LENGTH_ARRAY(lineSegmentId)) return true; 163 | // if (totalCloseDistance > 0.0) return true; 164 | 165 | // if no trajectory partition is close to a specific line segment 166 | return false; 167 | } 168 | 169 | void CDistanceOutlier::ConstructRangeOfTrajectory() 170 | { 171 | m_trajectoryRange.resize(m_nTrajectories); 172 | 173 | for (int i = 0; i < m_nTrajectories; i++) 174 | m_trajectoryRange[i].first = m_trajectoryRange[i].second = 0; 175 | 176 | for (int i = 0; i < m_nLineSegments; i++) 177 | { 178 | int trajectoryId = (*m_idArray)[i].first; 179 | // the first appearance of a specific trajectory 180 | if (m_trajectoryRange[trajectoryId].first == 0) 181 | m_trajectoryRange[trajectoryId].first = m_trajectoryRange[trajectoryId].second = i; 182 | // subsequent appearances of the trajectory 183 | else 184 | m_trajectoryRange[trajectoryId].second = i; 185 | } 186 | } 187 | 188 | #ifdef __INCORPORATE_DENSITY__ 189 | void CDistanceOutlier::MeasureDensityOfLineSegment() 190 | { 191 | #ifdef __PRECOMPUTE_DENSITY__ 192 | ifstream istr(m_densityFilePath); 193 | if (istr) 194 | { 195 | int nLineSegments, trajectoryId, offset; 196 | istr >> nLineSegments; 197 | m_densityArray.resize(nLineSegments); 198 | for (int i = 0; i < nLineSegments; i++) 199 | { 200 | istr >> trajectoryId >> offset >> m_densityArray[i]; 201 | #ifdef __PARTITION_PRUNING_OPTIMIZATION__ 202 | (*m_coarsePartitionIdMap)[IntIntPair(trajectoryId,offset)] = i; 203 | #endif 204 | } 205 | istr.close(); 206 | return; 207 | } 208 | #endif 209 | 210 | // calculate the standard deviation of the distances between line segments 211 | // START ... 212 | double avgDistance = 0.0; 213 | double avgSquaredError = 0.0; 214 | double stDev; 215 | 216 | for (int i = 0; i < m_nLineSegments; i++) 217 | for (int j = i + 1; j < m_nLineSegments; j++) 218 | avgDistance += (2 * DISTANCE_MATRIX(i, j) / (float)(m_nLineSegments * (m_nLineSegments - 1))); 219 | 220 | for (int i = 0; i < m_nLineSegments; i++) 221 | for (int j = i + 1; j < m_nLineSegments; j++) 222 | avgSquaredError += (2 * pow((DISTANCE_MATRIX(i, j) - avgDistance), 2) / (float)(m_nLineSegments * (m_nLineSegments - 1))); 223 | 224 | stDev = sqrt(avgSquaredError); 225 | // ... END 226 | 227 | // calculate the density of each line segment 228 | // START ... 229 | set trajectoryIds; 230 | int lineSegmentCount; 231 | int totalDensity = 0; 232 | float avgDensity; 233 | 234 | for (int i = 0; i < m_nLineSegments; i++) 235 | { 236 | trajectoryIds.clear(); 237 | lineSegmentCount = 0; 238 | 239 | #ifdef __NEVER_DEFINE__ 240 | // the density is defined as the number of distinct trajectories within a given radius 241 | // here, the radius is the standard deviation 242 | for (int j = 0; j < i; j++) 243 | if (DISTANCE_MATRIX(i, j) <= stDev) trajectoryIds.insert(m_idArray->GetAt(j).first); 244 | for (int j = i + 1; j < m_nLineSegments; j++) 245 | if (DISTANCE_MATRIX(i, j) <= stDev) trajectoryIds.insert(m_idArray->GetAt(j).first); 246 | 247 | totalDensity += (int)trajectoryIds.size(); 248 | m_densityArray.Add((float)trajectoryIds.size()); 249 | #endif 250 | 251 | // if the density is computed using the number of line segments, enable this block 252 | for (int j = 0; j < i; j++) 253 | if (DISTANCE_MATRIX(i, j) <= stDev) lineSegmentCount += 1; 254 | for (int j = i + 1; j < m_nLineSegments; j++) 255 | if (DISTANCE_MATRIX(i, j) <= stDev) lineSegmentCount += 1; 256 | 257 | totalDensity += lineSegmentCount; 258 | m_densityArray.push_back((float)lineSegmentCount); 259 | } 260 | 261 | avgDensity = (float)totalDensity / (float)m_nLineSegments; 262 | 263 | // keep the ratio of the average density to the density of a specific line segment 264 | for (int i = 0; i < m_nLineSegments; i++) 265 | m_densityArray[i] = avgDensity / m_densityArray[i]; 266 | // ... END 267 | 268 | #if 0 // enable this block to create a density file 269 | ofstream ofs(m_densityFilePath); 270 | if (ofs) 271 | { 272 | ofs << m_nLineSegments << endl; 273 | 274 | int currLineSegmentId = 0; 275 | CMDPoint *startPoint, *endPoint; 276 | CTypedPtrList& trajectoryList = m_document->m_trajectoryList; 277 | POSITION pos = trajectoryList.GetHeadPosition(); 278 | while (pos != NULL) 279 | { 280 | CTrajectory* pTrajectory = trajectoryList.GetNext(pos); 281 | 282 | for (int i = 0; i < pTrajectory->m_nPoints - 1; i++) 283 | { 284 | startPoint = &(pTrajectory->m_pointArray[i]); 285 | endPoint = &(pTrajectory->m_pointArray[i + 1]); 286 | 287 | if (MeasureDistanceFromPointToPoint(startPoint, endPoint) < MIN_LINESEGMENT_LENGTH) 288 | continue; 289 | 290 | ofs << pTrajectory->GetId() << ' ' << i << ' ' << m_densityArray[currLineSegmentId] << endl; 291 | currLineSegmentId++; 292 | } 293 | } 294 | 295 | ASSERT(m_nLineSegments == currLineSegmentId); 296 | ofs.close(); 297 | } 298 | #endif 299 | } 300 | #endif 301 | 302 | // this function is borrowed from the COutlierDetector class 303 | float CDistanceOutlier::MeasureDistanceFromPointToPoint(CMDPoint* point1, CMDPoint* point2) 304 | { 305 | int nDimensions = point1->GetNDimensions(); 306 | float squareSum = 0.0; 307 | 308 | for (int i = 0; i < nDimensions; i++) 309 | squareSum += pow((point2->GetCoordinate(i) - point1->GetCoordinate(i)), 2); 310 | 311 | return sqrt(squareSum); 312 | } 313 | 314 | #ifdef __VISUALIZE_DEBUG_INFO__ 315 | void CDistanceOutlier::GetLineSegmentPoint(int lineSegmentId, CPoint* start, CPoint *end) 316 | { 317 | int trajectoryId = (m_idArray->GetAt(lineSegmentId)).first; 318 | int index = (m_idArray->GetAt(lineSegmentId)).second; 319 | 320 | CTrajectory* pTrajectory = m_document->m_trajectoryList.GetAt(m_document->m_trajectoryList.FindIndex(trajectoryId)); 321 | 322 | CMDPoint *startPoint, *endPoint; 323 | #if defined(__USE_CONVENTIONAL_PARTITONING__) && !defined(__PARTITION_PRUNING_OPTIMIZATION__) 324 | startPoint = &(pTrajectory->m_partitionPointArray[index]); 325 | endPoint = &(pTrajectory->m_partitionPointArray[index + 1]); 326 | #elif defined(__USE_NO_PARTITIONING__) || defined(__PARTITION_PRUNING_OPTIMIZATION__) 327 | startPoint = &(pTrajectory->m_pointArray[index]); 328 | endPoint = &(pTrajectory->m_pointArray[index + 1]); 329 | #endif 330 | 331 | start->SetPoint((int)startPoint->GetCoordinate(0), (int)startPoint->GetCoordinate(1)); 332 | end->SetPoint((int)endPoint->GetCoordinate(0), (int)endPoint->GetCoordinate(1)); 333 | } 334 | 335 | void CDistanceOutlier::GetLineSegmentPoint(int trajectoryId, int index, CPoint* start, CPoint *end) 336 | { 337 | CTrajectory* pTrajectory = m_document->m_trajectoryList.GetAt(m_document->m_trajectoryList.FindIndex(trajectoryId)); 338 | 339 | CMDPoint *startPoint, *endPoint; 340 | #if defined(__USE_CONVENTIONAL_PARTITONING__) && !defined(__PARTITION_PRUNING_OPTIMIZATION__) 341 | startPoint = &(pTrajectory->m_partitionPointArray[index]); 342 | endPoint = &(pTrajectory->m_partitionPointArray[index + 1]); 343 | #elif defined(__USE_NO_PARTITIONING__) || defined(__PARTITION_PRUNING_OPTIMIZATION__) 344 | startPoint = &(pTrajectory->m_pointArray[index]); 345 | endPoint = &(pTrajectory->m_pointArray[index + 1]); 346 | #endif 347 | 348 | start->SetPoint((int)startPoint->GetCoordinate(0), (int)startPoint->GetCoordinate(1)); 349 | end->SetPoint((int)endPoint->GetCoordinate(0), (int)endPoint->GetCoordinate(1)); 350 | } 351 | #endif 352 | -------------------------------------------------------------------------------- /Measure.cpp: -------------------------------------------------------------------------------- 1 | // Measure.cpp : implementation file 2 | // 3 | 4 | #include "Param.h" 5 | #include "OutlierDetector.h" 6 | #include 7 | 8 | // COutlierDetector 9 | 10 | #define LOG2(_x) (float)(log((float)(_x)) / log((float)2)) 11 | 12 | /** 13 | * \brief Measures the model cost of using a partition to represent part of a trajectory 14 | * 15 | * In the MDL description of the paper this is known as L(H). It is the length of the partition. 16 | * @param [in] pTrajectory the trajectory that is being partitioned 17 | * @param [in] startPIndex the index of the staring point of the partition 18 | * @param [in] endPIndex the index of the ending point of the partition 19 | */ 20 | int COutlierDetector::ComputeModelCost(CTrajectory* pTrajectory, int startPIndex, int endPIndex) 21 | { 22 | CMDPoint* partitionStart = &(pTrajectory->m_pointArray[startPIndex]); 23 | CMDPoint* partitionEnd = &(pTrajectory->m_pointArray[endPIndex]); 24 | 25 | float distance = MeasureDistanceFromPointToPoint(partitionStart, partitionEnd); 26 | if (distance < 1.0) distance = 1.0; // to take logarithm 27 | 28 | return (int)ceil(LOG2(distance)); 29 | } 30 | 31 | /** 32 | * \brief Measures the encoding cost of using a partition to represent part of a trajectory 33 | * 34 | * In the MDL description of the paper this is known as L(D|H). It is the distance between the original trajectory 35 | * and the partition representation. 36 | * @param [in] pTrajectory the trajectory that is being partitioned 37 | * @param [in] startPIndex the index of the staring point of the partition 38 | * @param [in] endPIndex the index of the ending point of the partition 39 | */ 40 | int COutlierDetector::ComputeEncodingCost(CTrajectory* pTrajectory, int startPIndex, int endPIndex) 41 | { 42 | CMDPoint* partitionStart; 43 | CMDPoint* partitionEnd; 44 | CMDPoint* lineSegmentStart; 45 | CMDPoint* lineSegmentEnd; 46 | float perpendicularDistance; 47 | float angleDistance; 48 | int encodingCost = 0; 49 | 50 | partitionStart = &(pTrajectory->m_pointArray[startPIndex]); 51 | partitionEnd = &(pTrajectory->m_pointArray[endPIndex]); 52 | 53 | for (int i = startPIndex; i < endPIndex; i++) 54 | { 55 | lineSegmentStart = &(pTrajectory->m_pointArray[i]); 56 | lineSegmentEnd = &(pTrajectory->m_pointArray[i + 1]); 57 | 58 | perpendicularDistance = MeasurePerpendicularDistance(partitionStart, partitionEnd, lineSegmentStart, lineSegmentEnd); 59 | angleDistance = MeasureAngleDisntance(partitionStart, partitionEnd, lineSegmentStart, lineSegmentEnd); 60 | 61 | if (perpendicularDistance < 1.0) perpendicularDistance = 1.0; // to take logarithm 62 | if (angleDistance < 1.0) angleDistance = 1.0; // to take logarithm 63 | encodingCost += ((int)ceil(LOG2(perpendicularDistance)) + (int)ceil(LOG2(angleDistance))); 64 | } 65 | 66 | return encodingCost; 67 | } 68 | 69 | /** 70 | * \brief Measures the perpendicular distance between two line segments (see paper for definition of perpendicular distance) 71 | */ 72 | float COutlierDetector::MeasurePerpendicularDistance(CMDPoint* s1, CMDPoint* e1, CMDPoint* s2, CMDPoint* e2) 73 | { 74 | // we assume that the first line segment is longer than the second one 75 | float distance1; // the distance from a start point to the trajectory partition 76 | float distance2; // the distance from an end point to the trajectory partition 77 | 78 | distance1 = MeasureDistanceFromPointToLineSegment(s1, e1, s2); 79 | distance2 = MeasureDistanceFromPointToLineSegment(s1, e1, e2); 80 | 81 | // if the first line segment is exactly the same as the second one, the perpendicular distance should be zero 82 | if (distance1 == 0.0 && distance2 == 0.0) return 0.0; 83 | 84 | #if defined(__PARTITION_PRUNING_OPTIMIZATION__) 85 | if (m_partitionInfo.maxPerpDist < distance1) m_partitionInfo.maxPerpDist = distance1; 86 | if (m_partitionInfo.maxPerpDist < distance2) m_partitionInfo.maxPerpDist = distance2; 87 | #endif 88 | 89 | // return (d1^2 + d2^2) / (d1 + d2) as the perpendicular distance 90 | return ((pow(distance1, 2) + pow(distance2, 2)) / (distance1 + distance2)); 91 | } 92 | 93 | /** 94 | * \brief Measures the distance between a point and a line segment 95 | */ 96 | float COutlierDetector::MeasureDistanceFromPointToLineSegment(CMDPoint* s, CMDPoint* e, CMDPoint* p) 97 | { 98 | int nDimensions = p->GetNDimensions(); 99 | 100 | // NOTE: the variables m_vector1 and m_vector2 are declared as member variables 101 | 102 | // construct two vectors as follows 103 | // 1. the vector connecting the start point of the trajectory partition and a given point 104 | // 2. the vector representing the trajectory partition 105 | for (int i = 0; i < nDimensions; i++) 106 | { 107 | m_vector1.SetCoordinate(i, p->GetCoordinate(i) - s->GetCoordinate(i)); 108 | m_vector2.SetCoordinate(i, e->GetCoordinate(i) - s->GetCoordinate(i)); 109 | } 110 | 111 | // a coefficient (0 <= b <= 1) 112 | // NOTE: the variable m_coefficient is declared as a member variable 113 | m_coefficient = ComputeInnerProduct(&m_vector1, &m_vector2) / ComputeInnerProduct(&m_vector2, &m_vector2); 114 | 115 | // the projection on the trajectory partition from a given point 116 | // NOTE: the variable m_projectionPoint is declared as a member variable 117 | for (int i = 0; i < nDimensions; i++) 118 | m_projectionPoint.SetCoordinate(i, s->GetCoordinate(i) + m_coefficient * m_vector2.GetCoordinate(i)); 119 | 120 | // return the distance between the projection point and the given point 121 | return MeasureDistanceFromPointToPoint(p, &m_projectionPoint); 122 | } 123 | 124 | /** 125 | * \brief Measures the distance between two points 126 | */ 127 | float COutlierDetector::MeasureDistanceFromPointToPoint(CMDPoint* point1, CMDPoint* point2) 128 | { 129 | int nDimensions = point1->GetNDimensions(); 130 | float squareSum = 0.0; 131 | 132 | for (int i = 0; i < nDimensions; i++) 133 | squareSum += pow((point2->GetCoordinate(i) - point1->GetCoordinate(i)), 2); 134 | 135 | return sqrt(squareSum); 136 | } 137 | 138 | /** 139 | * \brief Computes the length of a vector 140 | */ 141 | float COutlierDetector::ComputeVectorLength(CMDPoint* vector) 142 | { 143 | int nDimensions = vector->GetNDimensions(); 144 | float squareSum = 0.0; 145 | 146 | for (int i = 0; i < nDimensions; i++) 147 | squareSum += pow(vector->GetCoordinate(i), 2); 148 | 149 | return sqrt(squareSum); 150 | } 151 | 152 | /** 153 | * \brief Computes the inner product between two vectors 154 | */ 155 | float COutlierDetector::ComputeInnerProduct(CMDPoint* vector1, CMDPoint* vector2) 156 | { 157 | int nDimensions = vector1->GetNDimensions(); 158 | float innerProduct = 0.0; 159 | 160 | for (int i = 0; i < nDimensions; i++) 161 | innerProduct += (vector1->GetCoordinate(i) * vector2->GetCoordinate(i)); 162 | 163 | return innerProduct; 164 | } 165 | 166 | /** 167 | * \brief Measures the angle distance between two line segments (see paper for definition of angle distance) 168 | */ 169 | float COutlierDetector::MeasureAngleDisntance(CMDPoint* s1, CMDPoint* e1, CMDPoint* s2, CMDPoint* e2) 170 | { 171 | int nDimensions = s1->GetNDimensions(); 172 | 173 | // NOTE: the variables m_vector1 and m_vector2 are declared as member variables 174 | 175 | // construct two vectors representing the trajectory partition and a line segment, respectively 176 | for (int i = 0; i < nDimensions; i++) 177 | { 178 | m_vector1.SetCoordinate(i, e1->GetCoordinate(i) - s1->GetCoordinate(i)); 179 | m_vector2.SetCoordinate(i, e2->GetCoordinate(i) - s2->GetCoordinate(i)); 180 | } 181 | 182 | // we assume that the first line segment is longer than the second one 183 | // i.e., vectorLength1 >= vectorLength2 184 | float vectorLength1 = ComputeVectorLength(&m_vector1); 185 | float vectorLength2 = ComputeVectorLength(&m_vector2); 186 | 187 | // if one of two vectors is a point, the angle distance becomes zero 188 | if (vectorLength1 == 0.0 || vectorLength2 == 0.0) return 0.0; 189 | 190 | #if defined(__PARTITION_PRUNING_OPTIMIZATION__) 191 | if (m_partitionInfo.minLength > vectorLength2) m_partitionInfo.minLength = vectorLength2; 192 | if (m_partitionInfo.maxLength < vectorLength2) m_partitionInfo.maxLength = vectorLength2; 193 | #endif 194 | 195 | // compute the inner product of the two vectors 196 | float innerProduct = ComputeInnerProduct(&m_vector1, &m_vector2); 197 | 198 | // compute the angle between two vectors by using the inner product 199 | float cosTheta = innerProduct / (vectorLength1 * vectorLength2); 200 | // compensate the computation error (e.g., 1.00001) 201 | // cos(theta) should be in the range [-1.0, 1.0] 202 | // START ... 203 | if (cosTheta > 1.0) cosTheta = 1.0; 204 | if (cosTheta < -1.0) cosTheta = -1.0; 205 | // ... END 206 | float sinTheta = sqrt(1 - pow(cosTheta, 2)); 207 | // if 90 <= theta <= 270, the angle distance becomes the length of the line segment 208 | // if (cosTheta < -1.0) sinTheta = 1.0; 209 | 210 | #if defined(__PARTITION_PRUNING_OPTIMIZATION__) 211 | if (m_partitionInfo.maxTheta < asin(sinTheta)) m_partitionInfo.maxTheta = asin(sinTheta); 212 | m_cosTheta = fabs(cosTheta); 213 | m_theta = asin(sinTheta); 214 | #endif 215 | 216 | return (vectorLength2 * sinTheta); 217 | } 218 | 219 | /** 220 | * \brief Computes the weighted distance between two line segments from component distances (see paper for definition of distance) 221 | */ 222 | float COutlierDetector::ComputeDistanceBetweenTwoLineSegments(CMDPoint* startPoint1, CMDPoint* endPoint1, CMDPoint* startPoint2, CMDPoint* endPoint2) 223 | { 224 | float perpendicularDistance, parallelDistance, angleDistance; 225 | 226 | SubComputeDistanceBetweenTwoLineSegments(startPoint1, endPoint1, startPoint2, endPoint2, perpendicularDistance, parallelDistance, angleDistance); 227 | 228 | // all the weights are equally set to 1 in default 229 | return WEIGHTED_DISTANCE(perpendicularDistance, parallelDistance, angleDistance); 230 | } 231 | 232 | /** 233 | * \brief Measures the perpendicular distance between two line segments (see paper for definition of perpendicular distance) and stores the result in distanceVector 234 | */ 235 | void COutlierDetector::ComputeDistanceBetweenTwoLineSegments(CMDPoint* startPoint1, CMDPoint* endPoint1, CMDPoint* startPoint2, CMDPoint* endPoint2, float* distanceVector) 236 | { 237 | float perpendicularDistance, parallelDistance, angleDistance; 238 | 239 | SubComputeDistanceBetweenTwoLineSegments(startPoint1, endPoint1, startPoint2, endPoint2, perpendicularDistance, parallelDistance, angleDistance); 240 | 241 | distanceVector[0] = perpendicularDistance; 242 | distanceVector[1] = parallelDistance; 243 | distanceVector[2] = angleDistance; 244 | } 245 | 246 | /** 247 | * \brief Measures component distances and stores them in the variables perpendicularDistance, parallelDistance, and angleDistance 248 | */ 249 | void COutlierDetector::SubComputeDistanceBetweenTwoLineSegments(CMDPoint* startPoint1, CMDPoint* endPoint1, CMDPoint* startPoint2, CMDPoint* endPoint2, float& perpendicularDistance, float& parallelDistance, float& angleDistance) 250 | { 251 | float perDistance1, perDistance2; 252 | float parDistance1, parDistance2; 253 | float length1, length2; 254 | 255 | // the length of the first line segment 256 | length1 = MeasureDistanceFromPointToPoint(startPoint1, endPoint1); 257 | // the length of the second line segment 258 | length2 = MeasureDistanceFromPointToPoint(startPoint2, endPoint2); 259 | 260 | // 05MAY2007: ignore a sort of detection errors---when a starting point coincides with an ending point 261 | if (length1 == 0 || length2 == 0) 262 | { 263 | perpendicularDistance = parallelDistance = angleDistance = 0.0; 264 | return; 265 | } 266 | 267 | // compute the perpendicular distance and the parallel distance 268 | // START ... 269 | if (length1 > length2) 270 | { 271 | perDistance1 = MeasureDistanceFromPointToLineSegment(startPoint1, endPoint1, startPoint2); 272 | if (m_coefficient < 0.5) parDistance1 = MeasureDistanceFromPointToPoint(startPoint1, &m_projectionPoint); 273 | else parDistance1 = MeasureDistanceFromPointToPoint(endPoint1, &m_projectionPoint); 274 | m_coefficient1 = m_coefficient; // for optimization 275 | 276 | perDistance2 = MeasureDistanceFromPointToLineSegment(startPoint1, endPoint1, endPoint2); 277 | if (m_coefficient < 0.5) parDistance2 = MeasureDistanceFromPointToPoint(startPoint1, &m_projectionPoint); 278 | else parDistance2 = MeasureDistanceFromPointToPoint(endPoint1, &m_projectionPoint); 279 | m_coefficient2 = m_coefficient; // for optimization 280 | } 281 | else 282 | { 283 | perDistance1 = MeasureDistanceFromPointToLineSegment(startPoint2, endPoint2, startPoint1); 284 | if (m_coefficient < 0.5) parDistance1 = MeasureDistanceFromPointToPoint(startPoint2, &m_projectionPoint); 285 | else parDistance1 = MeasureDistanceFromPointToPoint(endPoint2, &m_projectionPoint); 286 | m_coefficient1 = m_coefficient; // for optimization 287 | 288 | perDistance2 = MeasureDistanceFromPointToLineSegment(startPoint2, endPoint2, endPoint1); 289 | if (m_coefficient < 0.5) parDistance2 = MeasureDistanceFromPointToPoint(startPoint2, &m_projectionPoint); 290 | else parDistance2 = MeasureDistanceFromPointToPoint(endPoint2, &m_projectionPoint); 291 | m_coefficient2 = m_coefficient; // for optimization 292 | } 293 | 294 | #if defined(__PARTITION_PRUNING_OPTIMIZATION__) 295 | m_distance1 = perDistance1; 296 | m_distance2 = perDistance2; 297 | #endif 298 | 299 | // compute the perpendicular distance; take (d1^2 + d2^2) / (d1 + d2) 300 | if (!(perDistance1 == 0.0 && perDistance2 == 0.0)) 301 | perpendicularDistance = ((pow(perDistance1, 2) + pow(perDistance2, 2)) / (perDistance1 + perDistance2)); 302 | else 303 | perpendicularDistance = 0.0; 304 | 305 | // compute the parallel distance; take the minimum 306 | parallelDistance = (parDistance1 < parDistance2) ? parDistance1 : parDistance2; 307 | // ... END 308 | 309 | // compute the angle distance 310 | // START ... 311 | // MeasureAngleDisntance() assumes that the first line segment is longer than the second one 312 | if (length1 > length2) 313 | angleDistance = MeasureAngleDisntance(startPoint1, endPoint1, startPoint2, endPoint2); 314 | else 315 | angleDistance = MeasureAngleDisntance(startPoint2, endPoint2, startPoint1, endPoint1); 316 | // ... END 317 | 318 | return; 319 | } 320 | -------------------------------------------------------------------------------- /csv_parser.cpp: -------------------------------------------------------------------------------- 1 | 2 | /* INCLUDING HEADER FILES */ 3 | #include "csv_parser.hpp" 4 | 5 | 6 | /* BEGIN DEFINITION FOR PUBLIC METHODS */ 7 | bool csv_parser::init(FILE * input_file_pointer) 8 | { 9 | input_fp = input_file_pointer; 10 | 11 | if (input_fp == NULL) 12 | { 13 | fprintf(stderr, "Fatal error : unable to open input file from file pointer\n"); 14 | 15 | return false; 16 | } 17 | 18 | /* Resetting the internal pointer to the beginning of the stream */ 19 | rewind(input_fp); 20 | 21 | more_rows = true; 22 | 23 | _skip_lines(); 24 | 25 | return true; 26 | } 27 | 28 | bool csv_parser::init(const char * input_file) 29 | { 30 | const size_t filename_length = strlen(input_file); 31 | 32 | if (!filename_length) 33 | { 34 | fprintf(stderr, "Fatal error : invalid input file %s\n", input_file); 35 | 36 | return false; 37 | } 38 | 39 | input_filename = (char *) malloc(filename_length + 1); 40 | 41 | if (input_filename == NULL) 42 | { 43 | fprintf(stderr, "Fatal error : unable to allocate memory for file name buffer %s\n", input_file); 44 | 45 | return false; 46 | } 47 | 48 | memset(input_filename, 0, filename_length + 1); 49 | 50 | strcpy(input_filename, input_file); 51 | 52 | input_fp = fopen(input_file, "r"); 53 | 54 | if (input_fp == NULL) 55 | { 56 | fprintf(stderr, "Fatal error : unable to open input file %s\n", input_file); 57 | 58 | CSV_PARSER_FREE_BUFFER_PTR(input_filename); 59 | 60 | return false; 61 | } 62 | 63 | more_rows = true; 64 | 65 | _skip_lines(); 66 | 67 | return true; 68 | } 69 | 70 | void csv_parser::set_enclosed_char(char fields_enclosed_by, enclosure_type_t enclosure_mode) 71 | { 72 | if (fields_enclosed_by != 0) 73 | { 74 | enclosed_char = fields_enclosed_by; 75 | enclosed_length = 1U; 76 | enclosure_type = enclosure_mode; 77 | } 78 | } 79 | 80 | void csv_parser::set_field_term_char(char fields_terminated_by) 81 | { 82 | if (fields_terminated_by != 0) 83 | { 84 | field_term_char = fields_terminated_by; 85 | field_term_length = 1U; 86 | } 87 | } 88 | 89 | void csv_parser::set_line_term_char(char lines_terminated_by) 90 | { 91 | if (lines_terminated_by != 0) 92 | { 93 | line_term_char = lines_terminated_by; 94 | line_term_length = 1U; 95 | } 96 | } 97 | 98 | csv_row csv_parser::get_row(void) 99 | { 100 | csv_row current_row; 101 | 102 | /* This will store the length of the buffer */ 103 | unsigned int line_length = 0U; 104 | 105 | /* Character array buffer for the current record */ 106 | char * line = NULL; 107 | 108 | /* Grab one record */ 109 | _read_single_line(&line, &line_length); 110 | 111 | /* Select the most suitable field extractor based on the enclosure length */ 112 | switch(enclosure_type) 113 | { 114 | case ENCLOSURE_NONE : /* The fields are not enclosed by any character */ 115 | _get_fields_without_enclosure(¤t_row, line, &line_length); 116 | break; 117 | 118 | case ENCLOSURE_REQUIRED : /* The fields are enclosed by a character */ 119 | _get_fields_with_enclosure(¤t_row, line, &line_length); 120 | break; 121 | 122 | case ENCLOSURE_OPTIONAL : /* The fields may or may not be enclosed */ 123 | _get_fields_with_optional_enclosure(¤t_row, line, &line_length); 124 | break; 125 | 126 | default : 127 | _get_fields_with_optional_enclosure(¤t_row, line, &line_length); 128 | break; 129 | } 130 | 131 | /* Deallocate the current buffer */ 132 | CSV_PARSER_FREE_BUFFER_PTR(line); 133 | 134 | /* Keeps track of how many times this has method has been called */ 135 | record_count++; 136 | 137 | return current_row; 138 | } 139 | 140 | /* BEGIN DEFINITION FOR PROTECTED METHODS */ 141 | 142 | 143 | /* BEGIN DEFINITION FOR PRIVATE METHODS */ 144 | 145 | void csv_parser::_skip_lines(void) 146 | { 147 | /* Just in case the user accidentally sets ignore_num_lines to a negative number */ 148 | unsigned int number_of_lines_to_ignore = abs((int) ignore_num_lines); 149 | 150 | while(has_more_rows() && number_of_lines_to_ignore) 151 | { 152 | const csv_row row = get_row(); 153 | 154 | number_of_lines_to_ignore--; 155 | } 156 | 157 | record_count = 0U; 158 | } 159 | 160 | void csv_parser::_get_fields_without_enclosure(csv_row_ptr row, const char * line, const unsigned int * line_length) 161 | { 162 | char * field = NULL; 163 | 164 | if (*line_length > 0) 165 | { 166 | field = (char *) malloc(*line_length); 167 | 168 | memset(field, 0, *line_length); 169 | 170 | register unsigned int field_start = 0U; 171 | register unsigned int field_end = 0U; 172 | register unsigned int char_pos = 0U; 173 | 174 | while(char_pos < *line_length) 175 | { 176 | char curr_char = line[char_pos]; 177 | 178 | if (curr_char == field_term_char) 179 | { 180 | field_end = char_pos; 181 | 182 | const char * field_starts_at = line + field_start; 183 | 184 | /* Field width must exclude field delimiter characters */ 185 | const unsigned int field_width = field_end - field_start; 186 | 187 | /* Copy exactly field_width bytes from field_starts_at to field */ 188 | memcpy(field, field_starts_at, field_width); 189 | 190 | /* This must be a null-terminated character array */ 191 | field[field_width] = 0x00; 192 | 193 | string field_string_obj = field; 194 | 195 | row->push_back(field_string_obj); 196 | 197 | /* This is the starting point of the next field */ 198 | field_start = char_pos + 1; 199 | 200 | } else if (curr_char == line_term_char) 201 | { 202 | field_end = char_pos; 203 | 204 | const char * field_starts_at = line + field_start; 205 | 206 | /* Field width must exclude line terminating characters */ 207 | const unsigned int field_width = field_end - field_start; 208 | 209 | /* Copy exactly field_width bytes from field_starts_at to field */ 210 | memcpy(field, field_starts_at, field_width); 211 | 212 | /* This must be a null-terminated character array */ 213 | field[field_width] = 0x00; 214 | 215 | string field_string_obj = field; 216 | 217 | row->push_back(field_string_obj); 218 | } 219 | 220 | /* Move to the next character in the current line */ 221 | char_pos++; 222 | } 223 | 224 | /* Deallocate memory for field buffer */ 225 | CSV_PARSER_FREE_BUFFER_PTR(field); 226 | } 227 | } 228 | 229 | void csv_parser::_get_fields_with_enclosure(csv_row_ptr row, const char * line, const unsigned int * line_length) 230 | { 231 | char * field = NULL; 232 | 233 | if (*line_length > 0) 234 | { 235 | field = (char *) malloc(*line_length); 236 | 237 | memset(field, 0, *line_length); 238 | 239 | register unsigned int current_state = 0U; 240 | register unsigned int field_start = 0U; 241 | register unsigned int field_end = 0U; 242 | register unsigned int char_pos = 0U; 243 | 244 | while(char_pos < *line_length) 245 | { 246 | char curr_char = line[char_pos]; 247 | 248 | if (curr_char == enclosed_char) 249 | { 250 | current_state++; 251 | 252 | /* Lets find out if the enclosure character encountered is 253 | * a 'real' enclosure character or if it is an embedded character that 254 | * has been escaped within the field. 255 | */ 256 | register char previous_char = 0x00; 257 | 258 | if (char_pos > 0U) 259 | { 260 | /* The escaped char will have to be the 2rd or later character. */ 261 | previous_char = line[char_pos - 1]; 262 | 263 | if (previous_char == escaped_char) 264 | { 265 | --current_state; 266 | } 267 | } 268 | 269 | if (current_state == 1U && previous_char != escaped_char) 270 | { 271 | /* This marks the beginning of the column */ 272 | field_start = char_pos; 273 | 274 | } else if (current_state == 2U) 275 | { 276 | /* We have found the end of the current field */ 277 | field_end = char_pos; 278 | 279 | /* We do not need the enclosure characters */ 280 | const char * field_starts_at = line + field_start + 1U; 281 | 282 | /* Field width must exclude beginning and ending enclosure characters */ 283 | const unsigned int field_width = field_end - field_start - 1U; 284 | 285 | /* Copy exactly field_width bytes from field_starts_at to field */ 286 | memcpy(field, field_starts_at, field_width); 287 | 288 | /* This must be a null-terminated character array */ 289 | field[field_width] = 0x00; 290 | 291 | string field_string_obj = field; 292 | 293 | row->push_back(field_string_obj); 294 | 295 | /* Reset the state to zero value for the next field */ 296 | current_state = 0U; 297 | } 298 | } 299 | 300 | /* Move to the next character in the current line */ 301 | char_pos++; 302 | } 303 | 304 | /* If no enclosures were found in this line, the entire line becomes the only field. */ 305 | if (0 == row->size()) 306 | { 307 | string entire_line = line; 308 | 309 | row->push_back(entire_line); 310 | 311 | } else if (current_state == 1U) 312 | { 313 | /* The beginning enclosure character was found but 314 | * we could not locate the closing enclosure in the current line 315 | * So we need to copy the remainder of the line into the last field. 316 | */ 317 | 318 | /* We do not need the starting enclosure character */ 319 | const char * field_starts_at = line + field_start + 1U; 320 | 321 | /* Field width must exclude beginning characters */ 322 | const unsigned int field_width = *line_length - field_start - 1U; 323 | 324 | /* Copy exactly field_width bytes from field_starts_at to field */ 325 | memcpy(field, field_starts_at, field_width); 326 | 327 | /* This must be a null-terminated character array */ 328 | field[field_width] = 0x00; 329 | 330 | string field_string_obj = field; 331 | 332 | row->push_back(field_string_obj); 333 | } 334 | 335 | /* Release the buffer for the field */ 336 | CSV_PARSER_FREE_BUFFER_PTR(field); 337 | } 338 | } 339 | 340 | void csv_parser::_get_fields_with_optional_enclosure(csv_row_ptr row, const char * line, const unsigned int * line_length) 341 | { 342 | char * field = NULL; 343 | 344 | /* 345 | * How to extract the fields, when the enclosure char is optional. 346 | * 347 | * This is very similar to parsing the document without enclosure but with the following conditions. 348 | * 349 | * If the beginning char is an enclosure character, adjust the starting position of the string by + 1. 350 | * If the ending char is an enclosure character, adjust the ending position by -1 351 | */ 352 | if (*line_length > 0) 353 | { 354 | field = (char *) malloc(*line_length); 355 | 356 | memset(field, 0, *line_length); 357 | 358 | register unsigned int field_start = 0U; 359 | register unsigned int field_end = 0U; 360 | register unsigned int char_pos = 0U; 361 | 362 | while(char_pos < *line_length) 363 | { 364 | char curr_char = line[char_pos]; 365 | 366 | if (curr_char == field_term_char) 367 | { 368 | field_end = char_pos; 369 | 370 | const char * field_starts_at = line + field_start; 371 | 372 | /* Field width must exclude field delimiter characters */ 373 | unsigned int field_width = field_end - field_start; 374 | 375 | const char line_first_char = field_starts_at[0]; 376 | const char line_final_char = field_starts_at[field_width - 1]; 377 | 378 | /* If the enclosure char is found at either ends of the string */ 379 | unsigned int first_adjustment = (line_first_char == enclosed_char) ? 1U : 0U; 380 | unsigned int final_adjustment = (line_final_char == enclosed_char) ? 2U : 0U; 381 | 382 | /* We do not want to have any negative or zero field widths */ 383 | field_width = (field_width > 2U) ? (field_width - final_adjustment) : field_width; 384 | 385 | /* Copy exactly field_width bytes from field_starts_at to field */ 386 | memcpy(field, field_starts_at + first_adjustment, field_width); 387 | 388 | /* This must be a null-terminated character array */ 389 | field[field_width] = 0x00; 390 | 391 | string field_string_obj = field; 392 | 393 | row->push_back(field_string_obj); 394 | 395 | /* This is the starting point of the next field */ 396 | field_start = char_pos + 1; 397 | 398 | } else if (curr_char == line_term_char) 399 | { 400 | field_end = char_pos; 401 | 402 | const char * field_starts_at = line + field_start; 403 | 404 | /* Field width must exclude line terminating characters */ 405 | unsigned int field_width = field_end - field_start; 406 | 407 | const char line_first_char = field_starts_at[0]; 408 | const char line_final_char = field_starts_at[field_width - 1]; 409 | 410 | /* If the enclosure char is found at either ends of the string */ 411 | unsigned int first_adjustment = (line_first_char == enclosed_char) ? 1U : 0U; 412 | unsigned int final_adjustment = (line_final_char == enclosed_char) ? 2U : 0U; 413 | 414 | /* We do not want to have any negative or zero field widths */ 415 | field_width = (field_width > 2U) ? (field_width - final_adjustment) : field_width; 416 | 417 | /* Copy exactly field_width bytes from field_starts_at to field */ 418 | memcpy(field, field_starts_at + first_adjustment, field_width); 419 | 420 | /* This must be a null-terminated character array */ 421 | field[field_width] = 0x00; 422 | 423 | string field_string_obj = field; 424 | 425 | row->push_back(field_string_obj); 426 | } 427 | 428 | /* Move to the next character in the current line */ 429 | char_pos++; 430 | } 431 | 432 | /* Deallocate memory for field buffer */ 433 | CSV_PARSER_FREE_BUFFER_PTR(field); 434 | } 435 | } 436 | 437 | void csv_parser::_read_single_line(char ** buffer, unsigned int * buffer_len) 438 | { 439 | long int original_pos = ftell(input_fp); 440 | long int current_pos = original_pos; 441 | 442 | register int current_char = 0; 443 | 444 | /* Checking one character at a time until the end of a line is found */ 445 | while(true) 446 | { 447 | current_char = fgetc(input_fp); 448 | 449 | if (current_char == EOF) 450 | { 451 | /* We have reached the end of the file */ 452 | more_rows = false; 453 | 454 | break; 455 | 456 | } else if (current_char == line_term_char) 457 | { 458 | /* We have reached the end of the row */ 459 | current_pos++; 460 | 461 | break; 462 | 463 | } else { 464 | 465 | current_pos++; 466 | } 467 | } 468 | 469 | /* Let's try to peek one character ahead to see if we are at the end of the file */ 470 | if (more_rows) 471 | { 472 | current_char = fgetc(input_fp); 473 | 474 | more_rows = (current_char == EOF) ? false : true; 475 | } 476 | 477 | /* Find out how long this row is */ 478 | const size_t length_of_row = current_pos - original_pos; 479 | 480 | if (length_of_row > 0) 481 | { 482 | *buffer_len = length_of_row * sizeof(char) + 1; 483 | 484 | *buffer = (char *) realloc(*buffer, *buffer_len); 485 | 486 | memset(*buffer, 0, *buffer_len); 487 | 488 | /* Reset the internal pointer to the original position */ 489 | fseek(input_fp, original_pos, SEEK_SET); 490 | 491 | /* Copy the contents of the line into the buffer */ 492 | fread(*buffer, 1, length_of_row, input_fp); 493 | } 494 | } 495 | -------------------------------------------------------------------------------- /csv_parser.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * csv_parser Header File 3 | * 4 | * This object is used to parse text documents that are delimited by some 5 | * type of character. Some of the common ones use spaces, tabs, commas and semi-colons. 6 | * 7 | * This is a list of common characters encountered by this program 8 | * 9 | * This list was prepared from the data from http://www.asciitable.com 10 | * 11 | * @li DEC is how it would be represented in decimal form (base 10) 12 | * @li HEX is how it would be represented in hexadecimal format (base 16) 13 | * 14 | * @li DEC HEX Character Name 15 | * @li 0 0x00 null 16 | * @li 9 0x09 horizontal tab 17 | * @li 10 0x0A line feed, new line 18 | * @li 13 0x0D carriage return 19 | * @li 27 0x1B escape 20 | * @li 32 0x20 space 21 | * @li 33 0x21 double quote 22 | * @li 39 0x27 single quote 23 | * @li 44 0x2C comma 24 | * @li 92 0x5C backslash 25 | * 26 | * @author Israel Ekpo 27 | */ 28 | 29 | #ifndef CSV_PARSER_HPP_INCLUDED 30 | 31 | #define CSV_PARSER_HPP_INCLUDED 32 | 33 | #define LIBCSV_PARSER_MAJOR_VERSION 1 34 | 35 | #define LIBCSV_PARSER_MINOR_VERSION 0 36 | 37 | #define LIBCSV_PARSER_PATCH_VERSION 0 38 | 39 | #define LIBCSV_PARSER_VERSION_NUMBER 10000 40 | 41 | /* C++ header files */ 42 | #include 43 | #include 44 | 45 | /* C header files */ 46 | #include 47 | #include 48 | #include 49 | 50 | using namespace std; 51 | 52 | /** 53 | * @typedef csv_row 54 | * 55 | * Data structure used to represent a record. 56 | * 57 | * This is an alias for vector 58 | */ 59 | typedef vector csv_row; 60 | 61 | /** 62 | * @typedef csv_row_ptr 63 | * 64 | * Pointer to a csv_row object 65 | * 66 | * Expands to vector * 67 | */ 68 | typedef csv_row * csv_row_ptr; 69 | 70 | /** 71 | * @typedef enclosure_type_t 72 | * 73 | * This enum type is used to set the mode in which the CSV file is parsed. 74 | * 75 | * @li ENCLOSURE_NONE (1) means the CSV file does not use any enclosure characters for the fields 76 | * @li ENCLOSURE_REQUIRED (2) means the CSV file requires enclosure characters for all the fields 77 | * @li ENCLOSURE_OPTIONAL (3) means the use of enclosure characters for the fields is optional 78 | * 79 | * The ENCLOSURE_TYPE_BEGIN and ENCLOSURE_TYPE_END members of this enum definition are never to be used. 80 | */ 81 | typedef enum 82 | { 83 | ENCLOSURE_TYPE_BEGIN = 0, 84 | ENCLOSURE_NONE = 1, 85 | ENCLOSURE_REQUIRED = 2, 86 | ENCLOSURE_OPTIONAL = 3, 87 | ENCLOSURE_TYPE_END 88 | 89 | } enclosure_type_t; 90 | 91 | /** 92 | * @def CSV_PARSER_FREE_BUFFER_PTR(ptr) 93 | * 94 | * Used to deallocate buffer pointers 95 | * 96 | * It deallocates the pointer only if it is not null 97 | */ 98 | #define CSV_PARSER_FREE_BUFFER_PTR(ptr) \ 99 | if (ptr != NULL) \ 100 | { \ 101 | free(ptr); \ 102 | \ 103 | ptr = NULL; \ 104 | } 105 | 106 | /** 107 | * @def CSV_PARSER_FREE_FILE_PTR(fptr) 108 | * 109 | * Used to close open file handles 110 | * 111 | * It closes the file only if it is not null 112 | */ 113 | #define CSV_PARSER_FREE_FILE_PTR(fptr) \ 114 | if (fptr != NULL) \ 115 | { \ 116 | fclose(fptr); \ 117 | \ 118 | fptr = NULL; \ 119 | } 120 | 121 | /** 122 | * @class csv_parser 123 | * 124 | * The csv_parser object 125 | * 126 | * Used to parse text files to extract records and fields. 127 | * 128 | * We are making the following assumptions : 129 | * 130 | * @li The record terminator is only one character in length. 131 | * @li The field terminator is only one character in length. 132 | * @li The fields are enclosed by single characters, if any. 133 | * 134 | * @li The parser can handle documents where fields are always enclosed, not enclosed at all or optionally enclosed. 135 | * @li When fields are strictly all enclosed, there is an assumption that any enclosure characters within the field are escaped by placing a backslash in front of the enclosure character. 136 | * 137 | * The CSV files can be parsed in 3 modes. 138 | * @li (a) No enclosures 139 | * @li (b) Fields always enclosed. 140 | * @li (c) Fields optionally enclosed. 141 | * 142 | * For option (c) when the enclosure character is optional, if an enclosure character is spotted at either the beginning 143 | * or the end of the string, it is assumed that the field is enclosed. 144 | * 145 | * The csv_parser::init() method can accept a character array as the path to the CSV file. 146 | * Since it is overloaded, it can also accept a FILE pointer to a stream that is already open for reading. 147 | * 148 | * The set_enclosed_char() method accepts the field enclosure character as the first parameter and the enclosure mode as the second parameter which 149 | * controls how the text file is going to be parsed. 150 | * 151 | * @see csv_parser::set_enclosed_char() 152 | * @see enclosure_type_t 153 | * 154 | * @todo Add ability to parse files where fields/columns are terminated by strings instead of just one char. 155 | * @todo Add ability to set strings where lines start by. Currently lines do not have any starting char or string. 156 | * @todo Add ability to set strings where line end by. Currently lines can only end with a single char. 157 | * @todo Add ability to accept other escape characters besides the backslash character 0x5C. 158 | * @todo More support for improperly formatted CSV data files. 159 | * 160 | * @author Israel Ekpo 161 | */ 162 | class csv_parser 163 | { 164 | 165 | public : 166 | 167 | /** 168 | * Class constructor 169 | * 170 | * This is the default constructor. 171 | * 172 | * All the internal attributes are initialized here 173 | * 174 | * @li The enclosure character is initialized to NULL 0x00. 175 | * @li The escape character is initialized to the backslash character 0x5C. 176 | * @li The field delimiter character is initialized to a comma 0x2C. 177 | * @li The record delimiter character is initialized to a new line character 0x0A. 178 | * 179 | * @li The lengths of all the above-mentioned fields are initialized to 0,1,1 and 1 respectively. 180 | * @li The number of records to ignore is set to zero. 181 | * @li The more_rows internal attribute is set to false. 182 | * @li The pointer to the CSV input file is initialized to NULL 183 | * @li The pointer to the buffer for the file name is also initialized to NULL 184 | */ 185 | csv_parser() : enclosed_char(0x00), escaped_char(0x5C), 186 | field_term_char(0x2C), line_term_char(0x0A), 187 | enclosed_length(0U), escaped_length(1U), 188 | field_term_length(1U), line_term_length(1U), 189 | ignore_num_lines(0U), record_count(0U), 190 | input_fp(NULL), input_filename(NULL), 191 | enclosure_type(ENCLOSURE_NONE), 192 | more_rows(false) 193 | { } 194 | 195 | /** 196 | * Class destructor 197 | * 198 | * In the class destructor the file pointer to the input CSV file is closed and 199 | * the buffer to the input file name is also deallocated. 200 | * 201 | * @see csv_parser::input_fp 202 | * @see csv_parser::input_filename 203 | */ 204 | ~csv_parser() 205 | { 206 | CSV_PARSER_FREE_FILE_PTR(input_fp); 207 | 208 | CSV_PARSER_FREE_BUFFER_PTR(input_filename); 209 | } 210 | 211 | /** 212 | * Initializes the current object 213 | * 214 | * This init method accepts a pointer to the CSV file that has been opened for reading 215 | * 216 | * It also resets the file pointer to the beginning of the stream 217 | * 218 | * @overload bool init(FILE * input_file_pointer) 219 | * @param[in] input_file_pointer 220 | * @return bool Returns true on success and false on error. 221 | */ 222 | bool init(FILE * input_file_pointer); 223 | 224 | /** 225 | * Initializes the current object 226 | * 227 | * @li This init method accepts a character array as the path to the csv file. 228 | * @li It sets the value of the csv_parser::input_filename property. 229 | * @li Then it creates a pointer to the csv_parser::input_fp property. 230 | * 231 | * @overload bool init(const char * input_filename) 232 | * @param[in] input_filename 233 | * @return bool Returns true on success and false on error. 234 | */ 235 | bool init(const char * input_filename); 236 | 237 | /** 238 | * Defines the Field Enclosure character used in the Text File 239 | * 240 | * Setting this to NULL means that the enclosure character is optional. 241 | * 242 | * If the enclosure is optional, there could be fields that are enclosed, and fields that are not enclosed within the same line/record. 243 | * 244 | * @param[in] fields_enclosed_by The character used to enclose the fields. 245 | * @param[in] enclosure_mode How the CSV file should be parsed. 246 | * @return void 247 | */ 248 | void set_enclosed_char(char fields_enclosed_by, enclosure_type_t enclosure_mode); 249 | 250 | /** 251 | * Defines the Field Delimiter character used in the text file 252 | * 253 | * @param[in] fields_terminated_by 254 | * @return void 255 | */ 256 | void set_field_term_char(char fields_terminated_by); 257 | 258 | /** 259 | * Defines the Record Terminator character used in the text file 260 | * 261 | * @param[in] lines_terminated_by 262 | * @return void 263 | */ 264 | void set_line_term_char(char lines_terminated_by); 265 | 266 | /** 267 | * Returns whether there is still more data 268 | * 269 | * This method returns a boolean value indicating whether or not there are 270 | * still more records to be extracted in the current file being parsed. 271 | * 272 | * Call this method to see if there are more rows to retrieve before invoking csv_parser::get_row() 273 | * 274 | * @see csv_parser::get_row() 275 | * @see csv_parser::more_rows 276 | * 277 | * @return bool Returns true if there are still more rows and false if there is not. 278 | */ 279 | bool has_more_rows(void) 280 | { 281 | return more_rows; 282 | } 283 | 284 | /** 285 | * Defines the number of records to discard 286 | * 287 | * The number of records specified will be discarded during the parsing process. 288 | * 289 | * @see csv_parser::_skip_lines() 290 | * @see csv_parser::get_row() 291 | * @see csv_parser::has_more_rows() 292 | * 293 | * @param[in] lines_to_skip How many records should be skipped 294 | * @return void 295 | */ 296 | void set_skip_lines(unsigned int lines_to_skip) 297 | { 298 | ignore_num_lines = lines_to_skip; 299 | } 300 | 301 | /** 302 | * Return the current row from the CSV file 303 | * 304 | * The row is returned as a vector of string objects. 305 | * 306 | * This method should be called only if csv_parser::has_more_rows() is true 307 | * 308 | * @see csv_parser::has_more_rows() 309 | * @see csv_parser::get_record_count() 310 | * @see csv_parser::reset_record_count() 311 | * @see csv_parser::more_rows 312 | * 313 | * @return csv_row A vector type containing an array of strings 314 | */ 315 | csv_row get_row(void); 316 | 317 | /** 318 | * Returns the number of times the csv_parser::get_row() method has been invoked 319 | * 320 | * @see csv_parser::reset_record_count() 321 | * @return unsigned int The number of times the csv_parser::get_row() method has been invoked. 322 | */ 323 | unsigned int get_record_count(void) 324 | { 325 | return record_count; 326 | } 327 | 328 | /** 329 | * Resets the record_count internal attribute to zero 330 | * 331 | * This may be used if the object is reused multiple times. 332 | * 333 | * @see csv_parser::record_count 334 | * @see csv_parser::get_record_count() 335 | * @return void 336 | */ 337 | void reset_record_count(void) 338 | { 339 | record_count = 0U; 340 | } 341 | 342 | private : 343 | 344 | /** 345 | * Ignores N records in the CSV file 346 | * 347 | * Where N is the value of the csv_parser::ignore_num_lines internal property. 348 | * 349 | * The number of lines skipped can be defined by csv_parser::set_skip_lines() 350 | * 351 | * @see csv_parser::set_skip_lines() 352 | * 353 | * @return void 354 | */ 355 | void _skip_lines(void); 356 | 357 | /** 358 | * Reads a Single Line 359 | * 360 | * Reads a single record into the buffer passed by reference to the method 361 | * 362 | * @param[in,out] buffer A pointer to a character array for the current line. 363 | * @param[out] buffer_len A pointer to an integer storing the length of the buffer. 364 | * @return void 365 | */ 366 | void _read_single_line(char ** buffer, unsigned int * buffer_len); 367 | 368 | /** 369 | * Extracts the fields without enclosures 370 | * 371 | * This is used when the enclosure character is not set 372 | * @param[out] row The vector of strings 373 | * @param[in] line The character array buffer containing the current record/line 374 | * @param[in] line_length The length of the buffer 375 | */ 376 | void _get_fields_without_enclosure(csv_row_ptr row, const char * line, const unsigned int * line_length); 377 | 378 | /** 379 | * Extracts the fields with enclosures 380 | * 381 | * This is used when the enclosure character is set. 382 | * 383 | * @param[out] row The vector of strings 384 | * @param[in] line The character array buffer containing the current record/line 385 | * @param[in] line_length The length of the buffer 386 | */ 387 | void _get_fields_with_enclosure(csv_row_ptr row, const char * line, const unsigned int * line_length); 388 | 389 | /** 390 | * Extracts the fields when enclosure is optional 391 | * 392 | * This is used when the enclosure character is optional 393 | * 394 | * Hence, there could be fields that use it, and fields that don't. 395 | * 396 | * @param[out] row The vector of strings 397 | * @param[in] line The character array buffer containing the current record/line 398 | * @param[in] line_length The length of the buffer 399 | */ 400 | void _get_fields_with_optional_enclosure(csv_row_ptr row, const char * line, const unsigned int * line_length); 401 | 402 | protected : 403 | 404 | /** 405 | * The enclosure character 406 | * 407 | * If present or used for a field it is assumed that both ends of the fields are wrapped. 408 | * 409 | * This is that single character used in the document to wrap the fields. 410 | * 411 | * @see csv_parser::_get_fields_without_enclosure() 412 | * @see csv_parser::_get_fields_with_enclosure() 413 | * @see csv_parser::_get_fields_with_optional_enclosure() 414 | * 415 | * @var enclosed_char 416 | */ 417 | char enclosed_char; 418 | 419 | /** 420 | * The escape character 421 | * 422 | * For now the only valid escape character allowed is the backslash character 0x5C 423 | * 424 | * This is only important when the enclosure character is required or optional. 425 | * 426 | * This is the backslash character used to escape enclosure characters found within the fields. 427 | * 428 | * @see csv_parser::_get_fields_with_enclosure() 429 | * @see csv_parser::_get_fields_with_optional_enclosure() 430 | * @todo Update the code to accept other escape characters besides the backslash 431 | * 432 | * @var escaped_char 433 | */ 434 | char escaped_char; 435 | 436 | /** 437 | * The field terminator 438 | * 439 | * This is the single character used to mark the end of a column in the text file. 440 | * 441 | * Common characters used include the comma, tab, and semi-colons. 442 | * 443 | * This is the single character used to separate fields within a record. 444 | * 445 | * @var field_term_char 446 | */ 447 | char field_term_char; 448 | 449 | /** 450 | * The record terminator 451 | * 452 | * This is the single character used to mark the end of a record in the text file. 453 | * 454 | * The most popular one is the new line character however it is possible to use others as well. 455 | * 456 | * This is the single character used to mark the end of a record 457 | * 458 | * @see csv_parser::get_row() 459 | * 460 | * @var line_term_char 461 | */ 462 | char line_term_char; 463 | 464 | /** 465 | * Enclosure length 466 | * 467 | * This is the length of the enclosure character 468 | * 469 | * @see csv_parser::csv_parser() 470 | * @see csv_parser::set_enclosed_char() 471 | * 472 | * @var enclosed_length 473 | */ 474 | unsigned int enclosed_length; 475 | 476 | /** 477 | * The length of the escape character 478 | * 479 | * Right now this is really not being used. 480 | * 481 | * It may be used in future versions of the object. 482 | * 483 | * @todo Update the code to accept other escape characters besides the backslash 484 | * 485 | * @var escaped_length 486 | */ 487 | unsigned int escaped_length; 488 | 489 | /** 490 | * Length of the field terminator 491 | * 492 | * For now this is not being used. It will be used in future versions of the object. 493 | * 494 | * @var field_term_length 495 | */ 496 | unsigned int field_term_length; 497 | 498 | /** 499 | * Length of the record terminator 500 | * 501 | * For now this is not being used. It will be used in future versions of the object. 502 | * 503 | * @var line_term_length 504 | */ 505 | unsigned int line_term_length; 506 | 507 | /** 508 | * Number of records to discard 509 | * 510 | * This variable controls how many records in the file are skipped before parsing begins. 511 | * 512 | * @see csv_parser::_skip_lines() 513 | * @see csv_parser::set_skip_lines() 514 | * 515 | * @var ignore_num_lines 516 | */ 517 | unsigned int ignore_num_lines; 518 | 519 | /** 520 | * Number of times the get_row() method has been called 521 | * 522 | * @see csv_parser::get_row() 523 | * @var record_count 524 | */ 525 | unsigned int record_count; 526 | 527 | /** 528 | * The CSV File Pointer 529 | * 530 | * This is the pointer to the CSV file 531 | * 532 | * @var input_fp 533 | */ 534 | FILE * input_fp; 535 | 536 | /** 537 | * Buffer to input file name 538 | * 539 | * This buffer is used to store the name of the file that is being parsed 540 | * 541 | * @var input_filename 542 | */ 543 | char * input_filename; 544 | 545 | /** 546 | * Mode in which the CSV file will be parsed 547 | * 548 | * The various values are explained below 549 | * 550 | * @li ENCLOSURE_NONE (1) means the CSV file does not use any enclosure characters for the fields 551 | * @li ENCLOSURE_REQUIRED (2) means the CSV file requires enclosure characters for all the fields 552 | * @li ENCLOSURE_OPTIONAL (3) means the use of enclosure characters for the fields is optional 553 | * 554 | * @see csv_parser::get_row() 555 | * @see csv_parser::_read_single_line() 556 | * @see csv_parser::_get_fields_without_enclosure() 557 | * @see csv_parser::_get_fields_with_enclosure() 558 | * @see csv_parser::_get_fields_with_optional_enclosure() 559 | * 560 | * @var enclosure_type 561 | */ 562 | enclosure_type_t enclosure_type; 563 | 564 | /** 565 | * There are still more records to parse 566 | * 567 | * This boolean property is an internal indicator of whether there are still records in the 568 | * file to be parsed. 569 | * 570 | * @see csv_parser::has_more_rows() 571 | * @var more_rows 572 | */ 573 | bool more_rows; 574 | 575 | }; /* class csv_parser */ 576 | 577 | #endif /* CSV_PARSER_HPP_INCLUDED */ 578 | -------------------------------------------------------------------------------- /OutlierDetector.cpp: -------------------------------------------------------------------------------- 1 | // OutlierDetector.cpp : implementation file 2 | // 3 | 4 | #include "TrajData.h" 5 | #include "OutlierDetector.h" 6 | #include "DistanceOutlier.h" 7 | #include "Param.h" 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | 14 | // COutlierDetector 15 | 16 | /** 17 | * \brief Default constructor 18 | */ 19 | COutlierDetector::COutlierDetector() 20 | { 21 | } 22 | 23 | /** 24 | * \brief Constructor that accepts data 25 | */ 26 | COutlierDetector::COutlierDetector(TrajData* data) 27 | { 28 | m_data = data; 29 | } 30 | 31 | COutlierDetector::~COutlierDetector() 32 | { 33 | } 34 | 35 | 36 | // COutlierDetector member functions 37 | 38 | /** 39 | * \brief Resets the outlier detector object 40 | * 41 | * Clears all of the internal arrays and resets the counters. Does not clear the trajectory data. 42 | * 43 | * @return Successful or not 44 | */ 45 | bool COutlierDetector::ResetOutlierDetector() 46 | { 47 | vector& trajectoryList = m_data->m_trajectoryList; 48 | //list::iterator pos = trajectoryList.begin(); 49 | auto pos = trajectoryList.begin(); 50 | while (pos != trajectoryList.end()) 51 | { 52 | CTrajectory* pTrajectory = *pos; 53 | 54 | pTrajectory->m_nPartitionPoints = 0; 55 | pTrajectory->m_partitionPointArray.clear(); 56 | pTrajectory->m_partitionIndexArray.clear(); 57 | pTrajectory->m_partitionInfoArray.clear(); 58 | pTrajectory->m_nOutlyingPartitions = 0; 59 | pTrajectory->m_outlyingPartitionArray.clear(); 60 | pTrajectory->m_totalPartitionLength = 0.0; 61 | pTrajectory->m_outlyingPartitionLength = 0.0; 62 | pTrajectory->m_containOutlier = false; 63 | pos++; 64 | } 65 | m_data->m_nOutlyingPartitions = 0; 66 | 67 | while (!m_data->m_outlierList.empty()) 68 | { 69 | delete m_data->m_outlierList.front(); 70 | m_data->m_outlierList.pop_front(); 71 | } 72 | m_data->m_nOutliers = 0; 73 | 74 | return true; 75 | } 76 | 77 | /** 78 | * \brief Partitions the trajectories in the data 79 | * 80 | * Consults parameter __USE_CONVENTIONAL_PARTITONING__ to determine whether or not to use two-level partitioning scheme 81 | */ 82 | bool COutlierDetector::PartitionTrajectory() 83 | { 84 | #ifdef __USE_CONVENTIONAL_PARTITONING__ 85 | vector& trajectoryList = m_data->m_trajectoryList; 86 | auto pos = trajectoryList.begin(); 87 | while (pos != trajectoryList.end()) 88 | { 89 | CTrajectory* pTrajectory = *pos; 90 | if (!FindOptimalPartition(pTrajectory)) 91 | return false; 92 | pos++; 93 | } 94 | #endif 95 | 96 | if (!StoreTrajectoryPartitionIntoIndex()) 97 | return false; 98 | 99 | return true; 100 | } 101 | 102 | /** 103 | * \brief Finds optimal partitioning of a trajectory 104 | * 105 | * Consults __PARTITION_PRUNING_OPTIMIZATION__ to determine if the optimizations to the two-level partitioning should be used. 106 | * @param [in] pTrajectory the trajectory to partition 107 | */ 108 | bool COutlierDetector::FindOptimalPartition(CTrajectory* pTrajectory) 109 | { 110 | int nPoints = pTrajectory->m_nPoints; 111 | int startIndex = 0, length = 0; 112 | int fullPartitionMDLCost, partialPartitionMDLCost; 113 | 114 | // add the start point of a trajectory 115 | pTrajectory->AddPartitionPointToArray(pTrajectory->m_pointArray[0], 0); 116 | 117 | for (;;) 118 | { 119 | fullPartitionMDLCost = partialPartitionMDLCost = 0; 120 | 121 | #if defined(__PARTITION_PRUNING_OPTIMIZATION__) 122 | // reset some statistics per each trajectory 123 | m_partitionInfo.maxPerpDist = 0.0; 124 | m_partitionInfo.minLength = (float)INT_MAX; 125 | m_partitionInfo.maxLength = 0.0; 126 | m_partitionInfo.maxTheta = 0.0; 127 | #endif 128 | 129 | for (length = 1; startIndex + length < nPoints; length++) 130 | { 131 | // compute the total length of a trajectory 132 | fullPartitionMDLCost += ComputeModelCost(pTrajectory, startIndex + length - 1, startIndex + length); 133 | 134 | // compute the sum of (1) the length of a trajectory partition and 135 | // (2) the perpendicular and angle distances 136 | partialPartitionMDLCost = ComputeModelCost(pTrajectory, startIndex, startIndex + length) + 137 | ComputeEncodingCost(pTrajectory, startIndex, startIndex + length); 138 | 139 | if (fullPartitionMDLCost + MDL_COST_ADVANTAGE < partialPartitionMDLCost) 140 | { 141 | pTrajectory->AddPartitionPointToArray(pTrajectory->m_pointArray[startIndex + length - 1], startIndex + length - 1); 142 | 143 | #if defined(__PARTITION_PRUNING_OPTIMIZATION__) 144 | // maintain some statistics for each trajectory 145 | PartitionInfo aPartitionInfo = m_partitionInfo; 146 | pTrajectory->StorePartitionInfo(aPartitionInfo); 147 | #endif 148 | 149 | startIndex = startIndex + length - 1; 150 | length = 0; 151 | 152 | break; 153 | } 154 | } 155 | 156 | // if we reach at the end of a trajectory 157 | if (startIndex + length >= nPoints) 158 | break; 159 | } 160 | 161 | // add the end point of a trajectory 162 | pTrajectory->AddPartitionPointToArray(pTrajectory->m_pointArray[nPoints - 1], nPoints - 1); 163 | 164 | #if defined(__PARTITION_PRUNING_OPTIMIZATION__) 165 | // maintain some statistics for each trajectory; this is the last partition 166 | PartitionInfo aPartitionInfo = m_partitionInfo; 167 | pTrajectory->StorePartitionInfo(aPartitionInfo); 168 | #endif 169 | 170 | return true; 171 | } 172 | 173 | /** 174 | * \brief Saves the trajectory partitioning and precomputes some calculations (such as distances) 175 | */ 176 | bool COutlierDetector::StoreTrajectoryPartitionIntoIndex() 177 | { 178 | int nDimensions = m_data->m_nDimensions; 179 | CMDPoint* startPoint; 180 | CMDPoint* endPoint; 181 | float length, totalLengthPerTrajectory; 182 | 183 | m_nTotalLineSegments = 0; 184 | 185 | vector& trajectoryList = m_data->m_trajectoryList; 186 | auto pos = trajectoryList.begin(); 187 | while (pos != trajectoryList.end()) 188 | { 189 | CTrajectory* pTrajectory = *pos; 190 | totalLengthPerTrajectory = 0.0; // reset for each trajectory 191 | 192 | #if defined(__USE_CONVENTIONAL_PARTITONING__) 193 | for (int i = 0; i < pTrajectory->m_nPartitionPoints - 1; i++) 194 | { 195 | startPoint = &(pTrajectory->m_partitionPointArray[i]); 196 | endPoint = &(pTrajectory->m_partitionPointArray[i + 1]); 197 | #elif defined(__USE_NO_PARTITIONING__) 198 | for (int i = 0; i < pTrajectory->m_nPoints - 1; i++) 199 | { 200 | startPoint = &(pTrajectory->m_pointArray[i]); 201 | endPoint = &(pTrajectory->m_pointArray[i + 1]); 202 | #endif // __USE_CONVENTIONAL_PARTITONING__ 203 | 204 | // if a line segment is too short, ignore it 205 | if ((length = MeasureDistanceFromPointToPoint(startPoint, endPoint)) < MIN_LINESEGMENT_LENGTH) 206 | continue; 207 | 208 | m_nTotalLineSegments++; // increase the total number of line segments 209 | totalLengthPerTrajectory += length; // accumulate the length of a trajectory partition per trajectory 210 | 211 | // keep the pair of a trajectory id and an order in the trajectory 212 | m_idArray.push_back(IntIntPair(pTrajectory->GetId(), i)); 213 | // temporarily keep the coordinate of a line segment for efficient distance computation 214 | // m_lineSegmentArray will be released soon 215 | m_lineSegmentArray.push_back(PointPointPair(startPoint, endPoint)); 216 | // keep the length of a line segment; this is used for determining the close trajectory 217 | m_lengthArray.push_back(length); 218 | #if defined(__PARTITION_PRUNING_OPTIMIZATION__) 219 | m_partitionInfoArray.push_back(&(pTrajectory->m_partitionInfoArray[i])); 220 | #endif 221 | } 222 | 223 | pTrajectory->SetLength(totalLengthPerTrajectory); 224 | pos++; 225 | } 226 | 227 | m_data->m_nTrajectoryPartitions = m_nTotalLineSegments; 228 | 229 | // pre-calculate the distance between two line segments 230 | // START ... 231 | m_distanceIndex.resize(m_nTotalLineSegments); 232 | for (int i = 0; i < m_nTotalLineSegments; i++) m_distanceIndex[i].resize(m_nTotalLineSegments); 233 | 234 | // hold the perpendicular, parallel, angle distances 235 | float distance[3]; 236 | // used for measuring the error of pruning 237 | int nTotalPartitionPairs = 0, nPrunedPartitionPairs = 0, nFalsePrunedPairs = 0, nOptimalPrunedPairs = 0; 238 | // for debugging 239 | // float minRealDistance[3], maxRealDistance[3]; 240 | 241 | for (int i = 0; i < m_nTotalLineSegments; i++) 242 | { 243 | for (int j = i + 1; j < m_nTotalLineSegments; j++) 244 | { 245 | ComputeDistanceBetweenTwoLineSegments(m_lineSegmentArray[i].first, m_lineSegmentArray[i].second, 246 | m_lineSegmentArray[j].first, m_lineSegmentArray[j].second, distance); 247 | 248 | // NOTE: 249 | // 1) our distance measure is symmetric 250 | // 2) three weights are determined by users (or applications) 251 | m_distanceIndex[i][j] = m_distanceIndex[j][i] = WEIGHTED_DISTANCE(distance[0], distance[1], distance[2]); 252 | 253 | #if defined(__PARTITION_PRUNING_OPTIMIZATION__) 254 | 255 | #define PI (float)3.141593 256 | #define MIN(_x,_y) ((_x)<(_y) ? (_x):(_y)) 257 | #define MAX(_x,_y) ((_x)>(_y) ? (_x):(_y)) 258 | 259 | // for debugging 260 | // PartitionInfo forDebug1 = *(m_partitionInfoArray[i]), forDebug2 = *(m_partitionInfoArray[j]); 261 | nTotalPartitionPairs++; 262 | 263 | // compute the lower and upper bounds for the perpendicular distance 264 | // START ... 265 | float minPerpDistance, maxPerpDistance; 266 | 267 | minPerpDistance = MIN(m_distance1, m_distance2) - m_partitionInfoArray[i]->maxPerpDist - m_partitionInfoArray[j]->maxPerpDist; 268 | maxPerpDistance = MAX(m_distance1, m_distance2) + m_partitionInfoArray[i]->maxPerpDist + m_partitionInfoArray[j]->maxPerpDist; 269 | if (minPerpDistance < 0.0) minPerpDistance = 0.0; 270 | 271 | m_lowerBoundPerp = minPerpDistance; 272 | m_upperBoundPerp = maxPerpDistance; 273 | // ... END 274 | 275 | // compute the lower and upper bounds for the parallel distance 276 | // START ... 277 | float length1, length2; 278 | 279 | length1 = MeasureDistanceFromPointToPoint(m_lineSegmentArray[i].first, m_lineSegmentArray[i].second); 280 | length2 = MeasureDistanceFromPointToPoint(m_lineSegmentArray[j].first, m_lineSegmentArray[j].second); 281 | 282 | if (fabs(m_coefficient1) < 1 && fabs(m_coefficient2) < 1) { // enclose 283 | m_lowerBoundPara = (float)0.0; 284 | m_upperBoundPara = MAX(length1, length2); 285 | } 286 | else if (fabs(m_coefficient1) < 1 || fabs(m_coefficient2) < 1) { // intersect 287 | m_lowerBoundPara = (float)0.0; 288 | m_upperBoundPara = length1 + length2 - distance[1]; 289 | } 290 | else { // distinct 291 | m_lowerBoundPara = distance[1]; 292 | m_upperBoundPara = length1 + length2 + distance[1]; 293 | } 294 | // ... END 295 | 296 | // compute the lower and upper bounds for the angle distance 297 | // START ... 298 | float possibleMinTheta, possibleMaxTheta; 299 | float minMinLength, minMaxLength; 300 | 301 | possibleMinTheta = m_theta - m_partitionInfoArray[i]->maxTheta - m_partitionInfoArray[j]->maxTheta; 302 | possibleMaxTheta = m_theta + m_partitionInfoArray[i]->maxTheta + m_partitionInfoArray[j]->maxTheta; 303 | if (possibleMinTheta < 0.0) possibleMinTheta = 0.0; 304 | if (possibleMaxTheta > PI / 2) possibleMaxTheta = PI / 2; 305 | minMinLength = MIN(m_partitionInfoArray[i]->minLength, m_partitionInfoArray[j]->minLength); 306 | minMaxLength = MIN(m_partitionInfoArray[i]->maxLength, m_partitionInfoArray[j]->maxLength); 307 | 308 | m_lowerBoundAngle = minMinLength * sin(possibleMinTheta); 309 | m_upperBoundAngle = minMaxLength * sin(possibleMaxTheta); 310 | // ... END 311 | 312 | float estimatedLowerBound = WEIGHTED_DISTANCE(m_lowerBoundPerp, m_lowerBoundPara, m_lowerBoundAngle); 313 | float estimatedUpperBound = WEIGHTED_DISTANCE(m_upperBoundPerp, m_upperBoundPara, m_upperBoundAngle); 314 | 315 | if (estimatedLowerBound > m_data->m_paramDistance || estimatedUpperBound < m_data->m_paramDistance) 316 | { 317 | nPrunedPartitionPairs++; 318 | } 319 | else 320 | { 321 | int trajectoryId1 = m_idArray[i].first, index1 = m_idArray[i].second; 322 | int trajectoryId2 = m_idArray[j].first, index2 = m_idArray[j].second; 323 | if (trajectoryId1 == trajectoryId2) continue; 324 | m_data->m_trajectoryList; 325 | auto comp1 = [trajectoryId1](CTrajectory* traj) {return traj->m_trajectoryId == trajectoryId1;}; 326 | auto comp2 = [trajectoryId2](CTrajectory* traj) {return traj->m_trajectoryId == trajectoryId2;}; 327 | //CTrajectory* pTrajectory1 = m_data->m_trajectoryList.GetAt(m_data->m_trajectoryList.FindIndex(trajectoryId1)); 328 | //CTrajectory* pTrajectory2 = m_data->m_trajectoryList.GetAt(m_data->m_trajectoryList.FindIndex(trajectoryId2)); 329 | auto p1 = find_if(m_data->m_trajectoryList.begin(), m_data->m_trajectoryList.end(),comp1); 330 | auto p2 = find_if(m_data->m_trajectoryList.begin(), m_data->m_trajectoryList.end(),comp2); 331 | CTrajectory* pTrajectory1 = *p1; 332 | CTrajectory* pTrajectory2 = *p2; 333 | 334 | CMDPoint *startPoint1, *endPoint1, *startPoint2, *endPoint2; 335 | float length1, length2; 336 | float fineDistance[3]; 337 | 338 | for (int k = pTrajectory1->m_partitionIndexArray[index1]; k < pTrajectory1->m_partitionIndexArray[index1 + 1]; k++) 339 | { 340 | startPoint1 = &(pTrajectory1->m_pointArray[k]); 341 | endPoint1 = &(pTrajectory1->m_pointArray[k + 1]); 342 | length1 = MeasureDistanceFromPointToPoint(startPoint1, endPoint1); 343 | if (length1 < MIN_LINESEGMENT_LENGTH || length1 > MAX_LINESEGMENT_LENGTH) continue; 344 | 345 | for (int l = pTrajectory2->m_partitionIndexArray[index2]; l < pTrajectory2->m_partitionIndexArray[index2 + 1]; l++) 346 | { 347 | startPoint2 = &(pTrajectory2->m_pointArray[l]); 348 | endPoint2 = &(pTrajectory2->m_pointArray[l + 1]); 349 | length2 = MeasureDistanceFromPointToPoint(startPoint2, endPoint2); 350 | if (length2 < MIN_LINESEGMENT_LENGTH || length2 > MAX_LINESEGMENT_LENGTH) continue; 351 | 352 | ComputeDistanceBetweenTwoLineSegments(startPoint1, endPoint1, startPoint2, endPoint2, fineDistance); 353 | if (WEIGHTED_DISTANCE(fineDistance[0], fineDistance[1], fineDistance[2]) <= m_data->m_paramDistance) 354 | { 355 | if (m_closePartitionArrayIndexMap.count(IntIntPair(trajectoryId1,k)) != 0) { 356 | int index1 = m_closePartitionArrayIndexMap[IntIntPair(trajectoryId1,k)]; 357 | m_closePartitionArray[index1].push_back(IntIntPair(trajectoryId2,l)); 358 | } 359 | else { 360 | int size1 = (int)m_closePartitionArrayIndexMap.size(); 361 | m_closePartitionArrayIndexMap[IntIntPair(trajectoryId1,k)] = size1; 362 | m_closePartitionArray.resize(size1 + 1); 363 | m_closePartitionArray[size1].push_back(IntIntPair(trajectoryId2,l)); 364 | } 365 | if (m_closePartitionArrayIndexMap.count(IntIntPair(trajectoryId2,l)) != 0) { 366 | int index2 = m_closePartitionArrayIndexMap[IntIntPair(trajectoryId2,l)]; 367 | m_closePartitionArray[index2].push_back(IntIntPair(trajectoryId1,k)); 368 | } 369 | else { 370 | int size2 = (int)m_closePartitionArrayIndexMap.size(); 371 | m_closePartitionArrayIndexMap[IntIntPair(trajectoryId2,l)] = size2; 372 | m_closePartitionArray.resize(size2 + 1); 373 | m_closePartitionArray[size2].push_back(IntIntPair(trajectoryId1,k)); 374 | } 375 | m_finePartitionLengthMap[IntIntPair(trajectoryId1,k)] = length1; 376 | m_finePartitionLengthMap[IntIntPair(trajectoryId2,l)] = length2; 377 | #ifndef __PRECOMPUTE_DENSITY__ 378 | m_coarsePartitionIdMap[IntIntPair(trajectoryId1,k)] = i; 379 | m_coarsePartitionIdMap[IntIntPair(trajectoryId2,l)] = j; 380 | #endif 381 | } 382 | } 383 | } 384 | } 385 | 386 | // All line segments in this pair should be considered 387 | // START ... 388 | if (estimatedUpperBound < m_data->m_paramDistance) 389 | { 390 | int trajectoryId1 = m_idArray[i].first, index1 = m_idArray[i].second; 391 | int trajectoryId2 = m_idArray[j].first, index2 = m_idArray[j].second; 392 | if (trajectoryId1 == trajectoryId2) continue; 393 | 394 | auto comp1 = [trajectoryId1](CTrajectory* traj) {return traj->m_trajectoryId == trajectoryId1;}; 395 | auto comp2 = [trajectoryId2](CTrajectory* traj) {return traj->m_trajectoryId == trajectoryId2;}; 396 | //CTrajectory* pTrajectory1 = m_data->m_trajectoryList.GetAt(m_data->m_trajectoryList.FindIndex(trajectoryId1)); 397 | //CTrajectory* pTrajectory2 = m_data->m_trajectoryList.GetAt(m_data->m_trajectoryList.FindIndex(trajectoryId2)); 398 | auto p1 = find_if(m_data->m_trajectoryList.begin(), m_data->m_trajectoryList.end(),comp1); 399 | auto p2 = find_if(m_data->m_trajectoryList.begin(), m_data->m_trajectoryList.end(),comp2); 400 | CTrajectory* pTrajectory1 = *p1; 401 | CTrajectory* pTrajectory2 = *p2; 402 | 403 | CMDPoint *startPoint1, *endPoint1, *startPoint2, *endPoint2; 404 | float length1, length2; 405 | 406 | for (int k = pTrajectory1->m_partitionIndexArray[index1]; k < pTrajectory1->m_partitionIndexArray[index1 + 1]; k++) 407 | { 408 | startPoint1 = &(pTrajectory1->m_pointArray[k]); 409 | endPoint1 = &(pTrajectory1->m_pointArray[k + 1]); 410 | length1 = MeasureDistanceFromPointToPoint(startPoint1, endPoint1); 411 | if (length1 < MIN_LINESEGMENT_LENGTH || length1 > MAX_LINESEGMENT_LENGTH) continue; 412 | 413 | for (int l = pTrajectory2->m_partitionIndexArray[index2]; l < pTrajectory2->m_partitionIndexArray[index2 + 1]; l++) 414 | { 415 | startPoint2 = &(pTrajectory2->m_pointArray[l]); 416 | endPoint2 = &(pTrajectory2->m_pointArray[l + 1]); 417 | length2 = MeasureDistanceFromPointToPoint(startPoint2, endPoint2); 418 | if (length2 < MIN_LINESEGMENT_LENGTH || length2 > MAX_LINESEGMENT_LENGTH) continue; 419 | 420 | if (m_closePartitionArrayIndexMap.count(IntIntPair(trajectoryId1,k)) != 0) { 421 | int index1 = m_closePartitionArrayIndexMap[IntIntPair(trajectoryId1,k)]; 422 | m_closePartitionArray[index1].push_back(IntIntPair(trajectoryId2,l)); 423 | } 424 | else { 425 | int size1 = (int)m_closePartitionArrayIndexMap.size(); 426 | m_closePartitionArrayIndexMap[IntIntPair(trajectoryId1,k)] = size1; 427 | m_closePartitionArray.resize(size1 + 1); 428 | m_closePartitionArray[size1].push_back(IntIntPair(trajectoryId2,l)); 429 | } 430 | if (m_closePartitionArrayIndexMap.count(IntIntPair(trajectoryId2,l)) != 0) { 431 | int index2 = m_closePartitionArrayIndexMap[IntIntPair(trajectoryId2,l)]; 432 | m_closePartitionArray[index2].push_back(IntIntPair(trajectoryId1,k)); 433 | } 434 | else { 435 | int size2 = (int)m_closePartitionArrayIndexMap.size(); 436 | m_closePartitionArrayIndexMap[IntIntPair(trajectoryId2,l)] = size2; 437 | m_closePartitionArray.resize(size2 + 1); 438 | m_closePartitionArray[size2].push_back(IntIntPair(trajectoryId1,k)); 439 | } 440 | m_finePartitionLengthMap[IntIntPair(trajectoryId1,k)] = length1; 441 | m_finePartitionLengthMap[IntIntPair(trajectoryId2,l)] = length2; 442 | #ifndef __PRECOMPUTE_DENSITY__ 443 | m_coarsePartitionIdMap[IntIntPair(trajectoryId1,k)] = i; 444 | m_coarsePartitionIdMap[IntIntPair(trajectoryId2,l)] = j; 445 | #endif 446 | } 447 | } 448 | } 449 | // ... END 450 | 451 | #undef __VERIFY_TIGHTNESS__ // enable this block to measure the tightness of the bounds 452 | #ifdef __VERIFY_TIGHTNESS__ 453 | // verify the tightness of the lower and upper bounds 454 | // START ... 455 | int trajectoryId1 = m_idArray[i].first, index1 = m_idArray[i].second; 456 | int trajectoryId2 = m_idArray[j].first, index2 = m_idArray[j].second; 457 | 458 | CTrajectory* pTrajectory1 = m_data->m_trajectoryList.GetAt(m_data->m_trajectoryList.FindIndex(trajectoryId1)); 459 | CTrajectory* pTrajectory2 = m_data->m_trajectoryList.GetAt(m_data->m_trajectoryList.FindIndex(trajectoryId2)); 460 | 461 | CMDPoint *startPoint1, *endPoint1, *startPoint2, *endPoint2; 462 | float realLowerBoundPerp = (float)INT_MAX, realUpperBoundPerp = (float)0.0; 463 | float realLowerBoundPara = (float)INT_MAX, realUpperBoundPara = (float)0.0; 464 | float realLowerBoundAngle = (float)INT_MAX, realUpperBoundAngle = (float)0.0; 465 | float realLowerBound = (float)INT_MAX, realUpperBound = (float)0.0; 466 | float realDistance[3], realDistanceSum; 467 | 468 | for (int k = pTrajectory1->m_partitionIndexArray[index1]; k < pTrajectory1->m_partitionIndexArray[index1 + 1]; k++) 469 | { 470 | startPoint1 = &(pTrajectory1->m_pointArray[k]); 471 | endPoint1 = &(pTrajectory1->m_pointArray[k + 1]); 472 | 473 | for (int l = pTrajectory2->m_partitionIndexArray[index2]; l < pTrajectory2->m_partitionIndexArray[index2 + 1]; l++) 474 | { 475 | startPoint2 = &(pTrajectory2->m_pointArray[l]); 476 | endPoint2 = &(pTrajectory2->m_pointArray[l + 1]); 477 | 478 | ComputeDistanceBetweenTwoLineSegments(startPoint1, endPoint1, startPoint2, endPoint2, realDistance); 479 | realDistanceSum = WEIGHTED_DISTANCE(realDistance[0], realDistance[1], realDistance[2]); 480 | 481 | if (realLowerBoundPerp > realDistance[0]) realLowerBoundPerp = realDistance[0]; 482 | if (realUpperBoundPerp < realDistance[0]) realUpperBoundPerp = realDistance[0]; 483 | if (realLowerBoundPara > realDistance[1]) realLowerBoundPara = realDistance[1]; 484 | if (realUpperBoundPara < realDistance[1]) realUpperBoundPara = realDistance[1]; 485 | if (realLowerBoundAngle > realDistance[2]) realLowerBoundAngle = realDistance[2]; 486 | if (realUpperBoundAngle < realDistance[2]) realUpperBoundAngle = realDistance[2]; 487 | if (realLowerBound > realDistanceSum) { 488 | realLowerBound = realDistanceSum; 489 | // memcpy(minRealDistance, realDistance, sizeof(float) * 3); 490 | } 491 | if (realUpperBound < realDistanceSum) { 492 | realUpperBound = realDistanceSum; 493 | // memcpy(maxRealDistance, realDistance, sizeof(float) * 3); 494 | } 495 | } 496 | } 497 | 498 | // count the number of maximal prunings 499 | if (realLowerBound > m_data->m_paramDistance || realUpperBound < m_data->m_paramDistance) nOptimalPrunedPairs++; 500 | 501 | // count the number of false dismissals 502 | if ((estimatedLowerBound > m_data->m_paramDistance && realLowerBound <= m_data->m_paramDistance) || 503 | (estimatedUpperBound < m_data->m_paramDistance && realUpperBound >= m_data->m_paramDistance)) 504 | nFalsePrunedPairs++; 505 | // ... END 506 | #endif 507 | #endif // #if defined(__PARTITION_PRUNING_OPTIMIZATION__) 508 | } // for (int j = i + 1; j < m_nTotalLineSegments; j++) 509 | } // for (int i = 0; i < m_nTotalLineSegments; i++) 510 | // ... END 511 | 512 | #ifdef __VERIFY_TIGHTNESS__ 513 | FILE *fp; 514 | fopen_s(&fp, RESULT_FILE, "a"); 515 | CT2CA temp1(m_data->m_inputFilePath); 516 | std::string temp2(temp1); 517 | #ifdef __COMBO_I__ 518 | fprintf(fp, "COMBO I: %s %.1f => %d, %d, %d, %d\n", temp2.c_str(), g_DISTANCE_PARAMETER, nTotalPartitionPairs, nPrunedPartitionPairs, nFalsePrunedPairs, nOptimalPrunedPairs); 519 | #else 520 | fprintf(fp, "COMBO II: %s %.1f => %d, %d, %d, %d\n", temp2.c_str(), g_DISTANCE_PARAMETER, nTotalPartitionPairs, nPrunedPartitionPairs, nFalsePrunedPairs, nOptimalPrunedPairs); 521 | #endif 522 | fclose(fp); 523 | #endif // #ifdef __VERIFY_TIGHTNESS__ 524 | 525 | // deallocate the copy of all line segments 526 | m_lineSegmentArray.clear(); 527 | //m_lineSegmentArray.FreeExtra(); 528 | 529 | return true; 530 | } 531 | 532 | /** 533 | * \brief Finds outliers and stores them in the data member 534 | * 535 | * Finds outliers in the data that is contained in the m_data field of the object. 536 | * The outlying trajectories are stored in the m_outlierList member of the supplied data. 537 | * 538 | * @return Successful or not. 539 | */ 540 | bool COutlierDetector::DetectOutlier() 541 | { 542 | CDistanceOutlier distanceOutlier(m_data->m_nTrajectories, m_nTotalLineSegments, &m_idArray, &m_distanceIndex); 543 | distanceOutlier.m_data = m_data; // pass the pointer of the data 544 | 545 | // setup two parameters: p (i.e., fraction) and D (i.e., distance) 546 | distanceOutlier.SetFractionParameter(m_data->m_paramFraction); 547 | distanceOutlier.SetDistanceParameter(m_data->m_paramDistance); 548 | 549 | // setup the array of trajectory partitions' length 550 | distanceOutlier.SetLengthArray(&m_lengthArray); 551 | 552 | #ifdef __PRECOMPUTE_DENSITY__ 553 | distanceOutlier.SetDensityFilePath((m_data->m_inputFilePath + ".density")); 554 | #endif 555 | 556 | vector outlierFlagArray; 557 | #ifndef __PARTITION_PRUNING_OPTIMIZATION__ 558 | outlierFlagArray.SetSize(m_nTotalLineSegments); 559 | #else 560 | outlierFlagArray.resize((int)m_closePartitionArrayIndexMap.size()); 561 | 562 | // setup the information for pruning optimization 563 | distanceOutlier.SetCloseTrajectoryPartition(&m_closePartitionArrayIndexMap, &m_closePartitionArray); 564 | distanceOutlier.SetFinePartitionLength(&m_finePartitionLengthMap); 565 | distanceOutlier.SetCoarsePartitionId(&m_coarsePartitionIdMap); 566 | #endif 567 | 568 | // identify outlying trajectory partitions 569 | if (!distanceOutlier.DetectOutlyingLineSegment(&outlierFlagArray)) 570 | return false; 571 | 572 | int currOutlierId = 0; 573 | #ifndef __PARTITION_PRUNING_OPTIMIZATION__ 574 | int currLineSegmentId = 0; 575 | 576 | CTypedPtrList& trajectoryList = m_data->m_trajectoryList; 577 | POSITION pos = trajectoryList.GetHeadPosition(); 578 | while (pos != NULL) 579 | { 580 | CTrajectory* pTrajectory = trajectoryList.GetNext(pos); 581 | int currTrajectoryId = pTrajectory->GetId(); 582 | 583 | // identify which trajectory partitions are outlying 584 | if (currLineSegmentId < m_nTotalLineSegments) 585 | { 586 | // check whether a specific line segment belongs to the current trajectory 587 | while (m_idArray[currLineSegmentId].first == currTrajectoryId) 588 | { 589 | if (outlierFlagArray[currLineSegmentId]) 590 | { 591 | pTrajectory->AddOutlyingPartition(m_idArray[currLineSegmentId].second); 592 | m_data->m_nOutlyingPartitions++; 593 | } 594 | if (++currLineSegmentId >= m_nTotalLineSegments) break; 595 | } 596 | } 597 | 598 | // check if the proportion of outlying trajectory partitions is significant 599 | if (CheckOutlyingProportion(pTrajectory, g_MINIMUM_OUTLYING_PROPORTION)) 600 | { 601 | pTrajectory->m_containOutlier = true; 602 | GenerateAndSetupOutlier(pTrajectory, currOutlierId); 603 | currOutlierId++; // increase the number of outliers 604 | } 605 | } 606 | #else // #ifndef __PARTITION_PRUNING_OPTIMIZATION__ 607 | map::const_iterator iter; 608 | for (iter = m_closePartitionArrayIndexMap.begin(); iter != m_closePartitionArrayIndexMap.end(); iter++) 609 | { 610 | if (outlierFlagArray[iter->second]) 611 | { 612 | auto comp = [&](CTrajectory* traj) { return traj->m_trajectoryId == iter->first.first; }; 613 | CTrajectory* pTrajectory = *(find_if(m_data->m_trajectoryList.begin(), m_data->m_trajectoryList.end(), comp)); 614 | //CTrajectory* pTrajectory = m_data->m_trajectoryList.GetAt(m_data->m_trajectoryList.FindIndex(iter->first.first)); 615 | pTrajectory->AddOutlyingPartition(iter->first.second); 616 | m_data->m_nOutlyingPartitions++; 617 | } 618 | } 619 | 620 | vector& trajectoryList = m_data->m_trajectoryList; 621 | auto pos = trajectoryList.begin(); 622 | while (pos != trajectoryList.end()) 623 | { 624 | CTrajectory* pTrajectory = *pos; 625 | 626 | // check if the proportion of outlying trajectory partitions is significant 627 | if (CheckOutlyingProportion(pTrajectory, g_MINIMUM_OUTLYING_PROPORTION)) 628 | { 629 | pTrajectory->m_containOutlier = true; 630 | GenerateAndSetupOutlier(pTrajectory, currOutlierId); 631 | currOutlierId++; // increase the number of outliers 632 | } 633 | pos++; 634 | } 635 | #endif // #ifndef __PARTITION_PRUNING_OPTIMIZATION__ 636 | 637 | return true; 638 | } 639 | 640 | /** 641 | * \brief Checks if the trajectory contains a significant portion of outlying partitions 642 | */ 643 | bool COutlierDetector::CheckOutlyingProportion(CTrajectory* pTrajectory, float minProportion) 644 | { 645 | CMDPoint* startPoint; 646 | CMDPoint* endPoint; 647 | float totalOutlyingLength = 0.0; 648 | 649 | // measure the ratio of the outlying length to the entire length 650 | for (int i = 0; i < pTrajectory->m_nOutlyingPartitions; i++) 651 | { 652 | int index = pTrajectory->m_outlyingPartitionArray[i]; 653 | 654 | #if defined(__USE_CONVENTIONAL_PARTITONING__) && !defined(__PARTITION_PRUNING_OPTIMIZATION__) 655 | startPoint = &(pTrajectory->m_partitionPointArray[index]); 656 | endPoint = &(pTrajectory->m_partitionPointArray[index + 1]); 657 | #elif defined(__USE_NO_PARTITIONING__) || defined(__PARTITION_PRUNING_OPTIMIZATION__) 658 | startPoint = &(pTrajectory->m_pointArray[index]); 659 | endPoint = &(pTrajectory->m_pointArray[index + 1]); 660 | #endif 661 | 662 | totalOutlyingLength += MeasureDistanceFromPointToPoint(startPoint, endPoint); 663 | } 664 | 665 | // keep the length of outlying partitions for a future use 666 | pTrajectory->SetOutlyingLength(totalOutlyingLength); 667 | 668 | // check if the outlying proportion is larger than a given threshold 669 | if ((totalOutlyingLength / pTrajectory->GetLength()) >= minProportion) 670 | return true; 671 | else 672 | return false; 673 | } 674 | 675 | /** 676 | * \brief Marks the given trajectory as an outlier in the data member 677 | * 678 | * @return Successful or not. 679 | */ 680 | bool COutlierDetector::GenerateAndSetupOutlier(CTrajectory* pTrajectory, int outlierId) 681 | { 682 | COutlier* pOutlierItem = new COutlier(outlierId, pTrajectory->GetId(), m_data->m_nDimensions); 683 | 684 | // copy the information about an outlier to a newly created structure 685 | pOutlierItem->SetupInfo(pTrajectory); 686 | // append the newly created structure to the list 687 | m_data->m_outlierList.push_back(pOutlierItem); 688 | // increase the number of outlier trajectories 689 | m_data->m_nOutliers++; 690 | 691 | return true; 692 | } 693 | 694 | 695 | -------------------------------------------------------------------------------- /hurricane2000_2006.tra: -------------------------------------------------------------------------------- 1 | 2 2 | 111 3 | 0 87 920.0 692.0 899.0 685.0 877.0 680.0 862.0 677.0 848.0 673.0 833.0 668.0 818.0 663.0 802.0 659.0 786.0 655.0 768.0 655.0 756.0 654.0 746.0 653.0 734.0 648.0 719.0 643.0 704.0 640.0 690.0 638.0 678.0 635.0 664.0 633.0 651.0 630.0 643.0 623.0 635.0 614.0 628.0 604.0 615.0 594.0 601.0 581.0 587.0 566.0 574.0 552.0 560.0 539.0 547.0 525.0 533.0 512.0 523.0 501.0 516.0 489.0 514.0 478.0 515.0 467.0 520.0 457.0 533.0 449.0 547.0 441.0 562.0 432.0 580.0 426.0 597.0 420.0 617.0 416.0 637.0 412.0 658.0 410.0 678.0 409.0 694.0 409.0 707.0 409.0 715.0 411.0 715.0 417.0 715.0 427.0 711.0 434.0 706.0 439.0 698.0 446.0 687.0 454.0 676.0 461.0 665.0 466.0 658.0 470.0 651.0 470.0 642.0 470.0 635.0 468.0 629.0 464.0 624.0 458.0 620.0 453.0 619.0 451.0 618.0 447.0 618.0 444.0 618.0 440.0 619.0 436.0 620.0 433.0 621.0 429.0 623.0 426.0 625.0 421.0 627.0 417.0 628.0 411.0 633.0 400.0 641.0 388.0 646.0 374.0 660.0 360.0 679.0 339.0 705.0 317.0 732.0 293.0 746.0 268.0 760.0 230.0 797.0 205.0 845.0 180.0 870.0 145.0 900.0 120.0 975.0 110.0 1051.0 93.0 4 | 1 9 165.0 575.0 162.0 573.0 154.0 569.0 146.0 565.0 137.0 561.0 130.0 559.0 123.0 555.0 114.0 551.0 102.0 548.0 5 | 2 9 581.0 658.0 572.0 653.0 566.0 648.0 559.0 644.0 546.0 638.0 535.0 632.0 523.0 627.0 510.0 622.0 496.0 617.0 6 | 3 20 655.0 680.0 647.0 674.0 632.0 667.0 612.0 660.0 594.0 653.0 579.0 649.0 560.0 646.0 537.0 643.0 515.0 639.0 499.0 632.0 483.0 625.0 465.0 619.0 446.0 612.0 433.0 608.0 419.0 605.0 403.0 602.0 385.0 600.0 367.0 601.0 349.0 604.0 330.0 605.0 7 | 4 10 648.0 652.0 630.0 650.0 617.0 644.0 605.0 638.0 592.0 631.0 579.0 625.0 564.0 618.0 550.0 612.0 534.0 606.0 520.0 600.0 8 | 5 29 391.0 491.0 387.0 492.0 382.0 493.0 378.0 496.0 374.0 499.0 373.0 499.0 372.0 498.0 369.0 497.0 367.0 494.0 363.0 492.0 360.0 493.0 362.0 493.0 363.0 495.0 364.0 498.0 364.0 504.0 366.0 505.0 369.0 507.0 372.0 508.0 376.0 509.0 388.0 502.0 403.0 499.0 425.0 492.0 439.0 474.0 458.0 457.0 482.0 439.0 505.0 421.0 526.0 399.0 550.0 375.0 570.0 345.0 9 | 6 28 227.0 602.0 226.0 596.0 223.0 593.0 220.0 590.0 213.0 586.0 222.0 584.0 233.0 575.0 234.0 571.0 237.0 565.0 241.0 557.0 246.0 548.0 251.0 539.0 257.0 529.0 262.0 520.0 266.0 511.0 270.0 502.0 277.0 490.0 285.0 477.0 298.0 465.0 310.0 450.0 318.0 430.0 340.0 415.0 360.0 400.0 380.0 385.0 405.0 380.0 428.0 375.0 450.0 370.0 470.0 365.0 10 | 7 42 578.0 651.0 570.0 647.0 564.0 644.0 556.0 642.0 541.0 639.0 520.0 636.0 501.0 634.0 483.0 634.0 464.0 636.0 444.0 633.0 429.0 630.0 413.0 629.0 394.0 628.0 375.0 626.0 356.0 624.0 337.0 617.0 317.0 611.0 304.0 606.0 290.0 601.0 274.0 593.0 257.0 582.0 246.0 570.0 239.0 561.0 234.0 551.0 230.0 539.0 229.0 529.0 228.0 516.0 228.0 505.0 234.0 495.0 246.0 484.0 265.0 471.0 283.0 464.0 300.0 456.0 320.0 446.0 339.0 436.0 353.0 428.0 375.0 420.0 399.0 408.0 432.0 399.0 478.0 384.0 545.0 360.0 612.0 339.0 11 | 8 52 870.0 685.0 855.0 681.0 841.0 677.0 828.0 673.0 813.0 669.0 799.0 665.0 788.0 663.0 777.0 661.0 768.0 657.0 758.0 654.0 750.0 651.0 742.0 649.0 732.0 645.0 722.0 642.0 714.0 637.0 705.0 633.0 696.0 628.0 688.0 624.0 680.0 621.0 671.0 617.0 661.0 614.0 650.0 609.0 640.0 604.0 630.0 596.0 619.0 590.0 605.0 581.0 594.0 572.0 580.0 562.0 571.0 550.0 558.0 534.0 549.0 520.0 541.0 503.0 538.0 488.0 541.0 471.0 548.0 456.0 560.0 443.0 582.0 430.0 602.0 417.0 621.0 403.0 643.0 391.0 664.0 379.0 705.0 365.0 735.0 355.0 770.0 343.0 810.0 330.0 850.0 315.0 895.0 305.0 935.0 295.0 980.0 280.0 1010.0 250.0 1040.0 220.0 1060.0 180.0 12 | 9 28 804.0 688.0 793.0 686.0 781.0 685.0 770.0 684.0 759.0 684.0 747.0 683.0 736.0 679.0 724.0 678.0 712.0 676.0 699.0 675.0 687.0 676.0 675.0 678.0 662.0 683.0 650.0 687.0 639.0 691.0 628.0 693.0 614.0 695.0 599.0 695.0 583.0 696.0 567.0 697.0 551.0 697.0 534.0 695.0 520.0 693.0 505.0 690.0 491.0 687.0 477.0 683.0 465.0 681.0 451.0 681.0 13 | 10 32 271.0 639.0 267.0 638.0 264.0 634.0 260.0 631.0 252.0 626.0 246.0 623.0 240.0 621.0 236.0 621.0 233.0 621.0 231.0 621.0 228.0 621.0 226.0 621.0 223.0 621.0 221.0 622.0 222.0 624.0 222.0 623.0 221.0 623.0 220.0 621.0 216.0 620.0 212.0 617.0 205.0 614.0 196.0 610.0 186.0 605.0 175.0 601.0 165.0 597.0 152.0 593.0 139.0 588.0 130.0 582.0 121.0 574.0 110.0 568.0 100.0 565.0 90.0 562.0 14 | 11 26 286.0 510.0 292.0 505.0 301.0 503.0 314.0 502.0 327.0 501.0 341.0 498.0 357.0 497.0 369.0 494.0 376.0 491.0 382.0 487.0 393.0 479.0 404.0 469.0 417.0 446.0 433.0 426.0 460.0 400.0 500.0 370.0 530.0 340.0 560.0 310.0 600.0 290.0 640.0 270.0 690.0 250.0 740.0 240.0 800.0 240.0 860.0 250.0 930.0 260.0 1000.0 270.0 15 | 12 22 388.0 500.0 385.0 500.0 382.0 501.0 381.0 501.0 383.0 503.0 386.0 502.0 389.0 501.0 390.0 502.0 391.0 502.0 391.0 499.0 391.0 496.0 392.0 492.0 396.0 485.0 405.0 474.0 422.0 458.0 445.0 437.0 484.0 402.0 515.0 360.0 535.0 320.0 540.0 300.0 565.0 290.0 595.0 280.0 16 | 13 13 501.0 538.0 506.0 525.0 512.0 513.0 520.0 503.0 528.0 496.0 537.0 486.0 548.0 476.0 565.0 467.0 577.0 459.0 587.0 452.0 595.0 443.0 610.0 430.0 630.0 410.0 17 | 14 19 405.0 585.0 400.0 575.0 391.0 565.0 383.0 555.0 383.0 543.0 383.0 534.0 382.0 526.0 379.0 517.0 375.0 508.0 374.0 500.0 375.0 491.0 384.0 474.0 393.0 458.0 401.0 443.0 419.0 435.0 445.0 420.0 474.0 395.0 500.0 360.0 505.0 340.0 18 | 15 55 150.0 525.0 147.0 515.0 147.0 507.0 148.0 499.0 148.0 490.0 150.0 485.0 150.0 484.0 151.0 482.0 151.0 486.0 150.0 489.0 144.0 491.0 139.0 493.0 138.0 496.0 139.0 498.0 141.0 501.0 142.0 504.0 142.0 507.0 144.0 511.0 148.0 514.0 153.0 514.0 157.0 513.0 165.0 512.0 177.0 509.0 195.0 500.0 206.0 493.0 216.0 490.0 226.0 487.0 239.0 482.0 253.0 476.0 264.0 472.0 274.0 469.0 284.0 466.0 293.0 463.0 304.0 460.0 315.0 457.0 321.0 454.0 323.0 453.0 324.0 454.0 328.0 454.0 330.0 451.0 331.0 445.0 332.0 441.0 334.0 437.0 338.0 434.0 341.0 432.0 345.0 428.0 346.0 422.0 355.0 414.0 366.0 407.0 379.0 400.0 392.0 394.0 406.0 387.0 426.0 380.0 454.0 373.0 490.0 365.0 19 | 16 24 252.0 543.0 251.0 538.0 244.0 536.0 237.0 534.0 230.0 531.0 228.0 532.0 225.0 533.0 222.0 534.0 223.0 531.0 225.0 529.0 227.0 527.0 233.0 525.0 236.0 519.0 236.0 514.0 237.0 505.0 236.0 494.0 231.0 482.0 223.0 473.0 215.0 467.0 209.0 460.0 203.0 453.0 202.0 444.0 201.0 434.0 200.0 425.0 20 | 17 32 730.0 672.0 707.0 671.0 684.0 674.0 661.0 677.0 637.0 677.0 612.0 676.0 587.0 676.0 561.0 673.0 528.0 673.0 494.0 669.0 472.0 667.0 454.0 663.0 436.0 658.0 418.0 656.0 396.0 654.0 371.0 652.0 348.0 647.0 328.0 647.0 316.0 646.0 304.0 643.0 289.0 638.0 274.0 633.0 259.0 629.0 244.0 625.0 233.0 621.0 223.0 619.0 219.0 618.0 213.0 616.0 205.0 614.0 197.0 613.0 188.0 616.0 178.0 621.0 21 | 18 29 457.0 621.0 441.0 609.0 426.0 602.0 411.0 593.0 402.0 584.0 398.0 570.0 392.0 554.0 393.0 534.0 393.0 514.0 398.0 497.0 406.0 485.0 421.0 473.0 435.0 460.0 440.0 450.0 440.0 450.0 440.0 450.0 445.0 448.0 450.0 445.0 467.0 437.0 480.0 423.0 493.0 411.0 506.0 394.0 525.0 379.0 540.0 365.0 566.0 360.0 595.0 345.0 615.0 330.0 650.0 310.0 691.0 290.0 22 | 19 62 757.0 675.0 741.0 671.0 725.0 668.0 710.0 665.0 694.0 659.0 678.0 653.0 663.0 647.0 647.0 641.0 629.0 637.0 613.0 633.0 597.0 630.0 580.0 630.0 566.0 628.0 552.0 626.0 541.0 622.0 530.0 619.0 521.0 610.0 519.0 599.0 518.0 588.0 517.0 578.0 516.0 568.0 519.0 561.0 524.0 556.0 522.0 551.0 520.0 547.0 517.0 540.0 512.0 535.0 508.0 528.0 502.0 516.0 496.0 503.0 487.0 494.0 478.0 485.0 472.0 476.0 467.0 467.0 459.0 458.0 453.0 451.0 446.0 443.0 443.0 436.0 441.0 431.0 444.0 426.0 449.0 422.0 457.0 420.0 467.0 420.0 474.0 421.0 480.0 421.0 486.0 419.0 490.0 417.0 494.0 412.0 498.0 404.0 507.0 394.0 519.0 380.0 533.0 367.0 548.0 353.0 573.0 333.0 590.0 310.0 610.0 280.0 625.0 250.0 640.0 220.0 655.0 200.0 680.0 185.0 711.0 170.0 750.0 150.0 23 | 20 46 816.0 661.0 805.0 656.0 790.0 652.0 770.0 650.0 749.0 650.0 730.0 650.0 713.0 650.0 698.0 650.0 683.0 650.0 669.0 648.0 663.0 640.0 652.0 635.0 642.0 631.0 632.0 627.0 626.0 622.0 623.0 614.0 620.0 606.0 616.0 598.0 612.0 590.0 611.0 580.0 610.0 571.0 611.0 561.0 614.0 552.0 616.0 541.0 620.0 529.0 628.0 518.0 634.0 507.0 645.0 499.0 657.0 491.0 672.0 488.0 686.0 485.0 705.0 483.0 724.0 479.0 740.0 479.0 752.0 474.0 767.0 466.0 776.0 457.0 780.0 449.0 783.0 446.0 785.0 447.0 782.0 448.0 780.0 450.0 780.0 452.0 781.0 453.0 783.0 453.0 784.0 454.0 24 | 21 41 259.0 542.0 254.0 543.0 250.0 543.0 247.0 544.0 246.0 546.0 243.0 549.0 247.0 548.0 251.0 547.0 255.0 546.0 259.0 546.0 264.0 542.0 274.0 529.0 282.0 520.0 286.0 514.0 291.0 514.0 299.0 511.0 310.0 502.0 321.0 496.0 332.0 492.0 351.0 484.0 372.0 477.0 393.0 469.0 415.0 460.0 434.0 447.0 453.0 438.0 471.0 431.0 482.0 421.0 496.0 410.0 511.0 398.0 525.0 385.0 545.0 372.0 560.0 365.0 580.0 335.0 615.0 315.0 640.0 300.0 670.0 280.0 700.0 260.0 725.0 250.0 750.0 250.0 775.0 240.0 790.0 225.0 25 | 22 26 458.0 549.0 450.0 542.0 445.0 535.0 441.0 528.0 437.0 521.0 433.0 514.0 430.0 507.0 427.0 499.0 425.0 490.0 426.0 481.0 429.0 472.0 432.0 462.0 437.0 450.0 446.0 441.0 457.0 432.0 466.0 423.0 476.0 415.0 484.0 408.0 493.0 404.0 497.0 396.0 508.0 390.0 525.0 382.0 554.0 378.0 587.0 376.0 625.0 378.0 655.0 385.0 26 | 23 21 518.0 677.0 501.0 672.0 486.0 666.0 470.0 660.0 455.0 652.0 440.0 645.0 423.0 643.0 410.0 640.0 395.0 635.0 378.0 631.0 360.0 631.0 347.0 631.0 330.0 627.0 311.0 626.0 294.0 627.0 277.0 628.0 260.0 629.0 240.0 632.0 220.0 635.0 201.0 638.0 181.0 638.0 27 | 24 10 594.0 694.0 579.0 692.0 562.0 690.0 543.0 687.0 523.0 683.0 505.0 675.0 498.0 669.0 480.0 662.0 460.0 658.0 440.0 655.0 28 | 25 19 470.0 525.0 475.0 502.0 465.0 492.0 453.0 482.0 442.0 478.0 442.0 472.0 437.0 465.0 439.0 458.0 447.0 451.0 452.0 438.0 454.0 427.0 460.0 421.0 463.0 414.0 461.0 407.0 459.0 399.0 456.0 391.0 450.0 377.0 452.0 358.0 480.0 320.0 29 | 26 16 751.0 528.0 739.0 525.0 726.0 525.0 713.0 525.0 702.0 525.0 690.0 525.0 687.0 528.0 681.0 523.0 673.0 522.0 663.0 520.0 654.0 515.0 645.0 506.0 644.0 491.0 640.0 472.0 651.0 451.0 669.0 426.0 30 | 27 33 264.0 667.0 264.0 663.0 259.0 663.0 261.0 667.0 265.0 670.0 264.0 663.0 266.0 657.0 268.0 652.0 269.0 647.0 269.0 642.0 266.0 635.0 264.0 632.0 262.0 630.0 261.0 627.0 261.0 625.0 260.0 622.0 260.0 620.0 260.0 615.0 257.0 612.0 257.0 611.0 259.0 607.0 263.0 603.0 267.0 599.0 275.0 592.0 282.0 585.0 291.0 577.0 303.0 569.0 320.0 557.0 336.0 546.0 355.0 537.0 374.0 531.0 403.0 527.0 435.0 513.0 31 | 28 12 596.0 461.0 596.0 459.0 596.0 457.0 597.0 448.0 599.0 439.0 599.0 430.0 597.0 422.0 598.0 412.0 603.0 403.0 608.0 387.0 615.0 370.0 620.0 350.0 32 | 29 47 590.0 495.0 590.0 498.0 593.0 503.0 597.0 507.0 600.0 507.0 602.0 505.0 605.0 497.0 594.0 487.0 580.0 482.0 565.0 483.0 550.0 492.0 547.0 497.0 544.0 499.0 541.0 494.0 540.0 489.0 538.0 479.0 530.0 474.0 528.0 485.0 540.0 480.0 542.0 477.0 543.0 474.0 538.0 476.0 530.0 486.0 524.0 497.0 513.0 507.0 499.0 516.0 482.0 527.0 468.0 538.0 453.0 547.0 438.0 555.0 429.0 563.0 423.0 566.0 420.0 569.0 417.0 572.0 414.0 570.0 411.0 566.0 411.0 557.0 408.0 545.0 405.0 536.0 403.0 530.0 401.0 526.0 401.0 522.0 404.0 523.0 409.0 523.0 418.0 522.0 420.0 522.0 426.0 530.0 33 | 30 20 332.0 457.0 349.0 450.0 367.0 445.0 390.0 439.0 418.0 432.0 449.0 421.0 483.0 414.0 521.0 395.0 555.0 375.0 570.0 355.0 561.0 335.0 560.0 320.0 570.0 310.0 575.0 295.0 575.0 270.0 570.0 245.0 570.0 220.0 580.0 230.0 585.0 235.0 590.0 240.0 34 | 31 20 215.0 510.0 208.0 507.0 203.0 504.0 199.0 495.0 194.0 491.0 190.0 491.0 186.0 492.0 185.0 494.0 184.0 497.0 183.0 500.0 178.0 503.0 173.0 506.0 168.0 510.0 162.0 514.0 156.0 518.0 147.0 523.0 139.0 531.0 134.0 530.0 129.0 528.0 123.0 525.0 35 | 32 13 328.0 480.0 329.0 485.0 330.0 489.0 334.0 493.0 336.0 496.0 338.0 499.0 338.0 503.0 340.0 506.0 343.0 503.0 347.0 503.0 351.0 503.0 360.0 501.0 380.0 490.0 36 | 33 26 788.0 705.0 778.0 703.0 769.0 703.0 755.0 700.0 737.0 697.0 723.0 692.0 709.0 685.0 691.0 679.0 671.0 672.0 651.0 668.0 632.0 664.0 616.0 659.0 601.0 652.0 588.0 644.0 580.0 636.0 575.0 629.0 571.0 621.0 567.0 615.0 563.0 607.0 563.0 604.0 571.0 590.0 576.0 578.0 576.0 569.0 576.0 565.0 575.0 565.0 575.0 560.0 37 | 34 20 312.0 508.0 309.0 503.0 303.0 499.0 304.0 496.0 310.0 495.0 314.0 495.0 315.0 496.0 316.0 496.0 310.0 500.0 309.0 501.0 305.0 501.0 302.0 501.0 295.0 503.0 290.0 506.0 284.0 508.0 276.0 511.0 266.0 514.0 261.0 516.0 253.0 517.0 240.0 515.0 38 | 35 22 162.0 520.0 161.0 522.0 159.0 524.0 155.0 523.0 153.0 522.0 150.0 521.0 144.0 519.0 131.0 509.0 115.0 511.0 111.0 511.0 107.0 513.0 105.0 510.0 109.0 511.0 108.0 515.0 108.0 517.0 106.0 519.0 104.0 522.0 105.0 526.0 107.0 530.0 102.0 532.0 96.0 534.0 90.0 535.0 39 | 36 27 390.0 510.0 389.0 498.0 377.0 495.0 374.0 488.0 364.0 484.0 355.0 481.0 345.0 479.0 345.0 470.0 346.0 463.0 346.0 450.0 353.0 445.0 370.0 432.0 392.0 420.0 432.0 397.0 472.0 369.0 504.0 335.0 523.0 314.0 545.0 299.0 560.0 290.0 575.0 275.0 586.0 255.0 605.0 240.0 585.0 230.0 575.0 220.0 565.0 205.0 560.0 190.0 555.0 175.0 40 | 37 15 234.0 537.0 236.0 533.0 233.0 530.0 225.0 529.0 220.0 533.0 212.0 531.0 207.0 526.0 207.0 523.0 208.0 520.0 209.0 513.0 212.0 500.0 220.0 492.0 230.0 485.0 240.0 480.0 250.0 470.0 41 | 38 53 495.0 700.0 476.0 698.0 457.0 694.0 436.0 688.0 415.0 677.0 395.0 658.0 381.0 650.0 368.0 645.0 355.0 643.0 347.0 642.0 339.0 642.0 332.0 641.0 326.0 637.0 323.0 633.0 319.0 629.0 315.0 623.0 314.0 613.0 307.0 607.0 296.0 603.0 291.0 601.0 283.0 596.0 277.0 593.0 270.0 589.0 265.0 585.0 260.0 582.0 257.0 579.0 249.0 577.0 242.0 580.0 239.0 581.0 235.0 579.0 226.0 580.0 218.0 581.0 211.0 584.0 206.0 590.0 204.0 594.0 204.0 599.0 206.0 597.0 207.0 595.0 205.0 590.0 203.0 583.0 202.0 579.0 203.0 570.0 203.0 558.0 198.0 546.0 196.0 537.0 197.0 525.0 197.0 509.0 201.0 500.0 202.0 478.0 210.0 470.0 235.0 450.0 271.0 409.0 305.0 391.0 42 | 39 9 576.0 473.0 573.0 468.0 573.0 463.0 573.0 453.0 580.0 445.0 595.0 434.0 612.0 420.0 633.0 405.0 667.0 385.0 43 | 40 88 582.0 517.0 586.0 507.0 584.0 496.0 584.0 490.0 587.0 483.0 591.0 476.0 595.0 472.0 601.0 468.0 605.0 467.0 609.0 468.0 606.0 473.0 605.0 477.0 604.0 481.0 600.0 486.0 596.0 490.0 593.0 494.0 589.0 497.0 579.0 500.0 563.0 503.0 556.0 507.0 547.0 510.0 534.0 514.0 525.0 518.0 517.0 521.0 510.0 522.0 505.0 525.0 500.0 529.0 496.0 530.0 492.0 532.0 486.0 535.0 477.0 537.0 471.0 535.0 468.0 533.0 463.0 529.0 456.0 528.0 453.0 523.0 453.0 519.0 454.0 513.0 453.0 509.0 445.0 510.0 433.0 522.0 432.0 518.0 432.0 515.0 428.0 515.0 425.0 515.0 423.0 516.0 422.0 515.0 424.0 512.0 431.0 510.0 433.0 509.0 434.0 510.0 428.0 510.0 423.0 507.0 420.0 506.0 416.0 504.0 405.0 499.0 398.0 494.0 391.0 489.0 384.0 484.0 387.0 477.0 392.0 471.0 399.0 468.0 403.0 464.0 403.0 467.0 403.0 472.0 403.0 474.0 396.0 474.0 392.0 474.0 391.0 473.0 390.0 474.0 388.0 479.0 387.0 486.0 381.0 493.0 376.0 502.0 367.0 510.0 360.0 513.0 352.0 515.0 342.0 516.0 330.0 517.0 319.0 516.0 305.0 511.0 298.0 502.0 292.0 493.0 293.0 482.0 307.0 468.0 320.0 458.0 341.0 446.0 359.0 433.0 44 | 41 52 654.0 698.0 635.0 697.0 615.0 692.0 596.0 688.0 578.0 682.0 554.0 679.0 532.0 678.0 513.0 676.0 496.0 675.0 479.0 673.0 463.0 672.0 451.0 670.0 440.0 668.0 431.0 665.0 425.0 663.0 418.0 660.0 411.0 658.0 402.0 655.0 390.0 651.0 378.0 647.0 370.0 644.0 365.0 643.0 360.0 641.0 354.0 639.0 350.0 633.0 349.0 626.0 344.0 625.0 346.0 619.0 343.0 615.0 339.0 612.0 332.0 612.0 328.0 613.0 324.0 613.0 319.0 610.0 313.0 609.0 304.0 604.0 297.0 600.0 289.0 595.0 278.0 590.0 268.0 584.0 256.0 576.0 243.0 570.0 228.0 564.0 217.0 556.0 205.0 546.0 197.0 533.0 186.0 519.0 179.0 508.0 176.0 495.0 179.0 481.0 186.0 465.0 200.0 442.0 45 | 42 39 438.0 509.0 440.0 501.0 442.0 492.0 437.0 481.0 431.0 470.0 424.0 462.0 414.0 455.0 409.0 457.0 410.0 464.0 411.0 473.0 418.0 480.0 427.0 483.0 436.0 486.0 442.0 494.0 452.0 500.0 469.0 506.0 485.0 506.0 499.0 505.0 515.0 504.0 534.0 503.0 553.0 500.0 570.0 494.0 585.0 486.0 594.0 481.0 604.0 479.0 613.0 480.0 622.0 482.0 634.0 485.0 646.0 485.0 658.0 484.0 669.0 483.0 679.0 482.0 692.0 481.0 716.0 477.0 744.0 471.0 772.0 464.0 808.0 456.0 842.0 447.0 876.0 439.0 46 | 43 20 210.0 605.0 207.0 600.0 206.0 597.0 202.0 590.0 200.0 580.0 195.0 566.0 189.0 554.0 187.0 541.0 185.0 529.0 185.0 518.0 190.0 509.0 197.0 496.0 207.0 484.0 216.0 473.0 230.0 464.0 240.0 459.0 255.0 450.0 265.0 444.0 280.0 435.0 305.0 427.0 47 | 44 43 565.0 689.0 547.0 682.0 525.0 674.0 502.0 668.0 480.0 663.0 452.0 660.0 424.0 656.0 400.0 652.0 380.0 650.0 356.0 649.0 335.0 647.0 314.0 642.0 297.0 639.0 283.0 634.0 272.0 625.0 259.0 614.0 245.0 603.0 237.0 596.0 228.0 589.0 218.0 582.0 208.0 574.0 198.0 567.0 193.0 561.0 191.0 554.0 185.0 551.0 181.0 551.0 179.0 549.0 178.0 547.0 178.0 544.0 177.0 540.0 174.0 533.0 170.0 527.0 164.0 523.0 154.0 521.0 145.0 517.0 131.0 514.0 118.0 515.0 106.0 515.0 92.0 512.0 74.0 507.0 57.0 501.0 40.0 495.0 23.0 491.0 48 | 45 44 559.0 492.0 553.0 483.0 548.0 475.0 544.0 466.0 536.0 456.0 534.0 447.0 536.0 438.0 540.0 429.0 551.0 419.0 566.0 408.0 583.0 399.0 600.0 389.0 622.0 379.0 642.0 373.0 660.0 373.0 676.0 375.0 693.0 381.0 708.0 391.0 721.0 402.0 732.0 412.0 737.0 421.0 741.0 429.0 742.0 437.0 738.0 443.0 733.0 447.0 728.0 449.0 722.0 450.0 714.0 451.0 710.0 445.0 700.0 440.0 695.0 440.0 695.0 434.0 700.0 435.0 705.0 436.0 711.0 435.0 708.0 437.0 710.0 440.0 710.0 445.0 705.0 455.0 700.0 462.0 695.0 470.0 690.0 480.0 680.0 490.0 665.0 490.0 49 | 46 10 267.0 536.0 243.0 534.0 217.0 536.0 195.0 539.0 175.0 540.0 156.0 541.0 138.0 544.0 124.0 548.0 111.0 552.0 97.0 553.0 50 | 47 53 793.0 654.0 777.0 653.0 758.0 651.0 738.0 650.0 718.0 649.0 699.0 648.0 682.0 646.0 669.0 645.0 657.0 642.0 644.0 637.0 631.0 633.0 620.0 630.0 608.0 627.0 597.0 624.0 586.0 622.0 575.0 620.0 565.0 618.0 557.0 617.0 547.0 615.0 537.0 613.0 527.0 610.0 517.0 607.0 508.0 604.0 501.0 599.0 493.0 595.0 487.0 591.0 482.0 587.0 477.0 581.0 472.0 575.0 470.0 568.0 467.0 562.0 462.0 552.0 457.0 542.0 454.0 529.0 449.0 514.0 447.0 499.0 447.0 482.0 453.0 466.0 460.0 451.0 476.0 440.0 496.0 431.0 518.0 421.0 543.0 409.0 566.0 394.0 593.0 377.0 621.0 357.0 653.0 337.0 692.0 313.0 740.0 283.0 780.0 260.0 810.0 240.0 830.0 220.0 830.0 200.0 51 | 48 12 176.0 557.0 167.0 551.0 157.0 542.0 151.0 528.0 148.0 506.0 144.0 499.0 136.0 491.0 134.0 481.0 135.0 474.0 143.0 464.0 150.0 459.0 155.0 454.0 52 | 49 21 223.0 526.0 222.0 524.0 224.0 522.0 228.0 522.0 237.0 522.0 242.0 524.0 249.0 523.0 256.0 519.0 258.0 517.0 261.0 521.0 265.0 523.0 282.0 516.0 296.0 508.0 305.0 500.0 308.0 495.0 315.0 492.0 321.0 487.0 329.0 483.0 335.0 479.0 341.0 475.0 347.0 471.0 53 | 50 57 786.0 662.0 773.0 661.0 761.0 664.0 751.0 666.0 742.0 665.0 735.0 661.0 727.0 656.0 715.0 648.0 703.0 642.0 691.0 635.0 680.0 629.0 669.0 624.0 659.0 618.0 648.0 611.0 637.0 606.0 627.0 600.0 617.0 595.0 606.0 591.0 596.0 589.0 586.0 589.0 577.0 588.0 568.0 587.0 560.0 586.0 552.0 585.0 543.0 584.0 534.0 583.0 526.0 584.0 518.0 583.0 509.0 582.0 499.0 581.0 490.0 579.0 479.0 575.0 467.0 571.0 454.0 568.0 442.0 565.0 430.0 561.0 421.0 557.0 412.0 555.0 406.0 552.0 402.0 547.0 398.0 543.0 395.0 537.0 391.0 532.0 388.0 526.0 385.0 519.0 381.0 511.0 375.0 503.0 370.0 494.0 365.0 485.0 357.0 475.0 348.0 463.0 336.0 449.0 323.0 433.0 311.0 414.0 297.0 391.0 291.0 361.0 290.0 320.0 54 | 51 21 476.0 518.0 478.0 517.0 480.0 516.0 483.0 512.0 486.0 508.0 484.0 496.0 482.0 491.0 482.0 489.0 480.0 482.0 480.0 475.0 480.0 467.0 478.0 456.0 472.0 448.0 468.0 445.0 464.0 437.0 460.0 429.0 459.0 415.0 459.0 398.0 461.0 372.0 462.0 340.0 476.0 302.0 55 | 52 58 723.0 682.0 715.0 672.0 706.0 660.0 698.0 648.0 690.0 634.0 682.0 620.0 670.0 608.0 662.0 600.0 658.0 590.0 655.0 580.0 657.0 570.0 664.0 561.0 674.0 552.0 687.0 539.0 701.0 528.0 718.0 517.0 732.0 508.0 746.0 500.0 752.0 492.0 749.0 482.0 740.0 474.0 729.0 475.0 717.0 476.0 704.0 478.0 691.0 483.0 679.0 489.0 670.0 497.0 664.0 499.0 653.0 501.0 642.0 504.0 631.0 506.0 622.0 506.0 609.0 504.0 597.0 501.0 584.0 499.0 572.0 499.0 560.0 498.0 553.0 497.0 545.0 496.0 539.0 493.0 536.0 486.0 538.0 477.0 540.0 465.0 544.0 448.0 550.0 431.0 560.0 414.0 579.0 395.0 605.0 362.0 628.0 325.0 660.0 280.0 695.0 235.0 730.0 205.0 766.0 189.0 813.0 179.0 860.0 175.0 910.0 174.0 974.0 175.0 1040.0 170.0 56 | 53 41 253.0 615.0 251.0 613.0 247.0 610.0 240.0 607.0 231.0 603.0 225.0 600.0 218.0 597.0 211.0 594.0 204.0 590.0 195.0 588.0 185.0 586.0 179.0 587.0 175.0 588.0 171.0 591.0 171.0 591.0 170.0 590.0 168.0 590.0 166.0 591.0 166.0 593.0 166.0 595.0 164.0 596.0 160.0 595.0 157.0 597.0 155.0 599.0 153.0 601.0 155.0 604.0 160.0 605.0 162.0 607.0 162.0 612.0 164.0 614.0 165.0 615.0 165.0 616.0 163.0 618.0 162.0 621.0 161.0 625.0 159.0 628.0 157.0 631.0 155.0 634.0 153.0 637.0 150.0 640.0 147.0 642.0 57 | 54 14 412.0 609.0 403.0 599.0 396.0 591.0 387.0 582.0 385.0 573.0 381.0 564.0 376.0 560.0 378.0 553.0 380.0 545.0 390.0 544.0 397.0 543.0 407.0 542.0 417.0 541.0 423.0 541.0 58 | 55 80 730.0 705.0 723.0 705.0 716.0 704.0 711.0 701.0 705.0 698.0 697.0 695.0 689.0 693.0 681.0 691.0 675.0 688.0 669.0 685.0 663.0 682.0 657.0 678.0 650.0 674.0 644.0 669.0 640.0 665.0 634.0 660.0 628.0 656.0 624.0 650.0 620.0 645.0 619.0 639.0 619.0 637.0 621.0 637.0 622.0 635.0 623.0 633.0 624.0 630.0 626.0 627.0 627.0 624.0 625.0 624.0 623.0 626.0 621.0 624.0 619.0 622.0 611.0 619.0 603.0 616.0 597.0 614.0 590.0 613.0 580.0 614.0 571.0 615.0 564.0 615.0 559.0 612.0 554.0 605.0 546.0 596.0 540.0 585.0 536.0 575.0 531.0 564.0 533.0 549.0 536.0 529.0 539.0 512.0 543.0 498.0 552.0 486.0 565.0 478.0 580.0 476.0 595.0 475.0 605.0 481.0 609.0 493.0 610.0 507.0 604.0 517.0 595.0 526.0 580.0 533.0 563.0 536.0 545.0 534.0 527.0 529.0 509.0 521.0 492.0 511.0 472.0 505.0 449.0 506.0 429.0 507.0 420.0 505.0 420.0 499.0 428.0 496.0 436.0 496.0 442.0 500.0 447.0 507.0 445.0 513.0 440.0 517.0 437.0 523.0 436.0 530.0 437.0 536.0 440.0 535.0 437.0 530.0 430.0 523.0 59 | 56 22 338.0 671.0 343.0 667.0 348.0 663.0 352.0 661.0 356.0 659.0 362.0 657.0 370.0 650.0 375.0 641.0 378.0 634.0 380.0 628.0 386.0 621.0 394.0 611.0 405.0 595.0 419.0 576.0 441.0 565.0 463.0 556.0 484.0 546.0 505.0 537.0 529.0 526.0 558.0 511.0 589.0 496.0 623.0 481.0 60 | 57 15 755.0 525.0 752.0 539.0 742.0 559.0 732.0 576.0 721.0 593.0 723.0 596.0 726.0 600.0 728.0 605.0 730.0 590.0 730.0 578.0 729.0 569.0 729.0 562.0 733.0 550.0 738.0 540.0 750.0 525.0 61 | 58 25 317.0 497.0 312.0 490.0 310.0 485.0 309.0 484.0 308.0 484.0 307.0 485.0 306.0 486.0 310.0 487.0 313.0 482.0 318.0 476.0 326.0 470.0 336.0 458.0 348.0 447.0 363.0 440.0 379.0 432.0 398.0 427.0 417.0 422.0 440.0 415.0 469.0 405.0 504.0 392.0 550.0 373.0 607.0 355.0 658.0 339.0 725.0 330.0 773.0 326.0 62 | 59 43 564.0 671.0 546.0 668.0 526.0 665.0 505.0 664.0 484.0 664.0 463.0 663.0 443.0 660.0 423.0 651.0 403.0 640.0 385.0 635.0 370.0 630.0 354.0 629.0 338.0 628.0 328.0 630.0 318.0 629.0 308.0 626.0 298.0 622.0 289.0 616.0 281.0 610.0 273.0 603.0 265.0 597.0 257.0 591.0 245.0 585.0 234.0 580.0 224.0 575.0 217.0 571.0 210.0 569.0 202.0 566.0 194.0 560.0 194.0 556.0 194.0 553.0 194.0 548.0 196.0 543.0 204.0 536.0 212.0 530.0 219.0 523.0 239.0 510.0 260.0 498.0 287.0 481.0 310.0 465.0 335.0 445.0 351.0 429.0 365.0 410.0 63 | 60 25 508.0 686.0 489.0 683.0 468.0 678.0 447.0 671.0 424.0 662.0 402.0 651.0 382.0 644.0 363.0 640.0 346.0 637.0 332.0 633.0 319.0 626.0 307.0 618.0 293.0 608.0 284.0 595.0 278.0 583.0 274.0 570.0 271.0 556.0 276.0 539.0 284.0 519.0 292.0 499.0 303.0 477.0 319.0 455.0 341.0 431.0 362.0 407.0 389.0 388.0 64 | 61 46 882.0 677.0 870.0 676.0 858.0 674.0 845.0 671.0 832.0 668.0 819.0 665.0 807.0 662.0 792.0 659.0 779.0 653.0 765.0 648.0 752.0 640.0 740.0 632.0 728.0 623.0 718.0 610.0 711.0 597.0 704.0 583.0 700.0 567.0 697.0 554.0 694.0 541.0 697.0 527.0 702.0 519.0 711.0 511.0 722.0 507.0 723.0 503.0 723.0 501.0 728.0 501.0 732.0 502.0 730.0 498.0 728.0 495.0 724.0 491.0 720.0 491.0 714.0 494.0 711.0 493.0 708.0 495.0 702.0 497.0 697.0 498.0 692.0 497.0 688.0 496.0 680.0 492.0 675.0 483.0 667.0 477.0 662.0 469.0 658.0 461.0 654.0 453.0 650.0 445.0 648.0 436.0 65 | 62 9 644.0 710.0 626.0 708.0 608.0 704.0 586.0 699.0 565.0 695.0 541.0 690.0 517.0 685.0 492.0 682.0 472.0 679.0 66 | 63 68 748.0 689.0 732.0 688.0 717.0 687.0 702.0 685.0 685.0 681.0 671.0 677.0 655.0 672.0 642.0 667.0 632.0 663.0 622.0 658.0 615.0 653.0 607.0 646.0 600.0 641.0 591.0 634.0 584.0 628.0 577.0 623.0 571.0 619.0 564.0 616.0 556.0 614.0 550.0 612.0 542.0 611.0 532.0 610.0 519.0 608.0 507.0 606.0 493.0 604.0 479.0 602.0 465.0 600.0 450.0 597.0 436.0 594.0 421.0 590.0 409.0 586.0 396.0 582.0 386.0 578.0 375.0 573.0 365.0 568.0 357.0 562.0 350.0 558.0 343.0 553.0 337.0 547.0 329.0 543.0 325.0 540.0 321.0 536.0 315.0 532.0 309.0 532.0 306.0 530.0 298.0 528.0 293.0 526.0 283.0 522.0 277.0 519.0 267.0 514.0 263.0 508.0 260.0 499.0 254.0 490.0 250.0 485.0 248.0 480.0 249.0 475.0 253.0 466.0 257.0 457.0 268.0 439.0 279.0 422.0 287.0 413.0 300.0 395.0 323.0 372.0 343.0 357.0 367.0 347.0 395.0 338.0 427.0 334.0 458.0 328.0 67 | 64 27 319.0 482.0 320.0 484.0 320.0 486.0 318.0 487.0 315.0 487.0 312.0 487.0 309.0 485.0 306.0 479.0 305.0 472.0 304.0 465.0 306.0 458.0 309.0 451.0 313.0 444.0 322.0 436.0 334.0 428.0 352.0 419.0 371.0 409.0 395.0 398.0 425.0 390.0 465.0 383.0 504.0 370.0 550.0 350.0 600.0 330.0 660.0 315.0 720.0 300.0 780.0 289.0 811.0 280.0 68 | 65 16 448.0 510.0 443.0 510.0 438.0 509.0 434.0 508.0 428.0 503.0 416.0 499.0 408.0 495.0 402.0 489.0 395.0 482.0 389.0 474.0 385.0 462.0 385.0 442.0 386.0 424.0 387.0 401.0 391.0 385.0 395.0 369.0 69 | 66 87 824.0 703.0 813.0 703.0 797.0 703.0 779.0 705.0 764.0 707.0 750.0 709.0 735.0 711.0 718.0 711.0 701.0 710.0 686.0 707.0 666.0 705.0 649.0 702.0 632.0 698.0 615.0 694.0 595.0 692.0 575.0 690.0 556.0 687.0 539.0 688.0 522.0 687.0 506.0 684.0 489.0 682.0 474.0 680.0 459.0 677.0 445.0 674.0 430.0 670.0 417.0 667.0 405.0 663.0 392.0 658.0 381.0 653.0 372.0 648.0 362.0 643.0 353.0 638.0 342.0 632.0 335.0 627.0 324.0 626.0 316.0 623.0 310.0 620.0 304.0 618.0 296.0 616.0 288.0 612.0 279.0 609.0 272.0 605.0 265.0 601.0 259.0 596.0 253.0 591.0 249.0 584.0 244.0 576.0 240.0 570.0 235.0 563.0 230.0 553.0 226.0 544.0 221.0 533.0 218.0 521.0 218.0 511.0 221.0 500.0 223.0 486.0 226.0 475.0 235.0 462.0 243.0 453.0 260.0 446.0 277.0 438.0 295.0 430.0 315.0 423.0 333.0 416.0 345.0 420.0 360.0 425.0 360.0 440.0 355.0 455.0 342.0 472.0 325.0 490.0 315.0 510.0 313.0 525.0 309.0 536.0 303.0 539.0 294.0 541.0 283.0 542.0 272.0 548.0 259.0 552.0 239.0 549.0 227.0 540.0 214.0 535.0 205.0 529.0 190.0 521.0 178.0 511.0 173.0 508.0 168.0 504.0 158.0 499.0 70 | 67 64 500.0 641.0 493.0 640.0 485.0 637.0 474.0 636.0 465.0 633.0 460.0 629.0 452.0 628.0 446.0 624.0 438.0 619.0 429.0 615.0 422.0 614.0 415.0 614.0 410.0 612.0 406.0 608.0 401.0 606.0 393.0 603.0 384.0 600.0 380.0 598.0 375.0 596.0 372.0 588.0 377.0 583.0 378.0 578.0 377.0 572.0 375.0 565.0 377.0 558.0 379.0 552.0 380.0 545.0 383.0 534.0 386.0 528.0 392.0 526.0 398.0 524.0 405.0 524.0 408.0 526.0 411.0 528.0 413.0 532.0 415.0 535.0 412.0 538.0 410.0 543.0 407.0 545.0 404.0 545.0 400.0 542.0 396.0 540.0 388.0 539.0 378.0 538.0 369.0 536.0 357.0 536.0 344.0 535.0 331.0 534.0 318.0 531.0 306.0 529.0 294.0 527.0 284.0 523.0 277.0 517.0 273.0 507.0 267.0 499.0 261.0 489.0 264.0 475.0 268.0 468.0 276.0 459.0 290.0 446.0 316.0 427.0 336.0 417.0 353.0 412.0 375.0 415.0 71 | 68 48 808.0 688.0 793.0 688.0 779.0 688.0 767.0 688.0 756.0 684.0 747.0 679.0 740.0 670.0 730.0 661.0 720.0 655.0 712.0 650.0 703.0 644.0 692.0 641.0 684.0 640.0 676.0 637.0 666.0 633.0 655.0 632.0 648.0 630.0 640.0 625.0 635.0 619.0 630.0 613.0 627.0 604.0 622.0 592.0 617.0 577.0 612.0 563.0 606.0 551.0 605.0 538.0 613.0 525.0 623.0 510.0 637.0 493.0 656.0 475.0 670.0 455.0 681.0 432.0 688.0 405.0 695.0 374.0 695.0 345.0 695.0 319.0 711.0 296.0 735.0 270.0 780.0 239.0 840.0 205.0 930.0 180.0 1020.0 165.0 1080.0 160.0 1125.0 160.0 1170.0 155.0 1205.0 150.0 1220.0 147.0 1235.0 145.0 72 | 69 55 776.0 667.0 767.0 667.0 757.0 666.0 746.0 665.0 736.0 663.0 726.0 663.0 717.0 661.0 709.0 659.0 703.0 657.0 698.0 656.0 694.0 656.0 689.0 658.0 685.0 661.0 686.0 668.0 694.0 670.0 700.0 669.0 695.0 657.0 683.0 657.0 676.0 662.0 668.0 661.0 662.0 660.0 656.0 657.0 651.0 651.0 646.0 643.0 643.0 633.0 642.0 626.0 641.0 620.0 640.0 613.0 640.0 608.0 640.0 604.0 639.0 600.0 638.0 594.0 638.0 587.0 636.0 580.0 635.0 570.0 635.0 557.0 635.0 546.0 636.0 536.0 638.0 525.0 642.0 511.0 642.0 499.0 639.0 489.0 633.0 480.0 627.0 476.0 622.0 469.0 622.0 459.0 624.0 449.0 630.0 437.0 642.0 423.0 662.0 411.0 684.0 397.0 709.0 384.0 740.0 372.0 770.0 362.0 800.0 355.0 73 | 70 12 146.0 560.0 158.0 559.0 163.0 554.0 168.0 547.0 172.0 537.0 180.0 532.0 186.0 527.0 188.0 519.0 191.0 506.0 191.0 496.0 190.0 480.0 181.0 464.0 74 | 71 8 448.0 500.0 437.0 490.0 435.0 485.0 435.0 479.0 445.0 471.0 461.0 457.0 482.0 440.0 495.0 415.0 75 | 72 39 700.0 520.0 690.0 527.0 681.0 535.0 675.0 532.0 668.0 532.0 662.0 533.0 657.0 536.0 656.0 538.0 659.0 537.0 661.0 526.0 660.0 518.0 649.0 513.0 635.0 512.0 622.0 510.0 621.0 504.0 612.0 498.0 603.0 493.0 595.0 491.0 590.0 487.0 590.0 483.0 591.0 482.0 592.0 481.0 596.0 484.0 599.0 490.0 601.0 495.0 599.0 501.0 596.0 509.0 599.0 515.0 599.0 521.0 598.0 528.0 597.0 535.0 594.0 538.0 598.0 542.0 599.0 546.0 596.0 550.0 591.0 549.0 589.0 545.0 589.0 541.0 586.0 539.0 76 | 73 23 260.0 631.0 261.0 626.0 261.0 618.0 260.0 610.0 259.0 603.0 258.0 596.0 256.0 588.0 253.0 570.0 249.0 551.0 244.0 535.0 232.0 523.0 228.0 511.0 225.0 499.0 224.0 486.0 223.0 473.0 220.0 450.0 222.0 430.0 225.0 415.0 240.0 395.0 250.0 380.0 260.0 370.0 289.0 363.0 324.0 352.0 77 | 74 6 146.0 603.0 142.0 600.0 136.0 596.0 127.0 592.0 119.0 586.0 115.0 580.0 78 | 75 31 233.0 617.0 228.0 614.0 221.0 607.0 215.0 591.0 210.0 577.0 203.0 561.0 198.0 549.0 196.0 536.0 195.0 524.0 197.0 515.0 200.0 504.0 211.0 492.0 219.0 484.0 228.0 476.0 238.0 468.0 259.0 454.0 282.0 444.0 300.0 429.0 317.0 422.0 333.0 416.0 352.0 409.0 380.0 405.0 393.0 392.0 402.0 384.0 404.0 365.0 402.0 351.0 400.0 345.0 424.0 335.0 436.0 320.0 455.0 315.0 475.0 315.0 79 | 76 55 492.0 680.0 475.0 678.0 458.0 675.0 441.0 670.0 427.0 664.0 415.0 657.0 403.0 653.0 391.0 649.0 381.0 644.0 370.0 638.0 359.0 633.0 351.0 624.0 339.0 615.0 329.0 606.0 316.0 597.0 305.0 591.0 294.0 580.0 284.0 573.0 275.0 566.0 266.0 557.0 258.0 548.0 250.0 539.0 242.0 528.0 237.0 515.0 231.0 501.0 223.0 485.0 215.0 474.0 212.0 461.0 209.0 447.0 208.0 436.0 210.0 429.0 213.0 423.0 217.0 419.0 222.0 415.0 228.0 411.0 235.0 408.0 242.0 408.0 243.0 408.0 244.0 410.0 244.0 413.0 244.0 416.0 242.0 419.0 238.0 421.0 236.0 419.0 234.0 416.0 232.0 414.0 235.0 406.0 238.0 398.0 248.0 392.0 259.0 387.0 268.0 378.0 277.0 369.0 286.0 361.0 295.0 354.0 302.0 342.0 80 | 77 43 676.0 693.0 666.0 692.0 656.0 691.0 646.0 690.0 632.0 690.0 615.0 690.0 598.0 690.0 580.0 690.0 563.0 690.0 546.0 689.0 528.0 688.0 511.0 686.0 498.0 684.0 485.0 681.0 468.0 676.0 451.0 671.0 433.0 667.0 416.0 663.0 399.0 659.0 382.0 655.0 366.0 651.0 350.0 646.0 335.0 641.0 320.0 636.0 305.0 629.0 288.0 623.0 272.0 617.0 257.0 611.0 242.0 605.0 227.0 597.0 211.0 587.0 197.0 580.0 185.0 574.0 172.0 568.0 160.0 563.0 149.0 559.0 139.0 556.0 131.0 554.0 124.0 552.0 113.0 550.0 103.0 550.0 95.0 550.0 89.0 550.0 81 | 78 38 350.0 550.0 341.0 543.0 336.0 538.0 332.0 534.0 333.0 526.0 334.0 519.0 339.0 513.0 346.0 508.0 353.0 503.0 365.0 499.0 374.0 497.0 381.0 493.0 388.0 489.0 388.0 489.0 395.0 490.0 402.0 491.0 404.0 491.0 414.0 491.0 418.0 488.0 419.0 483.0 417.0 478.0 415.0 476.0 412.0 472.0 409.0 468.0 407.0 464.0 406.0 458.0 407.0 450.0 409.0 440.0 420.0 429.0 434.0 416.0 452.0 404.0 472.0 392.0 498.0 378.0 525.0 365.0 554.0 353.0 583.0 342.0 612.0 336.0 640.0 325.0 82 | 79 9 171.0 607.0 162.0 602.0 150.0 592.0 142.0 590.0 134.0 586.0 124.0 582.0 114.0 577.0 100.0 575.0 86.0 575.0 83 | 80 46 412.0 518.0 413.0 511.0 414.0 505.0 417.0 497.0 423.0 491.0 436.0 487.0 450.0 484.0 464.0 481.0 479.0 480.0 495.0 479.0 506.0 476.0 515.0 475.0 523.0 475.0 529.0 474.0 532.0 470.0 533.0 465.0 533.0 465.0 545.0 459.0 551.0 449.0 560.0 440.0 571.0 432.0 581.0 424.0 593.0 414.0 614.0 404.0 631.0 396.0 650.0 388.0 670.0 382.0 689.0 377.0 710.0 370.0 729.0 360.0 748.0 355.0 759.0 355.0 769.0 356.0 778.0 358.0 785.0 361.0 790.0 365.0 793.0 367.0 795.0 370.0 792.0 375.0 786.0 380.0 785.0 387.0 785.0 393.0 785.0 395.0 785.0 395.0 780.0 388.0 775.0 380.0 84 | 81 56 765.0 671.0 755.0 664.0 745.0 654.0 732.0 644.0 716.0 634.0 702.0 628.0 695.0 623.0 684.0 618.0 672.0 612.0 665.0 607.0 658.0 603.0 650.0 598.0 640.0 592.0 628.0 587.0 617.0 582.0 601.0 578.0 591.0 576.0 580.0 575.0 571.0 576.0 563.0 576.0 552.0 576.0 543.0 576.0 535.0 577.0 528.0 576.0 519.0 572.0 507.0 567.0 496.0 561.0 483.0 553.0 471.0 546.0 462.0 537.0 449.0 528.0 437.0 521.0 429.0 516.0 423.0 511.0 418.0 504.0 410.0 500.0 406.0 492.0 401.0 484.0 398.0 479.0 399.0 469.0 400.0 460.0 405.0 451.0 413.0 443.0 423.0 437.0 435.0 435.0 449.0 434.0 460.0 434.0 470.0 434.0 480.0 435.0 487.0 435.0 494.0 435.0 509.0 431.0 527.0 424.0 550.0 414.0 577.0 397.0 611.0 367.0 85 | 82 5 155.0 606.0 150.0 604.0 143.0 603.0 133.0 603.0 123.0 603.0 86 | 83 31 349.0 569.0 343.0 566.0 338.0 562.0 335.0 555.0 331.0 546.0 323.0 540.0 316.0 539.0 310.0 538.0 304.0 538.0 297.0 541.0 287.0 546.0 280.0 549.0 274.0 551.0 267.0 554.0 260.0 556.0 253.0 556.0 247.0 555.0 241.0 552.0 233.0 548.0 223.0 543.0 214.0 537.0 208.0 528.0 204.0 518.0 204.0 505.0 204.0 489.0 209.0 474.0 214.0 459.0 220.0 444.0 230.0 430.0 247.0 414.0 271.0 399.0 87 | 84 26 649.0 660.0 636.0 651.0 624.0 643.0 611.0 630.0 594.0 623.0 575.0 615.0 568.0 595.0 564.0 579.0 558.0 558.0 564.0 545.0 573.0 531.0 584.0 520.0 596.0 510.0 599.0 499.0 597.0 489.0 588.0 481.0 586.0 481.0 585.0 477.0 580.0 476.0 577.0 474.0 577.0 468.0 573.0 461.0 568.0 453.0 561.0 445.0 558.0 434.0 561.0 426.0 88 | 85 51 645.0 612.0 638.0 607.0 628.0 601.0 617.0 595.0 606.0 589.0 598.0 585.0 590.0 580.0 580.0 570.0 569.0 556.0 560.0 545.0 554.0 535.0 549.0 525.0 545.0 514.0 540.0 504.0 535.0 498.0 532.0 491.0 532.0 485.0 533.0 479.0 534.0 474.0 535.0 470.0 540.0 465.0 546.0 461.0 554.0 456.0 567.0 452.0 582.0 444.0 592.0 435.0 601.0 428.0 610.0 421.0 620.0 413.0 628.0 409.0 636.0 406.0 651.0 402.0 666.0 396.0 679.0 389.0 694.0 381.0 708.0 371.0 723.0 355.0 737.0 340.0 750.0 320.0 760.0 300.0 771.0 280.0 780.0 260.0 790.0 245.0 810.0 230.0 840.0 215.0 870.0 190.0 890.0 180.0 910.0 171.0 940.0 165.0 1000.0 170.0 1100.0 170.0 89 | 86 29 430.0 516.0 434.0 516.0 435.0 515.0 435.0 515.0 436.0 514.0 437.0 513.0 437.0 513.0 438.0 511.0 440.0 507.0 443.0 504.0 450.0 500.0 462.0 495.0 473.0 486.0 489.0 474.0 509.0 466.0 542.0 460.0 566.0 455.0 592.0 453.0 610.0 454.0 641.0 455.0 659.0 451.0 679.0 448.0 694.0 440.0 717.0 426.0 744.0 408.0 767.0 384.0 780.0 365.0 819.0 360.0 850.0 340.0 90 | 87 68 314.0 542.0 317.0 537.0 317.0 532.0 315.0 526.0 312.0 521.0 308.0 513.0 307.0 512.0 307.0 512.0 304.0 513.0 304.0 514.0 305.0 514.0 307.0 514.0 307.0 511.0 309.0 507.0 315.0 502.0 325.0 498.0 331.0 491.0 334.0 485.0 338.0 483.0 341.0 482.0 341.0 483.0 343.0 484.0 341.0 486.0 338.0 488.0 334.0 488.0 331.0 485.0 327.0 483.0 323.0 482.0 321.0 481.0 320.0 479.0 319.0 477.0 319.0 474.0 320.0 471.0 323.0 466.0 325.0 461.0 331.0 458.0 337.0 455.0 342.0 453.0 344.0 453.0 349.0 454.0 352.0 453.0 356.0 446.0 364.0 436.0 373.0 427.0 386.0 413.0 403.0 400.0 427.0 384.0 451.0 368.0 474.0 352.0 501.0 338.0 538.0 326.0 577.0 316.0 612.0 310.0 643.0 305.0 679.0 300.0 715.0 291.0 753.0 285.0 795.0 278.0 835.0 272.0 880.0 262.0 923.0 247.0 954.0 224.0 981.0 202.0 1016.0 183.0 1055.0 164.0 1090.0 144.0 1119.0 125.0 1166.0 112.0 91 | 88 28 555.0 667.0 551.0 665.0 549.0 661.0 547.0 656.0 545.0 651.0 543.0 644.0 542.0 638.0 541.0 633.0 538.0 628.0 536.0 625.0 534.0 623.0 532.0 621.0 530.0 619.0 528.0 615.0 528.0 611.0 529.0 606.0 530.0 601.0 531.0 592.0 533.0 576.0 532.0 562.0 528.0 548.0 524.0 533.0 518.0 517.0 499.0 498.0 478.0 495.0 463.0 498.0 462.0 508.0 472.0 514.0 92 | 89 34 401.0 587.0 393.0 584.0 385.0 581.0 377.0 578.0 370.0 576.0 362.0 574.0 353.0 572.0 341.0 569.0 328.0 567.0 312.0 565.0 297.0 563.0 284.0 561.0 273.0 559.0 260.0 558.0 248.0 558.0 238.0 557.0 231.0 555.0 224.0 552.0 217.0 548.0 209.0 544.0 201.0 540.0 193.0 535.0 185.0 529.0 177.0 522.0 170.0 514.0 164.0 506.0 159.0 495.0 159.0 484.0 160.0 473.0 164.0 463.0 175.0 453.0 186.0 442.0 199.0 430.0 220.0 405.0 93 | 90 16 244.0 611.0 238.0 609.0 233.0 607.0 228.0 605.0 221.0 602.0 212.0 597.0 202.0 595.0 190.0 595.0 183.0 597.0 178.0 599.0 168.0 602.0 159.0 608.0 151.0 614.0 144.0 621.0 136.0 628.0 130.0 631.0 94 | 91 7 782.0 462.0 798.0 452.0 815.0 441.0 833.0 429.0 850.0 412.0 867.0 390.0 870.0 364.0 95 | 92 8 303.0 527.0 298.0 517.0 291.0 505.0 284.0 495.0 272.0 487.0 254.0 482.0 242.0 488.0 244.0 497.0 96 | 93 14 894.0 471.0 897.0 470.0 899.0 469.0 900.0 468.0 904.0 466.0 907.0 462.0 911.0 459.0 917.0 457.0 928.0 455.0 947.0 453.0 972.0 446.0 995.0 439.0 1017.0 433.0 1040.0 423.0 97 | 94 45 315.0 624.0 312.0 624.0 310.0 625.0 308.0 625.0 306.0 625.0 304.0 626.0 304.0 631.0 303.0 637.0 302.0 640.0 301.0 642.0 301.0 643.0 297.0 638.0 289.0 634.0 282.0 634.0 278.0 630.0 272.0 627.0 266.0 626.0 260.0 621.0 253.0 619.0 248.0 617.0 245.0 614.0 242.0 609.0 239.0 605.0 236.0 599.0 233.0 597.0 232.0 594.0 230.0 592.0 229.0 590.0 229.0 587.0 230.0 584.0 232.0 582.0 239.0 576.0 246.0 569.0 257.0 560.0 269.0 550.0 290.0 538.0 312.0 520.0 340.0 499.0 380.0 467.0 421.0 432.0 465.0 395.0 500.0 375.0 525.0 360.0 550.0 350.0 580.0 345.0 98 | 95 10 425.0 642.0 415.0 635.0 404.0 627.0 395.0 622.0 378.0 614.0 373.0 602.0 371.0 584.0 370.0 565.0 376.0 549.0 392.0 521.0 99 | 96 18 291.0 696.0 289.0 693.0 287.0 690.0 287.0 687.0 287.0 684.0 288.0 681.0 289.0 677.0 290.0 672.0 289.0 668.0 288.0 666.0 286.0 664.0 284.0 663.0 279.0 663.0 274.0 664.0 269.0 667.0 265.0 671.0 259.0 673.0 251.0 673.0 100 | 97 33 479.0 668.0 472.0 664.0 466.0 662.0 458.0 659.0 450.0 658.0 440.0 657.0 429.0 656.0 416.0 653.0 401.0 652.0 385.0 652.0 369.0 653.0 346.0 652.0 324.0 650.0 303.0 647.0 283.0 644.0 269.0 642.0 259.0 642.0 252.0 643.0 245.0 645.0 244.0 643.0 244.0 640.0 244.0 639.0 246.0 639.0 248.0 638.0 250.0 636.0 250.0 634.0 248.0 633.0 245.0 631.0 245.0 629.0 248.0 630.0 252.0 633.0 257.0 636.0 265.0 646.0 101 | 98 42 620.0 530.0 625.0 533.0 633.0 533.0 641.0 528.0 652.0 523.0 665.0 520.0 678.0 517.0 689.0 510.0 699.0 501.0 704.0 491.0 699.0 485.0 701.0 486.0 702.0 488.0 695.0 493.0 691.0 501.0 687.0 512.0 688.0 526.0 692.0 536.0 698.0 545.0 704.0 550.0 710.0 552.0 711.0 554.0 710.0 559.0 707.0 562.0 704.0 567.0 702.0 572.0 702.0 577.0 706.0 582.0 716.0 580.0 727.0 574.0 742.0 565.0 760.0 552.0 781.0 533.0 801.0 517.0 825.0 509.0 852.0 501.0 884.0 498.0 918.0 498.0 954.0 498.0 991.0 493.0 1034.0 474.0 1090.0 447.0 102 | 99 43 608.0 485.0 600.0 484.0 592.0 486.0 585.0 486.0 575.0 489.0 565.0 492.0 558.0 499.0 562.0 503.0 573.0 500.0 581.0 492.0 587.0 486.0 594.0 481.0 602.0 475.0 611.0 468.0 621.0 461.0 629.0 457.0 638.0 455.0 650.0 455.0 663.0 455.0 677.0 457.0 689.0 456.0 702.0 457.0 712.0 457.0 722.0 458.0 733.0 460.0 745.0 462.0 754.0 463.0 762.0 466.0 763.0 469.0 763.0 477.0 758.0 484.0 751.0 492.0 740.0 499.0 729.0 506.0 719.0 513.0 712.0 518.0 707.0 526.0 700.0 535.0 695.0 541.0 694.0 545.0 696.0 549.0 699.0 553.0 708.0 553.0 103 | 100 36 744.0 561.0 739.0 558.0 734.0 553.0 730.0 548.0 727.0 544.0 724.0 543.0 721.0 543.0 719.0 543.0 717.0 544.0 716.0 546.0 715.0 548.0 714.0 550.0 711.0 554.0 703.0 557.0 696.0 562.0 692.0 564.0 690.0 566.0 687.0 567.0 684.0 568.0 679.0 570.0 676.0 574.0 671.0 579.0 664.0 581.0 654.0 583.0 644.0 583.0 634.0 581.0 627.0 578.0 621.0 573.0 616.0 570.0 610.0 569.0 604.0 569.0 598.0 567.0 586.0 563.0 573.0 558.0 558.0 552.0 543.0 537.0 104 | 101 37 250.0 600.0 247.0 590.0 243.0 581.0 237.0 575.0 229.0 569.0 222.0 564.0 222.0 557.0 222.0 550.0 226.0 542.0 237.0 532.0 246.0 525.0 250.0 520.0 256.0 512.0 259.0 505.0 265.0 497.0 272.0 487.0 281.0 472.0 293.0 457.0 325.0 445.0 370.0 430.0 401.0 412.0 432.0 391.0 458.0 374.0 480.0 360.0 515.0 340.0 550.0 326.0 585.0 307.0 648.0 292.0 710.0 285.0 755.0 270.0 810.0 260.0 870.0 255.0 940.0 255.0 980.0 255.0 1010.0 250.0 1045.0 235.0 1070.0 230.0 105 | 102 13 1070.0 230.0 1070.0 230.0 1070.0 230.0 1070.0 230.0 1070.0 230.0 1070.0 230.0 1070.0 230.0 1070.0 230.0 1070.0 230.0 1070.0 230.0 1070.0 230.0 1070.0 230.0 1070.0 230.0 106 | 103 17 367.0 477.0 367.0 470.0 365.0 462.0 363.0 455.0 364.0 448.0 365.0 441.0 368.0 434.0 368.0 426.0 370.0 417.0 375.0 409.0 382.0 402.0 395.0 390.0 416.0 376.0 437.0 362.0 467.0 345.0 500.0 328.0 535.0 315.0 107 | 104 23 519.0 637.0 511.0 632.0 502.0 628.0 494.0 623.0 486.0 619.0 478.0 615.0 470.0 610.0 461.0 604.0 452.0 601.0 442.0 598.0 432.0 596.0 423.0 594.0 413.0 593.0 398.0 592.0 389.0 588.0 378.0 587.0 366.0 585.0 350.0 584.0 337.0 582.0 324.0 579.0 311.0 576.0 298.0 572.0 283.0 568.0 108 | 105 26 883.0 684.0 873.0 680.0 861.0 674.0 847.0 666.0 833.0 658.0 819.0 651.0 805.0 643.0 790.0 633.0 776.0 624.0 761.0 616.0 745.0 608.0 729.0 599.0 713.0 591.0 698.0 583.0 685.0 574.0 673.0 564.0 659.0 556.0 649.0 552.0 639.0 548.0 631.0 543.0 625.0 535.0 619.0 527.0 612.0 517.0 609.0 506.0 611.0 494.0 617.0 479.0 109 | 106 43 484.0 673.0 470.0 670.0 456.0 667.0 442.0 663.0 429.0 660.0 417.0 657.0 405.0 654.0 396.0 649.0 388.0 643.0 380.0 637.0 373.0 631.0 366.0 626.0 360.0 622.0 354.0 617.0 348.0 609.0 344.0 600.0 336.0 592.0 326.0 584.0 315.0 576.0 307.0 568.0 301.0 561.0 296.0 553.0 292.0 547.0 290.0 539.0 291.0 530.0 293.0 519.0 296.0 506.0 301.0 494.0 308.0 481.0 316.0 468.0 320.0 455.0 324.0 442.0 328.0 434.0 330.0 429.0 332.0 424.0 333.0 418.0 333.0 411.0 333.0 401.0 329.0 387.0 325.0 369.0 330.0 355.0 342.0 344.0 356.0 335.0 110 | 107 62 706.0 659.0 700.0 653.0 690.0 646.0 677.0 640.0 665.0 636.0 652.0 634.0 639.0 632.0 631.0 630.0 622.0 627.0 614.0 624.0 603.0 619.0 594.0 615.0 587.0 610.0 581.0 607.0 574.0 604.0 567.0 601.0 557.0 597.0 543.0 591.0 529.0 585.0 512.0 583.0 499.0 581.0 490.0 573.0 477.0 562.0 468.0 556.0 460.0 549.0 454.0 538.0 447.0 529.0 441.0 519.0 439.0 507.0 438.0 496.0 438.0 487.0 443.0 476.0 450.0 464.0 459.0 451.0 468.0 437.0 481.0 426.0 498.0 415.0 521.0 394.0 537.0 369.0 544.0 345.0 560.0 336.0 587.0 324.0 617.0 314.0 648.0 308.0 674.0 302.0 704.0 300.0 742.0 303.0 783.0 305.0 825.0 302.0 861.0 294.0 890.0 275.0 895.0 254.0 895.0 234.0 895.0 218.0 894.0 204.0 888.0 191.0 875.0 180.0 855.0 174.0 835.0 170.0 810.0 170.0 786.0 170.0 760.0 180.0 111 | 108 57 562.0 598.0 555.0 597.0 547.0 594.0 537.0 591.0 529.0 586.0 524.0 582.0 520.0 577.0 518.0 572.0 518.0 566.0 519.0 560.0 520.0 553.0 521.0 544.0 523.0 535.0 526.0 527.0 532.0 518.0 538.0 509.0 546.0 502.0 554.0 497.0 562.0 492.0 565.0 489.0 565.0 489.0 565.0 489.0 564.0 488.0 562.0 487.0 561.0 485.0 562.0 481.0 563.0 476.0 567.0 467.0 571.0 459.0 579.0 450.0 594.0 439.0 613.0 431.0 637.0 425.0 667.0 420.0 698.0 419.0 730.0 420.0 765.0 422.0 801.0 423.0 839.0 424.0 874.0 422.0 915.0 415.0 960.0 399.0 996.0 362.0 1010.0 315.0 1005.0 275.0 995.0 255.0 960.0 230.0 915.0 235.0 905.0 240.0 900.0 255.0 900.0 275.0 923.0 293.0 955.0 310.0 1000.0 320.0 1015.0 315.0 1020.0 310.0 1025.0 295.0 112 | 109 62 880.0 681.0 868.0 681.0 854.0 681.0 839.0 680.0 820.0 678.0 800.0 675.0 781.0 671.0 762.0 668.0 744.0 664.0 730.0 660.0 717.0 656.0 704.0 652.0 692.0 645.0 679.0 638.0 667.0 630.0 657.0 623.0 647.0 616.0 638.0 611.0 629.0 606.0 621.0 601.0 616.0 597.0 612.0 592.0 609.0 586.0 606.0 580.0 603.0 573.0 596.0 564.0 590.0 560.0 583.0 557.0 575.0 556.0 565.0 556.0 556.0 555.0 548.0 551.0 540.0 546.0 533.0 541.0 530.0 532.0 530.0 522.0 530.0 510.0 530.0 498.0 534.0 486.0 541.0 475.0 549.0 464.0 563.0 451.0 583.0 443.0 604.0 436.0 625.0 429.0 646.0 423.0 668.0 414.0 691.0 405.0 718.0 394.0 741.0 380.0 753.0 368.0 767.0 359.0 783.0 353.0 802.0 351.0 824.0 350.0 848.0 345.0 873.0 340.0 899.0 328.0 930.0 310.0 967.0 287.0 999.0 261.0 1024.0 237.0 113 | 110 24 574.0 539.0 566.0 532.0 560.0 526.0 556.0 520.0 554.0 514.0 551.0 511.0 546.0 510.0 542.0 508.0 535.0 505.0 529.0 502.0 523.0 499.0 515.0 494.0 508.0 487.0 502.0 480.0 498.0 472.0 497.0 462.0 497.0 448.0 504.0 428.0 520.0 402.0 538.0 375.0 563.0 345.0 590.0 324.0 610.0 314.0 630.0 305.0 --------------------------------------------------------------------------------