flann-1.8.4-src/0000755000000000000000000000000012075346130012106 5ustar rootrootflann-1.8.4-src/examples/0000755000000000000000000000000012075346130013724 5ustar rootrootflann-1.8.4-src/examples/CMakeLists.txt0000644000000000000000000000222212075346130016462 0ustar rootrootadd_custom_target(examples ALL) if (BUILD_C_BINDINGS) add_executable(flann_example_c flann_example.c) target_link_libraries(flann_example_c flann) set_target_properties(flann_example_c PROPERTIES COMPILE_FLAGS -std=c99) add_dependencies(examples flann_example_c) install (TARGETS flann_example_c DESTINATION bin ) endif() if (HDF5_FOUND) include_directories(${HDF5_INCLUDE_DIR}) add_executable(flann_example_cpp flann_example.cpp) target_link_libraries(flann_example_cpp ${HDF5_LIBRARIES}) if (HDF5_IS_PARALLEL) target_link_libraries(flann_example_cpp ${MPI_LIBRARIES}) endif() add_dependencies(examples flann_example_cpp) install (TARGETS flann_example_cpp DESTINATION bin) if (USE_MPI AND HDF5_IS_PARALLEL) add_executable(flann_example_mpi flann_example_mpi.cpp) target_link_libraries(flann_example_mpi flann_cpp ${HDF5_LIBRARIES} ${MPI_LIBRARIES} ${Boost_LIBRARIES}) add_dependencies(examples flann_example_mpi) install (TARGETS flann_example_mpi DESTINATION bin) endif() else() message("hdf5 library not found, not compiling flann_example.cpp") endif() flann-1.8.4-src/examples/flann_example.cpp0000644000000000000000000000174312075346130017246 0ustar rootroot #include #include #include using namespace flann; int main(int argc, char** argv) { int nn = 3; Matrix dataset; Matrix query; load_from_file(dataset, "dataset.hdf5","dataset"); load_from_file(query, "dataset.hdf5","query"); Matrix indices(new int[query.rows*nn], query.rows, nn); Matrix dists(new float[query.rows*nn], query.rows, nn); // construct an randomized kd-tree index using 4 kd-trees Index > index(dataset, flann::KDTreeIndexParams(4)); index.buildIndex(); // do a knn search, using 128 checks index.knnSearch(query, indices, dists, nn, flann::SearchParams(128)); flann::save_to_file(indices,"result.hdf5","result"); delete[] dataset.ptr(); delete[] query.ptr(); delete[] indices.ptr(); delete[] dists.ptr(); return 0; } flann-1.8.4-src/examples/README0000644000000000000000000000051312075346130014603 0ustar rootroot These examples use some datasets that are not included in the source distribution. You can download the datasets from here: http://people.cs.ubc.ca/~mariusm/uploads/FLANN/datasets/dataset.hdf5 http://people.cs.ubc.ca/~mariusm/uploads/FLANN/datasets/dataset.dat http://people.cs.ubc.ca/~mariusm/uploads/FLANN/datasets/testset.dat flann-1.8.4-src/examples/flann_example.c0000644000000000000000000000450612075346130016706 0ustar rootroot #include #include #include float* read_points(const char* filename, int rows, int cols) { float* data; float *p; FILE* fin; int i,j; fin = fopen(filename,"r"); if (!fin) { printf("Cannot open input file.\n"); exit(1); } data = (float*) malloc(rows*cols*sizeof(float)); if (!data) { printf("Cannot allocate memory.\n"); exit(1); } p = data; for (i=0;i #include #include #include #include #define IF_RANK0 if (world.rank()==0) timeval start_time_; void start_timer(const std::string& message = "") { if (!message.empty()) { printf("%s", message.c_str()); fflush(stdout); } gettimeofday(&start_time_,NULL); } double stop_timer() { timeval end_time; gettimeofday(&end_time,NULL); return double(end_time.tv_sec-start_time_.tv_sec)+ double(end_time.tv_usec-start_time_.tv_usec)/1000000; } float compute_precision(const flann::Matrix& match, const flann::Matrix& indices) { int count = 0; assert(match.rows == indices.rows); size_t nn = std::min(match.cols, indices.cols); for(size_t i=0; i >* index) { boost::mpi::communicator world; int nn = 1; flann::Matrix query; flann::Matrix match; // flann::Matrix gt_dists; IF_RANK0 { flann::load_from_file(query, "sift100K.h5","query"); flann::load_from_file(match, "sift100K.h5","match"); // flann::load_from_file(gt_dists, "sift100K.h5","dists"); } boost::mpi::broadcast(world, query, 0); boost::mpi::broadcast(world, match, 0); flann::Matrix indices(new int[query.rows*nn], query.rows, nn); flann::Matrix dists(new float[query.rows*nn], query.rows, nn); IF_RANK0 { indices = flann::Matrix(new int[query.rows*nn], query.rows, nn); dists = flann::Matrix(new float[query.rows*nn], query.rows, nn); } // do a knn search, using 128 checks0 IF_RANK0 start_timer("Performing search...\n"); index->knnSearch(query, indices, dists, nn, flann::SearchParams(128)); IF_RANK0 { printf("Search done (%g seconds)\n", stop_timer()); printf("Indices size: (%d,%d)\n", (int)indices.rows, (int)indices.cols); printf("Checking results\n"); float precision = compute_precision(match, indices); printf("Precision is: %g\n", precision); } delete[] query.ptr(); delete[] match.ptr(); IF_RANK0 { delete[] indices.ptr(); delete[] dists.ptr(); } } int main(int argc, char** argv) { boost::mpi::environment env(argc, argv); boost::mpi::communicator world; //flann::Matrix dataset; IF_RANK0 start_timer("Loading data...\n"); // construct an randomized kd-tree index using 4 kd-trees flann::mpi::Index > index("sift100K.h5", "dataset", flann::KDTreeIndexParams(4)); //flann::load_from_file(dataset, "sift100K.h5","dataset"); //flann::Index > index( dataset, flann::KDTreeIndexParams(4)); world.barrier(); IF_RANK0 printf("Loading data done (%g seconds)\n", stop_timer()); IF_RANK0 printf("Index size: (%d,%d)\n", index.size(), index.veclen()); start_timer("Building index...\n"); index.buildIndex(); printf("Building index done (%g seconds)\n", stop_timer()); world.barrier(); printf("Searching...\n"); boost::thread t(boost::bind(search, &index)); t.join(); boost::thread t2(boost::bind(search, &index)); for(;;){}; return 0; } flann-1.8.4-src/doc/0000755000000000000000000000000012075346130012653 5ustar rootrootflann-1.8.4-src/doc/references.bib0000644000000000000000000002535612075346130015465 0ustar rootroot@inproceedings{arthur_kmeanspp_2007, title = {k-means++: the advantages of careful seeding}, booktitle = {Proceedings of the eighteenth annual ACM-SIAM symposium on Discrete algorithms}, publisher = {Society for Industrial and Applied Mathematics Philadelphia, PA, USA}, author = {D. Arthur and S. Vassilvitskii}, year = {2007}, pages = {1027--1035} }, @inproceedings{winder_learning_2007, title = {Learning Local Image Descriptors}, doi = {10.1109/CVPR.2007.382971}, abstract = {In this paper we study interest point descriptors for image matching and 3D reconstruction. We examine the building blocks of descriptor algorithms and evaluate numerous combinations of components. Various published descriptors such as SIFT, GLOH, and Spin images can be cast into our framework. For each candidate algorithm we learn good choices for parameters using a training set consisting of patches from a multi-image 3D reconstruction where accurate ground-truth matches are known. The best descriptors were those with log polar histogramming regions and feature vectors constructed from rectified outputs of steerable quadrature filters. At a 95\% detection rate these gave one third of the incorrect matches produced by SIFT.}, booktitle = {CVPR}, author = {S.A.J. Winder and M. Brown}, year = {2007}, keywords = {3D image reconstruction,feature vectors,GLOH images,image reconstruction,local image descriptors,log polar histogramming,SIFT images,Spin images,steerable quadrature filters,vectors}, pages = {1-8} }, @article{arya_optimal_1998, title = {An optimal algorithm for approximate nearest neighbor searching in fixed dimensions}, volume = {45}, url = {citeseer.ist.psu.edu/article/arya94optimal.html}, journal = {Journal of the ACM}, author = {Sunil Arya and David M. Mount and Nathan S. Netanyahu and Ruth Silverman and Angela Y. Wu}, year = {1998}, pages = {891-923} }, @article{lowe_sift_2004, title = {Distinctive image features from scale-invariant keypoints}, volume = {60}, journal = {Int. Journal of Computer Vision}, author = {David G. Lowe}, year = {2004}, pages = {91-110} }, @inproceedings{liu_efficient_2003, title = {Efficient Exact k-NN and Nonparametric Classification in High Dimensions}, booktitle = {Neural Information Processing Systems}, author = {T. Liu and A. W. Moore and A. Gray}, year = {2003} }, @inproceedings{nister_scalable_2006, title = {Scalable Recognition with a Vocabulary Tree}, isbn = {0-7695-2597-0}, doi = {http://dx.doi.org/10.1109/CVPR.2006.264}, booktitle = {CVPR}, author = {David Nister and Henrik Stewenius}, year = {2006}, pages = {2161-2168} }, @inproceedings{beis_shape_1997, title = {Shape indexing using approximate nearest-neighbor search in high dimensional spaces}, url = {citeseer.ist.psu.edu/beis97shape.html}, booktitle = {CVPR}, author = {Jeffrey S. Beis and David G. Lowe}, year = {1997}, pages = {1000-1006} }, @inproceedings{leibe_efficient_2006, title = {Efficient Clustering and Matching for Object Class Recognition}, url = {http://www.mis.informatik.tu-darmstadt.de/Publications/leibe-efficientclustering-bmvc06.pdf}, abstract = {In this paper we address the problem of building object class representations based on local features and fast matching in a large database. We propose an efficient algorithm for hierarchical agglomerative clustering. We examine different agglomerative and partitional clustering strategies and compare the quality of obtained clusters. Our combination of partitional-agglomerative clustering gives significant improvement in terms of efficiency while maintaining the same quality of clusters. We also propose a method for building data structures for fast matching in high dimensional feature spaces. These improvements allow to deal with large sets of training data typically used in recognition of multiple object classes.}, booktitle = {BMVC}, author = {B. Leibe and K. Mikolajczyk and B. Schiele}, year = {2006} }, @inproceedings{schindler_city-scale_2007, title = {City-Scale Location Recognition}, doi = {10.1109/CVPR.2007.383150}, abstract = {We look at the problem of location recognition in a large image dataset using a vocabulary tree. This entails finding the location of a query image in a large dataset containing 3times104 streetside images of a city. We investigate how the traditional invariant feature matching approach falls down as the size of the database grows. In particular we show that by carefully selecting the vocabulary using the most informative features, retrieval performance is significantly improved, allowing us to increase the number of database images by a factor of 10. We also introduce a generalization of the traditional vocabulary tree search algorithm which improves performance by effectively increasing the branching factor of a fixed vocabulary tree.}, booktitle = {CVPR}, journal = {Computer Vision and Pattern Recognition, 2007. CVPR '07. IEEE Conference on}, author = {G. Schindler and M. Brown and R. Szeliski}, year = {2007}, keywords = {feature matching,image matching,image retrieval,query image,trees (mathematics)city-scale location recognition,vocabulary tree}, pages = {1-7} }, @article{freidman_algorithm_1977, title = {An Algorithm for Finding Best Matches in Logarithmic Expected Time}, volume = {3}, issn = {0098-3500}, doi = {http://doi.acm.org/10.1145/355744.355745}, journal = {ACM Trans. Math. Softw.}, author = {Jerome H. Freidman and Jon Louis Bentley and Raphael Ari Finkel}, year = {1977}, pages = {209--226} }, @inproceedings{brin_near_1995, title = {Near Neighbor Search in Large Metric Spaces}, isbn = {1-55860-379-4}, booktitle = {VLDB}, author = {Sergey Brin}, year = {1995}, pages = {574-584} }, @inproceedings{liu_investigation_2004, title = {An investigation of practical approximate nearest neighbor algorithms}, url = {citeseer.ist.psu.edu/753047.html}, booktitle = {Neural Information Processing Systems}, author = {T. Liu and A. Moore and A. Gray and K. Yang}, year = {2004} }, @article{snavely_photo_2006, title = {Photo tourism: Exploring photo collections in 3{D}}, volume = {25}, journal = {ACM Transactions on Graphics (TOG)}, author = {N. Snavely and S. M. Seitz and R. Szeliski}, year = {2006}, pages = {835-846} }, @inproceedings{silpa-anan_localization_2004, title = {Localization using an imagemap}, booktitle = {Australasian Conference on Robotics and Automation}, author = {C. Silpa-Anan and R. Hartley}, year = {2004} }, @inproceedings{sivic_videogoogle_2003, title = {Video {G}oogle: A Text Retrieval Approach to Object Matching in Videos}, booktitle = {ICCV}, author = {J. Sivic and A. Zisserman}, year = {2003} }, @techreport{torralba_tiny_2007, Author = {A. Torralba and R. Fergus and W. T. Freeman}, Title = {Tiny Images}, Institution = {CSAIL, Massachusetts Institute of Technology}, Year = {2007}, URL = {http://dspace.mit.edu/handle/1721.1/37291}, Number = {MIT-CSAIL-TR-2007-024} }, @article{torralba_80_million_2008, author = {Antonio Torralba and Rob Fergus and William T. Freeman}, title = {80 Million Tiny Images: A Large Data Set for Nonparametric Object and Scene Recognition}, journal ={IEEE Transactions on Pattern Analysis and Machine Intelligence}, volume = {30}, number = {11}, issn = {0162-8828}, year = {2008}, pages = {1958-1970}, doi = {http://doi.ieeecomputersociety.org/10.1109/TPAMI.2008.128}, publisher = {IEEE Computer Society}, address = {Los Alamitos, CA, USA}, } @inproceedings{philbin_oxford_2007, title = {Object retrieval with large vocabularies and fast spatial matching}, booktitle = {CVPR}, author = {J. Philbin and O. Chum and M. Isard and J. Sivic and A. Zisserman}, year = {2007} } @article{andoni_near-optimal_2006, title = {Near-Optimal Hashing Algorithms for Approximate Nearest Neighbor in High Dimensions}, journal = {Proceedings of the 47th Annual IEEE Symposium on Foundations of Computer Science (FOCS'06)}, author = {A. Andoni}, year = {2006}, pages = {459-468} } @article{fukunaga_branch_1975, title = {A Branch and Bound Algorithm for Computing k-Nearest Neighbors}, volume = {24}, url = {http://portal.acm.org/citation.cfm?id=1311063.1311121\&coll=GUIDE\&dl=\&CFID=5674080\&CFTOKEN=11648065}, abstract = {Computation of the k-nearest neighbors generally requires a large number of expensive distance computations. The method of branch and bound is implemented in the present algorithm to facilitate rapid calculation of the k-nearest neighbors, by eliminating the necesssity of calculating many distances. Experimental results demonstrate the efficiency of the algorithm. Typically, an average of only 61 distance computations were made to find the nearest neighbor of a test sample among 1000 design samples.}, journal = {IEEE Trans. Comput.}, author = {K. Fukunaga and P. M. Narendra}, year = {1975}, keywords = {branch and bound,distance computation,hierarchical decomposition,k-nearest neighbors,tree-search algorithm.}, pages = {750-753} } @inproceedings{mikolajczyk_improving_2007, title = {Improving Descriptors for Fast Tree Matching by Optimal Linear Projection}, isbn = {1550-5499}, doi = {10.1109/ICCV.2007.4408871}, abstract = {In this paper we propose to transform an image descriptor so that nearest neighbor (NN) search for correspondences becomes the optimal matching strategy under the assumption that inter-image deviations of corresponding descriptors have Gaussian distribution. The Euclidean NN in the transformed domain corresponds to the NN according to a truncated Mahalanobis metric in the original descriptor space. We provide theoretical justification for the proposed approach and show experimentally that the transformation allows a significant dimensionality reduction and improves matching performance of a state-of-the art SIFT descriptor. We observe consistent improvement in precision-recall and speed of fast matching in tree structures at the expense of little overhead for projecting the descriptors into transformed space. In the context of SIFT vs. transformed M-SIFT comparison, tree search structures are evaluated according to different criteria and query types. All search tree experiments confirm that transformed M-SIFT performs better than the original SIFT.}, booktitle = {Computer Vision, 2007. ICCV 2007. IEEE 11th International Conference on}, journal = {Computer Vision, 2007. ICCV 2007. IEEE 11th International Conference on}, author = {Krystian Mikolajczyk and Jiri Matas}, year = {2007}, pages = {1-8} } @inproceedings{silpa-anan_optimized_2008, author = {Silpa-Anan, C. and Hartley, R.} , title = "Optimised {KD}-trees for fast image descriptor matching" , booktitle = {CVPR} , year = 2008 , url = "../Papers/PDF/SilpaAnan:CVPR08.pdf" , };flann-1.8.4-src/doc/images/0000755000000000000000000000000012075346130014120 5ustar rootrootflann-1.8.4-src/doc/images/cmake-gui.png0000644000000000000000000006453712075346130016507 0ustar rootrootPNG  IHDRf;^sBITO pHYs+ IDATxw}73wnwz[n`, !!NiB`!Nl'q~ypX`,0% "!$,*[);sL~u5w洙si#GHBR-\8Qj!CVa򐅙+S Y= +3WnV"=dmn=W oժLm8 (bZ/vNBDb>%#le;•q"~6;\]˯fT#~I,RZQ jLMPL3Nzq(i>#G.s^+rIEݬf(NyP^k/pQU?djdROXWWEw@H,%:wCyw+X>?5wP;= 'yRHli ,5њI.RHpN+@(GiaxbzNY ; y% =\*-<8e7ضEq?ʼ$ M?m]8f/ugg6SaͲN˱ԁYShܱcNJ+vTit2ivٲe̷zoٲe* 9iP 9~m=ݤzՆ"('UAiOĺrM|U^ev:z&jXQC=(zt\42jZo sMZm(M}nn&hocJ|s8 Vh2Mjub;OB}Bv|K5}Gs7ETJUTUٴzFK0H #gc~8Y $MWYDM/@J-!}FoMu{̵V\]K&3hmY};ֲb;vg˴W=Dz}xU]]إڜeFUg,]z6kT]o&\Dܟ4q[b  'pD{,ol9Lb. O~$c$_25aԱB%.ç)zc-VE iL0cԦMP}m˖׷v]O=7eKO{kZKv"j"Nզ!͹ )R!Y `f|1c뭦Ôz}F uy2&+4wԱB&;2js.QU5MNw7]l_fPrwѕu:PexЪk@_^$AuAuիlӀ- (v]>ux'<֕˟6 92&UdWbRٞhzB|jTlM`/2Jw ;v\*ڡMZb:ۜR4qӰnp=\_?k!nׂ@M+Y٪]lS] oW+&^oٱ=A9^gU){ڱF};Ҫ2u=Q/M3d[UUw9; ƍOzovJѤUx:sJp[4og&(E'~˸j>7T` sxxJ8rpP9a[vwmi޹]lr]R[]׀@e]@Mah)ur`t=^^hpJp5h^H/Fn :+!3… I1 L˴ |S;v% #BO݉1`#@zAuI)e\2wr O|Gp6%Z!FQ`=jY2~;Qɰ0W䫮GeUKxhohx_h+00zI-PUU $M|,S/zbjBCCA CEĠ9s|&yKc?&T'S~q;,40Dw((a4ױLnU\3` 7 'B]M̪7Nr|\7zٸJ5MH aBBKTM%atFÝ^#:>Z"S2y${iDVYpN6Z\!* 3!FzVxPdҎQ8j'Y- {u50G R G#k_ar VK74eZՊXP@T)+w֚ƞo7Z͆M}uc@/3P' -~V.ڴd|mʟ+Ke6LCcejD0Q"( aXcuĸMoT^=|)W>.Vj]CȬ w¹ RqQp m8m[u+ЉUU }[,hMSHu!0h1Z9` AÜUU>z`팒7=Zшe=Zjt\[":;̺Ō",͵_R#]7y-bdQ!d6:@4~!S~&U>M Ee\.W!!DS"d6vU.EUUA+^0ʲƖ/[B(˪Θ93JU+s^Ab;pʴ B284o,^qG688صw?FbhbYeYd(FEQڛL$QVO̺we˗/X NW-\.m*v] Yk_K{b=H~W۪z 9L8S7 bwn…kO(͜^>UeYWx/}1Xz.mEPZ<01aΊ#aBx>q6mc]jzݿ?!d…mZ{b[;Oh+XMlc@b[l)f2r<54mc޷o_gg}LӚ}Y dZhu+M3l {ZLէ]-%k􌶹Ś[S8̨1"ǭ=3)yN0*-ДT-T,ZHڷOȢϱ.% ԦONFhLHlWAk#~u| L0 Vpϊp4Bh -Fr>(g 4 d-yiƸ9{,jXB&4bCkok+ mY'===awqٺL&g[Z"Hxu|V.&40EQǑH$;}G9'HKJ6-֎ֈX<.׷oo~j^k!/)SOI$"PdY. ScTiddd_WWTZ|Y !=v;ŋُ@lBX,b3hd岔L&#(#G+ֶ>{緷 LE) ù|mT5L%LDt*Rϱ]$'%B&@g6_q B&L.\2 dpL(,j7>b1QoCp+000k.IEQEnsW5WUU%Ie˖2!Œte].eL;idhh7Xrʩ;Ե={vHT*/Y"˒$ɕl^FEQݻ'G$zu˗Xp!~Q4\.E8x -\$I$Il[]+%U%sf9p`i)2^$"`? @KKKGi dYx_6[,,Iٖvƽy1{gxv֔Vk(Nӧ LWq6@O6cr)+!d2REQ,j8sm=_X!hӁ@΢*_cx /3O|@'s?Euqvx6l ̾kuQcY#*y{ 0w\nQ̙(ğĂO֧CXぃUU ÿKzWip晓y۷ZiEq&3WmKk^Wı Ɗxc"tAkOk9Ʒ4;\Z+@|Č"c mV68 w-UҞ… 8B.\-2&L)okޙjREKur^]O&˛C-.LFc*ʺvb:5h+˳^n1i(eZ5F8v1J1Ӳsa`kLn_7|~o]kmQn{z@ڿ:ɴip1%-#FcsLEuvvVlqd]Zi*{k"qUx~ n[P-[vկ~n?~n=r7˖-s[ېo>mS޷o5.C[hѾ}|^)#FFȴ]ZL{u]TvVq6Ge ;p5#s_wO[o>r9q2iኽEYh!D^pX!"PaCv/ٺn+yeB NX/~v˽TQczBZx޽]i޽]B/^F)Lڴ* lCsNZ}vF22Ԙ uZk{E`צ4Zigy]~R9س;ÙgԘ? "MMMhXTд[^xҥKIh ƒ%{kdc2zF Rڴnw9h ]]>%I}Nx+vgEaZh ^~yѢNۥryݟ8ё*^#AZZ6?SO=qsv+ bB)<8ПL&h "H6}ms97HJJ6QVBj'$}[[[;{BPO2uʔdqCaS, ǏOҌ922o߾R|YfBjy5zoc-b?! R~;ID"AEQ}ѣCÃk[]kkm5{e4EQ p>v[C*jmmM&xIp,D"t:Jϱ]$'%B&@g6pqxh ! Lz\p]&4 EQdY.nHb(7!ͱ5ߒh Df͞NݖrcGeYf<ߑ4sKBwH|x{۲,bTZ=^=*I,]+_?KkJ$I岴SdId QThtϞXTmk,^\.KeRDcxsEUsL߂OvS ٹsǒ%KFGGoۮ(;q؜ٳwyCjْLr۶m<%ԯ}=>wܱwX PO>أb;VUqv` ʞó^TlWi;\Yu"^ Y18A R~HŋuۧYhJTcv=FiE:iU˨T-M5*QB/[sξ~m0VUI|[[?Czv}'/66o ۔mϧUԦ-H|X&㧢:U,D~oo[lٰaCk[۫ pm/YX$lCӏNvHCo#dVvHN%R'FeUBI8Mu΄.Wٻ]]]-Zx>UU㖂 KUQU*'~NL_R|z(}&&r//_W^4KB/Ӷ"&-ՓLTB֭[sVZYjDi)-4|{5*U]v{n_׮~;\u>MNCˉcנ-512׋fVZE lժmÉqQLʎYbt IDAT;/斯Uw9)e,YxbŋkJ9>g7bI+Cv39Brvדmm%YQU}oƊ-xeB}QmF/5MsYK-ж Zc}5y.ӸT9J%߾/ٿw!Iş\Q,nݺ5vvcZ% lY; סqmXڳg>})"\H!sϞ=K,ٳgljc{f$˲46"QYT;vLQEM-\裏^zɥƙL1MXGGYLYhs֊7Y}^݀6ʚኑȷ׷R9pkئgiŝ Oܦٺuϕ]ӱ1Hvƿݻwr)9,Ew&g.-ץl>?ghB5; DdBȑ#.Xf%h[]n6d ڦ-6 !D_-5f4Ua]j JX=Ї?L&L9ie宮8PB!ڸ_Ǎx0`/ͺ^ aW={.!S_g]Vuݖ !B!cUDm=x"Q*Tf2ۿ|>NM$o߶a]/ZZ/~nBu}3gD}k׮Ӧ/_\[tiKU"9rx%>QuB&@婪JD[ԠF]/g h[w6𣭵m8W2琀5 5Hr d@cb!~oI$( s5/и-;@* ! B&L.\2p`ǡ80"dx+^W낖k_vҔ`Cp~v W.m@s:\pW*!?3d'|gq!)rYg ȍ饗~\?e˖=۶m/6rJey;2@yW[~L [7n\k_7l馛o}ѢE<% ZZ[]ziKKKP ٖh<0Dz7lUX(u?RG}d[+ߤW֗j߳ƔP##'BQPrcR|Wx<.KeIV!,b>$Y)uȴeݣ89qO=#Cݺu|`HT$IS.Q5" _^Btğ̎2OŽ Au37KLzA Pؑ g P1##nb:#C f(9^&4įTؑYf|.۷kp`X(BA B":u9st[[d}|OJ1?qT7Ww˯[ϭ_\?;:tp`TTLDT26cN=]2׭ &dfqJ⾮r<}ƴ>;Hʊ*Ȳ^{-;tR&5@Sd%}LҦEDߟdK32D$).'2ߥ&\L:uS 5PTAu7r2*qDLm%tG I>c5SrK}Jb^SOnZ{E<T@,w+{7&ø8ıL.XVQc708@R"D/S%예Y2cj7*60˛o?]j6P͘>|>fzOr_  le _7Wxez1͜1}j\5fR2ʴs#Mͣ5v!.կ˿={vd2sd x0c Eʤբ7kƪM-aW5&^&!dn|蓗{o6BSOn2 @㍫n ÈD._eҋ\s]/ޤͩٛU8\3"pTЉLQ_pI3fx|3\sƍ%ITXOj= 5VL/bsKc#9sYکgg/%tJX暏mk0α=ƚ]9^ʮ *zВV}o#HasuO)hpYhD@LEQ ,eY$i|Br,KRYK Ie,ˊ6!IT.K$˲NƓ"+ڄ,I$"$ɒ,I<>eAeIrYea,sB&DQFX,*(b1mN,E}v4FQ1Eh4:>!m0;#h4Q1EhDchTѨ6D12~Qc]QaX,z%@=yuˌD"x<]7?T.dѺXbY.TL/c j8F[4'"d@E&OA&Ay^fe1MYf gʎ}86>GKק|1 y\.w%g17S̳.%@npD( ro뮻E\Oz]L`?+2׮]#\{]]]2cHۥ4*L6 _2WZu}]q<-Bٲe)`'#QIF(۲{/p ֭9gEhW_iӦ+򪫮ɤr#|Z.ޖV&׮]˟t$t掫cd&|*q]y*=R4x.32q |s]a45'B&41KihTw' PGLB9 ^dD"JDD2eaRE9'h̲v]&/2)E&A}Y2 V# d\f1DS+\;42 oٲkִh~>X3N\6{d!s˖-{qVc{x]sbdm h *!>?sq[n}_EnsR@]f7d= p*Qڵ?ΫE>P !*.:p]C DAUD+5T{j";;:t_z.; >OrFRFne@11 L69z@2e#oaEoT:!®;gϙ uL6꣗ȇ Mӧ>KN=eYAb"TUUXϱ=onmm>}B&4.“f;u8ppmdS̛ױpl6[G@"ԩSܹsr9ZL:֞Npu&42H$f24~HD?S-; 8H C5?5`1D"JlD2S-?yTQI43?BY($IR GQȄ2R466Jg L&+6 ZRc`z@̙3s}ddgԩy N8'V@fX>O„-f2Z|TB3qLIחdJ7<~[Ӱ:=\czъeMƁ:RnWW׼X6Tu%T MЄTK7\}E>{& y#O&ЎJj8+%{0_Ƣo}wgwygÆ \ .ԧ?ˍ6L5E&nvu3LY*p.㔶-gBk}:B&4(@KKˢŋo %2ejU5V8ce&L6-J͛7oll,ɴi\ !_4<2@?E"l6d[D"'JGLfMT.d}XȻhXpyeT*JR9qHT*QhX4ŁLh0BB)Jb1miim ##d"##B&r<<22w\.GKLϘq{mX-zmpp}ԱX>c$!mm3f̨T8>$Il&˫tÿoz--$UT7OPC@^fGG>ݭ&z2mڔTL02f1U1cBl1cuŠ($IV ^bFWWW,ɹ\q&@m}]SIC좗xl[pNn {heMࡵ(#K?SWWWGG>7֞"ObQĔ /0WK`W^iooַ1se5?Κ?2ʬ*|kjX>?OX,v_O%,W?k!s˖-k֜35T%|(BsVbjJg{'~׾m۶_$O*Ա.kGӆY?lTU]{/}6l}(իr$T7tLG zD`BP՘(}=;7r\O}*76+X5tLY*pAc!j*[ˡN(Z,}=_wm___GGM7<<4hLS =hs]:UFo5>Ft&32<,˒ZT d& ^FeS(us]f hB(ڹի-cccJQnRL&#];͞3QDelj',NK 8}}vɩ̜9"BTJӪ ӧ!dBݡÆ.“f;u8ppmdS̛ױpl6[nZDNLΝ3FI&nkkOS:2@!H$d2i#;Ȉ^&x!RB+WȌFcx"L%b6R" `h4Ɵ3u{ ԹIwr.VM}]&4:2 22{藘I&Tyk/Uӧӽ}޾~R[Ӵ4cJ3w2uƘT9G7?i&OTB ^ IDAT`D"|{WEu&g9t:}뭷79f @ãڼȄXsi86߈?} ne0m ́z[v28|K9d '?rimz~L.h! 롗kGƥFgxniX`Dkr"FFkv돰< z ɬGexbXe*r6X9zL{. \s]fXWeǮf48Hdf@csh#ؖT nSc $x dSc10 #$\*"Ȣuiw40rTi>50 = p[vLhj-5k8SvttLӴ8mb )C3<.,ҚƸP&X7|]w)6 P58gڵ<#:צu}sBl Ǔɤ D"!hQR*J2O COr_=p!"p76lP[ Zt&b@y]tѯ~O˿ MF3OsLóXի'/_}zmdJB#_!^暫//~&mM0OkfƇɤ?,ztu?L&ƍ_|1=ȹ4PEC暏UzSe_aOZcz@Cm dpA ! E&T.dV [Q,JEIz+epA ! B&_$Y3eGGGwwu&!DM뺻mz2"-pci*tC3<.䒠bm,r|7u]x+A `. X~~B4̵k>#^{mWWL&`tAWB;5u7h 43_2WZu}]q<-Bٲe) N[?p.;|%p2_~naݺu7x6@~:!Lx?iӦ+򪫮ɤ 8auxxef2ڵY&0rqC;ywC{˾ĺQ -#$@#D"x<D\."WwP;"H9 DD\N**Ԥ(N"b!Y2s@*L4Ĝ9s#M"1sf<gx߂ әtﱞBȲH(ƓɖsBFV(zz͞3gƌ|^Q'="DRTD9\,Eqh4'T"fsh4L7 T($=qUI?x`q, B&Lh.>|CFLh./_͈ Mdz-[^xWy̵^ęri3OkshM W_}'V\e~_CO?k׽ZfLׯM+ M@3سgpa痿w9x7gLJ,{,/|olؠ(sjB!z2B)Yqv)UH޼9y==GeYt:>Y\tEg޺b `zجhQ}+'L˲L$P A|0on|蓗{o6BSOn21sqЄ |~\*sq_!^暫//~&m@:98z̫dOMV2KL2x|3\sƍ%IְD;}xVmF84iW@d2nx+ct+9;?MfhHC暏UzΥuu&bkӴm8/ 4$0 B&4~˱^LLh ;noo>رcw}~s2)o,u2^6u^E*x_ya6ؚA뿴T6A5&:TT?dh;D8ǚRZ^km07fSl˧vչ,8mˤNn)uZ&Oƙ֜s6/kθݪ'mO mT͎)9F>r;)cW-gU]ͅLBa7 cm.m6&]ij`Qgx-v>c=lϻg!HW%s~ٱ%9?VqO^gi&n*6ƴug\eE1YRigZ񳯆V TfKUvcն%ؖϿ?*Yl{X;ݳ*!Bo+ =+T?ҍoϟڛކJ7@kOv[UHm3`>DSiM۳Zmn[vJN i \\dbFsj7T-S߻^쎦 SXE~S=[׍_t7}O_{}M>`52TƏ͖/mmEؖoJ*?1Vs:qo(=/;#cy{=a4?nw*٠J)ձ?r{ةCڳ ljP75^cZX0Yie{5f@wa[ep ! B&L.\2 dpAT$Iǎ ɒDT-j2 FD"-Y$eE(,˒$*F(F"ν^Yq>B"xīݚ(J>WJē )Cf\N$'/XN !u~X,b^Y166'8Q%*#;_H$Q,9T*Jcz;$I}.`eyVjJPJ3BfHWX,^,)BWX(WDTc]onVg5ȲHD"P4 DH#dʲ<44fH)SZZZDQ4-me&ALvSHDp  EEQUUQU%;3!Z[b%XJa&ajnԓٙDiR[_ J?iYձug۷o)3R*Oʺy\t_Xk9uu!X6Q#eVD/S"(+&BckU۶m5lr۶>᭬[^KvXj}6}Va6 @.SQ_gW_&8īb|!\J{bM+2Zka~47Rj-k?J =E[T1k0c1/OTmQ h:MX#k' X|LEUc֥>?Q>4m]UTcg7Ue7ˤI=!m}EaㄱlLUoF3xC϶L Eqna#z0 mgjA^g>lv3N %+|P{?l ki+h[Bk۳ѭtH{h ;P.6wtXJ-{6Ɯ%v3N,e,l>l|n6#?a}^fodأM=-? "SD^z~æd⠠> VIRvii*ƃj>*DZ^oMh:Nil D_/T!/?rq΋/Ǝ&FX,v:ezw=J{`J^' ?PfggÆ 'N|>5ٳg_ys ;fS…>RXxKBKL:ۿ݁Μ9{2Sʕ+R!pm_BɣPLVl7:vzz9B,%7W5Լ;Hj@i/S6R{W!I9^:v^R(9%KF)Sbf|GkU ue)(5nzc>ZG2C%%7G`Kw"7^j|ٝ0)S{>~R7tֲE_z#4_Fu6޵d:mc920UZVټvڭvGy'x,2q<0eZf`~wbP"t. c]B7R#Bk;ʔX- 5tǂ2tB5Ԑ4ծ` εLklګwRdȸ%M)e^\4MrtW "$&l L^OXLIDAT_^$/Jt)=|,-Ev⚩K`nO=e})lDĕHl#*s1ȕ2pc e2St-gL)0%`)=vHca2#(Œ&O#{+^.`gJ9I)}`հ.æ< /J5MyftfLy|Q}- ix~\!XԹJ:H]+hKܙ*j:rhdH$Bᣏ?>x`&nw._4' ?@})}gEm7 -UD gɝy[8Ҽ%vYhӤ6k橇&R"( kfyBOxBJ|.O$~_Z _B {^.yPaQqaȒ&Ll6f777;DҦe2:N`Kwq`a[waf2t`Z:Ujcccn0=t6D"% 뚝Nq(tʔ ԩ"t ҿqܺNM' aL˼,t?}hhj#' 2Uwѥ6u UAmjry<1+e0X>jgl1+m)l n!e dD"Nٳ Ng2魖bBzaƭ,cLݹ FP"Ԗy^"fވe<cv>IX0I%.cdj;RZTW n* ܨq!gνalX~k;( Al6W(L5eM44SCYv^5/Mm}1:h]>dr&d33wߝF,,n^" }1ӿ2LV_nzJ&lT\CAYv{U@.kP%]񼱅l%dQxVkeeryfV&D.K$K__k[PUT]2Z\KÌY{מ19 vVs?>[|v'~c-eS8_1՘8 9q3L^4;;vG ٳg_ys #)0E.\/ŋ/^t)ԾL?C8p̙/Te#8|rʕw=z ǏT.Ϟ>}zqq1BĂy썥Z9wO,,a?F.'N,uZrԩ|>{;F̲ju`RBWP}j|Q /fٛ7jnWj5~VkmK,Ku@ۗT=0T(լ߸Qt:-v{faʲWS~x4 Lo4ؗ#hX?H ZzvG8XbFC'bR˲K'-դ(Uvtij2a*}ymi6Y]d'ɞEYv!N+$ia\h3~)Vհ  BZՔ5Un4!e^URlY^^~_Og#2S>lBM38F,f]L}aXJŹǫڛoU|j:7w|ޒcʲZ]q^x4f(`B5}N|yaa瞿Fa*ʲ G>25pWL>MAYvS`uÏ>}O|GՊQR6+ hhM{YҳM5W6 jGeRhHmL3:ԧ0FSO?ۗjH*}M9#칻 v^qFkȣ1 !CN++ˉTRW_^ apI{ھ !D_9rdqaAx?r#'{u'_^\\P{an' G?ӧdӹcnE6p+ic6hhǬ0p[܋DŽ'+z޲)_ 1+h;F]#`t:oLkL:c pB )'L2pB )'L2pBPGJ`dIENDB`flann-1.8.4-src/doc/CMakeLists.txt0000644000000000000000000000120112075346130015405 0ustar rootrootfind_package(LATEX) if (EXISTS ${PDFLATEX_COMPILER} AND EXISTS ${BIBTEX_COMPILER}) include(${PROJECT_SOURCE_DIR}/cmake/UseLATEX.cmake) add_latex_document(manual.tex BIBFILES references.bib IMAGE_DIRS images DEFAULT_PDF) add_custom_command(OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/manual.pdf COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/manual.pdf ${CMAKE_CURRENT_SOURCE_DIR}/manual.pdf DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/manual.pdf ) add_custom_target(doc DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/manual.pdf) endif() install( FILES manual.pdf DESTINATION share/doc/flann OPTIONAL ) flann-1.8.4-src/doc/manual.pdf0000644000000000000000000105504512075346130014635 0ustar rootroot%PDF-1.5 % 161 0 obj << /Length 289 /Filter /FlateDecode >> stream x]Mo0 Aj<4 iP'vvH)ݘʨBǿC8Lı_7]j˚3CN Ө@5'微.S䱝n!+'$хzuc"tօbYב9, !,̪ͫ wts9J )ťiQ^|3y V ͸"rSYIWv&k'\Zn Y&oM=؄t)ѧ_6tSԳEF2ޔ!SejEM eo endstream endobj 169 0 obj << /Length 2726 /Filter /FlateDecode >> stream xڭZo8}wF|hu.#1.z?3J5p8rޞь+&U f3.% h!ҟs8\By[WxͪrY@(Û"iX"<%<5z\ڕ!12ȉ<bs]_ӴP@̖ $U5Q["ڟ}<;-y\t۬"7镕6g UW orI|%JG 0c \fbP?(-W\/ND8`_x i@=(&x1:!8( &p8`?S"TL LpJ`̔ q/#`/2n1 ecHZ'fB0bMbc0C{#rvζP <&B5 @kƄJbƤHv &A+j"`tQ]8q oSm<"P̏eg7Ǣ2?bNjA" ’R~J2X;FWD,Cix ЯRĶ';pc>A4yZْ }dW_Fp1/ cuS~}% eq+>`\a]Pɪ+|VZib -?c|QL3F1>IHuvTP&P&zgc!6O;jV襡amjYfy[MIU@5M@ϲmkj7xYo rbi2V tMZf5BDf z) Uc2?95EsH NU-_"A[Zʱ + fN~Y9|YY*bJwUES_VuL*A4өP,:1Bndh+S]nEi t}Af[aj"."LC]NImtcFTظr \~>lD0 e!2#x0d n3,*jHlW&ͶX`,joVY_ceOk1Dqoû$:F>Rt"mIg0MOLYmnA9!(ba "E,G)ibجVDk ANMvAiڤȗ)4"#6}3nKw-Չ;NiGT޳;<Ǵ5 (ZdKoTjPE/j R|wC'!.I]c'ANGd5q.o qb:<81̗.0rK}`  +x-_[Vzw<ˮ^Ǖ m3=u27޼ugUuCmm'_gq7X[1mDc.!ܬR6Zxzu;y'Qܵ\17q%`@:+B9MBܸ [`Ћ-P0+%(ΗHcuULJ3V0-7:]4,+ęyPYރj׃lХZ$H?.$Vp>Xv~[sog܏X3$,)>f)LLƲ3by !\ˤvZ f]* #WB ~$ů>\y\B q}ƍW,YYJF ż):,W {׿@[#1łzj2^=`~L &*(}AU C/_:]آ\5>JM%~,ImfٯJFHKүZ|Ŷ ˔s+P/+qR^`z=5c*> stream xڭ[koF_!dɼfwmM`4XHBRM#i=F$<̝/dV| Gdy3L˟1_.O!.~]|#@!QiM!Bh:t9Mi~|B0r9MʤHK.߱W^WG?]܎.4:ԗcZ\n_#QejRg0bAxͳ|O@P>ZcQ͋x@0%B sȘ(viɳJR"a=B/)c8qwr! c=F{G8Zڜ9!^X4>0TnGkBL5zcHӝs;x`R#&M0R IGh@BK"L! TҽV~HݻEed^hR<9qyRg u#*!)gfT}͞1 1X0gQN+4iA <$@, ~IQyJ# GRw}J 31$)}\drr%e.OiQM &>cbG Nc ih/VI9|8^YYX_9Yb]Fe,<|=l8Y-vwHXn{6v}@ UtZAjX <H(ZO*A`TD{pT9|C 4ǒ=@A1Ƈj$9X&zSצJmw[NJo[p[,G-SF7eýj2n4eֵ;‹fz\|j`$#SET~r]w$˺lP`!=@App2F senLv@i3 \$@  !2DT=O$x.H"d=3!xHP v9 ʑie3x8hL%s?Ȉ52D=qzch"FBzRe:HGwC"p+h "Q&)5MKIxGn<)zzgdmg6֗?W| endstream endobj 187 0 obj << /Length 1761 /Filter /FlateDecode >> stream xڭY]o6}  yh [[ B,GSCNeAc9$=T'{+$gH1Eߋ{G4Pޓ-'LL.q ʻ "ID(nbtaVpy_i=E6sJO_?u$}y?fA$Q=Sc|@.5 d'u@(=dB?H%-F#t㣀a@(rVURef/EbyIU$_7ZV{  **GpieU]}鬡eYz}aV dv m|d $' #hBrű^ZT`0Sx~ t B@B!煌 7A( wHZdׯ2y+(sa; _:naD4퐴NP"뵧k,0r$sZ[C!zM( k2XhzQ@(x<81&|{1YeƠlN%BVroj*ޜ;%PQsJ4 s`ٜib/bF{fۑcҎ+c,ՕV†9E)cQ׫˧'UP^v}1FmjS HeT٥MF{18b ,} pWa1/OϒǬ@$ϩ4k;tM{zvl_[_g\LAZUThHZH%Z}&s8 κS#T":JTX]׋Q 8 3Qi%S tLɷ0[mDe`?3:&_ަ I ]ܞn3hmvc$ >fM`M}WGUwƎ,RVLQ}b/9 tB` :3c 0(~(H p}E$9 nyz$ms42yl@xP&.KZėn3{R֯0>.hk4[ڦ{|H5seZ7't[u%dIE- _SJ Wwc3%R;'*ǹB%AK ŸU L"J/뛗?9'x9L >w٨גE ئg C"G![ z'1A8ޖE3i"tP },0b 8٢ra4sF6aT! ӈz^cih6wIj5e)k7B4Ka pG& =Lg_P)&cneO]‚Nԝf$46X;3AEyg .08ahK endstream endobj 196 0 obj << /Length 941 /Filter /FlateDecode >> stream xڭV]:}ﯰxJ%b<\iYJ @K,i6iM]_8~EUbgǽq ȒH&7F0*%wHoɧ#ׂ(*y Au 1T#"*L4t&WZLҒ :$K#@q%]R}evIYU/sVob8lLK:ϗ`m\qi<;I@=9Jv1afs.( jl<)zVY&OFGTFg"5`b \YTU=!ȡGgQ,XEѪx}%4z#^JEK>Ѕ D[NITPJ Ɓ v,LTqYZCkFrdeQJ gq8;kY\3*ldQn^ ҜRi3$r?G{#^=7i%IЊmcwưz4?ɓNZ(ٗI_'7Ţ0,#D,Y/B~wiQ=_OXkLs~oҲ@ϲbM]4+Y)dK`2(^Yn+/vl6*ll*:*3hFdyXbQ1^l8/7}b GaqGݗWթ/kV'~PFPG{ih:*-Nh!' Ip8C\Rk÷- |OmCiG@9yJ6aW!ܠA-'m&nF42`.nU;UV^Yq=vNQ$RmUޔPJm.wZ.amvmcWC$o a-_Q] endstream endobj 193 0 obj << /Type /XObject /Subtype /Image /Width 614 /Height 571 /BitsPerComponent 8 /ColorSpace /DeviceRGB /Length 22727 /Filter /FlateDecode >> stream xwtםHH޳X7<{홑HJXsz)Z47cOf9I(+Y") 9K"gF@@dX֭=(ܾ]vU}VUWeg 3!M3ɍhr#%%cj2!8'ILf\Yq^1\mⶒmMfvף c7{=}V ˛y@=q_ҨҤ[3˨q_~Wd,Ϣ Õ2 ]N{ 4^ji4I(էXҌͨ?L1L%+Tf3 {CjTiRJ֬e׊ƻD~ȔY2D+]f!}AK0:3:I:$yɨ#&݂֡B.߂ZLxqs[͆7Rj88ѼڮگƭVfhP&oe![sהܹݐ]fr|nߖ)oʤBFujV{JSʞ:pFGԻXh7LšC1h9bvPd,9n,͢QW>Hҕa:~Wu4L*㳷L:otS?k+MӵC~BXMWuƴ^Md=ZaQ@&uiPzqknx6u ɃN E1V$hm+/-PI> ٪! 7I}$;q0~L"Cf)@{gJ&P_` ͡!jNTOknެP\ic%po\նW;S!PyK=:a4 >+٬oФmSP̘9H, ^fW :4,j0elt5 ΝEJ ;&[2Ebɴ @SAҡ}yvY7-vo/}@Upn=M+C[Z[uɓWd=S-t:b0e0Ѱ]8mVUZO6?HXrMith4%.PݚJLZy0e*T91ZT1"F=4$:Țd3gیSa/LEIKgK f dG3$-Iv3)c {f@IYeR7f[ SafxSv*h4?_Y.S/Ŷ.Je&["Nh~h"\Rjx͊]$vzH ,1Uɸ 3eFB*5:aH o5jivTϰx6281wC{P7}̹9$bw-N*&MRAfVYkHtBmMۊ6mۺeǎ2#Tӄ4g YMR?;vio^YQ~k}Ť/'m|nqϯl`2׆bLceI ʘɸO %#GZLǘ=ݎr)%)i6r1utWS,p⚳SGzi11aJ3)cɭV0LNgUE}s:i: <,i*i;fWͥl+k.|[@n1'Ąnmh,\NbiWXUTwZZNńl?0kL>:`+8y:/3%y`kwX645m_h;QשKlNb7'r2SMk9 .2fE5ƴ| `SJc =cǜ[%gmMoqr:4WGZ,}LO b tXCc{3>~gӫGVSm sHHS0>sBVlӰ1YZ:uyp+i#-ܘ' >Z>fOs&9*1]bLB) :!+iZbv3su&3DZ,T_Zzu5UL g?5654!Ee+[c<6X,u¸$Kl+KöL%NST666HIIᔼGFcd3okmQ ydʕo~ m%-&&P ^Ϟc(L#]]+ @C|tJ U۶n)*ڴu;o߾Gy̧m۶m֚ծ;D/Vbn$舘hә3gZ!yśu5;[656._&zkll)M޽UUW!y̫.&!T]8/fffHIdxx$"lN{Ύ#IwiZ09 HIIIbG}Y STӂ]X>Y,cI8=MHsʜ{fZedc?z fyy˗Ex ۋkI(HI/^ZL!CsT:՜Th8=-czzm(4VN1jbLc^e|BG OY+h& И^ ј1)c$^c*D"hԫ1E:i;:h}6Es(-cH#2caSƒdMgRW3:-Lc3,c<ʰHWcbed,IkKLƚ/TOv*1Fb7eue1gU1-uk8"iLEgJ=`۳SσI%)59Ę$ 9q ^|XLeXJL1u) j"iZb}Tو)Z i 1]+}LY-(c*NL5}dM1}!$iݻw_|QWSDIӰ1YZc/>b3}GQ $'1f@=aLy}eKOi36:::2akiةH4h}Ԝ -gN5{laFs֧n=i_h|tzi: Ovo՘5654!Ee+[C3?$`^Z[[+ƴS,0qLV0XmJh-4>ΤӰ>b,6t".bi'c$H6D6vcF@i1Y ƌF1&$Q1+++z{{&''&Ѩo>>63d3333"ZclL:6.M)[2W[AUՅ+$/"o mʵq'Y׏,W>Ce&q# #gsYLYXYdYpEkdd7AkKp&RdQRwxxͧ*ok6PDҥK94_ڏ.+-|$0"\{t. Qd1S,a),opM:x`dx(YxѵkWW],)4%Z2#qm>^%ҟ2yC>= qX̉ȦzYT}i.W{m!.McPn>Af$ bS ~jkkoz?spA)<~*+ʶlt¹DBC(1eH1I3|{.ضmbgdY$RGgܘbQ1MڒXR%w_$^1`WGt$E©S'ٽ_#EU-^ze[[T6wJƩ;@jW~IrƘب OcO<׫Yތ\FNsbڧӹJ'SriKbIR8k5Z: ]?v;wlK.ܶmKEE@_rpzЙb1;;wӶ0SIboY'.T:1ј\:T/]~?~+*mi0D8(6%226oL2Ϳ?ă \7@ooC$Ċ.lݲ/5VK"[ O=M:d}Ŝ2ܘNk%Ę4֬Y-~|;Owv_t훘1u6 O~ܜK\M,1A=݅5}6~g~n^3'krH[KrH<}ISz}ў$ܾmsRٵyʊޞnc$Fg&ʓϚL}:6KlKp`,nla~h-wcxJ0f> NZJKKWF i8~^zd|x9ԗW) MYumʸ\;-Ed`z[XӒWO_~b*6uI %N{u$oV5ʤ5ffzўmߺ9qRt{Ν;oۺhӆʊHfэi4T-1`*SֱLH= HӾ%o1:1&̈14_<554l\7OSao,LIČÑg|aw dN%Nz}mw"\һܶusĥt-6m\Qt)6nXnMyYiwWWrэuZh}TczҮ& hL'ISS5jYcbl25i~߼vqOLNwV6 >6@vӬju<6 CFĒ~/ʣ 5!8N%S [h[bedX˘HwWgscÚ5]Kѥt+ex_e6lXvja uRوATkhuguLֶĊ4Q} "Ɯ2马5fEyYoo }_QYF~613>KA+=y&CFĒ_ybW&2lz6ېw_1{"]鏥/{έ.*֭[#>橓'5ѥN5i1(ɧӘm-'++-'FG62~7<=>^ZR"UYnL% ˕Ac)(cJJKKv% ++׮YudI ԩݕ'PXVPq6ڡy.\8V!+y&VD# U:>q'])oΟkjlMcϝ[U'Y#5~NlG\D~ 5.K< '[c|lL[q&Ƥ%O4.VWB oe2)Ϗ?NȳR'7돚nm߷(#Z[gYM:QR%Kr @J{ydk=|lɃC+M}k%@ٯ|I##?]} gK'ˏK1#/sbUqۿ=fc:}\_|锭Z[; c~_^0&t IS'm_g/Zct2;o{}j &.VWy}FAsϡs,ڴq~b/E ڹCmjĘnQCCSlAB)0d-.m r3g\bLcyi_3 vv=c=5i .0&bʏd^}S.v].%5E4nGLZ%k*tt3.1fMl3}̎h4LtGzzz44mPJX i8*kU>elِ!Gʊ+ThL[3N4uҟuC5&2Q4jLGPE2xTVzcQLxҟ12{TVIrTl6S, dQY>?*[W[s[-_T>= zݷ^];:ys7ߘ,c׭y7^[ʫݫ\gɫ[׮-//sڢ1UU~X_Z Yb.1ѾzK۴q}sSC{Z[Z/_vޓkO46x}I[KSwdڪg5VbL(pcXůF{lY_Wd+asŁŮ+sBHvGl%sٗcЪmJ*Wd5b+q+aL1ژ c3)cyBH1LQYI!cbLB!ycި1괍٬cBYc6wtiM-0&!5XLfƼlBoc#0f11=-uƌx,%5f_ƌo_ݻҘtK !$9`榆Tw_-&ޜh|:Zq:MEaCkMsIB!bLӧ{Sxܷ1uJ~ DGM5 !d1s㟹ŋ_I[^Vj"cڎīsCŌ9hΒurHB2l>[c4I˜'O=S nLW kL^c6u&9h̦luu?c-.;wyUEc?rB!6f1ŭjyOKspHFC:*."c\zØeD!ƘlN1Sqϟ=2I!Sߘm9lLB!$tcaLB!D˘lkB0Mc݃14짟~:99966622tuu6ɼWWW?wӧN:~ءC_Q^woݻo۶ysƍ֮Yre˖w|^{u+׿/~ӟo?}O>[ڣ_|ᇿ.'__=woo?}rY+|;e5E.W*똬i?u}ߐuRʺ?K˚_JjYx/]_^v%EyYl#;zTvΝ;+QmmlSeuvvV&Аlwx\AͻYwl!1[\RcB!`a'ci̥mg[bL1Bl%siS4fs rԘb֤1Ɯ? `!{cvv`L:lǘ1q2f'i4搭1:;cL}e3,19ܘ jYZwih< ŏfcv%Dܳ{g?dq 4]sTcBI1R}TW/{]s݋(,/+5ABH63>ܘj9;;SO{W^u]I!$ƬmnjPs1\ϘI]NMM,WBɨ1'94czwClOn]B n3&!Ic9c1 Z/?77wU848D{[뺵k:椾|1X>%Sc5 1x'm|iLO_:9p=y˗/ |.Efgg>g3!9moqƼ&KimRmRLK~Lvzz1#98oD?X4ڃ1!y194f ͘ h՘ cBJ)rWD_{! 9jKƜ3cM1mǘùi[zlXVt?{@?Ƅ|3DnSMN1M?örR$҄9߿rǧzd WYY1:2,%rטrۘy|T6\crN`HgIHexUh_oTØƼdkĄ1kk\4^ϣsUs1!LcHH~^w/rb|/ړ@*`LctJ,Pݘ:3~ݘ^ Bvӿv1]]bk̀ޞny7.|Ǥ0<1!'c@JGŢ=B_QW[{ al4quzjj@1!i4?cB"Z! 9ڼvv)U4-c^q4dL=]% jLcPLH_ӝfSTA= kiNu}mE&O\28?gK>{6m$g+Wl93oƴ:¶ZO3hժ\XӀ5^N㐼7@_hߊ.nIΘ3ƼXBVG1 ZOTUc/zV;}chpc2XVט_. B c8*Ncz81{_L{{{[Ft?{^h/$9jYc^|YۘԤԘj2`I^HYp싮x'hlv/Z`*++D`LSƼpӘ:Ƽ<*k>jھ$;j|R vuu>3 i<Ͽ ^jtd$-HIr֘ƼrcǶurјsytJL Ęa`` !{y߽/˱XOw$T$y%ό[NȘ꯷̅[c~֘1.yi~_]>ccccRDŘcf1 !)5scHtwG~HD F>$9g̫WSL1=u]3իʆy rȑёީɾXA9|X˫h:1cRhb:UVZؘIHMF#h*0&!yc |G;o_r'Y#5L+ָSƔNĘD$ip<+u%Qc΅`jd:8GH|mƜ57yՉBHs.Dc.h;24=eBg-$kl[\Rd||,648<+uh(3ccbLpGl80?5557w yv_j\ԸVc cݑ񉉡'kԧ 91@HWĵ_5.i4c&`j̪ .`L&>1~~/׉5WɮfnnN˫h:~cj)5]4v5Yb`f[42ME1-M%]8E΀bҊɩ[ 橓'._,g?۸a^>g3!%<&KimRՒrΰi{5=ƴ= yo̾h~ҥNvj/:{@_gƔ?'&#}L[S[cKgl-UNDcheKŏO>x}]mOwdo ܷ{++*D`LUc^'#Ge ͘ĘўΎgNBU~dxժ#CƄ5c`̀W40q$:WNZ8\${_b,6ՙ@*`LYnL&߿p7=tv1KgkLo(⣹ ό33\7ǎbRD`L;c~i ]?eޘRc~ч}}}#]Ӟnٽ$ FGz{{>Ƅ0X c^lkO?a̋nN<&i{k  N GhtrrR&gΡ\0ٚUƬrub ;rat>X(uԧ yј0&h"Lٓ?vle<+u& dK0&x`kKsuuAƜq04LOMN'gi"acszcƼi̙sŁŮ+s1!J%6攦1ϟscLB!iLS[cNM]7ts5BH nƜa NĘB טBƼiO99ǘB02kL1:5YſaӽXYO!81hL:@!3f<>RcWYZzx޽{:>NcB9_W^s6;UGe}\.cs i lyч19 |Qq-yWј}0fҘc3%Bj&1]ُ1cb̘1z1&`LWcaLZƌbLIcً1c9jkP@b̕9Kzs~䵄BmV,11=BƼiQI!7f4ڍ1 !`̤1c#B01{0&!-w~[|9!bHx#俦gZ7vSTvLǓi0{01IsPi:9]*TUTLWDc cbL5fwwW>S>*|8i tu|GC'ۗiEƌ`LUqWc+2hL[S'hL̓$NkbCF9:U%2yL}L]s,:oLtڟdƌD:ߘ>6mM^2QYŤ>4}c˜{5VӘr$k9do̮2[k(W8WjO|,;WuŘ$9h̎<3΁cvgoyLqO /[Yair:zm GeI63IIATscz m1;;1&!4Р1;0&!c6I!ccBFccBloo јK+ζ '.MOcZ 2J%2sɘm01[0&`̛7fy1ي1c4栣11&`LWcɘoj,4 8Zb~'c6ژ%{oɘauOY1lْzcٽwtj2՘Ęc67ǿ{zATC՘lLyC|_'/2И ?"M`̾^[c67ØGsw1 5lH1wqם$IWc@N|]]b+SKG0f11۾ IcfؘrĘQ ۘ u0&d>c6cLcF{Y1c٘+D.vML\D9G!dMV,11j0&!4f/$B\|/hk5fՅs,$1W\647)\f[v $R\zզ /}ח,yu o˖_vuBܹ}usцuĞE77mܵc;nޜco-L$Qh[!!ik̽)[\VRRQV|eE)L1ܳ{wƴVcB9c~;˳i];$B٘ӧO|xi<$KBHSrqů$-/+5zcB9ciL!st>t 8*bLYcڹ;HzS>c:9C ӷ1++ZY(uƴ=y 3=H1kk6n(޳ɘž];ŘR{@hFwcnX/51&1njE&5fdo1taL(Xc=Iey˜e%'O .Lnf[k^LQӧO|xƴcZǃ. !$c̺ڋݭ--9yqů$-/+5d7tc:Y#1 !$)fma*y䉻ozzZik7qmLBH3w0[\|םwJի:tɍkW B0f%%%Ϙs.5&.!%w#B0&!11&!1 !`L18}BI!_|e_eE? ǘB0f3g>yo?vDo='ƘB0fcǎ-Z;O?uQya)lPl| 1 SYb.?m-wZp߽_~!ya)  y`L!%e˖p&iԶ S() FCv~ؿ5= rL!s=BɖwZyi!rL!s2w0?Ēҽ~\r%@*ۄܚ,9FΪyӜZi!WWoi[b,dISVhpm'|*-ĥ홺&=-wA:%^Es= }̶-Q16W`YsCB]p :7-Եwvr5լ]z1Gu%:u=5Jm0󬳶L*cjnYǩ׭5K|z:0OC^1];:GD~UqLk^oٔOY)&u#>fNs!{B̩vSƏ O GWHW5?D|1l[sqz릔Geu8\{G;GWt{?iC󦾂BSsj_sx&^Ejv=B*[N=+3Ϟvӹ:+?'2Qo 1Rly'`/ObŘٶ@|rE/_wyoCZyiukWm"zxE_"V1 K]Vjuk@FXjղ{7vݔpUչy(_u,K~/b@ ] m۲y׎vl*ot6׮^pYdo .2ӓg>X΃YL*֮^u˖=w- /o/_4[y :ι,Q~/b@Vj[^d/di|y xo~gΩx_o4ӝF{ddxaՋ Ygܾ̕R^w rsx|7ݕ @D,i4,l6,&d:ȏ1ΟKWAFƗ`ͪK#tuIZ91f+Ǭ^L8E?gJML/wT 2*0'.V_Xz];KKWV@FƗ`U˖+H@cvG::&|#61f6m^4o}!W1cbL~c~ ~Gbfv$j:;uXvgԋ>NF^љvm͵fIsԣrj|Ncv8!01bIY csacsZoLnY☡'S(d1өiT&ŘlokimirBSuab8E%F3)RSsaΆl+*n1}gLkuƦcQY]^pf D2n̶'d#U&SW,1=-utߩN31]j";1 aonL1!CoijljS [$1y0!60M݂h1}xʫt.qt ַ]-GeQY. ˜ lnjhUc<@d*Ikl[U֩(hn1fV]G1pr̦TΕ9WS3YM9u˜cBF)=ڋiF6pG(61fF6m YEmM5*c;ۘ۷miooD)7-"Kd/l*P/&d֬˜cLyGgϜ.-)޹s`x5{7e=ݑ;d~,b@V԰qZ%\}yG|'ݸq 몫E ,Dy: Cs3T%eKQ$ݔ~C6m|W/.2,W,b@GJčsuNw$*^ɾʊ.2\LȖu5##ϟ=~C_iF?qu5sg?p"_ i1 ;Y;::244#-?<4(|arN84p}wq[o:@k;vr'\bo^x7xGy1SÇ={BɅ9x ܰnmccB!$"sl̍b9B!$w"ڰa? cn۲yff'Csmߺk//ݿh~I!8ů,G<Bƴs.G?/|C=׉1 !1n)zE>L_oouhO=س{gno } kIqjiOB1?Gho]ʿ/J36:4]kzTV^X2.!-k/vwwcul4l|UաQϳ>W;5ӧ?BHS\YcХ0tc*eݥ8VNvW1*O Oh1Fs`65RBLt[;j*fәS..u40 Ř~cGO:p5Խ S=E[ԚV15̚ܙh[MѓU\ۣS*yL.K7<lts`if|b֘)cU[ҥbmtBHsjr֘#i8*z#iu߮ywއX#[657PDcB ǘ.MsbbfƣK^B0f!k> !`LB!cB!BBHNh=)ٵS)5sN鼑z_GƬ-ڴݘK<{!z^X˒ȗB ٘M [lv5MR3Įs8Z]k:A?v[BbHWgbWcٽ3ʹ;+~CSU9 ^7'4f3T)Y^VrqswbWW\1Uo2XBq6f}]P}}#UV.<NX]588PW[swbwuTӯz2.RƜf`wÈ  ʳ"րLu+;S}W]k B!Fc>f. BLtr].3!`LC!yҒbI!?B1_]ʂ};o1 !`L1Wl~-\~1f6߉]== !$uK/zgz{G{z;Sg\ јYn4?Gho]&~7UJ~W/wb׹NͮpwBI1[[[{zz?YW{%=}Ln6=wbӻbA{BHS\Ych‚b&5gϩo_qwBɨ13r B0fN܉ݩdۓҜ֜ޝϹ;!`lAQ\ R#Ĺ4_ӢT#!`LB!cB!4}WZ1 !1s_쫬HT~^|BLr'=෿ǎȿo}܉MN9WBHpc66;vlyǏʣ Ks'v3 uR=;!$ό)޵k}~Q$W֝ﻮCf5;!ߘB%KD˖-4%/Į3ޜۍ]:K;!d1h.ٱc{OOdUugĶ#;۾D-z;4?x2&wb'՞Ν۝K#N.\hF{3yZɒg~:QnZiYǯSin5$*)S NՏڙGy1;.:yBH >> stream xڭXNH}WcPq/E"dDDl`%< 3 B><3HNթ:t[ KZ#c)R$ECRTgIO2J ")IʙD)0V7ikuh$kb0d' ,_dI"'Gw@1 d"y3AV DAc`$E("@$%PFKAIPXAq ^Ig!. E", RX8<W(@=gZ qk _n@\_$p#=чD 2F'TUD1XR- oapD  k.&'L!ekeȆ e9gtJYz$ r-AF# jnDyP&܂.2Zb\(Lfsu-Bq=4Q&{{SgyRR^٨b w͛Շq]Y7n7êيfŤ⒆ŘF4ϏTL/EU^ӐUvmM7YU?Zr]ctZ>KSqwf,^p bwC1nu6{CM̋`6!Tuyu }0&ˬ 6|+lX 46Ûs\rʳ`MVA[$TYvЎ1YmR~:ZW;1$__}郢VP.cFdu3WeU?\?'Ygu8vlnU Vd]yy Qf} ĴM1=4+0{mwz?|tF APkk^IFZ%kt}\’ >NqIrZ^z e-ʜh6lNy* ysƮ{{IzSz4̒CslP%[&闬.jݞ)÷-,D_82{a83drVwo_+1Cl3g&yn ӣg CJOۆ0pK#~|'- Ŝ<|UW.>qmC,>RUú||iU觨:=` _xEQ;q.+!Izܞ7c^|Kҷe5ΪZ)Sg,4*p>$x9Pݙ5 T>o_ehGa\zv. >al@zH~[_&'ga %D&h^,;5E*TqdmE~Vm> stream xڕXKsF W56\.34ۨ$̊\YP "I6K,W*qifHD%NWɝ/j-n[jݵMEǡ5E3t_)隶+zqIEjLJRұp*|3Y'"^5V~u}kx램G| ݜznWkgph5ao؂a2\ZmEVP`eG/߼pW|/*fIJ ܙQ*_i{x%8eI5&{ysM۳/i]pH |y(|W~˹%rdmh1NVYYOAao=Dx~x ,9C* Dkf||8H1x [" $q`Co\C)} =+r( | e=ܧPi=Y5篶(`8BJB@qMU$NF^*4nzL= ۲GYfؖ|#]sb5|Z(QhO%vD.HEOP~N-+-ܤk X,XGHoTe\;m,$q(c$X|n,PScHC egsypZ%P c":_DDܶ6#g0`ctK@ 6ØAZpW zJ (tGwc=<',Ef}רײ@~ز0kR=4xoԴ~ޘ 3u^A'|P,ERXvg5TTb0P*D; 6WmzLEd!#p*E"GH"4x+_K0&MR#3#&(jsA3`)z*ѻ @GQ|tۻ%hWO#eKΏa#YV vƇ{;Y&?A9dK fnV05[p6Ye< *fF|1BB0'GTR3 ղ׆}3C--:Kmwb5G@!k Vw=iF C@ V]MDhj*i Q=K,r6IewpDǞ<[Re|2n=4Fo[H^$|C ,c3~8-* evW!.lo"LO/Ɉ-F,Vnc=9Y,SSu'ȰoOl}[Py>C^K-9;÷] d|2Aů̞#nKbxBU?zN/!g` KZT8Oλ?.^Om!`%}ᾛQp'$"L/s4z^,E=V$݌)!,c5Vsem1c#6T1oI 8ԇ endstream endobj 206 0 obj << /Length 1827 /Filter /FlateDecode >> stream xڽZmo6_O2,Wd]m [ѭb)0Gv-9m7`}GRKKDHxw㗒M,XjB9DI E1yq@O(${G׺(K%LΨnluyz^˫W۬.eٴy]VO.dex\y~t7 *P\l"{,§vF˦]nB\[u=d Vvl[7uN! zC Ι:cSzS^pԳL>b[˷izZ_ipν8p䆯ybV0߄@??qWnӑY ɋO'1 xa`LoN޾'pk&ԛ 8/&oN'Ģi-(a !]ѻI؀ @/‚Po9>`5aiR(`:)E>fW3s"lw7#5N?@,CQ*'9 ,{Qf~cv5C*p1n"زߖ˜5?Cї ]*;hf ɐ5@@(+ Fw kkl]\iU 5VrzH6 a Bd Vt uw]qw }*U'&Î0#rA # TuQaqO}RD1 TQ1;G']JE%h:JԀݩG/!@Q@.ֆ ,|z1C~*Fe$A |CRDT(H5o1C@ٮLit-'LxJЅ0p7c5BWOG:"F;ۧ84gF(KQ(R0@plB؈0\`A@!04yQ;a;kN#WҬž$H9>`"|(E|\B_RE)B0rg}a»RRP7h;%c3 k@iwf#@IR@0ki0+<$RرFKfXbO~͋Tݽj@ L+$pG+ O",3#f3̤=0YS ؐ kTAm&fS,܆6U5nv@G.'X/T(w endstream endobj 211 0 obj << /Length 2234 /Filter /FlateDecode >> stream xڭZmo6_O|n{K[%oRjݶ7CR*DEy89(%lW:3 Uf5]ï%2r^!&|)EJ4j4#[P~U{Pϗe^B_uմ__zwwb%|c}J$rC/maEQ%D 3K؁P-'aZb~tx>?{f70YHhʰc:Y:iLD۰0bij|mAb~ozBlR&lߺE+SXij>?MPL8] sIAnBaMzm0H{u3 Zp3\Wn\/,΍gZ6?w\@S>lq>i[ !HaZҩ<Σ$ y0bd!VůMHe{wl;35SCٱ[# {K3`(Fm)Wi7e8Ok0th"u8K4#F@sFNI[5 0K_*s`^O}Edp*G#$@P9r"Jw]L:ϱF µB%a˜/U۷lK n}j?+oc 6v $BNru=(opl?Y a > &ooE^Пfυk9hjWW]7\dz ?\j e-)ۛi}<޼*~_ŵi⍨,b1Ҙ)Dp{/fggPM\ nID\ӬX(2EDpPD2S80uÇaP5"LPe\` @(Bv@6x-3<ڱvIp/{9oCD1$lRus| І%Y7 vS]bA zkAN.r?tHwI'H?obNQF9I7ˉ],#R[]R""!\H< \qq'%L:]ZΧA`}&>W':ṟT>M@Hӻq<oLg`!=!5W1{^>>֫Ю"wet-Z?nbzH!hɫ/ u<6Y?vtC1I|!\/7-|&o.a2|/&ς@Skz/hoc tUQ}>K29#:qy"_ W ]ۿyՂ$+ᕱ;n/yPxN7mQ|5, s,-,ҬXMmcIy;T4Ǥ(1 DQIf#U }&2/>ɸpPp~pKXjwrv|fE&y° endstream endobj 215 0 obj << /Length 2053 /Filter /FlateDecode >> stream xڭXے}߯V-sMNRe[Rʱ$ p q=K*+i>}?,.}G(RFuYGi mh^k]3y:qմQpgU],y>T,A>i UKmc%5V}>aak.h(>q eƋf,p{ór/iD҉aP5}:uu`]úvkjmpx~>unw(;ewx\˲k4fS˥k[\8]b-_$=Z5`@k-,-[slq 2RI3aRKm^~+,)ߺP&^6IЦ(gzk2*zjd &j{tzg qnyZanv\o8X7RZS{Pbg} a`ǁ"znMQr$yxl)f˶=n=la8{dl]wD/nhU]SoԷ'sunC=bivvUs3unBcL|cφ lPtjEam4FJD0^R6͓'?I9|S 9;>ƕ0n682Of0ž%NohOun*ʄ7AAر=VeWAJGaJa2Rx!OiQřA" !E¦v&DIE4*IAi@8V%u6O'uaKF'OG q{M6c:a-| !4Kk˴inwً?:-n~W5]۹{'Ny&0YEVɔQ*'|'> =če$}|>UA26τ,}hXҡj@%AO ˪Js)n~~&2J_v  *H t- (TN ɄIʬk`C['i'-`|VR] @8oYSK:[o+@"]C lnd;{NRyMK"=LDŽtFڴ!^J9gH6 |# LӿH3fHe)4:()̌AO$@%h ]G* I Z/fCXLpx귓L02/\ٴTJ U:RS8^%W>J ܍WJY[os4vP- wSCjYw-Zx30 `yf3T|v>% 7)2ڿHe6MYDXe\- 4UwŒ4΍d=/~|rWW~?^\Nb] +@,'(!CN!:64 "M@Q6 endstream endobj 221 0 obj << /Length 2185 /Filter /FlateDecode >> stream xڭX[s~ׯ[qL[I5R: D*P0HNcgwsY|s}իxJ#yOЋXzkdja_TwwU2::|}/V[9e>EM]Vo{&EdYe%Sl|]W2o+3zG||4(B0];&IrY U)M../$8C2VwT60c*% ]"V* {Z wd? 3H*ѩH,$2Vde 1Z조(1kmNY墔̿B -5)Zg{>MvyNL% H'tV?3&T 8d/X,CC5Ǹ5C"Bj~ \~T\u`"gj0g'JRLnRrǝhR;-c]`.g ^}BfNK'lҋ`RmYFL!c'ý`\W2x뗅*ydnzQmwMIQ Nˆm=W:`BZp϶k9Xb! T XХA_bY9|kh0#gV& @ohh郦-$O8aJa-EE `?^bJ ѥdQFxq6Y+ ^  g5 nkvhp`K6?-ݖXX`\* ;h2$;x0 ^XT̚x՝8RFGrN_8֜_cSZ()ă2qeF81i^;6;;m:yH:P[,0HUJ}zF>=}20BJ$4鐰I:aKIrZIA\/٢,yD6*w$d7Jz }z8ˇe5ΟSHbW")+ FAƬ,19>%`sCqحQ5qvCg LJ> stream xڭXms6_oGD(|N?9usniFCK3_\Jv߻/$tr9gj?~š}ތߍ0/x;E>, :TR_7ؑ~=r   1,X#2#Ն W] j)ZA1e,XF6K/t(=8A͕7NȐq&pY2K"ʈ4D'\a^6- Gşs(\ 07 p]h7f0)2L$2cGX5%5hkFGA 5p*l |4qSzѻdԭ1Wwf$JEf,&5W,/wPE]"ՁG ûp/VMU>|HBTvLhe0Tt>`䯨0CkTKG0" ;>'\mݍ8mƉd#N/^MB-!VGFa9YF";HuT(՜о9)OՄI1(ep@ɚ^^{ȲeJrJgJaVZ b67c CI Ln1dHn&/*EA*Ț(ˈrh].QYD, NPÉON@F'3nlK\PiSE=2 dL`w|NHL)'FP,q?JH3[& [8=c)y̢V7xJty̜iSݹ]ZNQ'f0nQwO_{d\@O=* j̖y%3\J*_2S T +DG`b"}ӏ;;n_Y:φ)K + K:%;ܞ{<)y'D ]H$շm<+1<a\U4gYCa|NŎ 㻞;XaPƣ(U*b~58x qrxaqj8 ˎ4Cˤ:xJNg"A e UޮbS5$9M`3"8 8酓gD夏/r4;Tac/ e I*i FH}Ed66Jp ] _vFu6 ڜl}MT>e2}QћHK:V{>40y~\;f~GTlXf ~8DU}6^܊Ke`X@?_WK endstream endobj 231 0 obj << /Length 2259 /Filter /FlateDecode >> stream xڭَBO ;l &y] ֮,yE%[jkz΋T*źwW{yX nwJ0 KPip[?_oX|VʮGӛc 85@<|?(ؔa%,?c&³Ş@9J#^ptF[me_5-gVln#u=<{^|Lg~fk$k+`$jhT3]K48Oa UegyHwW @(4QAZPY=\KT@ø,Gb=q9@MWȃ+R7$**r^|*b1O@"nE|rpIxeJ6.=0Y~Xo]'x_;t~BKZvIÎжn$@#? Y06|M-n-8; Cl4`8^UV|c0XTBoSoo"nA*`28 %KB պ|SEӒDE1,B~zqfC):~TB~dmQ<ɛv+Hr$pQ&B{sb+$ uVwZ3\'G:\wOжGkϢ#JqW#HbZ$C*Dj8=1ikG,IJa[1fq ދS:A^fIM1}bzzZR0HV/O%l1ztV5e5 k8-$trY?I5K/R\ EM0tT8DE7 ,٩VGRm3BsDMe;H41?]ω`>8 Q>lVPFl*lhzycݡVeԡꚮcԹ.ScV/nu)S5(sIDl$14Z_2hoxϞӊʏX w+98h8*]^(eee*OX_|ô -: gwGJ$S8ѨdaGذZّbNT*㍓cJ Dg).eۮapiKk"жxkcQ #!+3}74 x7S;t/S+캡?G7 *j^ROcPj,j"R7s7S@pcëָN_4;91/w M3to$JBI|vd:GOk<-j< ;Nxܥ^kjܑe%k*z 3Zcj#?HQc2~ d@eWrr.D\xN3).u7_eե\-Yjh#2 X]>}0q %%~4i߅ۧޅt&Q:{XXe|wnn@-CTx( De5T瓗 8Bƥܙ3t@ì7WaiwjT9UU]JMܐ onez?U ,rb1k3)"x@SS/`NIA1:i` !6Y; HI S7(PͽPY衮@3pjԈ g[Jn'WnN/}3_y ~CCJ z\S6RF߼"$ tX先$MNJ$*Lu[nS z$I|3)s ;nkYPC"bT%ub~׍ N 9y.?%~G?H3/JMK8)Hү R:CA8(tJrRjm endstream endobj 235 0 obj << /Length 2213 /Filter /FlateDecode >> stream xڽZKoHW4QO X 6HYܑHD9jREˑ%l6W__L,n~'<Y5\%qX%c,)%a mťy1,O`R;ַmc-/B A?%'bHvK*ǜᙆR0J *e D\%پG2wneE{\~} ̗M0Yؾ,HO #}vcNq^B 8wNF4D?CC7?O DD|2$̱< DqNE[z@Ds_vA#[lq/1rV tYUz}֝ONCP*EQܫ#& Nt7͊*xBPZ\L}$X_2{yUR]&nS(uj/Nul]VpMj7r#dеnBu:-Ҏ(<=h@b~4F1}tgkvL]V;4̛ 0 нh%Q=4|=-8b}ӘcBjy_/!k13AZV46{;Zz;,y;~6ɉ"0)D>S Yb_T 0?YXO,})8J*3 e2 _\ WI(WfE 7 c#But#[\v @B? dW⣸R{& 6Q)/ӇP7f C˿{=mv@/`6 l˖rHSOEf)@MA+™U3x}"%%p@]=GQEGQGFܳޔ|m;%Tp mܷc;| .ow3gCچkUD+HCU!UpEm(.>*> p\8ovu8>Ž>8ryޕc LȾm`n:C6Ƹy ejQT)&ߵ [ԖuYeqH\V$ '] WOgN[$%aU}D7 (6rيp#˅kYHYf@ lJ`>eӹ ؤ]`OW eفM?dh!\FR3}4g^P$ U`@++UlP/><=soݟ&Yg7bp4i+=*Uר+Iie\1nQURfI:@^0382Ss #iϑ:Alߑ7wL0UL4{& +Wif.߈6 < aE9tGFN@Ob=ݟ8 jӏ*luOb{+X539QzL;^t5S.'FnHi`K/&8#?P:¢ŠS5uOq"5(T&yTWA/[V endstream endobj 240 0 obj << /Length 2344 /Filter /FlateDecode >> stream xYKs6WHUH;d33[O!eԐT<ޭۍiR]Wa/l4_?nD%3Nlm/T6ma./ r.M>[K_ Zw"׏Ҧm- /oU7Ķe:B3$ yț_(AEcNm-l  14Lݝ<˙*E:d$Ͽ,8SIdD{ԁIC2CNj0;-.=J)JXJp$\` 0piMiRԃjn"@`t^6R":s{B)/j%Af!  dz8aDP.?'q40jfXbV7C㴞/B%HSoEXhPbC8ݯX2 fLoH;) Ӓ)-G*}Cj7>7U;Xה[|K8Hdejzۼ8XPbJJ.z}9[b\omR檂yZzhza2D@'axsN63Bt)Wg.4*:(Ƞ]r ?8$uT["mmIćq1]oЋ1Yua%q 6[MZ`2 -`3K/0 ئ܉BMNf>?l۹p#$Up$9 x:5h7`W~;3:0u@zsXYРRDu?!Fr,GLD4 ѬGѰA>ɾۏ8]SF0t)AR7th'k/| ŎAG/C,KaO&D/a e 摲;G) >h?|PSrK!Zш_iDLMPguwTcp!d%#kXKo%aL eiZv/3mܳZ)ivwܭJյf_R8b7Vܖ@ M|S#=L7`\@x"+9AEIwASw|}- n[n=T YvBXtC \xs9~2ve?ݻ!|Mއ:\9-{,G? OW>U!9! rR"Tg endstream endobj 244 0 obj << /Length 1938 /Filter /FlateDecode >> stream xڽY_6 OpEY- :\z}bعӏ%8i{mJ)d^O^b,h:q)IQ RS$gf<\]u^U>]_46_RD/ߥ=hS&ӌ}4y^5IӃ8{,5m _~5/=y9=nme4]X)Y=-ğz[doUv֢R)׶ݬBva^VE9~yU(6?iڷ87GRǯoċɌ{d&z#U}UJ%4VaR}Xax*B^D԰4'ƓE`/r +u' 79HsJ`E4$^'}͚]{`>_RCʮʕ}>SJGdWO>1Ү tOH0oY[VBB"cQ[lm'!K$L"PUS:_IR#wq,nNQeRUzM4%Xߒ d3B,:\qTB)dCg (x 1xs*L9^ T."c g8X|W2_)K]ʽ%Tqezw4,Z +Fzrzː\noEEz;gR)@lYUH D46- N87~"b@PFY؟Fl@.qv'B>oQ(ZZ*?\b"E^8u;I3Bc{󠒥}MMm[Kek ocuQ7W΁߾]U'| &|Y tdfr-;.* 1ܛr$6S4^pqZR"iBE'm w]}>7 dB,6ہ̥]aL.O c] C+pΖP&zjHw(`&ѿ7.C'\k/w{[ˍїwΐ۫8q2nkS%,6i@}-yOo>s s|;q}g/ٍl  v,^6-o}?&OwliTx`^FmC߹Mgpu}S'<{> stream xڭZmo_q@Bu4W `qFZA@+;Bl,y%G>Hܗٙy^\<{Eꦑ _k7EƮXhwӥìWi&?YyX4eusߟJFXO\ςKN24MT%d}K.S|V`Vjfwt'fѓP&M6?.7&Kcoͪ¢cĩ%q /Қ/GvA$YMY + [q|'OadMUq.O8A/no97oQ`:Mt@}:^r$)#Qnp/ow! A0i:u? xJƞ/J~U&a oՙvuq}TN_mX=o옹0nO\F8a'|XM Xۇͺ2/S?T% VMӞ)7#$p$zN8M5 PV4L[Ox!=,?zAT߼%W*̗wYCnoE[F=<>Z? Hxt,>ǧy|(s<*׏=a /a1bQ.8I;Q+ޜFOK]{S[Pf^Cy&hzXx,F H9Zc7+k[Q0BOB;(;sTnSg#3{N߱iX8zg_A82_L4V*>SXBӈs8OīJߤ# 4f9 ~Dn*c;, .Z&e-ő $VP[]I{eVȡĝF&uIqּX Q +`wvc}S/@Sv W x3%=By._ƸؙVZI0dΚB۽"2lM"*VֈV8~?A7~gbLڶYsۇ3V \˗C&G:a0q M.h״#Us,{ 8W~X)G"zǷÝx^$s6h_|CΉ^qK@H=RO "W.`*b*!BqE2'EnlґYpxC7x5>PGadurVO]HCܤ%!s,[9Kz*[Zw qL. S[.l@խu2B*sr1Ya>֣@ɤQSF:QS N#G`=7Ibpl7FY$6 wf!T= CQϪ7I]3GU家ۻ&iɥzL 7l/@bYR@:D3` QvVgܷQC?vpgIHBcm OPK"-|K k\S#:k$'Vlǒ}e3ۙA[<^"F$ HR=>"a*_ A9Ȉ ESb/:aV<DrkXjȐR/ܧ,JM#ڲ~3#"smጠ驺 xҙz2lQĀ*W rm J&7=4Qaᙥd7QCT}T i0_ro!$((;,glsQGMk,N\ }@ܺBf} َО.S<yC$LWB M t_wB߃#soG~Wps+| deza n2Koz+UrGF@y"7bL[4U1ԇQ6NʪQkIcZް~ˎf۶3GwauvܾBU;F3_2s 'N*FmN& yjZ&Wž׳#?u[Ʈߢ 8$׋ endstream endobj 254 0 obj << /Length 2878 /Filter /FlateDecode >> stream xڭr]_C L03C*Y'Nv'.LEIC>}ahEnŋ7,(f뙐ҏd]f0 w.qz.a;^MV-qacfƬ=- ,Cɗ{X;^^e;cƕק΂B?Ֆul "k3S ϓEw5/0gP,eex|!w@ qBbŐuUMPMk|EU NY3X!=_x4pWT8 =|xkP2!^oHnm2vր뵩,ߙg+{ϷbU"D{E`_B,2q;k`z<ȖKlM59ؒ~ߍ̣SVl2x=n$ ¯%Q1_y!VuFgl7,[8ͪ[}啱~Pj^!7H%yè&ИNw N8lxw,rDL|)JM YwfEI`D>[ O6oZh͠dn@Nj7:Jru7G/Tuvk0$%pF"qhՆ ⍧n nn=Vd Ϭ*`};9,DQ-XdѬZT+z}51t@?[,"n讴hnA>Bq{2eSF)1…ӌ H*@Mv٘7XSA^W.A֦,q Q[g14kII~Qh#ovc.@eP=1rr#vַfEqu[TCK)هn1m FJǁzgt_vOB{Ur暴t)3>B#W \3ѥ:}dz`MР/dvOT*v⿏D2IBB?(T I:"0xi@TZb?9U Z jjeE];_2ks3QZ" o p,B4EAi2x}G0]Go2x$uTi](, '`H"=U2.hAEyorO FYe U< ZZHIa5 >TycɚL~gL~PY: Gd&e0{P|ƱejB.aPC.2ur!V9fP9u?ԱM1'O'`p ::&|uؔJ fɾɫLuPj!؆5 J@WㅻƲ5ŕ6juyJk[Ƴ,/0I?pX<,QALF̤i>Hy}HV^mf ZhyBnSuI>,v)+Nj@2CHO9΋:fXL݆߮/ek%M¾v#ȝ l{l9ok4&Qdئfd*L9ت+hX2-D)o5տuRLaԡ'IoFJ(BI");a ֙N(=;!@H%SA8 4 +'Ǹe}boU:nۍ[ PF: eP"fɔp`~mVmH-PER |[Wpk u:ъ+3ΫmXf4mUUcgarFCJYG jqRD5K 5-1,wD5ȝ֣&c(l:QTG^bJnCcP)]:6rwZV7pNv)*mu=q"y Y?{LNc2TI8qQ|&iD-Ac$>/CDݕ&D[ L6)CCGWw|JRKN ӄɀ3Xa؛=ϼ[,f.xl.)Y :"4Yz'!( C: {|KQkh&䂙8K.T&~%;Q,4!(B}J ?Y:ZaL8g$<9)> $ ?HKנn{Ptf mrSԶTҲ* I>ٵk9d\$`2q0N+Dw8O=M`E[So2-CdD,̒봲?pG7he8N );zsQ`vs$"[I endstream endobj 259 0 obj << /Length 2246 /Filter /FlateDecode >> stream xڵZs6~_GcM7י&\SlqBQ*IE Vˆ~@`]|Ϋ۫o(b4}dN22…JnWoh F"}↥MmPeD*vme! .<:amvۦÙnXAh[&7a0Ƀ (PX M6!Z^,0Yo G tZvv,7EgqC]5YhB8`eS newۖwUMoSF0F S_2 ]wD$X hb2; E-Fԝm10M<9{GlADkRiI,DRBD:IEUvEO,lXn`څ. O'w!(Q r+7=Ыy[u,>9rc ֥VȘI,[8XFd_,N۫dhMI 2L&d?%'^tQ4߮w?YܫYrIhS},-kdAQX'G*I{/t8чfo 9sVY~%&=DI1cYUn+!,;PwZn[ٍwvFum,~E%hթ)Biy\@ 聢␱CP{L#0RdNNG^tt+>pXEpz۬l;ͧlٜ2$鯽m>0(z>)=% I( \KSI8Щ@9ݓ)"2>˞u[>=r3˞Tl)4XD|*TSPң>L [bOD} o0*Zֻ}{q$2 0qP*Gvx(~ J%|iA8 *lT~vקsTá-sFIӗo| ֽv@ h5 a>I߁ts;HrT^~U5$44,ftP|3$vO[oÒ*<|v׻wj\y}~0&FD]97 c նhj +_7۽{?@4}ckc_d_2$ξ^vnq>pZ(҃BTm%5`/d0]|+~Ԛ^#1XT- аWwY<kγ:cPDs:h‰˱ 9J* ZK Չza]{ _w~vwZ+,>+q2̟)k|r9=[n~PCv]^>G–z. [.,r~"6|Hqef#c~Λjl_&g<b7ЈT=fHᎢE;Ia|6 Њqܸ{AHO66oԜg뚊 liY90]STL#vl~ݓ@;YN[CWaDTc#*$9(!^ S-8+s9Эi=9+beeGB rh8giHdDӗlH&"fVp䋁{1?5ho1Nc|k_zeˆﶄ/?-(HeHxEv9 -fؖs`>_WHf(ɧ]~OadT] 4 Fd@srf9$o>צw5])L>2g/w u|+Sx ,}>S\|Ҋ7وޟ _>҈\ :9ⳟXI8!{/.tv+[d endstream endobj 264 0 obj << /Length 1915 /Filter /FlateDecode >> stream xڕXYs6~#hC:mZmcu3MGŚ#.H .zuq6' yh<' c+ "{ʬ.oK?m_]^W4y*f)Ad7/译[*4jM9e% R/ep]r%cNh~]vygo#}SQwFa뢨~r.| 3f^mM}X0>;i]me8Ke๠K-ڂ#L5VGZvp^ִZ^oi(\;QYg- }oˁb PEv'P \s|m5'3cG3qgRtM@㶫tR:ʅ-K#?k^d2n֕ a"EwɒNF,!yܖ2lιr>mZC$@CB!'Bd!d u`(+)خ$D'2r-w8:a'у,8 8}WyJS]7ԎJFJnue]ΰQlfJJj9>+v;R蚄^'A.ą11֫87X`Z-aIůQoRY!STY뒓b2;yJӳ/ayQ]'yz?I퀼EAqb8QVn΁ ?5@v}o6i+θAdzRmLycc(h8 Z^j[;IgFi}ݮ;-jԕ]^ͦs*eY7G^A쨶$Ш :jff.p}N Wm~7뀾 I}._h +cyv72q&_3z.=,9:>aK|UEC.w|3 z n:׶FOhQWX*Cu'i;`^Ô0N ^i<-E}V挞j }Xe(= 'l6Vu7Ç'W&u9Rk2TƋK{ډqz.Gn_4]^aQb:[ݗ?f ΋A_rg=CEwU'=D8q8GV;<\BϏI/W6lRQZ^LMnO0sOَ ;1 +؛ 5V7nm}(n| :'_d˘oljB^΀wBȮNsҾ &޳w7]>;nŜ8؀? endstream endobj 271 0 obj << /Length 2575 /Filter /FlateDecode >> stream xڭYr۸SNʼn8Y9Mmٌ D$%Z#gL?_*P%@$:AĤ W᫷?GbqOCOW8a:Ź˫_'pg%X)GSU,{kʁX,yyK*'xɿ^|b_8|x+'$x$!Ă_o5nO6I4k˛>@0%fo\hZìrueg4\Hu\>/YYw"ahq' x$II KI!+O7wvqu3/| xXŃ'>#TT@1Hl]Q0% /SY/D +F<4̾ۂ$u~X!9wB+*_l0KsY48JӊiH>59ГT.`E⢭)rF*f>L{ho<yjNk[tm,tè19c=%XFA4ȣAI, Q{#B9'Ar؟"lUȸڸ`a u&EIeЮ59WRغA6$abĚI-P PIHQ>#}P")+BuibO-:bǏdOĭZm8=qx9(^;v?tf .-k\9]̇&u(0x8]俧KSra{,b1 LNP($jcd)6tS K$o qPOhM~Fc1Lz9թH5 ++lE)ec8=?(y!LoXJ仪/Lk-ÿ5UOBMa/ k= 4 t(KBNa$~_3Dץ1Bi7\# nI߽fS&=IOJ{ $SAiyc}X$)*)rmܕ*=[nڻ^g  Rʢ7mnXUQ>yj!' _$QG|V?Kh*]-MOPJw9X$x5ݦ{\RKU㦴B#vR )EpsS5Os `fP+z%T$NV c I8Jȫјyo$%4+wQÃ][v,Ngw@@Wb|$뺫0I+jüJS,G ,Gm\7kɁJ4RWp q/f٣ *rSxŲr,R{c/}sJaW%Fj7XJo\!Z*%;JqPW.Z,3_sݦYN?{<=\eA[2 TJk/s2R5:fB;*Nb؝G%*+|=\b3[㏏+]O{z]y?QѼȫK-) V%O|FVN@=IV.d41~*m"D(M$r Qxsx<bQd17eS@p4P#K-iDӟPH12z6:@iD֥| Y^m"&!#ysmAv& *X*[yAjNɲ('Vz#q5&?wi6S7"x&="@%(Dg3IEUbYLW>sC$|씱Q%}uaG%'aAinf% Ԣ'iIzߠ % G ;Iv)L=s4i! `|7Q:s U,Lirwwߛnmwj]SwFWanZ'P *9,3cV҅1# WiU&vcABoip3[F];=D=΂*{jWɪi7J2 dҵY\;jy29jfz vzվuՌhCovZ|Xa_tKn?'``u!}GaȔhǬyϘPhphCO|[  H [qxۑ[L\$TXmc-P;!׌~L_qy͎ endstream endobj 275 0 obj << /Length 2545 /Filter /FlateDecode >> stream xڭZ_oϧУsX3EoE!PlVd;CRdvt֋MQ g8ߐoJ9$Jj¸LovooEsw_|,[aU*o!n0lu~Ke)V,6ck Kioiʎy[84)4mUkl^sy0v)?Uē%6YŸA_OU $AJn=k),_lmUm qBT0Z;l#TY}LžidʲrϫEa`VaP|ZK H\fL} IP)$j{i&)$n6r2?o>]ա8QF9Rul"\t;Wc|ug1c$Q1C?6UDXkEd֖H; zpwQef>wp +0| B17v&c[]E"\l+4%S_J =kfu͍'v2։ӌx2\$ƛ$: #RCV|ߘnQYm^/S0z< |:}[gN#lM/׾|jtvy@zsKXh`S'A)l n ^Ή #\-)8q*ŭNCy{\;-&RIGJRE*bQ 9 _R=[ XApg h0g"p͘uD(a>G$M`$$* $@%Z$,J84˜4R)Aa/%9.1n}:'?|Yu@VͨWơ%FEۢjŽ<($Z#3{v,, Y\8q[jg$h909b!ߓ`Ybau~2Xf r`!{;=lm,x;ȳ_žq 2\/Eo{Uqe&5`ԆS>uדTGCu=bzt]]'[<ۮUiBLQ)@fJSP08Wgb+0q35c^+6HfʯWP'a3 LnDbmҳ8bq24!UN;u1y`)!'3xtz΂ ,4:W zPDI"$fA\q"8FQ}ܷ3(^Tq")͡`oK* l(L1 z^VD":qY (9ċ⡕` 6z>sE× UYl4urqƦQTP,*H@-=9CCCځ!y=6z^,+A)tVtz*~ oW?@4qD~˜"|YE$eWΰ?yݒ4espgs?ū x8 `{~)sTAAm d/*B̛?n_hEb}2yRӳ`4_+Ews'y*|N|6y"1YlEu]A k_LϒQe4!0AY]ː nlw`}];> stream xڭY]s6}F> `:8ƍmceڙQ$],J m#$p9,1mĄ :6Q4BEE@N1EPfOgg,EJ¸v#%~F SD&zp"hE7g9 jb "gCYBIrΉ5]/kI.icӽG4bI,]Fhĉ5s/ "RnX6&RDXM!M!ƹ͠jn:l0#%quɞ/gwܦYW_'}l-IsE֌Pn!($ϪbVʤ Z`1IҀ9Cّ[⧚X$ M^<+zdžЖ$T}-l b>d]TӌGHbb,r8ZsWi_s1W>7e/!7{/YK"_q3Ff S&]Z&QшI XU/;#`WEDgyb{+)+p"r=G[ ofc'`{ D`Ү{))<IF1t8W)􅬫`$XW}pztk `uwu yt, 5ǫo97tu?:Ԗlz `^O1'mYyHePaEVrO; i[.׷[v `&g`K"VcEw2^l@8`af_2*Yţ#ڄMYgD_楰+>JCCM_d{iY͐_ⶊ^c 6VC;X>HD/햛zy d ygMe(R4~BQˤ6Uil+*N`(SDpW%:Iݏ$u,maa 3&AS$}.v f ؎k4ddY>W/%g ~aj* sERtSщU}-lK}MH@f<3LN`j?7=?54I@0 ylǻ]_k{ؤH?CyYP)M>˘@_Pu@$p'V#/^LO0ڛ̠k{ᅦJ6,]$bg|.d狿xK?޼l =PǛ$p6P~+79l{On~tqq^y/-StO.7W7W[E0_M}Ϳ W>)J5VI?4Վucuw7As>9Ņxpw%wxn p0{ T`™6( Al$^jzb~0VGl "W p GoWV6K~[T88s6Qtξ/U2I15A,x!צo%l#Is) lghJ0:6pMǠ>w{/KlӱXR@J_ 4)f/,rs vqӸ~/Ts D L[B+v(c"PoZ8h5pChPlNUODSzj:+6 7GGܒ e@̡4(;[2K4 J-(:8{K1V'I@3 ԃw,ʺ86,;I1p:|aW$&cCh_$Y(w$;nУ#mK6&JBMhB(sbYص½I Q(\`W?(.0 %VwVV`K`v߇tIP@ "GSo-vA endstream endobj 285 0 obj << /Length 3118 /Filter /FlateDecode >> stream xڭZKϯm5UL©U!*irq$jXek4H%3@n4uc{y{ͤZ5{YϤ¥,N(mg/هy!4B;;?u]x_~~7g3/|R"2:LeS6Ŋ3gg}\sM\WmSM}Á$p1(e\˶ͷk1PN^{Zߞh>əJRI?K-wO~Nf+3}6VfZHG2xFTu^2c]Mpթp:}WE̘k_/K$,)*/*B%|&${:Bt,{Srs)07 S#|g<3/¼Z!_N%Рz6 ˜ jL&(m?EfQWn6!)W˺ۢᇜ~,g O30 ͳ Ua  M({X$Gޤ3&t70;- |n&&jB)5o+ສw<Ϣiv$,UM@Ťd+,\\bfK@&GeFRnh.V1k%@ގgmh <$΍9[櫴F "WHjX!)3?*Blް֜Lw2gHa6HA#G:"phYv7:/}Wo@qD :hM %j:f1`lP w _,:<u[O񏭦nƠ!O }9 Nű1։MQȵ\|)@|be)jn$_8x]h$G:8/h:;XEvS:-S $tQ cRǀ&MIy E~< )+F{_x\;s:w6ȂԯѡJmJܥ.a Y:u͸dZ j)g|FDG1qW{A gcN:֛ek0a/SS)I9JS$jJ&7JX"-+ʼGQvD:| ql},V78WɁ 5)M s*wEr %,l:ozU!j3XGlY#xʻK,6p8k#.FR.7b'ZJ)Nv`w =iKG/i:O-T}\F +9g~ugb!X8+>DT:ge| OM7^wMZhoQGHY06w2 K؅vVQ|߆X/M E*M@h>$ 42CGsJL8\ @S`*9QyP1ษ@zН8-[-x0P>CYZ2xn%tNȎt5&cI!kB@:MN ;] K7YKm_K~d{ WETjs<qsY`i%Qu3;i2o┡Dz5`Y“e\UuvQ\ZER.m̙/M] ϶x\8PqΨ|.!d) /RCO֤$ 5TW @+D޹@J*}7;5Y>Rj(t_>P̲[~d.tB3.Ŵ/M]5נ ,iXe]n*d;ȥ&Ӈ0cLP6ODA(/na8Y 8Dz.Mu{|T7|M, ,˘R endstream endobj 290 0 obj << /Length 2411 /Filter /FlateDecode >> stream xYɒWbXS@i[sm4:I,w-Zj}sC-jMGh Tb̗/~)6':ܟ6* 46I:7'7?; ڨ$Ӕ'G0P}l0&<rCOw/`ڡ8=r*VYn|OdWcwu{zmkQ^5}mw@hZ`X_LϭC[;_͡_Mr4ѩ(ĻȻDNlʼngrԈ;WӚsױ2Z5|m{X{($+x(SFx-j2"-ݲeʻE%B8R,GBC7|汣nm ]~.?B ٙ޿ב)PPǵ 86qI AEB"PE>/#.L0 gFk"&H(8Ro!*nfA)l$ttUT252Ha~,; L?"}Y53Svsdg%Z7׋׋#Lxz/4mbG_Q"uDR §5G,])htgm /@sv>CAŞ `#ˈNyB &"Keo_q*U-<1' %~'9=Y&-9(dM$ 791jt/̇},B=/3G~+R:N3va,%X YwJ+)7Tt:4 鲕c9*sGgu9!o?)!M[_uMѹȂ)㧡,5;3]LNfz#_]r͒%Jxʾ(&Bz2IR MOm9i<؏5w*s[Yw ٰ`"C)`Ƣ- ]H`X+o ;Z~r@[ayxpsÐFqR`Iy_9& ]ara);)R@!ĚF5MG\[Kt9e((wOb8rT2g'(>*3Q}<~+,Y;d䩟,}cn?rݥJgq Jlb.z;G_(yHa(y)Ĉd<~8 B:xE;^C>c(%G5HQ@F S"q5^C9Q~Wh[U(=w,[J_yh@\p7<.I󇄟TL(8rEN:8(X L}@JD{9%:4 $ endstream endobj 294 0 obj << /Length 2719 /Filter /FlateDecode >> stream xڭYK6ϯ[4(/ 6@r[d=BVsnu[R-58cI,U_l QXe7wȭ,Jg]R}pͿn~r#.i݇tmR yǍR;h6=M."Yvn/n2WK Mnu'u.{r}a |IۭrI3ve~0_.9Oe6S廸-,/oU|flVdzRY8M[us%Ąow[C;ac_l+o_WG<2}뽸K'L"& mMFAedki _["F@.$?-i>6(/ˢš\@q *X0iEXZHc-?6ίp_ќBtth6hv=LԁI^_cS']ݚ'd}s+d&櫌}y&)T61~vDL" qcș|k|HC 02Y $ apx=Cv4v#](ֈdQ(pDL:n?LP|ڝ6n\"eϞix]B=35"ũ` :"(ĥ@d&bLA%gnj,r~2!~vCܴ]y҅l@)>.qdЎ3s"[^4IցrzlmWXH˾d19v0({ҙLsT;8[Uw}[PFmJAPZhl2K10e^rg8N h3 X d+~BJQ Gؚk x('_?Qp-dÂca֋Q,"Eu +r I r40UQs""-^Fd$a+(D&ta}9KAƚE Տ9c)t6zdn%;O¢Ǭ|$>+O c%a|rWPֆ$~8RmtC #SV1;s[u3DĿ] [iDa Ø^''V*BU n D8/rHuEV`EH+D B΋gaﱭA29X]B * **=]X$fD&:Q f#~ pWNw΅) QIE:9^#PrdM[beR-FkQL( ah>nQ$ Ue"ՋD \{hOTQArN&S :pD`D*wi)vRsأD+H)_HM\W/T}YtLAY5di%R!> U(q 0"Ӄ5R)1>D+@_Nd yGZ4)x6hWƐ߽“"͊[W=|6Q߃pDŷ%1:ɴe([lnb& n)%"'j+A1L[ -wmN;F vPUmʥ(҃_<2 /zκU4Foq;l"wbĝ CǺuK C Cx衢%1ނCjGd_t0g6JZ9#&Ά*2ѥZE! endstream endobj 199 0 obj << /Type /ObjStm /N 100 /First 861 /Length 1669 /Filter /FlateDecode >> stream xYo7 ~Bۋ,(A@V4}Ez[.}:;wg`$%Q>R.VqQ.ƌ';h'G)Xeǂq5 %x˜.{-N cp%ؓ\IBuU}q1&bu .R1P,%CE]E#?^&&LX3FQ1D|B5[0cxê Qu$dB>~ d(x`xk)Oتj8*6$kBV PKq Is.&mB0 LYɎb:q V3QsPC  $,*!GIk:/lIЁp"'L8ItpFR JbFU42DT CzΈ=Y^W͞?9uO+s]45_qcbu $.n淭+2{}hɝ9t.o0Ln=wnUo;8W@C>2LX$yNgf.>a¡;ȌJ p]P ׀]dPJ44 RobB!ƮT;0{u>w>Sg'SsnvIi K; P8bt;Krͪ|x-oo%1:”B0>Vp,rvz;WO R[cO˗K(~^꯻>/?FN2C}Q gW1GO%Iu{~\xǰ;8JP PT| SD`S$1஻a-B{nQca^ '^X gFaEhA'.je2jh^o>" Mpn~}$Շ5j״:ʴgΪ̡+G?"Q Zg뜁vO')#P/cPs?pJ]J4 EbbdDs  Ir]wߒ5mגU&C*٩-b|Jav9HFLs}&ڭr ̺hG-)*v僷 `)kU#DVt,>(C]JJ*BJjȥdbu+)QJJ8 2F =WtSM_>}zi9fe il(޾=1(:O=7#sE(FT\J %R*֦ǑG.aD>'p%Pmh+qHߑAYЏcHݍ'er<)#Is3'"2 X`s_)LE{7 ."7k-ku.=(N-}6ړMF0ׁ sнj ()5 endstream endobj 299 0 obj << /Length 2025 /Filter /FlateDecode >> stream xڽYo6_GY~З+о j36[J%9iCbicoQyw;Gś&jIC 3 …ʮV٧y^.$eyBcYv;2<]ǮΕ]xz&K{ӴJC8ɕcUr63ޞ/W߼WrJ OFYu/~v2 ,c%M"rs VcF&{C7 Lh.xޛeZmMlZ6@Xm[/q*/Kww|T*{p{^] /M(.JLl8lo 4+[hESgm\} ͂kO^C.^z}ZWTDul: Z XSįNmxAZ`g苍-7C.m;|:4ivss3hs;Dr>˄]E9#Je@A(QeP (l!|pjAēkvoB>Q*^u;!;+&`lm^[JmD-,T+nTzQݼZnvK'F [vQhdLOSxeVMy7l>cB~VbSKLقhuRq.,DQRbD*{J3d/TYFWV7ѥcxaEs[?a#%Ej(X~}j]]#J*\u}%߆u؞A`Ec< ALl0q8( 2 X=)(-R+/IA;Fةy0A ƆW.Kljo ".(㧔B;zu&X2 P.n5őQҪ+sH-N6&y[/Mxnl UO!G2[065\WUAqIPpr@-#4'-lP|x2 _Gld2?q6x@tN' %X~S] 8ğonc?^1 B|}p@mF ۝Ew߀1Pbi|CU}/G 8x-8?GGQtoNdaΡ3E,{z9wcDY4kJS==~>UjBn1)b!y9?Ƥ;bʙxO(¬G3/Lv*eF#\=c mڕkП6qn )~!e@, EUHBE*LXHp"/M( endstream endobj 304 0 obj << /Length 1970 /Filter /FlateDecode >> stream xڵYMoWRDb.?E1E/ CEXt,D&!)7\D333W?tv1!NY4Bٷ{7lK+b 3sΓneà(] &g dI8KnfɿPq|̟7*Mjc]P.m41rrm7,l^_6EY^].qL\0l1JJyu3/W?Lq)H RfX_}Ng,>DlE3A`Ts﫟"˔hͼ*MpGs5*da}9'&3wvβe3.C$%ToŲ~,@Rta"nYeӆ K9n I SX7ftkGw)W<(]0 jQmʦ_K%(Op:l,dDˏZ'mAESmmcޱp::\.A8mm e,@dl{֍z] 7f$ܨmqUݏ P BK"p /MU=~yENtϋXA1͆jp i¶ BwId"̕0u`v"g)lR_Mu/z[ZgC .S71.$pȱZIW,G X36xWA%js4јUH1MЃ&TU"S*b;kHk€eDh{aG ):7u21Q2 AB##WB+@ʺkIÅ4}5,JwI^5 lٿU7pX /p t%څºE[^ĝëWJ8ڱۉn!?gI&S'`g/#z/6sAP& 0k7Sl囮Y]+/9TMimX+qon 2 Ǭ ҕ0 IsQPj숃F.MՅ_Ux{(`7eK"i$cK"OQf/u2yO{ɝ%oA{ب߷yJQǏ  p|̵-Vu օY0iʏjD`gq.yזoa!LB kM;_N^A>OB4hڧ%!r@4 $E=5 ؃"PpMȻDŽbGxU-D>n!?̎3HGleP^Xs/Uk`PG I6%Ѓ&%x9UsL׳O)SGin:\fnSsorPl2~C3Cvydt#FpHIJI 52b G.F,L5jJl# n>&3&h@Ȍd"Ic/(nr}ohBUǒG)+~u^8Sl̈?!"r&g+ pEb.8b(㝯9xiV~_TcS矯a(> stream xڵY[oF~ЛBb97^؇i)lq4hids-I5RD;apn4ڜ(0N-af3d D@R5 qTЋCKH!B#$X*t.۵UlD4 $Tp^~S⨎hh$Sp~/X6oT.2 Q0*]ڵ~osXA \UMݥĿcY@yV%Q*hZ{{{%(Lc>:!S\z&"@DO14LzL{E=q,GIgÌfuD$ x8)1CNm. <>xx2s6@e(FJKI*Be(iV%D: B%YgEkkﱍX.G5"ԧ0O'гPKqB?BWÈѨxK 04 W?}Wuqr^W7i"w Ϻjx7ض*㥢lZ쒧LC2/<mA] 'Au렊职YisD^cߗp8<샼u ! v/~7rkGfq+HvWo޽~_kaGҖ%",s ZmYy+-imd847.p<_^r̭՝;5sj<9cUګVdށ{iX E"dt3g遈eUzkRMa|!uS񨶟#Ptga/hv35q_u/T{?\:<Ewo"h/4E(Lc6;{L#w]X5c+3o\?fwS &M[QKJ 7 PH]E_l»!Ej-LSѡ#;E,:oʂt$9v%9oyt \ۦ[<(K[\]ӕ^/;BʷyF0q KHs$X(l^xQ,miWL>t)@+#NI&ljk&{z5cV:ɳ~W,y"aYH0}QlqmJ߹gɕl]rYjw,T>ZD{+rZZmmmǬ>^P8r.ZT`фˌ&M7MI8>qRr#菬ʡ0>kZ.L#"A϶rzP聆z Wp~Mgzɯ/XohoqGL#~DhޯIJ$HQdVUu"%b)6M3MGgw7B J6]y5^Ebפn6{:H%!䋷/_o2*, i'J_e]DŽqZ ARe$N[G0Hh_ˤ.$o endstream endobj 316 0 obj << /Length 2279 /Filter /FlateDecode >> stream xڭr6_Z( w̴tko_L!ԒT\(Np?^6RjeTBQVp<wYso\Ho(ڢ6r\hXxmbuÁzv3$6n‘̸m!eQ,[X-ĪGuE43FR.^k72~(K4"QV\@ 0P\U\r{ ӈEL+bLi\Ň4!GDD:Vets񯋟P˙NHʴC%z*Emmtt vaWmׇdTj9iB8SU~uW7'aW>?@6BٗD!k97P]p:^nU;0W6YƯ˄" n`0F  @eP}HE1"h@ɞZ흓:ѯP6K;\]TЂ_eMDMOECbOmۢ(rp3gכmgs)}Rin[zPhyY:v/ׄ)F@y^`q*_A@Pid}(7Y)qk߬^!@I E$zh ߷_ EK?FscNKC G;H\i3 i=Έxnn~smV6 6;OA$ OSirteQ-K?b$S*= ^MQ 2}# b͓ $(N:22Hڛm+ gYUOvC X~} : Ȕh}JxFBxb 1eDH}0؛MC:|G A0ߖ>x; XK Iv6;P\#\JؐDyv\'-pLAH:f <5~èXIvhm?qwi޸ނc-&Acbs.PzzH &+>At)o/E)7c2aQXo*,~rHM!#7ۻ dJáK߯2rz*P:G;+Nt~40O`q endstream endobj 333 0 obj << /Length1 2309 /Length2 18538 /Length3 0 /Length 19888 /Filter /FlateDecode >> stream xڌt[ Ǎm$M۶m{ŶNc4ƶm' ۘ'{~㜱z12beZA;#3-#@XVHLKFbl L dag aGLP b `d0q1s108ch1t0lNdvfy00rr:Zd ́6 v@g AclEOFghDghGIpp6(@_r6S%[8Klgf|-N..&@GGv @h/c]#@;zXؚL-y1:gwg_NvֆF &0`o~NƎNtNq+GEmMllNOhQw7?/&..@I|`̀Vv6t76+=o%_>^v@ S+,##`4C 4迣;@c }1a&vn1)W)$dee22X> >?"Jښ8Ez!ɠP7<_%u\SGohcaoyvq Y -I:~lh$f4Qp66 -l vN]8ZF9cKW+)EmL=::z~?ll?\|vu@/_ @/  /bgKAz?#A _W>b*A1 AȠ_`}p0/bY4?$66? #'G _=_Oƿ۹8#܇? @X|,?aA6B'뇫HCAOgQ#O5O}X-u|nSj.N!q?*A1_ʰ~;}\>r9EElGs?8 .t(?dL=?JG$ORϲ8~&Wtî,sYu<LΑRz8pyBLYuN0yy}OV`gkçжDgxvi0*ޯjVୠ]Rd. C٤^fiUtIi PܑoPsމa}΢ b<7*Tq>haߢϐ{ Ha-yEm.e',H*`;ft}q3pV3! &+7M")·{|%%PN5C}vE}4iCxNjۙE_n8#.l I*'9;#4P=GWtOk n]l΁0lpxyZg>X~87Ɯ~D#Ʉ%x^1,Űnㄯ >dqF?38y^ƿxzrTӝKӟ ;jVu)IҶIӣ6l[?[dh}2y,KN\&ci`>mZ5C|TD:]=L&hN~|ԒSw^΍=C'_mZFZraMUz; t̕C Zb0#a'/b"͜j텏V#-tޞ]Z-2j奉9pHgK%W;Hʳ󳰜RFܸf¬Y  ' 84~Y~j ~ïV;n CudP3SӹYd"+av:@ńs^p kv̸a:SO:ɟN` P(_d,QHLjm6Imw>{GϨ|JJԝ*(6xbOvCk_Vx`:5[opmd-V  WQljcOgMBvVNkvxByq gFYqsJ:]Ƞ@cݍ^6k[#ݎk%:pTE#H[d(D^vdw˰unv<O']1}["HI7zK@IJEFt6;{ ]Ց=Dӿ+KhxÀ~F3&F@'^r]rBDItXt;e AtLD Jy Wz%<"M%YkBǑϛ5Y+Ug5R)OMdHLT=MoSK]cw?sFHzI˷\V}vWHaGL$E`h]t=0 V=߫|LBhRpGCqɟxJ0E,m%I<5BE;n-:zWΐAk~& h.Z<_ [^N5z* K4DAd;t逭Nk[̓Jդ\p\rƷ.nK%#lnlL}b5Xwp- ot;9>/)Żhď/U*mt7KT#y+x>'fܓ]uH?%+z~>?^r3=ц>cj#'V 9[A3nxzk]5 >)+YޟYxy&#hnUkTAqyI5H.'Zβto$:2Zs9JavpIQ` -{ӈan[EƫA_2.R/Tf-4-%J: )fVLM1u^Cz+`cyFubZaN%Yn*A]VJߩc F *rlwG62 ޳RSb{P16TIE߰9pCQn*{4,=o"YX.bB%EiеPM{zpQظR+ ? 5eqOh ,ɖhD].ENr,`׻τZo4F~ʪC. * ̒/^czeP-$ڹI#C6oq \Cdk, @SvOkp*7[dt]6"e(P"o[vdᅥY֥4a :1c<ܭ$J6U9Uw1:=s3 vIh {+BW:f'QSR6Qř]&|Nviʑ r-}ZUXvЯ+my2 f\FR0aO2mICGzerJjRHG@_qudg;S5h.΋~)kaX,-{<$B:y%/ux#ny^!<]o (NkKH3ϓ |b"\CG,rPp0ȲI |EB"fC(3{\,HON=h}a |w :h^-IW+Y3G%,^DCT(/rICN6\w?cѱ&R 9Z0H:aJ$bi5S.(V],j rA1NP&8Kzb@Q4_iV(|R |~ǫ[cgjT../'ܹ7G`}2s%ʩ cɄlNh٧1ә._CDڧQbNKe/ZzUC扅'v _/s\5E|19$'e {HrvnSU"!ڌsBs}PQrV p^OnMW xS|+d˻-}IUfoVHQh-vD5lPWkOb= }i.9ˎ42mۈui̍ey)ۄBWKq89\`/R2 wes!7.(lSS^7t z/© |:o{Z{1<_<=)_IkWyhS{6vi+L `|(<pxPj/oׂ`rzKe\QƀtU\9a֩scڜ.x?z`|&/Dx|1'xgM`Cv^i,2A KI 뛿gʴȹzz(df*fJ9*Y3ϬOfNL4Y9H ГSa,O댤vm)XDY ˾͌h__ X^1F(- V 1zvzh[oɾwZ'MY T(x[a䋽8Pi-KY6M؋Ienʷl=` J .7y7N'FOFn#g}N4pjHRbd#ڔE鎩2fFuڍI_Zk܅ag vx l|q"2:E=wH NQgޮt:w4!G3TyZ炴Ȭq)w 8lciVf ̰!R^pd1X%c՝rPlJQi=5n_oʑte9n 8Oٟg:߬mft$CELt 2SʱKA0,J7xRrco=N`ؑ9(aby`iU[.x$2:.5%Y^ͣC֠-noG\m?e*ஶLΚbn%+Ry^d#[6~_)3Q)5Im.Bn38DG0hwE\`xIE%z)iFlܧ6!5q m},}buA ;j3*?QdXoW@&Oe~Dƀ->V ~l&fָt "xuߓ< Lû3ԡ.L,4Q,(u@fL}2O/Ye8UXk['J Ukh IGt߭,HrP+vCQoA ;^ʹ}ޫ sqf0s5޺H\ރag݃`oS:#UYT: K~x>7OI+2D_ʗj|E.(ISo6'+K7ѕN. &+µ;M<6Xg2ySnk7Wސnݏl'_z* Onn1q«y%`@P[_iV Yz?=@^w0%_p۶E$ZFjM>IäB;~`@U)0F(IURniŒ}]:g'et@YUl22ݔU@0ᵑ).]"|,l">nc.CpD)ƒg1wt~xږZfY\ ̂nm7hW:Ur%J5 ~~C>57f|t:-0Hi}A)oJ&HkiD& Tʤc!Kz* (Խ/%!/f E%OhF3LTNobK[= LGc`%Bw9 -^tn PἝ9ޟ\{Wqq5#A z"K]WH Vf,ш\-e!y!'-hH'B3X /k %4nq`+m6g(7& z>øѢțS@c3 ߇6(q{_MavۑZ:ݬ\no =O0:VP0iwjͭÊoq\8NS^8PDh;6eovѦzZT๏.1tEDj]6>/(hz*QI :$X$NY!P\?+ K"(i_ς̝. Lc_PِS*X0l*wmvozYYU΄'4zzD\vc0HK3])b^*2PATRA#T28)H=Sjs~ |% JjyY"'B\n'g"ޡՠiytAɒ~l%UL=jhD_C;(<sD># B Ǒi]qC w`rEtN4s\.v )#KsQ&X{+3 $n*Ox]e)~ Z+/,kO@ X *s,G!^[.$ߡgL]6fz|NM=uAjhuoCD%b6kȈW pb-—cH0gFA"Ԫ!ZvP9oZ+\Xנbxo=LW$ï)]899H?qR$\]-l|'G2=\~l5Sf`8S- @WgM~jT @U *C$0yfK)9=\sI.SDH60ݣ2(Ni'\M 5KՇJ*6-*r k׵ !HNKVk<<.eq[\7JI'?8)O fO23>N6vB36F|nhjBPAR`$P%i]{wf$/mԑ\`WVOu,R]0ȰO1[ ˆ%흨"}|hTCч#[_*a0xP:xMH%.%ҖcqJM,$ov!$\FJR=BZ $!63^/`}UIwtLw^`zq9wոol8HAP>d'@ K9`yJQo_@BlY' 8BCp)@ֿ>pi"PVXK s 2@nf*e HU SeTtZ/;_t?+/91D1k2cb}͍ (K7ܜ4yfG:@-!v΢iE؋nm?Qԧc%!4&wQI+ᦛ=Z);5i@N>0Kc ׵LWUq4('\%JC]4^T22uV;rQpcsq֋;n%#x/UVtǨ17 ׶D{w]t!AY/PԼhx{dsM5dL?Rz 2a/&Bk\.1가5)f;ftB-• . }AWaۗ~tmsNYI&)Òez\IyK[RE'{LVCQgozQ=*.*)O]޼UK[xͲ*; ;[W2}÷1z$8y\)[}ƯL$7BgĢ$!WaF}Qȏ3LI$<*ZИC#۶:x~1(Y v`gLi/im'LNۇ;IL/$. 5 SizmĒR GZ` 5=oə`f%@-Eop:NA=UR$+(xL~Uύ1> ;__a l/׀d*Y[<{7._+=sە.l@y#$7sS:Fs_Z,w|6}S#02çO2;vûr-u{rOR1CWݺM&.Hd;-с _D͕SEDp"AѵeG%vS}I]eZ֪2%AZ9O~""p0EWy)D=X.TLbSMJ\}^$ұ4Abq?)e1=ca f~:Ca'/Qm{ݲUv?M4{n 9v(0uU-i[g dBqZ[P]V~ 9DLJ {?KaD膋OcQ`G)DLE@Bz+9&+-iAe Du^dk9gH'P_~ WjyקմV`y4Φo>qӾe;VAEH7{OO&e[C0u^hYn'.$^|71L|o|ʧN73({Ն^uk0CaNL>:4S R"g:K%57R:;z(Xe1{<Ɩ&Q[^ˏ7WuSx'?Hv#YO/~9IgaׯD6%󌊏` kqY_Ym:1uّb1EdL̾טĈ\ed䥛6؋c`%VI|9"tY]xjw#kęDǭpgM&HJM׉,&VWD$,[Gs-ǫ~iߚNhC14K%t/Z'On|mC(. {xg͈s܁O2ăKl Q{ra'Iis|吓{SBc -0h^w!XwF`4kXVJD%k2]Ӕ}8י<ٵS?R]>lXr? /;H y)(EDl_;\I0g9w(&Hd/ywT Cu,X&x6/;wk;_ ,`)I{ڀȓaJ2[϶ާ(4]ai)nV*X"MlUN4?gᚘR"X)&]ӹ8I?JTͻ"ڹF댈P=A>Xi˓ xRlq%!Y?˜A7{F*#F O}T`x8Ub@ҵ: ]%uyN^$L(4 ,jlE:5"4z-{t$]'OXѰX o~v,p;I%x }GM s+^xyQzm"ރo$fDgnH4:7QP;.KQà?a 7X +B#qVƳ G*GGJrq7Au4ݦ$d8m(CU3؜=hZw&9/GU̝{75V̌DǓW wN@Aob7C3`cfPHhhYq#{{$H^@Ft{S16OAV\*1{{1*/U#w*WxVjg]u=_LPQ>n۸־Mr*mw?{::!C2^USsanhO UGΙ 74[cj,XA`XaC%શpu`uS^}ީkmyn6~jc60DtU#W S%Q|tQ66n(hy eD8%Fxn]Bk\Ss?H5n9}NREm>lLR#;+Zf%|mr@SRGΨ.ֳw쐶 i$ N{do!jM2\ݦo7Auߺtjlٲfmy3*i&B !BO\=y=ֲZN kE lۙ=9$CY\έ\Q)i# px_m) 5?ğ)VrF tW[wӈjeNJnߩ&i^$[)'CZ$U Yd:&4ȁmf[mcphth' \T=9)$ng/uz kśg*2Т-&4i$>SrTj9+8ҳd$s.sxJ$1/iR7/<9UUX:.P k:ڐxh.ZRjUAEZ:<6T /$֐w+AnHu#4P,cqٵZهBեPJ [ս__&1vR?b{4[4T$&ࠎ7Uў9o5qٱ t֕ㆰ;"eSrgN1OS}Fy#~}WnFa.ܹ+/SFrrGz7v i&\f=;!YgOaN WS8K2h\ߟbь1`PK [-N0P99CpF.WOqGe"7a ʷ<Ы88teQ}DW;=|i U(!%ͦݗJI:P+M\BoL1ow@iuY.tD, vKP0h.u `o,@ˤ>wF[Uh8 #.pH^ۦYt3GK4gP"ÕdHR:&>Ea)_Uļ( * YfYI\}L1SI?'!fra Wv3m@<ނEI,nڡ]wާ9Nvߊ Һ!Ioާ"Y~#u61X sdfxփ9'HfjCzXNGicTϑK.ZxvC7OZC5mz;\~ t`^^k ن3qSMnYOmVu/ipem*.n.FԀ&AE-^H`zhpd cW?X[&7wH:}5E\@ɛw@\aN)o?}M"ʔX)XnZW‚`m+"))HokOum:Uu%OzҾ7gɵɡrv^8*JRHD~g>:[oG,E BtVfQD"I>A|oCB~<DN"h=ߎؔ[NfF,,J/FDgWf. 5K VZNfo{_nv-.[Fnv9#S2 I?$vئ@Xtyp,XsOXg_IA "̢ ':Xȟ+<1)2"W C7{6q_b\8uf~'T 6hU[o-;εY4U  gw:# ĂFR&S-:p _`~JV{I$YregmbJڲjcU5ɩ XPgxdHCeӯX25cҮa8RV+t{R[7O|$KT=QRVd}8z=hu~e%mA27^pb c%Ona)f ~ףGQbX'/_؅~pbʤޓV ®d}1ުE3b根 =ȱj"516ˎK H`3M C>T؉]/O6>„iiJՅJ;_Sgnbِìx0_bgz4b^|m%+%R/Lo 1*Ub*=i#JhZD[X({$TI/+g(K_d<꽜TE\DOT׈ػm_W a7~P׽ °Adžjge`зku<.krJwBv2j.HOP7@»PS6MWE?i4, ' c3Ep l\ ;NfB03~r V E!MK۷S ! u] ~h|@HG 6#E NL(q-帾šNۙ4@|IԄMݐ'$!F62Hh%d,gƨyLssBX0S\J3#]Eӊ\Xh;ZT0$̂y<|..⥷&fh?Cű"lwZ= yEvDf84K2 Q(!ɨhN e4ps {|`ƴ`!@LG=vz.D/6/Yס e2 &KJma~ JTmy5K8 @FF( S6-a&^V8Ew "y`ʢhrЦކB86S9 +PŁpW|yZ3Qe<:z *dbV9REp)݌*[5_n ܯlL#!83|ZSaϻrOC ?=ʋVf#Y7 4̧n1)FE6 [|[jxӔaθvogɢ;Ds17JǢ%][A{ ޖh6'0bF6.nϱ +V2t섒FPӃV`|1RYP uUEY?Gޤ'úw(FJ',ƪ?;T8}iio>QqɅ3u"%lo*HXn{RWꃱ9_DcZ| ``uއ=ݢbojӎ ea`R#l8'on :{vA8mÒF_&w! VLf>?h)G'.j/v=NmR:r(]rg rH(q}AfrܓL6X7)R>jb&Oc '.C@mkHHceB+6mjA\ytHYC =Emo8g&FI$7 -^~@$(S"چ"z6ćE:`6 R-p#2\xS0S5q7@]'$s ]H=]JԬ'5]Kg$'76ޔ+h7 #(®t ጂ!D}F, +Gr=請!Mͩ$3mvI s5|1chsU۔|7T\g*L +<,#9bhElX{cx%_$=C \cN'Xu|o;p2 _ oc sH )=|Oˬu(fۺk7r4{~] ,TpҲq-xfj1%aQ9)_1('kn)q[BБɽNfNjm+Z֏FE=|%t"HPM}PujT+?Z0ElMF8`M߄qъ3=Ts3&aWy[,YKgߕœ+7DljyVY#PEV^ 5"M]DvXlp Y{JLwũeg+S1Vj$ o4gBvS֫%!O~heZ6wzJ+Q Do4;9yfla] JyfNAA~X.) .-0FƭœJMM&M}VkAP8bgO'||@k5ell(s=j?ʎ^K$Uq8{:*p*N}!mJf k,=<2-v_'pFO 1k}B9'(>sxkf^[AV|y3NhZepYF^ bP`aB |ƘtGi69;!Q_X(}8\'ey|ih:ֹ+lT!$el&:/3Z:`.SMq3}KDlqշQ?]E9Ւ5i*=4l*wG$iOh}/x\glv,FK'\*m%ں>LҺM}{陷?i xd6 *KqP#+uV2/1ҨԠ!L2M=[ȫ#ʊ eAFVR̪[ _Bp4pbjaFĻؑ0z3 =/F\ke3;$Q US){t4jߓi%{c )'cmB9_ދY{d)!2mS-k-9zm[nY%> stream xڍT 44%Kpkng3'33?¼$N su6% 2] Yɿ|ޖYxy?,ivtpX-@oH&n +߆%$vv9` #md~'@lfay@II5%)$mwxpsX8^n6U_ձQo_!nu_c Jo llfoo-z(+vN/~vuy E ڠ ɺMv{U.fVi[p[=Hlo3gfPq~?MߔRf78oCj@V{-7q '?nc?C?JMl? CoQ([T8@-?jCoUkMoL!~oL·[Jrpu[V·2·:loRmouou r|~Le~;͎?¸ uuuזZ[B#-蟌otqw׆7q7qn·Ax4ۃ['<@fHsf!ֵ!b,fhvX:\;݊%blK߈.xÇ&=<%M!-L z$VOH¢!G+K&+J>{G]Xܮ^ɅEY5 w|䪡Z*9Jph(Q=LuF|V=+xݨYs+bSȀˉ Gx϶d!7"p)< ~t_E8L\zM*QH5G̉;+&rikM1L3Bݳdsts1+cP|;Woq?$'Z{Tf } agvXJ&4Ǘ8n{ @o#GgM5h-+p#H-%Y_ͨazƔcdwP%0_3l'-WVg nkKj>X]LR;3| Y}0%0?_'(l/ (N0 s0}~K'qЂ!#tK{䁩=5B3`ɴ>Gz} |鮌gjsDIJҕ+5;^Ko_V @Zz#Dpv ,\E8ԋIZ,U)=K) U(͍y~wsGu $~&\_acWߵ2jIqsV#[ٶ#įd65*uv S Lpҭ_$9vǸ*>] }zܨm5hXMao$A a؅44]vR>/&  &!0{Jò!HM*?L5?~pvཇ,voºeck"?Tt!@*S%< IEnﬤt<Ô͹tJa۸)>HdUKƕu6a/Wf>/26ώ | i`&qMnVh}\y)j3Og:. |XR0 "u桩N0sjy:Ucmd*Py!(ɡ =?2m]N'!Y&.} %U`શcRv1pp}RӥʕXsO xRNr2 jC/;KVý&y֓sRj? 5$Y%,.ڛ>f6#fIhߑwЖ.3M!P2qQ O|M{GU gԆjr1fHD6%(/5t*<%6Sɷ5OEY-2 ʅVW933G%YDKrrLvH$rO<#6qsvpJ(csлJ71tui(gc  vDu~TFa s q𗑏ivr:EGn'$m$XFݤ ڮ6MKWP>/ˌî LNV_$dE/ a5PO|]SJ;)y;gO.wV<ޗ_}r݌zY%HH8婓 q ̜)ӫ߳$6f^# {5Pj8i=%1/ƶr)6|O^t wO~GАϾ%BH牙uwj2 P+pGWA1U9W7qPmvs6"Я%Ld"p ]T«'[*GXJ.X4ru/S0;|y e lf|K=?'E3FOn TKCדk R+mWa73D{ 98=r нHhE\Vj%X٩ŕ,Xb,cbd"wuKW~~&lZ0݉cI Oyiw37]n8d|cSȐJI0B" 4q?3\;NB H}OTCPsrY2aV;5$FU[{;4:9**ק.;iăb.I7s‰S(gBE4qLch7fN]VȮoޱBZKX用#yr Wh)27 Z0S9 nSx=FXh6SHID _y{qԤs4PiΩCF^o>)Zw×,KCk.El 5 l ȁ3 brGW>6V\A!ltn~좢!LiCLzaf)&g[P=;Tj%Jk6gE'_RUY?)(-2) 5"~@0pbuo{\PwP\Eئ~zANv-ď6o&tխPޫ76I}a\$XDt'-h56x㶚, ߄@{rcM򻔰.{{XlU{ʌiVu7a'Ջy+TYȺ_>4!~ƀ>l I`҄ʊwhL\6h@mcbLVE۵f{JQ,qi鍁 :5ݦ|+ݙ{&KUg 5d8-}k?=E2!a۹RkLs BL)MMZ _r,d m>{XABHofkZ0%J5 )[p~ŦvAX7,V|͝mDgtg-oCuᬍӅlT\E[Ed)s^GiPƇPѦ 'nGrQB1^O~:LF{ijT2;bZ}.Qk樝sl[^צ;AiGeNNjsi- .4uh#x;7f'8j !ѼdN`鉲O#WE'VK["q}#d!s)_/'{ m6[[o.ٝ 7m;9rNY\z--/e1c/Dшk0iv%s9pDo=k+> Rܖ|`>vR5}B$!Sc͢S4WG/#KMNIFxL'AGQ囋(_,֤+pKك3V)&ؤb7Y+#X:10K:^ʘ&Գ~Hy& }7~@[ؠ@_'d$8F_=2_jchtcYOhe#mDB o:EϨ({X^ɑjg5HwUQ4ZVL&-Ci'bZ25Ǎm{<2?mI&smZOvx2x.XyZSީ}Ο,Nx \b)rWXpɛ丂~$B8M z\SVP)O{:I\/jADSҤ(F[3>W0֩ZXE|89䫑M񕽑A~^uXXh*b'ěՈ0 ñ')RC;.w1Qt.9իz]T|mDCF)xSKmSxL73odzxW&UiK?@6+DЯ+  "1Ð'M-Udlo"2<r$x~|",iilR]]ŸއC핂kh0X; ǧ!!F@6kzXZUgI"B,WJF]< ]5iUf݁yE4= Ƶ Nlm8Gp*U%<xBMZu9er?њsP8};sa; Fze$fٕ1:Qrx]9¥\ :=ة]/"= E8j;4W_CO4)/쌁6: TT|A $lhwPw3j`L5j&c>S]f23iJ&U-q+s$κ]N=0`>|Kb>C!(T)ura6j5S;|=U_T>W 0wٵ3ה Fɋ}\Δmh`ڧ.[Ⱥλ#v}Kil'hd*@lxiW>W:4_ @/գ{LO[+苾er opqZ$Ѹ(.02{N .0x7Te\u?w'k?;vڤ@ 3>D$\t?B$#>Xgӓkni#w5>Y>+Pf-wǹ;Ǫ$*'1OWYz;B(;괔1Fw>X-Mֹyn5+y7ow1#O^]ER y _!a xp1>E,w}..<9UYG}/X3'͕QV!dW 0Hp:W8z?k)Hg➩mΆB9q%j&T2IX {Q)$ pro'ϾƘ-u י1g×S]QHg4A|d>5륷\h$Dg!lլi3pARf:!(LGU][—K];!Iq^ԧ TUɡ n_#mmuO1Yi8z La> 5*VLO4Qd+?g[Nè"{s&dMi'Ƅr8M0m(Oa٤ ݴ?0< 5H6ZҮԎ+OP h2"#5ۯ;ӣdteۂaRi!)g`&-X}ҰQĨ/9~j2ڷ 4޿{5fJ86A{&Doh{eϊOʢOJdRp>-jJtip|N1leǦE*9"r,|;RzUx'NLmqt`հ&@l#.&׶ FYy U<٪F4T<f;`n6A;=z)yd\6ax1gEdJ?D,/ő+fw4B:ۭB v2]9L)jd3kG*>1J ouχ,1 :3 4h{Y.ZpC_]W]+dm/w=U"Ervm#NKT0]96`ܣٿEpj2Y|`cO]؂1z+>IuS(y$GrWhZΨmPpjկ2GV'ΨG,]:gݥ ,>V~ i_X-hOTl 9KtAt*2&sݮGM)/()SָgD˻ E6 5szMLQ[ֵm'ݢދqM~K|IAZg5WN_W Ҿ 1yV8ꢑ܂%s,Β9,3X[?P3whq[!9&?DiSJwJlzzPO1=%%IXI\ح/7e k?[HvYf͔cӨ rSLlȦl{j4N Kf^QW< DA4C Vgx\s\IY \MNBOUSفHm'ޘre4#;͕Bd:~oe6NR!/6;f9?HDS'5`R4W0i9[*q)}s ,NuVTuPb- 9ō%LjHA)׎8@'E0(xP~ rIMTÂ爭NljViE樞zb)E,XdѰuȿXʏ24K>.3!m ?Io8tUl@Wd<@SF񺃴 !jGfX~-os*B +B}2)&c=j_Us@vǜmF*yJ<{Fdz4=Sd6FU4{i=\adf5G#7FTժ YS}Ve MRےW 콦&-_+COsѹ&~U0v@.GjbmK;Iˮ U ~rC(2g\-0"`:K0D]\1z]lfI+e*['27Dztotx޳t^ٛ4_?1tyZtuu)kXlW..ǎ"rAq`Vx,#//;R 2N_>.ju;#HxԑQem VQ]CGs+Rsh)N~y1 Yy;,`&[ ʊ.u×*❥^ڑw~w}t=Xb8J[:℠ a!|ܿM@=Z^(p(L%י 1Gs+BJsRH1Z\>%J:?_Na{b,1Dw޽?)l\yj ʙ[#={q4,4CISPsDO tXAҳy#<-q|?O9y;`FG;9EHLDD5x:e. #} b#TOUb{Bo-%j}&[ ̋ջ&.^x@2oHLϽB0eZb"B贏4CiEim)E>XgcӖ}fQ.v@JVC6Eq׷ƈeSTztwd3a2)p×g,0]dXd`3(˟0QRhg`;0vY!"{*+iomA |HN\vJ_IJOG2eĐ( CBH ˜ea ͼ҄@CQaga"V|E>VسC?S/Wp8{HbꥹD7uE]=l u {^?$ z=yqCu텄nCҴ'l N hS6']1։jȜ|\/ɟNo™u7-6mn6N<|4Єa؜s`hDvDK/Uƌ"l'Αګty>g| A*-F.u5,%ArTtp(;nyx^G gI|c+"0hH0Yhcmw3{Hb&F{^hvdthPP%A#̻b]M +,MR|J^sͥHA-[EmT03@~ݎ@u!f3g%*D`J{<`:_ƴ0+t$֒ފpK/* w?܁lZNA@:])(f~7ꉿnzN> !Tޥ2 -q'Bd!OuSh:mc﯏k 5*ާ?rC2% #{!U?"KgǢa꼩#lZ 4]%y+ -x(fs&)!Ze t*Z=uXmLwp1yjv3ύrT[.x+h(wK Pڼj]QHLlP#ʙC#8,V6pm\hMN~J0 ,COa&8{ Di ,p/ dӶ}wݝGiI/V\w!B2O&Y%T!KF2)O(./Ak.S9cϟ\+0τtwOnmځ'kEl0;v630Py(B&dE2LS?gj=XL#y͹_rޭ5)S/X%N`$B8Pޛh5U6/*Evv ^#[TO 0?꺾AABɒ<ڄ3^ڄ9κb;R7t Ʃ "%1֙4I3t7MA1[DzBX0ݖ,lK\W #h46Vɺf25,8H>io<}޽f$hQf?m,B{e N p@&>tbƝ<;[ڜHP4O%fZ\ r"Նb?(w<+_gX߇˗@e[!<&O膺DZk"_ 3LӵV#\eE."!:Ct*_4Ha f+Qb;]2SDA?gCs}N_8#ԱBxQ)q1Z)Tlw,M]!Oéa%x2=SDr=B˓84RWqW|Z?J5Bڳjr!$[h0yҲh3z W +^ . J{8ڬtVrKwB\&?ӛFsܨ9fJEtU\xd%Iʢ_Fiw~/{!'nkl4.z( ܑRGh*nqgIWXbE endstream endobj 337 0 obj << /Length1 1812 /Length2 11119 /Length3 0 /Length 12265 /Filter /FlateDecode >> stream xڍP.Lw E;ŝ  ;iq^xq-štw2|gIh(Uՙ& i=M $`cdac@Ѱڂhp2I P lwsyyll@+3 @lrBC,,y~ЙypہL% dh PZN X"LW+%@ rt~ PځjaiBlu:[+Sӳ.P4VӀ Y/߁p @{w+{ -"u2f Ng hlG@;ÿs2u@Xl;1KٛI@P'IZ9Lݝ؃]=FVf0sj[98$y#Al||fj;;oO0nmez@tPGgPfVP byVn=g~d03?US[筶_-G- `fqx;N_)~w||w5zP?llo[G+vCOGuP?jh%j%x&:3; ןr+'i+7O2=V Uًtϻgj|8=Oy7=!eo 6<#ψf ?8`eC]={(`U-XUA|V ~^+3XM]wfVп _j}?. 7_TZm˳>Ӟ_4sv?=G7_P sn?,3\ eal*d]vS-N}\$Fy /5eQz]кSMvE;3IՋDksa>h%(qQ%B5Wy[+-n4|prGWui">)dH;M(T B( UCX}dݑ.OCXRC\UZrK\2=Mdp?ѧ*YnP%uZ'GzܒaAqߦ9D-R5 $^;i[Vlȇ+/NrVHӛcRJ~,>niv1m}S-ňEm?@m|E<͡Ǘf٦!R꧃ɉbVcHӃ1-CU}4vzݲWFᇻv/5QvW^Unv" 6;y("l3"(5uV_%y9lLrާ p 'K݁l@RQOPkӂg0VdHk<8g\ gc>~66?F mW8Kt1b/Zr#kHfJX~d\pZg cPuQ<u QhH]u[g E2ޣno20Wl=t ٲZf~f\E\pLb=&u=m\E&<٣Z0iEA_g[N~LS4^]Q`o1}'*/ߦiL{~!Ƶ ͑^4hm3G<ʫ W: B.^>nXYܮ6>Vs͘@ %!v5cxBwu[_9@[w&C^M$` e9sX'mbtjf{DWI؁׾Ӕ{,@4t,>0ZNu6"AЬ8Ik/.&_W(\K<)\@eνo @9N ,~S0dHHWfғTKQM84/pApIl1R!CB7pWbN&noEչymX ݽ 882o@c#jQK%<]>#lg6omE\Vu6eu^`xmK3Sg4k|fس.l˶AV&⪘FMX[~g4 };tM M ?hVZ.%Wmm8a#LQZ[Ee0"֔ WحR[hքJ*K/S ߐku49esRh0Z}P Ӑ%zti {("^YzRwWq d︯ |5MYEƹm ߹n$ٴYethD#ޢb/ٻy9֔^!ovj8z >spL)x6 }\HՙEcEPgm_L@?Qha"w}b[j-V`^LzU-!"F(|d&ւf?g ԨIR|vV:J=Zl"Z5 X\Ҙiѫhx4~p{;j)ii̼b MO۫ȞT"eo[1l #w?IfD>|)E/4> sc)JuE4>+}UC(FepHҥ.e-Y-G,rB]hܼcq\E#?+c+h: ,hOX j}GTEXֈo/s~2$9S m\V \[5*3u Z l7#W 9< GEYNXҬCYv!dBid*HB/QknuLߚ @K|zfxZ}]ӣf1 Ʒ;BU!p~]{`FUB39MfE#N(=PY}ē[tVɍȞV-trU$^=,ա7l1at%Hicͤ !ڟWRe`V 9ˡkj߭2mhl($~3QqTރ>"#g>MUf2t!K|ҫB됌U[Ҹm_љ'jKwNILm5;H;k/CMN"$*{iCtp 2f^:/5|yڼW]6Z 3UYx5es-Ws!< LmyWȘWq*5+_݅I/J =<FLVFl# ⧰%Nac6m-2R3xJ-1H4b"P#@ zʣ 1C`$Dg ͕N_I"8{ֿAK*2zMjӄ HŋU&p; ґɊvUGWf c٠!>dd,k %J%ASq䪥ɋ+ 9_aF5ِ&'NCFֹOO.͗֏X0Ѣiw#dM;,N$ۏ0E }f({7Vbx{k{I<ر">pxۗ&w=Ԧ~$ܔrZْ#6ł)S}%|??;$5ܯ8͏_=@62Hlsy-X {#Q+cӒvD[a~Kğԙ M2lRi0bۍEnVZG>%U`A~X*pu7XZvp;~aVxw%yyҕ[2asg-g)%<XVd`HFo= ?X!B/ }߯\2`ޢoa8ӘgHWLV{?ݞ依K{L2!wg)&dz(q ff93{^$f娖J.]e{ȸkZ4fH‰Ou'T$I!:ᦻNdk!2c3ZYBVxg%b %mDV 8Mf:Tqt+HvEK )?Q 0pU}ԟ(VDnwH23i 9X 0`e|8-TѕM;QA(p*Foϻ {4>oD$C:\s&LQs{{.KO$Ы&!sO,ZR!J. 6tm4 8tWX}˧S ΅cC1EՉx~`Am9[G2~َO56T+ byw^O/Ip\4E%FRN 3 ?&o|NiРޕSpbU"n6! ["&g嶼@% c$F|)ɫ Bj(N*R ׂ.L6QѠ|5la^*k_cJ'CSTra jZ#ѿ ?%b>0VM%nI(&خ<*HoDE~| Os.Љh]lD` |Jrǒ3^UkzڙY5 9hwڏ7XaRWIbT7KQ| wB.N*~-/lfStRdcjl!զ7P/\^m90M`i5FWmYq\U'-6k%4JI&[ьx (Ҋ,)m2[mP!a#bt7i.UdQ׃n$vy1W*b x{3DxG;ST!k$mTZDp3'>8N> FfK<|:' ^(&өD6~6ۦ1'!VWA Dg̥KuX`OufpRôJ o|[XDWgՍ%`x@%B&EiubB:uŢFg̍N3Q^ 1[£X;ttJ`AeB][9?ʜB5;,xiu#[ DCa39gՕ>࿱xW)J=fB3yjKAOCVI@T6=zd&DDJ*Cy=|;MLe أ;Y-Nayp;̲mK۹j\ϲw~!^s%r&*UȐeEԤJ{gG-b!]kp0 [Qܛv5R! ٣>XGkk9a~w\=N|!jy21 ]Z ݻ"҃RGM5D))P LC $TrK||mz'Jcp':vaFwATkc eyM4b"tSg.Q(yIL¼8(ATIeʇ?a̓I}"5F7^=C5yYѭK$wk%.FU,@l@%0=σcSh0@97}/ev³W|{YI#AÃ/WCðSj*Y>,H.x2Fch/*H?J !Il"Qnk KЗ8:'dKz  o;hazl|x |i*Dzh$m;k}CBOyu/Gq`7|h.!T|NãI-zMwKg;neh)|)eowQ) LED4k藔DH͢>(T(nz60޴}7 P3f"K-r2CM?WL+;Covz`RNN^^~_ۀ́IFD2~3fTWf1/t%ƤH ܿP q]posӦTEW:*.N论,Ye1|U$30t4'.^C/|-)H&0x̑=ՒZ{Rp0w8<*ЖiiJhur,y.K kʏ-ajEhh ~)۪ =j&*3DZBS >p"Ke޷Na{|JO5'= P{*1YGTr69ִ(@Kh?6E`Xꢍ34aehkr2bԲPm`i\f:$:V}*5#[^~W3{<^$sꃑp5獾3:UjUcq0g͇x@3ЬUs9و51]zL;'L 2Hr][S[k(L; i+k1OsCB5ҝjy=qRFĕE˪(jw<{(].$A:& sG V5/fuvL9`B@ *N*# /1Tip9-k3:36=r>)A՛]ﺒ(f(VQ=J3|n#+Fٖw^Ϗ hy&̓'%b=fPկ\1@8 %2Db" ۸~:3JУo-[q5!* ..6+̆p_6'ANlb\ x-_jY"D2jJ4ht\?I+ iF3q7, JKhDwۢ:~JET Wo\(4Ĺ(]u7M1Rz*;yV_akah@c?xQhTgԖ vC9Co&-vuz=7iLSNnz1e܃KZ=kz$p0e.nS{7=@@'5Ӧ.R\8WpfGu#Uedoz %Ns;s5ྦɑ H1$ XmlPeNߗ6Fo {vq}8{ڐls,5*//RT.H,- 7{8kKM MaE=mD>S~xH.=bهB_XxU=P,l>2.#YR7}̉RnZ4Hm֔v?Y"|Td[{gޡ/RmABʽ(~ seM{bm$WnHi[;Iݽ_)6_FwvHvV%mOE%*K}{8(JPF/n« 6}kkxB S8/ȓHnKU~1iPxt7Ehd*в-TS+!o-UPWGO1LxSP]KX}"3~"|I!f&$P`]$gNv<JPT;>CA!Vpek-k$N;q7ԺU,-ꬔ2ĚM s? @"=9*hreYnSiOD+o^<‘0 9C%t6{קM-Xf ޷؀eU3MbqE!L%9z{OIWoҖ!G6n&.4f D2pH~Uvyܐ.bW?~Z6iKk̘K ,߲ u/d0o0GrJv.vZwD D8b-_O sivma1HK#ds!1>bAy$w>J ;Z4WD[kg.=<8Yɥ,UϦO =C"EẀ<]z>YisSS J]Nd>')"vi;?ߞ  ݞwV$;n΅ĜzBC}*EF:0&)Έ?"WȧDr c6 -2ց9'&Fq݋`"KnaHU~96yS]݆X9qGKP2ênp˱Ąp ;I8vlܒ5~Gjd/72T:_\E; {c=×Dr U%I&6r%Vj2MO;mED59^c+=MΆ(gWTqfHO 4@mBzɭݜx7玊'BWhm&rS48/GL>st[s#Z~yiO!<<:YQNJ 8xώj@f1 鼱C%&{*:' AoCB]EOyGodCsD3>Ǖqx%|i u%iٓo$,ZZLZ=zO̽_"wFo;u@_Yu=|j̐N93"(^'1 Mi4Q2T-ӗ}=Y!7Oi۬vk7aMѴ3lvRrf0lyd%w}/3 aDGݫhF,`-Dg=D.a?J+ԩ%M=*+*23R#UP[Gl:NDC-ȗ 9N4uŽ m&-T>w2x}<=wHv"bn龨pkV"\'6nY^B=?5*/jY̶"#Y[w!ùJ endstream endobj 339 0 obj << /Length1 1466 /Length2 6743 /Length3 0 /Length 7733 /Filter /FlateDecode >> stream xڍTk5L "))!C7ݠt 0  1C tKKHHIH4z{Z߷fg+k 5 #y@@qXXaHG_n<C W()<  @ q p(=` ^*ucQ@8{l퐨c~C8 119'+ 4H;DH`C"<==yNnW[in' iЅA]=ր_`'dx,}; vPG wCUí=u3'YO7௻xAiWF0b0prýap[  RVEz!`DUVߓr:0 _ 0gD_mPV@89AH7_)\Ե{٬ ˰m~vw3\ܡ*\xlHPTD@u@ v|{;CA(g fE=;@ 5 XAmapP?6j0/)=(zY#/#?Gx|y@~! DP/c OC qxE $oBP~Ww ewGaOs+Eew$J8j#e 5*H0Jrp[y@@?~2 j CB]p7د/ JuW ?!JeCQ"Jp`WW7(K B>^8*0lx,& mPlc/S_0HwWW̿o Bf*9OIsc~|s\dҸJ^fƜZl7Hܾ]S,"E5d^g%r@I7]GTG $D NB.uvVHss$ǭ`U8mDoIt^g} &rΰ8hAfНطYwi+Rzp>1 xCMt$ { ֯LKEu&~(LyroM)SI!𹸏E: $t'\.WCc9_E4Y6vn=Cjwo* zhn->Q>ByiFVqO&e3C7vq&}+o*61S-Vx63ђ.Yw'dzV[%?Kvk[(&"VzJuk拳}&%\HJn'T:$\)MUB M) R|UMG`G"CKҞ/*6r !whڴj n`^sX0]?9c=yGN͊q1A{~gȏ{E+0X7! /[eB4l^O3Õ~GX₫WU].nέ̰l2[Z->_t>wA.mSǥ~>jv?sVI9%suPd4tO!;A.K%꫒8ɇ.3k/bicŅ%]v{l0}4ȹ˕ޛ4'^kJ&vM$!77e '@eॉy /0,!Oiي.mřQ&~\O1 kM,V u  ,%+iڦtw-ÉWEu&ډyV Y}c9&!ḡ-kaLF]a;7'=os 9I3y<{wqe=ZixV+VnGشedz˔j&/_3Ua \wdrUx:Nf-nH=M+f罅F'&Cz*m1r' *C%i):I0](?[MbyY,c-8 ;th"Tm2Q =_1{#Hd +oR4cRjzP y^iRw>NaN? } *9ȑvz;YU:BH#eJpAQp Y |4!FOбK9ᳯ^Âr$;әw=`)L|0sܕ0$Lj ^̘X٠R-aRK +c$%-I1zi%qv+וg{_c+pG18P⏊) tȭX^ k)j_hv&\Y) C5SQ {nT8C}돂A}6fjQBs& aP|Fk'ݶ@ VڧVJʒ@g~6r%DۀbMZlI]c<yW4fCԅY.1ϩE]h zgSp:} ;uZ@>X[jЄp$B@I)kJ9Va5uѤb`]p؊kG\eNn!M ~rpPt!xm`[c8'rõT#FEj7?oyZ(4`?Ќ0yb @c?%:%~1A }.\lHL=2?&^ *2!rsa`Go^oApꖟ[&WNRe~ /;iƌjwtAÁ {Z@|}7TCH'ڸ ntKKavݮȌ%I}Q2S77# Qk;'3LҢ.] ?LyD&.9ڝHfQt&N fm cMqb6uZMr+ҒMBʾ%-qjۗaxJ;3Jf*wIqoؖFn_ VY#jTiHUڠDߢYyj̆oFX(kv/P%{82 ߋ=O"x&<# 6N"5Z[~`o[|2(貦d[m?-VljYτ[(Rs%gaQp>31bsx=1-m4k$t=垲!jAct>DKsW_1p ;USX9e"/*ii_8kqW脕8{شn8ub !32d^!RV_Ww&d6J.3Fޥ҃uvË$Hbi9_W$hF\ޟ 3ߚEg~ɘO͡_hQoOʻQVOfyuR}\+869Va)v[ɔE8i8#jU=}-OJ8%ߚMQF=0cMKh>eQKَ\elTP E3NF?UxqlJJ$6U} VZ>X4S >9ƹ\Y:A>!cgjFnCPPLpoH._1{fϕpEq'`{~mu,|nEVDA'0!C4+zqƢ5Ê]x7\fb1b{tpN'7ڢ׼%LOv+i֜f; 'wgw]lR -?PX{.䡶t 3\dlܼhpI"JxHb1I/ЉOj8 >+4bwSQyn̓;^WȤ0]'XV^9l5pNyy.9q0c9}z@=rGM78FqL&;O | zv.'cnS%l';=pX#6Cr:;I;i#F*ޅ|Ʉl"*}7Rq5Q0"1aW8*^rEˇ./~JPp ecS80 8N?0|C˘Ku`ӱx^7Eꪚx5Lv1AY]3caU7x%G3]f¡ qߏ5;8$#s^/%+Ҙ&,5({I$\R8XH2&~DKHu$N`uPk;C&wHu˅-"R8ݰOAʽw%T-󗌑4E*s o^9K 4Զ-ڑctOl }ך/!P929h%̮ڐvJB"TFͻU;&wl !NģnklIZw0< jn{`*{Fbm6p`X7Аc(j 7T?* 0 S8}9Ҋ XpdOZm?atHUPFU|]N1Wp2EC3`}KKX+\ovi<.⢕Z֩jZE 5]=xA&aˮeK(Fc3s@/..JؖZT[c})4/\ Rg'bd}f]&f._&O9t[:"M^ΎKl,2mF(4TAX#u]w; 3ngrO ňh^M~њ).K(.dN=r-hĊBhon2=Z[Ia8ۻ&fD@r7n(QM':Ьi\VdUɀYv{ +}h':|.U ЏM=xY-p9݉uT* r|myF|s8%~t-XSf&aKhҤ~0 K;Y WwlRmrи:+b}nHg<0<#8u֏̣^y7ǯpv MN׺A']Í t„OO_]  ǞE9ԏ Mog)k_.!Eo \Bt<kB۫>cp%Gs eG@">/qb0J :icEܤk\= yTOeoګZћf^,K3ELR8]jKcrҸʢĖ ո_ܷ xO6Bl5X2j}K2O4CLEWXˈhahrgg"k*vIbH?)5џL$&;&VF9O974"rTYrғT#5Z-ܯ5Qx&a8?6땿Z Oa;A-- 9BB7\gXFSPzi7^,˄7n`(H|Y.r,l?5jqV~Q8_wv+{=t7\~=2c! ]@f*×s'uEqR/=Q O`z}c5pXMmaA]K7| DÑwPIYpP/XG`*z[Jy@ )al eRO9Np~XMk9~˔y=8 f(e'n(ϮBYHSFMT鈿 +zXHC_d?5Y6$t2)l X&Xcہaul#+Sr~ F+Zow~$ y(rU6< +1I^ye훡((IlA׼vJBáU}&YM%?֤I|6iK_t*V;VYOddUrI 78Fz3ښQonnU ԕ_I>L 0]&i?ZN_{n®‚0Y~ jh{Qj4 S[r\ʻB k!*CQ7FA\F'eȚ%7x,\R7~93Rx))[uWg6C)?q1Ldž}D˖ zu|vcqCtec/Q[c5L3U~UXO5m#fTq9zz|7搨:ID#(0Hjyػ: &AkCbxNuF>gyޱ~ r958.ץO/Mﻖyd>8< F$eݯT0#thcyv2&{U\k/\+b,D x]ηWffxr-mw4hyQzzk9Tqin0JʌdrU=ll<Z8!6P-m ]Si.d199Aa!R 7Ne׶(Il^~tua,__biRQ1R~g=%<^k7Hڬ.ʇwW9¾I+$bb1fZDGNᖮʙ 3K9N}oQ:rO'[,f|+FDܲ y,uBC|[7)!].Oߌı\'}:V3/Ȫn(rZGA-Z25q{kĎ^Tţ{tx!fM) TtR6[=i}j=G 3G*'AV..їW&ȯѭԷ^*nzuS\GڃyUy 81(h[p=&Nm*bUE UEMμ>6q(su2=?Miz'ԯgZAU+*;QdJkǗu0]QnazDc2>aoZb$c&5]TȉF~l' 8%EU.G}o)Khq{FF WP jf|3*cTW|knvb,)i xa煲`915~e"4[lǰ4;rXN!" 0ڏfhsItznb={m4`d2]' w%p%Ê\92s9S2Aj,ηVSz*VU: !j.?M8 :Rs|DjRϱ72.Η"l"!;:ok_/鸠&mAnRD"1T%c|4*<ˣVG.7\6DJRg|726dZɓ|?k&zv\ϖ}k \㏁? C9'\ي:i5ʔ=|)Z~&xwJ"do܄I6dž %ч#^(GZP0YȾvB>71ibۏ/)K~@^ +(ӋnMV uzLv?M\yQWhjN),ߑ.n\iDf,!t'9'nqWv!mVD oنs@ǦLć9.sқ,i|ΊZ.S_U~&VﮇA?lydo-J*s!?ξC_/ז8:enVvS Cfra]D( mM.]萜ӣI O];M:W_rfJbkJɯcbH,5djtnz3qn;R.QOx0"-;DaMXІ_@;Jux5`{).٩?^%z{T}\d"$9Uٟ*t[~l&I YIp)/ *@\8I}Uee.*Omܔϋ=Xm&El "ׯ.q :i>_ }pE;.CWBoTT&4};G/{!&y}=_Cen!)fɸAP j(!JW]sN A!N 3tֽߺvqdQ@\(F_ vrXd0 * >qVֽ3 rZf}R5LK6 \ ȩ;坊4Q(!eO4uWѓK6de[QV0ceݮH rR\ ~*sGUOo>~!%jeAUE.VXpV>k h 2mPUx WO:2D{дRkz ]xBnHR;S_A'b.#o`џ2+ǎG;,ǀUv3]'R%E:5anaJumNZkB@kIrMB!i {\r40kV ~GV{DS %cdcNa`O_ay\R5||&9 n/ߵUԝTUG?(9h)qV1 fa!mQmMw1Aѵk 鵣mgEL#HWgQX=0w|n5" .&ͦ~*~ JK u̺iN;2v=U+qF_seRNc m.,Oߣ,n AǨ ۴2۩*skByĶeB~i)gh^7z,y3?O-V [ꐶ`ؤM0dٶ;^abB-%TgmQQ+8C2wHiy}S-+o1MX4 <ߣ"e'&|ae4{g'fݔl>_3\E\Sm4\!3wsegёY{r5@Sc7~BOMl9+:1G;n0xPAx-6pkiNqt1E$[[\: A5Ba8% t1%έbkn#&d X)惼>4a,acOѪ56CuB%'qNDq̽ &{3upyj0oɨ&MAVɵ֟Uq٪]* 4G|8܇0vSy?N^(>CYm{ܚEʼnMF\de?!>9~RPa KBvLJ ǟa pb ,ʒ>U T͆Qaߦo Ul uxQ~|G~6TF4 ƓD TT;ig#ܚ>ǖw*?f U.XRU:-;_"NМ鱪vɖPZ#'"7s"ԚuFq?cz>j >6 3|1^j];=vwXU+}ւ}]ulj"W1Hv=զخc C[Ir;"ށc`, vH"lT^R[I5:̀Usd'S eaF;4k;98n/zh'qGO8uJV3kx. jbBqԜ!tmeHK;HQ5.‡aJ&.2}S[I"Lg6l \'ࠥZO״(N፠RQJg>15zۏ1#d߈3 S AgoԱs36Uc0 q.F#f?ZvssFlC\c բeGseYi]0T S4x#ˁw"$-ct!sPܹh0$< )㴫pɳG?'uR=(9ˢʐx*ٞd6)]ym u>G;[ފԴmCd+4{1éc`SoΫ 88If*x4}\;ԃ-䫺؛h ɓ(e$jDqm N> V*CA /4PGiAn%ISܣ 1)L.Њ4D,M7⿴gۮ :QulTGz=Cee A1bO*)K;J-.$g;g;rԎ B>'ubymvZQ1~)aä@ORrŶ}@.6:S@Uw'/myA^Ζ 8.7bONXsNjKhZP"*X^ʴG14υg|)~kp[4σfl[>+utoEf@/Z,?p:Ue/㉰M ҥ7iv⋌3IVb/D{(b0y(Ýܳڷv/r4TiqlvSlwC3ٮegΉSUUG1ic{^6y1FiܿiD^3eC@ę)@Q2x"3ݧ 5i'EQ(ޭ.gĂ#O V%K\J{R? /b, q/3)3{^`S^ї>Fꮊ5hHIZV0AO/_.eφ_uKfMu^aڙ"|$JRyNdw )Vso‹PY7& R-^FFtИ:zvPZ-- %b@ƸRǭ'.^,ӌ  '%1Uf>Im;i&' [.dW #Fw%v/hT&HI st؜WPv(н٦Z  z$$m jqZ)x 9z:*E"(", %k,W|uM668 wFOHmO]]Bo w L-78 vu*lr \J!u{ش) ;ʢVȹqa1h<Dk"A"g`K 戏-'nv ,N@wywVbDu[c/=ݾGt(p)cHmV?S?rV5)]^o,jafa/oc7(lM+q )=+}LE+[h _Z]NMJh 7"zw3#JZ?Ԉ]/EvkcF)?C{+e/V^?!Cd|O_;^ ylLmtpRUl[sq?E5 g ueonw$!}0B^ªp͚KcnpSy4ƨD7kvph Wm XӹowMpfTv]5u٤ mҀ1S:2,C6/Uϫ> "qTYسc;j1Wʧ;$<_*O"3._'Nx26—tP ݕ8]f:#ƌF 5;L#>Y `A/a%ſ~RV7IWA ՋDΟIVR;?_k+z+N;_LRTy=JGfiݵ<_ȷgp`(:/圪׷΋]G\sgA+Io'.|\dMԮ7+B*Xg,3=L@ߺ!5E${)sy>Ҿ]ыCxU Ijeɘp4lf0yA+D}q&1a8 k.v#^YE/A:rj%:zH>gBJd!>aX?A)Էi[W"KN0&&«Aǣ KŤN%z<%rIOdG|to|̹FC, ƚ%iCx>:0 n*xHvRa:xx D"Y4Sh[b@}V"kSex_$~/.NH_ß0f.hCAU7YHo>.# =$VEXQ% P48o#}ۄ$ȃZcMF킉bf#ݤw|XϒFf}tygfva܅pr:qOI[,o xi8ӽbp :޹bI:pVe>̵{'u'-C NZtwc#4em\WR>=]hFvǧOEZ[$H4ޢPڋM][n`3^jӓpo1u/JxZ\y>Zmy{ٌ!uTʮ~N2s]Rea=@-=Q 1h#PɆn_ O~ӗ9C;1HPɰ`]# .3Ӳ|oa*gjdqČF5oE KH]Xp79I#]CSi`hw+sf|if~JF qd[n6TW'id<6ĿROS? ]+hqiP2&Bv,V?l{wTZV&B6'Z,m0&}vKL9kO 9|fc}`L$Jh@ Fid>{M,U=fLk3݈N/nv&ww &t(eD22#kr덳BХT7TtiuV8snb5lI=ćaDj~d *KCS€P<+ ᨠyM\ovh]f|#WߢtW>,:C3SCyqS=K2XXKsⰑPr2xMW1k!}4%#boO+?V)9MÉ`1} b\K=rqOm45)Fa4Hm#7Tr]tƣnہR6r5׹)N"V)&ȏZ1jJE͕R44@6 ;JG9WƛRuHM1KLwn/R8 \*RjUs˽Eł-GoѾid5p@ LiOa=$}1 h"/?+Sh EYK{sȸ7hK30v?6Cv0I_K@e1}зY@XU HS% ę4+ 7XJ \k'x5-mH g궿Zl|;aʂGJ1Mj"^Ώ-89YxB`32B}6n ޓ,M{.`ut@Ϙԇ8=!Z]hn$R#؆HzK(j>LHDsY6P,W}ܻ.-4߳\R#ec12k-Hfzx ! ТcL.e]|A93or$0ճrEcLq^}~ؾr{^d[)ɳ[վwA džx]=mR$v$ULGY<WP4wIe׌z3S);}>OU#Q:*O^V.=R^p&ٶ[ F3[Mm73|)SwN݁h=y"D{ _đ9 JXRrH#S-1ifudgN,'/d?IZ9Py hu#D$t?,Kᓲ@4 ;t%F wee iU(loq謞[-Tb345%LOo@U%qTFH俬ZLd18#XײXJM JpVApAV$+ZF@&V<5LR!삏WfίgKv,=JLow+ƐAA;96LţF$Kr/'4oޱOB[%M6d()5<=wkϓkV~{A(q)#!|.AA4SyMkǏ=' ,mB~qk;ٲpI^N:\A!N<֣7&y ȹ`wX(B8t54rM19?ȷr iSYYJX9iT#Z'<;`]C jFE dWQ.r~P Dm Kܲ15)a;ӢMO f7#R0|UKoYG> CX4 C;l 1 AtO/J~<ԫ)  Q\<{-IUcY'Ր#ɝW6kR̭='@NpD%F*nnHҟ/o1zݳTـ9$[Zv *E,|y(C{^Ivj[6R.N|!mvY@G\EHu1{|3w8] =o$% hnƚLygfS=Yׯ80jp3uEe@@]:DՈ^VUN!ohW"Lb:aO,+k" z}9(t{~x{VF݃ ,/$\ l!?ZRJaaI,)6i9^о5԰LgT ^omttvU\ŏxDY${v3/aHD%QUMhHl OWb?[Q̠?vGh"UP Ub.mf 0;2~O_:<}4y +ܤ5TOJGZaiC-n tnEO ?lg|h{:xd:U6HկĭNyhZ/TP@)O:!J'uZ#-fezD&_7Z./X^펂S  MFV⵼amsxc݃s)fWQ*Ek+:OKu`O ;0 O=I~8Nۓ5xZe.|ӴX(~o\:g,ű;&S008Hc;)j"&H .T<["G:eo~sWQ &T]'3rMg qD*Pl,]l h}R8C qekS.ӐFDOmTI7(dz& ~<ıH$q@u50<; w.vGTOE$~\0л*Uǚp͉qr+yeeߌw6h$l 7QJ $~ k2 DOn{픔{Y.%ߎ|Y2Ao~zmS!_yNAδ՚8K8F֯oZ4I@/YZ ᳋IE9>k|"0f$ @eƘcN-XU'LrY(\~`5F/I[_2y 6c{y +5d c͉FBqϠfã[ {"W|,5ּqOv8 ~c&+!1kbǃslz64P;xjÄ,HM,36Ȝ,3誶m@؃Ud,]_ُaxqOk `ؘԥO+'D-+:}2*:>;wrL1T?N(/;(UZ%Fu>](zS)z~f?įIwͼɿ34G֎UFpb~SxMxXc7aD}{(]n /kiէRbpbQqKwHǨYQasQSaN2Ȃ_}1pd53Gs5aκ;VM5BE%˯8 6̉8 |Fk(Q_(*9l: S\]DO׵$YtEm6;Kx ѥ $6Ĝ\!wk_*ŀ(}RF'YFFcq``IOw~#p FwsNX&#!kkt6vPU)&mUEE(y(2Z4Ѣ&wfrI-~2xkq4cRCjÉ;%x4zU& g ZO P(o 1SϏy -M?e + 2O`X4hH9!8 *$Ѷܓ :F(ܤy4@f[Oi?%*+a.O BmMӖQz}f(HOM۠) c if) uo{|{H]߉]{?ˡx#)bb";)Tܙ[`#OBdx+&{PƀG4[B 0o[Q7 ',ťx2n"\ѧH߃+_6MgG_8mz FXXlssDKEw$FNIAC*AZ FE/BW^7sѤ3qڭl- 6r*.sW"*ʩ!b_ YoV\U+, AQ ٤1BflUk-p iV0q rlzF%t;wӳ+JJ ve+v%)䏙șu1C=ɥ2c=t:x|S{CE#|[0Wf{YD ]BZ+BG8E!3A WK3Qi/o@Ė[~ٲZ+`}ã|4d. L<0Cl,op} Ʉ..VX|=t8qzJ0mC>߼?7v'- %LUqa ,(xA{ oT&nj?/ jT=5H*>s5c(~>RJKL{ςȖ֍ͣAJ7U 2 WfnWY-YτAxXüh T͊9R~5NR~2r>͜X!L@~c6kD5y%#L0 ɏxV͢CƧī vr8'Hé[)%c驽}Ci$0=|?y|xC#luUj!zT8M *jU`p(v̘F,r= k?bL>C"bM-I&%G"+U𤋮ui<=s4g,dQAOXZJm&,qrA/d%E(JAI'N^?)O2FSdxy_@VBc7-+5ḋj\dGGQEW߅0fRCڮ{8_I%ȍTVeȹCE9,t-AGF=hrLc~73j=l7ꀞƋ#ۏp5CBwk o0J@>2"CzzZ+/ZG<%jϼF $ )n ?@96п=0s|T[_T)L kV-E"Tr8 g7q]bbS?' :5lW֌ɮ@D #YFJp$:J3pûD ^9J=Vugqӱ?EJ1A<Ӂ >#{kЫ,V޼nI+iB"^WG=Wp>K+'7 RY8S4:-t41Z"0g6 2Rxίi9'Ou` S>qa?@οOEow#ghN|PE缘[Z/=@_dY9qǑW?P~EkS`\0+yAQ'">=? ?'$פ/A\X1|-怎,:0lt 5x‹^F*(v &,Q U̠t(Q%CAe]7Rr.:n EzVRQ2dbp$z,"׮|}: o|( |སn^0؀N3 VPֻ~OZRWN4Wh3xQZ!L)i?!40$߽Vy7Z$_?6/QN)4/޼?d IJExFӤU@[@ \:NU--:L!K hvd "A3QdA*@_$g~3F?$ᴛMv5RU^/So:WR ,Hi7>>0l+WAZ򹖐 .LL@ǘP9qAh{1%s̥`d+8TҹuV)%ck}#wz K:ሖMPlV:eS\1y^(=^WC6+w_YW-tYpk?;L7]p55ŃҲ!UP`G)7XXy% z5+Cxi`$fv$#[xo˃ lYQ 3aަk'W# /_d߆i9).tf% pMb$b{Qt?m,bU?'s9#;*GvaQi̘ݎ:bҾv&E6(\_w1!DcMinZ)L&XC4ܰ<8'܇Kޣ6Kj #X=^A^ hv_2\a4| Lh&yC V(ЙnX >HI78#LMՔi C,ʗm?D 7NZMWAF6זj)Z/6}NsFY=ԜƬz{#uie?Xe{l#tUX˰eO[PF _S =  ɉr\y~z  "O#&BUb nfL} 8ߠ;#Ho58 \c (OTMcd79tpCEXPay?ߕ_RUgm?OJPϣt'EYbn"f TbmE/6^!p,fOz4nCm+e?3Ry)Ӗ Wۊ{ɾ fIU!%@1ЪMN萆xiLD"Svgnꥸ4̉1GȥhᬈۣCI`NkT;#/AXl4gRhϬ(߳nGE.D 7*Vkxp4р^G I\Ph 2цculnr |(n{w49/.4.ۑuL XN3743?`S3G8{i6 7 2 yi+}ÇLV<$`nX2RfL.c#7fA9v{(Mk6:[rK+Ҫ"ʞXREA/O_Q^T)Q7MA3 ӏ/)eHs5_eE~Z0А#tRwXKmU[n^QgN&S3"(?wv>P"?b$ARZeS=JM^;OrpZ7T8z*]8=4UkSS@#w翅̭XKZ+\QϴU#7"$Ӈ'tblpdR*dӛms3cxI <"v4Up*/֣M1YYŶ@Xޫ|Q={nxz\&%Kh#d3n7ջHd3t뾃J&YthԝP.A_?[O5y*f%ˆoAxY9("C G-nmb^KVK3 /)VW,#1>3𲼙f# `EjL@:rS>x*"fwZɲ@xnx(fFq C} !^ya\[WyZ۳ˡ]N|B-xFE/[eԁѾws@Fz{ }2Ϊݽc Q[ZJPQ Q9Gt6z )W\7Nz0!/Y$.ws+*XꅰC?ɴEn5 ȰZ-|1/ϻwuvRj{f ("[2Vcm 2ֿs T3sH|||2jT&ߩOR* f\ʢu'׻5M)ŲGeH-Y_tox.H|gpt`)άDUM5)<;9;t*v~wQ䞑kQ/)6S*zQT:W;4;PU`wkM܈xЙP$mTy_roS9!vviY2яZ^b5ͩ w :mb4>oj;wHŵ3drO~4 6ɤð:j=#qVDŽ`r[qMZGwm͑Gٿd\h8.K08]PnT Kah?9K8+;I@x0Vlme\ڥW5Ъ<N5mاz+f7 ЃOmT'YocTn\hAQ>{~_J⺇|Tp@By(s3Ȭ*]2RV׀kQ}w<WN+ O}KO dP=߽(_@\+]:# @_\c L^$.hrt5<1i^XK>p 7߯I#hJ!s1r"/Nj(}Mtku.&ʏ &2t-Pxo[Xڂ.0XJi\DS \QB9A 8Ma6^K_.c"`ȒɏS׊ݤ:2 p@M36==ILB1>6d 7 endstream endobj 343 0 obj << /Length1 1814 /Length2 10972 /Length3 0 /Length 12117 /Filter /FlateDecode >> stream xڍTj-L#-]9tHJwC3!Jw4H99{k]=%, ٹ8)M.nrck1u0[(D_R00hT/% @Bai%@]0饠N0[kc|0Y0HH:a @;>v9``Ý999@.P3nan`KoU#Ofm[?ZP+;<l- W%xlRP9!+l\\+w![ i X:jp8rp>@ ǀ?&d%5Gs:]8\l~S]e RPGG0{>i[=9Y{XZ&aĩuv+Hhf @/ {Xp. z$?vp+ߎF\\K[ 8lm l'~|=.ߟe 8xr()Ko߫WP7;/ $(|:ـTXABRx/g'Uo߁d]p3A<**݀oMV[ں:WzIh"kT[)cy[Xbs{7 p[@,@Gyq(1H`aQ~AoHp**#G?8#ߴ9-! /?_qJ\ z+;=JLJ >Ϭ60w+\u|Xqc?,\a5}T/1\^Z Y+Iξ=):G rEOa Z]J>%t!B==I4Asfsidp@a Ď/g@{6ή?<W5vjgct_g/=GS=a!8#șzQL`Sm;zs6K79! TEҏޥE d.=&mU͟S)a&r2Nd"7%hǟ=N]m4{nrЙY5mSh }|T9 Cw1k,>~&jj5j شΪwM;cXaDk6mŁ":agґ1)Ht=%G IC6P%*E̋6&u!S,̚w̟߶u$kr 1b[7&\I77#bz&ޟE8ʭ^a:$SW>ˋ4j#.QjgA! X (_a['w܉D*e4I8Hg?%yIi8~oEG"{[K[7$\ܽV:qD[v?I #CJ>B~F)$d7ux6,£5CjIaTڙz-wF\3UiwO8 G? I2o9$0Dp>gBKᅚŽa+kھC/vpIyǷ897[q fNV6xWSwOclQ~YPTLPE<=(%t-Uy&W\#$߰9"QPcIA-$(4FXp2ښqmTYDgdqזr3 ! >}5=; |5wj:7^ƌ0[q+> 3L2r=$P;!~^{ÕK-w 9&+1:*2t"DzŐ];%j\2 ;K Sٿd $=X 6h?Yh"h!Q*Q$ "uubD;k^ s&iȓu𽯚 @[i"&gSpt2aef),XU {ڈ 0•#=vה}Ɠ}kh $uRό^OC]ǚ*e'eZ'Q(E/$ nt7Cv9E.*r}+q}ٕD\\61oklc"&n+7tZyb k F2K[7C~ygh8'*Z&js_F!D D"'I=OY3n9$1L|2gnf.3~ׇϧn$S]RE"S86}KLct vxoiƇp6!6/H:0{Z+g=_ΑPtt'VKk;\ 2nRG|@)]ҙCCeכle$rcX-Oh:ɪ6UC8ϐK=6^˓ 'ZdE]-,p= pl9hPsҪͧ>&xe@f:>dRrW>|gx{˳݅r_Xf3(96S̤@-g.ޓQ:W-l(- YyrGzgIۥrN.V%RE9]PX <׆&뗯R(bSkAgRD1@lcTMmZŦ]5^?I|h(&;DbKVn3-oW9[pRvKYOԨ'TZ~;/` }}*/3SK|Iu0vHaB~ %ESQ䠃54 o/՜ϒpEM/NGJSfyq:'̌L* +귺o՜FRf jAJI. ,fl9QSl??u7F1SeJy5Mb&ko 2p_D̕`MC\V=`HHM-X{.HbI+deP.F3/ŗjwƇ_lnZS.@\%޻;q-iVԘu9+JjϫSA$B>%MkO__VLPZt}vwJDJ $!L*'( *K?[(p>1 s2E+YAGCmB!O fͫ/]~fy[ hէxS$ʋbl::]Yx7rz܉Oxkz(tYnw RNxS2şDRiCo͆PjTѿQ}#iep鏞Įק~ 7Jra \MCiJLC'CqD>wc E=CPW`u^eDg)p5l]˗$[5]!ws-vW^ÍMHwDON[iNW(p%CO;xC! JT=jR$eB|z&L+dk{VO}UsbGC#?Td| 'AZC xn,Ka;aYF{QV" R[m&vDie1{R."T>~/iL{g͕ė @ɉݾ۾USr\wD֗8:Nlk;ݑ_*cqrVYgH% /c rc"#}vгp׬`x~v2_!QR325MWp ض\VMX n`2J@3vaQ<9Ʌ`!1O\i(,PJutީ]-ki)5Ldw<ƺ=:*ԭWT+~s z/~RDWbE~MOnk_H/:rf+Eե|C>ruADp0\''1H!gtPê=#ꖆ|(VkӇ;6Iveb<8GB.>>%c|YQMɪԳu0KWL 9EZ1@=)A"[% U+j {may3a =f&x ԅ-weK;=xiwR+rS1ic] /mO 5WLS 7uNRj,{I@+ [Y*`-wyt+Ȫ084KG\2Ci+\u4T)R6*F،rxd|5%$W.xJۗ cK>q ѥc<1,ՏrWY iaNasxobLcz]sE^>[ *[5V,!+&"iY|n,*xKqg\Aض__YgPi#zkKi=׉)}1Ю "L3"wHдTҲBԫ̎#̼3"<#Y31HE~?& f,;(S~v=rՎo^r)N|Pl?ƅBbʭTJaHD ~ rIHSKwsJrE@}S7N .qσci@6qWif˧' #6ǒGσ3;8}]~zNdg2 qEv,G]Fֺ xF.$:UDedXvTDECTenr A6%Eu* 7MfBmu2zz+c85x\U=fbNFoDTKjٙn)3ɏыyt#;o^e{=32` niDe nVu]}|=1|Y==sl,c+6b=bn. zIyȉp^} 2SqފLG[ۘ٧@ky3ynM%ʍ@Niܨ ڹ6hMA{RKkbk@ӹJQ+ Q;96jsn$pv$ ǟ붚m{i2-ךyhi2sxo{)ȎԓJ`5>3^_TvEk㓐 )g33ccb~H,f/7rګwS|3ԂAFp(]7KjXsYhB(Lώ^:uxD IN!E9pFmvצK e =Q6 ]ԒatObob,^c?cz=Y~)l;HwQSPg9iϓPnGMX!o[ɹ4׮AtgsҊFju B[ky{I!{k-r~~avg| 1he-),ؤ׌~~*Ok;I-EjLfchiXD8ꑆ{DUƅ|V5y6n$YU-o!c\$qMISRmؐG+;cRڸ!cE f\(%^C͎~Ȁ3?:%29d"ݧBSWƏaIosU^ i9d0<Łw'a Gۻlpm ^3`// ׎_j{\YT~#xXDe}-u)9ɟ<=运y:L*Ƹ;\o=6_uih1)#脾'>=ߞE+Bx^%27uR =VQuz9`F/!xj)t٢ |M*ʨʹ|^q.[*>'EQg|PQ\4&;@_+wP1HLNv.3]Z/iSawon _@&Nm='4=uY펦1esu#n #oqZenT&Fg{C2z@ԧv6⻽o>`nè oz`?,a. X XNZ'NEX(Kv;!$oN ;ŶeqUdrtF3Y)]^7WrgILT5>{UM:ܙ}.PFlY&_uۇfAJQ}s5 2v4#zRQ'ddnFf}Isu;:^EdT=C>ra]7^CR}dPsId"CR=Yns!O`gXeՋtD#N2ٯ)^eb5Nܹ&=@ ߴg ԜEG|""GC/YK窋K{-<qܧr#c~T4$76A53/ d| *j/Zܲy.j2{16UT3EdmCWqӓ17HUD"RbLV$'"e.HŠluGo֋e1k#eU4/5_ٵ?wkMkWt>XU9$7p̺>5v^< $~8|DzV&T93HMNFf@YRı\99ϭ;\$Ѓh K\ɴg3qQAc ?VX)~!D-'CKUK 6)pkФݼC(~Q+_[ cj Br^iӛrڿgLM~yGw8Ӂh'֝\26؊ً>$[]>Kϳ͹@zI/GW@h(}R`Rc>syfˁ l;Dmp`ш`E@NvdL3^Wf[lַM'w(X]LWw=+|m:kf3 reߝvşbe~~]pw| ~3:Urg%i"&m*+(j]!]*x9Z,d7T:MBimW dzʰ9}KI=kTb/ >Ɇ"*wXK"52inJ} H107e[A)jȢ4YƜnA S!|-\kc7i$q”, }r"at=<"_]k 8[_Tofur>QMZ^\\rAJ~sJrQV/2 %)iEù) 'Y!|?NߨOis1#?]rsKv"(Bu(<i Ef۱ Hw}V`$!*5+u3$OCN< .* T5`ח~&*TmwF:`ʁM8nBX2'FaW70M@*56፟,Lky NϡA/= Sރ@c4;^.e9jhq,'li?ŏBRI9?HfݚsCwZ04|Ccil?X3f,F@jA*kYM[iLh8ſ])qCq操-_#"/aq'.@c=S~^}Wx=>F*"HgH~w1~BG.!'Yo4RkR̟} ( h"]n6dv%&9 mߍ3qiX>ڗHP/:<ढ़9sqDqi[K0`$$^tҡ%7ѼHrDzۭsXz{ӣuV033wyaKʡ·oh33H6dP͓r17Q ŁŲ?Hsߧ0ݮ To58g^ZQ@^kUC!%]`LM]iYBz؛UU+ߥf;?@PX SDr8+ԯ|*(׬7dBUZ58!qoPK 2{ U/<ڨhe& $]/Չ03p:ʿbo qg`4ciu}}}܂8NF5`K i6EL ËaEXȊ?9ײOVג][Vj4 幁fR#;byg[$.}iD|Aj(9м'f EvNi?ہd[֌% \O5׳By bht眷u5m^|Svjp-uqP8y7th7s*[(VfA/C6)J|n Д4Ar|,̌0{i-V\wڏJ}+ tQY"$F 6-MI03.m| 1R;V6 ݬ9ͧ nS<0/+~!jf&h90컎ο%qC~s|]xgʋo7]U2 mvA8d*KsߕA<Dren_K. y.Ou Ax: z8gw"ٲVy~V #I/aWW۫煨 /C:?\ιwM-=){K.(N)Jdi|2(4D_.G.0i@9KubkM1{oD|3FA%L`:39fOjT͛cLeA"+z%EC-6gtJ z"ɝ*ISc{7˚}-Y]!dSޮ u$xyH.-'[t-`R(~5?u;5NuK'B9׷ݪLN<. *9p|s_7e0UjX{~yQ-DdjNf5iAEUuj1SiFs E^\}#{MG-\5Ta\gF8 *']&Ӈ07!U[  w8fmַAG]'! $y )cO<$ʥ[7:/SGuw~P]dDm`Cub~5=+Q*pq LɆcuӦmEd< cj!:_] mEʉ}9?AKIQ=.J &[SK,:7RC% M4u=:-yuenJTiFx{vzCMiBp;{Mҏ:/d B#<-˓ZsM?HYl(X|~B:9z-d3]k cT=2)P JSY Y4ؕb\ /҅ 6XGĹ\-UNS~';%cX2CВ58,-/Qէ ],ӆTan٪9a/w+^DN-C7RYYH+ⷉW%a1x= =я>$9{_WUU{fldtΈdV]HF%A",o<#"U5rzZDʭ>ke`vCWm-2hB%sn_ƸHJ!#! tXy3'[Z1f+ԩ-'nפ eeL/G1~7M "bxBv $) G#}kqn=qp}-MADrzNj^io!tlwٹX␏/oL@+W_ڃD%\-* %]bdɽJ ],B[=c5+OU4ɩËT Ql<>,r:L7g aB6r2:Dc6ŰYb})5@]yE;&yˮnŕ!`!{'18:ƽl2_RPGI"ZBhn$;<ĒVM::%D31yQİ6̰àX5Qbo[=A|ϕ6Wk Esi,Ty%'idgU[{.|q)'ۡC˞OY">)}u)qgB "_#1,4U\7JBW01MlJ]4J&]1x]qy8'$$|cHYtVGUg' :YBtCFJdO!+9TjM[Jts;Rؔd@'*&vk -_41zYw̨5!ɇNQH1kʬNҊ5^j8dHcv{8^y~-ψR=.DKծ1ɩ2'}]vr"/!9x6G endstream endobj 345 0 obj << /Length1 1741 /Length2 10563 /Length3 0 /Length 11663 /Filter /FlateDecode >> stream xڍP-h6H%x$CB;3_^Quڧ*rU &1KGs#؍ `e`feeG2#Qi]\A`H^mfnqJ`= `geO ?@d Pb;HTN. k60N9]@f` ᵣ=@t6nnN,,f̎.tO @ tZ P6sɌ irӮhiA@k;xmАS8+nll+B d3 G'37l *Ҋn^n3fff {3׀'7H^ E "e^oY l)"q>I ڽY- aĢ9$ y5!cXYYy@g†NN?̯ }V$ +$_W3 o#$66% `jZ_j ߟ^e'|YTdO\ `bbqrx8]o۪jl[9zw*h:wPv|2@ YX-^?wSH6s{dw׭Pr| d%%rnf!A /*O g A`+菷?}{}O\_g|]n)pcعf..fHb.%뷲,`G+= axX, n?(x_h^#A#We,VANU,6N6?6пk3A>?޿r__(N]_ ލۿ+e k#F޿M]^@ G 0ۺ1bO Y=tL.hit9!.7bi_0VwhEWȞ}6G?=$O#-M M~)V?HIStO;[ꃳ;/j!瀌W`=jnwZq%sToܘHμof'_qoUhR@_cMS/~hae/|5:FZ'5 P腗<ٌFt!HwH4 x 9qLlw7~,?]2BcZouA+OgtXuf](5(v:+&Rj&OZv~i EG;!;4iMIR%Y-io}T8rݭ[k;g\Y,#Tkg1ߨ;{>kzm F9t'ԑdS+_x*G#.6!lbEmLP.]٭zt>1xAj)!LhUֵ9C'+^R W=։V%mOVFvǚ` JmZU,8PwI;U]'c[/b|ʌ*/ S|cdoGbх<`}y m47' lBDp :[$E-bS4:)t%ȝ\GƘd6NSkIqxN(&yGc{-c"ad*Hq3(J0#AZ(Bfjs:vՓ|2UKOid-Tm8vu0UI:r:!Z|ϑ-k+FL9Lw7<#d;/odB2l@ю(|axz?& _}|N: Ѷ;CƎMߐ`P}l>s %y3fc׫%cd$y6)Efb^^ˤ?SBMXZERƨd5}teO}ed76i^F =k{ZgfPNЉ܄0<=7@ bLcu[FQ-+] jji4rZ NA ieC3m7NJա#HgXmtw/)#Aqr5$'}v($\ +~ǎ0o*JFhf~F_ \՜=صYc*WU8]-ִ/s=O'⵷1N6kph &JW7~Z1뙟cǏzgTj\Hqby'YtG4 qy:YqP*N,ʩX_E7yBꎗs%s牊M)Y}yil1FS8wWf?҉T(k} \Ig`+SX6(o_Xّl3M dCٲL ;$ll2rf7FULk`*1؂ĊОQĆsI[|| [ɨ:#H2O`7^?ZR]֔km`Ģ3f"_'Y xg Vo6xK.=95?lim`}'Sq M[\5 G@β')6aWVZaj_.?QutD<:26w3ô uDZbRꗛ*7hr8b-cO2͈)硖Th#EZ5cEj7Q#>b=JAu7Tʷطo>']K,:O2 _T6v%Y&yaVlc彾?`j&y56L 80,CRcpe ~fflۼ{=[J!`^KK\ q(H3g*;wob]&,++̫BgS rHEhk΂ޚFG{7oHq~eQ; pɰ!E%Cm-?o'P`BzdaUIP$(`mPv"0B0Yтu7!Pw(z㊰ +w:B04uKҴYӪ)S[}9;Qh>qRrӧs.`GOї u`)Y&(` 4X! f =ϏI h. #ƙxZR{?~b1n3u*"6sܽR"\P%}=M 8@R2;&e[f'Y5l?+ ~htN T]>v$o1Ue=ۈoku1\ף.a;QA6gۧ1FΌ|' *xė"b#D{o,W-Ro>'d?T휋Wl]/V=#zäz > dYS:CF\D _X&̝X8  #.R{QoVl r4{51u2;I28ţΝ{YЇU;ឺ9󆨶#_˯yV'3fBN@fb1B?PrC jUުN6mCE)'9B {HeDV!gɚT< x`V1x;I{If'U6G(R1)kOguF[X|MbWdğ)'Ž s,K a X2xrߓ:]>%+=~֐v&pHթy#J1K?GG&"oȨ'pqE>|[IE:ܭ";cAu+rYcWqn.oR ܯIVp"~!FO$Fh BMRs6â7ՠ]ʼnvd2C>LEy}W(;v@@l^2Uw&]IR#4$?bhXzop]12&8O:BeᆖeY=Snm3iqk5kdЗ8[ arh$c!otw81(25bV'i5c`?H妶IL ıjҒ+D.Vp4ׄ6R&NtrtS@(9jj8K3:-H*`ͣ,M)Lww˧R.K)(_w|A#5St= y-xdou0$PzyxW]}5ˉhIqmC(F~t^5gIuk;L]E,=tbO+-fGIZ!cLX@Aũ#1F9^T~UGEjS'{F^HsdaQG/-SHp\B"9#gUÊJ[OT|rAޟHwo>(41ᶏT͉k֞r]/-JӵDgw'&S`'`2loEr.Q 0ҚUseQҽWYXxUw!5V=v@Ui$`lN]М[Lʛ߶͡Ϲt<ڣ|{0*(㕽&\CoVT4I!l8p>8m5(|6\z;:Y+kj,.Yq4\ѡq3yhZڹҰh".a3[% %),Az8\3O f6}`:eDb)_se ֙j  > wڨm>tj&n|+o<W7DvGXD l(|]x<WÈOĩ^K 1 .B> M&/ɹ]t,hm[0t]>&~􊫥 GK2Ik[ڵPXKbk29~~,eiȊKl/{yaY]bx~dS vqkx<Kp$C:N$: PcSkM4S/$ꉵDNo!z-gfHUkwj񹁦:x{uʊn^(õDN=ZΔ:B˓8١U{t.kugiD|,V9ouC>< MJQ|\DrM.fHZC\>+Ude O4qTQߐ xD+.Gy9W_K G@ ߬i,mU֭aJQ»(Hqi^'Dڃ)WX*žmٟ^h?0<.Xy];TMaI\qZ\2FT/j()sה< kC#;EjSćR}5Ha0#~rW4U.EcjN4?{>19@Y"8(=b|3i#A߃T뵊/PE\x2iSe< 'Xr{~Z긛 Hb:M*ͤ#M"ljCYl8#]$%`A9Q(bQ&Hb~% JVC.FߛޕH#S.w а w0 ?$XEv/|n֟RGG+V- ckk; jsYD3k(Qt['I7#)tl*Y|X=+'ߥMcNnq;w$^|O"i'q ]GtʟFnP .TJۼ3+@iLNDBך"ͪ~ci 8wxՑOdQSmgw܅٬K1|u[f:m@[Y(ɋT"]~+6Kdj,)U@N?7 uKQy%`6C"zam ȳn ZfDتOqw# T G"w|{QL=6/!}?Spb޲(@nDIӈq&Ɋ2q҅ZFFH1n$-O:I&$4 ?רȫY]\ab77ΐkL{kŦu"Nޞ۷/)-($Hd<#&۬O=46t geRGn}I&ك}')}6@#dC͓@*3 rsbbֈnr^6 TLPe^oR^. L }GhBQ[!1cT2:"04~Õ)2oPO#+:*P[Q)0Y1ɖzO"E&JiF3lWQ+J"4HfNopBc陰ni[74%A*vʁ:#mX]" ӈi|)r5͠j6VGgc1"P &YT&k&؄gZ!d<1TR1gmgE6$7qLe ' UFO5cXs6;U6>_WQM&F4Ƃy Jھih'8$+$F㜰m DIM4Ai !=Dݑ?eW!ޢW֔.f/pt6}.ůn?i<*hT,/|Ȝ՞{r.VDn<|Rjp91g-wZnNȕ(~u1.5KR;[V:5څIl^0jiOo, : !s:Pa = @sZ@\Q,꯽@E5Eeއ&D:IBƿߟ( uLŇ (qS՚;gЏ̆ԠrBY;jP˝ZL?Zjbs*C4ĚH5fB&lP/)܇ c%/2شѢL0k]ZT+,+trWb>Pf|=T)K~߁v_&JU-Թ&4:-6Ya02 # +E"0vD 9f;D+҂L5O &'I!zHno=dw, p]Ŷk O%\Y5Q!kwɊq _NS:GY[x;kStHqscUUc>ƥm~YW,"x[R.[E8}9gH+t4 ELDʃVi<VǨ|Vz4jeQ|Ӑ3LK%TF)*pǠWԝhG? [qD w}:'ú2պf\qf6Iв '(~YAy1OUG&E˫ ŹJK ;Nga 23g$]ֈwguTrkGBM!gMo+16fDb{m R؊'ȹ~C ڬ>^7 -ߐ(C; DJb}(q;oT4[ʂwS( 8-#-QbDŽM]DTꕚIX$qR^s)k\ G2C%NStT_zxE3 _'>`1ٷmTpFX_dX;HցźyY[TWHqBfbK>dF-%>ot3^V&]cnHMu wZR*3bA3#qz BIcQ'ti}ui+[N!XB/e W>e|Ih"GAoāWӟr wōwd[*+yc rq3gǹ <[*VY풔zUCdm2E]pʆKa%wP-.c}XR{ endstream endobj 347 0 obj << /Length1 1395 /Length2 6096 /Length3 0 /Length 7041 /Filter /FlateDecode >> stream xڍWT컧SQa] JL`1`t4 !tJJ7H! !q_9O?=9+9 p_H$ P5@" 0hEB O(&*֩h7]8 K IH@aH/G8B :tZpēwE@*\!)) %7jtHg=` BJ%DK <'yn> 0xB/=70" # jy`]`wpÁNgDP`= œPW@_]GB0_`WO8: ԕ `4?y#HOO/Ҡ/Y wsDS" [=V sp]hVs b HBR @P΂C~~h@#C /H7["8@;FOv<X~}>Y{M,Lyۤ GA~a1@HHJ >;[k蟌0G8@ s]GV 1=KM!e?h{r2+ b/$z#t轀9%օ8@Ӫ7C %B=ա(i:+1{B=3~!?l]wA?%I6AЫj0{ï/zhI /^N708@ 8D)*D?VetAB^z~_M@P{iLģ>k#r5s^9a+Jq(3_o4Gpil4hvgh[.~+P-`$A!ՙ T}wPz6eiUhG`]=+.ɏIʼfd^bU) aNv 'CZt3WCSo? np!6?Js1 bSkůKq+aI=XZAi~hOJTLdYGp{e}aKMT)/&z(X81{ѕO5ygwq|%1t< ҈=ոI{׈6F2QYT^k-*$WpO CP&ɸ>J򨞄l`sAYq 7q&%bs+UBA4nB^ atSV; ߨ,mQ7RV97Ou1[Uk9Kvɕtޏ5 %'&4,E߫r/N'+:o= L_ёc6nU%{c5q>^6]_Y65%ZyP,l#l~,m[-.ôioD d>pGgܚzڠ{AAStKl>Ny7OFIqxAx^4޷VM˳ڭ^l?gU>]\Ti=&F4J`;.Ya)_IP2L?`%N֯!)c奅y@/bV~^ώ,v[tFYYY>UNVLkV} 󦳓OάI>`n{~:9΃%]zm8儊][?̀9r iPS,Z/EtEHO*bT@{grW"{|wҰXyU{:JZ^K}[[Hڣ >}$"*\"׭1ƞN3̔^B5BIڣV9;̗%QI&[4K?rNpmcMJ<0DEKJ,ԍ=SZi ϺwgS0%KGC?祵_:/h%I\ eN'p:*sL M/{BEdO=Oosޛj{ā7f櫕(+=V*uo ͡2 ņ6\g%cݴL.C/mu>Ч`֟7V[gKe;X21a^;2_=pRL^ebQ* |m|J6KCWw笌\`f\%]En>xЖS>>Sus'xKVZVR'ԚM]5xK~Dt. VŊH#"NZϺ9*56,C6v= O:պ~zTۏyϴ_DIZj?5g #5+1 Z,Υ;ϓ9B A( !ƨU0I;Ytn6mu aeL #͆~&z٪߽{dʥ݃po&C5ܽ oxfbه"(L(n ]mh"#GE&5&ksMsForIqھ$-t2˾^V%'g]ؤVRr9F7o'x;lK}pgįdW~jɨD`;YZRpLGIS?w8$o{GZ[e41 CG2¤g}o+՛}2됻  nc򌬃l=5$ԉ&bZ,]X4' S=>U?a6~x:>_eM٘ YofxM\:tRUOVͭcȱ|Ch{9}pVx$}ӃzVD0ΉoRZ87.;fO=kJvjcRysJT@0h8,7:NFQ>e^wO<߅GFȴgU4Q?8)jΕm e'dTFv  h0S0ƫzMBմ=wcՌj ^7a,x}6TU4زsbon=== 6>(p%ew  euc\2έ®Boͨf_[v9Щ~${r^c΍npWCG{]zS<]asv^Ԗ&,SXVGIub2$sejV۰]X3 bWqci!Rܚ,_l?pmu5P%%e>fgxr ΥVTau2,Ѵlé<3QQCpE 0puZP+9?i>q"tmRk]U}# aǥ#c{ٛX/J&ǫ|(=KuN%jr=VqTn!D:$2hʕG@Sk2 (߶8ΜR-8|!K)F]l ksgsGUD",BW4/Lg I oh.rt\^ !ar& 0 \8FM4_y+B߾pkӘ(=1KXrL['D5F%i *usQfr@X+\y^$6"ׅ=G&Vol8fŃGXz6~[44aOvfxOy8 ^[Ow? s>K~UG9F98{dj*DR]Uޅ_ IP 0V4ke xgV8_5ei,$g`T8Lrc%٪=]Ivo)d D'p*m$dΣ$6۵b{^&W*8kmH+J*a>e - ΄G ? {p_vyI˿u%eQ΅X|3G}U^"1HQ|i&74}{H9>Ru.C+,MEp;߀$F JD*7I͟ m͹[ tc,;ҥH4]>2H^JO0aNRWwWhyRj쨨I;iֲrB%jPQ i^AF| g7C զ%wQZ~8U^S&6c8Sx2&2,`>G9K2"k G2&x Ԏ1ܟ8ef4(y1mYZwY"YAgof\-楶aU._C @֣x- fQR775͆RTn HV@#8:嘨@fwI #PdX3E.JT\;>ˁ 83rE0Jgj?˝UzFQ[ gm[_. MU ] }&Omղ6fz]zoÓ&e]l쨵~(rzі dˏeRfJ=[xZ#Í{ FS2{ΕE-9!oIW ȑ\[lo/'eNj$&:͛\3&H_( endstream endobj 349 0 obj << /Length1 1395 /Length2 6090 /Length3 0 /Length 7032 /Filter /FlateDecode >> stream xڍVT컧$F42 tl1r4J  Ҋ( RJJ()W{νgl}~{TT D1PDK~~3FQrͮCx:w3@>@"-`_hPpu(7_ AbU:`@p A( G+ @S4 ++)Abhu_\! L 4sEx6E;c8@(o| kMu s^G`,a_<῍_j|8O'p8o/qo 0,@ɎWÝ wxA_Oxn9Q.HJK\7Mh NT\(*.B @!YZ#(dA9@_0|"wC4p?Ka/C7?meV(b,~# @%6;!|iB񛡂rޚ s@( ~`?&8~]RC;9q)i KR@Np߼Ph,> tFc)) _2-/o"Kp?@nu{ua (E(n sLC&";bsAiIC`_y ,1Ÿ$>dd 09vJu7JЙWpwV]|R/(+g"DR+P-W9C+ӧ2j]^ ʮ={sڗ[q[k-jۘZuH3yC(q_ *=&ŨH]xJ"kWQsDdg6®AbF8ݵlZ' "k")+Jaޟ𤠔-bRsRs3rS$wĥ9l&X?K=0eLPJ! }I]vA$o%|؎ >l2EkO>bw8̈r)dR'=m{|C[VMF |[H>IP{tϩ|ԝ&%2T٬M|$rJ;F s!-dXFu9NׂGCg]\h[-j2v}ÿ{CyL6kiW骹rtIh Y&0{6# ҷvifB5^L0wXYu\'cӠzH%%MuRFP'~fz'>t|Dn797]b4I:֙#@_㌠ԛ4B(zYs:I!l~W ]˹iYBuSqEiuԽ~v_S&&}SF$ED #ߌ6+Lb62ի^O*8ɫEJ56|2 $o*3#Ƅ8aC㳚nš yu=:'gj<ޞ<NXAtڭrHr8ž^_x9ӵ-yQ$7IT. >6 dWʞ?O}V mA=xAʧ<E7hWtuƸ5Ӭ4tHex|"\dn:EZ9~NܦΩSX>`">6E9#aIr<ŏ;4Zm {d_= Fx@NCx޵)msFi4ANvbkmۘؗi_*h%ku+alJi :Y?uf cT`39y}YW_?|W: U͸$,T ʮ?,pjUDOXt=0]P"a8":#Ȱ殪MyHF^0\?ד)~Hkr5$U>YH'i:2 ::7J_Q*y 윪G"M 1, $[܊cV5wfeF X#z=Ghls}Ix ĕC3^ԙ?KŽgj6oSچ'8wV9R1&sjk#dY) ? 8[t1s 7dc*DM]%?]rkJ:U+V& =mwЉNwEyZ)&/8m.®d4.q(zn^WY)5Jt'\ZxV{y#[E\MHiM;I }žO$䧗F~?a&}d^2^0'<휘hlN~-Ez xOHb,9}'[/Cvs^\ /jBl0V6c v,mFN:`r9 ^gC-86XNI3=k*bWEa2+=25k{L~\hbQ7zW(6>z~L )@y.qO"? cż[}6hހ`y.&2%ћ7jNJ[iӥ&iNu폥dܪQ֜!Fɯi4-Y KTi>0NcqDUϊۘȃ͸W&J_?k{]Vty2zfv !>mF9z}7w%ikU_DhEHI~a7<ӞJ@] *g$c'Ad籨k|YՔ+L6mî- {&ϗ>3-$j AfQafQ ݋Z xtHpgg~AIRvM;;-Syq߶ G눱SVmw3 uj#}Z5gm>5kWpdյUUj{@ pL׊Oc62hT ۵۷Biwt]iiOF+ uXhl$lai7%1؅Gy) ) WI|\@2EIp>d^j\Ml+8\^, 5_Ϳ5tPO! ιh37<#D}is§@O ϖEBfi FM2qpw'{ep\#pZ`G@T"0+'c3 Mɔ(g藥XvTr9 ا$Y-% f~Ga?@CT a(p).qGq%4#[ 3kE6aWكTӄ|WPr=EJp ~ZV XKAֆM=?ׯQhUJi49~RrH Ƙq٧ d8QR%=3=rU@Yֳy#IT:|Յ]تS0nv3!5nMN:[- eǠ{JsTi+.cRH"۬lFE}gZS}ĻSm)7QzsBL 0Y۬ngwX`mnjӍt`XnYWyRW+nXpIj9K 1fr.sT#ȪSJ$ (ie ǖDG69K*YYm_lF-ENCZPuj&ltW[@&uVXZ/c#"*¸|#8r {ւmߒE7;,oTw;㨊rQW^YPT?@4'AfTW>bΎxcUv\pmehTH<>ْdk#fVC =gqI[?֦ѝvcw%rm.RAF"Y;Yo $:A󰯒Sz/C)$Uٕ j7n{7zg+ؑO1EO2I2s?/Pzʷv-i)0M C)L7nZ]e2e =՗W_}C,n%(& 6Լ7-;LHCTA>gdg&%ed?xnB`w%tZ3u?%0LS=ɷ٘~7FsGQ v{ wmUh:n00& Ln_љyAPuٗBtjp:V$,e3V,mWH]6>{goϹ$5BVFw"-HT]W;-!w?x.}Yl&{}=Z K.9҉\B {>gkrQv6̛wrp鮉d`-}\\d4JJśv3ѱ?&pxK3ܚ$x o>[j^ F)pw ;~Y !;\w_%. HHF3zT)LE&Qvq-.a@Gͭdm$%:2O;__*m'?oCamޢ#gy߄1LŹ e.SP \<\s-34Zu:>cra6S +/l5zX8@ _å|6Y$"AL*yoRZv} n-Q`~mԓҗT*!4_iA.^ʜ ڰ[o j}I\TĮʾ UdBǑv/{"G 'ϧ}ynxFeb| ^k)Whu([/&\ޛ"hvp6&UpEܹ!~|l֔m3BwX0RTѷ'vY1x䷋F@LRb޺P,ұh CDF&||Cg;_ #a endstream endobj 351 0 obj << /Length1 1898 /Length2 11739 /Length3 0 /Length 12917 /Filter /FlateDecode >> stream xڍTX-; ݂C ݃w .];<{fg_UkUݽܳ=5P&PRqqPSk\Xjm3"啓4uyuS]vN;;ߎNIS7@ o:PK;x:]^w@gN`e3 ftBJ.@M {sR Y89;Yg\@g\Oa,(Mk_JA@k+x!PqBrVˁ 'ݿHljnno` A 0"0Xh v7u3M^, -0}/uN ggy=d)G} '{y{w_kK Z+PN_ߜ:֬$ti~`* x;.N@_,@.3wWh~y'l|2z- {o?/ULW[_RO1{9ylv6^6GɪU $ݿe'5.Aސ ?_vaXM@`_u"_:X hr_dA9D4h r1} P3`fgc묙۾>%ί7 :J⏙:9z^+xϾ@]^C|N('k+A^ۅUo`UjXMF|Vk* (gd9X; kOk v _+Gqf|_}^Wh7/AJ?tJq|ײpx*UߐX*=_N9ozQmj[ok޺3 OS3{/8c &Wg~\sKZޒ]${>lG kNTkyy4WiA9A?^x(VGL¬)h !Oʇ{+QW4:[ͣX>eX2Cg5KDLĀs9su=3B&ψ{Y䭿s7RILEOD {32I-"O8]Z4:K6aޣQVu?7#&bœZkM+!O@:]_;m2sfRIyk1Lz/&ڜywUSR|]p<:#8{*Qʿʒt " yī>$(=1q+Gl`?-jC2 ~*ђ5[=ؘ0)Dt[Lk[vP "fQP+Gњ]," F$w&XG7QU56_ƁޘbszW7^9uQInaKeOX>LP|>NJS[!d>oYns̏_,` 7H^E,7,n~;I*;wo^u&"=l(bBfɮI;α`Z;OMX oe=% Id<p o[Wp#7J+)[i@GlDf᎚E[EpG ŕ*eub%A4t/ KQV? N\2z.pjğ NZrD4f\SxkA5Ǵ]})2rhDP/[Ijj:j>bG5m)$~-Bពj>v/b}TjNJ su+A֔XtA> ]H2%{ǙxsAx@7OLfM:q0YK Cٍ&Ƣ7ZO.1g&\LyK~#̨|P棨V .ܹJ 1r?A] , N]`mH(=wTo ~? $?e(T?8_&RfH6 lC%d JZQ3/5tԩo݌Pt+&TH~U(9F{ > B i"@ƾZ#5fqT6RD0 hG|;Y0ի4xb_A>:(drr:4`tû?Xo/1ɆF-X@?}l.BQTQoB{-t-!!zb#mWlMO;G2;=i~+bzdyÎ`0PdlgXf [w;Pr[e^2ÒE vks6)BmF ]< '†r'Sp?_Tc눤yfg#1Saԋ#`qvܫrXgki,2DC#[s.y4#sl& S^_~fgorp{3oq_(mxw83ܴ5OmEgL'lZxn8 ["1rUźj!2Vl% R;JΌR9ȧyс&q+"Mh8^D%Konjmx]ޮjMbv oOކy،^S FuC$Wp9M_]%+iE֐l{]W%h5tHmwӉ jR{tvHtfNzĮFcV?z xs*/[@%V6(`3MԾ_t!<*%V+ߍHϣ:!+fZdaO3~$810p4 *kX*'z; WŠXI(z%/_2\4b1 3^t}jh.^$l}޲v"1f \2.C.RAiz2G8Narmn,_'hR"Ǐ .&*D' XOg1 9-]X2QyI&1#E1obvd`!/ VK7adGPQ>XtfJUz]ٍ+ߜ!@82qϪwޅ(d: YKX^S.{hzh6H O8 G1#1Wƞ ;XbT -G] 0y~ǔ 0"-ǻBuV>`5fb灒r&U`x/47Δv~l](-)+K!:-=lGztZ!46SIBަjFuI ed--כ1`5tfu"sN[\4.1 >cFk#( \MUanXowC6:[!%eM2Vr)QFRw5}Y/ ;\taHÆ[zdBu)=M"(J9h5C&ߙ34i$ 6Ho˹Wˡc)vr}t}+r6C02$p)uܨ"3wҮfK䠑}c)lY6eėha&kUXUsZWH>EܴD-{>..}70w}7S8x v/N`Y3]M\[`ߏqOMQ!6?B3?2խw1YY F[#(4#;uE6)4n&D~yr'\ c NO{.E:5 I;;J5pᲕo9eR>ѓ7xs͞W6:3|,'G8|H\#`kƤM{Z/I~$蔗*`l,6[8V +>(J-T ':CCICvSrhXSQWЁH晟W@m٨? hm' Wϥ^xttc=N2ZʧlRKD3H fd!ᙹ<88߸ȁȄ-6 O2E˷>OAm{>O0>:7;3=@KU%3*%PWĎx'3LQKV3յkl,w-AH[|l`C|[SHB}Kg7 iU>x ^~ʁKJ776âxH*3#icTւ(qYXqd,'0,ҟƘa5]Mo{J`7e\wrDA}tN^t;~ 饡X)p(eX4Ю|ۊEzVNԗӨp+^R7Al$eNMA'D! ofZG ~NU)_{ȵBK/>q׽ӔcKQ jC$"&W-F.{\ط;jnZ/[Ղ^U>ʩ TNزf F INDd. t.#)49fVʔ.!-N) 3)m?e T>ǛTYPF8p{UuUm{ @翐qp R,\UȬR|b߷+#YvG_1gfl~f|͕ߡMz8xiSN?10专#_W+pcxF(Kel +ZS|r^9}q7 .ojZ$Buʝ8?UwDxDZ3^V5RU).jQ@5p\@ #{tj蘡FXAJrPbq{Z[,223~n1'A6,ed tw˛%Il)н)7-bTw"{1WT| -@gk϶cd>i߾`L' 5mۉs}*=|o4v+(g=>De/W{Eչ=EVr{EiO;<ӸE 8dO#9h}EXG{#Wv }A=]?c,W_!ngŔ 즩G5A~|i4D wc,p=m>tงqs*(=YaG:B]sDÑ#|>'F4d9iRh^1vFUv={#UǽMRR T襾4A}dЮ?%*G5Z |%c R@57)5W|LXMh`Ch\\;ˢ o깏<5s.P"c}63X8?\RҒ.\KoYƽg:7"Ͱ|4шKa!C`ۦ/D8p\V~KoOijMH3EӒ:3@ ]m?MrpTȹTOH<_A F`dߝer~RKzԜY>.-gUo 4Ng ׹z[G[pPD&CxP,mz]8n8vڳP-j>^K1oaXHugEIÊ_ajOTlDY;u2J`MRyQ+=+i:b]&N~~|*gAf&QET^mw?4yl6TUCu5RS `\w7h~UmONz غFQj% g뜺eWWp@OB#t trbN]@6k62dNm&``{,K AYudc=WSPYFX{mYraE=tS;ao?= Q4=]Yep7sH&н+iV{f6[-\T HS@P:z`Q_$s䤁KM)(C?pt] 2/V=3R<zޥsaˀ*M"Tm_ҙ<[B3=D{G ۙjYOԡ6H)/>ps E$ M|1 ذ<-/֦?D"qiA\Uzm %>`cᨢ#$`dD:YGU kAwcP,3/l-*Wgl`%ugT =x$z|lָJuyeq=_5LrDXx]iߴKe:b/ ~#%İ/Ӕ~xjP4fMmtLQ,}l< {HuT:dz!ȉ ն2DCVbɼM f _xQ8Px7Pj<7s@Ib1wFIw{U)=bՙS5 =҆]1{T!V~%?a6y ߷tE02[I={eίkf!^Yv:Tj s'QMRN? OͪgJ9!+RcgETpŰ/krXE$g™&ݺ3[#IiKd[WkC YԈSFOzݛװ^og>꼴 Qe oI-йL`KPn/=oܘ L#7~0@ H؂1gx-Nk,(.%$Ci"' z~-҇Xۭ~ELYn~bhg7KN7Z4Ic ;/ϊgVn rud+]9 ݡZm_L%M?NY%`Ѫz5sBƑXh%CΘ=|iȷhYɤ39oVf2U"?iQ=aS8Mbݸ}O ǀ '3DAk).8*?Ӷ]I<3;in%6~)by7x'F_` /ڑVdZIjCOoF0`Hdu@c?9@Aѽ*ڍW',v5`wZFVE=X2+e hU맟+Eьnv<ل,% *&"^-{ GAou_"F!t445~\GӮ(~&|AϜL1:a;دƘy`ETkp^ yEEk`=c@--֘Ł0.#G{98!>9f'VJNw SՖn@NA- Sz+VSNS<%`&Q_ZT+cIX'߸q|8oSjVH]WQCt:.b l @aJ՞~뀩 s3OHU,QF&Ihc$hE`*M)MdomwpnT{Kdf sT1UOǗ>8?{(cMsCL8=F"6kgtݸe7VQ3 ?˯t R\pކbPit1 0 ly=X]>*+| u.Xn/\iYoe &_|=okNConĆVG5|B 6<;`Pg]9\%ާ-X;^;SQf gM ի W" XP7DTZO9g y>zPq{曈K|)њߞlb&jcjqq4.Jjaw.e T.Д0Kjґ$CRQύ s?ة_R"y?}:$w%2>:˂cRF2JG0j%ťcNw~:‘Dx'~簞,gsp< S<DG)4$ s(Ml6/ ֆM/9HDTk :;$x ŦJWJV":F^r B/W5KWґ_ǖS D vChǰ5l3,%Ia;7fGI4k]`^ZdޯA[?u+#q41yQ|qnY_m\5= ?^ib3UTxFBYgZn^$kuxDѺe .hT{g% 2,}>\q xǻE, ;P0p'I%QAVzYkq'%| ܯ2Hum"oڜNx[V⇸F4hV" 4H)C V,fQ&y벲:v!)@=iZٝE ֐3Uwn*( /Jl%> stream xڍx4߶VU[M{AMZU{(iQ{׮jnROڻ'$g|v=C>y{DG   P"`g7`qv?_ )PhEm  ?B@ߊ%@!HvE #  Ap6qEG`C AC'큀7?ɏp$ bErO0r""P  `6C<Cu-_ _ 0B( UG@p_ y0Zw >fuC!P/ܠˬ WDB($0Gx>9@h{ áu?:h?#J  IW#_7oo! p@ӀB $ @yxB'AA=ApxG{@}@ ^.{AAK;4@KшP=S,KM`-rXƲ_1]ߚ8 Y﨣rGHg-BtV:{fw"hl UgCU°lj&Nmҍ4\ s!qͅDA!Ti bz0&aū :v =u,U{V1'O?7k+O.)4(}lGܷ<3ڨGΩu#2Nsn_aQ >` /U4ċ(~짊6i6yt84~'8q\s]A @N9DRxv0?6aI7 r"D},ɡ`&H]k)R謇#EK ƀ(TE⇗Ofb|5wLBqEVJN" gF mj.Cl hE]]03d)g6OcHYπ1P[ia8 fRɵZj<7Fxa(.Z grA:dk3dN6ڬphG5rUŧ؉^&89q9aMFfBl\ZrV`&8;g-ͨ+]?+fI`?!.r|C,eȂw~86yj=yrlE&KhD6sw $YsgmN0W+&jWC Y?^yZcFȋg)H-x2^e׌>v]B'Uz %ߞ.<ԏ#(0+ckopzҐ˝ =wn l7ZH-\-.皞4{f eX:XHb/ƌ +N(cMQǽݿ;'ꌑz5J XK`UU,x7/ת^0_nހjf+10PMzSJe<}"fڀbNԚCZ? ~SZ=n1|_*5=2l:Xċ}J;$ՎuԌ][` Ǵ EԽ@;0]#i.J㡬&zCـ?C*zLd~'}E27ξFYBzM\WiKFhO߶dݒݺi$Ћzܒؗqȳ.1>4\eyFgNzW0%CGX! z|piq; ߞqlxH,tȯ4+rX*~,QĢ|cO&Nf7I£yEn|̄%b/Jkȇ9YR.G^{߮!>/R9pQ|O|0 Iz„z4+O剝fxQXba[}͏gwO;|UnËv40ޡ2J yr3p{9?7NoD ,?"pS\kzǗ2f,\S!_`Ħ7S&j/r$޻ܝI"[ǓST_ҩ+ꍂ !mrW侫}A`Xuc }?Qc8y Yh x2W~4I_M~PΛ&~0/aI7o:UWogcu#h.(K\uVଡ଼xzrh\tdx4%ݧйl{9a07p9] ;vC]既\)E=i)TH;O}U5E˴7nWg8?m kf/n! 'rh:W\hz6OFW_>yUo4FkXDoE۔yDBXzҪrt"2WKپv e;C%?Us5F;V&k,Z/TJߧ)-ɺN7S-Hv+D-j9>֩hVى V;2[r8{rV,8v6$w1Ȳ4lMGšt "ݩ;Z< \2֞^V}l)%sҌfkRK(VեdX w{XgVÒ'b~޹N{񕮊YSxlMԎ꣗ɇq6\^ܲ<=d KGH0|tMqlFX@ 15Pft8uhbRz7+~BjueJL#`p(QpY-o%YQc嫷W1Ь׳eۛCɸFyWk8#l1xS?oKWh{mp9E?1y;HJ2E݉ C{[x?9C{_Q d&9zbz0={N(SI31saw6 \lQe `tޯ`RDvr4q Gu$lI7HWpP8&yj֬'3wV¿8ֵ9G NWֹ8"{:/ڧ8ig?n_2Y2{rUd4z$kǯXw9&+Ď-ĝJ)X% Q#\ obk\Q'޹ɳŁ}Ov64OJ_hS+;503U]ti*KN͎ɋѴ~^l=CwyxvMF2]BvD/Dy/Kfɞcm!CZ`Y#t⢷TPqӒ\QZRuz0K70hA5㔇gNNU=wZ|=W-~X) _PEl}#rSɝGb4O0\/O:>"'Ӡl>.୳S ?f]Vưh. GO ZoN~wt) qPrc9ܟQ Yuז|-)ÿiT]b"wu+%nŇ!l0M*j$6Qdx(f|Ҵ6c dш)[l>B̋UFYPf j1 pߘ#O{DjG6oY_nwFXê|>qyQ$Tdťtx(mPl9mx7VP&kyaJ5^SU B .7/ )=ew-G"c/{nXbtbnfsz P, =zu L&n|sAiwϻϻxQ}x km׆׭Yak2}g,nw Dyh6uC8޾Ǹ E ϷzC?  \,/-g$P$^.T#KsQ4|mFwH ]^{OgC}~N{b"4E&}AM0T`xAhIfP0j]2+Iiˋ0o"<~?ZM' s{;һ)4ugvˋ‡WTbReFb2W@H:DenSڂc# ^>ǭƎBvo8;ٓ_mu' .]sQ+DޱQ˖A~y%ӝ!N3͇oX/AT1XU5w@ܔa&~'/9r?: RgV,E?z}A}o;kͽoCh!s|~A>~j' u`+mS͈YA"%< 2r,$4LXnN#&pHN /]{N:B׈bKofuҕZS%=43˸%Sa2$IP7>V^]DLZeE(jٰрXx!v? {gV|Jawcvk7?0}]!bXlI;OG`N,㺁Ke>Iɰ"L9J4ZaںE@;&tf^Jg'|(Ss`p:8/ {A[7d2َ, lK߫gfqzu0?/0Wؐw v㒽߾Uo|vrdV[1VCmb /<$; ?] qג.ټPs>D7v="+!iJ4rm-{s.V ?J1d }\C ~ii!em$}q;eDD yeU>T!늹Tjߝj]slW.<-GaT<%ciZOds2]1=tM jzwich2, z{gFX_(#ꁗ;Ǹ9?IU endstream endobj 355 0 obj << /Length1 1857 /Length2 13026 /Length3 0 /Length 14182 /Filter /FlateDecode >> stream xڍPq иKpw` 4@@p'hpw'C!H\߫j1瘶Z]M$edHh+pr8898Ph 7I_ )79&m}!8;N>!N~!nBis@K9xml/u `dp p-͝*P[KEKs%L"PnawvcfxM12@hhm[0/5 x18-ANw'+:@KAr89? 6tvt1w; 2;`doav0x!ٺ9@VB`2A,.P;nj?Ҽ# A?iܽ_51]:N`Wwߜ?6!%@,mr2 e b@A~>v/BX- /f_F/ _&/f+)ʩI{:%%|ظyl\NN.ˇ g?࿻WF'kg_C{3 3+:3srXp!v,;uwpovm8(R@ Zdvw^F$l^/;" ~RC-mڥ\K Hy?Yڿ-+zQ֕qtC\|s77s/ xAZ 3%2 폋%0@(_ *^*z/|3Y@?Nh/ @*6<!ː^.//?_/_:shT/j: tOcJ}_Iȿ oK@^Kw:?> ;%✳p]mpM'8KmR=@)  #&{}"{uH!6Dk-VI<ѻ#;׮Rg iifbV_;r" ͕+lOcGG"pcKdWJHLt(Aߌ'PܦpX*eob(]f}#S2LoȐ2^`j g*lO˟oG+h4nKmҜmJ|ygTS$on^Ҟ29|QYTT٩_p|kM!Iө#Z>S#An1`Vv- hdy^lytzj쏿_SM sSFm!xL `]@H[9s esxd"\0?yRIL \zeb$9N.'9=!L/9Rf:#zDk%kț$:kK<.u4tjբrӨ.jĉpsպϫmb+/YsvLEٚ|zl̿S4FQ934JTyxKw_JS ƪu,g,}rUR;7 )7ӐLfm+* D~8)-=V lͫ^"Aͭɽ})kh,E߫V|85[ xzk C8Z!8C+ U5G72u5,/{92w *A~WzK/(yܲGobcSm\|d\},1_Opc "uW ZgfoW-3I5A8梺8?(BłkzvURـϐhx`ۢÎK=iynǫN%OE֞@P}e9US;wOϬ<9.C\I1vd] -l&/nv:`UɤIecIAKQTr@z/ =?0V O[i,X"/:XɎ.z $I@'ANk!4eJW|7_ѻY>{6a@ͤVӲs ц[遐qu-D7v/`ͬ[-e~ywi3g7&_4e./,{*,tz/*HT{IfTrTr3\xh*}l|$5ޣz U(b^NJHeLj@"pҮ vMLT'jq)u&$ #n[T`1"v;G!,ڿ _=&7UE >x*SW 6)*1HR̳@QMna;+1j#VpZ$Uf%,j'ɽ8ª[=R _+ W.WE,-%^V=}\ո:z\sr ;P׶"t%om.-OM맽jsjV/GnW\};t(bH0HP1ɲo|g*r{#vw!w[#TxiP;92{g(<4dM7T4(1r*3vB\}1(]] Y\}7"X:rR7n.gpBy] VȎ!cI&`O~sp\ՃQR,YIfLeR7At=]3s`'!6 !mH%Rpe"&jR s)n8ryxT}b`">4}q:6,(ܒ6p˄e ȱc\|k}``_c["Qm D L8s8R\/93gr~4ּvY܊BϡDq:ȾBO;7U#: := Z@:'XWK@%\e/ַ="NƒTY7RiOW;=fSxBIg6i&:î9-= xW\g-v~eoչ|W~ U߼q-|{⇨.a4EjGL\E4EQiC)KͻoBϏ͜38Mp҅~#fas-r5W]Z޼:-+g%3F._f~3􁗁ׯ\&C N !.㓮qVf獷IoB ~z~oF ]Fi3rptJeo0◅+ׄ\keָ18f4hN{(,z΍?nRb%%b;p23^/ԇyKG|>젮 `wlg(C_1 Ne<20;r3y:)f0.T}xiYPF* 4 ڸiQ9;Ķ]/}*m}C._1%+cb9h+;o e6[3:Q9ط&:00OYtKqN .?=P>Xo*ώhV/~8&7jL<#E9כ*MSb "۪g0|3z x4!se&qi!6J՟ŨWN` 9{Qǥ3` 1[g-AuVuWӻ#w7]f %Ҽo,~|[!H{>0CT\vk:}MI=HCC\ c2Y?q#_g34[\_&pgjt@~+-W}үڐ{ԹHݮUK۬NcrIK}~^*LOozۍy8dv"S.?+<0anw:=9wjй FΏۛwb).&MFnM$tlH ܆Nl 'PF2[ |ujؗ-'?Uh9Q,uG9bOz %$OUWVV2"4λ}d2 ZD]Xf3eF*)I "^yz**RߕaOOίVEoY uA~ebVղUr7+ ,q/ #w:ʱp5~Ya-,|}@T7=ֳ&8irPm P޼j45R^VUK\#K ND͖Q UU5$)V sTPW'~i} Ӯ뫣24[I:S%wQb⁵ j!<k?~_=ELKG oy#NE8a(4CUh?9@T`pk3@%V5 24Ey-*S%.NA]a7?Nf^XBMG)yGhGqaT4jY[AJKό,}!\.tx*(J-ZK5t8#ӷTCt f;W K~PòoIUPlmLuI: nL" 2KLT2T%Ov-碟5}Vl۝}ЉV[i)Ad]Ina"k@6:, ٓ(4=M&i\̟xҔ%j%G*ƕgAv4.d,B}Z aW?09 d!dMD@$ O%; ?طZ!?\Q&I &Sooa瑲WD6{ o钯 ,)˾Q%$jK,oܓy_%}p(33a^ nikGiq> nd[=H*߅SݥRH=$Ҏe(4a7pgᨿ}Wk !2uu~z~{mӀ f\X0Q8JON̴Jc!Ю]m8!*ZS eټQ蓚ݷPDy1N֥nl253/qlʽZ2A+ C k@ŭ^ iɂ#1TIQ%Co7l܄dʱ!O)pYh! ,}bT:%$^r;)䠘@EN3xx( ֵӔCGFzpOx,G!sR z@aL͹5r~ ݌[֎7MQHFCyZ0b4hP{; 0~t0=">淄L~!bFuv'Q0ߗjJ~apĶߟ[8qjLTwU,ٰ#>t}L˓֥T{x@'"Ɨi$^}7EW0txa "7)͏66Ѥ`,n0Zk ՕH ȝ>zi|d\@] >jmA766>fmcxL&T[|좭^wAa͢y=yȲb|QdqM+-]%{ [|O6sY?$tL3jT}@t m:ƿi4;PLBG(Pmjտ(8Jc5Jܛ+z4.<7Vw Y)ڢt^$Uാ gCsoֳ#; ּA/&ڏ3^beAhE;H-R\ydC5']erΛOW”EwP(yu`_{pz3z,AsaƤ &뒠\ 1;\43;I˹'R^z8-pX&jaһ͛%[,Hro2?2,OFWU=vp189ɬa3yjJ(u#͛$0wg@蛻X*k?d@'3M}?tG*ɔDJf=Y/FhI-[%w3/G]'jub†Xg[E.GN>C lΟz89娮1~ձiDOy/ԳTl6>1E2*A ͅSDZȏ,OpMDks^ fk5Ĉ&3[;MT-;^f{MM QW-F뙫6v`]8'ف* (?zT&(S'&.E7# K9%xr)@No \v##c N ei뛓aJKt-'*cǩX̓{9l:A-Si~lA [5^<δ'Uk@(,W#ymJ0ձ3N&WdcK)_4S=[;30JSC99}wp?(mk(*Ǵ8${f@Iu2[q2c2((BdI7iB ;L7 xѱ6OC.$_QޕԊ= Iz"pگqf)Zeש|E{ U|e3Z~t֮Ƴ܎$Vs&<e jb %9~h{AW2-ґBY.ԧЍa?+>kd]^K3`edLCeiw3΍y&/m[3Kg[ Lxf:Bkͯu"f}~۩n?2^)4P e }y#+x৯_CGQܮob n_Y難&GfD%ɓej9v6eDE{F2JA4a(g \۱O览+cT/Xk"egNjH<9%IJ2G 1NA 2?9|m"#~_~/P~,@&mC[BTˎmgMqˮ}xJq:C&ri;ȸ˛V$9#h˛kH<,,:t(X3zAe;DHcܶ+3"kǘ *Q\[.7Pkʦ{`Q`ĭ>;ofC9 u0k ~vA BLTj=F.|xUΓ2ׯ~ Ifla~z>1|]8x"xb |ISΝ!|Y]Ib-p6pZf~=]Ew7͏CY;>?Ta`ObBqtYc ̮Wu7fHgfXz~=)"'N`\{+[r χVM f"lBOJ-#FTߜT~bG0 g`WHc]DS"e%IXcʟ aoٖEo16""Wx? /<|}VfΏ~0IN7GZhyg#̮d-~˧8;c9qdOx5BoR<;DzG_U z|BJūY=[d3~-CebY?8y)'ýzf i(6_q 'qOY,1t~++,OU!׬ M[R^s]coINh%+=꫻~ Sr|a0uW/mn>L.^u}]#C {m8g)Z:8jt/{`V,nq}YT+%|U*;6:$AH>9cY,aC} EIa$]? +5v2^TXeToSwڱل{ z^=FE\h-18NJW*j c K2%PT>{z35+Ĭp~EZZ~P ~NT Ҷ|Rt)G唟wKqX;#(4:cN!.tPImb_3T< 0jnTjlZ}w9Mx' qE9+dtC[;'邑Y%v27 lZp G,`T'o)Z|Z_dA> #3"<_Gw% 3#ӲqINYbPP>Y{ummzii\wU`13 ܈0np&إ%-ٟ1I BjLd;@#V5xiѱ. X.XP D?/HG!SŸ&+p)7b5)ug]wUV]H#T$WqŘҡ(ȎJd?zq>BT]8 ^჈i;uoM#Z&.oNz)̵OQADQEYakτGq/A;&Ǟ)XF2+sk1ȯ?#H&#·GoT|F/M1tvVѦMbFJExsɟ60+8h1s&!mL,ꔕc*0@̒xxb50j9ϽU>x"1/L4*RYϏҮVޣ_Iq&x4# 8{hMiԂdjţR~amᩣ"{ΏQ3FzJvk칬!J,c` m?hm ݊Yi1d_HKnjP67% IYun]1jGɜz:lCҾN̟q̎P^\w;1rR3gW7F2WrYqͮ) Z®$ǸE|fq~+r*=qљP;urŨ%oOa0UC2~ 2Ntk]+Uq0!1$pJ˵'5bW8naz }@ݹ0(]RӧZ)poϯTG'kGkA[ j exs#_>?lb}nx(',RH7fp+'sPaE® 9)M`y _|up\ŊUr52{%A;7ÎX,UlRnYY'gJ4lMY8Z/uq8)L0R2s LLqZfz4tvv3_jҡoO2 _tZ̽gn?S7s DEJw l̸D4 a(2Ur|} 5]*~mEZps#oV|Hd;ĕ<.@i;s?:&"0;}k"I;XecGd{/l;vξDp!Z*NyRLnkS Sp:>Lq̷8\DbWs"?M鎭:1)msx1ߔűUٺ:rbIC(9~ę-GD1gux>..#yWQGn7bceS.$7cp9|șw˚C|'*ښ endstream endobj 357 0 obj << /Length1 2443 /Length2 16882 /Length3 0 /Length 18308 /Filter /FlateDecode >> stream xڌP w>@pw6;[Kp oιszYݽ{=( mmx"rL&&V&&xrrUs'+@Gs[X8 @2Q' @ `aab01qց jbn cHElM͜@<20sss} d t0728AFV[#s3sradtuue0vdu0LMp5w2(.@c_) Njf/XmAGm;@EJ` .}/G66023q71[ NnNt mA \ ̭ AnR2w~FvN VPlEl6N'j4ݝ͵u2116+ cg;F5s{gm@"?2S݌"PudK `Jmnt4pޞT/gf9 6@aP:Lc0O 3rcwE$h¶nOz6= ++_/?-U40wl'ecb W ' տ i &v&#/+;[Yivvm-h?l_,46wZ)'І٘Zn@cEs'#'?my2*:u虙rF;ԬU@F/_0pp0pg ;;@G`c:e 0uFDBF?4iQ0q2 f`XR]A v?.@OAlF?Į4 7AY Ach*%8W=n_rfdkr%_h_ *?,@Q4/d ڸ?LLl _O & * ? TD]~ nmlA?ԠAg@omv ζ5̠"̠|$:6?@.NF'3?q30 sG#[ZwY@%p tzϝc*B݀FKFA5AmUB3)KOȰIԕwBI=bTįu- J^/_㕧w[&j U^-!;s읹`y2k"e#/|5quTЧ7 w@k, +uu|+s|Z31c(&j)Cpg^ڳbnmM}T6/)#/|8 \%w!ܗnjaeq˽71.gWWzw)vCFb Sm2j2A Vq%hd5O#f3C |5"o1#$ٵkrCuF'(rzv`|44i ) ~0UN) SQ hzG--#MXҭ"IӞ^tq נՉ9K@m%&h V~_6`:<*I\fw0;<#8Cf4U9da,ēIˋCt?0WkrPYp,u՝*ƪNڠ'Q㏋Wk>R̺Na8q)|NAlUNUgEޘ(Z,^oY{Zb Υm?[V℥ 6s#T/9vJlLFsl:'HZӲv3Yt~ |_U!`vdFDx _?@118=4 粘:Km|8C>u80,=}͗!̂\={ c|:lTl> OmuJ>9Il2WA~8̸?kg&e^ce[j,^^Y9D$_JFtV`P B* =:P7~D\zoc!IsZ~P(AtEbΗKZOѦꨒg(aSd(Qdf(?gem(xFJ(# r|֌[9f7K酾Ͼz 0()r[P///J㮆)mЕd+qͱDW"Xha0=@kkA^1lnLjTd8jZ}m5e]:2?Ts]ixZtY*<> gG~qn.[b/ln_ىvcsZ^"n ) G K17m$alj!:nMg!!`mO}hڿott1FjmQdN{ M9m GqQЯ2Ћ~-R=/3wdˁk, YđC]Z Pk垘-^z} qTzlCU>ܒU *})@S(I6yh(CE9_כoMŸB,5}[ d_K_ yA5~ N>?nV v&fŷE.${QS̄!cD| $ wƌIOAK`ry\y1ڨca1y8=+e~ޫScoX潈x}n~wluV~+ɞtg^ok1s=V%+ÄxLKyHDPU98Ֆ '_XR䃩 աW)E߯rC4?g?݅v6|>T;1:m-l iGJ`J/=P[X" CƉ.B5󶾎TAZ1l5ѫHC8o/PQOxqC'.@g $aXyCS:+(hnW#JRt;e oE!j\~s ;dCj p^xd"y+nr54n>-sm׉ OK+SsЫҜdDOkSտRA"<(U"0S%5!"'iR|S#1C߀}tOS~;q:3 MA]wJ\B"0K2|yiCD2y}ggl߲znt˞zƍtҷ8l+Cos~9I&N.xu?a75 : yt5}?{?D"?<9;N0jhM+|`]iX" gK #u =(;xv' 4JR?}(`w^&Rz*O *lĶi˜|Gi{x[$ѩG[mЌ2&}w0|fؗ`Ξ/s6!GoW9e5W x{u@)b9Hꒇ%җZꗣ\u']ݯ0dҲruC醅cJ&FS3u|V>Z2*ay~_a$7oWZ", ֜Lw|_s#P)٬w+;*H7sO2zAy;tC2m#~B^|Z麯S,ަm>6ϟ1T43ϔ[P0-5SN\i.(1@,d˟B,6uEU>-yq.cü TKEm#GӘӂ_mOdJ0$Yg~n躵sfdz!a6\OWug90ulʇy5SGHŶ!1X^TAGSpÂ{ ?F5"C p+Wqxqa]hH`R,奾@ GͭIxܑ%2ꦫ4U~*)  %| fndR^h f8Is' T ~'UOtHJ3d奼-+Gᛤ:}fBr~3C' gLkUJIdQA]aG8%f릌nUӧ3j,W"gJdvZR2:at3B)ʊcİ| yqHaN aҬbT=i0" / @3%&ᾟ^%jU#-~M4lƶ0'hQܝRɎY qԒ5Qm4W4xw,]ь"|KK[+=͐*WI詪muv^<WuX#?nT8#.ygݝ$~TMQS`aUsU R&²K'!LgM Zg.4 :%2P #{=o.Tėy((',([ɟ,bX cB;/H5ku/?$MKJ)8y[%#1i35Y BӄS?Z9($Ez9jЯ%yB~SJT)lz M }hJ)`IMst l%rV5y-mf$ ?vh [ [0w@ў+WC7&'!i"E /{皍^NW E;شRXm~z0l-Vhp Xlx"PNYivEC9 7 )+"r#"CcH/gY=>OU첉Ф?a%J1eKsQJBU&MchZiʱV5lC'S !]Ykٮae2 Y?]yUӡh_O,B*~%UQsȅҊDGA6͔n&aNj!Z#WO ϋI 7?iیDƨMm N߳V˞+)!y,JX LX󰊈*b pwΆ|8$7pG-gY1m%[]gl`ãONwqG 3F/;||.'Mc7|O`߇~q9XOaԐ:pd>Wc?׶ n}5jj G(d3LwUO${s|^NidjHY݁uPgαM|}MUU |Ƃ4Lå]n%9n.D"$uMB'$"X˭B cZq^9?hr΁O7 yiݺ})+ wś"'ii30|*\'螙sZelXhӝ^C8׭)"e5Ɣfe9kMCHºMab4[(U8r#w<x)%$Pn}|:Rc5Tǖald9^nFi15m'e Z 8o={>B>1C]ca3LߎOWh>J⸍9Y mU $tG\Qhm&^c5[1`_*%eitL%iNH(ŒysE5 dPS;+T!zLysU/*gԥqTNJ%JX0#Mn-e~GT3q&:Xa*e֕4O#\xU):/\CDQp_+)}a+Js?$=kT=ၥe [kyZRX+ [;̔_aX]yd]:xUCχAy:WivŤ #<MBi'I9fo,Eo3V&' 'Jh8"I{(_Iy3kw%򇯅 4"]7.gUe'x~vJ+BM)ya:}TmK;+zM??lkt.g=UO \Wֹ^p߱ߞR}?w4p OHBj4kOhJoϖnHiFVg*tKtLSP&S\`vIJ=bmUT7rxuJ%ӟt7S ]ۘX(l,Ϡl&+\2{yX  =-Nh$dir}zx6?zԵv>l}EL5d:&Zb>/eiI!`)=3U=|ў37ٿ..HF%l$֪EW !,K1RX<|-D>˚tƗnUCtGԦCU %m(ΑCOr\-VG0]5\(ĹZݡ9%Zw+D"JLV'?ɤ:s"rUv}S~F"vEX nl56hU9_CrfDԇuY2&WT|u=w[8W%m.g JV!h&T)6hF f}H M،?4틼KDOXEDپ%jAiNZ_eP(i>O{L5.*f łޫL\ӱ,Kވ+-Sq= {`4K®nA)$h[qUR+,'pU+(-mˎ\U9ֳX.1QjV5r-55 Z; o򬊭~!0XMIPD976}枻.1%ccd<6.#LY]1ߟ/J[ޅ^>vSw^H@2GWٳXbv{47D\^0m1tӋ ܾ v|烴:N ,ÒAP-"V,?`\GOLf0POܲND1'm/9ʖY~f?}f#iUGNm[ɷ*( 6+>:Ր NԙD }t ܣ;0@td+"Ѐ&Tab?8%MLWT.]mMϷEVd4xTZ.x[EȾ  *xWlASc8x 4x8\ڢ.y述T#[®quOD(x\ɡ9jT- T,_bai`AB=ҫ + 5ۿ87tQ\ֳ uK-9lkМ-ed.w~b{j; 2s~YqS] ڞu9AV6gG%sW!W/FK 9Kzť˙E*R /n,>\)ibQ\ND3X[%NFUKܐ+ģzV>$dJ|_q_ S{Zfy)pM*UիazY H a[I2B2 56$R3fNg<؜4W~6ddsdGuy,Am5 ROFcފ?) C~kT+^]y/s,*Dqf֕YTޕ ͆7q^DP&Smԇ"&L4VLfZx%(v+jD/x:enbETzqbbTvaP{)pHu&hG7Q}B͈a,`o2{K}F~C_iG37EsR*ւĝZV1ʵwjW6NHFgIVBBAϽ5W8lgNXy~}}M 7BU-fFxQ|FlhvQ 4箏@8(Q/'uJXd89W4PKڨ3g5* ft,G9fshҤi *̾,wt#*v>t'|o[՝pCy̙6j{t"WQ!ыW(7B5kZ䠆5GyV ϭv.Bc<:u@rGʴY;ħ<2B],("r׆4UPdc Wt}[WcϦfYIw%óݑϸڄ`Bk7FnqXn6>vwtBp/)h,`rLe!{0w9mwژK\u,e5wc/HK)8W炶 ;G9rֈz76z뵉UU޴ d]b'|+ʢjLǑ)H i?Ea)5SjqC~tظc.R\z,j}4Ψmkl&ͮ)nۄ;YwA6]7(Bڲ(: BE U}97 '_2  ϦN:BL89P,PCzk\/4;u/ڰb ܎˭KOsb qR3GEz}ŨI}>cp`6 6@ Y81vlT2\2k%:ڿwo<ސ9ۏ!X_hh8"ȯHE=gsVcY9Q:kxil9A *tMۺKvzT?+wbMVr0T~غ)/G|z@ixOJ/ZUy]x[1N{=D\MYIF'ډnyEj/jR12Bs!ǁ2fD "C00X1"W`&9+C3{ ib߭=*8#! w6(%g(8p6/%YMYjQ3+ U  K{=7g]5>2E^=-o1;li[OQpJղz ugZKDFό;~ثǶ?OOJ'%lz}Eđ{ͮxrRLx-/of/n.?'G|`bڋ#/֓_`[ "8o3S/x\Wp!.ٺ>4r׳D%m5S Nl2Ǻ>(~ip5ԣ$_؆:)B⊷N' a>iOy`{d0lyBʱSQ!7'i}m8r]:SMtRe;ԣ zyO{MYiUx$z[zUX>]Ο+,s&)Bn =K2"?}*Y;ހczgzA;9HIHk7h=h(KX(`m-s~X.4;K}كv $ٞ/y8+#R Ea-!r9g㢧aȔ幾paD{3.qI[$<>sFۺ o 3yV`Jd:; sNBаwd {h[mh  kd4m8 ,8k*xd'4JO .'ELJ;3bV,$ \8|ߎyW)r{,E5paU['^!n8TEh;6퇅yk׸aK̐K͡XR׍q"+Y܆ՃtՃeβ.RGew5=\Fc̑,uRd\`lJbg͙i +U7wݺ3EbU]J0v| "Q!)ˋ/5$Lxƙhb1fs4aU ^/$Ɲ:^=p .ߤkEKTEXk+Xaj5V(";CћѴJ'+ Wc(Bݐa@oa=[)h~ۨHJ~U:/ Iy.jlXLr`(;ȝ גZC4qވ$BLY*밤&kp4q')fh]hvbXرGj30j` 0]jqO?/],jgO˾iQξ-~y*53zW"*·5 HOJq7u eŕeWd}/՞n$wodCԯ5%e)_~A;E-R|]<]^eJsVYOrˍmSY$ &C1;hNNLk'5<8>钃m>8&!rBEJpfδonՒ&/Aiἷܫ-* g"t}5.4i@ fË9\o"D$JINgmOVrC43B~(ދf?q̓<"ot6܋tEifܰ6eH80y#óS>m%u3zQnEiϰ4(arv^ţ1R.Rs'OWD2z?ʳQ;I+ς1ka*($ J.)2M^} s8NaTB^nڛ| Mqȃ83bYn 36V+<6^26x=0CKy"&Vw rGC'袙Tlj!L}xs{fUws%J ; U%4n7+]"6#/i]C%&73ψP5ctӵUi6CԜ;rI>&,S'؏4TmP%X*':o˾͘w+7bPN;Vzu&)rHn(&V+F3?/Hf-tyVE6jcMWú1b~;Ϸ`v痷MVNAhБsN#^Onآt,a~{o[i['`JTQT2OpI"b/(XhtYԪX[YM>$L4)%+))O+?A6<͛ ]u2$PwZsCG~q4V|(-]4B vIDk ++÷LvړC ԓZ`k`[7}JYcr/~W䛀2BqWk? GFQf5}[/샳5cv7pJ^QEI[@B`_ё*j4$;<.Du&Z/ `<"tt0@^HwF*C3v3W,^$s¢t~uB= ֽU\*KEof%P~vݦf/'|lvr]e|v9$+5.h&S^"گL]*o`>ˊmz:\e%81&RnOAp[g-l ;n@to /bUE#_6Yf tٞ6.##K^AUHd ]MށN AE}Bp"e )n,3Y(LLӤ%3-߃"{^5WO fZ9h?c! *4BI |C&?(jgWrrMc&Lv#9ԢTo(vz<@2ҋGs@t3wHiN, ,>Q9)3]ǟ6|OAkC;YW ʹ ; M:4|3fcgҬ K> u>eWn_(p64zHCz`ECłs˗|Otyc{'9NEE7n,}np>Vlfq4'+̠i{+SLELH"C'fRAuja<8ao2‘-K`XNө˻~s?aYjk'[f|2~m>ՙ6FYW8mp-c6^ӿ9:AZpQDr)'*n$qrK)|P?yLV]ѣ9Mƪv,Ʀ=$DBg!I҃QYq}u8h}ʎFƑJkY>a, O^vE!k9,bQ+nNs+.2 >e-PqTJ96>S]Fn]?jɴ1wu 0Y&1'T]8% l4EEKei[4Ȅ 0ԪY~_ J \F0A/H-,û I9ޠfa uߠ9otQoOuz. ƫ2EN}P2ST TN H_y$9e!s5}k .ŭʬ?;ءgfRJjZjwMB^vr<սOکQL<{6FlILG.dJKx9@\it}Z·@5* _rs^ѶߑmHRb,_a6]W_!i<;m'3Y"Vt@<74+OHu 9|+"(`W=È(ovjpQmig`5; 6Z#ԣ !qW^\uDV'= i+x;]yL|**㵸CaX+/F7|BXhk?Z;VQ߹Q~ dLj)fx7k*C+@8>OU `BWZzP>m-aN5 3a{6gF0=tfT҅),~'>N7XI.ε))Wy;Qnrsa3L h+`$k䁞k;+amzs鰯׋L>m1|zVxpz=FkwcwRRs68w)wZ^2nӴv; qk.\XɡRlp$ M(D"mD>cÚΔ30](1T>ۧ֟(>i]Lgp[OFN{ov:9Ñ< )>bmޏ9?c힃T$t+/l[ǷkBa6bQP(Umi#~, ȅp,ozy,l[X8YؐݬN)u{S\H`7BvMS.1-b2{W9 JDҶx*~٪JqǺ@qzR𗠣&j1 pR|)rD8h^=~>D\@a?za,QQ endstream endobj 359 0 obj << /Length1 2791 /Length2 13286 /Length3 0 /Length 14864 /Filter /FlateDecode >> stream xڍPX  ww n . !K Nv]W[T<}i9gPdt289Y8XZZvv.VvvN$-Frr@hȤ;e'G= +'dg @d Pf(89ݐh$]A6`H}[08:w,f`$=@{ ,jj- m@70@73V$ oA@G7 wGK+)Pu:m38X9uߎ@6prp6s9Z@@+ 0smhf9oa73@F\`!=7 W3؍ d"o7*K;ZJ:98nH- efvN+olڎ w?&ҳ𰳳s. oZo|V7 + vuS_Y@k#ҳwh740`Ͽ ehlWTU5tՙfNB `pxo_T Oj3,< *NIߐu67!w{f { ,d5.EVZVlYqGkry-@` Mx9՜@o ;ArAZ YFvpu<3WW3o$vpq|9 i klN`+F6g`|F6g$`dMqMA+>#Htg U$꿈]A?#HtglZ3 >#H.z"Htg4qA,!黽%_97o1d@nv!.̟^f@{1?w_' p+? @2pL쿉s88<=lό -\d2lwq\G 쭞@ҵyg $JB.L6ogRy!R;g1xBAb9;_9=g )3g5$3wOw9\  y0Krz 7|bsyv)hi\ . 1ώy ܀o}8qI%R4|&K 7wSK!a kҍQ עdWT[7Uo'RnƤ2Tl^@ݲf,V5?npTO{ &vy?ݹլB~2 t=VvTbaR7٤(:ĥh}CxW'|ଘfo6Y@ {ySZ1P4@1Nͣ)5t=lرy8m >t;EQNI{A̲21G31cVFWݎSl)aU2#z˄PFwf$ÐMFRz(^:iy^ZU,zHbޥѫx8}9܇ .I`Xd:9_g,{l^F:l?QX vɱ\My L4劜7,[˨Rcxnxg({9r ë-KZoT~΀ x].eRn]ޣH-4[x1zmz8bPUd>3?i;ch'7L}3ΩZhU%{[O5gCTE+J拧-}\P!^F/Ko˄Oup;6Ey/Oiu%ɽ n+{;RI^.[ *%|xK/~BO m8h#&axfh`}h۾iY辸K΢j fJw :5:>^ߝ -S%%$0L(Hfy7~$D'Ww`Pa3URAJ0</d=e:y;"`<~U'QZ9}F6m[KUěd3evltaFWۓFBT)٩+ݻڌ?87}QCeZ/7@W۱t֗N^]*K&^6X&)*\bCWDU}!r RtY|mL+ 6u%'inڃ:b!M)Umm;3Ue8P$v( ke69LƖn!vyPZXg'W*[18Wg=.h kJq.)X˷A;Ka"(_۶{d'zt~,M{SRV?%BBݚxL֬Q' u)h^9vQIj*TC~PmܺřqׯhPV%P׸w?E1)Z ѺgpAa|HA$(@JIX8Sޛ_:z<<O%k\bܱ3{Odr*Fk\s](ꘃl~_Ĉ"q.!.\3Z)k݀p|+*ڈ"U"kE>0盛a :_.{xs2N0)*xrDOSD`Pc']guO* H<$#FZ}[<4wN8{%іJ8C$; ~1 ^Hry/6Dz:WTiB \& nݾaN" dž4Iup(H{KO/Z}T "ɣHH H^h:1+m /-2nLVDۧ0ar@~{ˑAz/_+c3v81Yl:5/ϦMQo 6|Ym KI|mf[e3b核{Tj8"Aۚ_cO~qX1|Y)˵ֶaqs1>8whmr6^[ l-Tt똩CvS)3ܲ|ŏ̖M%7^xӏv!h60\rBؾ YI9Q;xVTy֢8Em?atl,Cآ3!^r‚+f. ,f*ELv2\cu3Ж-kF?ߺ-nr:A'ğ-wߛKHJ-%1!OhGTɮ RR酬:Ewp5\ \kq ԫLc=*cm1Y ,TƸF*2UclYѸh&ץb#&2nGv(ZŲi8x>N pXGfH 1҇curEc?`#K$nۉ븂zOHZ5Rd8N2:ںF-$Fax)J`WK/,ˣQUQ!EЧ7S/yV v ^#ɲyԵ}}n(?f1yFq܂){J@^D6&s/Վ>mzi,$?M[P`I_*BDžð.F꽥 (u6Sdu|u_J,JZNCH?aDؓ:$$*GL8jVh1K=hϿ\%z%F\ISf|9_=N mSE~]|g[NhS}2*bǢ$ﮟJa2`&g=Q=JwP=K߈P]Y.gOraq xF;@ (~u\>EyW:+Dža~" )kڝZE(wE1W}rʮ%Y3]^feXIppȂi<};^^uf7`XExk~3&~BK ሙI5x3f6hUwϸ;'[+,]4I`blDZ)уȔZM!߷UM4{Lwk=.C$L--fqSUgdZLjJRoC߰rݢT"n2`$r3xa)Zy ,Z !rd R'M-jk8@-Œ>a}3 ЮVXZԚiv-߶d9N(kUÄ?ou) Zju`pGi~];!tx* B'Y!#V/ iLEW\XqY^{]C.)X9Sa^NT:loQK|)l:Hè\>o<7'bjSa[+Lƍ,N[k4z[ t_ t\ǹeLiٗ[64:Õ }\ϛ&;2Ia}&8FC  Kq7I8^Tb~'>e!]H Of%K `1T 僧YMOk8|4*.%r?iI w$*.t6 Y>4M!HƎ{0&.K"ٺ;lRoA~s®sBp6J0-m06N84h{3NV˄). egV{zeu-M:>iOJFō9:"(`$D6D;˂,%V 0i J7:Bo~&ݼ 0r5fR<^6ZG tRװRTo9zn4j%Kl4@kTrTGGrc}@Зd:ĽBݥh1Hc:;̊! f*ɷ(%4F:DgŐk:>='۸9_ hBQe]SOt7!_qvfY~vg:& Pgnn-y lGHB#p1lj:uA!]l=٥8qmO5 ac0J*qڇf:}Tx MHĚqW̑OhDֺA̫ Xw<8MMr_t|b\m~2\+Pը"F z4kc,Y[} ǣ!ջ{WVIdfU}>#,|ZWѻۄs]Qd lj|OQ&1IO0/^A']u#na#]Df|p_k$ş tpګV)ɂ%E(ë,M9'k\(z8ʳ>:q{*qU.>ǟ.h1}jߥgJ'| NVv'1smzĶ(M(nbgdK$ELNNEMH7S?ȕ[SqWa+]8O}#'HqWg¬Pua(=_ӯ/)R =V8YZEK}lz: laSn$>D 'i88?RpFGQGt&31z<:Ћ4^"2z[-落bFc!:4灌$k95/?ܗWOBlk<#_4❚tUwq#CZI[wVͰ\*  &1<ӛ9j@B?H,X(J:w= {'+ U#Yj~HY oܔ}<\h[8 )h70&&Q Ͱ^,,< F[ Ȉ/7yw/P4ޣo,xq0hePl:_ &=+x.JM0 _ld92aH_Q׽/tMk  Ni]qc+c@i4-~њ(o.W~7?z clǘ'Ϳ`k|8,AAKa:ՄwofchD]uB腭_ Nwlf o _#,+cfDf 'Iaxпo_'bM9v5.2]Y \aL48žsX)4?/Ni=S1²/w%o"18d6g0~%U_weE)B#qC#ٯaiz \H: Vٸ)"zmrq\0R4~5}wW#]9+Pr;M G{MO+ԇ8qĉW~Xj*ռ:j%2AGWS1sLfzKLxn"< j 5")x.H,7\ɕѪ|^#ӏbFw4oj=Nmִ9oY:ܼJ02&9+.IGj-"4! tgѱY֓xs5h\uISO.Pgg+PIK9y$A90Ng @I/mvƿ:K)Ke -Y:\ ;:%j :jɈu JMI7DU:הΝY钮ykXby۝..f!mSm8<}rDB?H{c4I w.mxRM{ ONZS! jbj1yacwdΊCm2:k=j{tZ* L + '%pڶȧ|5!q#pJoWXlm#\ϚoL9[TQ˕C|zW5MAzem050b;p@f!dҳ-##k(|AGo lv3] 酅f?zh)yp-&0 ^mݑ+nKJx^KڗO_9Ҧ>!IUf_;Ȇ­?rWzJԈ7ؾ+De6!wje,.vb^3"IWrU4~L ąsJ"&2_B`VIR\z:S$ ihjnCe֙bUO?Xu/ob.wImk."Ns]^i:\˄[p? i<o!I-4cj]?TgE*֓Eߥ:L۞`aܰ÷~a}26\V itZ5 r@a-y!kJ{ })5\ZnmM'UV^jvq$PG }~EaZW^Dg]jWr=Ka `h5.)!?m} BV. "2qy$oYMf)ٛyHzzywGʋ"Zpȭd .Xi.O]W8@Og)%ib:7q.P:ܳwC5ԥ<= C臥`#2+BR͏F%3iU)ÀEq潑D{/$Hod ҭYcM#E{h}s wԴF{t{\sBr!kO3Nrc&/L7uyv__H6}^>7PsdgZ߇ʽX~RV'QEXCI6d_ܠd֭WnJ%c<Ï9dySFT^zLZ:z9<ďӯl>6u۬3VN ]XSs?FRt?3 'C8;w` FkAoNƽL+}MmV, + i!V [X&˰B 㴨ЇJ-X7nNt)` z)D̾^&r?-/K'gs:K{b)YO{/->^"")$%KM.ge*֦=4M(2ׄ3|{>ơ%ɀlqŘ{[8IJ=upу 8&0g&teC͊v]Z ʋ,G5Sc};qW9Oi/|pVtA b&7,$}=uI!X#R7|FOHB]I뗤>Jw~a^ćx5Yec `LZ,Nf` 4Dj*Ԏio-!7 /gxꓧJ{{ -^?X,qs}>K9r wU?-+*_ptNRdxނ<6o̺eM۝DQ(fm|X諒g(Y2[SM/YmaCuS&pyY D g֗w᭧̏`Ϳ1%a?B}N/ؽj9K5f:8Pn,T⾡M&“~ "$\lIh.`'ZL1U{',\Fgcr׌ OOMx}vYm4GW'[$^$ X~ecyq7J &tMfyN&2| d3&V=ȆZmc"vƎCc2#W/]YI#cI  3K䙤B`*(ᵤ2Dg Vc%ylq%S94&}u}P~c#^6oS7Œ~$\Wފ-TK'Um{l) ϩ)~97PL9ybU/ %7u #אMv>PHqWʝv;S8svyFYIDO?ҥ.N0b;?:j m $0p#ԐWG_Mi267Aʒ$10̦cShl>zۥ^Zj{~xLX0K7/2PMGv! QS "o4vm#W-4 dx߈E!.cvnjn(XRN,x8,|ˀ. h[]y"9xu/H,vAaɋM ;ۺqGitZS ۺ!o p`m]-=mIQ)&Q#.#q'n,wOНd XmUtTL?. $Ǘ62  0*U* gq?sŰ< '$LAs'BKHK=h1!!!j ?L_ GF g,;hB_.uA`Ռ)5A92u9+\5Ե`j;QlҊp>(U~VuG[A6\ɶ 'zɄ\ě)z4ovRY,ug2l/,fxP uȃ }mxPqvJ iI'}H{|9KXtE߱lojǰJz I lRcf㱽VH{U4o<*PmZfEj)|tn̛ }s)[Zh%(vUL@Kn {徇zD@OރIkNÖ;QߔNᵾfIm(R \ЌwS_Z{TdNk:*3#p6BtǭP[K)؀27)FtL9STaY WNTu*nh&*DR"<Ϩdmֺ84em~mhů 7]Py-dwzޱ,4{3;,.%+߂BJt{wI{ RqWEz`S\y B"4$"OW$҃Kg[2fl6Fo7['H>%,hFթNw%kưzamPH 8N7Ѷ"h?vVNdnJRD6dG9+Y7u7v!s>V%ŌWQ]WH40Ic:xjVlɤ_wK=xl[a ӒsWP%)dI%X\J"? endstream endobj 301 0 obj << /Type /ObjStm /N 100 /First 922 /Length 4286 /Filter /FlateDecode >> stream x[rH}Wq&novw{`)MR=|͓UEYt'l ʓKeANJRT$JX*4xG߮^}W9iyHYSD6TƌԕS*)!tPJ)uaM$FE#"tb(ӒE1JA2LHR%)c YV.#5f0BmBVVICu^CXGS bcCxRHR;*o;2H# :VVQMh0$&[IdCĎ ,PK5X#d Xk@HdHBIˀ[*\yYG<@+' ;h0F txEkEH. 3 KZO=\ G#YVߍ% fY (il;йXg&HZImKNh]n9uޒp4AMW#NmDcn󩞷}D/ek^ n̍!\K'>MoiҔ09JqiNM(hkXM֒T<0.-T2^ e1"$uFvώ ^Rʭ3rMJĉ.|ģfYNRR+m tvh4;z+ zPqgD>G%UTh gP4+IZ[Rr$=mjh+tY43 жtD-vrDihJܚFJ}n? "tEEgύO7-E[*Nn 6iOHsv=;jG'#:(lALdU:2oRAx,@{m*k5,!fC*j-znH.ɞM9 ^bJt3 $UH{M 5+y8xVs[kf۔>j5hƞHEPg ; p_lӾܺC[}GJ{Wi$rW>Wv nFZĻ.7L90@$ _-GD%wZq۰B.m#Lس\&&S{$"ahJ0ǧ1r z)98p\,#TQrH%WO^sast%A8ԓXc^.úVyIsUuF9jm=BF h,lɀS9!]/%xp: E-g)hb;S8FP$Vba|ƃP`%D[x,MGArRd4|*ә t@kRs)ʣ-2%)O{`$8PiGXUk{; V0kux _-N 9Ό仭33˾Wjb1qOU;$T0qZZG_ZE-))D3d{!F-==9Zf>yy.3wN%u#mSN~3+h[юMG1Rg Õ%M6HnV syX-4=-skɨr:|lo~L:N"s=pfU؏pacwKB\Gg~yfͻ oPg&S]5Khz:<(Dm=ng"o}n8Fזu'̧M<68d-{EК ,4 Lg([Tဪb<#W \PdrTkT$.#T>иESq96O@`(n(zǙju\Ǜtۇ|?wy$ݘO?+)[W4v9<]≷ODywz ]F$ n)|guE_lWĈ)(/Mw]qO)Gx.^RSQ||*D-$>_4+Y|_R/L!\,bR,*ɽ3Zf3u~XuXϾ|Yյk).__.-]ϸH[&=޻zv.L;C;`Yc)YځC!.\^~sу15p}p݂fneTo'nU? :}=Glnòe?/㜩y_n΅糼]V\ϱ+F_~x-{HjScf:=;~*~~D19D*ɦ+Fq Fq9|t[/ك7'{a6?dn-u ,U~OY[UGQ'9(}~ݽWP'涬Aތ:~O>ZEY*sk>\Gޝc@uASW]O)<J |l{ЍmOc8rl@{@mơ8 cgT.le޾Z;u|+ }{f   endstream endobj 383 0 obj << /Author()/Title()/Subject()/Creator(LaTeX with hyperref package)/Producer(pdfTeX-1.40.13)/Keywords() /CreationDate (D:20130115223603-08'00') /ModDate (D:20130115223603-08'00') /Trapped /False /PTEX.Fullbanner (This is pdfTeX, Version 3.1415926-2.4-1.40.13 (TeX Live 2012/Debian) kpathsea version 6.1.0) >> endobj 363 0 obj << /Type /ObjStm /N 34 /First 272 /Length 1113 /Filter /FlateDecode >> stream xڭWMo7WH0 1M"iO(bK*.wCH)^ސ]+ (Z Z R1 tpJ k`p P[-Hдnh xO7T5Ѝ` A8mhhBIR\LJt(f0臒n&ESGD1'U'8@bǺrbZ|}6=~ٺ$d0ng`T#+086LV F30:Q3302IJb)@:?}{VnIIF%>![=A=]?~zKQahRDC1yİB9g6u,//َ!:Fme(+lhfMM厳eM^?׼ش"T[dZ[,6]]t ޛ*Am߯5wci%s݁v7~}WG7z#;w^߶_埇n,zyw|>|tay{s=_uב+?oe endstream endobj 384 0 obj << /Type /XRef /Index [0 385] /Size 385 /W [1 3 1] /Root 382 0 R /Info 383 0 R /ID [ ] /Length 941 /Filter /FlateDecode >> stream xMLUWϺ( pACQZR*E Nitؤ8̘6IiS9i;iɗֹy)_Rn6* ,m*2@ l Jx[A-^Yb/.Q/2?7z<ߪlۍӲ&Ќ׋ZM[A><B;؉_v ;A0;;nЃW{&n!x'֐}<pM 87w9."s0w9 - @=ϯ!q<<yŻ<ཋUxKIOUI0$/"8$/rAւyYpY ny/#3-rMpE jA)5zy; Y~?QI1Iq=OS 1pJ w[8 +J}^]gg&i%V×)u-rHէ^eBJ$E'<~ Jx ->_idkVz«܊ OǪ]G^GS]{2f!}bYW3/0AcPe=AR#J;ۀjd2&~ N1 BSJ#:CE d(f MA%Uy,K#U8 3'֤gnV5O#'Q*OU]5i'`h;Vin6 Hj; :ATn}T9sݜ6`|A髊uw5lUFpը\Rm՘JIW]VWUz#*: |<̩|ƪ9(82 Έ^-t%-): +hNAs eќ4UI{ݕ endstream endobj startxref 284024 %%EOF flann-1.8.4-src/doc/manual.tex0000644000000000000000000017414712075346130014670 0ustar rootroot\documentclass[letter,10pt]{article} \usepackage{latexsym} \usepackage{amsmath} \usepackage{amssymb} \usepackage{amsthm} \usepackage{amsfonts} \usepackage{epsfig} \usepackage{url} \usepackage{fancyvrb} \usepackage{hyperref} %opening \title{FLANN - Fast Library for Approximate Nearest Neighbors\\[0.5cm] User Manual\\[1cm]} \author{Marius Muja, mariusm@cs.ubc.ca\\David Lowe, lowe@cs.ubc.ca} \begin{document} \begin{titlepage} \vspace{10cm} \maketitle \thispagestyle{empty} \end{titlepage} \section{Introduction} We can define the \emph{nearest neighbor search (NSS)} problem in the following way: given a set of points $P=p_1,p_2,\dots,p_n$ in a metric space $X$, these points must be preprocessed in such a way that given a new query point $q \in X$, finding the point in $P$ that is nearest to $q$ can be done quickly. The problem of nearest neighbor search is one of major importance in a variety of applications such as image recognition, data compression, pattern recognition and classification, machine learning, document retrieval systems, statistics and data analysis. However, solving this problem in high dimensional spaces seems to be a very difficult task and there is no algorithm that performs significantly better than the standard brute-force search. This has lead to an increasing interest in a class of algorithms that perform approximate nearest neighbor searches, which have proven to be a good-enough approximation in most practical applications and in most cases, orders of magnitude faster that the algorithms performing the exact searches. FLANN (Fast Library for Approximate Nearest Neighbors) is a library for performing fast approximate nearest neighbor searches. FLANN is written in the C++ programming language. FLANN can be easily used in many contexts through the C, MATLAB and Python bindings provided with the library. \subsection{Quick Start} \label{sec:quickstart} This section contains small examples of how to use the FLANN library from different programming languages (C++, C, MATLAB and Python). \begin{itemize} \item \textbf{C++} \begin{Verbatim}[fontsize=\scriptsize,frame=single] // file flann_example.cpp #include #include #include int main(int argc, char** argv) { int nn = 3; flann::Matrix dataset; flann::Matrix query; flann::load_from_file(dataset, "dataset.hdf5","dataset"); flann::load_from_file(query, "dataset.hdf5","query"); flann::Matrix indices(new int[query.rows*nn], query.rows, nn); flann::Matrix dists(new float[query.rows*nn], query.rows, nn); // construct an randomized kd-tree index using 4 kd-trees flann::Index > index(dataset, flann::KDTreeIndexParams(4)); index.buildIndex(); // do a knn search, using 128 checks index.knnSearch(query, indices, dists, nn, flann::SearchParams(128)); flann::save_to_file(indices,"result.hdf5","result"); delete[] dataset.ptr(); delete[] query.ptr(); delete[] indices.ptr(); delete[] dists.ptr(); return 0; } \end{Verbatim} \item \textbf{C} \begin{Verbatim}[fontsize=\scriptsize,frame=single] /* file flann_example.c */ #include "flann.h" #include #include /* Function that reads a dataset */ float* read_points(char* filename, int *rows, int *cols); int main(int argc, char** argv) { int rows,cols; int t_rows, t_cols; float speedup; /* read dataset points from file dataset.dat */ float* dataset = read_points("dataset.dat", &rows, &cols); float* testset = read_points("testset.dat", &t_rows, &t_cols); /* points in dataset and testset should have the same dimensionality */ assert(cols==t_cols); /* number of nearest neighbors to search */ int nn = 3; /* allocate memory for the nearest-neighbors indices */ int* result = (int*) malloc(t_rows*nn*sizeof(int)); /* allocate memory for the distances */ float* dists = (float*) malloc(t_rows*nn*sizeof(float)); /* index parameters are stored here */ struct FLANNParameters p = DEFAULT_FLANN_PARAMETERS; p.algorithm = FLANN_INDEX_AUTOTUNED; /* or FLANN_INDEX_KDTREE, FLANN_INDEX_KMEANS, ... /* p.target_precision = 0.9; /* want 90% target precision */ /* compute the 3 nearest-neighbors of each point in the testset */ flann_find_nearest_neighbors(dataset, rows, cols, testset, t_rows, result, dists, nn, &p); ... free(dataset); free(testset); free(result); free(dists); return 0; } \end{Verbatim} \item \textbf{MATLAB} \begin{Verbatim}[fontsize=\scriptsize,frame=single] % create random dataset and test set dataset = single(rand(128,10000)); testset = single(rand(128,1000)); % define index and search parameters params.algorithm = 'kdtree'; params.trees = 8; params.checks = 64; % perform the nearest-neighbor search [result, dists] = flann_search(dataset,testset,5,params); \end{Verbatim} \item \textbf{Python} \begin{Verbatim}[fontsize=\scriptsize,frame=single] from pyflann import * from numpy import * from numpy.random import * dataset = rand(10000, 128) testset = rand(1000, 128) flann = FLANN() result,dists = flann.nn(dataset,testset,5,algorithm="kmeans", branching=32, iterations=7, checks=16); \end{Verbatim} \end{itemize} \section{Downloading and compiling FLANN} \label{sec:downloading_and_compiling} FLANN can be downloaded from the following address: \begin{center} \texttt{http://www.cs.ubc.ca/$\sim$mariusm/flann} \end{center} After downloading and unpacking, the following files and directories should be present: \begin{itemize} \item \texttt{bin}: directory various for scripts and binary files \item \texttt{doc}: directory containg this documentation \item \texttt{examples}: directory containg examples of using FLANN \item \texttt{src}: directory containg the source files \item \texttt{test}: directory containg unit tests for FLANN \end{itemize} To compile the FLANN library the \textit{CMake}\footnote{http://www.cmake.org/} build system is required. Below is an example of how FLANN can be compiled on Linux (replace x.y.z with the corresponding version number). \begin{Verbatim}[fontsize=\scriptsize,frame=single] $ cd flann-x.y.z-src $ mkdir build $ cd build $ cmake .. $ make \end{Verbatim} On windows the steps are very similar: \begin{Verbatim}[fontsize=\scriptsize,frame=single] > "C:\Program Files\Microsoft Visual Studio 9.0\VC\vcvarsall.bat" > cd flann-x.y.z-src > mkdir build > cd build > cmake .. > nmake \end{Verbatim} There are several compile options that can be configured before FLANN is compiled, for example the build type (Release, RelWithDebInfo, Debug) or whether to compile the C, Python or the MATLAB bindings. To change any of this options use the \texttt{cmake-gui} application after \texttt{cmake} has finished (see figure \ref{fig:cmake-gui}). \begin{Verbatim}[fontsize=\scriptsize,frame=single] > cmake-gui . \end{Verbatim} \begin{figure}[h] \begin{center} \includegraphics[width=0.9\textwidth]{images/cmake-gui.png} \caption{Configuring the FLANN compile options} \label{fig:cmake-gui} \end{center} \end{figure} \subsection{Upgrading from a previous version} This section contains changes that you need to be aware of when upgrading from a previous version of FLANN. \begin{description} \item[Version 1.7.0] A small breaking API change in \texttt{flann::Matrix} requires client code to be updated. In order to release the memory used by a matrix, use: \begin{Verbatim}[fontsize=\scriptsize] delete[] matrix.ptr(); \end{Verbatim} instead of: \begin{Verbatim}[fontsize=\scriptsize] delete[] matrix.data; \end{Verbatim} or: \begin{Verbatim}[fontsize=\scriptsize] matrix.free(); \end{Verbatim} The member \texttt{data} of \texttt{flann::Matrix} is not publicly accessible any more, use the \texttt{ptr()} method to obtain the pointer to the memory buffer. \end{description} \subsection{Compiling FLANN with multithreading support} For taking advantage of multithreaded search, the project that uses FLANN needs to be compiled with a compiler that supports the OpenMP standard and the OpenMP support must be enabled. The number of cores to be used can be selected with the \texttt{cores} field in the \texttt{SearchParams} structure. By default a single core will be used. Setting the \texttt{cores} field to zero will automatically use as many threads as cores available on the machine. \section{Using FLANN} \subsection{Using FLANN from C++} The core of the FLANN library is written in C++. To make use of the full power and flexibility of the templated code one should use the C++ bindings if possible. To use the C++ bindings you only need to include the the library header file \texttt{flann.hpp}. An example of the compile command that must be used will look something like this: \begin{Verbatim}[fontsize=\footnotesize] g++ flann_example.cpp -I $FLANN_ROOT/include -o flann_example_cpp \end{Verbatim} where \texttt{\$FLANN\_ROOT} is the library main directory. The following sections describe the public C++ API. \subsubsection{flann::Index} \label{sec:flann::Index} The FLANN nearest neighbor index class. This class is used to abstract different types of nearest neighbor search indexes. The class is templated on the distance functor to be used for computing distances between pairs of features. \begin{Verbatim}[fontsize=\footnotesize,frame=single] namespace flann { template class Index { typedef typename Distance::ElementType ElementType; typedef typename Distance::ResultType DistanceType; public: Index(const IndexParams& params, Distance distance = Distance() ); Index(const Matrix& points, const IndexParams& params, Distance distance = Distance() ); ~Index(); void buildIndex(); void buildIndex(const Matrix& points); void addPoints(const Matrix& points, float rebuild_threshold = 2); void removePoint(size_t point_id); ElementType* getPoint(size_t point_id); int knnSearch(const Matrix& queries, Matrix& indices, Matrix& dists, size_t knn, const SearchParams& params); int knnSearch(const Matrix& queries, std::vector< std::vector >& indices, std::vector >& dists, size_t knn, const SearchParams& params); int radiusSearch(const Matrix& queries, Matrix& indices, Matrix& dists, float radius, const SearchParams& params); int radiusSearch(const Matrix& queries, std::vector< std::vector >& indices, std::vector >& dists, float radius, const SearchParams& params); void save(std::string filename); int veclen() const; int size() const; IndexParams getParameters() const; flann_algorithm_t getType() const; }; } \end{Verbatim} \textbf{The Distance functor} The distance functor is a class whose \texttt{operator()} computes the distance between two features. If the distance is also a kd-tree compatible distance it should also provide an \texttt{accum\_dist()} method that computes the distance between individual feature dimensions. A typical distance functor looks like this (see the \texttt{dist.h} file for more examples): \begin{Verbatim}[fontsize=\footnotesize,frame=single] template struct L2 { typedef bool is_kdtree_distance; typedef T ElementType; typedef typename Accumulator::Type ResultType; template ResultType operator()(Iterator1 a, Iterator2 b, size_t size, ResultType /*worst_dist*/ = -1) const { ResultType result = ResultType(); ResultType diff; for(size_t i = 0; i < size; ++i ) { diff = *a++ - *b++; result += diff*diff; } return result; } template inline ResultType accum_dist(const U& a, const V& b, int) const { return (a-b)*(a-b); } }; \end{Verbatim} In addition to \texttt{operator()} and \texttt{accum\_dist()}, a distance functor should also define the \texttt{ElementType} and the \texttt{ResultType} as the types of the elements it operates on and the type of the result it computes. If a distance functor can be used as a kd-tree distance (meaning that the full distance between a pair of features can be accumulated from the partial distances between the individual dimensions) a typedef \texttt{is\_kdtree\_distance} should be present inside the distance functor. If the distance is not a kd-tree distance, but it's a distance in a vector space (the individual dimensions of the elements it operates on can be accessed independently) a typedef \texttt{is\_vector\_space\_distance} should be defined inside the functor. If neither typedef is defined, the distance is assumed to be a metric distance and will only be used with indexes operating on generic metric distances. \\ \textbf{flann::Index::Index} Constructs a nearest neighbor search index for a given dataset. \begin{Verbatim}[fontsize=\footnotesize,frame=single] Index(const IndexParams& params, Distance distance = Distance() ); Index(const Matrix& points, const IndexParams& params, Distance distance = Distance() ); \end{Verbatim} \begin{description} \item[features] Matrix containing the features(points) that should be indexed, stored in a row-major order (one point on each row of the matrix). The size of the matrix is $num\_features \times dimensionality$. \item[params] Structure containing the index parameters. The type of index that will be constructed depends on the type of this parameter. The possible parameter types are: \textbf{LinearIndexParams} When passing an object of this type, the index will perform a linear, brute-force search. \begin{Verbatim}[fontsize=\footnotesize] struct LinearIndexParams : public IndexParams { }; \end{Verbatim} \textbf{KDTreeIndexParams} When passing an object of this type the index constructed will consist of a set of randomized kd-trees which will be searched in parallel. \begin{Verbatim}[fontsize=\footnotesize] struct KDTreeIndexParams : public IndexParams { KDTreeIndexParams( int trees = 4 ); }; \end{Verbatim} \begin{description} \item[trees] The number of parallel kd-trees to use. Good values are in the range [1..16] \end{description} \textbf{KMeansIndexParams} When passing an object of this type the index constructed will be a hierarchical k-means tree. \begin{Verbatim}[fontsize=\footnotesize] struct KMeansIndexParams : public IndexParams { KMeansIndexParams( int branching = 32, int iterations = 11, flann_centers_init_t centers_init = FLANN_CENTERS_RANDOM, float cb_index = 0.2 ); }; \end{Verbatim} \begin{description} \item[branching]{ The branching factor to use for the hierarchical k-means tree } \item[iterations]{ The maximum number of iterations to use in the k-means clustering stage when building the k-means tree. If a value of -1 is used here, it means that the k-means clustering should be iterated until convergence} \item[centers\_init]{ The algorithm to use for selecting the initial centers when performing a k-means clustering step. The possible values are CENTERS\_RANDOM (picks the initial cluster centers randomly), CENTERS\_GONZALES (picks the initial centers using Gonzales' algorithm) and CENTERS\_KMEANSPP (picks the initial centers using the algorithm suggested in \cite{arthur_kmeanspp_2007}) } \item[cb\_index]{ This parameter (cluster boundary index) influences the way exploration is performed in the hierarchical kmeans tree. When \texttt{cb\_index} is zero the next kmeans domain to be explored is choosen to be the one with the closest center. A value greater then zero also takes into account the size of the domain.} \end{description} \textbf{CompositeIndexParams} When using a parameters object of this type the index created combines the randomized kd-trees and the hierarchical k-means tree. \begin{Verbatim}[fontsize=\footnotesize] struct CompositeIndexParams : public IndexParams { CompositeIndexParams( int trees = 4, int branching = 32, int iterations = 11, flann_centers_init_t centers_init = FLANN_CENTERS_RANDOM, float cb_index = 0.2 ); }; \end{Verbatim} \textbf{KDTreeSingleIndexParams} When passing an object of this type the index will contain a single kd-tree optimized for searching lower dimensionality data (for example 3D point clouds) \begin{Verbatim}[fontsize=\footnotesize] struct KDTreeSingleIndexParams : public IndexParams { KDTreeSingleIndexParams( int leaf_max_size = 10 ); }; \end{Verbatim} \begin{description} \item[max\_leaf\_size] The maximum number of points to have in a leaf for not branching the tree any more. \end{description} \textbf{KDTreeCuda3dIndexParams} When passing an object of this type the index will be a single kd-tree that is built and performs searches on a CUDA compatible GPU. Search performance is best for large numbers of search and query points. For more information, see section \ref{sec:flann::cuda} \begin{Verbatim}[fontsize=\footnotesize] struct KDTreeCuda3dIndexParams : public IndexParams { KDTreeCuda3dIndexParams( int leaf_max_size = 64 ); }; \end{Verbatim} \begin{description} \item[max\_leaf\_size] The maximum number of points to have in a leaf for not branching the tree any more. \end{description} \textbf{HierarchicalClusteringIndexParams} When passing an object of this type the index constructed will be a hierarchical clustering index. This type of index works with any metric distance and can be used for matching binary features using Hamming distances. \begin{Verbatim}[fontsize=\footnotesize] struct HierarchicalClusteringIndexParams : public IndexParams { HierarchicalClusteringIndexParams(int branching = 32, flann_centers_init_t centers_init = FLANN_CENTERS_RANDOM, int trees = 4, int leaf_max_size = 100) }; \end{Verbatim} \begin{description} \item[branching]{ The branching factor to use for the hierarchical clustering tree } \item[centers\_init]{ The algorithm to use for selecting the initial centers when performing a k-means clustering step. The possible values are CENTERS\_RANDOM (picks the initial cluster centers randomly), CENTERS\_GONZALES (picks the initial centers using Gonzales' algorithm) and CENTERS\_KMEANSPP (picks the initial centers using the algorithm suggested in \cite{arthur_kmeanspp_2007}) } \item[trees] The number of parallel trees to use. Good values are in the range [3..8] \item[leaf\_size] The maximum number of points a leaf node should contain. \end{description} \textbf{LshIndexParams} When passing an object of this type the index constructed will be a multi-probe LSH (Locality-Sensitive Hashing) index. This type of index can only be used for matching binary features using Hamming distances. \begin{Verbatim}[fontsize=\footnotesize] struct LshIndexParams : public IndexParams { LshIndexParams(unsigned int table_number = 12, unsigned int key_size = 20, unsigned int multi_probe_level = 2); }; \end{Verbatim} \begin{description} \item[table\_number]{ The number of hash tables to use } \item[key\_size]{ The length of the key in the hash tables} \item[multi\_probe\_level] Number of levels to use in multi-probe (0 for standard LSH) \end{description} \textbf{AutotunedIndexParams} When passing an object of this type the index created is automatically tuned to offer the best performance, by choosing the optimal index type (randomized kd-trees, hierarchical kmeans, linear) and parameters for the dataset provided. \begin{Verbatim}[fontsize=\footnotesize] struct AutotunedIndexParams : public IndexParams { AutotunedIndexParams( float target_precision = 0.9, float build_weight = 0.01, float memory_weight = 0, float sample_fraction = 0.1 ); }; \end{Verbatim} \begin{description} \item[target\_precision]{ Is a number between 0 and 1 specifying the percentage of the approximate nearest-neighbor searches that return the exact nearest-neighbor. Using a higher value for this parameter gives more accurate results, but the search takes longer. The optimum value usually depends on the application. } \item[build\_weight]{ Specifies the importance of the index build time raported to the nearest-neighbor search time. In some applications it's acceptable for the index build step to take a long time if the subsequent searches in the index can be performed very fast. In other applications it's required that the index be build as fast as possible even if that leads to slightly longer search times.} \item[memory\_weight]{Is used to specify the tradeoff between time (index build time and search time) and memory used by the index. A value less than 1 gives more importance to the time spent and a value greater than 1 gives more importance to the memory usage.} \item[sample\_fraction]{Is a number between 0 and 1 indicating what fraction of the dataset to use in the automatic parameter configuration algorithm. Running the algorithm on the full dataset gives the most accurate results, but for very large datasets can take longer than desired. In such case using just a fraction of the data helps speeding up this algorithm while still giving good approximations of the optimum parameters.} \end{description} \textbf{SavedIndexParams} This object type is used for loading a previously saved index from the disk. \begin{Verbatim}[fontsize=\footnotesize] struct SavedIndexParams : public IndexParams { SavedIndexParams( std::string filename ); }; \end{Verbatim} \begin{description} \item[filename]{ The filename in which the index was saved. } \end{description} \end{description} \subsubsection{flann::Index::buildIndex} Builds the nearest neighbor search index. There are two versions of the \texttt{buildIndex} method, one that uses the points provided as argument and one that uses the points provided to the constructor when the object was constructed. \begin{Verbatim}[fontsize=\footnotesize,frame=single] void buildIndex(); void buildIndex(const Matrix& points); \end{Verbatim} \subsubsection{flann::Index::addPoints} The \texttt{addPoints} method can be used to incrementally add points to the index after the index was build. To avoid the index getting unbalanced, the \texttt{addPoints} method has the option of rebuilding the index after a large number of points have been added. The \texttt{rebuild\_threshold} parameter controls when the index is rebuild, by default this is done when it doubles in size (\texttt{rebuild\_threshold} = 2). \begin{Verbatim}[fontsize=\footnotesize,frame=single] void addPoints(const Matrix& points, float rebuild_threshold = 2); \end{Verbatim} \subsubsection{flann::Index::removePoint} The \texttt{removePoint} method removes one point with the specified \texttt{point\_id} from the index. The indices (of the remaining points) returned by the nearest neighbor operations do not change when points are removed from the index. \begin{Verbatim}[fontsize=\footnotesize,frame=single] void removePoint(size_t point_id); \end{Verbatim} \subsubsection{flann::Index::getPoint} The \texttt{getPoint} method returns a pointer to the data point with the specified \texttt{point\_id}. \begin{Verbatim}[fontsize=\footnotesize,frame=single] ElementType* getPoint(size_t point_id); \end{Verbatim} \subsubsection{flann::Index::knnSearch} Performs a K-nearest neighbor search for a set of query points. There are two signatures for this method, one that takes pre-allocated \texttt{flann::Matrix} objects for returning the indices of and distances to the neighbors found, and one that takes \texttt{std::vector} that will re resized automatically as needed. \begin{Verbatim}[fontsize=\footnotesize,frame=single] int Index::knnSearch(const Matrix& queries, Matrix& indices, Matrix& dists, size_t knn, const SearchParams& params); int Index::knnSearch(const Matrix& queries, std::vector< std::vector >& indices, std::vector >& dists, size_t knn, const SearchParams& params); \end{Verbatim} \begin{description} \item[queries]{Matrix containing the query points. Size of matrix is ($num\_queries \times dimentionality $)} \item[indices]{Matrix that will contain the indices of the K-nearest neighbors found (size should be at least $num\_queries \times knn$ for the pre-allocated version).} \item[dists]{Matrix that will contain the distances to the K-nearest neighbors found (size should be at least $num\_queries \times knn$ for the pre-allocated version).} \item[knn]{Number of nearest neighbors to search for.} \item[params]{Search parameters.} Structure containing parameters used during search. \textbf{SearchParameters} \begin{Verbatim}[fontsize=\footnotesize] struct SearchParams { SearchParams(int checks = 32, float eps = 0, bool sorted = true); int checks; float eps; bool sorted; int max_neighbors; tri_type use_heap; int cores; bool matrices_in_gpu_ram; }; \end{Verbatim} \begin{description} \item[checks] specifies the maximum leafs to visit when searching for neighbours. A higher value for this parameter would give better search precision, but also take more time. For all leafs to be checked use the value \texttt{CHECKS\_UNLIMITED}. If automatic configuration was used when the index was created, the number of checks required to achieve the specified precision was also computed, to use that value specify \texttt{CHECKS\_AUTOTUNED}. \item[eps] Search for eps-approximate neighbors (only used by KDTreeSingleIndex and KDTreeCuda3dIndex). \item[sorted] Used only by radius search, specifies if the neighbors returned should be sorted by distance. \item[max\_neighbours] Specifies the maximum number of neighbors radius search should return (default: -1 = unlimited). Only used for radius search. \item[use\_heap] Use a heap data structure to manage the list of neighbors internally (possible values: FLANN\_False, FLANN\_True, FLANN\_Undefined). A heap is more efficient for a large number of neighbors and less efficient for a small number of neighbors. Default value is FLANN\_Undefined, which lets the code choose the best option depending on the number of neighbors requested. Only used for KNN search. \item[cores] How many cores to assign to the search (specify 0 for automatic core selection). \item[matrices\_in\_gpu\_ram] for GPU search indicates if matrices are already in GPU ram. \end{description} \end{description} \subsubsection{flann::Index::radiusSearch} Performs a radius nearest neighbor search for a set of query points. There are two signatures for this method, one that takes pre-allocated \texttt{flann::Matrix} objects for returning the indices of and distances to the neighbors found, and one that takes \texttt{std::vector} that will resized automatically as needed. \begin{Verbatim}[fontsize=\footnotesize,frame=single] int Index::radiusSearch(const Matrix& queries, Matrix& indices, Matrix& dists, float radius, const SearchParams& params); int Index::radiusSearch(const Matrix& queries, std::vector< std::vector >& indices, std::vector >& dists, float radius, const SearchParams& params); \end{Verbatim} \begin{description} \item[queries]{The query point. Size of matrix is ($num\_queries \times dimentionality $).} \item[indices]{Matrix that will contain the indices of the K-nearest neighbors found. For the pre-allocated version, only as many neighbors are returned as many columns in this matrix. If fewer neighbors are found than columns in this matrix, the element after that last index returned is -1. In case of the std::vector version, the rows will be resized as needed to fit all the neighbors to be returned, except if the ``max\_neighbors'' search parameter is set.} \item[dists]{Matrix that will contain the distances to the K-nearest neighbors found. The same number of values are returned here as for the \texttt{indices} matrix.} \item[radius]{The search radius} \item[params]{Search parameters} \end{description} The method returns the number of nearest neighbors found. \subsubsection{flann::Index::save} Saves the index to a file. \begin{Verbatim}[fontsize=\footnotesize,frame=single] void Index::save(std::string filename); \end{Verbatim} \begin{description} \item[filename]{The file to save the index to} \end{description} \subsubsection{flann::hierarchicalClustering} \label{flann::hierarchicalClustering} Clusters the given points by constructing a hierarchical k-means tree and choosing a cut in the tree that minimizes the clusters' variance. \begin{Verbatim}[fontsize=\footnotesize,frame=single] template int hierarchicalClustering(const Matrix& features, Matrix& centers, const KMeansIndexParams& params, Distance d = Distance()) \end{Verbatim} \begin{description} \item[features]{The points to be clustered} \item[centers]{The centers of the clusters obtained. The number of rows in this matrix represents the number of clusters desired. However, because of the way the cut in the hierarchical tree is choosen, the number of clusters computed will be the highest number of the form $(branching-1)*k+1$ that's lower than the number of clusters desired, where $branching$ is the tree's branching factor (see description of the KMeansIndexParams). } \item[params]{Parameters used in the construction of the hierarchical k-means tree} \end{description} The function returns the number of clusters computed. \subsubsection{flann::KdTreeCuda3dIndex} \label{sec:flann::cuda} FLANN provides a CUDA implementation of the kd-tree build and search algorithms to improve the build and query speed for large 3d data sets. This section will provide all the necessary information to use the \texttt{KdTreeCuda3dIndex} index type. \textbf{Building:} If CMake detects a CUDA install during the build (see section \ref{sec:downloading_and_compiling}), a library \texttt{libflann\_cuda.so} will be built. \textbf{Basic Usage:} To be able to use the new index type, you have to include the FLANN header this way: \begin{Verbatim}[fontsize=\footnotesize,frame=single] #define FLANN_USE_CUDA #include \end{Verbatim} If you define the symbol \texttt{FLANN\_USE\_CUDA} before including the FLANN header, you will have to link \texttt{libflann\_cuda.so} or \texttt{libflann\_cuda\_s.a} with your project. However, you will not have to compile your source code with \texttt{nvcc}, except if you use other CUDA code, of course. You can then create your index by using the \texttt{KDTreeCuda3dIndexParams} to create the index. The index will take care of copying all the data from and to the GPU for you, both for index creation and search. A word of caution: A general guideline for deciding whether to use the CUDA kd tree or a (multi-threaded) CPU implementation is hard to give, since it depends on the combination of CPU and GPU in each system and the data sets. For example, on a system with a Phenom II 955 CPU and a Geforce GTX 260 GPU, the maximum search speedup on a synthetic (random) data set is a factor of about 8-9 vs the single-core CPU search, and starts to be reached at about 100k search and query points. (This includes transfer times.) Build time does not profit as much from the GPU acceleration; here the benefit is about 2x at 200k points, but this largely depends on the data set. The only way to know which implementation is best suited is to try it. \textbf{Advanced Usage:} In some cases, you might already have your data in a buffer on the GPU. In this case, you can re-use these buffers instead of copying the buffers back to system RAM for index creation and search. The way to do this is to pass GPU pointers via the \texttt{flann::Matrix} inputs and tell the index via the index and search params to treat the pointers as GPU pointers. \begin{Verbatim}[fontsize=\footnotesize,frame=single] thrust::device_vector points_vector( n_points ); // ... fill vector here... float* gpu_pointer = (float*)thrust::raw_pointer_cast(&points_vector[0]); flann::Matrix matrix_gpu(gpu_pointer,n_points,3, 4); flann::KDTreeCuda3dIndexParams params; params["input_is_gpu_float4"]=true; flann::Index > flannindex( matrix_gpu, params ); flannindex.buildIndex(); \end{Verbatim} \begin{description} \item First, a GPU buffer of float4 values is created and filled with points. \footnote{For index creation, only \texttt{float4} points are supported, \texttt{float3} or structure-of-array (SOA) representations are currently not supported since \texttt{float4} proved to be best in terms of access speed for tree creation and search.} \item Then, a GPU pointer to the buffer is stored in a flann matrix with 3 columns and a stride of 4 (since the last element in the \texttt{float4} is unused). \item Last, the index is created. The \texttt{input\_is\_gpu\_float4} flag tells the index to treat the matrix as a gpu buffer. \end{description} Similarly, you can specify GPU buffers for the search routines that return the result via flann matrices (but not for those that return them via \texttt{std::vector}s). To do this, the pointers in the index and dist matrices have to point to GPU buffers and the \texttt{cols} value has to be set to 3 (as we are dealing with 3d points). Here, any value for \texttt{stride} can be used. \begin{Verbatim}[fontsize=\footnotesize,frame=single] flann::Matrix indices_gpu(gpu_pointer_indices,n_points, knn, stride); flann::Matrix dists_gpu(gpu_pointer_dists,n_points, knn, stride); flann::SearchParams params; params.matrices_in_gpu_ram = true; flannindex.knnSearch( queries_gpu ,indices_gpu,dists_gpu,knn, params); \end{Verbatim} \begin{description} \item Note that you cannot mix matrices in system and CPU ram here! \end{description} \textbf{Search Parameters:} The search routines support three parameters: \begin{itemize} \item \texttt{eps} - used for approximate knn search. The maximum possible error is $e= d_{best} * eps$, i.e. the distance of the returned neighbor is at maximum $eps$ times larget than the distance to the real best neighbor. \item \texttt{use\_heap} - used in knn and radius search. If set to true, a heap structure will be used in the search to keep track of the distance to the farthest neighbor. Beneficial with about $k>64$ elements. \item \texttt{sorted} - if set to true, the results of the radius and knn searches will be sorted in ascending order by their distance to the query point. \item \texttt{matrices\_in\_gpu\_ram} - set to true to indicate that all (input and output) matrices are located in GPU RAM. \end{itemize} \subsection{Using FLANN from C} FLANN can be used in C programs through the C bindings provided with the library. Because there is no template support in C, there are bindings provided for the following data types: \texttt{unsigned char}, \texttt{int}, \texttt{float} and \texttt{double}. For each of the functions below there is a corresponding version for each of the for data types, for example for the function: \begin{Verbatim}[fontsize=\footnotesize,frame=single] flan_index_t flann_build_index(float* dataset, int rows, int cols, float* speedup, struct FLANNParameters* flann_params); \end{Verbatim} there are also the following versions: \begin{Verbatim}[fontsize=\footnotesize,frame=single] flan_index_t flann_build_index_byte(unsigned char* dataset, int rows, int cols, float* speedup, struct FLANNParameters* flann_params); flan_index_t flann_build_index_int(int* dataset, int rows, int cols, float* speedup, struct FLANNParameters* flann_params); flan_index_t flann_build_index_float(float* dataset, int rows, int cols, float* speedup, struct FLANNParameters* flann_params); flan_index_t flann_build_index_double(double* dataset, int rows, int cols, float* speedup, struct FLANNParameters* flann_params); \end{Verbatim} \subsubsection{flann\_build\_index()} \begin{Verbatim}[fontsize=\footnotesize,frame=single] flan_index_t flann_build_index(float* dataset, int rows, int cols, float* speedup, struct FLANNParameters* flann_params); \end{Verbatim} This function builds an index and return a reference to it. The arguments expected by this function are as follows: \begin{description} \item[dataset, rows and cols] - are used to specify the input dataset of points: dataset is a pointer to a $\rm{rows} \times \rm{cols}$ matrix stored in row-major order (one feature on each row) \item [speedup] - is used to return the approximate speedup over linear search achieved when using the automatic index and parameter configuration (see section \ref{sec:flann_build_index}) \item [flann\_params] - is a structure containing the parameters passed to the function. This structure is defined as follows: \begin{Verbatim}[fontsize=\footnotesize] struct FLANNParameters { enum flann_algorithm_t algorithm; /* the algorithm to use */ /* search parameters */ int checks; /* how many leafs (features) to check in one search */ float cb_index; /* cluster boundary index. Used when searching the kmeans tree */ /* kdtree index parameters */ int trees; /* number of randomized trees to use (for kdtree) */ /* kmeans index parameters */ int branching; /* branching factor (for kmeans tree) */ int iterations; /* max iterations to perform in one kmeans cluetering (kmeans tree) */ enum flann_centers_init_t centers_init; /* algorithm used for picking the initial cluster centers for kmeans tree */ /* autotuned index parameters */ float target_precision; /* precision desired (used for autotuning, -1 otherwise) */ float build_weight; /* build tree time weighting factor */ float memory_weight; /* index memory weigthing factor */ float sample_fraction; /* what fraction of the dataset to use for autotuning */ /* LSH parameters */ unsigned int table_number_; /** The number of hash tables to use */ unsigned int key_size_; /** The length of the key in the hash tables */ unsigned int multi_probe_level_; /** Number of levels to use in multi-probe LSH, 0 for standard LSH */ /* other parameters */ enum flann_log_level_t log_level; /* determines the verbosity of each flann function */ long random_seed; /* random seed to use */ }; \end{Verbatim} The \texttt{algorithm} and \texttt{centers\_init} fields can take the following values: \begin{Verbatim}[fontsize=\footnotesize] enum flann_algorithm_t { FLANN_INDEX_LINEAR = 0, FLANN_INDEX_KDTREE = 1, FLANN_INDEX_KMEANS = 2, FLANN_INDEX_COMPOSITE = 3, FLANN_INDEX_KDTREE_SINGLE = 3, FLANN_INDEX_SAVED = 254, FLANN_INDEX_AUTOTUNED = 255 }; enum flann_centers_init_t { FLANN_CENTERS_RANDOM = 0, FLANN_CENTERS_GONZALES = 1, FLANN_CENTERS_KMEANSPP = 2 }; \end{Verbatim} The \texttt{algorithm} field is used to manually select the type of index used. The \texttt{centers\_init} field specifies how to choose the inital cluster centers when performing the hierarchical k-means clustering (in case the algorithm used is k-means): \texttt{FLANN\_CENTERS\_RANDOM} chooses the initial centers randomly, \texttt{FLANN\_CENTERS\_GONZALES} chooses the initial centers to be spaced apart from each other by using Gonzales' algorithm and \texttt{FLANN\_CENTERS\_KMEANSPP} chooses the initial centers using the algorithm proposed in \cite{arthur_kmeanspp_2007}. The fields: \texttt{checks}, \texttt{cb\_index}, \texttt{trees}, \texttt{branching}, \texttt{iterations}, \texttt{target\_precision}, \texttt{build\_weight}, \texttt{memory\_weight} and \texttt{sample\_fraction} have the same meaning as described in \ref{sec:flann::Index}. The \texttt{random\_seed} field contains the random seed useed to initialize the random number generator. The field \texttt{log\_level} controls the verbosity of the messages generated by the FLANN library functions. It can take the following values: \begin{Verbatim}[fontsize=\footnotesize] enum flann_log_level_t { FLANN_LOG_NONE = 0, FLANN_LOG_FATAL = 1, FLANN_LOG_ERROR = 2, FLANN_LOG_WARN = 3, FLANN_LOG_INFO = 4 }; \end{Verbatim} \end{description} \subsubsection{flann\_find\_nearest\_neighbors\_index()} \begin{Verbatim}[fontsize=\footnotesize,frame=single] int flann_find_nearest_neighbors_index(FLANN_INDEX index_id, float* testset, int trows, int* indices, float* dists, int nn, int checks, struct FLANNParameters* flann_params); \end{Verbatim} This function searches for the nearest neighbors of the \texttt{testset} points using an index already build and referenced by \texttt{index\_id}. The \texttt{testset} is a matrix stored in row-major format with \texttt{trows} rows and the same number of columns as the dimensionality of the points used to build the index. The function computes \texttt{nn} nearest neighbors for each point in the \texttt{testset} and stores them in the \texttt{indices} matrix (which is a $\rm{trows} \times \rm{nn}$ matrix stored in row-major format). The memory for the \texttt{result} matrix must be allocated before the \texttt{flann\_find\_nearest\_neighbors\_index()} function is called. The distances to the nearest neighbors found are stored in the \texttt{dists} matrix. The \texttt{checks} parameter specifies how many tree traversals should be performed during the search. \subsubsection{flann\_find\_nearest\_neighbors()} \begin{Verbatim}[fontsize=\footnotesize,frame=single] int flann_find_nearest_neighbors(float* dataset, int rows, int cols, float* testset, int trows, int* indices, float* dists, int nn, struct FLANNParameters* flann_params); \end{Verbatim} This function is similar to the \texttt{flann\_find\_nearest\_neighbors\_index()} function, but instread of using a previously constructed index, it constructs the index, does the nearest neighbor search and deletes the index in one step. \subsubsection{flann\_radius\_search()} \begin{Verbatim}[fontsize=\footnotesize,frame=single] int flann_radius_search(FLANN_INDEX index_ptr, float* query, /* query point */ int* indices, /* array for storing the indices */ float* dists, /* similar, but for storing distances */ int max_nn, /* size of arrays indices and dists */ float radius, /* search radius (squared radius for euclidian metric) */ int checks, /* number of features to check, sets the level of approximation */ FLANNParameters* flann_params); \end{Verbatim} This function performs a radius search to single query point. The indices of the neighbors found and the distances to them are stored in the \texttt{indices} and dists \texttt{arrays}. The \texttt{max\_nn} parameter sets the limit of the neighbors that will be returned (the size of the \texttt{indices} and \texttt{dists} arrays must be at least \texttt{max\_nn}). \subsubsection{flann\_save\_index()} \begin{Verbatim}[fontsize=\footnotesize,frame=single] int flann_save_index(flann_index_t index_id, char* filename); \end{Verbatim} This function saves an index to a file. The dataset for which the index was built is not saved with the index. \subsubsection{flann\_load\_index()} \begin{Verbatim}[fontsize=\footnotesize,frame=single] flann_index_t flann_load_index(char* filename, float* dataset, int rows, int cols); \end{Verbatim} This function loads a previously saved index from a file. Since the dataset is not saved with the index, it must be provided to this function. \subsubsection{flann\_free\_index()} \begin{Verbatim}[fontsize=\footnotesize,frame=single] int flann_free_index(FLANN_INDEX index_id, struct FLANNParameters* flann_params); \end{Verbatim} This function deletes a previously constructed index and frees all the memory used by it. \subsubsection{flann\_set\_distance\_type} \label{flann::setDistanceType} This function chooses the distance function to use when computing distances between data points. \begin{Verbatim}[fontsize=\footnotesize,frame=single] void flann_set_distance_type(enum flann_distance_t distance_type, int order); \end{Verbatim} \begin{description} \item[distance\_type] The distance type to use. Possible values are \begin{Verbatim}[fontsize=\footnotesize] enum flann_distance_t { FLANN_DIST_EUCLIDEAN = 1, // squared euclidean distance FLANN_DIST_MANHATTAN = 2, FLANN_DIST_MINKOWSKI = 3, FLANN_DIST_HIST_INTERSECT = 5, FlANN_DIST_HELLINGER = 6, FLANN_DIST_CHI_SQUARE = 7, // chi-square FLANN_DIST_KULLBACK_LEIBLER = 8, // kullback-leibler divergence }; \end{Verbatim} \item[order] Used in for the \texttt{FLANN\_DIST\_MINKOWSKI} distance type, to choose the order of the Minkowski distance. \end{description} \subsubsection{flann\_compute\_cluster\_centers()} Performs hierarchical clustering of a set of points (see \ref{flann::hierarchicalClustering}). \begin{Verbatim}[fontsize=\footnotesize,frame=single] int flann_compute_cluster_centers(float* dataset, int rows, int cols, int clusters, float* result, struct FLANNParameters* flann_params); \end{Verbatim} \bigskip See section \ref{sec:quickstart} for an example of how to use the C/C++ bindings. \subsection{Using FLANN from MATLAB} The FLANN library can be used from MATLAB through the following wrapper functions: \texttt{flann\_build\_index}, \texttt{flann\_search}, \texttt{flann\_save\_index}, \texttt{flann\_load\_index}, \texttt{flann\_set\_distance\_type} and \texttt{flann\_free\_index}. The \texttt{flann\_build\_index} function creates a search index from the dataset points, \texttt{flann\_search} uses this index to perform nearest-neighbor searches, \texttt{flann\_save\_index} and \texttt{flann\_load\_index} can be used to save and load an index to/from disk, \texttt{flann\_set\_distance\_type} is used to set the distance type to be used when building an index and \texttt{flann\_free\_index} deletes the index and releases the memory it uses. % Note that in the binary distribution of FLANN the MEX file is linked against % the shared version of FLANN (\texttt{flann.so} or \texttt{flann.dll}), so on Linux you must set the % LD\_LIBRARY\_PATH environment variable accordingly prior to starting MATLAB. On Windows is enough % to have \texttt{flann.dll} in the same directory with the MEX file. The following sections describe in more detail the FLANN matlab wrapper functions and show examples of how they may be used. \subsubsection{flann\_build\_index} \label{sec:flann_build_index} This function creates a search index from the initial dataset of points, index used later for fast nearest-neighbor searches in the dataset. \begin{Verbatim}[fontsize=\footnotesize,frame=single] [index, parameters, speedup] = flann_build_index(dataset, build_params); \end{Verbatim} The arguments passed to the \texttt{flann\_build\_index} function have the following meaning: \begin{description} \item [\texttt{dataset}] is a $d \times n$ matrix containing $n$ $d$-dimensional points, stored in a column-major order (one feature on each column) \item [\texttt{build\_params}] - is a MATLAB structure containing the parameters passed to the function. \end{description} The \texttt{build\_params} is used to specify the type of index to be built and the index parameters. These have a big impact on the performance of the new search index (nearest-neighbor search time) and on the time and memory required to build the index. The optimum parameter values depend on the dataset characteristics (number of dimensions, distribution of points in the dataset) and on the application domain (desired precision for the approximate nearest neighbor searches). The \texttt{build\_params} argument is a structure that contains one or more of the following fields: \begin{description} \item[\texttt{algorithm}] - the algorithm to use for building the index. The possible values are: \texttt{'linear'}, \texttt{'kdtree'}, \texttt{'kmeans'}, \texttt{'composite'} or \texttt{'autotuned'}. The \texttt{'linear'} option does not create any index, it uses brute-force search in the original dataset points, \texttt{'kdtree'} creates one or more randomized kd-trees, \texttt{'kmeans'} creates a hierarchical kmeans clustering tree, \texttt{'composite'} is a mix of both kdtree and kmeans trees and the \texttt{'autotuned'} automatically determines the best index and optimum parameters using a cross-validation technique. \vspace{0.5cm} \hspace{-1cm} \textbf{Autotuned index:} in case the algorithm field is \texttt{'autotuned'}, the following fields should also be present: \item[\texttt{target\_precision}] - is a number between 0 and 1 specifying the percentage of the approximate nearest-neighbor searches that return the exact nearest-neighbor. Using a higher value for this parameter gives more accurate results, but the search takes longer. The optimum value usually depends on the application. \item[\texttt{build\_weight}] - specifies the importance of the index build time raported to the nearest-neighbor search time. In some applications it's acceptable for the index build step to take a long time if the subsequent searches in the index can be performed very fast. In other applications it's required that the index be build as fast as possible even if that leads to slightly longer search times. (Default value: 0.01) \item[\texttt{memory\_weight}] - is used to specify the tradeoff between time (index build time and search time) and memory used by the index. A value less than 1 gives more importance to the time spent and a value greater than 1 gives more importance to the memory usage. \item[\texttt{sample\_fraction}] - is a number between 0 and 1 indicating what fraction of the dataset to use in the automatic parameter configuration algorithm. Running the algorithm on the full dataset gives the most accurate results, but for very large datasets can take longer than desired. In such case, using just a fraction of the data helps speeding up this algorithm, while still giving good approximations of the optimum parameters. \vspace{0.5cm} \hspace{-1cm} \textbf{Randomized kd-trees index:} in case the algorithm field is \texttt{'kdtree'}, the following fields should also be present: \item[\texttt{trees}] - the number of randomized kd-trees to create. \vspace{0.5cm} \hspace{-1cm} \textbf{Hierarchical k-means index:} in case the algorithm type is \texttt{'means'}, the following fields should also be present: \item[\texttt{branching}] - the branching factor to use for the hierarchical kmeans tree creation. While kdtree is always a binary tree, each node in the kmeans tree may have several branches depending on the value of this parameter. \item[\texttt{iterations}] - the maximum number of iterations to use in the kmeans clustering stage when building the kmeans tree. A value of -1 used here means that the kmeans clustering should be performed until convergence. \item[\texttt{centers\_init}] - the algorithm to use for selecting the initial centers when performing a kmeans clustering step. The possible values are 'random' (picks the initial cluster centers randomly), 'gonzales' (picks the initial centers using the Gonzales algorithm) and 'kmeanspp' (picks the initial centers using the algorithm suggested in \cite{arthur_kmeanspp_2007}). If this parameters is omitted, the default value is 'random'. \item[\texttt{cb\_index}] - this parameter (cluster boundary index) influences the way exploration is performed in the hierarchical kmeans tree. When \texttt{cb\_index} is zero the next kmeans domain to be explored is choosen to be the one with the closest center. A value greater then zero also takes into account the size of the domain. \vspace{0.5cm} \hspace{-1cm} \textbf{Composite index:} in case the algorithm type is \texttt{'composite'}, the fields from both randomized kd-tree index and hierarchical k-means index should be present. \end{description} The \texttt{flann\_build\_index} function returns the newly created \texttt{index}, the \texttt{parameters} used for creating the index and, if automatic configuration was used, an estimation of the \texttt{speedup} over linear search that is achieved when searching the index. Since the parameter estimation step is costly, it is possible to save the computed parameters and reuse them the next time an index is created from similar data points (coming from the same distribution). \subsubsection{flann\_search} This function performs nearest-neighbor searches using the index already created: \begin{Verbatim}[fontsize=\footnotesize,frame=single] [result, dists] = flann_search(index, testset, k, parameters); \end{Verbatim} The arguments required by this function are: \begin{description} \item[\texttt{index}] - the index returned by the \texttt{flann\_build\_index} function \item[\texttt{testset}] - a $d \times m$ matrix containing $m$ test points whose k-nearest-neighbors need to be found \item[\texttt{k}] - the number of nearest neighbors to be returned for each point from \texttt{testset} \item[\texttt{parameters}] - structure containing the search parameters. Currently it has only one member, \texttt{parameters.checks}, denoting the number of times the tree(s) in the index should be recursively traversed. A higher value for this parameter would give better search precision, but also take more time. If automatic configuration was used when the index was created, the number of checks required to achieve the specified precision is also computed. In such case, the parameters structure returned by the \texttt{flann\_build\_index} function can be passed directly to the \texttt{flann\_search} function. \end{description} The function returns two matrices, each of size $k \times m$. The first one contains, in which each column, the indexes (in the dataset matrix) of the $k$ nearest neighbors of the corresponding point from testset, while the second one contains the corresponding distances. The second matrix can be omitted when making the call if the distances to the nearest neighbors are not needed. For the case where a single search will be performed with each index, the \texttt{flann\_search} function accepts the dataset instead of the index as first argument, in which case the index is created searched and then deleted in one step. In this case the parameters structure passed to the \texttt{flann\_search} function must also contain the fields of the \texttt{build\_params} structure that would normally be passed to the \texttt{flann\_build\_index} function if the index was build separately. \begin{Verbatim} [result, dists] = flann_search(dataset, testset, k, parameters); \end{Verbatim} \subsubsection{flann\_save\_index} This function saves an index to a file so that it can be reused at a later time without the need to recompute it. Only the index will be saved to the file, not also the data points for which the index was created, so for the index to be reused the data points must be saved separately. \begin{Verbatim}[fontsize=\footnotesize,frame=single] flann_save_index(index, filename) \end{Verbatim} The argumenst required by this function are: \begin{description} \item[\texttt{index}] - the index to be saved, created by \texttt{flann\_build\_index} \item[\texttt{filename}] - the name of the file in which to save the index \end{description} \subsubsection{flann\_load\_index} This function loads a previously saved index from a file. It needs to be passed as a second parameter the dataset for which the index was created, as this is not saved together with the index. \begin{Verbatim}[fontsize=\footnotesize,frame=single] index = flann_load_index(filename, dataset) \end{Verbatim} The argumenst required by this function are: \begin{description} \item[\texttt{filename}] - the file from which to load the index \item[\texttt{dataset}] - the dataset for which the index was created \end{description} This function returns the index object. \subsubsection{flann\_set\_distance\_type} \label{matlab:flannSetDistanceType} This function chooses the distance function to use when computing distances between data points. \begin{Verbatim}[fontsize=\footnotesize,frame=single] flann_set_distance_type(type, order) \end{Verbatim} The argumenst required by this function are: \begin{description} \item[\texttt{type}] - the distance type to use. Possible values are: \texttt{'euclidean'}, \texttt{'manhattan'}, \texttt{'minkowski'}, \texttt{'max\_dist'} ($L\_{infinity}$ - distance type is not valid for kd-tree index type since it's not dimensionwise additive), \texttt{'hik'} (histogram intersection kernel), \texttt{'hellinger'},\texttt{'cs'} (chi-square) and \texttt{'kl'} (Kullback-Leibler). \item[\texttt{order}] - only used if distance type is \texttt{'minkowski'} and represents the order of the minkowski distance. \end{description} \subsubsection{flann\_free\_index} This function must be called to delete an index and release all the memory used by it: \begin{Verbatim}[fontsize=\footnotesize,frame=single] flann_free_index(index); \end{Verbatim} \subsubsection{Examples} Let's look at a few examples showing how the functions described above are used: \subsubsection{Example 1:} In this example the index is constructed using automatic parameter estimation, requesting 70\% as desired precision and using the default values for the build time and memory usage factors. The index is then used to search for the nearest-neighbors of the points in the testset matrix and finally the index is deleted. \begin{Verbatim}[fontsize=\footnotesize,frame=single] dataset = single(rand(128,10000)); testset = single(rand(128,1000)); build_params.algorithm = 'autotuned'; build_params.target_precision = 0.7; build_params.build_weight = 0.01; build_params.memory_weight = 0; [index, parameters] = flann_build_index(dataset, build_params); result = flann_search(index,testset,5,parameters); flann_free_index(index); \end{Verbatim} % \bibliographystyle{alpha} % \bibliography{references} \subsubsection{Example 2:} In this example the index constructed with the parameters specified manually. \begin{Verbatim}[fontsize=\footnotesize,frame=single] dataset = single(rand(128,10000)); testset = single(rand(128,1000)); index = flann_build_index(dataset,struct('algorithm','kdtree','trees',8)); result = flann_search(index,testset,5,struct('checks',128)); flann_free_index(index); \end{Verbatim} \subsubsection{Example 3:} In this example the index creation, searching and deletion are all performed in one step: \begin{Verbatim}[fontsize=\footnotesize,frame=single] dataset = single(rand(128,10000)); testset = single(rand(128,1000)); [result,dists] = flann_search(dataset,testset,5,struct('checks',128,'algorithm',... 'kmeans','branching',64,'iterations',5)); \end{Verbatim} \subsection{Using FLANN from python} The python bindings are automatically installed on the system with the FLANN library if the option \texttt{BUILD\_PYTHON\_BINDINGS} is enabled. You may need to set the \texttt{PYTHONPATH} to the location of the bindings if you installed FLANN in a non-standard location. The python bindings also require the numpy package to be installed. To use the python FLANN bindings the package \texttt{pyflann} must be imported (see the python example in section \ref{sec:quickstart}). This package contains a class called FLANN that handles the nearest-neighbor search operations. This class containg the following methods: \begin{description} \item [\texttt{def build\_index(self, dataset, **kwargs)}] :\\ This method builds and internally stores an index to be used for future nearest neighbor matchings. It erases any previously stored index, so in order to work with multiple indexes, multiple instances of the FLANN class must be used. The \texttt{dataset} argument must be a 2D numpy array or a matrix, stored in a row-major order (a feature on each row of the matrix). The rest of the arguments that can be passed to the method are the same as those used in the \texttt{build\_params} structure from section \ref{sec:flann_build_index}. Similar to the MATLAB version, the index can be created using manually specified parameters or the parameters can be automatically computed (by specifying the target\_precision, build\_weight and memory\_weight arguments). The method returns a dictionary containing the parameters used to construct the index. In case automatic parameter selection is used, the dictionary will also contain the number of checks required to achieve the desired target precision and an estimation of the speedup over linear search that the library will provide. \item [\texttt{def nn\_index(self, testset, num\_neighbors = 1, **kwargs)}] :\\ This method searches for the \texttt{num\_neighbors} nearest neighbors of each point in \texttt{testset} using the index computed by \texttt{build\_index}. Additionally, a parameter called checks, denoting the number of times the index tree(s) should be recursivelly searched, must be given. Example: \begin{Verbatim}[fontsize=\scriptsize,frame=single] from pyflann import * from numpy import * from numpy.random import * dataset = rand(10000, 128) testset = rand(1000, 128) flann = FLANN() params = flann.build_index(dataset, algorithm="autotuned", target_precision=0.9, log_level = "info"); print params result, dists = flann.nn_index(testset,5, checks=params["checks"]); \end{Verbatim} \item[\texttt{def nn(self, dataset, testset, num\_neighbors = 1, **kwargs)}]:\\ This method builds the index, performs the nearest neighbor search and deleted the index, all in one step. \item [\texttt{def save\_index(self, filename)}] :\\ This method saves the index to a file. The dataset form which the index was build is not saved. \item [\texttt{def load\_index(self, filename, pts)}] :\\ Load the index from a file. The dataset for which the index was build must also be provided since is not saved with the index. \item [\texttt{def set\_distance\_type(distance\_type, order = 0)}] :\\ This function (part of the pyflann module) sets the distance type to be used. See \ref{matlab:flannSetDistanceType} for possible values of the distance\_type. \end{description} See section \ref{sec:quickstart} for an example of how to use the Python bindings. \bibliographystyle{alpha} \bibliography{references} \end{document} flann-1.8.4-src/test/0000755000000000000000000000000012075346130013065 5ustar rootrootflann-1.8.4-src/test/flann_kmeans_test.cpp0000644000000000000000000000774712075346130017303 0ustar rootroot#include #include #include #include #include "flann_tests.h" using namespace flann; /** * Test fixture for SIFT 10K dataset */ class KMeans_SIFT10K : public DatasetTestFixture { protected: KMeans_SIFT10K() : DatasetTestFixture("sift10K.h5") {} }; TEST_F(KMeans_SIFT10K, TestSearch) { TestSearch >(data, flann::KMeansIndexParams(7, 3, FLANN_CENTERS_RANDOM, 0.4), query, indices, dists, knn, flann::SearchParams(128), 0.75, gt_indices); } TEST_F(KMeans_SIFT10K, TestAddIncremental) { TestAddIncremental >(data, flann::KMeansIndexParams(7, 3, FLANN_CENTERS_RANDOM, 0.4), query, indices, dists, knn, flann::SearchParams(110), 0.75, gt_indices); } TEST_F(KMeans_SIFT10K, TestAddIncremental2) { TestAddIncremental2 >(data, flann::KMeansIndexParams(7, 3, FLANN_CENTERS_RANDOM, 0.4), query, indices, dists, knn, flann::SearchParams(110), 0.75, gt_indices); } TEST_F(KMeans_SIFT10K, TestRemove) { TestRemove >(data, flann::KMeansIndexParams(7, 3, FLANN_CENTERS_RANDOM, 0.4), query, indices, dists, knn, flann::SearchParams(128)); } TEST_F(KMeans_SIFT10K, TestSave) { TestSave >(data, flann::KMeansIndexParams(7, 3, FLANN_CENTERS_RANDOM, 0.4), query, indices, dists, knn, flann::SearchParams(128), 0.75, gt_indices); } TEST_F(KMeans_SIFT10K, TestCopy) { TestCopy >(data, flann::KMeansIndexParams(7, 3, FLANN_CENTERS_RANDOM, 0.4), query, indices, dists, knn, flann::SearchParams(128), 0.75, gt_indices); } TEST_F(KMeans_SIFT10K, TestCopy2) { TestCopy2 > >(data, flann::KMeansIndexParams(7, 3, FLANN_CENTERS_RANDOM, 0.4), query, indices, dists, knn, flann::SearchParams(128), 0.75, gt_indices); } /** * Test fixture for SIFT 100K dataset */ class KMeans_SIFT100K : public DatasetTestFixture { protected: KMeans_SIFT100K() : DatasetTestFixture("sift100K.h5") {} }; TEST_F(KMeans_SIFT100K, TestSearch) { TestSearch >(data, flann::KMeansIndexParams(32, 11, FLANN_CENTERS_RANDOM, 0.4), query, indices, dists, knn, flann::SearchParams(96), 0.75, gt_indices); } TEST_F(KMeans_SIFT100K, TestAddIncremental) { TestAddIncremental >(data, flann::KMeansIndexParams(32, 11, FLANN_CENTERS_RANDOM, 0.4), query, indices, dists, knn, flann::SearchParams(128), 0.75, gt_indices); } TEST_F(KMeans_SIFT100K, TestAddIncremental2) { TestAddIncremental2 >(data, flann::KMeansIndexParams(32, 11, FLANN_CENTERS_RANDOM, 0.4), query, indices, dists, knn, flann::SearchParams(128), 0.75, gt_indices); } TEST_F(KMeans_SIFT100K, TestRemove) { TestRemove >(data, flann::KMeansIndexParams(32, 11, FLANN_CENTERS_RANDOM, 0.4), query, indices, dists, knn, flann::SearchParams(128) ); } TEST_F(KMeans_SIFT100K, TestSave) { TestSave >(data, flann::KMeansIndexParams(32, 11, FLANN_CENTERS_RANDOM, 0.4), query, indices, dists, knn, flann::SearchParams(96), 0.75, gt_indices); } /** * Test fixture for SIFT 10K dataset with byte feature elements */ class KMeans_SIFT10K_byte : public DatasetTestFixture { protected: KMeans_SIFT10K_byte() : DatasetTestFixture("sift10K_byte.h5") {} }; TEST_F(KMeans_SIFT10K_byte, TestSearch) { TestSearch >(data, flann::KMeansIndexParams(7, 3, FLANN_CENTERS_RANDOM, 0.4), query, indices, dists, knn, flann::SearchParams(128), 0.75, gt_indices); } class KMeans_SIFT100K_byte : public DatasetTestFixture { protected: KMeans_SIFT100K_byte() : DatasetTestFixture("sift100K_byte.h5") {} }; TEST_F(KMeans_SIFT100K_byte, TestSearch) { TestSearch >(data, flann::KMeansIndexParams(32, 11, FLANN_CENTERS_RANDOM, 0.4), query, indices, dists, knn, flann::SearchParams(80), 0.75, gt_indices); } int main(int argc, char** argv) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } flann-1.8.4-src/test/flann_autotuned_test.cpp0000644000000000000000000001226712075346130020026 0ustar rootroot#include #include #include #include #include #include "flann_tests.h" using namespace flann; class Autotuned_SIFT100K : public FLANNTestFixture { protected: flann::Matrix data; flann::Matrix query; flann::Matrix match; flann::Matrix dists; flann::Matrix indices; void SetUp() { dists = flann::Matrix(new float[1000*5], 1000, 5); indices = flann::Matrix(new size_t[1000*5], 1000, 5); printf("Reading test data..."); fflush(stdout); flann::load_from_file(data, "sift100K.h5","dataset"); flann::load_from_file(query,"sift100K.h5","query"); flann::load_from_file(match,"sift100K.h5","match"); printf("done\n"); } void TearDown() { delete[] data.ptr(); delete[] query.ptr(); delete[] match.ptr(); delete[] dists.ptr(); delete[] indices.ptr(); } }; TEST_F(Autotuned_SIFT100K, TestSearch) { flann::log_verbosity(FLANN_LOG_INFO); Index > index(data, flann::AutotunedIndexParams(0.8,0.01,0,0.1)); // 80% precision start_timer("Building autotuned index..."); index.buildIndex(); printf("done (%g seconds)\n", stop_timer()); index.save("autotuned.idx"); start_timer("Searching KNN..."); index.knnSearch(query, indices, dists, 5, flann::SearchParams(FLANN_CHECKS_AUTOTUNED) ); printf("done (%g seconds)\n", stop_timer()); float precision = compute_precision(match, indices); EXPECT_GE(precision, 0.75); printf("Precision: %g\n", precision); } TEST_F(Autotuned_SIFT100K, SavedTest) { float precision; // ------------------------------------- // autotuned index printf("Loading autotuned index\n"); flann::Index > autotuned_index(data, flann::SavedIndexParams("autotuned.idx")); const flann::IndexParams index_params = autotuned_index.getParameters(); printf("The index has the following parameters:\n"); flann::print_params(index_params); printf("Index type is: %d\n", autotuned_index.getType()); start_timer("Searching KNN..."); autotuned_index.knnSearch(query, indices, dists, 5, flann::SearchParams(-2) ); printf("done (%g seconds)\n", stop_timer()); precision = compute_precision(match, indices); EXPECT_GE(precision, 0.75); printf("Precision: %g\n", precision); } TEST_F(Autotuned_SIFT100K, TestCopy) { float precision; // ------------------------------------- // autotuned index printf("Loading autotuned index\n"); flann::Index > index(data, flann::SavedIndexParams("autotuned.idx")); start_timer("Searching KNN..."); index.knnSearch(query, indices, dists, 5, flann::SearchParams(-2) ); printf("done (%g seconds)\n", stop_timer()); precision = compute_precision(match, indices); EXPECT_GE(precision, 0.75); printf("Precision: %g\n", precision); // test copy constructor Index > index2(index); start_timer("Searching KNN..."); index2.knnSearch(query, indices, dists, 5, flann::SearchParams(-2) ); printf("done (%g seconds)\n", stop_timer()); float precision2 = compute_precision(match, indices); printf("Precision: %g\n", precision2); EXPECT_EQ(precision, precision2); // test assignment operator Index > index3(data, flann::LinearIndexParams()); index3 = index; start_timer("Searching KNN..."); index3.knnSearch(query, indices, dists, 5, flann::SearchParams(-2) ); printf("done (%g seconds)\n", stop_timer()); float precision3 = compute_precision(match, indices); printf("Precision: %g\n", precision3); EXPECT_EQ(precision, precision3); } TEST_F(Autotuned_SIFT100K, TestCopy2) { float precision; // ------------------------------------- // autotuned index printf("Loading autotuned index\n"); flann::AutotunedIndex > index(data); FILE* f = fopen("autotuned.idx", "r"); index.loadIndex(f); fclose(f); start_timer("Searching KNN..."); index.knnSearch(query, indices, dists, 5, flann::SearchParams(-2) ); printf("done (%g seconds)\n", stop_timer()); precision = compute_precision(match, indices); EXPECT_GE(precision, 0.75); printf("Precision: %g\n", precision); // test copy constructor AutotunedIndex > index2(index); start_timer("Searching KNN..."); index2.knnSearch(query, indices, dists, 5, flann::SearchParams(-2) ); printf("done (%g seconds)\n", stop_timer()); float precision2 = compute_precision(match, indices); printf("Precision: %g\n", precision2); EXPECT_EQ(precision, precision2); // test assignment operator AutotunedIndex > index3(data); index3 = index; start_timer("Searching KNN..."); index3.knnSearch(query, indices, dists, 5, flann::SearchParams(-2) ); printf("done (%g seconds)\n", stop_timer()); float precision3 = compute_precision(match, indices); printf("Precision: %g\n", precision3); EXPECT_EQ(precision, precision3); } int main(int argc, char** argv) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } flann-1.8.4-src/test/memusage_nn.py0000755000000000000000000000333412075346130015743 0ustar rootroot#!/usr/bin/env python import sys from os.path import * from pyflann import * from guppy import hpy from numpy.random import rand import os, gc _proc_status = '/proc/%d/status' % os.getpid() _scale = {'kB': 1024.0, 'mB': 1024.0*1024.0, 'KB': 1024.0, 'MB': 1024.0*1024.0} def _VmB(VmKey): '''Private. ''' global _proc_status, _scale # get pseudo file /proc//status try: t = open(_proc_status) v = t.read() t.close() except: return 0.0 # non-Linux? # get VmKey line e.g. 'VmRSS: 9999 kB\n ...' i = v.index(VmKey) v = v[i:].split(None, 3) # whitespace if len(v) < 3: return 0.0 # invalid format? # convert Vm value to bytes return float(v[1]) * _scale[v[2]] def memory(since=0.0): '''Return memory usage in bytes. ''' return _VmB('VmSize:') - since def resident(since=0.0): '''Return resident memory usage in bytes. ''' return _VmB('VmRSS:') - since def stacksize(since=0.0): '''Return stack size in bytes. ''' return _VmB('VmStk:') - since if __name__ == '__main__': print 'Profiling Memory usage for pyflann; CTRL-C to stop.' print 'Increasing total process memory, relative to the python memory, ' print 'implies a memory leak in the external libs.' print 'Increasing python memory implies a memory leak in the python code.' h = hpy() while True: s = str(h.heap()) print 'Python: %s; Process Total: %s' % (s[:s.find('\n')], memory()) X1 = rand(50000, 2) X2 = rand(50000, 2) pf = FLANN() nnlist = pf.nn(X1, X2) del X1 del X2 del nnlist del pf gc.collect() flann-1.8.4-src/test/test_nn_index.py0000755000000000000000000000352312075346130016306 0ustar rootroot#!/usr/bin/env python from pyflann import * from copy import copy from numpy import * from numpy.random import * import unittest class Test_PyFLANN_nn(unittest.TestCase): def setUp(self): self.nn = FLANN() class Test_PyFLANN_nn_index(unittest.TestCase): def testnn_index(self): dim = 10 N = 100 x = rand(N, dim) nn = FLANN() nn.build_index(x) nnidx, nndist = nn.nn_index(x) correct = all(nnidx == arange(N, dtype = index_type)) nn.delete_index() self.assertTrue(correct) def testnn_index_random_permute(self): numtests = 500 dim = 10 N = 100 nns = [None]*numtests x = [rand(N, dim) for i in range(numtests)] correct = ones(numtests, dtype=bool_) for i in permutation(numtests): nns[i] = FLANN() nns[i].build_index(x[i]) # For kicks if rand() < 0.5: nns[i].kmeans(x[i], 5) if rand() < 0.5: nns[i].nn(x[i], x[i]) for i in permutation(numtests): nnidx,nndist = nns[i].nn_index(x[i]) correct[i] = all(nnidx == arange(N, dtype = index_type)) for i in reversed(range(numtests)): if rand() < 0.5: nns[i].delete_index() else: del nns[i] self.assertTrue(all(correct)) def testnn_index_bad_index_call_noindex(self): nn = FLANN() self.assertRaises(FLANNException, lambda: nn.nn_index(rand(5,5))) def testnn_index_bad_index_call_delindex(self): nn = FLANN() nn.build_index(rand(5,5)) nn.delete_index() self.assertRaises(FLANNException, lambda: nn.nn_index(rand(5,5))) if __name__ == '__main__': unittest.main() flann-1.8.4-src/test/CMakeLists.txt0000644000000000000000000000541212075346130015627 0ustar rootroot add_custom_target(tests) add_custom_target(flann_gtests) add_custom_target(test) add_custom_target(flann_gtest) add_dependencies(tests flann_gtests) add_dependencies(test flann_gtest) add_dependencies(test tests) set(EXECUTABLE_OUTPUT_PATH ${TEST_OUTPUT_PATH}) if (PYTHON_EXECUTABLE) flann_download_test_data(sift10K.h5 0964a910946d2dd5fe28337507a8abc3) flann_download_test_data(sift10K_byte.h5 f835e0148df4618a81f67febfda2b4d0) flann_download_test_data(sift100K.h5 ae2b08f93f3d9f89f5d68566b0406102) flann_download_test_data(sift100K_byte.h5 b772255fd2044e9d2a5a0183953e4705) flann_download_test_data(cloud.h5 dfc77bad391a3ae739a9874f4a5dc0d7) flann_download_test_data(brief100K.h5 e1e781c0955917bc2f0a27b6344c2342) endif() if (GTEST_FOUND AND HDF5_FOUND) include_directories(${HDF5_INCLUDE_DIR}) set(TEST_LIBRARIES "${HDF5_LIBRARIES}") if (HDF5_IS_PARALLEL) set(TEST_LIBRARIES "${TEST_LIBRARIES};${MPI_LIBRARIES}") endif() flann_add_gtest(flann_linear_test flann_linear_test.cpp) target_link_libraries(flann_linear_test flann_cpp ${TEST_LIBRARIES}) flann_add_gtest(flann_kdtree_test flann_kdtree_test.cpp) target_link_libraries(flann_kdtree_test flann_cpp ${TEST_LIBRARIES}) flann_add_gtest(flann_kmeans_test flann_kmeans_test.cpp) target_link_libraries(flann_kmeans_test flann_cpp ${TEST_LIBRARIES}) flann_add_gtest(flann_kdtree_single_test flann_kdtree_single_test.cpp) target_link_libraries(flann_kdtree_single_test flann_cpp ${TEST_LIBRARIES}) flann_add_gtest(flann_hierarchical_test flann_hierarchical_test.cpp) target_link_libraries(flann_hierarchical_test flann_cpp ${TEST_LIBRARIES}) flann_add_gtest(flann_lsh_test flann_lsh_test.cpp) target_link_libraries(flann_lsh_test flann_cpp ${TEST_LIBRARIES}) flann_add_gtest(flann_autotuned_test flann_autotuned_test.cpp) target_link_libraries(flann_autotuned_test flann_cpp ${TEST_LIBRARIES}) if (OPENMP_FOUND) flann_add_gtest(flann_multithreaded_test flann_multithreaded_test.cpp) target_link_libraries(flann_multithreaded_test flann_cpp ${TEST_LIBRARIES}) endif() endif() if (GTEST_FOUND AND HDF5_FOUND AND BUILD_CUDA_LIB) set(CUDA_NVCC_FLAGS "${CUDA_NVCC_FLAGS};-Xcompiler;-fPIC;-arch=sm_13" ) if (NVCC_COMPILER_BINDIR) set(CUDA_NVCC_FLAGS "${CUDA_NVCC_FLAGS};--compiler-bindir=${NVCC_COMPILER_BINDIR}") endif() flann_add_cuda_gtest(flann_cuda_test flann_cuda_test.cu) target_link_libraries(flann_cuda_test flann_cpp ${HDF5_LIBRARIES} flann_cuda) endif() #---------- pyunit tests -------------- if (PYTHON_EXECUTABLE) flann_add_pyunit(test_nn.py) flann_add_pyunit(test_nn_index.py) flann_add_pyunit(test_index_save.py) flann_add_pyunit(test_nn_autotune.py) flann_add_pyunit(test_clustering.py) endif() flann-1.8.4-src/test/flann_cuda_test.cu0000644000000000000000000003721312075346130016555 0ustar rootroot#include #include #define FLANN_USE_CUDA #include #include #include #include #include #include using namespace flann; float compute_precision(const flann::Matrix& match, const flann::Matrix& indices) { int count = 0; assert(match.rows == indices.rows); size_t nn = std::min(match.cols, indices.cols); for (size_t i=0; i data; flann::Matrix query; flann::Matrix match; flann::Matrix dists; flann::Matrix indices; void SetUp() { printf("Reading test data..."); fflush(stdout); flann::load_from_file(data, "cloud.h5","dataset"); flann::load_from_file(query,"cloud.h5","query"); flann::load_from_file(match,"cloud.h5","indices"); dists = flann::Matrix(new float[query.rows*5], query.rows, 5); indices = flann::Matrix(new int[query.rows*5], query.rows, 5); printf("done\n"); } void TearDown() { delete[] data.ptr(); delete[] query.ptr(); delete[] match.ptr(); delete[] dists.ptr(); delete[] indices.ptr(); } }; TEST_F(Flann_3D, KDTreeSingleTest) { flann::Index > index(data, flann::KDTreeSingleIndexParams(12, false)); start_timer("Building kd-tree index..."); index.buildIndex(); printf("done (%g seconds)\n", stop_timer()); start_timer("Searching KNN..."); index.knnSearch(query, indices, dists, 5, flann::SearchParams(-1) ); printf("done (%g seconds)\n", stop_timer()); float precision = compute_precision(match, indices); EXPECT_GE(precision, 0.99); printf("Precision: %g\n", precision); } TEST_F(Flann_3D, KDTreeCudaTest) { flann::Index > index(data, flann::KDTreeCuda3dIndexParams()); start_timer("Building kd-tree index..."); index.buildIndex(); printf("done (%g seconds)\n", stop_timer()); start_timer("Searching KNN..."); index.knnSearch(query, indices, dists, 5, flann::SearchParams(-1) ); printf("done (%g seconds)\n", stop_timer()); float precision = compute_precision(match, indices); EXPECT_GE(precision, 0.99); printf("Precision: %g\n", precision); } class Flann_3D_Random_Cloud : public FLANNTestFixture { protected: flann::Matrix data; flann::Matrix query; flann::Matrix dists; flann::Matrix indices; flann::Matrix gt_dists; flann::Matrix gt_indices; void SetUp() { const int n_points=10000; printf("creating random point cloud (%d points)...", n_points); data = flann::Matrix(new float[n_points*3], n_points, 3); srand(1); for( int i=0; i(new float[n_points*3], n_points, 3); for( int i=0; i(new float[query.rows*max_nn], query.rows, max_nn); gt_dists = flann::Matrix(new float[query.rows*max_nn], query.rows, max_nn); indices = flann::Matrix(new int[query.rows*max_nn], query.rows, max_nn); gt_indices = flann::Matrix(new int[query.rows*max_nn], query.rows, max_nn); Index > index(data, flann::LinearIndexParams()); start_timer("Building linear index..."); index.buildIndex(); printf("done (%g seconds)\n", stop_timer()); start_timer("Searching KNN..."); index.knnSearch(data, gt_indices, gt_dists, max_nn, flann::SearchParams() ); // for( int i=0; i > index(data, flann::KDTreeCuda3dIndexParams()); start_timer("Building kd-tree index..."); index.buildIndex(); printf("done (%g seconds)\n", stop_timer()); start_timer("Searching KNN..."); indices.cols=1; dists.cols=1; index.knnSearch(query, indices, dists, 1, flann::SearchParams() ); printf("done (%g seconds)\n", stop_timer()); // float precision = compute_precision(gt_indices,indices); float precision = computePrecisionDiscrete(gt_dists,dists, 0); EXPECT_GE(precision, 0.99); printf("Precision: %g\n", precision); } TEST_F(Flann_3D_Random_Cloud, Test4NN) { flann::Index > index(data, flann::KDTreeCuda3dIndexParams()); start_timer("Building kd-tree index..."); index.buildIndex(); printf("done (%g seconds)\n", stop_timer()); start_timer("Searching KNN..."); indices.cols=4; dists.cols=4; index.knnSearch(query, indices, dists, 4, flann::SearchParams() ); printf("done (%g seconds)\n", stop_timer()); // float precision = compute_precision(gt_indices,indices); float precision = computePrecisionDiscrete(gt_dists,dists, 1e-08); EXPECT_GE(precision, 0.99); printf("Precision: %g\n", precision); } TEST_F(Flann_3D_Random_Cloud, Test4NNGpuBuffers) { thrust::host_vector data_host(data.rows); for( int i=0; i data_device = data_host; thrust::host_vector query_host(data.rows); for( int i=0; i query_device = query_host; flann::Matrix data_device_matrix( (float*)thrust::raw_pointer_cast(&data_device[0]),data.rows,3,4*4); flann::Matrix query_device_matrix( (float*)thrust::raw_pointer_cast(&query_device[0]),data.rows,3,4*4); flann::KDTreeCuda3dIndexParams index_params; index_params["input_is_gpu_float4"]=true; flann::Index > index(data_device_matrix, index_params); start_timer("Building kd-tree index..."); index.buildIndex(); printf("done (%g seconds)\n", stop_timer()); thrust::device_vector indices_device(query.rows*4); thrust::device_vector dists_device(query.rows*4); flann::Matrix indices_device_matrix( (int*)thrust::raw_pointer_cast(&indices_device[0]),query.rows,4); flann::Matrix dists_device_matrix( (float*)thrust::raw_pointer_cast(&dists_device[0]),query.rows,4); start_timer("Searching KNN..."); indices.cols=4; dists.cols=4; flann::SearchParams sp; sp.matrices_in_gpu_ram=true; index.knnSearch(query_device_matrix, indices_device_matrix, dists_device_matrix, 4, sp ); printf("done (%g seconds)\n", stop_timer()); flann::Matrix indices_host( new int[ query.rows*4],query.rows,4 ); flann::Matrix dists_host( new float[ query.rows*4],query.rows,4 ); thrust::copy( dists_device.begin(), dists_device.end(), dists_host.ptr() ); thrust::copy( indices_device.begin(), indices_device.end(), indices_host.ptr() ); // float precision = compute_precision(gt_indices,indices); float precision = computePrecisionDiscrete(gt_dists,dists_host, 1e-08); EXPECT_GE(precision, 0.99); printf("Precision: %g\n", precision); delete [] indices_host.ptr(); delete [] dists_host.ptr(); } TEST_F(Flann_3D_Random_Cloud, TestRadiusSearchVector) { flann::Index > index(data, flann::KDTreeCuda3dIndexParams()); start_timer("Building kd-tree index..."); index.buildIndex(); printf("done (%g seconds)\n", stop_timer()); float r = 0.1; std::vector< std::vector > indices; std::vector< std::vector > dists; start_timer("Radius search, r=0.1"); index.radiusSearch( query, indices,dists, r*r, flann::SearchParams() ); printf("done (%g seconds)", stop_timer()); start_timer("verifying results..."); for( int i=0; i > index(data, flann::KDTreeCuda3dIndexParams()); start_timer("Building kd-tree index..."); index.buildIndex(); printf("done (%g seconds)\n", stop_timer()); float r = 0.05; flann::Matrix counts( new int[query.rows], query.rows,1); flann::Matrix dummy( 0,0,0 ); flann::SearchParams counting_params; counting_params.max_neighbors=0; start_timer("counting neighbors..."); index.radiusSearch( query, counts,dummy, r*r, counting_params ); printf("done (%g seconds)", stop_timer()); int max_neighbors=0; for( int i=0; i 0 ); flann::Matrix indices( new int[max_neighbors*query.rows], query.rows, max_neighbors ); flann::Matrix dists( new float[max_neighbors*query.rows], query.rows, max_neighbors ); start_timer("Radius search, r=0.05"); index.radiusSearch( query, indices,dists, r*r, flann::SearchParams() ); printf("done (%g seconds)", stop_timer()); start_timer("verifying results..."); for( int i=0; i > index(data, flann::KDTreeCuda3dIndexParams()); start_timer("Building kd-tree index..."); index.buildIndex(); printf("done (%g seconds)\n", stop_timer()); float r = 0.02; std::vector< std::vector > indices; std::vector< std::vector > dists; start_timer("Radius search, r=0.02..."); index.radiusSearch( query, indices,dists, r*r, flann::SearchParams() ); printf("done (%g seconds)\n", stop_timer()); start_timer("verifying results..."); for( int i=0; i #include #include #include #include "flann_tests.h" using namespace flann; class LshIndex_Brief100K : public FLANNTestFixture { protected: typedef flann::Hamming Distance; typedef Distance::ElementType ElementType; typedef Distance::ResultType DistanceType; flann::Matrix data; flann::Matrix query; flann::Matrix gt_indices; flann::Matrix dists; flann::Matrix gt_dists; flann::Matrix indices; unsigned int k_nn_; void SetUp() { k_nn_ = 3; printf("Reading test data..."); fflush(stdout); flann::load_from_file(data, "brief100K.h5", "dataset"); flann::load_from_file(query, "brief100K.h5", "query"); dists = flann::Matrix(new DistanceType[query.rows * k_nn_], query.rows, k_nn_); indices = flann::Matrix(new size_t[query.rows * k_nn_], query.rows, k_nn_); printf("done\n"); // The matches are bogus so we compute them the hard way // flann::load_from_file(match,"brief100K.h5","indices"); flann::Index index(data, flann::LinearIndexParams()); index.buildIndex(); start_timer("Searching KNN for ground truth..."); gt_indices = flann::Matrix(new size_t[query.rows * k_nn_], query.rows, k_nn_); gt_dists = flann::Matrix(new DistanceType[query.rows * k_nn_], query.rows, k_nn_); index.knnSearch(query, gt_indices, gt_dists, k_nn_, flann::SearchParams(-1)); printf("done (%g seconds)\n", stop_timer()); } void TearDown() { delete[] data.ptr(); delete[] query.ptr(); delete[] gt_indices.ptr(); delete[] gt_dists.ptr(); delete[] dists.ptr(); delete[] indices.ptr(); } }; TEST_F(LshIndex_Brief100K, TestSearch) { TestSearch(data, flann::LshIndexParams(12, 20, 2), query, indices, dists, k_nn_, flann::SearchParams(-1), 0.9, gt_indices, gt_dists); } TEST_F(LshIndex_Brief100K, TestAddIncremental) { TestAddIncremental(data, flann::LshIndexParams(12, 20, 2), query, indices, dists, k_nn_, flann::SearchParams(-1), 0.9, gt_indices, gt_dists); } TEST_F(LshIndex_Brief100K, TestIncremental2) { TestAddIncremental2(data, flann::LshIndexParams(12, 20, 2), query, indices, dists, k_nn_, flann::SearchParams(-1), 0.9, gt_indices, gt_dists); } TEST_F(LshIndex_Brief100K, TestRemove) { TestRemove(data, flann::LshIndexParams(12, 20, 2), query, indices, dists, k_nn_, flann::SearchParams(-1)); } TEST_F(LshIndex_Brief100K, TestSave) { TestSave(data, flann::LshIndexParams(12, 20, 2), query, indices, dists, k_nn_, flann::SearchParams(-1), 0.9, gt_indices, gt_dists); } TEST_F(LshIndex_Brief100K, TestCopy) { TestCopy(data, flann::LshIndexParams(12, 20, 2), query, indices, dists, k_nn_, flann::SearchParams(-1), 0.9, gt_indices, gt_dists); } TEST_F(LshIndex_Brief100K, TestCopy2) { TestCopy2 >(data, flann::LshIndexParams(12, 20, 2), query, indices, dists, k_nn_, flann::SearchParams(-1), 0.9, gt_indices, gt_dists); } int main(int argc, char** argv) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } flann-1.8.4-src/test/test_clustering.py0000755000000000000000000000565512075346130016673 0ustar rootroot#!/usr/bin/env python import sys from os.path import * from pyflann import * import os from copy import copy from numpy import * from numpy.random import * import unittest, time class Test_PyFLANN_clustering(unittest.TestCase): def setUp(self): self.nn = FLANN(iterations=11) ################################################################################ def test_Rand(self): x = rand(100, 10000) nK = 10 centroids = self.nn.kmeans(x, nK) self.assertTrue(len(centroids) == nK) def test2d_small(self): self.__nd_random_clustering_test(2,2) def test3d_small(self): self.__nd_random_clustering_test(3,3) def test4d_small(self): self.__nd_random_clustering_test(4,4) def test3d_large(self): self.__nd_random_clustering_test(3,3, 1000) def test10d_large(self): self.__nd_random_clustering_test(10,2,10) def test500d(self): self.__nd_random_clustering_test(500,2, 10) def __nd_random_clustering_test(self, dim, N, dup=1): """ Make a set of random points, then pass the same ones to the query points. Each point should be closest to itself. """ seed(0) x = rand(N, dim) xc = concatenate(tuple([x for i in range(dup)])) if dup > 1: xc += randn(xc.shape[0], xc.shape[1])*0.000001/dim rnseed = int(time.time()) centroids = self.nn.kmeans(xc[permutation(len(xc))], N, centers_init = "random", random_seed=2) mindists = array([[ sum((d1-d2)**2) for d1 in x] for d2 in centroids]).min(0) #print mindists for m in mindists: self.assertAlmostEqual(m, 0.0, 1) rnseed = int(time.time()) centroids = self.nn.kmeans(xc[permutation(len(xc))], N, centers_init = "gonzales", random_seed=2) mindists = array([[ sum((d1-d2)**2) for d1 in x] for d2 in centroids]).min(0) #print mindists for m in mindists: self.assertAlmostEqual(m, 0.0, 1) centroids = self.nn.kmeans(xc[permutation(len(xc))], N, centers_init = "kmeanspp", random_seed=2) mindists = array([[ sum((d1-d2)**2) for d1 in x] for d2 in centroids]).min(0) #print mindists for m in mindists: self.assertAlmostEqual(m, 0.0, 1) def testrandomnumber_same(self): data = rand(1000,2) # Random, so we can get a lot of local minima rnseed = int(time.time()) cl1 = self.nn.kmeans(data, 50, random_seed = rnseed) cl2 = self.nn.kmeans(data, 50, random_seed = rnseed) self.assertTrue(all(cl1 == cl2)) def testrandnumber_different(self): data = rand(1000,100) # Random, so we can get a lot of local minima rnseed = int(time.time()) cl1 = self.nn.kmeans(data, 50, random_seed = rnseed) cl2 = self.nn.kmeans(data, 50) self.assertTrue(any(cl1 != cl2)) if __name__ == '__main__': unittest.main() flann-1.8.4-src/test/flann_hierarchical_test.cpp0000644000000000000000000000630512075346130020430 0ustar rootroot#include #include #include #include #include "flann_tests.h" using namespace flann; class HierarchicalIndex_Brief100K : public FLANNTestFixture { protected: typedef flann::Hamming Distance; typedef Distance::ElementType ElementType; typedef Distance::ResultType DistanceType; flann::Matrix data; flann::Matrix query; flann::Matrix gt_indices; flann::Matrix dists; flann::Matrix gt_dists; flann::Matrix indices; unsigned int k_nn_; void SetUp() { k_nn_ = 3; printf("Reading test data..."); fflush(stdout); flann::load_from_file(data, "brief100K.h5", "dataset"); flann::load_from_file(query, "brief100K.h5", "query"); printf("done\n"); flann::Index index(data, flann::LinearIndexParams()); index.buildIndex(); start_timer("Searching KNN for ground truth..."); gt_indices = flann::Matrix(new size_t[query.rows * k_nn_], query.rows, k_nn_); gt_dists = flann::Matrix(new DistanceType[query.rows * k_nn_], query.rows, k_nn_); index.knnSearch(query, gt_indices, gt_dists, k_nn_, flann::SearchParams(-1)); printf("done (%g seconds)\n", stop_timer()); dists = flann::Matrix(new DistanceType[query.rows * k_nn_], query.rows, k_nn_); indices = flann::Matrix(new size_t[query.rows * k_nn_], query.rows, k_nn_); } void TearDown() { delete[] data.ptr(); delete[] query.ptr(); delete[] dists.ptr(); delete[] indices.ptr(); delete[] gt_indices.ptr(); delete[] gt_dists.ptr(); } }; TEST_F(HierarchicalIndex_Brief100K, TestSearch) { TestSearch(data, flann::HierarchicalClusteringIndexParams(), query, indices, dists, k_nn_, flann::SearchParams(2000), 0.9, gt_indices, gt_dists); } TEST_F(HierarchicalIndex_Brief100K, TestAddIncremental) { TestAddIncremental(data, flann::HierarchicalClusteringIndexParams(), query, indices, dists, k_nn_, flann::SearchParams(2000), 0.87, gt_indices, gt_dists); } TEST_F(HierarchicalIndex_Brief100K, TestAddIncremental2) { TestAddIncremental2(data, flann::HierarchicalClusteringIndexParams(), query, indices, dists, k_nn_, flann::SearchParams(2000), 0.87, gt_indices, gt_dists); } TEST_F(HierarchicalIndex_Brief100K, TestRemove) { TestRemove(data, flann::HierarchicalClusteringIndexParams(), query, indices, dists, k_nn_, flann::SearchParams(2000)); } TEST_F(HierarchicalIndex_Brief100K, TestSave) { TestSave(data, flann::HierarchicalClusteringIndexParams(), query, indices, dists, k_nn_, flann::SearchParams(2000), 0.87, gt_indices, gt_dists); } TEST_F(HierarchicalIndex_Brief100K, TestCopy) { TestCopy(data, flann::HierarchicalClusteringIndexParams(), query, indices, dists, k_nn_, flann::SearchParams(2000), 0.87, gt_indices, gt_dists); } TEST_F(HierarchicalIndex_Brief100K, TestCopy2) { TestCopy2 >(data, flann::HierarchicalClusteringIndexParams(), query, indices, dists, k_nn_, flann::SearchParams(2000), 0.87, gt_indices, gt_dists); } int main(int argc, char** argv) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } flann-1.8.4-src/test/memusage_clustering.py0000755000000000000000000000325212075346130017506 0ustar rootroot#!/usr/bin/env python import sys from os.path import * from pyflann import * from guppy import hpy from numpy.random import rand import os, gc _proc_status = '/proc/%d/status' % os.getpid() _scale = {'kB': 1024.0, 'mB': 1024.0*1024.0, 'KB': 1024.0, 'MB': 1024.0*1024.0} def _VmB(VmKey): '''Private. ''' global _proc_status, _scale # get pseudo file /proc//status try: t = open(_proc_status) v = t.read() t.close() except: return 0.0 # non-Linux? # get VmKey line e.g. 'VmRSS: 9999 kB\n ...' i = v.index(VmKey) v = v[i:].split(None, 3) # whitespace if len(v) < 3: return 0.0 # invalid format? # convert Vm value to bytes return float(v[1]) * _scale[v[2]] def memory(since=0.0): '''Return memory usage in bytes. ''' return _VmB('VmSize:') - since def resident(since=0.0): '''Return resident memory usage in bytes. ''' return _VmB('VmRSS:') - since def stacksize(since=0.0): '''Return stack size in bytes. ''' return _VmB('VmStk:') - since if __name__ == '__main__': print 'Profiling Memory usage for pyflann; CTRL-C to stop.' print 'Increasing total process memory, relative to the python memory, ' print 'implies a memory leak in the external libs.' print 'Increasing python memory implies a memory leak in the python code.' h = hpy() while True: s = str(h.heap()) print 'Python: %s; Process Total: %s' % (s[:s.find('\n')], memory()) X = rand(30000, 2) pf = FLANN() cl = pf.kmeans(X, 20) del X del cl del pf gc.collect() flann-1.8.4-src/test/flann_multithreaded_test.cpp0000644000000000000000000002210512075346130020641 0ustar rootroot#include #include #include #include #include using namespace flann; template float compute_precision(const flann::Matrix& match, const flann::Matrix& indices) { int count = 0; assert(match.rows == indices.rows); size_t nn = std::min(match.cols, indices.cols); for (size_t i=0; i data_; flann::Matrix query_; flann::Matrix match_; flann::Matrix dists_; flann::Matrix indices_; int knn_; void SetUp() { knn_ = 5; printf("Reading test data..."); fflush(stdout); flann::load_from_file(data_, "cloud.h5","dataset"); flann::load_from_file(query_,"cloud.h5","query"); flann::load_from_file(match_,"cloud.h5","match"); dists_ = flann::Matrix(new float[query_.rows*knn_], query_.rows, knn_); indices_ = flann::Matrix(new size_t[query_.rows*knn_], query_.rows, knn_); printf("done\n"); } void TearDown() { delete[] data_.ptr(); delete[] query_.ptr(); delete[] match_.ptr(); delete[] dists_.ptr(); delete[] indices_.ptr(); } }; TEST_F(FlannTest, HandlesSingleCoreSearch) { flann::Index > index(data_, flann::KDTreeSingleIndexParams(50, false)); start_timer("Building kd-tree index..."); index.buildIndex(); printf("done (%g seconds)\n", stop_timer()); int checks = -1; float eps = 0.0f; bool sorted = true; int cores = 1; start_timer("Searching KNN..."); SearchParams params(checks,eps,sorted); params.cores = cores; index.knnSearch(query_, indices_, dists_, knn_, params); printf("done (%g seconds)\n", stop_timer()); float precision = compute_precision(match_, indices_); EXPECT_GE(precision, 0.99); printf("Precision: %g\n", precision); } TEST_F(FlannTest, HandlesMultiCoreSearch) { flann::Index > index(data_, flann::KDTreeSingleIndexParams(50, false)); start_timer("Building kd-tree index..."); index.buildIndex(); printf("done (%g seconds)\n", stop_timer()); int checks = -1; float eps = 0.0f; bool sorted = true; int cores = 2; start_timer("Searching KNN..."); SearchParams params(checks,eps,sorted); params.cores = cores; index.knnSearch(query_, indices_, dists_, knn_, params); printf("done (%g seconds)\n", stop_timer()); float precision = compute_precision(match_, indices_); EXPECT_GE(precision, 0.99); printf("Precision: %g\n", precision); } /* Test Fixture which loads the cloud.h5 cloud as data and query matrix and holds two dists and indices matrices for comparing single and multi core KNN search */ class FlannCompareKnnTest : public FLANNTestFixture { protected: flann::Matrix data_; flann::Matrix query_; flann::Matrix dists_single_; flann::Matrix indices_single_; flann::Matrix dists_multi_; flann::Matrix indices_multi_; int knn_; void SetUp() { knn_ = 5; printf("Reading test data..."); fflush(stdout); flann::load_from_file(data_, "cloud.h5","dataset"); flann::load_from_file(query_,"cloud.h5","query"); dists_single_ = flann::Matrix(new float[query_.rows*knn_], query_.rows, knn_); indices_single_ = flann::Matrix(new size_t[query_.rows*knn_], query_.rows, knn_); dists_multi_ = flann::Matrix(new float[query_.rows*knn_], query_.rows, knn_); indices_multi_ = flann::Matrix(new size_t[query_.rows*knn_], query_.rows, knn_); printf("done\n"); } void TearDown() { delete[] data_.ptr(); delete[] query_.ptr(); delete[] dists_single_.ptr(); delete[] indices_single_.ptr(); delete[] dists_multi_.ptr(); delete[] indices_multi_.ptr(); } }; TEST_F(FlannCompareKnnTest, CompareMultiSingleCoreKnnSearch) { flann::Index > index(data_, flann::KDTreeSingleIndexParams(50, false)); start_timer("Building kd-tree index..."); index.buildIndex(); printf("done (%g seconds)\n", stop_timer()); SearchParams params; params.checks = -1; params.eps = 0.0f; params.sorted = true; start_timer("Searching KNN (single core)..."); params.cores = 1; int single_neighbor_count = index.knnSearch(query_, indices_single_, dists_single_, knn_, params); printf("done (%g seconds)\n", stop_timer()); start_timer("Searching KNN (multi core)..."); params.cores = 0; int multi_neighbor_count = index.knnSearch(query_, indices_multi_, dists_multi_, knn_, params); printf("done (%g seconds)\n", stop_timer()); EXPECT_EQ(single_neighbor_count, multi_neighbor_count); printf("Checking results...\n"); float precision = compute_precision(indices_single_, indices_multi_); EXPECT_GE(precision, 0.99); printf("Precision: %g\n", precision); } /* Test Fixture which loads the cloud.h5 cloud as data and query matrix and holds two dists and indices matrices for comparing single and multi core radius search */ class FlannCompareRadiusTest : public FLANNTestFixture { protected: flann::Matrix data_; flann::Matrix query_; flann::Matrix dists_single_; flann::Matrix indices_single_; flann::Matrix dists_multi_; flann::Matrix indices_multi_; float radius_; void SetUp() { radius_ = 0.1f; printf("Reading test data..."); fflush(stdout); flann::load_from_file(data_, "cloud.h5","dataset"); flann::load_from_file(query_,"cloud.h5","query"); int reserve_size = data_.rows / 1000; dists_single_ = flann::Matrix(new float[query_.rows*reserve_size], query_.rows, reserve_size); indices_single_ = flann::Matrix(new int[query_.rows*reserve_size], query_.rows, reserve_size); dists_multi_ = flann::Matrix(new float[query_.rows*reserve_size], query_.rows, reserve_size); indices_multi_ = flann::Matrix(new int[query_.rows*reserve_size], query_.rows, reserve_size); printf("done\n"); } void TearDown() { delete[] data_.ptr(); delete[] query_.ptr(); delete[] dists_single_.ptr(); delete[] indices_single_.ptr(); delete[] dists_multi_.ptr(); delete[] indices_multi_.ptr(); } void runTest(const flann::Index >& index, SearchParams params) { start_timer("Searching Radius (single core)..."); params.cores = 1; int single_neighbor_count = index.radiusSearch(query_, indices_single_, dists_single_, radius_, params); printf("done (%g seconds)\n", stop_timer()); start_timer("Searching Radius (multi core)..."); params.cores = 0; int multi_neighbor_count = index.radiusSearch(query_, indices_multi_, dists_multi_, radius_, params); printf("done (%g seconds)\n", stop_timer()); EXPECT_EQ(single_neighbor_count, multi_neighbor_count); printf("Checking results...\n"); float precision = compute_precision(indices_single_, indices_multi_); EXPECT_GE(precision, 0.99); printf("Precision: %g\n", precision); } }; TEST_F(FlannCompareRadiusTest, CompareMultiSingleCoreRadiusSearchSorted) { flann::Index > index(data_, flann::KDTreeSingleIndexParams(50, false)); start_timer("Building kd-tree index..."); index.buildIndex(); printf("done (%g seconds)\n", stop_timer()); SearchParams params; params.checks = -1; params.eps = 0.0f; params.sorted = true; runTest(index, params); } TEST_F(FlannCompareRadiusTest, CompareMultiSingleCoreRadiusSearchUnsorted) { flann::Index > index(data_, flann::KDTreeSingleIndexParams(50, false)); start_timer("Building kd-tree index..."); index.buildIndex(); printf("done (%g seconds)\n", stop_timer()); SearchParams params; params.checks = -1; params.eps = 0.0f; params.sorted = false; runTest(index, params); } int main(int argc, char** argv) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } flann-1.8.4-src/test/test_nn_autotune.py0000755000000000000000000000455512075346130017051 0ustar rootroot#!/usr/bin/env python import sys from os.path import * import os from pyflann import * from copy import copy from numpy import * from numpy.random import * import unittest class Test_PyFLANN_nn(unittest.TestCase): def setUp(self): self.nn = FLANN(log_level="warning") ################################################################################ # The typical def test_nn_2d_10pt(self): self.__nd_random_test_autotune(2, 2) def test_nn_autotune_2d_1000pt(self): self.__nd_random_test_autotune(2, 1000) def test_nn_autotune_100d_1000pt(self): self.__nd_random_test_autotune(100, 1000) def test_nn_autotune_500d_100pt(self): self.__nd_random_test_autotune(500, 100) # # ########################################################################################## # # Stress it should handle # def test_nn_stress_1d_1pt_kmeans_autotune(self): self.__nd_random_test_autotune(1, 1) def __ensure_list(self,arg): if type(arg)!=list: return [arg] else: return arg def __nd_random_test_autotune(self, dim, N, num_neighbors = 1, **kwargs): """ Make a set of random points, then pass the same ones to the query points. Each point should be closest to itself. """ seed(0) x = rand(N, dim) xq = rand(N, dim) perm = permutation(N) # compute ground truth nearest neighbors gt_idx, gt_dist = self.nn.nn(x,xq, algorithm='linear', num_neighbors=num_neighbors) for tp in [0.70, 0.80, 0.90]: nidx,ndist = self.nn.nn(x, xq, algorithm='autotuned', sample_fraction=1.0, num_neighbors = num_neighbors, target_precision = tp, checks=-2, **kwargs) correctness = 0.0 for i in range(N): l1 = self.__ensure_list(nidx[i]) l2 = self.__ensure_list(gt_idx[i]) correctness += float(len(set(l1).intersection(l2)))/num_neighbors correctness /= N self.assertTrue(correctness >= tp*0.9, 'failed #1: targ_prec=%f, N=%d,correctness=%f' % (tp, N, correctness)) if __name__ == '__main__': unittest.main() flann-1.8.4-src/test/test_index_save.py0000755000000000000000000000650712075346130016636 0ustar rootroot#!/usr/bin/env python from pyflann import * from copy import copy from numpy import * from numpy.random import * import unittest class Test_PyFLANN_nn(unittest.TestCase): def setUp(self): self.nn = FLANN() class Test_PyFLANN_nn_index(unittest.TestCase): def testnn_index_save_kdtree_1(self): self.run_nn_index_save_perturbed(64,1000, algorithm="kdtree", trees=1) def testnn_index_save_kdtree_4(self): self.run_nn_index_save_perturbed(64,1000, algorithm="kdtree", trees=4) def testnn_index_save_kdtree_10(self): self.run_nn_index_save_perturbed(64,1000, algorithm="kdtree", trees=10) def testnn_index_save_kmeans_2(self): self.run_nn_index_save_perturbed(64,1000, algorithm="kmeans", branching=2, iterations=11) def testnn_index_save_kmeans_16(self): self.run_nn_index_save_perturbed(64,1000, algorithm="kmeans", branching=16, iterations=11) def testnn_index_save_kmeans_32(self): self.run_nn_index_save_perturbed(64,1000, algorithm="kmeans", branching=32, iterations=11) def testnn_index_save_kmeans_64(self): self.run_nn_index_save_perturbed(64,1000, algorithm="kmeans", branching=64, iterations=11) def testnn__save_kdtree_1(self): self.run_nn_index_save_rand(64,10000,1000, algorithm="kdtree", trees=1, checks=128) def testnn__save_kdtree_4(self): self.run_nn_index_save_rand(64,10000,1000, algorithm="kdtree", trees=4, checks=128) def testnn__save_kdtree_10(self): self.run_nn_index_save_rand(64,10000,1000, algorithm="kdtree", trees=10, checks=128) def testnn__save_kmeans_2(self): self.run_nn_index_save_rand(64,1000,1000, algorithm="kmeans", branching=2, iterations=11, checks=64) def testnn__save_kmeans_8(self): self.run_nn_index_save_rand(64,10000,1000, algorithm="kmeans", branching=8, iterations=11, checks=32) def testnn__save_kmeans_16(self): self.run_nn_index_save_rand(64,10000,1000, algorithm="kmeans", branching=16, iterations=11, checks=40) def testnn__save_kmeans_32(self): self.run_nn_index_save_rand(64,10000,1000, algorithm="kmeans", branching=32, iterations=11, checks=56) def run_nn_index_save_perturbed(self, dim, N, **kwargs): x = rand(N, dim) nn = FLANN() nn.build_index(x, **kwargs) nn.save_index("index.dat") nn.delete_index(); nn = FLANN() nn.load_index("index.dat",x) x_query = x + randn(x.shape[0], x.shape[1])*0.0001/dim nnidx, nndist = nn.nn_index(x_query) correct = all(nnidx == arange(N, dtype = index_type)) nn.delete_index() self.assertTrue(correct) def run_nn_index_save_rand(self, dim, N, Nq, **kwargs): x = rand(N, dim) x_query = rand(Nq,dim) # build index, search and delete it nn = FLANN() nn.build_index(x, **kwargs) nnidx, nndist = nn.nn_index(x_query, checks=kwargs["checks"]) nn.save_index("index.dat") del nn # now reload index and search again nn = FLANN() nn.load_index("index.dat",x) nnidx2, nndist2 = nn.nn_index(x_query, checks=kwargs["checks"]) del nn correct = all(nnidx == nnidx2) self.assertTrue(correct) if __name__ == '__main__': unittest.main() flann-1.8.4-src/test/flann_kdtree_test.cpp0000644000000000000000000000657412075346130017300 0ustar rootroot#include #include #include #include #include "flann_tests.h" using namespace flann; /** * Test fixture for SIFT 10K dataset */ class KDTree_SIFT10K : public DatasetTestFixture { protected: KDTree_SIFT10K() : DatasetTestFixture("sift10K.h5") {} }; TEST_F(KDTree_SIFT10K, TestSearch) { TestSearch >(data, flann::KDTreeIndexParams(4), query, indices, dists, knn, flann::SearchParams(256), 0.75, gt_indices); } TEST_F(KDTree_SIFT10K, TestAddIncremental) { TestAddIncremental >(data, flann::KDTreeIndexParams(4), query, indices, dists, knn, flann::SearchParams(256), 0.75, gt_indices); } TEST_F(KDTree_SIFT10K, TestAddIncremental2) { TestAddIncremental2 >(data, flann::KDTreeIndexParams(4), query, indices, dists, knn, flann::SearchParams(256), 0.75, gt_indices); } TEST_F(KDTree_SIFT10K, TestRemove) { TestRemove >(data, flann::KDTreeIndexParams(4), query, indices, dists, knn, flann::SearchParams(256) ); } TEST_F(KDTree_SIFT10K, TestSave) { TestSave >(data, flann::KDTreeIndexParams(4), query, indices, dists, knn, flann::SearchParams(256), 0.75, gt_indices); } TEST_F(KDTree_SIFT10K, TestCopy) { TestCopy >(data, flann::KDTreeIndexParams(4), query, indices, dists, knn, flann::SearchParams(256), 0.75, gt_indices); } TEST_F(KDTree_SIFT10K, TestCopy2) { TestCopy2 > >(data, flann::KDTreeIndexParams(4), query, indices, dists, knn, flann::SearchParams(256), 0.75, gt_indices); } /** * Test fixture for SIFT 100K dataset */ class KDTree_SIFT100K : public DatasetTestFixture { protected: KDTree_SIFT100K() : DatasetTestFixture("sift100K.h5") {} }; TEST_F(KDTree_SIFT100K, TestSearch) { TestSearch >(data, flann::KDTreeIndexParams(4), query, indices, dists, knn, flann::SearchParams(128), 0.75, gt_indices); } TEST_F(KDTree_SIFT100K, TestAddIncremental) { TestAddIncremental >(data, flann::KDTreeIndexParams(4), query, indices, dists, knn, flann::SearchParams(256), 0.75, gt_indices); } TEST_F(KDTree_SIFT100K, TestAddIncremental2) { TestAddIncremental2 >(data, flann::KDTreeIndexParams(4), query, indices, dists, knn, flann::SearchParams(256), 0.75, gt_indices); } TEST_F(KDTree_SIFT100K, TestRemove) { TestRemove >(data, flann::KDTreeIndexParams(4), query, indices, dists, knn, flann::SearchParams(128) ); } /** * Test fixture for SIFT 10K dataset with byte feature elements */ class KDTree_SIFT10K_byte : public DatasetTestFixture { protected: KDTree_SIFT10K_byte() : DatasetTestFixture("sift10K_byte.h5") {} }; TEST_F(KDTree_SIFT10K_byte, TestSearch) { TestSearch >(data, flann::KDTreeIndexParams(4), query, indices, dists, knn, flann::SearchParams(256), 0.75, gt_indices); } class KDTree_SIFT100K_byte : public DatasetTestFixture { protected: KDTree_SIFT100K_byte() : DatasetTestFixture("sift100K_byte.h5") {} }; TEST_F(KDTree_SIFT100K_byte, TestSearch) { TestSearch >(data, flann::KDTreeIndexParams(4), query, indices, dists, knn, flann::SearchParams(128), 0.75, gt_indices); } int main(int argc, char** argv) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } flann-1.8.4-src/test/flann_kdtree_single_test.cpp0000644000000000000000000002355712075346130020641 0ustar rootroot#include #include #include #include #include "flann_tests.h" using namespace flann; class KDTreeSingle :public DatasetTestFixture { protected: KDTreeSingle() : DatasetTestFixture("cloud.h5") {} }; TEST_F(KDTreeSingle, TestSearch) { TestSearch >(data, flann::KDTreeSingleIndexParams(12, false), query, indices, dists, knn, flann::SearchParams(-1), 0.99, gt_indices); } TEST_F(KDTreeSingle, TestSearchPadded) { flann::Matrix data_padded; flann::load_from_file(data_padded, "cloud.h5", "dataset_padded"); flann::Matrix data2(data_padded.ptr(), data_padded.rows, 3, data_padded.cols*sizeof(float)); TestSearch >(data2, flann::KDTreeSingleIndexParams(12, false), query, indices, dists, knn, flann::SearchParams(-1), 0.99, gt_indices); delete[] data_padded.ptr(); } TEST_F(KDTreeSingle, TestAddIncremental) { TestAddIncremental >(data, flann::KDTreeSingleIndexParams(12, false), query, indices, dists, knn, flann::SearchParams(-1), 0.99, gt_indices); } TEST_F(KDTreeSingle, TestAddIncremental2) { TestAddIncremental2 >(data, flann::KDTreeSingleIndexParams(12, false), query, indices, dists, knn, flann::SearchParams(-1), 0.99, gt_indices); } TEST_F(KDTreeSingle, TestRemove) { TestRemove >(data, flann::KDTreeSingleIndexParams(12, false), query, indices, dists, knn, flann::SearchParams(-1)); } TEST_F(KDTreeSingle, TestSave) { TestSave >(data, flann::KDTreeSingleIndexParams(12, false), query, indices, dists, knn, flann::SearchParams(-1), 0.99, gt_indices); } TEST_F(KDTreeSingle, TestSearchReorder) { TestSearch >(data, flann::KDTreeSingleIndexParams(12, true), query, indices, dists, knn, flann::SearchParams(-1), 0.99, gt_indices); } TEST_F(KDTreeSingle, TestSaveReorder) { TestSave >(data, flann::KDTreeSingleIndexParams(12, true), query, indices, dists, knn, flann::SearchParams(-1), 0.99, gt_indices); } TEST_F(KDTreeSingle, TestCopy) { TestCopy >(data, flann::KDTreeSingleIndexParams(12, false), query, indices, dists, knn, flann::SearchParams(-1), 0.99, gt_indices); // repeat tests with reorder=true TestCopy >(data, flann::KDTreeSingleIndexParams(12, true), query, indices, dists, knn, flann::SearchParams(-1), 0.99, gt_indices); } TEST_F(KDTreeSingle, TestCopy2) { TestCopy2 > >(data, flann::KDTreeSingleIndexParams(12, false), query, indices, dists, knn, flann::SearchParams(-1), 0.99, gt_indices); // repeat tests with reorder=true TestCopy2 > >(data, flann::KDTreeSingleIndexParams(12, true), query, indices, dists, knn, flann::SearchParams(-1), 0.99, gt_indices); } TEST_F(KDTreeSingle, TestNoNeighbours) { flann::Index > index(data, flann::KDTreeSingleIndexParams(12, false)); start_timer("Building kd-tree index..."); index.buildIndex(); printf("done (%g seconds)\n", stop_timer()); start_timer("Searching KNN..."); index.knnSearch(query, indices, dists, knn, flann::SearchParams(-1) ); printf("done (%g seconds)\n", stop_timer()); float min_dist = dists[0][0]; for (size_t i=0;i::infinity()); } std::vector > indices2; std::vector > dists2; start_timer("Searching radius smaller than minimum distance..."); int count2 = index.radiusSearch(query, indices2, dists2, min_dist/2, flann::SearchParams(-1) ); printf("done (%g seconds)\n", stop_timer()); EXPECT_EQ(0,count2); for (size_t i=0;ix=x; this->y=y; this->z=z;} float x,y,z; }; class KDTreeSinglePointCloud : public FLANNTestFixture { public: void SetUp() { float resolution = 0.1f; for (float z = -0.5f; z <= 0.5f; z += resolution) for (float y = -0.5f; y <= 0.5f; y += resolution) for (float x = -0.5f; x <= 0.5f; x += resolution) cloud_.push_back (MyPoint (x, y, z)); cloud_mat_ = flann::Matrix(&cloud_[0].x, cloud_.size(), 3); srand(static_cast (time (NULL))); // Randomly create a new point cloud for (size_t i = 0; i < 640*480; ++i) cloud_big_.push_back (MyPoint (static_cast (1024 * rand () / (RAND_MAX + 1.0)), static_cast (1024 * rand () / (RAND_MAX + 1.0)), static_cast (1024 * rand () / (RAND_MAX + 1.0)))); cloud_big_mat_ = flann::Matrix(&cloud_big_[0].x, cloud_big_.size(), 3); } std::vector cloud_; flann::Matrix cloud_mat_; std::vector cloud_big_; flann::Matrix cloud_big_mat_; }; TEST_F(KDTreeSinglePointCloud, TestRadiusSearch) { std::vector > k_indices; std::vector > k_distances; { flann::Index > index(cloud_mat_, flann::KDTreeSingleIndexParams(12, false)); index.buildIndex(); L2_Simple euclideanDistance; MyPoint test_point(0.0f, 0.0f, 0.0f); flann::Matrix test_mat(&test_point.x, 1, 3); double max_dist = 0.15*0.15; std::set brute_force_result; for (unsigned int i=0; i::iterator brute_force_result_it = brute_force_result.find (k_indices[0][i]); bool ok = brute_force_result_it != brute_force_result.end (); //if (!ok) cerr << k_indices[i] << " is not correct...\n"; //else cerr << k_indices[i] << " is correct...\n"; EXPECT_EQ (ok, true); if (ok) brute_force_result.erase (brute_force_result_it); } //for (set::const_iterator it=brute_force_result.begin(); it!=brute_force_result.end(); ++it) //cerr << "FLANN missed "<<*it<<"\n"; bool error = brute_force_result.size () > 0; //if (error) cerr << "Missed too many neighbors!\n"; EXPECT_EQ (error, false); } { flann::Index > index(flann::KDTreeSingleIndexParams(15)); index.buildIndex(cloud_big_mat_); start_timer("radiusSearch..."); flann::SearchParams params(-1); index.radiusSearch(cloud_big_mat_, k_indices, k_distances, 0.1*0.1, params); printf("done (%g seconds)\n", stop_timer()); } { flann::Index > index(flann::KDTreeSingleIndexParams(15)); index.buildIndex(cloud_big_mat_); start_timer("radiusSearch (max neighbors in radius)..."); flann::SearchParams params(-1); params.max_neighbors = 10; index.radiusSearch(cloud_big_mat_, k_indices, k_distances, 0.1*0.1, params); printf("done (%g seconds)\n", stop_timer()); } { flann::Index > index(flann::KDTreeSingleIndexParams(15)); index.buildIndex(cloud_big_mat_); start_timer("radiusSearch (unsorted results)..."); flann::SearchParams params(-1); params.sorted = false; index.radiusSearch(cloud_big_mat_, k_indices, k_distances, 0.1*0.1, params); printf("done (%g seconds)\n", stop_timer()); } } TEST_F(KDTreeSinglePointCloud, TestKNearestSearch) { unsigned int no_of_neighbors = 20; { flann::Index > index(cloud_mat_, flann::KDTreeSingleIndexParams(12, false)); index.buildIndex(); L2_Simple euclideanDistance; MyPoint test_point (0.01f, 0.01f, 0.01f); flann::Matrix test_mat(&test_point.x, 1, 3); std:: multimap sorted_brute_force_result; for (size_t i = 0; i < cloud_.size (); ++i) { float distance = euclideanDistance (cloud_mat_[i], test_mat[0],3); sorted_brute_force_result.insert (std::make_pair (distance, static_cast (i))); } float max_dist = 0.0f; unsigned int counter = 0; for (std::multimap::iterator it = sorted_brute_force_result.begin (); it != sorted_brute_force_result.end () && counter < no_of_neighbors; ++it) { max_dist = std::max (max_dist, it->first); ++counter; } std::vector< std::vector > k_indices(1); k_indices[0].resize (no_of_neighbors); std::vector > k_distances(1); k_distances[0].resize (no_of_neighbors); index.knnSearch(test_mat, k_indices, k_distances, no_of_neighbors, flann::SearchParams(-1)); //if (k_indices.size() != no_of_neighbors) cerr << "Found "< > index(flann::KDTreeSingleIndexParams(15)); index.buildIndex(cloud_big_mat_); start_timer("K nearest neighbour search..."); flann::SearchParams params(-1); params.sorted = false; std::vector > k_indices; std::vector > k_distances; index.knnSearch (cloud_big_mat_, k_indices, k_distances, no_of_neighbors, params); printf("done (%g seconds)\n", stop_timer()); } } int main(int argc, char** argv) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } flann-1.8.4-src/test/test_nn.py0000755000000000000000000000765512075346130015131 0ustar rootroot#!/usr/bin/env python import sys from os.path import * import os from pyflann import * from copy import copy from numpy import * from numpy.random import * import unittest class Test_PyFLANN_nn(unittest.TestCase): def setUp(self): self.nn = FLANN() ################################################################################ # The typical def test_nn_2d_10pt_kmeans(self): self.__nd_random_test(2, 2, algorithm='kdtree') def test_nn_2d_1000pt_kmeans(self): self.__nd_random_test(2, 1000, algorithm='kmeans') def test_nn_100d_1000pt_kmeans(self): self.__nd_random_test(100, 1000, algorithm='kmeans') def test_nn_500d_100pt_kmeans(self): self.__nd_random_test(500, 100, algorithm='kmeans') def test_nn_2d_1000pt_kdtree(self): self.__nd_random_test(2, 1000, algorithm='kdtree') def test_nn_100d_1000pt_kdtree(self): self.__nd_random_test(100, 1000, algorithm='kdtree') def test_nn_500d_100pt_kdtree(self): self.__nd_random_test(500, 100, algorithm='kdtree') def test_nn_2d_1000pt_linear(self): self.__nd_random_test(2, 1000, algorithm='linear') def test_nn_100d_50pt_linear(self): self.__nd_random_test(100, 50, algorithm='linear') def test_nn_2d_1000pt_composite(self): self.__nd_random_test(2, 1000, algorithm='composite') def test_nn_100d_1000pt_composite(self): self.__nd_random_test(100, 1000, algorithm='composite') def test_nn_500d_100pt_composite(self): self.__nd_random_test(500, 100, algorithm='composite') def test_nn_multtrees_2d_1000pt_kmeans(self): self.__nd_random_test(2, 1000, algorithm='kmeans', trees=8) def test_nn_multtrees_100d_1000pt_kmeans(self): self.__nd_random_test(100, 1000, algorithm='kmeans', trees=8) def test_nn_multtrees_500d_100pt_kmeans(self): self.__nd_random_test(500, 100, algorithm='kmeans', trees=8) ########################################################################################## # Stress it should handle def test_nn_stress_1d_1pt_kmeans(self): self.__nd_random_test(1, 1, algorithm='kmeans') def test_nn_stress_1d_1pt_linear(self): self.__nd_random_test(1, 1, algorithm='linear') def test_nn_stress_1d_1pt_kdtree(self): self.__nd_random_test(1, 1, algorithm='kdtree') def test_nn_stress_1d_1pt_composite(self): self.__nd_random_test(1, 1, algorithm='composite') def __nd_random_test(self, dim, N, type=float32, num_neighbors = 10, **kwargs): """ Make a set of random points, then pass the same ones to the query points. Each point should be closest to itself. """ seed(0) x = array(rand(N, dim), dtype=type) perm = permutation(N) idx,dists = self.nn.nn(x, x[perm], **kwargs) self.assertTrue(all(idx == perm)) # Make sure it's okay if we do make all the points equal x_mult_nn = concatenate([x for i in range(num_neighbors)]) nidx,ndists = self.nn.nn(x_mult_nn, x, num_neighbors = num_neighbors, **kwargs) correctness = 0.0 for i in range(N): correctness += float(len(set(nidx[i]).intersection([i + n*N for n in range(num_neighbors)])))/num_neighbors self.assertTrue(correctness / N >= 0.99, 'failed #1: N=%d,correctness=%f' % (N, correctness/N)) # now what happens if they are slightly off x_mult_nn += randn(x_mult_nn.shape[0], x_mult_nn.shape[1])*0.0001/dim n2idx,n2dists = self.nn.nn(x_mult_nn, x, num_neighbors = num_neighbors, **kwargs) for i in range(N): correctness += float(len(set(n2idx[i]).intersection([i + n*N for n in range(num_neighbors)])))/num_neighbors self.assertTrue(correctness / N >= 0.99, 'failed #2: N=%d,correctness=%f' % (N, correctness/N)) if __name__ == '__main__': unittest.main() flann-1.8.4-src/test/flann_linear_test.cpp0000644000000000000000000000462012075346130017262 0ustar rootroot#include #include #include #include #include "flann_tests.h" using namespace flann; /** * Test fixture for SIFT 10K dataset */ class Linear_SIFT10K : public DatasetTestFixture { protected: Linear_SIFT10K() : DatasetTestFixture("sift10K.h5") {} }; TEST_F(Linear_SIFT10K, TestSearch) { TestSearch >(data, flann::LinearIndexParams(), query, indices, dists, knn, flann::SearchParams(0), 1.0, gt_indices); } TEST_F(Linear_SIFT10K, TestRemove) { TestRemove >(data, flann::LinearIndexParams(), query, indices, dists, knn, flann::SearchParams(0)); } TEST_F(Linear_SIFT10K, TestSave) { TestSave >(data, flann::LinearIndexParams(), query, indices, dists, knn, flann::SearchParams(0), 1.0, gt_indices); } TEST_F(Linear_SIFT10K, TestCopy) { TestCopy >(data, flann::LinearIndexParams(), query, indices, dists, knn, flann::SearchParams(0), 1.0, gt_indices); } TEST_F(Linear_SIFT10K, TestCopy2) { TestCopy >(data, flann::LinearIndexParams(), query, indices, dists, knn, flann::SearchParams(0), 1.0, gt_indices); } /** * Test fixture for SIFT 100K dataset */ class Linear_SIFT100K : public DatasetTestFixture { protected: Linear_SIFT100K() : DatasetTestFixture("sift100K.h5") {} }; TEST_F(Linear_SIFT100K, TestSearch) { TestSearch >(data, flann::LinearIndexParams(), query, indices, dists, knn, flann::SearchParams(0), 1.0, gt_indices); } /** * Test fixture for SIFT 10K dataset with byte feature elements */ class Linear_SIFT10K_byte : public DatasetTestFixture { protected: Linear_SIFT10K_byte() : DatasetTestFixture("sift10K_byte.h5") {} }; TEST_F(Linear_SIFT10K_byte, Linear) { TestSearch >(data, flann::LinearIndexParams(), query, indices, dists, knn, flann::SearchParams(0), 1.0, gt_indices); } class Linear_SIFT100K_byte : public DatasetTestFixture { protected: Linear_SIFT100K_byte() : DatasetTestFixture("sift100K_byte.h5") {} }; TEST_F(Linear_SIFT100K_byte, TestSearch) { TestSearch >(data, flann::LinearIndexParams(), query, indices, dists, knn, flann::SearchParams(0), 1.0, gt_indices); } int main(int argc, char** argv) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } flann-1.8.4-src/test/flann_tests.h0000644000000000000000000005327412075346130015571 0ustar rootroot/*********************************************************************** * Software License Agreement (BSD License) * * Copyright 2008-2012 Marius Muja (mariusm@cs.ubc.ca). All rights reserved. * Copyright 2008-2012 David G. Lowe (lowe@cs.ubc.ca). All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #ifndef FLANN_TESTS_H_ #define FLANN_TESTS_H_ #include #include #include template float compute_precision(const flann::Matrix& match, const flann::Matrix& indices) { int count = 0; assert(match.rows == indices.rows); size_t nn = std::min(match.cols, indices.cols); for (size_t i=0; i float computePrecisionDiscrete(const flann::Matrix& gt_dists, const flann::Matrix& dists) { int count = 0; assert(gt_dists.rows == dists.rows); size_t nn = std::min(gt_dists.cols, dists.cols); std::vector gt_sorted_dists(nn), sorted_dists(nn), intersection(nn); for (size_t i = 0; i < gt_dists.rows; ++i) { std::copy(gt_dists[i], gt_dists[i] + nn, gt_sorted_dists.begin()); std::sort(gt_sorted_dists.begin(), gt_sorted_dists.end()); std::copy(dists[i], dists[i] + nn, sorted_dists.begin()); std::sort(sorted_dists.begin(), sorted_dists.end()); typename std::vector::iterator end = std::set_intersection(gt_sorted_dists.begin(), gt_sorted_dists.end(), sorted_dists.begin(), sorted_dists.end(), intersection.begin()); count += (end - intersection.begin()); } return float(count) / (nn * gt_dists.rows); } const char* index_type_to_name(flann_algorithm_t index_type) { switch (index_type) { case FLANN_INDEX_LINEAR: return "linear"; case FLANN_INDEX_KDTREE: return "randomized kd-tree"; case FLANN_INDEX_KMEANS: return "k-means"; case FLANN_INDEX_COMPOSITE: return "composite"; case FLANN_INDEX_KDTREE_SINGLE: return "single kd-tree"; case FLANN_INDEX_HIERARCHICAL: return "hierarchical"; case FLANN_INDEX_LSH: return "LSH"; #ifdef FLANN_USE_CUDA case FLANN_INDEX_KDTREE_CUDA: return "kd-tree CUDA"; #endif case FLANN_INDEX_SAVED: return "saved"; case FLANN_INDEX_AUTOTUNED: return "autotuned"; default: return "(unknown)"; } } class FLANNTestFixture : public ::testing::Test { protected: clock_t start_time_; void start_timer(const std::string& message = "") { if (!message.empty()) { printf("%s", message.c_str()); fflush(stdout); } start_time_ = clock(); } double stop_timer() { return double(clock()-start_time_)/CLOCKS_PER_SEC; } template void TestSearch(const flann::Matrix& data, const flann::IndexParams& index_params, const flann::Matrix& query, flann::Matrix& indices, flann::Matrix& dists, size_t knn, const flann::SearchParams& search_params, float expected_precision, const flann::Matrix& gt_indices, const flann::Matrix& gt_dists = flann::Matrix()) { flann::seed_random(0); Index index(data, index_params); char message[256]; const char* index_name = index_type_to_name(index.getType()); sprintf(message, "Building %s index... ", index_name); start_timer( message ); index.buildIndex(); printf("done (%g seconds)\n", stop_timer()); start_timer("Searching KNN..."); index.knnSearch(query, indices, dists, knn, search_params ); printf("done (%g seconds)\n", stop_timer()); float precision; if (gt_dists.ptr()==NULL) { precision = compute_precision(gt_indices, indices); } else { precision = computePrecisionDiscrete(gt_dists, dists); } EXPECT_GE(precision, expected_precision); printf("Precision: %g\n", precision); } template void TestAddIncremental(const flann::Matrix& data, const flann::IndexParams& index_params, const flann::Matrix& query, flann::Matrix& indices, flann::Matrix& dists, size_t knn, const flann::SearchParams& search_params, float expected_precision, flann::Matrix& gt_indices, const flann::Matrix& gt_dists = flann::Matrix()) { flann::seed_random(0); size_t size1 = data.rows/2-1; size_t size2 = data.rows-size1; Matrix data1(data[0], size1, data.cols); Matrix data2(data[size1], size2, data.cols); Index index(data1, index_params); char message[256]; const char* index_name = index_type_to_name(index.getType()); sprintf(message, "Building %s index... ", index_name); start_timer( message ); index.buildIndex(); EXPECT_EQ(index.size(), data1.rows); index.addPoints(data2); printf("done (%g seconds)\n", stop_timer()); EXPECT_EQ(index.size(), data.rows); start_timer("Searching KNN..."); index.knnSearch(query, indices, dists, knn, search_params); printf("done (%g seconds)\n", stop_timer()); float precision; if (gt_dists.ptr()==NULL) { precision = compute_precision(gt_indices, indices); } else { precision = computePrecisionDiscrete(gt_dists, dists); } EXPECT_GE(precision, expected_precision); printf("Precision: %g\n", precision); for (size_t i=0;i index2(data, flann::SavedIndexParams("test_saved_index.idx")); index2.buildIndex(); EXPECT_EQ(index2.size(), data.rows); flann::Matrix indices2(new size_t[query.rows*knn], query.rows, knn); flann::Matrix dists2(new typename Distance::ResultType[query.rows*knn], query.rows, knn); start_timer("Searching KNN after saving and reloading index..."); index2.knnSearch(query, indices2, dists2, knn, search_params ); printf("done (%g seconds)\n", stop_timer()); for (size_t i=0;i void TestAddIncremental2(const flann::Matrix& data, const flann::IndexParams& index_params, const flann::Matrix& query, flann::Matrix& indices, flann::Matrix& dists, size_t knn, const flann::SearchParams& search_params, float expected_precision, flann::Matrix& gt_indices, const flann::Matrix& gt_dists = flann::Matrix()) { flann::seed_random(0); size_t size1 = data.rows/2+1; size_t size2 = data.rows-size1; Matrix data1(data[0], size1, data.cols); Matrix data2(data[size1], size2, data.cols); Index index(data1, index_params); char message[256]; const char* index_name = index_type_to_name(index.getType()); sprintf(message, "Building %s index... ", index_name); start_timer( message ); index.buildIndex(); EXPECT_EQ(index.size(), data1.rows); index.addPoints(data2); printf("done (%g seconds)\n", stop_timer()); EXPECT_EQ(index.size(), data.rows); start_timer("Searching KNN..."); index.knnSearch(query, indices, dists, knn, search_params); printf("done (%g seconds)\n", stop_timer()); float precision; if (gt_dists.ptr()==NULL) { precision = compute_precision(gt_indices, indices); } else { precision = computePrecisionDiscrete(gt_dists, dists); } EXPECT_GE(precision, expected_precision); printf("Precision: %g\n", precision); for (size_t i=0;i index2(data, flann::SavedIndexParams("test_saved_index.idx")); index2.buildIndex(); EXPECT_EQ(index2.size(), data.rows); flann::Matrix indices2(new size_t[query.rows*knn], query.rows, knn); flann::Matrix dists2(new typename Distance::ResultType[query.rows*knn], query.rows, knn); start_timer("Searching KNN after saving and reloading index..."); index2.knnSearch(query, indices2, dists2, knn, search_params ); printf("done (%g seconds)\n", stop_timer()); for (size_t i=0;i void TestSave(const flann::Matrix& data, const flann::IndexParams& index_params, const flann::Matrix& query, flann::Matrix& indices, flann::Matrix& dists, size_t knn, const flann::SearchParams& search_params, float expected_precision, flann::Matrix& gt_indices, const flann::Matrix& gt_dists = flann::Matrix()) { flann::seed_random(0); Index index(data, index_params); char message[256]; const char* index_name = index_type_to_name(index.getType()); sprintf(message, "Building %s index... ", index_name); start_timer( message ); index.buildIndex(); printf("done (%g seconds)\n", stop_timer()); EXPECT_EQ(index.size(), data.rows); start_timer("Searching KNN..."); index.knnSearch(query, indices, dists, knn, search_params ); printf("done (%g seconds)\n", stop_timer()); float precision; if (gt_dists.ptr()==NULL) { precision = compute_precision(gt_indices, indices); } else { precision = computePrecisionDiscrete(gt_dists, dists); } printf("Precision: %g\n", precision); EXPECT_GE(precision, expected_precision); printf("Saving index\n"); index.save("test_saved_index.idx"); printf("Loading index\n"); Index index2(data, flann::SavedIndexParams("test_saved_index.idx")); index2.buildIndex(); EXPECT_EQ(index2.size(), data.rows); flann::Matrix indices2(new size_t[query.rows*knn], query.rows, knn); flann::Matrix dists2(new typename Distance::ResultType[query.rows*knn], query.rows, knn); start_timer("Searching KNN after saving and reloading index..."); index2.knnSearch(query, indices2, dists2, knn, search_params ); printf("done (%g seconds)\n", stop_timer()); float precision2; if (gt_dists.ptr()==NULL) { precision2 = compute_precision(gt_indices, indices2); } else { precision2 = computePrecisionDiscrete(gt_dists, dists2); } printf("Precision: %g\n", precision2); EXPECT_EQ(precision, precision2); for (size_t i=0;i void TestCopy(const flann::Matrix& data, const flann::IndexParams& index_params, const flann::Matrix& query, flann::Matrix& indices, flann::Matrix& dists, size_t knn, const flann::SearchParams& search_params, float expected_precision, flann::Matrix& gt_indices, const flann::Matrix& gt_dists = flann::Matrix()) { flann::seed_random(0); Index index(data, index_params); char message[256]; const char* index_name = index_type_to_name(index.getType()); sprintf(message, "Building %s index... ", index_name); start_timer( message ); index.buildIndex(); printf("done (%g seconds)\n", stop_timer()); start_timer("Searching KNN..."); index.knnSearch(query, indices, dists, knn, search_params ); printf("done (%g seconds)\n", stop_timer()); float precision; if (gt_dists.ptr()==NULL) { precision = compute_precision(gt_indices, indices); } else { precision = computePrecisionDiscrete(gt_dists, dists); } printf("Precision: %g\n", precision); EXPECT_GE(precision, expected_precision); // test copy constructor Index index2(index); start_timer("Searching KNN..."); index2.knnSearch(query, indices, dists, knn, search_params ); printf("done (%g seconds)\n", stop_timer()); float precision2; if (gt_dists.ptr()==NULL) { precision2 = compute_precision(gt_indices, indices); } else { precision2 = computePrecisionDiscrete(gt_dists, dists); } printf("Precision: %g\n", precision2); EXPECT_EQ(precision, precision2); // test assignment operator Index index3(data, index_params); index3 = index; start_timer("Searching KNN..."); index3.knnSearch(query, indices, dists, knn, search_params ); printf("done (%g seconds)\n", stop_timer()); float precision3; if (gt_dists.ptr()==NULL) { precision3 = compute_precision(gt_indices, indices); } else { precision3 = computePrecisionDiscrete(gt_dists, dists); } printf("Precision: %g\n", precision3); EXPECT_EQ(precision, precision3); } template void TestCopy2(const flann::Matrix& data, const flann::IndexParams& index_params, const flann::Matrix& query, flann::Matrix& indices, flann::Matrix& dists, size_t knn, const flann::SearchParams& search_params, float expected_precision, flann::Matrix& gt_indices, const flann::Matrix& gt_dists = flann::Matrix()) { flann::seed_random(0); Index index(data, index_params); char message[256]; const char* index_name = index_type_to_name(index.getType()); sprintf(message, "Building %s index... ", index_name); start_timer( message ); index.buildIndex(); printf("done (%g seconds)\n", stop_timer()); start_timer("Searching KNN..."); index.knnSearch(query, indices, dists, knn, search_params ); printf("done (%g seconds)\n", stop_timer()); float precision; if (gt_dists.ptr()==NULL) { precision = compute_precision(gt_indices, indices); } else { precision = computePrecisionDiscrete(gt_dists, dists); } printf("Precision: %g\n", precision); EXPECT_GE(precision, expected_precision); // test copy constructor Index index2(index); start_timer("Searching KNN..."); index2.knnSearch(query, indices, dists, knn, search_params ); printf("done (%g seconds)\n", stop_timer()); float precision2; if (gt_dists.ptr()==NULL) { precision2 = compute_precision(gt_indices, indices); } else { precision2 = computePrecisionDiscrete(gt_dists, dists); } printf("Precision: %g\n", precision2); EXPECT_EQ(precision, precision2); // test assignment operator Index index3(data, index_params); index3 = index; start_timer("Searching KNN..."); index3.knnSearch(query, indices, dists, knn, search_params ); printf("done (%g seconds)\n", stop_timer()); float precision3; if (gt_dists.ptr()==NULL) { precision3 = compute_precision(gt_indices, indices); } else { precision3 = computePrecisionDiscrete(gt_dists, dists); } printf("Precision: %g\n", precision3); EXPECT_EQ(precision, precision3); } template void TestRemove(const flann::Matrix& data, const flann::IndexParams& index_params, const flann::Matrix& query, flann::Matrix& indices, flann::Matrix& dists, size_t knn, const flann::SearchParams& search_params) { flann::seed_random(0); Index< Distance > index(data, index_params); char message[256]; const char* index_name = index_type_to_name(index.getType()); sprintf(message, "Building %s index... ", index_name); start_timer( message ); index.buildIndex(); printf("done (%g seconds)\n", stop_timer()); start_timer("Searching KNN before removing points..."); index.knnSearch(query, indices, dists, knn, search_params ); printf("done (%g seconds)\n", stop_timer()); EXPECT_EQ(index.size(), data.rows); // remove about 50% of neighbours found std::set neighbors; for (size_t i=0;i::iterator it = neighbors.begin(); it!=neighbors.end();++it) { index.removePoint(*it); removed.set(*it); } // also remove 10% of the initial points size_t offset = data.rows/10; for (size_t i=0;i index2(data, flann::SavedIndexParams("test_saved_index.idx")); index2.buildIndex(); EXPECT_EQ(index2.size(), new_size); flann::Matrix indices2(new size_t[query.rows*knn], query.rows, knn); flann::Matrix dists2(new typename Distance::ResultType[query.rows*knn], query.rows, knn); start_timer("Searching KNN after saving and reloading index..."); index2.knnSearch(query, indices2, dists2, knn, search_params ); printf("done (%g seconds)\n", stop_timer()); for (size_t i=0;i class DatasetTestFixture : public FLANNTestFixture { protected: std::string filename_; flann::Matrix data; flann::Matrix query; flann::Matrix gt_indices; flann::Matrix dists; flann::Matrix indices; int knn; DatasetTestFixture(const std::string& filename) : filename_(filename), knn(5) { } void SetUp() { knn = 5; printf("Reading test data..."); fflush(stdout); flann::load_from_file(data, filename_.c_str(), "dataset"); flann::load_from_file(query, filename_.c_str(), "query"); flann::load_from_file(gt_indices, filename_.c_str(), "match"); dists = flann::Matrix(new DistanceType[query.rows*knn], query.rows, knn); indices = flann::Matrix(new size_t[query.rows*knn], query.rows, knn); printf("done\n"); } void TearDown() { delete[] data.ptr(); delete[] query.ptr(); delete[] gt_indices.ptr(); delete[] dists.ptr(); delete[] indices.ptr(); } }; #endif /* FLANN_TESTS_H_ */ flann-1.8.4-src/src/0000755000000000000000000000000012075346130012675 5ustar rootrootflann-1.8.4-src/src/matlab/0000755000000000000000000000000012075346130014135 5ustar rootrootflann-1.8.4-src/src/matlab/nearest_neighbors.cpp0000644000000000000000000005326112075346130020351 0ustar rootroot/* Copyright 2008-2009 Marius Muja (mariusm@cs.ubc.ca). All rights reserved. Copyright 2008-2009 David G. Lowe (lowe@cs.ubc.ca). All rights reserved. THE BSD LICENSE Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* Workaround for MSVC 10, Matlab incompatibility */ #if (_MSC_VER >= 1600) #include #define __STDC_UTF_16__ #endif #include #include #include #include struct TypedIndex { flann_index_t index; flann_datatype_t type; }; template static mxArray* to_mx_array(T value) { mxArray* mat = mxCreateDoubleMatrix(1,1,mxREAL); double* ptr = mxGetPr(mat); *ptr = value; return mat; } static void matlabStructToFlannStruct( const mxArray* mexParams, FLANNParameters& flannParams ) { flannParams.algorithm = (flann_algorithm_t)(int)*(mxGetPr(mxGetField(mexParams, 0,"algorithm"))); // kdtree flannParams.trees = (int)*(mxGetPr(mxGetField(mexParams, 0,"trees"))); // kmeans flannParams.branching = (int)*(mxGetPr(mxGetField(mexParams, 0,"branching"))); flannParams.iterations = (int)*(mxGetPr(mxGetField(mexParams, 0,"iterations"))); flannParams.centers_init = (flann_centers_init_t)(int)*(mxGetPr(mxGetField(mexParams, 0,"centers_init"))); flannParams.cb_index = (float)*(mxGetPr(mxGetField(mexParams, 0,"cb_index"))); // autotuned flannParams.target_precision = (float)*(mxGetPr(mxGetField(mexParams, 0,"target_precision"))); flannParams.build_weight = (float)*(mxGetPr(mxGetField(mexParams, 0,"build_weight"))); flannParams.memory_weight = (float)*(mxGetPr(mxGetField(mexParams, 0,"memory_weight"))); flannParams.sample_fraction = (float)*(mxGetPr(mxGetField(mexParams, 0,"sample_fraction"))); // misc flannParams.log_level = (flann_log_level_t)(int)*(mxGetPr(mxGetField(mexParams, 0,"log_level"))); flannParams.random_seed = (int)*(mxGetPr(mxGetField(mexParams, 0,"random_seed"))); // search flannParams.checks = (int)*(mxGetPr(mxGetField(mexParams, 0,"checks"))); flannParams.eps = (float)*(mxGetPr(mxGetField(mexParams, 0,"eps"))); flannParams.sorted = (int)*(mxGetPr(mxGetField(mexParams, 0,"sorted"))); flannParams.max_neighbors = (int)*(mxGetPr(mxGetField(mexParams, 0,"max_neighbors"))); flannParams.cores = (int)*(mxGetPr(mxGetField(mexParams, 0,"cores"))); } static mxArray* flannStructToMatlabStruct( const FLANNParameters& flannParams ) { const char* fieldnames[] = {"algorithm", "checks", "eps", "sorted", "max_neighbors", "cores", "trees", "leaf_max_size", "branching", "iterations", "centers_init", "cb_index"}; mxArray* mexParams = mxCreateStructMatrix(1, 1, sizeof(fieldnames)/sizeof(const char*), fieldnames); mxSetField(mexParams, 0, "algorithm", to_mx_array(flannParams.algorithm)); mxSetField(mexParams, 0, "checks", to_mx_array(flannParams.checks)); mxSetField(mexParams, 0, "eps", to_mx_array(flannParams.eps)); mxSetField(mexParams, 0, "sorted", to_mx_array(flannParams.sorted)); mxSetField(mexParams, 0, "max_neighbors", to_mx_array(flannParams.max_neighbors)); mxSetField(mexParams, 0, "cores", to_mx_array(flannParams.cores)); mxSetField(mexParams, 0, "trees", to_mx_array(flannParams.trees)); mxSetField(mexParams, 0, "leaf_max_size", to_mx_array(flannParams.trees)); mxSetField(mexParams, 0, "branching", to_mx_array(flannParams.branching)); mxSetField(mexParams, 0, "iterations", to_mx_array(flannParams.iterations)); mxSetField(mexParams, 0, "centers_init", to_mx_array(flannParams.centers_init)); mxSetField(mexParams, 0, "cb_index", to_mx_array(flannParams.cb_index)); return mexParams; } static void check_allowed_type(const mxArray* datasetMat) { if (!mxIsSingle(datasetMat) && !mxIsDouble(datasetMat) && !mxIsUint8(datasetMat) && !mxIsInt32(datasetMat)) { mexErrMsgTxt("Data type must be floating point single precision, floating point double precision, " "8 bit unsigned integer or 32 bit signed integer"); } } /** * Input arguments: dataset (matrix), testset (matrix), n (int), params (struct) * Output arguments: indices(matrix), dists(matrix) */ static void _find_nn(int nOutArray, mxArray* OutArray[], int nInArray, const mxArray* InArray[]) { /* Check the number of input arguments */ if(nInArray != 4) { mexErrMsgTxt("Incorrect number of input arguments, expecting:\n" "dataset, testset, nearest_neighbors, params"); } /* Check the number of output arguments */ if(nOutArray > 2) { mexErrMsgTxt("One or two outputs required."); } const mxArray* datasetMat = InArray[0]; const mxArray* testsetMat = InArray[1]; check_allowed_type(datasetMat); check_allowed_type(testsetMat); int dcount = mxGetN(datasetMat); int length = mxGetM(datasetMat); int tcount = mxGetN(testsetMat); if (mxGetM(testsetMat) != length) { mexErrMsgTxt("Dataset and testset features should have the same size."); } const mxArray* nnMat = InArray[2]; if ((mxGetM(nnMat)!=1)||(mxGetN(nnMat)!=1)|| !mxIsNumeric(nnMat)) { mexErrMsgTxt("Number of nearest neighbors should be a scalar."); } int nn = (int)(*mxGetPr(nnMat)); const mxArray* pStruct = InArray[3]; if (!mxIsStruct(pStruct)) { mexErrMsgTxt("Params must be a struct object."); } FLANNParameters p; matlabStructToFlannStruct(pStruct, p); int* result = (int*)malloc(tcount*nn*sizeof(int)); float* dists = NULL; double* ddists = NULL; /* do the search */ if (mxIsSingle(datasetMat)) { float* dataset = (float*) mxGetData(datasetMat); float* testset = (float*) mxGetData(testsetMat); dists = (float*)malloc(tcount*nn*sizeof(float)); flann_find_nearest_neighbors_float(dataset,dcount,length,testset, tcount, result, dists, nn, &p); } else if (mxIsDouble(datasetMat)) { double* dataset = (double*) mxGetData(datasetMat); double* testset = (double*) mxGetData(testsetMat); ddists = (double*)malloc(tcount*nn*sizeof(double)); flann_find_nearest_neighbors_double(dataset,dcount,length,testset, tcount, result, ddists, nn, &p); } else if (mxIsUint8(datasetMat)) { unsigned char* dataset = (unsigned char*) mxGetData(datasetMat); unsigned char* testset = (unsigned char*) mxGetData(testsetMat); dists = (float*)malloc(tcount*nn*sizeof(float)); flann_find_nearest_neighbors_byte(dataset,dcount,length,testset, tcount, result, dists, nn, &p); } else if (mxIsInt32(datasetMat)) { int* dataset = (int*) mxGetData(datasetMat); int* testset = (int*) mxGetData(testsetMat); dists = (float*)malloc(tcount*nn*sizeof(float)); flann_find_nearest_neighbors_int(dataset,dcount,length,testset, tcount, result, dists, nn, &p); } /* Allocate memory for Output Matrix */ OutArray[0] = mxCreateDoubleMatrix(nn, tcount, mxREAL); /* Get pointer to Output matrix and store result */ double* pOut = mxGetPr(OutArray[0]); for (int i=0; i 1) { /* Allocate memory for Output Matrix */ OutArray[1] = mxCreateDoubleMatrix(nn, tcount, mxREAL); /* Get pointer to Output matrix and store result*/ double* pDists = mxGetPr(OutArray[1]); if (dists!=NULL) { for (int i=0; i 2) { mexErrMsgTxt("One or two outputs required."); } const mxArray* indexMat = InArray[0]; TypedIndex* typedIndex = *(TypedIndex**)mxGetData(indexMat); const mxArray* testsetMat = InArray[1]; check_allowed_type(testsetMat); int tcount = mxGetN(testsetMat); const mxArray* nnMat = InArray[2]; if ((mxGetM(nnMat)!=1)||(mxGetN(nnMat)!=1)) { mexErrMsgTxt("Number of nearest neighbors should be a scalar."); } int nn = (int)(*mxGetPr(nnMat)); int* result = (int*)malloc(tcount*nn*sizeof(int)); float* dists = NULL; double* ddists = NULL; const mxArray* pStruct = InArray[3]; FLANNParameters p; matlabStructToFlannStruct(pStruct, p); if (mxIsSingle(testsetMat)) { if (typedIndex->type != FLANN_FLOAT32) { mexErrMsgTxt("Index type must match testset type"); } float* testset = (float*) mxGetData(testsetMat); dists = (float*)malloc(tcount*nn*sizeof(float)); flann_find_nearest_neighbors_index_float(typedIndex->index,testset, tcount, result, dists, nn, &p); } else if (mxIsDouble(testsetMat)) { if (typedIndex->type != FLANN_FLOAT64) { mexErrMsgTxt("Index type must match testset type"); } double* testset = (double*) mxGetData(testsetMat); ddists = (double*)malloc(tcount*nn*sizeof(double)); flann_find_nearest_neighbors_index_double(typedIndex->index,testset, tcount, result, ddists, nn, &p); } else if (mxIsUint8(testsetMat)) { if (typedIndex->type != FLANN_UINT8) { mexErrMsgTxt("Index type must match testset type"); } unsigned char* testset = (unsigned char*) mxGetData(testsetMat); dists = (float*)malloc(tcount*nn*sizeof(float)); flann_find_nearest_neighbors_index_byte(typedIndex->index,testset, tcount, result, dists, nn, &p); } else if (mxIsInt32(testsetMat)) { if (typedIndex->type != FLANN_INT32) { mexErrMsgTxt("Index type must match testset type"); } int* testset = (int*) mxGetData(testsetMat); dists = (float*)malloc(tcount*nn*sizeof(float)); flann_find_nearest_neighbors_index_int(typedIndex->index,testset, tcount, result, dists, nn, &p); } /* Allocate memory for Output Matrix */ OutArray[0] = mxCreateDoubleMatrix(nn, tcount, mxREAL); /* Get pointer to Output matrix and store result*/ double* pOut = mxGetPr(OutArray[0]); for (int i=0; i 1) { /* Allocate memory for Output Matrix */ OutArray[1] = mxCreateDoubleMatrix(nn, tcount, mxREAL); /* Get pointer to Output matrix and store result*/ double* pDists = mxGetPr(OutArray[1]); if (dists!=NULL) { for (int i=0; i 3)) { mexErrMsgTxt("Incorrect number of outputs."); } const mxArray* datasetMat = InArray[0]; check_allowed_type(datasetMat); int dcount = mxGetN(datasetMat); int length = mxGetM(datasetMat); const mxArray* pStruct = InArray[1]; /* get index parameters */ FLANNParameters p; matlabStructToFlannStruct(pStruct, p); float speedup = -1; TypedIndex* typedIndex = new TypedIndex(); if (mxIsSingle(datasetMat)) { float* dataset = (float*) mxGetData(datasetMat); typedIndex->index = flann_build_index_float(dataset,dcount,length, &speedup, &p); typedIndex->type = FLANN_FLOAT32; } else if (mxIsDouble(datasetMat)) { double* dataset = (double*) mxGetData(datasetMat); typedIndex->index = flann_build_index_double(dataset,dcount,length, &speedup, &p); typedIndex->type = FLANN_FLOAT64; } else if (mxIsUint8(datasetMat)) { unsigned char* dataset = (unsigned char*) mxGetData(datasetMat); typedIndex->index = flann_build_index_byte(dataset,dcount,length, &speedup, &p); typedIndex->type = FLANN_UINT8; } else if (mxIsInt32(datasetMat)) { int* dataset = (int*) mxGetData(datasetMat); typedIndex->index = flann_build_index_int(dataset,dcount,length, &speedup, &p); typedIndex->type = FLANN_INT32; } mxClassID classID; if (sizeof(flann_index_t)==4) { classID = mxUINT32_CLASS; } else if (sizeof(flann_index_t)==8) { classID = mxUINT64_CLASS; } /* Allocate memory for Output Matrix */ OutArray[0] = mxCreateNumericMatrix(1, 1, classID, mxREAL); /* Get pointer to Output matrix and store result*/ TypedIndex** pOut = (TypedIndex**)mxGetData(OutArray[0]); pOut[0] = typedIndex; if (nOutArray > 1) { OutArray[1] = flannStructToMatlabStruct(p); } if (nOutArray > 2) { OutArray[2] = mxCreateDoubleMatrix(1, 1, mxREAL); double* pSpeedup = mxGetPr(OutArray[2]); *pSpeedup = speedup; } } /** * Inputs: index (index pointer) */ static void _free_index(int nOutArray, mxArray* OutArray[], int nInArray, const mxArray* InArray[]) { /* Check the number of input arguments */ if(!((nInArray == 1)&&((mxGetN(InArray[0])*mxGetM(InArray[0]))==1))) { mexErrMsgTxt("Expecting a single scalar argument: the index ID"); } TypedIndex* typedIndex = *(TypedIndex**)mxGetData(InArray[0]); if (typedIndex->type==FLANN_FLOAT32) { flann_free_index_float(typedIndex->index, NULL); } else if (typedIndex->type==FLANN_FLOAT64) { flann_free_index_double(typedIndex->index, NULL); } else if (typedIndex->type==FLANN_UINT8) { flann_free_index_byte(typedIndex->index, NULL); } else if (typedIndex->type==FLANN_INT32) { flann_free_index_int(typedIndex->index, NULL); } delete typedIndex; } /** * Inputs: level */ static void _set_log_level(int nOutArray, mxArray* OutArray[], int nInArray, const mxArray* InArray[]) { if (nInArray != 1) { mexErrMsgTxt("Incorrect number of input arguments: expecting log_level"); } const mxArray* llMat = InArray[0]; if ((mxGetM(llMat)!=1)||(mxGetN(llMat)!=1)|| !mxIsNumeric(llMat)) { mexErrMsgTxt("Log Level should be a scalar."); } int log_level = (int)(*mxGetPr(llMat)); flann_log_verbosity(log_level); } /** * Inputs: type (flann_distance_t), order(int) */ static void _set_distance_type(int nOutArray, mxArray* OutArray[], int nInArray, const mxArray* InArray[]) { if( ((nInArray != 1)&&(nInArray != 2))) { mexErrMsgTxt("Incorrect number of input arguments"); } const mxArray* distMat = InArray[0]; if ((mxGetM(distMat)!=1)||(mxGetN(distMat)!=1)|| !mxIsNumeric(distMat)) { mexErrMsgTxt("Distance type should be a scalar."); } int distance_type = (int)(*mxGetPr(distMat)); int order = 0; if (nInArray==2) { const mxArray* ordMat = InArray[1]; if ((mxGetM(ordMat)!=1)||(mxGetN(ordMat)!=1)|| !mxIsNumeric(ordMat)) { mexErrMsgTxt("Distance order should be a scalar."); } order = (int)(*mxGetPr(ordMat)); } flann_set_distance_type((flann_distance_t)distance_type, order); } /** * Inputs: index (index pointer), filename (string) */ static void _save_index(int nOutArray, mxArray* OutArray[], int nInArray, const mxArray* InArray[]) { /* Check the number of input arguments */ if(nInArray != 2) { mexErrMsgTxt("Incorrect number of input arguments"); } const mxArray* indexMat = InArray[0]; TypedIndex* typedIndex = *(TypedIndex**)mxGetData(indexMat); // get the selector if(!mxIsChar(InArray[1])) { mexErrMsgTxt("'filename' should be a string"); } char filename[128]; mxGetString(InArray[1],filename,128); if (typedIndex->type==FLANN_FLOAT32) { flann_save_index_float(typedIndex->index, filename); } else if (typedIndex->type==FLANN_FLOAT64) { flann_save_index_double(typedIndex->index, filename); } else if (typedIndex->type==FLANN_UINT8) { flann_save_index_byte(typedIndex->index, filename); } else if (typedIndex->type==FLANN_INT32) { flann_save_index_int(typedIndex->index, filename); } } /** * Inputs: filename (string), matrix */ static void _load_index(int nOutArray, mxArray* OutArray[], int nInArray, const mxArray* InArray[]) { if(nInArray != 2) { mexErrMsgTxt("Incorrect number of input arguments"); } // get the selector if(!mxIsChar(InArray[0])) { mexErrMsgTxt("'filename' should be a string"); } char filename[128]; mxGetString(InArray[0],filename,128); const mxArray* datasetMat = InArray[1]; check_allowed_type(datasetMat); int dcount = mxGetN(datasetMat); int length = mxGetM(datasetMat); TypedIndex* typedIndex = new TypedIndex(); if (mxIsSingle(datasetMat)) { float* dataset = (float*) mxGetData(datasetMat); typedIndex->index = flann_load_index_float(filename, dataset,dcount,length); typedIndex->type = FLANN_FLOAT32; } else if (mxIsDouble(datasetMat)) { double* dataset = (double*) mxGetData(datasetMat); typedIndex->index = flann_load_index_double(filename, dataset,dcount,length); typedIndex->type = FLANN_FLOAT64; } else if (mxIsUint8(datasetMat)) { unsigned char* dataset = (unsigned char*) mxGetData(datasetMat); typedIndex->index = flann_load_index_byte(filename, dataset,dcount,length); typedIndex->type = FLANN_UINT8; } else if (mxIsInt32(datasetMat)) { int* dataset = (int*) mxGetData(datasetMat); typedIndex->index = flann_load_index_int(filename, dataset,dcount,length); typedIndex->type = FLANN_INT32; } mxClassID classID; if (sizeof(flann_index_t)==4) { classID = mxUINT32_CLASS; } else if (sizeof(flann_index_t)==8) { classID = mxUINT64_CLASS; } /* Allocate memory for Output Matrix */ OutArray[0] = mxCreateNumericMatrix(1, 1, classID, mxREAL); /* Get pointer to Output matrix and store result*/ TypedIndex** pOut = (TypedIndex**)mxGetData(OutArray[0]); pOut[0] = typedIndex; } struct mexFunctionEntry { const char* name; void (* function)(int, mxArray**, int, const mxArray**); }; static mexFunctionEntry __functionTable[] = { { "find_nn", &_find_nn}, { "build_index", &_build_index}, { "index_find_nn", &_index_find_nn}, { "free_index", &_free_index}, { "save_index", &_save_index}, { "load_index", &_load_index}, { "set_log_level", &_set_log_level}, { "set_distance_type", &_set_distance_type}, }; static void print_selector_error() { char buf[512]; char* msg = buf; sprintf(msg, "%s", "Expecting first argument to be one of: "); msg = buf+strlen(buf); for (int i=0; i0))/n; assert(precision>0.9); assert(sum(~(match(1,:)-result(1,:)).*(dists(1,:)-ndists(1,:)))==0); end run_test('kd-tree search',@test_kdtree_search); function test_kmeans_search [result, ndists] = flann_search(dataset, testset, 10, struct('algorithm','kmeans',... 'branching',32,... 'iterations',3,... 'checks',120)); n = size(match,2); precision = (n-sum(abs(result(1,:)-match(1,:))>0))/n; assert(precision>0.9); assert(sum(~(match(1,:)-result(1,:)).*(dists(1,:)-ndists(1,:)))==0); end run_test('k-means search',@test_kmeans_search); function test_composite_search [result, ndists] = flann_search(dataset, testset, 10, struct('algorithm','composite',... 'branching',32,... 'iterations',3,... 'trees', 1,... 'checks',64)); n = size(match,2); precision = (n-sum(abs(result(1,:)-match(1,:))>0))/n; assert(precision>0.9); assert(sum(~(match(1,:)-result(1,:)).*(dists(1,:)-ndists(1,:)))==0); end run_test('composite search',@test_composite_search); function test_autotune_search [result, ndists] = flann_search(dataset, testset, 10, struct('algorithm','autotuned',... 'target_precision',0.95,... 'build_weight',0.01,... 'memory_weight',0)); n = size(match,2); precision = (n-sum(abs(result(1,:)-match(1,:))>0))/n; assert(precision>0.9); assert(sum(~(match(1,:)-result(1,:)).*(dists(1,:)-ndists(1,:)))==0); end run_test('search with autotune',@test_autotune_search); function test_index_kdtree_search [index, search_params ] = flann_build_index(dataset, struct('algorithm','kdtree', 'trees',8,... 'checks',64)); [result, ndists] = flann_search(index, testset, 10, search_params); n = size(match,2); precision = (n-sum(abs(result(1,:)-match(1,:))>0))/n; assert(precision>0.9); assert(sum(~(match(1,:)-result(1,:)).*(dists(1,:)-ndists(1,:)))==0); end run_test('index kd-tree search',@test_index_kdtree_search); function test_index_kmeans_search [index, search_params ] = flann_build_index(dataset, struct('algorithm','kmeans',... 'branching',32,... 'iterations',3,... 'checks',120)); [result, ndists] = flann_search(index, testset, 10, search_params); n = size(match,2); precision = (n-sum(abs(result(1,:)-match(1,:))>0))/n; assert(precision>0.9); assert(sum(~(match(1,:)-result(1,:)).*(dists(1,:)-ndists(1,:)))==0); end run_test('index kmeans search',@test_index_kmeans_search); function test_index_kmeans_search_gonzales [index, search_params ] = flann_build_index(dataset, struct('algorithm','kmeans',... 'branching',32,... 'iterations',3,... 'checks',120,... 'centers_init','gonzales')); [result, ndists] = flann_search(index, testset, 10, search_params); n = size(match,2); precision = (n-sum(abs(result(1,:)-match(1,:))>0))/n; assert(precision>0.9); assert(sum(~(match(1,:)-result(1,:)).*(dists(1,:)-ndists(1,:)))==0); end run_test('index kmeans search gonzales',@test_index_kmeans_search_gonzales); function test_index_kmeans_search_kmeanspp [index, search_params ] = flann_build_index(dataset, struct('algorithm','kmeans',... 'branching',32,... 'iterations',3,... 'checks',120,... 'centers_init','kmeanspp')); [result, ndists] = flann_search(index, testset, 10, search_params); n = size(match,2); precision = (n-sum(abs(result(1,:)-match(1,:))>0))/n; assert(precision>0.9); assert(sum(~(match(1,:)-result(1,:)).*(dists(1,:)-ndists(1,:)))==0); end run_test('index kmeans search kmeanspp',@test_index_kmeans_search_kmeanspp); function test_index_composite_search [index, search_params ] = flann_build_index(dataset,struct('algorithm','composite',... 'branching',32,... 'iterations',3,... 'trees', 1,... 'checks',64)); [result, ndists] = flann_search(index, testset, 10, search_params); n = size(match,2); precision = (n-sum(abs(result(1,:)-match(1,:))>0))/n; assert(precision>0.9); assert(sum(~(match(1,:)-result(1,:)).*(dists(1,:)-ndists(1,:)))==0); end run_test('index composite search',@test_index_composite_search); function test_index_autotune_search [index, search_params, speedup ] = flann_build_index(dataset,struct('algorithm','autotuned',... 'target_precision',0.95,... 'build_weight',0.01,... 'memory_weight',0)); [result, ndists] = flann_search(index, testset, 10, search_params); n = size(match,2); precision = (n-sum(abs(result(1,:)-match(1,:))>0))/n; assert(precision>0.9); assert(sum(~(match(1,:)-result(1,:)).*(dists(1,:)-ndists(1,:)))==0); end run_test('index autotune search',@test_index_autotune_search); status(); end flann-1.8.4-src/src/matlab/flann_save_index.m0000644000000000000000000000303312075346130017615 0ustar rootroot%Copyright 2008-2009 Marius Muja (mariusm@cs.ubc.ca). All rights reserved. %Copyright 2008-2009 David G. Lowe (lowe@cs.ubc.ca). All rights reserved. % %THE BSD LICENSE % %Redistribution and use in source and binary forms, with or without %modification, are permitted provided that the following conditions %are met: % %1. Redistributions of source code must retain the above copyright % notice, this list of conditions and the following disclaimer. %2. Redistributions in binary form must reproduce the above copyright % notice, this list of conditions and the following disclaimer in the % documentation and/or other materials provided with the distribution. % %THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR %IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES %OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. %IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, %INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT %NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, %DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY %THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT %(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF %THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. function flann_save_index(index_id, filename) %FLANN_SAVE_INDEX Saves an index to disk % % Marius Muja, March 2010 nearest_neighbors('save_index',index_id, filename); endflann-1.8.4-src/src/matlab/flann_search.m0000644000000000000000000000675412075346130016752 0ustar rootroot%Copyright 2008-2009 Marius Muja (mariusm@cs.ubc.ca). All rights reserved. %Copyright 2008-2009 David G. Lowe (lowe@cs.ubc.ca). All rights reserved. % %THE BSD LICENSE % %Redistribution and use in source and binary forms, with or without %modification, are permitted provided that the following conditions %are met: % %1. Redistributions of source code must retain the above copyright % notice, this list of conditions and the following disclaimer. %2. Redistributions in binary form must reproduce the above copyright % notice, this list of conditions and the following disclaimer in the % documentation and/or other materials provided with the distribution. % %THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR %IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES %OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. %IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, %INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT %NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, %DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY %THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT %(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF %THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. function [indices, dists] = flann_search(data, testset, n, search_params) %NN_SEARCH Fast approximate nearest neighbors search % % Performs a fast approximate nearest neighbor search using an % index constructed using flann_build_index or directly a % dataset. % Marius Muja, January 2008 algos = struct( 'linear', 0, 'kdtree', 1, 'kmeans', 2, 'composite', 3, 'saved', 254, 'autotuned', 255 ); center_algos = struct('random', 0, 'gonzales', 1, 'kmeanspp', 2 ); log_levels = struct('none', 0, 'fatal', 1, 'error', 2, 'warning', 3, 'info', 4); function value = id2value(map, id) fields = fieldnames(map); for i = 1:length(fields), val = cell2mat(fields(i)); if map.(val) == id value = val; break; end end end function id = value2id(map,value) id = map.(value); end default_params = struct('algorithm', 'kdtree' ,'checks', 32, 'eps', 0.0, 'sorted', 1, 'max_neighbors', -1, 'cores', 1, 'trees', 4, 'branching', 32, 'iterations', 5, 'centers_init', 'random', 'cb_index', 0.4, 'target_precision', 0.9,'build_weight', 0.01, 'memory_weight', 0, 'sample_fraction', 0.1, 'log_level', 'warning', 'random_seed', 0); if ~isstruct(search_params) error('The "search_params" argument must be a structure'); end params = default_params; fn = fieldnames(search_params); for i = [1:length(fn)], name = cell2mat(fn(i)); params.(name) = search_params.(name); end if ~isnumeric(params.algorithm), params.algorithm = value2id(algos,params.algorithm); end if ~isnumeric(params.centers_init), params.centers_init = value2id(center_algos,params.centers_init); end if ~isnumeric(params.log_level), params.log_level = value2id(log_levels,params.log_level); end if (size(data,1)==1 && size(data,2)==1) % we already have an index [indices,dists] = nearest_neighbors('index_find_nn', data, testset, n, params); else % create the index and search [indices,dists] = nearest_neighbors('find_nn', data, testset, n, params); end end flann-1.8.4-src/src/matlab/flann_load_index.m0000644000000000000000000000305212075346130017577 0ustar rootroot%Copyright 2008-2009 Marius Muja (mariusm@cs.ubc.ca). All rights reserved. %Copyright 2008-2009 David G. Lowe (lowe@cs.ubc.ca). All rights reserved. % %THE BSD LICENSE % %Redistribution and use in source and binary forms, with or without %modification, are permitted provided that the following conditions %are met: % %1. Redistributions of source code must retain the above copyright % notice, this list of conditions and the following disclaimer. %2. Redistributions in binary form must reproduce the above copyright % notice, this list of conditions and the following disclaimer in the % documentation and/or other materials provided with the distribution. % %THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR %IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES %OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. %IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, %INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT %NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, %DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY %THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT %(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF %THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. function index = flann_load_index(filename, dataset) %FLANN_LOAD_INDEX Loads an index from disk % % Marius Muja, March 2009 index = nearest_neighbors('load_index', filename, dataset); endflann-1.8.4-src/src/CMakeLists.txt0000644000000000000000000000023712075346130015437 0ustar rootroot add_subdirectory( cpp ) if (BUILD_MATLAB_BINDINGS) add_subdirectory( matlab ) endif() if (BUILD_PYTHON_BINDINGS) add_subdirectory( python ) endif() flann-1.8.4-src/src/cpp/0000755000000000000000000000000012075346130013457 5ustar rootrootflann-1.8.4-src/src/cpp/CMakeLists.txt0000644000000000000000000001062712075346130016225 0ustar rootroot#include_directories(${CMAKE_SOURCE_DIR}/include algorithms util nn .) add_definitions(-D_FLANN_VERSION=${FLANN_VERSION}) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/flann/config.h.in ${CMAKE_CURRENT_SOURCE_DIR}/flann/config.h) file(GLOB_RECURSE C_SOURCES flann.cpp) file(GLOB_RECURSE CPP_SOURCES flann_cpp.cpp) file(GLOB_RECURSE CU_SOURCES *.cu) add_library(flann_cpp_s STATIC ${CPP_SOURCES}) if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANG) set_target_properties(flann_cpp_s PROPERTIES COMPILE_FLAGS -fPIC) endif() set_property(TARGET flann_cpp_s PROPERTY COMPILE_DEFINITIONS FLANN_STATIC FLANN_USE_CUDA) if (BUILD_CUDA_LIB) SET(CUDA_NVCC_FLAGS -DFLANN_USE_CUDA) if(CMAKE_COMPILER_IS_GNUCC) set(CUDA_NVCC_FLAGS "${CUDA_NVCC_FLAGS};-Xcompiler;-fPIC;-arch=sm_13" ) if (NVCC_COMPILER_BINDIR) set(CUDA_NVCC_FLAGS "${CUDA_NVCC_FLAGS};--compiler-bindir=${NVCC_COMPILER_BINDIR}") endif() else() set(CUDA_NVCC_FLAGS "${CUDA_NVCC_FLAGS};-arch=sm_13" ) endif() cuda_add_library(flann_cuda_s STATIC ${CU_SOURCES}) set_property(TARGET flann_cuda_s PROPERTY COMPILE_DEFINITIONS FLANN_STATIC) endif() if(CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_COMPILER_IS_GNUCC) add_library(flann_cpp SHARED "") set_target_properties(flann_cpp PROPERTIES LINKER_LANGUAGE CXX) target_link_libraries(flann_cpp -Wl,-whole-archive flann_cpp_s -Wl,-no-whole-archive) if (BUILD_CUDA_LIB) cuda_add_library(flann_cuda SHARED "") set_target_properties(flann_cuda PROPERTIES LINKER_LANGUAGE CXX) target_link_libraries(flann_cuda -Wl,-whole-archive flann_cuda_s -Wl,-no-whole-archive) set_property(TARGET flann_cpp_s PROPERTY COMPILE_DEFINITIONS FLANN_USE_CUDA) # target_link_libraries(flann_cuda cudpp_x86_64) endif() else() add_library(flann_cpp SHARED ${CPP_SOURCES}) if (BUILD_CUDA_LIB) cuda_add_library(flann_cuda SHARED ${CPP_SOURCES}) set_property(TARGET flann_cpp PROPERTY COMPILE_DEFINITIONS FLANN_USE_CUDA) endif() endif() set_target_properties(flann_cpp PROPERTIES VERSION ${FLANN_VERSION} SOVERSION ${FLANN_SOVERSION} DEFINE_SYMBOL FLANN_EXPORTS ) if (BUILD_CUDA_LIB) set_target_properties(flann_cuda PROPERTIES VERSION ${FLANN_VERSION} SOVERSION ${FLANN_SOVERSION} DEFINE_SYMBOL FLANN_EXPORTS ) endif() if (USE_MPI AND HDF5_IS_PARALLEL) add_executable(flann_mpi_server flann/mpi/flann_mpi_server.cpp) target_link_libraries(flann_mpi_server flann_cpp ${HDF5_LIBRARIES} ${MPI_LIBRARIES} ${Boost_LIBRARIES}) add_executable(flann_mpi_client flann/mpi/flann_mpi_client.cpp) target_link_libraries(flann_mpi_client flann_cpp ${HDF5_LIBRARIES} ${MPI_LIBRARIES} ${Boost_LIBRARIES}) install (TARGETS flann_mpi_client flann_mpi_server DESTINATION bin) endif() if (BUILD_C_BINDINGS) add_library(flann_s STATIC ${C_SOURCES}) if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANG) set_target_properties(flann_s PROPERTIES COMPILE_FLAGS -fPIC) endif() set_property(TARGET flann_s PROPERTY COMPILE_DEFINITIONS FLANN_STATIC) if(CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_COMPILER_IS_GNUCC) add_library(flann SHARED "") set_target_properties(flann PROPERTIES LINKER_LANGUAGE CXX) target_link_libraries(flann -Wl,-whole-archive flann_s -Wl,-no-whole-archive) else() add_library(flann SHARED ${C_SOURCES}) endif() set_target_properties(flann PROPERTIES VERSION ${FLANN_VERSION} SOVERSION ${FLANN_SOVERSION} DEFINE_SYMBOL FLANN_EXPORTS ) endif() if(WIN32) if (BUILD_C_BINDINGS) install ( TARGETS flann RUNTIME DESTINATION share/flann/matlab ) endif() endif(WIN32) install ( TARGETS flann_cpp flann_cpp_s RUNTIME DESTINATION bin LIBRARY DESTINATION ${FLANN_LIB_INSTALL_DIR} ARCHIVE DESTINATION ${FLANN_LIB_INSTALL_DIR} ) if (BUILD_CUDA_LIB) install ( TARGETS flann_cuda flann_cuda_s RUNTIME DESTINATION bin LIBRARY DESTINATION ${FLANN_LIB_INSTALL_DIR} ARCHIVE DESTINATION ${FLANN_LIB_INSTALL_DIR} ) endif() if (BUILD_C_BINDINGS) install ( TARGETS flann flann_s RUNTIME DESTINATION bin LIBRARY DESTINATION ${FLANN_LIB_INSTALL_DIR} ARCHIVE DESTINATION ${FLANN_LIB_INSTALL_DIR} ) endif() install ( DIRECTORY flann DESTINATION include FILES_MATCHING PATTERN "*.h" PATTERN "*.hpp" ) flann-1.8.4-src/src/cpp/flann/0000755000000000000000000000000012075346130014555 5ustar rootrootflann-1.8.4-src/src/cpp/flann/util/0000755000000000000000000000000012075346130015532 5ustar rootrootflann-1.8.4-src/src/cpp/flann/util/allocator.h0000644000000000000000000001426012075346130017666 0ustar rootroot/*********************************************************************** * Software License Agreement (BSD License) * * Copyright 2008-2009 Marius Muja (mariusm@cs.ubc.ca). All rights reserved. * Copyright 2008-2009 David G. Lowe (lowe@cs.ubc.ca). All rights reserved. * * THE BSD LICENSE * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #ifndef FLANN_ALLOCATOR_H_ #define FLANN_ALLOCATOR_H_ #include #include namespace flann { /** * Allocates (using C's malloc) a generic type T. * * Params: * count = number of instances to allocate. * Returns: pointer (of type T*) to memory buffer */ template T* allocate(size_t count = 1) { T* mem = (T*) ::malloc(sizeof(T)*count); return mem; } /** * Pooled storage allocator * * The following routines allow for the efficient allocation of storage in * small chunks from a specified pool. Rather than allowing each structure * to be freed individually, an entire pool of storage is freed at once. * This method has two advantages over just using malloc() and free(). First, * it is far more efficient for allocating small objects, as there is * no overhead for remembering all the information needed to free each * object or consolidating fragmented memory. Second, the decision about * how long to keep an object is made at the time of allocation, and there * is no need to track down all the objects to free them. * */ const size_t WORDSIZE=16; const size_t BLOCKSIZE=8192; class PooledAllocator { /* We maintain memory alignment to word boundaries by requiring that all allocations be in multiples of the machine wordsize. */ /* Size of machine word in bytes. Must be power of 2. */ /* Minimum number of bytes requested at a time from the system. Must be multiple of WORDSIZE. */ int remaining; /* Number of bytes left in current block of storage. */ void* base; /* Pointer to base of current block of storage. */ void* loc; /* Current location in block to next allocate memory. */ int blocksize; public: int usedMemory; int wastedMemory; /** Default constructor. Initializes a new pool. */ PooledAllocator(int blocksize = BLOCKSIZE) { this->blocksize = blocksize; remaining = 0; base = NULL; usedMemory = 0; wastedMemory = 0; } /** * Destructor. Frees all the memory allocated in this pool. */ ~PooledAllocator() { free(); } void free() { void* prev; while (base != NULL) { prev = *((void**) base); /* Get pointer to prev block. */ ::free(base); base = prev; } base = NULL; remaining = 0; usedMemory = 0; wastedMemory = 0; } /** * Returns a pointer to a piece of new memory of the given size in bytes * allocated from the pool. */ void* allocateMemory(int size) { int blocksize; /* Round size up to a multiple of wordsize. The following expression only works for WORDSIZE that is a power of 2, by masking last bits of incremented size to zero. */ size = (size + (WORDSIZE - 1)) & ~(WORDSIZE - 1); /* Check whether a new block must be allocated. Note that the first word of a block is reserved for a pointer to the previous block. */ if (size > remaining) { wastedMemory += remaining; /* Allocate new storage. */ blocksize = (size + sizeof(void*) + (WORDSIZE-1) > BLOCKSIZE) ? size + sizeof(void*) + (WORDSIZE-1) : BLOCKSIZE; // use the standard C malloc to allocate memory void* m = ::malloc(blocksize); if (!m) { fprintf(stderr,"Failed to allocate memory.\n"); return NULL; } /* Fill first word of new block with pointer to previous block. */ ((void**) m)[0] = base; base = m; int shift = 0; //int shift = (WORDSIZE - ( (((size_t)m) + sizeof(void*)) & (WORDSIZE-1))) & (WORDSIZE-1); remaining = blocksize - sizeof(void*) - shift; loc = ((char*)m + sizeof(void*) + shift); } void* rloc = loc; loc = (char*)loc + size; remaining -= size; usedMemory += size; return rloc; } /** * Allocates (using this pool) a generic type T. * * Params: * count = number of instances to allocate. * Returns: pointer (of type T*) to memory buffer */ template T* allocate(size_t count = 1) { T* mem = (T*) this->allocateMemory((int)(sizeof(T)*count)); return mem; } }; } inline void* operator new (std::size_t size, flann::PooledAllocator& allocator) { return allocator.allocateMemory(size) ; } #endif //FLANN_ALLOCATOR_H_ flann-1.8.4-src/src/cpp/flann/util/heap.h0000644000000000000000000002345512075346130016631 0ustar rootroot/*********************************************************************** * Software License Agreement (BSD License) * * Copyright 2008-2009 Marius Muja (mariusm@cs.ubc.ca). All rights reserved. * Copyright 2008-2009 David G. Lowe (lowe@cs.ubc.ca). All rights reserved. * * THE BSD LICENSE * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #ifndef FLANN_HEAP_H_ #define FLANN_HEAP_H_ #include #include namespace flann { /** * Priority Queue Implementation * * The priority queue is implemented with a heap. A heap is a complete * (full) binary tree in which each parent is less than both of its * children, but the order of the children is unspecified. */ template class Heap { /** * Storage array for the heap. * Type T must be comparable. */ std::vector heap; int length; /** * Number of element in the heap */ int count; public: /** * Constructor. * * Params: * size = heap size */ Heap(int size) { length = size; heap.reserve(length); count = 0; } /** * * Returns: heap size */ int size() { return count; } /** * Tests if the heap is empty * * Returns: true is heap empty, false otherwise */ bool empty() { return size()==0; } /** * Clears the heap. */ void clear() { heap.clear(); count = 0; } struct CompareT : public std::binary_function { bool operator()(const T& t_1, const T& t_2) const { return t_2 < t_1; } }; /** * Insert a new element in the heap. * * We select the next empty leaf node, and then keep moving any larger * parents down until the right location is found to store this element. * * Params: * value = the new element to be inserted in the heap */ void insert(const T& value) { /* If heap is full, then return without adding this element. */ if (count == length) { return; } heap.push_back(value); static CompareT compareT; std::push_heap(heap.begin(), heap.end(), compareT); ++count; } /** * Returns the node of minimum value from the heap (top of the heap). * * Params: * value = out parameter used to return the min element * Returns: false if heap empty */ bool popMin(T& value) { if (count == 0) { return false; } value = heap[0]; static CompareT compareT; std::pop_heap(heap.begin(), heap.end(), compareT); heap.pop_back(); --count; return true; /* Return old last node. */ } }; template class IntervalHeap { struct Interval { T left; T right; }; /** * Storage array for the heap. * Type T must be comparable. */ std::vector heap; size_t capacity_; size_t size_; public: /** * Constructor. * * Params: * size = heap size */ IntervalHeap(int capacity) : capacity_(capacity), size_(0) { heap.resize(capacity/2 + capacity%2 + 1); // 1-based indexing } /** * @return Heap size */ size_t size() { return size_; } /** * Tests if the heap is empty * @return true is heap empty, false otherwise */ bool empty() { return size_==0; } /** * Clears the heap. */ void clear() { size_ = 0; } void insert(const T& value) { /* If heap is full, then return without adding this element. */ if (size_ == capacity_) { return; } // insert into the root if (size_<2) { if (size_==0) { heap[1].left = value; heap[1].right = value; } else { if (value1 && value < heap[par].left) { heap[pos].left = heap[par].left; pos = par; par = pos/2; } heap[pos].left = value; ++size_; if (size_%2) { // duplicate element in last position if size is odd heap[last_pos].right = heap[last_pos].left; } } else { size_t pos = last_pos; size_t par = pos/2; while (pos>1 && heap[par].right < value) { heap[pos].right = heap[par].right; pos = par; par = pos/2; } heap[pos].right = value; ++size_; if (size_%2) { // duplicate element in last position if size is odd heap[last_pos].left = heap[last_pos].right; } } } /** * Returns the node of minimum value from the heap * @param value out parameter used to return the min element * @return false if heap empty */ bool popMin(T& value) { if (size_ == 0) { return false; } value = heap[1].left; size_t last_pos = size_/2 + size_%2; T elem = heap[last_pos].left; if (size_ % 2) { // odd number of elements --last_pos; } else { heap[last_pos].left = heap[last_pos].right; } --size_; if (size_<2) return true; size_t crt=1; // root node size_t child = crt*2; while (child <= last_pos) { if (child < last_pos && heap[child+1].left < heap[child].left) ++child; // pick the child with min if (!(heap[child].left class BoundedHeap { IntervalHeap interval_heap_; size_t capacity_; public: BoundedHeap(size_t capacity) : interval_heap_(capacity), capacity_(capacity) { } /** * Returns: heap size */ int size() { return interval_heap_.size(); } /** * Tests if the heap is empty * Returns: true is heap empty, false otherwise */ bool empty() { return interval_heap_.empty(); } /** * Clears the heap. */ void clear() { interval_heap_.clear(); } void insert(const T& value) { if (interval_heap_.size()==capacity_) { T max; interval_heap_.getMax(max); if (max All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY Andreas Mützel ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Andreas Mützel BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ namespace flann { namespace cuda { template __device__ __host__ void swap( T& x, T& y ) { T t=x; x=y; y=t; } namespace heap { //! moves an element down the heap until all children are smaller than the elemnent //! if c is a less-than comparator, it do this until all children are larger template __host__ __device__ void sift_down( RandomAccessIterator array, size_t begin, size_t length, GreaterThan c = GreaterThan() ) { while( 2*begin+1 < length ) { size_t left = 2*begin+1; size_t right = 2*begin+2; size_t largest=begin; if((left < length)&& c(array[left], array[largest]) ) largest=left; if((right < length)&& c(array[right], array[largest]) ) largest=right; if( largest != begin ) { cuda::swap( array[begin], array[largest] ); begin=largest; } else return; } } //! creates a max-heap in the array beginning at begin of length "length" //! if c is a less-than comparator, it will create a min-heap template __host__ __device__ void make_heap( RandomAccessIterator begin, size_t length, GreaterThan c = GreaterThan() ) { int i=length/2-1; while( i>=0 ) { sift_down( begin, i, length, c ); i--; } } //! verifies if the array is a max-heap //! if c is a less-than comparator, it will verify if it is a min-heap template __host__ __device__ bool is_heap( RandomAccessIterator begin, size_t length, GreaterThan c = GreaterThan() ) { for( unsigned i=0; i __host__ __device__ void sift_down( RandomAccessIterator key, RandomAccessIterator2 value, size_t begin, size_t length, GreaterThan c = GreaterThan() ) { while( 2*begin+1 < length ) { size_t left = 2*begin+1; size_t right = 2*begin+2; size_t largest=begin; if((left < length)&& c(key[left], key[largest]) ) largest=left; if((right < length)&& c(key[right], key[largest]) ) largest=right; if( largest != begin ) { cuda::swap( key[begin], key[largest] ); cuda::swap( value[begin], value[largest] ); begin=largest; } else return; } } //! creates a max-heap in the array beginning at begin of length "length" //! if c is a less-than comparator, it will create a min-heap template __host__ __device__ void make_heap( RandomAccessIterator key, RandomAccessIterator2 value, size_t length, GreaterThan c = GreaterThan() ) { int i=length/2-1; while( i>=0 ) { sift_down( key, value, i, length, c ); i--; } } } } } #endifflann-1.8.4-src/src/cpp/flann/util/cuda/result_set.h0000644000000000000000000003704412075346130021020 0ustar rootroot/********************************************************************** * Software License Agreement (BSD License) * * Copyright 2011 Andreas Muetzel (amuetzel@uni-koblenz.de). All rights reserved. * * THE BSD LICENSE * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #ifndef FLANN_UTIL_CUDA_RESULTSET_H #define FLANN_UTIL_CUDA_RESULTSET_H #include #include __device__ __forceinline__ float infinity() { return __int_as_float(0x7f800000); } #ifndef INFINITY #define INFINITY infinity() #endif namespace flann { namespace cuda { //! result set for the 1nn search. Doesn't do any global memory accesses on its own, template< typename DistanceType > struct SingleResultSet { int bestIndex; DistanceType bestDist; const DistanceType epsError; __device__ SingleResultSet( DistanceType eps ) : bestIndex(-1),bestDist(INFINITY), epsError(eps){ } __device__ inline float worstDist() { return bestDist; } __device__ inline void insert(int index, DistanceType dist) { if( dist <= bestDist ) { bestIndex=index; bestDist=dist; } } DistanceType* resultDist; int* resultIndex; __device__ inline void setResultLocation( DistanceType* dists, int* index, int thread, int stride ) { resultDist=dists+thread*stride; resultIndex=index+thread*stride; if( stride != 1 ) { for( int i=1; i struct GreaterThan { __device__ bool operator()(DistanceType a, DistanceType b) { return a>b; } }; // using this and the template uses 2 or 3 registers more than the direct implementation in the kNearestKernel, but // there is no speed difference. // Setting useHeap as a template parameter leads to a whole lot of things being // optimized away by nvcc. // Register counts are the same as when removing not-needed variables in explicit specializations // and the "if( useHeap )" branches are eliminated at compile time. // The downside of this: a bit more complex kernel launch code. template< typename DistanceType, bool useHeap > struct KnnResultSet { int foundNeighbors; DistanceType largestHeapDist; int maxDistIndex; const int k; const bool sorted; const DistanceType epsError; __device__ KnnResultSet(int knn, bool sortResults, DistanceType eps) : foundNeighbors(0),largestHeapDist(INFINITY),k(knn), sorted(sortResults), epsError(eps){ } // __host__ __device__ // KnnResultSet(const KnnResultSet& o):foundNeighbors(o.foundNeighbors),largestHeapDist(o.largestHeapDist),k(o.k){ } __device__ inline DistanceType worstDist() { return largestHeapDist; } __device__ inline void insert(int index, DistanceType dist) { if( foundNeighbors()); largestHeapDist=resultDist[0]; } else { findLargestDistIndex(); } } foundNeighbors++; } else if( dist < largestHeapDist ) { if( useHeap ) { resultDist[0]=dist; resultIndex[0]=index; flann::cuda::heap::sift_down(resultDist,resultIndex,0,k,GreaterThan()); largestHeapDist=resultDist[0]; } else { resultDist[maxDistIndex]=dist; resultIndex[maxDistIndex]=index; findLargestDistIndex(); } } } __device__ void findLargestDistIndex( ) { largestHeapDist=resultDist[0]; maxDistIndex=0; for( int i=1; i largestHeapDist ) { maxDistIndex=i; largestHeapDist=resultDist[i]; } } float* resultDist; int* resultIndex; __device__ inline void setResultLocation( DistanceType* dists, int* index, int thread, int stride ) { resultDist=dists+stride*thread; resultIndex=index+stride*thread; for( int i=0; i()); for( int i=k-1; i>0; i-- ) { flann::cuda::swap( resultDist[0], resultDist[i] ); flann::cuda::swap( resultIndex[0], resultIndex[i] ); flann::cuda::heap::sift_down( resultDist,resultIndex, 0, i, GreaterThan() ); } } } }; template struct CountingRadiusResultSet { int count_; DistanceType radius_sq_; int max_neighbors_; __device__ CountingRadiusResultSet(DistanceType radius, int max_neighbors) : count_(0),radius_sq_(radius), max_neighbors_(max_neighbors){ } __device__ inline DistanceType worstDist() { return radius_sq_; } __device__ inline void insert(int index, float dist) { if( dist < radius_sq_ ) { count_++; } } int* resultIndex; __device__ inline void setResultLocation( DistanceType* /*dists*/, int* count, int thread, int stride ) { resultIndex=count+thread*stride; } __device__ inline void finish() { if(( max_neighbors_<=0) ||( count_<=max_neighbors_) ) resultIndex[0]=count_; else resultIndex[0]=max_neighbors_; } }; template struct RadiusKnnResultSet { int foundNeighbors; DistanceType largestHeapDist; int maxDistElem; const int k; const bool sorted; const DistanceType radius_sq_; int* segment_starts_; // int count_; __device__ RadiusKnnResultSet(DistanceType radius, int knn, int* segment_starts, bool sortResults) : foundNeighbors(0),largestHeapDist(radius),k(knn), sorted(sortResults), radius_sq_(radius),segment_starts_(segment_starts) { } // __host__ __device__ // KnnResultSet(const KnnResultSet& o):foundNeighbors(o.foundNeighbors),largestHeapDist(o.largestHeapDist),k(o.k){ } __device__ inline DistanceType worstDist() { return largestHeapDist; } __device__ inline void insert(int index, DistanceType dist) { if( dist < radius_sq_ ) { if( foundNeighbors()); largestHeapDist=resultDist[0]; } else { findLargestDistIndex(); } } foundNeighbors++; } else if( dist < largestHeapDist ) { if( useHeap ) { resultDist[0]=dist; resultIndex[0]=index; flann::cuda::heap::sift_down(resultDist,resultIndex,0,k,GreaterThan()); largestHeapDist=resultDist[0]; } else { resultDist[maxDistElem]=dist; resultIndex[maxDistElem]=index; findLargestDistIndex(); } } } } __device__ void findLargestDistIndex( ) { largestHeapDist=resultDist[0]; maxDistElem=0; for( int i=1; i largestHeapDist ) { maxDistElem=i; largestHeapDist=resultDist[i]; } } DistanceType* resultDist; int* resultIndex; __device__ inline void setResultLocation( DistanceType* dists, int* index, int thread, int /*stride*/ ) { resultDist=dists+segment_starts_[thread]; resultIndex=index+segment_starts_[thread]; } __device__ inline void finish() { if( sorted ) { if( !useHeap ) flann::cuda::heap::make_heap(resultDist,resultIndex,k,GreaterThan()); for( int i=foundNeighbors-1; i>0; i-- ) { flann::cuda::swap( resultDist[0], resultDist[i] ); flann::cuda::swap( resultIndex[0], resultIndex[i] ); flann::cuda::heap::sift_down( resultDist,resultIndex, 0, i, GreaterThan() ); } } } }; // Difference to RadiusKnnResultSet: Works like KnnResultSet, doesn't pack the results densely (as the RadiusResultSet does) template struct KnnRadiusResultSet { int foundNeighbors; DistanceType largestHeapDist; int maxDistIndex; const int k; const bool sorted; const DistanceType epsError; const DistanceType radius_sq; __device__ KnnRadiusResultSet(int knn, bool sortResults, DistanceType eps, DistanceType radius) : foundNeighbors(0),largestHeapDist(radius),k(knn), sorted(sortResults), epsError(eps),radius_sq(radius){ } // __host__ __device__ // KnnResultSet(const KnnResultSet& o):foundNeighbors(o.foundNeighbors),largestHeapDist(o.largestHeapDist),k(o.k){ } __device__ inline DistanceType worstDist() { return largestHeapDist; } __device__ inline void insert(int index, DistanceType dist) { if( dist < largestHeapDist ) { if( foundNeighbors()); largestHeapDist=resultDist[0]; } else { findLargestDistIndex(); } } foundNeighbors++; } else { //if( dist < largestHeapDist ) if( useHeap ) { resultDist[0]=dist; resultIndex[0]=index; flann::cuda::heap::sift_down(resultDist,resultIndex,0,k,GreaterThan()); largestHeapDist=resultDist[0]; } else { resultDist[maxDistIndex]=dist; resultIndex[maxDistIndex]=index; findLargestDistIndex(); } } } } __device__ void findLargestDistIndex( ) { largestHeapDist=resultDist[0]; maxDistIndex=0; for( int i=1; i largestHeapDist ) { maxDistIndex=i; largestHeapDist=resultDist[i]; } } DistanceType* resultDist; int* resultIndex; __device__ inline void setResultLocation( DistanceType* dists, int* index, int thread, int stride ) { resultDist=dists+stride*thread; resultIndex=index+stride*thread; for( int i=0; i()); for( int i=k-1; i>0; i-- ) { flann::cuda::swap( resultDist[0], resultDist[i] ); flann::cuda::swap( resultIndex[0], resultIndex[i] ); flann::cuda::heap::sift_down( resultDist,resultIndex, 0, i, GreaterThan() ); } } } }; //! fills the radius output buffer. //! IMPORTANT ASSERTION: ASSUMES THAT THERE IS ENOUGH SPACE FOR EVERY NEIGHBOR! IF THIS ISN'T //! TRUE, USE KnnRadiusResultSet! (Otherwise, the neighbors of one element might overflow into the next element, or past the buffer.) template< typename DistanceType > struct RadiusResultSet { DistanceType radius_sq_; int* segment_starts_; int count_; bool sorted_; __device__ RadiusResultSet(DistanceType radius, int* segment_starts, bool sorted) : radius_sq_(radius), segment_starts_(segment_starts), count_(0), sorted_(sorted){ } __device__ inline DistanceType worstDist() { return radius_sq_; } __device__ inline void insert(int index, DistanceType dist) { if( dist < radius_sq_ ) { resultIndex[count_]=index; resultDist[count_]=dist; count_++; } } int* resultIndex; DistanceType* resultDist; __device__ inline void setResultLocation( DistanceType* dists, int* index, int thread, int /*stride*/ ) { resultIndex=index+segment_starts_[thread]; resultDist=dists+segment_starts_[thread]; } __device__ inline void finish() { if( sorted_ ) { flann::cuda::heap::make_heap( resultDist,resultIndex, count_, GreaterThan() ); for( int i=count_-1; i>0; i-- ) { flann::cuda::swap( resultDist[0], resultDist[i] ); flann::cuda::swap( resultIndex[0], resultIndex[i] ); flann::cuda::heap::sift_down( resultDist,resultIndex, 0, i, GreaterThan() ); } } } }; } } #endif flann-1.8.4-src/src/cpp/flann/util/serialization.h0000644000000000000000000002121012075346130020554 0ustar rootroot#ifndef SERIALIZATION_H_ #define SERIALIZATION_H_ #include #include #include namespace flann { namespace serialization { struct access { template static inline void serialize(Archive& ar, T& type) { type.serialize(ar); } }; template inline void serialize(Archive& ar, T& type) { access::serialize(ar,type); } template struct Serializer { template static inline void load(InputArchive& ar, T& val) { serialization::serialize(ar,val); } template static inline void save(OutputArchive& ar, const T& val) { serialization::serialize(ar,const_cast(val)); } }; #define BASIC_TYPE_SERIALIZER(type)\ template<> \ struct Serializer \ {\ template\ static inline void load(InputArchive& ar, type& val)\ {\ ar.load(val);\ }\ template\ static inline void save(OutputArchive& ar, const type& val)\ {\ ar.save(val);\ }\ } #define ENUM_SERIALIZER(type)\ template<>\ struct Serializer\ {\ template\ static inline void load(InputArchive& ar, type& val)\ {\ int int_val;\ ar & int_val;\ val = (type) int_val;\ }\ template\ static inline void save(OutputArchive& ar, const type& val)\ {\ int int_val = (int)val;\ ar & int_val;\ }\ } // declare serializers for simple types BASIC_TYPE_SERIALIZER(char); BASIC_TYPE_SERIALIZER(unsigned char); BASIC_TYPE_SERIALIZER(short); BASIC_TYPE_SERIALIZER(unsigned short); BASIC_TYPE_SERIALIZER(int); BASIC_TYPE_SERIALIZER(unsigned int); BASIC_TYPE_SERIALIZER(long); BASIC_TYPE_SERIALIZER(unsigned long); BASIC_TYPE_SERIALIZER(float); BASIC_TYPE_SERIALIZER(double); BASIC_TYPE_SERIALIZER(bool); // serializer for std::vector template struct Serializer > { template static inline void load(InputArchive& ar, std::vector& val) { size_t size; ar & size; val.resize(size); for (size_t i=0;i static inline void save(OutputArchive& ar, const std::vector& val) { ar & val.size(); for (size_t i=0;i struct Serializer > { template static inline void load(InputArchive& ar, std::map& map_val) { size_t size; ar & size; for (size_t i = 0; i < size; ++i) { K key; ar & key; V value; ar & value; map_val[key] = value; } } template static inline void save(OutputArchive& ar, const std::map& map_val) { ar & map_val.size(); for (typename std::map::const_iterator i=map_val.begin(); i!=map_val.end(); ++i) { ar & i->first; ar & i->second; } } }; template struct Serializer { template static inline void load(InputArchive& ar, T*& val) { ar.load(val); } template static inline void save(OutputArchive& ar, T* const& val) { ar.save(val); } }; template struct Serializer { template static inline void load(InputArchive& ar, T (&val)[N]) { ar.load(val); } template static inline void save(OutputArchive& ar, T const (&val)[N]) { ar.save(val); } }; struct binary_object { void const * ptr_; size_t size_; binary_object( void * const ptr, size_t size) : ptr_(ptr), size_(size) {} binary_object(const binary_object & rhs) : ptr_(rhs.ptr_), size_(rhs.size_) {} binary_object & operator=(const binary_object & rhs) { ptr_ = rhs.ptr_; size_ = rhs.size_; return *this; } }; inline const binary_object make_binary_object(/* const */ void * t, size_t size){ return binary_object(t, size); } template<> struct Serializer { template static inline void load(InputArchive& ar, const binary_object& b) { ar.load_binary(const_cast(b.ptr_), b.size_); } template static inline void save(OutputArchive& ar, const binary_object& b) { ar.save_binary(b.ptr_, b.size_); } }; template<> struct Serializer { template static inline void load(InputArchive& ar, binary_object& b) { ar.load_binary(const_cast(b.ptr_), b.size_); } template static inline void save(OutputArchive& ar, const binary_object& b) { ar.save_binary(b.ptr_, b.size_); } }; template struct bool_ { static const bool value = C_; typedef bool value_type; }; class ArchiveBase { public: void* getObject() { return object_; } void setObject(void* object) { object_ = object; } private: void* object_; }; template class InputArchive : public ArchiveBase { protected: InputArchive() {}; public: typedef bool_ is_loading; typedef bool_ is_saving; template Archive& operator& (T& val) { Serializer::load(*static_cast(this),val); return *static_cast(this); } }; template class OutputArchive : public ArchiveBase { protected: OutputArchive() {}; public: typedef bool_ is_loading; typedef bool_ is_saving; template Archive& operator& (const T& val) { Serializer::save(*static_cast(this),val); return *static_cast(this); } }; class SizeArchive : public OutputArchive { size_t size_; public: SizeArchive() : size_(0) { } template void save(const T& val) { size_ += sizeof(val); } template void save_binary(T* ptr, size_t size) { size_ += size; } void reset() { size_ = 0; } size_t size() { return size_; } }; // //class PrintArchive : public OutputArchive //{ //public: // template // void save(const T& val) // { // std::cout << val << std::endl; // } // // template // void save_binary(T* ptr, size_t size) // { // std::cout << "" << std::endl; // } //}; class SaveArchive : public OutputArchive { FILE* stream_; bool own_stream_; public: SaveArchive(const char* filename) { stream_ = fopen(filename, "w"); own_stream_ = true; } SaveArchive(FILE* stream) : stream_(stream), own_stream_(false) { } ~SaveArchive() { if (own_stream_) { fclose(stream_); } } template void save(const T& val) { fwrite(&val, sizeof(val), 1, stream_); } template void save(T* const& val) { // don't save pointers //fwrite(&val, sizeof(val), 1, handle_); } template void save_binary(T* ptr, size_t size) { fwrite(ptr, size, 1, stream_); } }; class LoadArchive : public InputArchive { FILE* stream_; bool own_stream_; public: LoadArchive(const char* filename) { stream_ = fopen(filename, "r"); own_stream_ = true; } LoadArchive(FILE* stream) : stream_(stream), own_stream_(false) { } ~LoadArchive() { if (own_stream_) { fclose(stream_); } } template void load(T& val) { size_t ret = fread(&val, sizeof(val), 1, stream_); if (ret!=1) { throw FLANNException("Error loading from file"); } } template void load(T*& val) { // don't load pointers //fread(&val, sizeof(val), 1, handle_); } template void load_binary(T* ptr, size_t size) { size_t ret = fread(ptr, size, 1, stream_); if (ret!=1) { throw FLANNException("Error loading from file"); } } }; } // namespace serialization } // namespace flann #endif // SERIALIZATION_H_ flann-1.8.4-src/src/cpp/flann/util/matrix.h0000644000000000000000000000727512075346130017222 0ustar rootroot/*********************************************************************** * Software License Agreement (BSD License) * * Copyright 2008-2009 Marius Muja (mariusm@cs.ubc.ca). All rights reserved. * Copyright 2008-2009 David G. Lowe (lowe@cs.ubc.ca). All rights reserved. * * THE BSD LICENSE * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #ifndef FLANN_DATASET_H_ #define FLANN_DATASET_H_ #include "flann/general.h" #include "flann/util/serialization.h" #include namespace flann { typedef unsigned char uchar; class Matrix_ { public: Matrix_() : rows(0), cols(0), stride(0), type(FLANN_NONE), data(NULL) { }; Matrix_(void* data_, size_t rows_, size_t cols_, flann_datatype_t type_, size_t stride_ = 0) : rows(rows_), cols(cols_), stride(stride_), type(type_) { data = static_cast(data_); if (stride==0) stride = flann_datatype_size(type)*cols; } /** * Operator that returns a (pointer to a) row of the data. */ inline void* operator[](size_t index) const { return data+index*stride; } void* ptr() const { return data; } size_t rows; size_t cols; size_t stride; flann_datatype_t type; protected: uchar* data; template void serialize(Archive& ar) { ar & rows; ar & cols; ar & stride; ar & type; if (Archive::is_loading::value) { data = new uchar[rows*stride]; } ar & serialization::make_binary_object(data, rows*stride); } friend struct serialization::access; }; /** * Class that implements a simple rectangular matrix stored in a memory buffer and * provides convenient matrix-like access using the [] operators. * * This class has the same memory structure as the un-templated class flann::Matrix_ and * it's directly convertible from it. */ template class Matrix : public Matrix_ { public: typedef T type; Matrix() : Matrix_() { } Matrix(T* data_, size_t rows_, size_t cols_, size_t stride_ = 0) : Matrix_(data_, rows_, cols_, flann_datatype_value::value, stride_) { if (stride==0) stride = sizeof(T)*cols; } /** * Operator that returns a (pointer to a) row of the data. */ inline T* operator[](size_t index) const { return reinterpret_cast(data+index*stride); } T* ptr() const { return reinterpret_cast(data); } }; } #endif //FLANN_DATASET_H_ flann-1.8.4-src/src/cpp/flann/util/timer.h0000644000000000000000000000463212075346130017030 0ustar rootroot/*********************************************************************** * Software License Agreement (BSD License) * * Copyright 2008-2009 Marius Muja (mariusm@cs.ubc.ca). All rights reserved. * Copyright 2008-2009 David G. Lowe (lowe@cs.ubc.ca). All rights reserved. * * THE BSD LICENSE * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #ifndef FLANN_TIMER_H #define FLANN_TIMER_H #include namespace flann { /** * A start-stop timer class. * * Can be used to time portions of code. */ class StartStopTimer { clock_t startTime; public: /** * Value of the timer. */ double value; /** * Constructor. */ StartStopTimer() { reset(); } /** * Starts the timer. */ void start() { startTime = clock(); } /** * Stops the timer and updates timer value. */ double stop() { clock_t stopTime = clock(); value += ( (double)stopTime - startTime) / CLOCKS_PER_SEC; return value; } /** * Resets the timer value to 0. */ void reset() { value = 0; } }; } #endif // FLANN_TIMER_H flann-1.8.4-src/src/cpp/flann/util/saving.h0000644000000000000000000000721612075346130017200 0ustar rootroot/*********************************************************************** * Software License Agreement (BSD License) * * Copyright 2008-2009 Marius Muja (mariusm@cs.ubc.ca). All rights reserved. * Copyright 2008-2009 David G. Lowe (lowe@cs.ubc.ca). All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE NNIndexGOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #ifndef FLANN_SAVING_H_ #define FLANN_SAVING_H_ #include #include #include #include "flann/general.h" #include "flann/util/serialization.h" #ifdef FLANN_SIGNATURE_ #undef FLANN_SIGNATURE_ #endif #define FLANN_SIGNATURE_ "FLANN_INDEX" namespace flann { /** * Structure representing the index header. */ struct IndexHeader { char signature[16]; char version[16]; flann_datatype_t data_type; flann_algorithm_t index_type; flann_distance_t distance_type; size_t rows; size_t cols; IndexHeader() { memset(signature, 0, sizeof(signature)); strcpy(signature, FLANN_SIGNATURE_); memset(version, 0, sizeof(version)); strcpy(version, FLANN_VERSION_); } private: template void serialize(Archive& ar) { ar & signature; ar & version; ar & data_type; ar & index_type; ar & rows; ar & cols; } friend struct serialization::access; }; /** * Saves index header to stream * * @param stream - Stream to save to * @param index - The index to save */ template void save_header(FILE* stream, const Index& index) { IndexHeader header; header.data_type = flann_datatype_value::value; header.index_type = index.getType(); header.rows = index.size(); header.cols = index.veclen(); fwrite(&header, sizeof(header),1,stream); } /** * * @param stream - Stream to load from * @return Index header */ inline IndexHeader load_header(FILE* stream) { IndexHeader header; int read_size = fread(&header,sizeof(header),1,stream); if (read_size!=1) { throw FLANNException("Invalid index file, cannot read"); } if (strcmp(header.signature,FLANN_SIGNATURE_)!=0) { throw FLANNException("Invalid index file, wrong signature"); } return header; } namespace serialization { ENUM_SERIALIZER(flann_algorithm_t); ENUM_SERIALIZER(flann_centers_init_t); ENUM_SERIALIZER(flann_log_level_t); ENUM_SERIALIZER(flann_datatype_t); } } #endif /* FLANN_SAVING_H_ */ flann-1.8.4-src/src/cpp/flann/util/object_factory.h0000644000000000000000000000547312075346130020711 0ustar rootroot/*********************************************************************** * Software License Agreement (BSD License) * * Copyright 2008-2009 Marius Muja (mariusm@cs.ubc.ca). All rights reserved. * Copyright 2008-2009 David G. Lowe (lowe@cs.ubc.ca). All rights reserved. * * THE BSD LICENSE * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #ifndef FLANN_OBJECT_FACTORY_H_ #define FLANN_OBJECT_FACTORY_H_ #include namespace flann { class CreatorNotFound { }; template class ObjectFactory { typedef ObjectFactory ThisClass; typedef std::map ObjectRegistry; // singleton class, private constructor ObjectFactory() {} public: bool subscribe(UniqueIdType id, ObjectCreator creator) { if (object_registry.find(id) != object_registry.end()) return false; object_registry[id] = creator; return true; } bool unregister(UniqueIdType id) { return object_registry.erase(id) == 1; } ObjectCreator create(UniqueIdType id) { typename ObjectRegistry::const_iterator iter = object_registry.find(id); if (iter == object_registry.end()) { throw CreatorNotFound(); } return iter->second; } static ThisClass& instance() { static ThisClass the_factory; return the_factory; } private: ObjectRegistry object_registry; }; } #endif /* FLANN_OBJECT_FACTORY_H_ */ flann-1.8.4-src/src/cpp/flann/util/sampling.h0000644000000000000000000000504412075346130017520 0ustar rootroot/*********************************************************************** * Software License Agreement (BSD License) * * Copyright 2008-2009 Marius Muja (mariusm@cs.ubc.ca). All rights reserved. * Copyright 2008-2009 David G. Lowe (lowe@cs.ubc.ca). All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #ifndef FLANN_SAMPLING_H_ #define FLANN_SAMPLING_H_ #include "flann/util/matrix.h" #include "flann/util/random.h" namespace flann { template Matrix random_sample(Matrix& srcMatrix, size_t size, bool remove = false) { UniqueRandom rand_unique(srcMatrix.rows); Matrix newSet(new T[size * srcMatrix.cols], size,srcMatrix.cols); T* src,* dest; for (size_t i=0; i(rand_int(srcMatrix.rows-i)); } else { r = static_cast(rand_unique.next()); } dest = newSet[i]; src = srcMatrix[r]; std::copy(src, src+srcMatrix.cols, dest); if (remove) { src = srcMatrix[srcMatrix.rows-i-1]; dest = srcMatrix[r]; std::copy(src, src+srcMatrix.cols, dest); } } if (remove) { srcMatrix.rows -= size; } return newSet; } } // namespace #endif /* FLANN_SAMPLING_H_ */ flann-1.8.4-src/src/cpp/flann/util/random.h0000644000000000000000000000741212075346130017167 0ustar rootroot/*********************************************************************** * Software License Agreement (BSD License) * * Copyright 2008-2009 Marius Muja (mariusm@cs.ubc.ca). All rights reserved. * Copyright 2008-2009 David G. Lowe (lowe@cs.ubc.ca). All rights reserved. * * THE BSD LICENSE * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #ifndef FLANN_RANDOM_H #define FLANN_RANDOM_H #include #include #include #include #include "flann/general.h" namespace flann { /** * Seeds the random number generator * @param seed Random seed */ inline void seed_random(unsigned int seed) { srand(seed); } /* * Generates a random double value. */ /** * Generates a random double value. * @param high Upper limit * @param low Lower limit * @return Random double value */ inline double rand_double(double high = 1.0, double low = 0) { return low + ((high-low) * (std::rand() / (RAND_MAX + 1.0))); } /** * Generates a random integer value. * @param high Upper limit * @param low Lower limit * @return Random integer value */ inline int rand_int(int high = RAND_MAX, int low = 0) { return low + (int) ( double(high-low) * (std::rand() / (RAND_MAX + 1.0))); } class RandomGenerator { public: ptrdiff_t operator() (ptrdiff_t i) { return rand_int(i); } }; /** * Random number generator that returns a distinct number from * the [0,n) interval each time. */ class UniqueRandom { std::vector vals_; int size_; int counter_; public: /** * Constructor. * @param n Size of the interval from which to generate * @return */ UniqueRandom(int n) { init(n); } /** * Initializes the number generator. * @param n the size of the interval from which to generate random numbers. */ void init(int n) { static RandomGenerator generator; // create and initialize an array of size n vals_.resize(n); size_ = n; for (int i = 0; i < size_; ++i) vals_[i] = i; // shuffle the elements in the array std::random_shuffle(vals_.begin(), vals_.end(), generator); counter_ = 0; } /** * Return a distinct random integer in greater or equal to 0 and less * than 'n' on each call. It should be called maximum 'n' times. * Returns: a random integer */ int next() { if (counter_ == size_) { return -1; } else { return vals_[counter_++]; } } }; } #endif //FLANN_RANDOM_H flann-1.8.4-src/src/cpp/flann/util/dynamic_bitset.h0000644000000000000000000001107712075346130020707 0ustar rootroot/*********************************************************************** * Software License Agreement (BSD License) * * Copyright 2008-2009 Marius Muja (mariusm@cs.ubc.ca). All rights reserved. * Copyright 2008-2009 David G. Lowe (lowe@cs.ubc.ca). All rights reserved. * * THE BSD LICENSE * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ /*********************************************************************** * Author: Vincent Rabaud *************************************************************************/ #ifndef FLANN_DYNAMIC_BITSET_H_ #define FLANN_DYNAMIC_BITSET_H_ //#define FLANN_USE_BOOST 1 #if FLANN_USE_BOOST #include typedef boost::dynamic_bitset<> DynamicBitset; #else #include namespace flann { /** Class re-implementing the boost version of it * This helps not depending on boost, it also does not do the bound checks * and has a way to reset a block for speed */ class DynamicBitset { public: /** @param default constructor */ DynamicBitset() : size_(0) { } /** @param only constructor we use in our code * @param the size of the bitset (in bits) */ DynamicBitset(size_t size) { resize(size); reset(); } /** Sets all the bits to 0 */ void clear() { std::fill(bitset_.begin(), bitset_.end(), 0); } /** @brief checks if the bitset is empty * @return true if the bitset is empty */ bool empty() const { return bitset_.empty(); } /** @param set all the bits to 0 */ void reset() { std::fill(bitset_.begin(), bitset_.end(), 0); } /** @brief set one bit to 0 * @param */ void reset(size_t index) { bitset_[index / cell_bit_size_] &= ~(size_t(1) << (index % cell_bit_size_)); } /** @brief sets a specific bit to 0, and more bits too * This function is useful when resetting a given set of bits so that the * whole bitset ends up being 0: if that's the case, we don't care about setting * other bits to 0 * @param */ void reset_block(size_t index) { bitset_[index / cell_bit_size_] = 0; } /** @param resize the bitset so that it contains at least size bits * @param size */ void resize(size_t size) { size_ = size; bitset_.resize(size / cell_bit_size_ + 1); } /** @param set a bit to true * @param index the index of the bit to set to 1 */ void set(size_t index) { bitset_[index / cell_bit_size_] |= size_t(1) << (index % cell_bit_size_); } /** @param gives the number of contained bits */ size_t size() const { return size_; } /** @param check if a bit is set * @param index the index of the bit to check * @return true if the bit is set */ bool test(size_t index) const { return (bitset_[index / cell_bit_size_] & (size_t(1) << (index % cell_bit_size_))) != 0; } private: template void serialize(Archive& ar) { ar & size_; ar & bitset_; } friend struct serialization::access; private: std::vector bitset_; size_t size_; static const unsigned int cell_bit_size_ = CHAR_BIT * sizeof(size_t); }; } // namespace flann #endif #endif // FLANN_DYNAMIC_BITSET_H_ flann-1.8.4-src/src/cpp/flann/util/any.h0000644000000000000000000001605212075346130016476 0ustar rootroot#ifndef FLANN_ANY_H_ #define FLANN_ANY_H_ /* * (C) Copyright Christopher Diggins 2005-2011 * (C) Copyright Pablo Aguilar 2005 * (C) Copyright Kevlin Henney 2001 * * Distributed under the Boost Software License, Version 1.0. (See * accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt * * Adapted for FLANN by Marius Muja */ #include #include #include namespace flann { namespace anyimpl { struct bad_any_cast : public std::runtime_error { bad_any_cast() : std::runtime_error("Cannot convert 'any' value") { } }; struct empty_any { }; inline std::ostream& operator <<(std::ostream& out, const empty_any&) { out << "[empty_any]"; return out; } struct base_any_policy { virtual void static_delete(void** x) = 0; virtual void copy_from_value(void const* src, void** dest) = 0; virtual void clone(void* const* src, void** dest) = 0; virtual void move(void* const* src, void** dest) = 0; virtual void* get_value(void** src) = 0; virtual ::size_t get_size() = 0; virtual const std::type_info& type() = 0; virtual void print(std::ostream& out, void* const* src) = 0; }; template struct typed_base_any_policy : base_any_policy { virtual ::size_t get_size() { return sizeof(T); } virtual const std::type_info& type() { return typeid(T); } }; template struct small_any_policy : typed_base_any_policy { virtual void static_delete(void**) { } virtual void copy_from_value(void const* src, void** dest) { new (dest) T(* reinterpret_cast(src)); } virtual void clone(void* const* src, void** dest) { *dest = *src; } virtual void move(void* const* src, void** dest) { *dest = *src; } virtual void* get_value(void** src) { return reinterpret_cast(src); } virtual void print(std::ostream& out, void* const* src) { out << *reinterpret_cast(src); } }; template struct big_any_policy : typed_base_any_policy { virtual void static_delete(void** x) { if (* x) delete (* reinterpret_cast(x)); *x = NULL; } virtual void copy_from_value(void const* src, void** dest) { *dest = new T(*reinterpret_cast(src)); } virtual void clone(void* const* src, void** dest) { *dest = new T(**reinterpret_cast(src)); } virtual void move(void* const* src, void** dest) { (*reinterpret_cast(dest))->~T(); **reinterpret_cast(dest) = **reinterpret_cast(src); } virtual void* get_value(void** src) { return *src; } virtual void print(std::ostream& out, void* const* src) { out << *reinterpret_cast(*src); } }; template struct choose_policy { typedef big_any_policy type; }; template struct choose_policy { typedef small_any_policy type; }; struct any; /// Choosing the policy for an any type is illegal, but should never happen. /// This is designed to throw a compiler error. template<> struct choose_policy { typedef void type; }; /// Specializations for small types. #define SMALL_POLICY(TYPE) \ template<> \ struct choose_policy { typedef small_any_policy type; \ } SMALL_POLICY(signed char); SMALL_POLICY(unsigned char); SMALL_POLICY(signed short); SMALL_POLICY(unsigned short); SMALL_POLICY(signed int); SMALL_POLICY(unsigned int); SMALL_POLICY(signed long); SMALL_POLICY(unsigned long); SMALL_POLICY(float); SMALL_POLICY(bool); //#undef SMALL_POLICY /// This function will return a different policy for each type. template base_any_policy* get_policy() { static typename choose_policy::type policy; return &policy; } } // namespace anyimpl class any { typedef any any_t; // workaround for the NVCC compiler under windows private: // fields anyimpl::base_any_policy* policy; void* object; public: /// Initializing constructor. template any(const T& x) : policy(anyimpl::get_policy()), object(NULL) { assign(x); } /// Empty constructor. any() : policy(anyimpl::get_policy()), object(NULL) { } /// Special initializing constructor for string literals. any(const char* x) : policy(anyimpl::get_policy()), object(NULL) { assign(x); } /// Copy constructor. any(const any& x) : policy(anyimpl::get_policy()), object(NULL) { assign(x); } /// Destructor. ~any() { policy->static_delete(&object); } /// Assignment function from another any. any& assign(const any& x) { reset(); policy = x.policy; policy->clone(&x.object, &object); return *this; } /// Assignment function. template any_t& assign(const T& x) { reset(); policy = anyimpl::get_policy(); policy->copy_from_value(&x, &object); return *this; } /// Assignment operator. template any_t& operator=(const T& x) { return assign(x); } /// Assignment operator, specialed for literal strings. /// They have types like const char [6] which don't work as expected. any& operator=(const char* x) { return assign(x); } /// Utility functions any& swap(any& x) { std::swap(policy, x.policy); std::swap(object, x.object); return *this; } /// Cast operator. You can only cast to the original type. template T& cast() { if (policy->type() != typeid(T)) throw anyimpl::bad_any_cast(); T* r = reinterpret_cast(policy->get_value(&object)); return *r; } /// Cast operator. You can only cast to the original type. template const T& cast() const { if (policy->type() != typeid(T)) throw anyimpl::bad_any_cast(); void* obj = const_cast(object); T* r = reinterpret_cast(policy->get_value(&obj)); return *r; } /// Returns true if the any contains no value. bool empty() const { return policy->type() == typeid(anyimpl::empty_any); } /// Frees any allocated memory, and sets the value to NULL. void reset() { policy->static_delete(&object); policy = anyimpl::get_policy(); } /// Returns true if the two types are the same. bool compatible(const any& x) const { return policy->type() == x.policy->type(); } /// Returns if the type is compatible with the policy template bool has_type() { return policy->type() == typeid(T); } const std::type_info& type() const { return policy->type(); } friend std::ostream& operator <<(std::ostream& out, const any& any_val); }; inline std::ostream& operator <<(std::ostream& out, const any& any_val) { any_val.policy->print(out,&any_val.object); return out; } } #endif // FLANN_ANY_H_ flann-1.8.4-src/src/cpp/flann/util/params.h0000644000000000000000000001031112075346130017162 0ustar rootroot/*********************************************************************** * Software License Agreement (BSD License) * * Copyright 2008-2011 Marius Muja (mariusm@cs.ubc.ca). All rights reserved. * Copyright 2008-2011 David G. Lowe (lowe@cs.ubc.ca). All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #ifndef FLANN_PARAMS_H_ #define FLANN_PARAMS_H_ #include "any.h" #include "flann/general.h" #include #include namespace flann { namespace anyimpl { SMALL_POLICY(flann_algorithm_t); SMALL_POLICY(flann_centers_init_t); SMALL_POLICY(flann_log_level_t); SMALL_POLICY(flann_datatype_t); } typedef std::map IndexParams; typedef enum { FLANN_False = 0, FLANN_True = 1, FLANN_Undefined } tri_type; struct SearchParams { SearchParams(int checks_ = 32, float eps_ = 0.0, bool sorted_ = true ) : checks(checks_), eps(eps_), sorted(sorted_) { max_neighbors = -1; use_heap = FLANN_Undefined; cores = 1; matrices_in_gpu_ram = false; } // how many leafs to visit when searching for neighbours (-1 for unlimited) int checks; // search for eps-approximate neighbours (default: 0) float eps; // only for radius search, require neighbours sorted by distance (default: true) bool sorted; // maximum number of neighbors radius search should return (-1 for unlimited) int max_neighbors; // use a heap to manage the result set (default: FLANN_Undefined) tri_type use_heap; // how many cores to assign to the search (used only if compiled with OpenMP capable compiler) (0 for auto) int cores; // for GPU search indicates if matrices are already in GPU ram bool matrices_in_gpu_ram; }; inline bool has_param(const IndexParams& params, std::string name) { return params.find(name)!=params.end(); } template T get_param(const IndexParams& params, std::string name, const T& default_value) { IndexParams::const_iterator it = params.find(name); if (it != params.end()) { return it->second.cast(); } else { return default_value; } } template T get_param(const IndexParams& params, std::string name) { IndexParams::const_iterator it = params.find(name); if (it != params.end()) { return it->second.cast(); } else { throw FLANNException(std::string("Missing parameter '")+name+std::string("' in the parameters given")); } } inline void print_params(const IndexParams& params) { IndexParams::const_iterator it; for(it=params.begin(); it!=params.end(); ++it) { std::cout << it->first << " : " << it->second << std::endl; } } inline void print_params(const SearchParams& params) { std::cout << "checks : " << params.checks << std::endl; std::cout << "eps : " << params.eps << std::endl; std::cout << "sorted : " << params.sorted << std::endl; std::cout << "max_neighbors : " << params.max_neighbors << std::endl; } } #endif /* FLANN_PARAMS_H_ */ flann-1.8.4-src/src/cpp/flann/util/lsh_table.h0000644000000000000000000004251012075346130017642 0ustar rootroot/*********************************************************************** * Software License Agreement (BSD License) * * Copyright 2008-2009 Marius Muja (mariusm@cs.ubc.ca). All rights reserved. * Copyright 2008-2009 David G. Lowe (lowe@cs.ubc.ca). All rights reserved. * * THE BSD LICENSE * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ /*********************************************************************** * Author: Vincent Rabaud *************************************************************************/ #ifndef FLANN_LSH_TABLE_H_ #define FLANN_LSH_TABLE_H_ #include #include #include #include // TODO as soon as we use C++0x, use the code in USE_UNORDERED_MAP #if USE_UNORDERED_MAP #include #else #include #endif #include #include #include "flann/util/dynamic_bitset.h" #include "flann/util/matrix.h" namespace flann { namespace lsh { //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** What is stored in an LSH bucket */ typedef uint32_t FeatureIndex; /** The id from which we can get a bucket back in an LSH table */ typedef unsigned int BucketKey; /** A bucket in an LSH table */ typedef std::vector Bucket; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** POD for stats about an LSH table */ struct LshStats { std::vector bucket_sizes_; size_t n_buckets_; size_t bucket_size_mean_; size_t bucket_size_median_; size_t bucket_size_min_; size_t bucket_size_max_; size_t bucket_size_std_dev; /** Each contained vector contains three value: beginning/end for interval, number of elements in the bin */ std::vector > size_histogram_; }; /** Overload the << operator for LshStats * @param out the streams * @param stats the stats to display * @return the streams */ inline std::ostream& operator <<(std::ostream& out, const LshStats& stats) { size_t w = 20; out << "Lsh Table Stats:\n" << std::setw(w) << std::setiosflags(std::ios::right) << "N buckets : " << stats.n_buckets_ << "\n" << std::setw(w) << std::setiosflags(std::ios::right) << "mean size : " << std::setiosflags(std::ios::left) << stats.bucket_size_mean_ << "\n" << std::setw(w) << std::setiosflags(std::ios::right) << "median size : " << stats.bucket_size_median_ << "\n" << std::setw(w) << std::setiosflags(std::ios::right) << "min size : " << std::setiosflags(std::ios::left) << stats.bucket_size_min_ << "\n" << std::setw(w) << std::setiosflags(std::ios::right) << "max size : " << std::setiosflags(std::ios::left) << stats.bucket_size_max_; // Display the histogram out << std::endl << std::setw(w) << std::setiosflags(std::ios::right) << "histogram : " << std::setiosflags(std::ios::left); for (std::vector >::const_iterator iterator = stats.size_histogram_.begin(), end = stats.size_histogram_.end(); iterator != end; ++iterator) out << (*iterator)[0] << "-" << (*iterator)[1] << ": " << (*iterator)[2] << ", "; return out; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** Lsh hash table. As its key is a sub-feature, and as usually * the size of it is pretty small, we keep it as a continuous memory array. * The value is an index in the corpus of features (we keep it as an unsigned * int for pure memory reasons, it could be a size_t) */ template class LshTable { public: /** A container of all the feature indices. Optimized for space */ #if USE_UNORDERED_MAP typedef std::unordered_map BucketsSpace; #else typedef std::map BucketsSpace; #endif /** A container of all the feature indices. Optimized for speed */ typedef std::vector BucketsSpeed; /** Default constructor */ LshTable() { } /** Default constructor * Create the mask and allocate the memory * @param feature_size is the size of the feature (considered as a ElementType[]) * @param key_size is the number of bits that are turned on in the feature */ LshTable(unsigned int /*feature_size*/, unsigned int /*key_size*/) { std::cerr << "LSH is not implemented for that type" << std::endl; throw; } /** Add a feature to the table * @param value the value to store for that feature * @param feature the feature itself */ void add(unsigned int value, const ElementType* feature) { // Add the value to the corresponding bucket BucketKey key = getKey(feature); switch (speed_level_) { case kArray: // That means we get the buckets from an array buckets_speed_[key].push_back(value); break; case kBitsetHash: // That means we can check the bitset for the presence of a key key_bitset_.set(key); buckets_space_[key].push_back(value); break; case kHash: { // That means we have to check for the hash table for the presence of a key buckets_space_[key].push_back(value); break; } } } /** Add a set of features to the table * @param dataset the values to store */ void add(const std::vector< std::pair >& features) { #if USE_UNORDERED_MAP buckets_space_.rehash((buckets_space_.size() + features.size()) * 1.2); #endif // Add the features to the table for (size_t i = 0; i < features.size(); ++i) { add(features[i].first, features[i].second); } // Now that the table is full, optimize it for speed/space optimize(); } /** Get a bucket given the key * @param key * @return */ inline const Bucket* getBucketFromKey(BucketKey key) const { // Generate other buckets switch (speed_level_) { case kArray: // That means we get the buckets from an array return &buckets_speed_[key]; break; case kBitsetHash: // That means we can check the bitset for the presence of a key if (key_bitset_.test(key)) return &buckets_space_.find(key)->second; else return 0; break; case kHash: { // That means we have to check for the hash table for the presence of a key BucketsSpace::const_iterator bucket_it, bucket_end = buckets_space_.end(); bucket_it = buckets_space_.find(key); // Stop here if that bucket does not exist if (bucket_it == bucket_end) return 0; else return &bucket_it->second; break; } } return 0; } /** Compute the sub-signature of a feature */ size_t getKey(const ElementType* /*feature*/) const { std::cerr << "LSH is not implemented for that type" << std::endl; throw; return 1; } /** Get statistics about the table * @return */ LshStats getStats() const; private: /** defines the speed fo the implementation * kArray uses a vector for storing data * kBitsetHash uses a hash map but checks for the validity of a key with a bitset * kHash uses a hash map only */ enum SpeedLevel { kArray, kBitsetHash, kHash }; /** Initialize some variables */ void initialize(size_t key_size) { speed_level_ = kHash; key_size_ = key_size; } /** Optimize the table for speed/space */ void optimize() { // If we are already using the fast storage, no need to do anything if (speed_level_ == kArray) return; // Use an array if it will be more than half full if (buckets_space_.size() > (unsigned int)((1 << key_size_) / 2)) { speed_level_ = kArray; // Fill the array version of it buckets_speed_.resize(1 << key_size_); for (BucketsSpace::const_iterator key_bucket = buckets_space_.begin(); key_bucket != buckets_space_.end(); ++key_bucket) buckets_speed_[key_bucket->first] = key_bucket->second; // Empty the hash table buckets_space_.clear(); return; } // If the bitset is going to use less than 10% of the RAM of the hash map (at least 1 size_t for the key and two // for the vector) or less than 512MB (key_size_ <= 30) if (((std::max(buckets_space_.size(), buckets_speed_.size()) * CHAR_BIT * 3 * sizeof(BucketKey)) / 10 >= size_t(1 << key_size_)) || (key_size_ <= 32)) { speed_level_ = kBitsetHash; key_bitset_.resize(1 << key_size_); key_bitset_.reset(); // Try with the BucketsSpace for (BucketsSpace::const_iterator key_bucket = buckets_space_.begin(); key_bucket != buckets_space_.end(); ++key_bucket) key_bitset_.set(key_bucket->first); } else { speed_level_ = kHash; key_bitset_.clear(); } } template void serialize(Archive& ar) { int val; if (Archive::is_saving::value) { val = (int)speed_level_; } ar & val; if (Archive::is_loading::value) { speed_level_ = (SpeedLevel) val; } ar & key_size_; ar & mask_; if (speed_level_==kArray) { ar & buckets_speed_; } if (speed_level_==kBitsetHash || speed_level_==kHash) { ar & buckets_space_; } if (speed_level_==kBitsetHash) { ar & key_bitset_; } } friend struct serialization::access; /** The vector of all the buckets if they are held for speed */ BucketsSpeed buckets_speed_; /** The hash table of all the buckets in case we cannot use the speed version */ BucketsSpace buckets_space_; /** What is used to store the data */ SpeedLevel speed_level_; /** If the subkey is small enough, it will keep track of which subkeys are set through that bitset * That is just a speedup so that we don't look in the hash table (which can be mush slower that checking a bitset) */ DynamicBitset key_bitset_; /** The size of the sub-signature in bits */ unsigned int key_size_; // Members only used for the unsigned char specialization /** The mask to apply to a feature to get the hash key * Only used in the unsigned char case */ std::vector mask_; }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Specialization for unsigned char template<> inline LshTable::LshTable(unsigned int feature_size, unsigned int subsignature_size) { initialize(subsignature_size); // Allocate the mask mask_ = std::vector((size_t)ceil((float)(feature_size * sizeof(char)) / (float)sizeof(size_t)), 0); // A bit brutal but fast to code std::vector indices(feature_size * CHAR_BIT); for (size_t i = 0; i < feature_size * CHAR_BIT; ++i) indices[i] = i; std::random_shuffle(indices.begin(), indices.end()); // Generate a random set of order of subsignature_size_ bits for (unsigned int i = 0; i < key_size_; ++i) { size_t index = indices[i]; // Set that bit in the mask size_t divisor = CHAR_BIT * sizeof(size_t); size_t idx = index / divisor; //pick the right size_t index mask_[idx] |= size_t(1) << (index % divisor); //use modulo to find the bit offset } // Set to 1 if you want to display the mask for debug #if 0 { size_t bcount = 0; BOOST_FOREACH(size_t mask_block, mask_){ out << std::setw(sizeof(size_t) * CHAR_BIT / 4) << std::setfill('0') << std::hex << mask_block << std::endl; bcount += __builtin_popcountll(mask_block); } out << "bit count : " << std::dec << bcount << std::endl; out << "mask size : " << mask_.size() << std::endl; return out; } #endif } /** Return the Subsignature of a feature * @param feature the feature to analyze */ template<> inline size_t LshTable::getKey(const unsigned char* feature) const { // no need to check if T is dividable by sizeof(size_t) like in the Hamming // distance computation as we have a mask const size_t* feature_block_ptr = reinterpret_cast (feature); // Figure out the subsignature of the feature // Given the feature ABCDEF, and the mask 001011, the output will be // 000CEF size_t subsignature = 0; size_t bit_index = 1; for (std::vector::const_iterator pmask_block = mask_.begin(); pmask_block != mask_.end(); ++pmask_block) { // get the mask and signature blocks size_t feature_block = *feature_block_ptr; size_t mask_block = *pmask_block; while (mask_block) { // Get the lowest set bit in the mask block size_t lowest_bit = mask_block & (-(ptrdiff_t)mask_block); // Add it to the current subsignature if necessary subsignature += (feature_block & lowest_bit) ? bit_index : 0; // Reset the bit in the mask block mask_block ^= lowest_bit; // increment the bit index for the subsignature bit_index <<= 1; } // Check the next feature block ++feature_block_ptr; } return subsignature; } template<> inline LshStats LshTable::getStats() const { LshStats stats; stats.bucket_size_mean_ = 0; if ((buckets_speed_.empty()) && (buckets_space_.empty())) { stats.n_buckets_ = 0; stats.bucket_size_median_ = 0; stats.bucket_size_min_ = 0; stats.bucket_size_max_ = 0; return stats; } if (!buckets_speed_.empty()) { for (BucketsSpeed::const_iterator pbucket = buckets_speed_.begin(); pbucket != buckets_speed_.end(); ++pbucket) { stats.bucket_sizes_.push_back(pbucket->size()); stats.bucket_size_mean_ += pbucket->size(); } stats.bucket_size_mean_ /= buckets_speed_.size(); stats.n_buckets_ = buckets_speed_.size(); } else { for (BucketsSpace::const_iterator x = buckets_space_.begin(); x != buckets_space_.end(); ++x) { stats.bucket_sizes_.push_back(x->second.size()); stats.bucket_size_mean_ += x->second.size(); } stats.bucket_size_mean_ /= buckets_space_.size(); stats.n_buckets_ = buckets_space_.size(); } std::sort(stats.bucket_sizes_.begin(), stats.bucket_sizes_.end()); // BOOST_FOREACH(int size, stats.bucket_sizes_) // std::cout << size << " "; // std::cout << std::endl; stats.bucket_size_median_ = stats.bucket_sizes_[stats.bucket_sizes_.size() / 2]; stats.bucket_size_min_ = stats.bucket_sizes_.front(); stats.bucket_size_max_ = stats.bucket_sizes_.back(); // TODO compute mean and std /*float mean, stddev; stats.bucket_size_mean_ = mean; stats.bucket_size_std_dev = stddev;*/ // Include a histogram of the buckets unsigned int bin_start = 0; unsigned int bin_end = 20; bool is_new_bin = true; for (std::vector::iterator iterator = stats.bucket_sizes_.begin(), end = stats.bucket_sizes_.end(); iterator != end; ) if (*iterator < bin_end) { if (is_new_bin) { stats.size_histogram_.push_back(std::vector(3, 0)); stats.size_histogram_.back()[0] = bin_start; stats.size_histogram_.back()[1] = bin_end - 1; is_new_bin = false; } ++stats.size_histogram_.back()[2]; ++iterator; } else { bin_start += 20; bin_end += 20; is_new_bin = true; } return stats; } // End the two namespaces } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #endif /* FLANN_LSH_TABLE_H_ */ flann-1.8.4-src/src/cpp/flann/util/cutil_math.h0000644000000000000000000010757212075346130020050 0ustar rootroot/* * Copyright 1993-2010 NVIDIA Corporation. All rights reserved. * * Please refer to the NVIDIA end user license agreement (EULA) associated * with this source code for terms and conditions that govern your use of * this software. Any use, reproduction, disclosure, or distribution of * this software and related documentation outside the terms of the EULA * is strictly prohibited. * */ /* This file implements common mathematical operations on vector types (float3, float4 etc.) since these are not provided as standard by CUDA. The syntax is modelled on the Cg standard library. This is part of the CUTIL library and is not supported by NVIDIA. Thanks to Linh Hah for additions and fixes. */ #ifndef CUTIL_MATH_H #define CUTIL_MATH_H #include "cuda_runtime.h" typedef unsigned int uint; typedef unsigned short ushort; #ifndef __CUDACC__ #include //////////////////////////////////////////////////////////////////////////////// // host implementations of CUDA functions //////////////////////////////////////////////////////////////////////////////// inline float fminf(float a, float b) { return a < b ? a : b; } inline float fmaxf(float a, float b) { return a > b ? a : b; } inline int max(int a, int b) { return a > b ? a : b; } inline int min(int a, int b) { return a < b ? a : b; } inline float rsqrtf(float x) { return 1.0f / sqrtf(x); } #endif //////////////////////////////////////////////////////////////////////////////// // constructors //////////////////////////////////////////////////////////////////////////////// inline __host__ __device__ float2 make_float2(float s) { return make_float2(s, s); } inline __host__ __device__ float2 make_float2(float3 a) { return make_float2(a.x, a.y); } inline __host__ __device__ float2 make_float2(int2 a) { return make_float2(float(a.x), float(a.y)); } inline __host__ __device__ float2 make_float2(uint2 a) { return make_float2(float(a.x), float(a.y)); } inline __host__ __device__ int2 make_int2(int s) { return make_int2(s, s); } inline __host__ __device__ int2 make_int2(int3 a) { return make_int2(a.x, a.y); } inline __host__ __device__ int2 make_int2(uint2 a) { return make_int2(int(a.x), int(a.y)); } inline __host__ __device__ int2 make_int2(float2 a) { return make_int2(int(a.x), int(a.y)); } inline __host__ __device__ uint2 make_uint2(uint s) { return make_uint2(s, s); } inline __host__ __device__ uint2 make_uint2(uint3 a) { return make_uint2(a.x, a.y); } inline __host__ __device__ uint2 make_uint2(int2 a) { return make_uint2(uint(a.x), uint(a.y)); } inline __host__ __device__ float3 make_float3(float s) { return make_float3(s, s, s); } inline __host__ __device__ float3 make_float3(float2 a) { return make_float3(a.x, a.y, 0.0f); } inline __host__ __device__ float3 make_float3(float2 a, float s) { return make_float3(a.x, a.y, s); } inline __host__ __device__ float3 make_float3(float4 a) { return make_float3(a.x, a.y, a.z); } inline __host__ __device__ float3 make_float3(int3 a) { return make_float3(float(a.x), float(a.y), float(a.z)); } inline __host__ __device__ float3 make_float3(uint3 a) { return make_float3(float(a.x), float(a.y), float(a.z)); } inline __host__ __device__ int3 make_int3(int s) { return make_int3(s, s, s); } inline __host__ __device__ int3 make_int3(int2 a) { return make_int3(a.x, a.y, 0); } inline __host__ __device__ int3 make_int3(int2 a, int s) { return make_int3(a.x, a.y, s); } inline __host__ __device__ int3 make_int3(uint3 a) { return make_int3(int(a.x), int(a.y), int(a.z)); } inline __host__ __device__ int3 make_int3(float3 a) { return make_int3(int(a.x), int(a.y), int(a.z)); } inline __host__ __device__ uint3 make_uint3(uint s) { return make_uint3(s, s, s); } inline __host__ __device__ uint3 make_uint3(uint2 a) { return make_uint3(a.x, a.y, 0); } inline __host__ __device__ uint3 make_uint3(uint2 a, uint s) { return make_uint3(a.x, a.y, s); } inline __host__ __device__ uint3 make_uint3(uint4 a) { return make_uint3(a.x, a.y, a.z); } inline __host__ __device__ uint3 make_uint3(int3 a) { return make_uint3(uint(a.x), uint(a.y), uint(a.z)); } inline __host__ __device__ float4 make_float4(float s) { return make_float4(s, s, s, s); } inline __host__ __device__ float4 make_float4(float3 a) { return make_float4(a.x, a.y, a.z, 0.0f); } inline __host__ __device__ float4 make_float4(float3 a, float w) { return make_float4(a.x, a.y, a.z, w); } inline __host__ __device__ float4 make_float4(int4 a) { return make_float4(float(a.x), float(a.y), float(a.z), float(a.w)); } inline __host__ __device__ float4 make_float4(uint4 a) { return make_float4(float(a.x), float(a.y), float(a.z), float(a.w)); } inline __host__ __device__ int4 make_int4(int s) { return make_int4(s, s, s, s); } inline __host__ __device__ int4 make_int4(int3 a) { return make_int4(a.x, a.y, a.z, 0); } inline __host__ __device__ int4 make_int4(int3 a, int w) { return make_int4(a.x, a.y, a.z, w); } inline __host__ __device__ int4 make_int4(uint4 a) { return make_int4(int(a.x), int(a.y), int(a.z), int(a.w)); } inline __host__ __device__ int4 make_int4(float4 a) { return make_int4(int(a.x), int(a.y), int(a.z), int(a.w)); } inline __host__ __device__ uint4 make_uint4(uint s) { return make_uint4(s, s, s, s); } inline __host__ __device__ uint4 make_uint4(uint3 a) { return make_uint4(a.x, a.y, a.z, 0); } inline __host__ __device__ uint4 make_uint4(uint3 a, uint w) { return make_uint4(a.x, a.y, a.z, w); } inline __host__ __device__ uint4 make_uint4(int4 a) { return make_uint4(uint(a.x), uint(a.y), uint(a.z), uint(a.w)); } //////////////////////////////////////////////////////////////////////////////// // negate //////////////////////////////////////////////////////////////////////////////// inline __host__ __device__ float2 operator-(float2 &a) { return make_float2(-a.x, -a.y); } inline __host__ __device__ int2 operator-(int2 &a) { return make_int2(-a.x, -a.y); } inline __host__ __device__ float3 operator-(float3 &a) { return make_float3(-a.x, -a.y, -a.z); } inline __host__ __device__ int3 operator-(int3 &a) { return make_int3(-a.x, -a.y, -a.z); } inline __host__ __device__ float4 operator-(float4 &a) { return make_float4(-a.x, -a.y, -a.z, -a.w); } inline __host__ __device__ int4 operator-(int4 &a) { return make_int4(-a.x, -a.y, -a.z, -a.w); } //////////////////////////////////////////////////////////////////////////////// // addition //////////////////////////////////////////////////////////////////////////////// inline __host__ __device__ float2 operator+(float2 a, float2 b) { return make_float2(a.x + b.x, a.y + b.y); } inline __host__ __device__ void operator+=(float2 &a, float2 b) { a.x += b.x; a.y += b.y; } inline __host__ __device__ float2 operator+(float2 a, float b) { return make_float2(a.x + b, a.y + b); } inline __host__ __device__ float2 operator+(float b, float2 a) { return make_float2(a.x + b, a.y + b); } inline __host__ __device__ void operator+=(float2 &a, float b) { a.x += b; a.y += b; } inline __host__ __device__ int2 operator+(int2 a, int2 b) { return make_int2(a.x + b.x, a.y + b.y); } inline __host__ __device__ void operator+=(int2 &a, int2 b) { a.x += b.x; a.y += b.y; } inline __host__ __device__ int2 operator+(int2 a, int b) { return make_int2(a.x + b, a.y + b); } inline __host__ __device__ int2 operator+(int b, int2 a) { return make_int2(a.x + b, a.y + b); } inline __host__ __device__ void operator+=(int2 &a, int b) { a.x += b; a.y += b; } inline __host__ __device__ uint2 operator+(uint2 a, uint2 b) { return make_uint2(a.x + b.x, a.y + b.y); } inline __host__ __device__ void operator+=(uint2 &a, uint2 b) { a.x += b.x; a.y += b.y; } inline __host__ __device__ uint2 operator+(uint2 a, uint b) { return make_uint2(a.x + b, a.y + b); } inline __host__ __device__ uint2 operator+(uint b, uint2 a) { return make_uint2(a.x + b, a.y + b); } inline __host__ __device__ void operator+=(uint2 &a, uint b) { a.x += b; a.y += b; } inline __host__ __device__ float3 operator+(float3 a, float3 b) { return make_float3(a.x + b.x, a.y + b.y, a.z + b.z); } inline __host__ __device__ void operator+=(float3 &a, float3 b) { a.x += b.x; a.y += b.y; a.z += b.z; } inline __host__ __device__ float3 operator+(float3 a, float b) { return make_float3(a.x + b, a.y + b, a.z + b); } inline __host__ __device__ void operator+=(float3 &a, float b) { a.x += b; a.y += b; a.z += b; } inline __host__ __device__ int3 operator+(int3 a, int3 b) { return make_int3(a.x + b.x, a.y + b.y, a.z + b.z); } inline __host__ __device__ void operator+=(int3 &a, int3 b) { a.x += b.x; a.y += b.y; a.z += b.z; } inline __host__ __device__ int3 operator+(int3 a, int b) { return make_int3(a.x + b, a.y + b, a.z + b); } inline __host__ __device__ void operator+=(int3 &a, int b) { a.x += b; a.y += b; a.z += b; } inline __host__ __device__ uint3 operator+(uint3 a, uint3 b) { return make_uint3(a.x + b.x, a.y + b.y, a.z + b.z); } inline __host__ __device__ void operator+=(uint3 &a, uint3 b) { a.x += b.x; a.y += b.y; a.z += b.z; } inline __host__ __device__ uint3 operator+(uint3 a, uint b) { return make_uint3(a.x + b, a.y + b, a.z + b); } inline __host__ __device__ void operator+=(uint3 &a, uint b) { a.x += b; a.y += b; a.z += b; } inline __host__ __device__ int3 operator+(int b, int3 a) { return make_int3(a.x + b, a.y + b, a.z + b); } inline __host__ __device__ uint3 operator+(uint b, uint3 a) { return make_uint3(a.x + b, a.y + b, a.z + b); } inline __host__ __device__ float3 operator+(float b, float3 a) { return make_float3(a.x + b, a.y + b, a.z + b); } inline __host__ __device__ float4 operator+(float4 a, float4 b) { return make_float4(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w); } inline __host__ __device__ void operator+=(float4 &a, float4 b) { a.x += b.x; a.y += b.y; a.z += b.z; a.w += b.w; } inline __host__ __device__ float4 operator+(float4 a, float b) { return make_float4(a.x + b, a.y + b, a.z + b, a.w + b); } inline __host__ __device__ float4 operator+(float b, float4 a) { return make_float4(a.x + b, a.y + b, a.z + b, a.w + b); } inline __host__ __device__ void operator+=(float4 &a, float b) { a.x += b; a.y += b; a.z += b; a.w += b; } inline __host__ __device__ int4 operator+(int4 a, int4 b) { return make_int4(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w); } inline __host__ __device__ void operator+=(int4 &a, int4 b) { a.x += b.x; a.y += b.y; a.z += b.z; a.w += b.w; } inline __host__ __device__ int4 operator+(int4 a, int b) { return make_int4(a.x + b, a.y + b, a.z + b, a.w + b); } inline __host__ __device__ int4 operator+(int b, int4 a) { return make_int4(a.x + b, a.y + b, a.z + b, a.w + b); } inline __host__ __device__ void operator+=(int4 &a, int b) { a.x += b; a.y += b; a.z += b; a.w += b; } inline __host__ __device__ uint4 operator+(uint4 a, uint4 b) { return make_uint4(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w); } inline __host__ __device__ void operator+=(uint4 &a, uint4 b) { a.x += b.x; a.y += b.y; a.z += b.z; a.w += b.w; } inline __host__ __device__ uint4 operator+(uint4 a, uint b) { return make_uint4(a.x + b, a.y + b, a.z + b, a.w + b); } inline __host__ __device__ uint4 operator+(uint b, uint4 a) { return make_uint4(a.x + b, a.y + b, a.z + b, a.w + b); } inline __host__ __device__ void operator+=(uint4 &a, uint b) { a.x += b; a.y += b; a.z += b; a.w += b; } //////////////////////////////////////////////////////////////////////////////// // subtract //////////////////////////////////////////////////////////////////////////////// inline __host__ __device__ float2 operator-(float2 a, float2 b) { return make_float2(a.x - b.x, a.y - b.y); } inline __host__ __device__ void operator-=(float2 &a, float2 b) { a.x -= b.x; a.y -= b.y; } inline __host__ __device__ float2 operator-(float2 a, float b) { return make_float2(a.x - b, a.y - b); } inline __host__ __device__ float2 operator-(float b, float2 a) { return make_float2(b - a.x, b - a.y); } inline __host__ __device__ void operator-=(float2 &a, float b) { a.x -= b; a.y -= b; } inline __host__ __device__ int2 operator-(int2 a, int2 b) { return make_int2(a.x - b.x, a.y - b.y); } inline __host__ __device__ void operator-=(int2 &a, int2 b) { a.x -= b.x; a.y -= b.y; } inline __host__ __device__ int2 operator-(int2 a, int b) { return make_int2(a.x - b, a.y - b); } inline __host__ __device__ int2 operator-(int b, int2 a) { return make_int2(b - a.x, b - a.y); } inline __host__ __device__ void operator-=(int2 &a, int b) { a.x -= b; a.y -= b; } inline __host__ __device__ uint2 operator-(uint2 a, uint2 b) { return make_uint2(a.x - b.x, a.y - b.y); } inline __host__ __device__ void operator-=(uint2 &a, uint2 b) { a.x -= b.x; a.y -= b.y; } inline __host__ __device__ uint2 operator-(uint2 a, uint b) { return make_uint2(a.x - b, a.y - b); } inline __host__ __device__ uint2 operator-(uint b, uint2 a) { return make_uint2(b - a.x, b - a.y); } inline __host__ __device__ void operator-=(uint2 &a, uint b) { a.x -= b; a.y -= b; } inline __host__ __device__ float3 operator-(float3 a, float3 b) { return make_float3(a.x - b.x, a.y - b.y, a.z - b.z); } inline __host__ __device__ void operator-=(float3 &a, float3 b) { a.x -= b.x; a.y -= b.y; a.z -= b.z; } inline __host__ __device__ float3 operator-(float3 a, float b) { return make_float3(a.x - b, a.y - b, a.z - b); } inline __host__ __device__ float3 operator-(float b, float3 a) { return make_float3(b - a.x, b - a.y, b - a.z); } inline __host__ __device__ void operator-=(float3 &a, float b) { a.x -= b; a.y -= b; a.z -= b; } inline __host__ __device__ int3 operator-(int3 a, int3 b) { return make_int3(a.x - b.x, a.y - b.y, a.z - b.z); } inline __host__ __device__ void operator-=(int3 &a, int3 b) { a.x -= b.x; a.y -= b.y; a.z -= b.z; } inline __host__ __device__ int3 operator-(int3 a, int b) { return make_int3(a.x - b, a.y - b, a.z - b); } inline __host__ __device__ int3 operator-(int b, int3 a) { return make_int3(b - a.x, b - a.y, b - a.z); } inline __host__ __device__ void operator-=(int3 &a, int b) { a.x -= b; a.y -= b; a.z -= b; } inline __host__ __device__ uint3 operator-(uint3 a, uint3 b) { return make_uint3(a.x - b.x, a.y - b.y, a.z - b.z); } inline __host__ __device__ void operator-=(uint3 &a, uint3 b) { a.x -= b.x; a.y -= b.y; a.z -= b.z; } inline __host__ __device__ uint3 operator-(uint3 a, uint b) { return make_uint3(a.x - b, a.y - b, a.z - b); } inline __host__ __device__ uint3 operator-(uint b, uint3 a) { return make_uint3(b - a.x, b - a.y, b - a.z); } inline __host__ __device__ void operator-=(uint3 &a, uint b) { a.x -= b; a.y -= b; a.z -= b; } inline __host__ __device__ float4 operator-(float4 a, float4 b) { return make_float4(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w); } inline __host__ __device__ void operator-=(float4 &a, float4 b) { a.x -= b.x; a.y -= b.y; a.z -= b.z; a.w -= b.w; } inline __host__ __device__ float4 operator-(float4 a, float b) { return make_float4(a.x - b, a.y - b, a.z - b, a.w - b); } inline __host__ __device__ void operator-=(float4 &a, float b) { a.x -= b; a.y -= b; a.z -= b; a.w -= b; } inline __host__ __device__ int4 operator-(int4 a, int4 b) { return make_int4(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w); } inline __host__ __device__ void operator-=(int4 &a, int4 b) { a.x -= b.x; a.y -= b.y; a.z -= b.z; a.w -= b.w; } inline __host__ __device__ int4 operator-(int4 a, int b) { return make_int4(a.x - b, a.y - b, a.z - b, a.w - b); } inline __host__ __device__ int4 operator-(int b, int4 a) { return make_int4(b - a.x, b - a.y, b - a.z, b - a.w); } inline __host__ __device__ void operator-=(int4 &a, int b) { a.x -= b; a.y -= b; a.z -= b; a.w -= b; } inline __host__ __device__ uint4 operator-(uint4 a, uint4 b) { return make_uint4(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w); } inline __host__ __device__ void operator-=(uint4 &a, uint4 b) { a.x -= b.x; a.y -= b.y; a.z -= b.z; a.w -= b.w; } inline __host__ __device__ uint4 operator-(uint4 a, uint b) { return make_uint4(a.x - b, a.y - b, a.z - b, a.w - b); } inline __host__ __device__ uint4 operator-(uint b, uint4 a) { return make_uint4(b - a.x, b - a.y, b - a.z, b - a.w); } inline __host__ __device__ void operator-=(uint4 &a, uint b) { a.x -= b; a.y -= b; a.z -= b; a.w -= b; } //////////////////////////////////////////////////////////////////////////////// // multiply //////////////////////////////////////////////////////////////////////////////// inline __host__ __device__ float2 operator*(float2 a, float2 b) { return make_float2(a.x * b.x, a.y * b.y); } inline __host__ __device__ void operator*=(float2 &a, float2 b) { a.x *= b.x; a.y *= b.y; } inline __host__ __device__ float2 operator*(float2 a, float b) { return make_float2(a.x * b, a.y * b); } inline __host__ __device__ float2 operator*(float b, float2 a) { return make_float2(b * a.x, b * a.y); } inline __host__ __device__ void operator*=(float2 &a, float b) { a.x *= b; a.y *= b; } inline __host__ __device__ int2 operator*(int2 a, int2 b) { return make_int2(a.x * b.x, a.y * b.y); } inline __host__ __device__ void operator*=(int2 &a, int2 b) { a.x *= b.x; a.y *= b.y; } inline __host__ __device__ int2 operator*(int2 a, int b) { return make_int2(a.x * b, a.y * b); } inline __host__ __device__ int2 operator*(int b, int2 a) { return make_int2(b * a.x, b * a.y); } inline __host__ __device__ void operator*=(int2 &a, int b) { a.x *= b; a.y *= b; } inline __host__ __device__ uint2 operator*(uint2 a, uint2 b) { return make_uint2(a.x * b.x, a.y * b.y); } inline __host__ __device__ void operator*=(uint2 &a, uint2 b) { a.x *= b.x; a.y *= b.y; } inline __host__ __device__ uint2 operator*(uint2 a, uint b) { return make_uint2(a.x * b, a.y * b); } inline __host__ __device__ uint2 operator*(uint b, uint2 a) { return make_uint2(b * a.x, b * a.y); } inline __host__ __device__ void operator*=(uint2 &a, uint b) { a.x *= b; a.y *= b; } inline __host__ __device__ float3 operator*(float3 a, float3 b) { return make_float3(a.x * b.x, a.y * b.y, a.z * b.z); } inline __host__ __device__ void operator*=(float3 &a, float3 b) { a.x *= b.x; a.y *= b.y; a.z *= b.z; } inline __host__ __device__ float3 operator*(float3 a, float b) { return make_float3(a.x * b, a.y * b, a.z * b); } inline __host__ __device__ float3 operator*(float b, float3 a) { return make_float3(b * a.x, b * a.y, b * a.z); } inline __host__ __device__ void operator*=(float3 &a, float b) { a.x *= b; a.y *= b; a.z *= b; } inline __host__ __device__ int3 operator*(int3 a, int3 b) { return make_int3(a.x * b.x, a.y * b.y, a.z * b.z); } inline __host__ __device__ void operator*=(int3 &a, int3 b) { a.x *= b.x; a.y *= b.y; a.z *= b.z; } inline __host__ __device__ int3 operator*(int3 a, int b) { return make_int3(a.x * b, a.y * b, a.z * b); } inline __host__ __device__ int3 operator*(int b, int3 a) { return make_int3(b * a.x, b * a.y, b * a.z); } inline __host__ __device__ void operator*=(int3 &a, int b) { a.x *= b; a.y *= b; a.z *= b; } inline __host__ __device__ uint3 operator*(uint3 a, uint3 b) { return make_uint3(a.x * b.x, a.y * b.y, a.z * b.z); } inline __host__ __device__ void operator*=(uint3 &a, uint3 b) { a.x *= b.x; a.y *= b.y; a.z *= b.z; } inline __host__ __device__ uint3 operator*(uint3 a, uint b) { return make_uint3(a.x * b, a.y * b, a.z * b); } inline __host__ __device__ uint3 operator*(uint b, uint3 a) { return make_uint3(b * a.x, b * a.y, b * a.z); } inline __host__ __device__ void operator*=(uint3 &a, uint b) { a.x *= b; a.y *= b; a.z *= b; } inline __host__ __device__ float4 operator*(float4 a, float4 b) { return make_float4(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w); } inline __host__ __device__ void operator*=(float4 &a, float4 b) { a.x *= b.x; a.y *= b.y; a.z *= b.z; a.w *= b.w; } inline __host__ __device__ float4 operator*(float4 a, float b) { return make_float4(a.x * b, a.y * b, a.z * b, a.w * b); } inline __host__ __device__ float4 operator*(float b, float4 a) { return make_float4(b * a.x, b * a.y, b * a.z, b * a.w); } inline __host__ __device__ void operator*=(float4 &a, float b) { a.x *= b; a.y *= b; a.z *= b; a.w *= b; } inline __host__ __device__ int4 operator*(int4 a, int4 b) { return make_int4(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w); } inline __host__ __device__ void operator*=(int4 &a, int4 b) { a.x *= b.x; a.y *= b.y; a.z *= b.z; a.w *= b.w; } inline __host__ __device__ int4 operator*(int4 a, int b) { return make_int4(a.x * b, a.y * b, a.z * b, a.w * b); } inline __host__ __device__ int4 operator*(int b, int4 a) { return make_int4(b * a.x, b * a.y, b * a.z, b * a.w); } inline __host__ __device__ void operator*=(int4 &a, int b) { a.x *= b; a.y *= b; a.z *= b; a.w *= b; } inline __host__ __device__ uint4 operator*(uint4 a, uint4 b) { return make_uint4(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w); } inline __host__ __device__ void operator*=(uint4 &a, uint4 b) { a.x *= b.x; a.y *= b.y; a.z *= b.z; a.w *= b.w; } inline __host__ __device__ uint4 operator*(uint4 a, uint b) { return make_uint4(a.x * b, a.y * b, a.z * b, a.w * b); } inline __host__ __device__ uint4 operator*(uint b, uint4 a) { return make_uint4(b * a.x, b * a.y, b * a.z, b * a.w); } inline __host__ __device__ void operator*=(uint4 &a, uint b) { a.x *= b; a.y *= b; a.z *= b; a.w *= b; } //////////////////////////////////////////////////////////////////////////////// // divide //////////////////////////////////////////////////////////////////////////////// inline __host__ __device__ float2 operator/(float2 a, float2 b) { return make_float2(a.x / b.x, a.y / b.y); } inline __host__ __device__ void operator/=(float2 &a, float2 b) { a.x /= b.x; a.y /= b.y; } inline __host__ __device__ float2 operator/(float2 a, float b) { return make_float2(a.x / b, a.y / b); } inline __host__ __device__ void operator/=(float2 &a, float b) { a.x /= b; a.y /= b; } inline __host__ __device__ float2 operator/(float b, float2 a) { return make_float2(b / a.x, b / a.y); } inline __host__ __device__ float3 operator/(float3 a, float3 b) { return make_float3(a.x / b.x, a.y / b.y, a.z / b.z); } inline __host__ __device__ void operator/=(float3 &a, float3 b) { a.x /= b.x; a.y /= b.y; a.z /= b.z; } inline __host__ __device__ float3 operator/(float3 a, float b) { return make_float3(a.x / b, a.y / b, a.z / b); } inline __host__ __device__ void operator/=(float3 &a, float b) { a.x /= b; a.y /= b; a.z /= b; } inline __host__ __device__ float3 operator/(float b, float3 a) { return make_float3(b / a.x, b / a.y, b / a.z); } inline __host__ __device__ float4 operator/(float4 a, float4 b) { return make_float4(a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w); } inline __host__ __device__ void operator/=(float4 &a, float4 b) { a.x /= b.x; a.y /= b.y; a.z /= b.z; a.w /= b.w; } inline __host__ __device__ float4 operator/(float4 a, float b) { return make_float4(a.x / b, a.y / b, a.z / b, a.w / b); } inline __host__ __device__ void operator/=(float4 &a, float b) { a.x /= b; a.y /= b; a.z /= b; a.w /= b; } inline __host__ __device__ float4 operator/(float b, float4 a){ return make_float4(b / a.x, b / a.y, b / a.z, b / a.w); } //////////////////////////////////////////////////////////////////////////////// // min //////////////////////////////////////////////////////////////////////////////// inline __host__ __device__ float2 fminf(float2 a, float2 b) { return make_float2(fminf(a.x,b.x), fminf(a.y,b.y)); } inline __host__ __device__ float3 fminf(float3 a, float3 b) { return make_float3(fminf(a.x,b.x), fminf(a.y,b.y), fminf(a.z,b.z)); } inline __host__ __device__ float4 fminf(float4 a, float4 b) { return make_float4(fminf(a.x,b.x), fminf(a.y,b.y), fminf(a.z,b.z), fminf(a.w,b.w)); } inline __host__ __device__ int2 min(int2 a, int2 b) { return make_int2(min(a.x,b.x), min(a.y,b.y)); } inline __host__ __device__ int3 min(int3 a, int3 b) { return make_int3(min(a.x,b.x), min(a.y,b.y), min(a.z,b.z)); } inline __host__ __device__ int4 min(int4 a, int4 b) { return make_int4(min(a.x,b.x), min(a.y,b.y), min(a.z,b.z), min(a.w,b.w)); } inline __host__ __device__ uint2 min(uint2 a, uint2 b) { return make_uint2(min(a.x,b.x), min(a.y,b.y)); } inline __host__ __device__ uint3 min(uint3 a, uint3 b) { return make_uint3(min(a.x,b.x), min(a.y,b.y), min(a.z,b.z)); } inline __host__ __device__ uint4 min(uint4 a, uint4 b) { return make_uint4(min(a.x,b.x), min(a.y,b.y), min(a.z,b.z), min(a.w,b.w)); } //////////////////////////////////////////////////////////////////////////////// // max //////////////////////////////////////////////////////////////////////////////// inline __host__ __device__ float2 fmaxf(float2 a, float2 b) { return make_float2(fmaxf(a.x,b.x), fmaxf(a.y,b.y)); } inline __host__ __device__ float3 fmaxf(float3 a, float3 b) { return make_float3(fmaxf(a.x,b.x), fmaxf(a.y,b.y), fmaxf(a.z,b.z)); } inline __host__ __device__ float4 fmaxf(float4 a, float4 b) { return make_float4(fmaxf(a.x,b.x), fmaxf(a.y,b.y), fmaxf(a.z,b.z), fmaxf(a.w,b.w)); } inline __host__ __device__ int2 max(int2 a, int2 b) { return make_int2(max(a.x,b.x), max(a.y,b.y)); } inline __host__ __device__ int3 max(int3 a, int3 b) { return make_int3(max(a.x,b.x), max(a.y,b.y), max(a.z,b.z)); } inline __host__ __device__ int4 max(int4 a, int4 b) { return make_int4(max(a.x,b.x), max(a.y,b.y), max(a.z,b.z), max(a.w,b.w)); } inline __host__ __device__ uint2 max(uint2 a, uint2 b) { return make_uint2(max(a.x,b.x), max(a.y,b.y)); } inline __host__ __device__ uint3 max(uint3 a, uint3 b) { return make_uint3(max(a.x,b.x), max(a.y,b.y), max(a.z,b.z)); } inline __host__ __device__ uint4 max(uint4 a, uint4 b) { return make_uint4(max(a.x,b.x), max(a.y,b.y), max(a.z,b.z), max(a.w,b.w)); } //////////////////////////////////////////////////////////////////////////////// // lerp // - linear interpolation between a and b, based on value t in [0, 1] range //////////////////////////////////////////////////////////////////////////////// inline __device__ __host__ float lerp(float a, float b, float t) { return a + t*(b-a); } inline __device__ __host__ float2 lerp(float2 a, float2 b, float t) { return a + t*(b-a); } inline __device__ __host__ float3 lerp(float3 a, float3 b, float t) { return a + t*(b-a); } inline __device__ __host__ float4 lerp(float4 a, float4 b, float t) { return a + t*(b-a); } //////////////////////////////////////////////////////////////////////////////// // clamp // - clamp the value v to be in the range [a, b] //////////////////////////////////////////////////////////////////////////////// inline __device__ __host__ float clamp(float f, float a, float b) { return fmaxf(a, fminf(f, b)); } inline __device__ __host__ int clamp(int f, int a, int b) { return max(a, min(f, b)); } inline __device__ __host__ uint clamp(uint f, uint a, uint b) { return max(a, min(f, b)); } inline __device__ __host__ float2 clamp(float2 v, float a, float b) { return make_float2(clamp(v.x, a, b), clamp(v.y, a, b)); } inline __device__ __host__ float2 clamp(float2 v, float2 a, float2 b) { return make_float2(clamp(v.x, a.x, b.x), clamp(v.y, a.y, b.y)); } inline __device__ __host__ float3 clamp(float3 v, float a, float b) { return make_float3(clamp(v.x, a, b), clamp(v.y, a, b), clamp(v.z, a, b)); } inline __device__ __host__ float3 clamp(float3 v, float3 a, float3 b) { return make_float3(clamp(v.x, a.x, b.x), clamp(v.y, a.y, b.y), clamp(v.z, a.z, b.z)); } inline __device__ __host__ float4 clamp(float4 v, float a, float b) { return make_float4(clamp(v.x, a, b), clamp(v.y, a, b), clamp(v.z, a, b), clamp(v.w, a, b)); } inline __device__ __host__ float4 clamp(float4 v, float4 a, float4 b) { return make_float4(clamp(v.x, a.x, b.x), clamp(v.y, a.y, b.y), clamp(v.z, a.z, b.z), clamp(v.w, a.w, b.w)); } inline __device__ __host__ int2 clamp(int2 v, int a, int b) { return make_int2(clamp(v.x, a, b), clamp(v.y, a, b)); } inline __device__ __host__ int2 clamp(int2 v, int2 a, int2 b) { return make_int2(clamp(v.x, a.x, b.x), clamp(v.y, a.y, b.y)); } inline __device__ __host__ int3 clamp(int3 v, int a, int b) { return make_int3(clamp(v.x, a, b), clamp(v.y, a, b), clamp(v.z, a, b)); } inline __device__ __host__ int3 clamp(int3 v, int3 a, int3 b) { return make_int3(clamp(v.x, a.x, b.x), clamp(v.y, a.y, b.y), clamp(v.z, a.z, b.z)); } inline __device__ __host__ int4 clamp(int4 v, int a, int b) { return make_int4(clamp(v.x, a, b), clamp(v.y, a, b), clamp(v.z, a, b), clamp(v.w, a, b)); } inline __device__ __host__ int4 clamp(int4 v, int4 a, int4 b) { return make_int4(clamp(v.x, a.x, b.x), clamp(v.y, a.y, b.y), clamp(v.z, a.z, b.z), clamp(v.w, a.w, b.w)); } inline __device__ __host__ uint2 clamp(uint2 v, uint a, uint b) { return make_uint2(clamp(v.x, a, b), clamp(v.y, a, b)); } inline __device__ __host__ uint2 clamp(uint2 v, uint2 a, uint2 b) { return make_uint2(clamp(v.x, a.x, b.x), clamp(v.y, a.y, b.y)); } inline __device__ __host__ uint3 clamp(uint3 v, uint a, uint b) { return make_uint3(clamp(v.x, a, b), clamp(v.y, a, b), clamp(v.z, a, b)); } inline __device__ __host__ uint3 clamp(uint3 v, uint3 a, uint3 b) { return make_uint3(clamp(v.x, a.x, b.x), clamp(v.y, a.y, b.y), clamp(v.z, a.z, b.z)); } inline __device__ __host__ uint4 clamp(uint4 v, uint a, uint b) { return make_uint4(clamp(v.x, a, b), clamp(v.y, a, b), clamp(v.z, a, b), clamp(v.w, a, b)); } inline __device__ __host__ uint4 clamp(uint4 v, uint4 a, uint4 b) { return make_uint4(clamp(v.x, a.x, b.x), clamp(v.y, a.y, b.y), clamp(v.z, a.z, b.z), clamp(v.w, a.w, b.w)); } //////////////////////////////////////////////////////////////////////////////// // dot product //////////////////////////////////////////////////////////////////////////////// inline __host__ __device__ float dot(float2 a, float2 b) { return a.x * b.x + a.y * b.y; } inline __host__ __device__ float dot(float3 a, float3 b) { return a.x * b.x + a.y * b.y + a.z * b.z; } inline __host__ __device__ float dot(float4 a, float4 b) { return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w; } inline __host__ __device__ int dot(int2 a, int2 b) { return a.x * b.x + a.y * b.y; } inline __host__ __device__ int dot(int3 a, int3 b) { return a.x * b.x + a.y * b.y + a.z * b.z; } inline __host__ __device__ int dot(int4 a, int4 b) { return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w; } inline __host__ __device__ uint dot(uint2 a, uint2 b) { return a.x * b.x + a.y * b.y; } inline __host__ __device__ uint dot(uint3 a, uint3 b) { return a.x * b.x + a.y * b.y + a.z * b.z; } inline __host__ __device__ uint dot(uint4 a, uint4 b) { return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w; } //////////////////////////////////////////////////////////////////////////////// // length //////////////////////////////////////////////////////////////////////////////// inline __host__ __device__ float length(float2 v) { return sqrtf(dot(v, v)); } inline __host__ __device__ float length(float3 v) { return sqrtf(dot(v, v)); } inline __host__ __device__ float length(float4 v) { return sqrtf(dot(v, v)); } //////////////////////////////////////////////////////////////////////////////// // normalize //////////////////////////////////////////////////////////////////////////////// inline __host__ __device__ float2 normalize(float2 v) { float invLen = rsqrtf(dot(v, v)); return v * invLen; } inline __host__ __device__ float3 normalize(float3 v) { float invLen = rsqrtf(dot(v, v)); return v * invLen; } inline __host__ __device__ float4 normalize(float4 v) { float invLen = rsqrtf(dot(v, v)); return v * invLen; } //////////////////////////////////////////////////////////////////////////////// // floor //////////////////////////////////////////////////////////////////////////////// inline __host__ __device__ float2 floorf(float2 v) { return make_float2(floorf(v.x), floorf(v.y)); } inline __host__ __device__ float3 floorf(float3 v) { return make_float3(floorf(v.x), floorf(v.y), floorf(v.z)); } inline __host__ __device__ float4 floorf(float4 v) { return make_float4(floorf(v.x), floorf(v.y), floorf(v.z), floorf(v.w)); } //////////////////////////////////////////////////////////////////////////////// // frac - returns the fractional portion of a scalar or each vector component //////////////////////////////////////////////////////////////////////////////// inline __host__ __device__ float fracf(float v) { return v - floorf(v); } inline __host__ __device__ float2 fracf(float2 v) { return make_float2(fracf(v.x), fracf(v.y)); } inline __host__ __device__ float3 fracf(float3 v) { return make_float3(fracf(v.x), fracf(v.y), fracf(v.z)); } inline __host__ __device__ float4 fracf(float4 v) { return make_float4(fracf(v.x), fracf(v.y), fracf(v.z), fracf(v.w)); } //////////////////////////////////////////////////////////////////////////////// // fmod //////////////////////////////////////////////////////////////////////////////// inline __host__ __device__ float2 fmodf(float2 a, float2 b) { return make_float2(fmodf(a.x, b.x), fmodf(a.y, b.y)); } inline __host__ __device__ float3 fmodf(float3 a, float3 b) { return make_float3(fmodf(a.x, b.x), fmodf(a.y, b.y), fmodf(a.z, b.z)); } inline __host__ __device__ float4 fmodf(float4 a, float4 b) { return make_float4(fmodf(a.x, b.x), fmodf(a.y, b.y), fmodf(a.z, b.z), fmodf(a.w, b.w)); } //////////////////////////////////////////////////////////////////////////////// // absolute value //////////////////////////////////////////////////////////////////////////////// inline __host__ __device__ float2 fabs(float2 v) { return make_float2(fabs(v.x), fabs(v.y)); } inline __host__ __device__ float3 fabs(float3 v) { return make_float3(fabs(v.x), fabs(v.y), fabs(v.z)); } inline __host__ __device__ float4 fabs(float4 v) { return make_float4(fabs(v.x), fabs(v.y), fabs(v.z), fabs(v.w)); } inline __host__ __device__ int2 abs(int2 v) { return make_int2(abs(v.x), abs(v.y)); } inline __host__ __device__ int3 abs(int3 v) { return make_int3(abs(v.x), abs(v.y), abs(v.z)); } inline __host__ __device__ int4 abs(int4 v) { return make_int4(abs(v.x), abs(v.y), abs(v.z), abs(v.w)); } //////////////////////////////////////////////////////////////////////////////// // reflect // - returns reflection of incident ray I around surface normal N // - N should be normalized, reflected vector's length is equal to length of I //////////////////////////////////////////////////////////////////////////////// inline __host__ __device__ float3 reflect(float3 i, float3 n) { return i - 2.0f * n * dot(n,i); } //////////////////////////////////////////////////////////////////////////////// // cross product //////////////////////////////////////////////////////////////////////////////// inline __host__ __device__ float3 cross(float3 a, float3 b) { return make_float3(a.y*b.z - a.z*b.y, a.z*b.x - a.x*b.z, a.x*b.y - a.y*b.x); } //////////////////////////////////////////////////////////////////////////////// // smoothstep // - returns 0 if x < a // - returns 1 if x > b // - otherwise returns smooth interpolation between 0 and 1 based on x //////////////////////////////////////////////////////////////////////////////// inline __device__ __host__ float smoothstep(float a, float b, float x) { float y = clamp((x - a) / (b - a), 0.0f, 1.0f); return (y*y*(3.0f - (2.0f*y))); } inline __device__ __host__ float2 smoothstep(float2 a, float2 b, float2 x) { float2 y = clamp((x - a) / (b - a), 0.0f, 1.0f); return (y*y*(make_float2(3.0f) - (make_float2(2.0f)*y))); } inline __device__ __host__ float3 smoothstep(float3 a, float3 b, float3 x) { float3 y = clamp((x - a) / (b - a), 0.0f, 1.0f); return (y*y*(make_float3(3.0f) - (make_float3(2.0f)*y))); } inline __device__ __host__ float4 smoothstep(float4 a, float4 b, float4 x) { float4 y = clamp((x - a) / (b - a), 0.0f, 1.0f); return (y*y*(make_float4(3.0f) - (make_float4(2.0f)*y))); } #endif flann-1.8.4-src/src/cpp/flann/util/result_set.h0000644000000000000000000005702212075346130020102 0ustar rootroot/*********************************************************************** * Software License Agreement (BSD License) * * Copyright 2008-2009 Marius Muja (mariusm@cs.ubc.ca). All rights reserved. * Copyright 2008-2009 David G. Lowe (lowe@cs.ubc.ca). All rights reserved. * * THE BSD LICENSE * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #ifndef FLANN_RESULTSET_H #define FLANN_RESULTSET_H #include #include #include #include #include #include namespace flann { /* This record represents a branch point when finding neighbors in the tree. It contains a record of the minimum distance to the query point, as well as the node at which the search resumes. */ template struct BranchStruct { T node; /* Tree node at which search resumes */ DistanceType mindist; /* Minimum distance to query for all nodes below. */ BranchStruct() {} BranchStruct(const T& aNode, DistanceType dist) : node(aNode), mindist(dist) {} bool operator<(const BranchStruct& rhs) const { return mindist struct DistanceIndex { DistanceIndex(DistanceType dist, size_t index) : dist_(dist), index_(index) { } bool operator<(const DistanceIndex& dist_index) const { return (dist_ < dist_index.dist_) || ((dist_ == dist_index.dist_) && index_ < dist_index.index_); } DistanceType dist_; size_t index_; }; template class ResultSet { public: virtual ~ResultSet() {} virtual bool full() const = 0; virtual void addPoint(DistanceType dist, size_t index) = 0; virtual DistanceType worstDist() const = 0; }; /** * KNNSimpleResultSet does not ensure that the element it holds are unique. * Is used in those cases where the nearest neighbour algorithm used does not * attempt to insert the same element multiple times. */ template class KNNSimpleResultSet : public ResultSet { public: typedef DistanceIndex DistIndex; KNNSimpleResultSet(size_t capacity_) : capacity_(capacity_) { // reserving capacity to prevent memory re-allocations dist_index_.resize(capacity_, DistIndex(std::numeric_limits::max(),-1)); clear(); } ~KNNSimpleResultSet() { } /** * Clears the result set */ void clear() { worst_distance_ = std::numeric_limits::max(); dist_index_[capacity_-1].dist_ = worst_distance_; count_ = 0; } /** * * @return Number of elements in the result set */ size_t size() const { return count_; } /** * Radius search result set always reports full * @return */ bool full() const { return count_==capacity_; } /** * Add a point to result set * @param dist distance to point * @param index index of point */ void addPoint(DistanceType dist, size_t index) { if (dist>=worst_distance_) return; if (count_ < capacity_) ++count_; size_t i; for (i=count_-1; i>0; --i) { #ifdef FLANN_FIRST_MATCH if ( (dist_index_[i-1].dist_>dist) || ((dist==dist_index_[i-1].dist_)&&(dist_index_[i-1].index_>index)) ) #else if (dist_index_[i-1].dist_>dist) #endif { dist_index_[i] = dist_index_[i-1]; } else break; } dist_index_[i].dist_ = dist; dist_index_[i].index_ = index; worst_distance_ = dist_index_[capacity_-1].dist_; } /** * Copy indices and distances to output buffers * @param indices * @param dists * @param num_elements Number of elements to copy * @param sorted Indicates if results should be sorted */ void copy(size_t* indices, DistanceType* dists, size_t num_elements, bool sorted = true) { size_t n = std::min(count_, num_elements); for (size_t i=0; i dist_index_; }; /** * K-Nearest neighbour result set. Ensures that the elements inserted are unique */ template class KNNResultSet : public ResultSet { public: typedef DistanceIndex DistIndex; KNNResultSet(int capacity) : capacity_(capacity) { // reserving capacity to prevent memory re-allocations dist_index_.resize(capacity_, DistIndex(std::numeric_limits::max(),-1)); clear(); } ~KNNResultSet() { } /** * Clears the result set */ void clear() { worst_distance_ = std::numeric_limits::max(); dist_index_[capacity_-1].dist_ = worst_distance_; count_ = 0; } size_t size() const { return count_; } bool full() const { return count_ == capacity_; } void addPoint(DistanceType dist, size_t index) { if (dist >= worst_distance_) return; size_t i; for (i = count_; i > 0; --i) { #ifdef FLANN_FIRST_MATCH if ( (dist_index_[i-1].dist_<=dist) && ((dist!=dist_index_[i-1].dist_)||(dist_index_[i-1].index_<=index)) ) #else if (dist_index_[i-1].dist_<=dist) #endif { // Check for duplicate indices size_t j = i - 1; while (dist_index_[j].dist_ == dist) { if (dist_index_[j].index_ == index) { return; } --j; } break; } } if (count_ < capacity_) ++count_; for (size_t j = count_-1; j > i; --j) { dist_index_[j] = dist_index_[j-1]; } dist_index_[i].dist_ = dist; dist_index_[i].index_ = index; worst_distance_ = dist_index_[capacity_-1].dist_; } /** * Copy indices and distances to output buffers * @param indices * @param dists * @param num_elements Number of elements to copy * @param sorted Indicates if results should be sorted */ void copy(size_t* indices, DistanceType* dists, size_t num_elements, bool sorted = true) { size_t n = std::min(count_, num_elements); for (size_t i=0; i dist_index_; }; template class KNNResultSet2 : public ResultSet { public: typedef DistanceIndex DistIndex; KNNResultSet2(size_t capacity_) : capacity_(capacity_) { // reserving capacity to prevent memory re-allocations dist_index_.reserve(capacity_); clear(); } ~KNNResultSet2() { } /** * Clears the result set */ void clear() { dist_index_.clear(); worst_dist_ = std::numeric_limits::max(); is_full_ = false; } /** * * @return Number of elements in the result set */ size_t size() const { return dist_index_.size(); } /** * Radius search result set always reports full * @return */ bool full() const { return is_full_; } /** * Add another point to result set * @param dist distance to point * @param index index of point * Pre-conditions: capacity_>0 */ void addPoint(DistanceType dist, size_t index) { if (dist>=worst_dist_) return; if (dist_index_.size()==capacity_) { // if result set if filled to capacity, remove farthest element std::pop_heap(dist_index_.begin(), dist_index_.end()); dist_index_.pop_back(); } // add new element dist_index_.push_back(DistIndex(dist,index)); if (is_full_) { // when is_full_==true, we have a heap std::push_heap(dist_index_.begin(), dist_index_.end()); } if (dist_index_.size()==capacity_) { if (!is_full_) { std::make_heap(dist_index_.begin(), dist_index_.end()); is_full_ = true; } // we replaced the farthest element, update worst distance worst_dist_ = dist_index_[0].dist_; } } /** * Copy indices and distances to output buffers * @param indices * @param dists * @param num_elements Number of elements to copy * @param sorted Indicates if results should be sorted */ void copy(size_t* indices, DistanceType* dists, size_t num_elements, bool sorted = true) { if (sorted) { // std::sort_heap(dist_index_.begin(), dist_index_.end()); // sort seems faster here, even though dist_index_ is a heap std::sort(dist_index_.begin(), dist_index_.end()); } else { if (num_elements dist_index_; bool is_full_; }; /** * Unbounded radius result set. It will hold as many elements as * are added to it. */ template class RadiusResultSet : public ResultSet { public: typedef DistanceIndex DistIndex; RadiusResultSet(DistanceType radius_) : radius_(radius_) { // reserving some memory to limit number of re-allocations dist_index_.reserve(1024); clear(); } ~RadiusResultSet() { } /** * Clears the result set */ void clear() { dist_index_.clear(); } /** * * @return Number of elements in the result set */ size_t size() const { return dist_index_.size(); } /** * Radius search result set always reports full * @return */ bool full() const { return true; } /** * Add another point to result set * @param dist distance to point * @param index index of point * Pre-conditions: capacity_>0 */ void addPoint(DistanceType dist, size_t index) { if (dist dist_index_; }; /** * Bounded radius result set. It limits the number of elements * it can hold to a preset capacity. */ template class KNNRadiusResultSet : public ResultSet { public: typedef DistanceIndex DistIndex; KNNRadiusResultSet(DistanceType radius_, size_t capacity_) : radius_(radius_), capacity_(capacity_) { // reserving capacity to prevent memory re-allocations dist_index_.reserve(capacity_); clear(); } ~KNNRadiusResultSet() { } /** * Clears the result set */ void clear() { dist_index_.clear(); worst_dist_ = radius_; is_heap_ = false; } /** * * @return Number of elements in the result set */ size_t size() const { return dist_index_.size(); } /** * Radius search result set always reports full * @return */ bool full() const { return true; } /** * Add another point to result set * @param dist distance to point * @param index index of point * Pre-conditions: capacity_>0 */ void addPoint(DistanceType dist, size_t index) { if (dist>=worst_dist_) return; if (dist_index_.size()==capacity_) { // if result set is filled to capacity, remove farthest element std::pop_heap(dist_index_.begin(), dist_index_.end()); dist_index_.pop_back(); } // add new element dist_index_.push_back(DistIndex(dist,index)); if (is_heap_) { std::push_heap(dist_index_.begin(), dist_index_.end()); } if (dist_index_.size()==capacity_) { // when got to full capacity, make it a heap if (!is_heap_) { std::make_heap(dist_index_.begin(), dist_index_.end()); is_heap_ = true; } // we replaced the farthest element, update worst distance worst_dist_ = dist_index_[0].dist_; } } /** * Copy indices and distances to output buffers * @param indices * @param dists * @param num_elements Number of elements to copy * @param sorted Indicates if results should be sorted */ void copy(size_t* indices, DistanceType* dists, size_t num_elements, bool sorted = true) { if (sorted) { // std::sort_heap(dist_index_.begin(), dist_index_.end()); // sort seems faster here, even though dist_index_ is a heap std::sort(dist_index_.begin(), dist_index_.end()); } else { if (num_elements dist_index_; }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * This is a result set that only counts the neighbors within a radius. */ template class CountRadiusResultSet : public ResultSet { DistanceType radius; size_t count; public: CountRadiusResultSet(DistanceType radius_ ) : radius(radius_) { clear(); } ~CountRadiusResultSet() { } void clear() { count = 0; } size_t size() const { return count; } bool full() const { return true; } void addPoint(DistanceType dist, size_t index) { if (dist class UniqueResultSet : public ResultSet { public: struct DistIndex { DistIndex(DistanceType dist, unsigned int index) : dist_(dist), index_(index) { } bool operator<(const DistIndex dist_index) const { return (dist_ < dist_index.dist_) || ((dist_ == dist_index.dist_) && index_ < dist_index.index_); } DistanceType dist_; unsigned int index_; }; /** Default cosntructor */ UniqueResultSet() : worst_distance_(std::numeric_limits::max()) { } /** Check the status of the set * @return true if we have k NN */ inline bool full() const { return is_full_; } /** Copy the set to two C arrays * @param indices pointer to a C array of indices * @param dist pointer to a C array of distances * @param n_neighbors the number of neighbors to copy */ void copy(size_t* indices, DistanceType* dist, int n_neighbors, bool sorted = true) { if (n_neighbors<0) n_neighbors = dist_indices_.size(); int i = 0; typedef typename std::set::const_iterator Iterator; for (Iterator dist_index = dist_indices_.begin(), dist_index_end = dist_indices_.end(); (dist_index != dist_index_end) && (i < n_neighbors); ++dist_index, ++indices, ++dist, ++i) { *indices = dist_index->index_; *dist = dist_index->dist_; } } /** The number of neighbors in the set * @return */ size_t size() const { return dist_indices_.size(); } /** The distance of the furthest neighbor * If we don't have enough neighbors, it returns the max possible value * @return */ inline DistanceType worstDist() const { return worst_distance_; } protected: /** Flag to say if the set is full */ bool is_full_; /** The worst distance found so far */ DistanceType worst_distance_; /** The best candidates so far */ std::set dist_indices_; }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** Class that holds the k NN neighbors * Faster than KNNResultSet as it uses a binary heap and does not maintain two arrays */ template class KNNUniqueResultSet : public UniqueResultSet { public: /** Constructor * @param capacity the number of neighbors to store at max */ KNNUniqueResultSet(unsigned int capacity) : capacity_(capacity) { this->is_full_ = false; this->clear(); } /** Add a possible candidate to the best neighbors * @param dist distance for that neighbor * @param index index of that neighbor */ inline void addPoint(DistanceType dist, size_t index) { // Don't do anything if we are worse than the worst if (dist >= worst_distance_) return; dist_indices_.insert(DistIndex(dist, index)); if (is_full_) { if (dist_indices_.size() > capacity_) { dist_indices_.erase(*dist_indices_.rbegin()); worst_distance_ = dist_indices_.rbegin()->dist_; } } else if (dist_indices_.size() == capacity_) { is_full_ = true; worst_distance_ = dist_indices_.rbegin()->dist_; } } /** Remove all elements in the set */ void clear() { dist_indices_.clear(); worst_distance_ = std::numeric_limits::max(); is_full_ = false; } protected: typedef typename UniqueResultSet::DistIndex DistIndex; using UniqueResultSet::is_full_; using UniqueResultSet::worst_distance_; using UniqueResultSet::dist_indices_; /** The number of neighbors to keep */ unsigned int capacity_; }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** Class that holds the radius nearest neighbors * It is more accurate than RadiusResult as it is not limited in the number of neighbors */ template class RadiusUniqueResultSet : public UniqueResultSet { public: /** Constructor * @param capacity the number of neighbors to store at max */ RadiusUniqueResultSet(DistanceType radius) : radius_(radius) { is_full_ = true; } /** Add a possible candidate to the best neighbors * @param dist distance for that neighbor * @param index index of that neighbor */ void addPoint(DistanceType dist, size_t index) { if (dist < radius_) dist_indices_.insert(DistIndex(dist, index)); } /** Remove all elements in the set */ inline void clear() { dist_indices_.clear(); } /** Check the status of the set * @return alwys false */ inline bool full() const { return true; } /** The distance of the furthest neighbor * If we don't have enough neighbors, it returns the max possible value * @return */ inline DistanceType worstDist() const { return radius_; } private: typedef typename UniqueResultSet::DistIndex DistIndex; using UniqueResultSet::dist_indices_; using UniqueResultSet::is_full_; /** The furthest distance a neighbor can be */ DistanceType radius_; }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** Class that holds the k NN neighbors within a radius distance */ template class KNNRadiusUniqueResultSet : public KNNUniqueResultSet { public: /** Constructor * @param capacity the number of neighbors to store at max */ KNNRadiusUniqueResultSet(DistanceType radius, size_t capacity) : KNNUniqueResultSet(capacity) { this->radius_ = radius; this->clear(); } /** Remove all elements in the set */ void clear() { dist_indices_.clear(); worst_distance_ = radius_; is_full_ = true; } private: using KNNUniqueResultSet::dist_indices_; using KNNUniqueResultSet::is_full_; using KNNUniqueResultSet::worst_distance_; /** The maximum distance of a neighbor */ DistanceType radius_; }; } #endif //FLANN_RESULTSET_H flann-1.8.4-src/src/cpp/flann/util/logger.h0000644000000000000000000000747312075346130017175 0ustar rootroot/*********************************************************************** * Software License Agreement (BSD License) * * Copyright 2008-2009 Marius Muja (mariusm@cs.ubc.ca). All rights reserved. * Copyright 2008-2009 David G. Lowe (lowe@cs.ubc.ca). All rights reserved. * * THE BSD LICENSE * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #ifndef FLANN_LOGGER_H #define FLANN_LOGGER_H #include #include #include "flann/defines.h" namespace flann { class Logger { Logger() : stream(stdout), logLevel(FLANN_LOG_WARN) {} ~Logger() { if ((stream!=NULL)&&(stream!=stdout)) { fclose(stream); } } static Logger& instance() { static Logger logger; return logger; } void _setDestination(const char* name) { if (name==NULL) { stream = stdout; } else { stream = fopen(name,"w"); if (stream == NULL) { stream = stdout; } } } int _log(int level, const char* fmt, va_list arglist) { if (level > logLevel ) return -1; int ret = vfprintf(stream, fmt, arglist); return ret; } public: /** * Sets the logging level. All messages with lower priority will be ignored. * @param level Logging level */ static void setLevel(int level) { instance().logLevel = level; } /** * Returns the currently set logging level. * @return current logging level */ static int getLevel() { return instance().logLevel; } /** * Sets the logging destination * @param name Filename or NULL for console */ static void setDestination(const char* name) { instance()._setDestination(name); } /** * Print log message * @param level Log level * @param fmt Message format * @return */ static int log(int level, const char* fmt, ...) { va_list arglist; va_start(arglist, fmt); int ret = instance()._log(level,fmt,arglist); va_end(arglist); return ret; } #define LOG_METHOD(NAME,LEVEL) \ static int NAME(const char* fmt, ...) \ { \ va_list ap; \ va_start(ap, fmt); \ int ret = instance()._log(LEVEL, fmt, ap); \ va_end(ap); \ return ret; \ } LOG_METHOD(fatal, FLANN_LOG_FATAL) LOG_METHOD(error, FLANN_LOG_ERROR) LOG_METHOD(warn, FLANN_LOG_WARN) LOG_METHOD(info, FLANN_LOG_INFO) LOG_METHOD(debug, FLANN_LOG_DEBUG) private: FILE* stream; int logLevel; }; } #endif //FLANN_LOGGER_H flann-1.8.4-src/src/cpp/flann/config.h.in0000644000000000000000000000336212075346130016604 0ustar rootroot/*********************************************************************** * Software License Agreement (BSD License) * * Copyright 2008-2011 Marius Muja (mariusm@cs.ubc.ca). All rights reserved. * Copyright 2008-2011 David G. Lowe (lowe@cs.ubc.ca). All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #ifndef FLANN_CONFIG_H_ #define FLANN_CONFIG_H_ #ifdef FLANN_VERSION_ #undef FLANN_VERSION_ #endif #define FLANN_VERSION_ "${FLANN_VERSION}" #endif /* FLANN_CONFIG_H_ */ flann-1.8.4-src/src/cpp/flann/flann.hpp0000644000000000000000000003250612075346130016372 0ustar rootroot/*********************************************************************** * Software License Agreement (BSD License) * * Copyright 2008-2009 Marius Muja (mariusm@cs.ubc.ca). All rights reserved. * Copyright 2008-2009 David G. Lowe (lowe@cs.ubc.ca). All rights reserved. * * THE BSD LICENSE * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #ifndef FLANN_HPP_ #define FLANN_HPP_ #include #include #include #include #include "flann/general.h" #include "flann/util/matrix.h" #include "flann/util/params.h" #include "flann/util/saving.h" #include "flann/algorithms/all_indices.h" namespace flann { /** * Sets the log level used for all flann functions * @param level Verbosity level */ inline void log_verbosity(int level) { if (level >= 0) { Logger::setLevel(level); } } /** * (Deprecated) Index parameters for creating a saved index. */ struct SavedIndexParams : public IndexParams { SavedIndexParams(std::string filename) { (*this)["algorithm"] = FLANN_INDEX_SAVED; (*this)["filename"] = filename; } }; template class Index { public: typedef typename Distance::ElementType ElementType; typedef typename Distance::ResultType DistanceType; typedef NNIndex IndexType; Index(const IndexParams& params, Distance distance = Distance() ) : index_params_(params) { flann_algorithm_t index_type = get_param(params,"algorithm"); loaded_ = false; Matrix features; if (index_type == FLANN_INDEX_SAVED) { nnIndex_ = load_saved_index(features, get_param(params,"filename"), distance); loaded_ = true; } else { flann_algorithm_t index_type = get_param(params, "algorithm"); nnIndex_ = create_index_by_type(index_type, features, params, distance); } } Index(const Matrix& features, const IndexParams& params, Distance distance = Distance() ) : index_params_(params) { flann_algorithm_t index_type = get_param(params,"algorithm"); loaded_ = false; if (index_type == FLANN_INDEX_SAVED) { nnIndex_ = load_saved_index(features, get_param(params,"filename"), distance); loaded_ = true; } else { flann_algorithm_t index_type = get_param(params, "algorithm"); nnIndex_ = create_index_by_type(index_type, features, params, distance); } } Index(const Index& other) : loaded_(other.loaded_), index_params_(other.index_params_) { nnIndex_ = other.nnIndex_->clone(); } Index& operator=(Index other) { this->swap(other); return *this; } virtual ~Index() { delete nnIndex_; } /** * Builds the index. */ void buildIndex() { if (!loaded_) { nnIndex_->buildIndex(); } } void buildIndex(const Matrix& points) { nnIndex_->buildIndex(points); } void addPoints(const Matrix& points, float rebuild_threshold = 2) { nnIndex_->addPoints(points, rebuild_threshold); } /** * Remove point from the index * @param index Index of point to be removed */ void removePoint(size_t point_id) { nnIndex_->removePoint(point_id); } /** * Returns pointer to a data point with the specified id. * @param point_id the id of point to retrieve * @return */ ElementType* getPoint(size_t point_id) { return nnIndex_->getPoint(point_id); } /** * Save index to file * @param filename */ void save(std::string filename) { FILE* fout = fopen(filename.c_str(), "wb"); if (fout == NULL) { throw FLANNException("Cannot open file"); } nnIndex_->saveIndex(fout); fclose(fout); } /** * \returns number of features in this index. */ size_t veclen() const { return nnIndex_->veclen(); } /** * \returns The dimensionality of the features in this index. */ size_t size() const { return nnIndex_->size(); } /** * \returns The index type (kdtree, kmeans,...) */ flann_algorithm_t getType() const { return nnIndex_->getType(); } /** * \returns The amount of memory (in bytes) used by the index. */ int usedMemory() const { return nnIndex_->usedMemory(); } /** * \returns The index parameters */ IndexParams getParameters() const { return nnIndex_->getParameters(); } /** * \brief Perform k-nearest neighbor search * \param[in] queries The query points for which to find the nearest neighbors * \param[out] indices The indices of the nearest neighbors found * \param[out] dists Distances to the nearest neighbors found * \param[in] knn Number of nearest neighbors to return * \param[in] params Search parameters */ int knnSearch(const Matrix& queries, Matrix& indices, Matrix& dists, size_t knn, const SearchParams& params) const { return nnIndex_->knnSearch(queries, indices, dists, knn, params); } /** * * @param queries * @param indices * @param dists * @param knn * @param params * @return */ int knnSearch(const Matrix& queries, Matrix& indices, Matrix& dists, size_t knn, const SearchParams& params) const { return nnIndex_->knnSearch(queries, indices, dists, knn, params); } /** * \brief Perform k-nearest neighbor search * \param[in] queries The query points for which to find the nearest neighbors * \param[out] indices The indices of the nearest neighbors found * \param[out] dists Distances to the nearest neighbors found * \param[in] knn Number of nearest neighbors to return * \param[in] params Search parameters */ int knnSearch(const Matrix& queries, std::vector< std::vector >& indices, std::vector >& dists, size_t knn, const SearchParams& params) { return nnIndex_->knnSearch(queries, indices, dists, knn, params); } /** * * @param queries * @param indices * @param dists * @param knn * @param params * @return */ int knnSearch(const Matrix& queries, std::vector< std::vector >& indices, std::vector >& dists, size_t knn, const SearchParams& params) const { return nnIndex_->knnSearch(queries, indices, dists, knn, params); } /** * \brief Perform radius search * \param[in] queries The query points * \param[out] indices The indinces of the neighbors found within the given radius * \param[out] dists The distances to the nearest neighbors found * \param[in] radius The radius used for search * \param[in] params Search parameters * \returns Number of neighbors found */ int radiusSearch(const Matrix& queries, Matrix& indices, Matrix& dists, float radius, const SearchParams& params) const { return nnIndex_->radiusSearch(queries, indices, dists, radius, params); } /** * * @param queries * @param indices * @param dists * @param radius * @param params * @return */ int radiusSearch(const Matrix& queries, Matrix& indices, Matrix& dists, float radius, const SearchParams& params) const { return nnIndex_->radiusSearch(queries, indices, dists, radius, params); } /** * \brief Perform radius search * \param[in] queries The query points * \param[out] indices The indinces of the neighbors found within the given radius * \param[out] dists The distances to the nearest neighbors found * \param[in] radius The radius used for search * \param[in] params Search parameters * \returns Number of neighbors found */ int radiusSearch(const Matrix& queries, std::vector< std::vector >& indices, std::vector >& dists, float radius, const SearchParams& params) const { return nnIndex_->radiusSearch(queries, indices, dists, radius, params); } /** * * @param queries * @param indices * @param dists * @param radius * @param params * @return */ int radiusSearch(const Matrix& queries, std::vector< std::vector >& indices, std::vector >& dists, float radius, const SearchParams& params) const { return nnIndex_->radiusSearch(queries, indices, dists, radius, params); } private: IndexType* load_saved_index(const Matrix& dataset, const std::string& filename, Distance distance) { FILE* fin = fopen(filename.c_str(), "rb"); if (fin == NULL) { return NULL; } IndexHeader header = load_header(fin); if (header.data_type != flann_datatype_value::value) { throw FLANNException("Datatype of saved index is different than of the one to be created."); } IndexParams params; params["algorithm"] = header.index_type; IndexType* nnIndex = create_index_by_type(header.index_type, dataset, params, distance); rewind(fin); nnIndex->loadIndex(fin); fclose(fin); return nnIndex; } void swap( Index& other) { std::swap(nnIndex_, other.nnIndex_); std::swap(loaded_, other.loaded_); std::swap(index_params_, other.index_params_); } private: /** Pointer to actual index class */ IndexType* nnIndex_; /** Indices if the index was loaded from a file */ bool loaded_; /** Parameters passed to the index */ IndexParams index_params_; }; /** * Performs a hierarchical clustering of the points passed as argument and then takes a cut in the * the clustering tree to return a flat clustering. * @param[in] points Points to be clustered * @param centers The computed cluster centres. Matrix should be preallocated and centers.rows is the * number of clusters requested. * @param params Clustering parameters (The same as for flann::KMeansIndex) * @param d Distance to be used for clustering (eg: flann::L2) * @return number of clusters computed (can be different than clusters.rows and is the highest number * of the form (branching-1)*K+1 smaller than clusters.rows). */ template int hierarchicalClustering(const Matrix& points, Matrix& centers, const KMeansIndexParams& params, Distance d = Distance()) { KMeansIndex kmeans(points, params, d); kmeans.buildIndex(); int clusterNum = kmeans.getClusterCenters(centers); return clusterNum; } } #endif /* FLANN_HPP_ */ flann-1.8.4-src/src/cpp/flann/general.h0000644000000000000000000001200012075346130016334 0ustar rootroot/*********************************************************************** * Software License Agreement (BSD License) * * Copyright 2008-2009 Marius Muja (mariusm@cs.ubc.ca). All rights reserved. * Copyright 2008-2009 David G. Lowe (lowe@cs.ubc.ca). All rights reserved. * * THE BSD LICENSE * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #ifndef FLANN_GENERAL_H_ #define FLANN_GENERAL_H_ #include "defines.h" #include #include #include namespace flann { class FLANNException : public std::runtime_error { public: FLANNException(const char* message) : std::runtime_error(message) { } FLANNException(const std::string& message) : std::runtime_error(message) { } }; template struct flann_datatype_value { static const flann_datatype_t value = FLANN_NONE; }; template<> struct flann_datatype_value { static const flann_datatype_t value = FLANN_INT8; }; template<> struct flann_datatype_value { static const flann_datatype_t value = FLANN_INT16; }; template<> struct flann_datatype_value { static const flann_datatype_t value = FLANN_INT32; }; #ifdef LLONG_MAX template<> struct flann_datatype_value { static const flann_datatype_t value = FLANN_INT64; }; #endif template<> struct flann_datatype_value { static const flann_datatype_t value = FLANN_UINT8; }; template<> struct flann_datatype_value { static const flann_datatype_t value = FLANN_UINT16; }; template<> struct flann_datatype_value { static const flann_datatype_t value = FLANN_UINT32; }; #ifdef ULLONG_MAX template<> struct flann_datatype_value { static const flann_datatype_t value = FLANN_UINT64; }; #endif template<> struct flann_datatype_value { static const flann_datatype_t value = FLANN_FLOAT32; }; template<> struct flann_datatype_value { static const flann_datatype_t value = FLANN_FLOAT64; }; template struct flann_datatype_type { typedef void type; }; template<> struct flann_datatype_type { typedef char type; }; template<> struct flann_datatype_type { typedef short type; }; template<> struct flann_datatype_type { typedef int type; }; #ifdef LLONG_MAX template<> struct flann_datatype_type { typedef long long type; }; #endif template<> struct flann_datatype_type { typedef unsigned char type; }; template<> struct flann_datatype_type { typedef unsigned short type; }; template<> struct flann_datatype_type { typedef unsigned int type; }; #ifdef ULLONG_MAX template<> struct flann_datatype_type { typedef unsigned long long type; }; #endif template<> struct flann_datatype_type { typedef float type; }; template<> struct flann_datatype_type { typedef double type; }; inline size_t flann_datatype_size(flann_datatype_t type) { switch (type) { case FLANN_INT8: return sizeof(flann_datatype_type::type); case FLANN_INT16: return sizeof(flann_datatype_type::type); case FLANN_INT32: return sizeof(flann_datatype_type::type); case FLANN_INT64: return sizeof(flann_datatype_type::type); case FLANN_UINT8: return sizeof(flann_datatype_type::type); case FLANN_UINT16: return sizeof(flann_datatype_type::type); case FLANN_UINT32: return sizeof(flann_datatype_type::type); case FLANN_UINT64: return sizeof(flann_datatype_type::type); case FLANN_FLOAT32: return sizeof(flann_datatype_type::type); case FLANN_FLOAT64: return sizeof(flann_datatype_type::type); default: return 0; } } } #endif /* FLANN_GENERAL_H_ */ flann-1.8.4-src/src/cpp/flann/defines.h0000644000000000000000000001031412075346130016342 0ustar rootroot/*********************************************************************** * Software License Agreement (BSD License) * * Copyright 2008-2011 Marius Muja (mariusm@cs.ubc.ca). All rights reserved. * Copyright 2008-2011 David G. Lowe (lowe@cs.ubc.ca). All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #ifndef FLANN_DEFINES_H_ #define FLANN_DEFINES_H_ #include "config.h" #ifdef FLANN_EXPORT #undef FLANN_EXPORT #endif #ifdef WIN32 /* win32 dll export/import directives */ #ifdef FLANN_EXPORTS #define FLANN_EXPORT __declspec(dllexport) #elif defined(FLANN_STATIC) #define FLANN_EXPORT #else #define FLANN_EXPORT __declspec(dllimport) #endif #else /* unix needs nothing */ #define FLANN_EXPORT #endif #ifdef FLANN_DEPRECATED #undef FLANN_DEPRECATED #endif #ifdef __GNUC__ #define FLANN_DEPRECATED __attribute__ ((deprecated)) #elif defined(_MSC_VER) #define FLANN_DEPRECATED __declspec(deprecated) #else #pragma message("WARNING: You need to implement FLANN_DEPRECATED for this compiler") #define FLANN_DEPRECATED #endif #undef FLANN_PLATFORM_64_BIT #undef FLANN_PLATFORM_32_BIT #if __amd64__ || __x86_64__ || _WIN64 || _M_X64 #define FLANN_PLATFORM_64_BIT #else #define FLANN_PLATFORM_32_BIT #endif #undef FLANN_ARRAY_LEN #define FLANN_ARRAY_LEN(a) (sizeof(a)/sizeof(a[0])) #ifdef __cplusplus namespace flann { #endif /* Nearest neighbour index algorithms */ enum flann_algorithm_t { FLANN_INDEX_LINEAR = 0, FLANN_INDEX_KDTREE = 1, FLANN_INDEX_KMEANS = 2, FLANN_INDEX_COMPOSITE = 3, FLANN_INDEX_KDTREE_SINGLE = 4, FLANN_INDEX_HIERARCHICAL = 5, FLANN_INDEX_LSH = 6, #ifdef FLANN_USE_CUDA FLANN_INDEX_KDTREE_CUDA = 7, #endif FLANN_INDEX_SAVED = 254, FLANN_INDEX_AUTOTUNED = 255, }; enum flann_centers_init_t { FLANN_CENTERS_RANDOM = 0, FLANN_CENTERS_GONZALES = 1, FLANN_CENTERS_KMEANSPP = 2, }; enum flann_log_level_t { FLANN_LOG_NONE = 0, FLANN_LOG_FATAL = 1, FLANN_LOG_ERROR = 2, FLANN_LOG_WARN = 3, FLANN_LOG_INFO = 4, FLANN_LOG_DEBUG = 5 }; enum flann_distance_t { FLANN_DIST_EUCLIDEAN = 1, FLANN_DIST_L2 = 1, FLANN_DIST_MANHATTAN = 2, FLANN_DIST_L1 = 2, FLANN_DIST_MINKOWSKI = 3, FLANN_DIST_MAX = 4, FLANN_DIST_HIST_INTERSECT = 5, FLANN_DIST_HELLINGER = 6, FLANN_DIST_CHI_SQUARE = 7, FLANN_DIST_KULLBACK_LEIBLER = 8, FLANN_DIST_HAMMING = 9, FLANN_DIST_HAMMING_LUT = 10, FLANN_DIST_HAMMING_POPCNT = 11, FLANN_DIST_L2_SIMPLE = 12, }; enum flann_datatype_t { FLANN_NONE = -1, FLANN_INT8 = 0, FLANN_INT16 = 1, FLANN_INT32 = 2, FLANN_INT64 = 3, FLANN_UINT8 = 4, FLANN_UINT16 = 5, FLANN_UINT32 = 6, FLANN_UINT64 = 7, FLANN_FLOAT32 = 8, FLANN_FLOAT64 = 9 }; enum flann_checks_t { FLANN_CHECKS_UNLIMITED = -1, FLANN_CHECKS_AUTOTUNED = -2, }; #ifdef __cplusplus } #endif #endif /* FLANN_DEFINES_H_ */ flann-1.8.4-src/src/cpp/flann/flann_cpp.cpp0000644000000000000000000000314112075346130017220 0ustar rootroot/*********************************************************************** * Software License Agreement (BSD License) * * Copyright 2008-2009 Marius Muja (mariusm@cs.ubc.ca). All rights reserved. * Copyright 2008-2009 David G. Lowe (lowe@cs.ubc.ca). All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #include "flann/flann.hpp" flann-1.8.4-src/src/cpp/flann/algorithms/0000755000000000000000000000000012075346130016726 5ustar rootrootflann-1.8.4-src/src/cpp/flann/algorithms/kdtree_cuda_builder.h0000644000000000000000000007171612075346130023073 0ustar rootroot/*********************************************************************** * Software License Agreement (BSD License) * * Copyright 2011 Andreas Muetzel (amuetzel@uni-koblenz.de). All rights reserved. * * THE BSD LICENSE * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #ifndef FLANN_CUDA_KD_TREE_BUILDER_H_ #define FLANN_CUDA_KD_TREE_BUILDER_H_ #include #include #include #include #include #include #include #include // #define PRINT_DEBUG_TIMING namespace flann { // template< typename T > // void print_vector( const thrust::device_vector& v ) // { // for( int i=0; i< v.size(); i++ ) // { // std::cout< // void print_vector( const thrust::device_vector& v1, const thrust::device_vector& v2 ) // { // for( int i=0; i< v1.size(); i++ ) // { // std::cout< // void print_vector( const thrust::device_vector& v1, const thrust::device_vector& v2, const thrust::device_vector& v3 ) // { // for( int i=0; i< v1.size(); i++ ) // { // std::cout< // void print_vector_by_index( const thrust::device_vector& v,const thrust::device_vector& ind ) // { // for( int i=0; i< v.size(); i++ ) // { // std::cout<& i ) { return (thrust::get<1>(i)& 1)==0; } }; //! just for convenience: access a float4 by an index in [0,1,2] //! (casting it to a float* and accessing it by the index is way slower...) __host__ __device__ float get_value_by_index( const float4& f, int i ) { switch(i) { case 0: return f.x; case 1: return f.y; default: return f.z; } } //! mark a point as belonging to the left or right child of its current parent //! called after parents are split struct MovePointsToChildNodes { MovePointsToChildNodes( int* child1, SplitInfo* splits, float* x, float* y, float* z, int* ox, int* oy, int* oz, int* lrx, int* lry, int* lrz ) : child1_(child1), splits_(splits), x_(x), y_(y), z_(z), ox_(ox), oy_(oy), oz_(oz), lrx_(lrx), lry_(lry), lrz_(lrz){} // int dim; // float threshold; int* child1_; SplitInfo* splits_; // coordinate values float* x_, * y_, * z_; // owner indices -> which node does the point belong to? int* ox_, * oy_, * oz_; // temp info: will be set to 1 of a point is moved to the right child node, 0 otherwise // (used later in the scan op to separate the points of the children into continuous ranges) int* lrx_, * lry_, * lrz_; __device__ void operator()( const thrust::tuple& data ) { int index = thrust::get<0>(data); int owner = ox_[index]; // before a split, all points at the same position in the index array have the same owner int point_ind1=thrust::get<1>(data); int point_ind2=thrust::get<2>(data); int point_ind3=thrust::get<3>(data); int leftChild=child1_[owner]; int split_dim; float dim_val1, dim_val2, dim_val3; SplitInfo split; lrx_[index]=0; lry_[index]=0; lrz_[index]=0; // this element already belongs to a leaf node -> everything alright, no need to change anything if( leftChild==-1 ) { return; } // otherwise: load split data, and assign this index to the new owner split = splits_[owner]; split_dim=split.split_dim; switch( split_dim ) { case 0: dim_val1=x_[point_ind1]; dim_val2=x_[point_ind2]; dim_val3=x_[point_ind3]; break; case 1: dim_val1=y_[point_ind1]; dim_val2=y_[point_ind2]; dim_val3=y_[point_ind3]; break; default: dim_val1=z_[point_ind1]; dim_val2=z_[point_ind2]; dim_val3=z_[point_ind3]; break; } int r1=leftChild +(dim_val1 > split.split_val); ox_[index]=r1; int r2=leftChild+(dim_val2 > split.split_val); oy_[index]=r2; oz_[index]=leftChild+(dim_val3 > split.split_val); lrx_[index] = (dim_val1 > split.split_val); lry_[index] = (dim_val2 > split.split_val); lrz_[index] = (dim_val3 > split.split_val); // return thrust::make_tuple( r1, r2, leftChild+(dim_val > split.split_val) ); } }; //! used to update the left/right pointers and aabb infos after the node splits struct SetLeftAndRightAndAABB { int maxPoints; int nElements; SplitInfo* nodes; int* counts; int* labels; float4* aabbMin; float4* aabbMax; const float* x,* y,* z; const int* ix, * iy, * iz; __host__ __device__ void operator()( int i ) { int index=labels[i]; int right; int left = counts[i]; nodes[index].left=left; if( i < nElements-1 ) { right=counts[i+1]; } else { // index==nNodes right=maxPoints; } nodes[index].right=right; aabbMin[index].x=x[ix[left]]; aabbMin[index].y=y[iy[left]]; aabbMin[index].z=z[iz[left]]; aabbMax[index].x=x[ix[right-1]]; aabbMax[index].y=y[iy[right-1]]; aabbMax[index].z=z[iz[right-1]]; } }; //! - decide whether a node has to be split //! if yes: //! - allocate child nodes //! - set split axis as axis of maximum aabb length struct SplitNodes { int maxPointsPerNode; int* node_count; int* nodes_allocated; int* out_of_space; int* child1_; int* parent_; SplitInfo* splits; __device__ void operator()( thrust::tuple node ) // float4: aabbMin, aabbMax { int& parent=thrust::get<0>(node); int& child1=thrust::get<1>(node); SplitInfo& s=thrust::get<2>(node); const float4& aabbMin=thrust::get<3>(node); const float4& aabbMax=thrust::get<4>(node); int my_index = thrust::get<5>(node); bool split_node=false; // first, each thread block counts the number of nodes that it needs to allocate... __shared__ int block_nodes_to_allocate; if( threadIdx.x== 0 ) block_nodes_to_allocate=0; __syncthreads(); // don't split if all points are equal // (could lead to an infinite loop, and doesn't make any sense anyway) bool all_points_in_node_are_equal=aabbMin.x == aabbMax.x && aabbMin.y==aabbMax.y && aabbMin.z==aabbMax.z; int offset_to_global=0; // maybe this could be replaced with a reduction... if(( child1==-1) &&( s.right-s.left > maxPointsPerNode) && !all_points_in_node_are_equal ) { // leaf node split_node=true; offset_to_global = atomicAdd( &block_nodes_to_allocate,2 ); } __syncthreads(); __shared__ int block_left; __shared__ bool enough_space; // ... then the first thread tries to allocate this many nodes... if( threadIdx.x==0) { block_left = atomicAdd( node_count, block_nodes_to_allocate ); enough_space = block_left+block_nodes_to_allocate < *nodes_allocated; // if it doesn't succeed, no nodes will be created by this block if( !enough_space ) { atomicAdd( node_count, -block_nodes_to_allocate ); *out_of_space=1; } } __syncthreads(); // this thread needs to split it's node && there was enough space for all the nodes // in this block. //(The whole "allocate-per-block-thing" is much faster than letting each element allocate // its space on its own, because shared memory atomics are A LOT faster than // global mem atomics!) if( split_node && enough_space ) { int left = block_left + offset_to_global; splits[left].left=s.left; splits[left].right=s.right; splits[left+1].left=0; splits[left+1].right=0; // split axis/position: middle of longest aabb extent float4 aabbDim=aabbMax-aabbMin; int maxDim=0; float maxDimLength=aabbDim.x; float4 splitVal=(aabbMax+aabbMin); splitVal*=0.5f; for( int i=1; i<=2; i++ ) { float val = get_value_by_index(aabbDim,i); if( val > maxDimLength ) { maxDim=i; maxDimLength=val; } } s.split_dim=maxDim; s.split_val=get_value_by_index(splitVal,maxDim); child1_[my_index]=left; splits[my_index]=s; parent_[left]=my_index; parent_[left+1]=my_index; child1_[left]=-1; child1_[left+1]=-1; } } }; //! computes the scatter target address for the split operation, see Sengupta,Harris,Zhang,Owen: Scan Primitives for GPU Computing //! in my use case, this is about 2x as fast as thrust::partition struct set_addr3 { const int* val_, * f_; int npoints_; __device__ int operator()( int id ) { int nf = f_[npoints_-1] + (val_[npoints_-1]); int f=f_[id]; int t = id -f+nf; return val_[id] ? f : t; } }; //! converts a float4 point (xyz) to a tuple of three float vals (used to separate the //! float4 input buffer into three arrays in the beginning of the tree build) struct pointxyz_to_px_py_pz { __device__ thrust::tuple operator()( const float4& val ) { return thrust::make_tuple(val.x, val.y, val.z); } }; } // namespace kd_tree_builder_detail } // namespace cuda std::ostream& operator <<(std::ostream& stream, const cuda::kd_tree_builder_detail::SplitInfo& s) { stream<<"(split l/r: "<< s.left <<" "<< s.right<< " split:"<& points, int max_leaf_size ) : /*out_of_space_(1,0),node_count_(1,1),*/ max_leaf_size_(max_leaf_size) { points_=&points; int prealloc = points.size()/max_leaf_size_*16; allocation_info_.resize(3); allocation_info_[NodeCount]=1; allocation_info_[NodesAllocated]=prealloc; allocation_info_[OutOfSpace]=0; // std::cout<size()<(prealloc,-1); parent_=new thrust::device_vector(prealloc,-1); cuda::kd_tree_builder_detail::SplitInfo s; s.left=0; s.right=0; splits_=new thrust::device_vector(prealloc,s); s.right=points.size(); (*splits_)[0]=s; aabb_min_=new thrust::device_vector(prealloc); aabb_max_=new thrust::device_vector(prealloc); index_x_=new thrust::device_vector(points_->size()); index_y_=new thrust::device_vector(points_->size()); index_z_=new thrust::device_vector(points_->size()); owners_x_=new thrust::device_vector(points_->size(),0); owners_y_=new thrust::device_vector(points_->size(),0); owners_z_=new thrust::device_vector(points_->size(),0); leftright_x_ = new thrust::device_vector(points_->size(),0); leftright_y_ = new thrust::device_vector(points_->size(),0); leftright_z_ = new thrust::device_vector(points_->size(),0); tmp_index_=new thrust::device_vector(points_->size()); tmp_owners_=new thrust::device_vector(points_->size()); tmp_misc_=new thrust::device_vector(points_->size()); points_x_=new thrust::device_vector(points_->size()); points_y_=new thrust::device_vector(points_->size()); points_z_=new thrust::device_vector(points_->size()); delete_node_info_=false; } ~CudaKdTreeBuilder() { if( delete_node_info_ ) { delete child1_; delete parent_; delete splits_; delete aabb_min_; delete aabb_max_; delete index_x_; } delete index_y_; delete index_z_; delete owners_x_; delete owners_y_; delete owners_z_; delete points_x_; delete points_y_; delete points_z_; delete leftright_x_; delete leftright_y_; delete leftright_z_; delete tmp_index_; delete tmp_owners_; delete tmp_misc_; } //! build the tree //! general idea: //! - build sorted lists of the points in x y and z order (to be able to compute tight AABBs in O(1) ) //! - while( nodes to split exist ) //! - split non-child nodes along longest axis if the number of points is > max_points_per_node //! - for each point: determine whether it is in a node that was split. If yes, mark it as belonging to the left or right child node of its current parent node //! - reorder the points so that the points of a single node are continuous in the node array //! - update the left/right pointers and AABBs of all nodes void buildTree() { // std::cout<<"buildTree()"<begin(), points_->end(), thrust::make_zip_iterator(thrust::make_tuple(points_x_->begin(), points_y_->begin(),points_z_->begin()) ), cuda::kd_tree_builder_detail::pointxyz_to_px_py_pz() ); thrust::counting_iterator it(0); thrust::copy( it, it+points_->size(), index_x_->begin() ); thrust::copy( index_x_->begin(), index_x_->end(), index_y_->begin() ); thrust::copy( index_x_->begin(), index_x_->end(), index_z_->begin() ); thrust::device_vector tmpv(points_->size()); // create sorted index list -> can be used to compute AABBs in O(1) thrust::copy(points_x_->begin(), points_x_->end(), tmpv.begin()); thrust::sort_by_key( tmpv.begin(), tmpv.end(), index_x_->begin() ); thrust::copy(points_y_->begin(), points_y_->end(), tmpv.begin()); thrust::sort_by_key( tmpv.begin(), tmpv.end(), index_y_->begin() ); thrust::copy(points_z_->begin(), points_z_->end(), tmpv.begin()); thrust::sort_by_key( tmpv.begin(), tmpv.end(), index_z_->begin() ); (*aabb_min_)[0]=make_float4((*points_x_)[(*index_x_)[0]],(*points_y_)[(*index_y_)[0]],(*points_z_)[(*index_z_)[0]],0); (*aabb_max_)[0]=make_float4((*points_x_)[(*index_x_)[points_->size()-1]],(*points_y_)[(*index_y_)[points_->size()-1]],(*points_z_)[(*index_z_)[points_->size()-1]],0); #ifdef PRINT_DEBUG_TIMING cudaDeviceSynchronize(); std::cout<<" initial stuff:"< cit(0); thrust::for_each( thrust::make_zip_iterator(thrust::make_tuple( parent_->begin(), child1_->begin(), splits_->begin(), aabb_min_->begin(), aabb_max_->begin(), cit )), thrust::make_zip_iterator(thrust::make_tuple( parent_->begin()+last_node_count, child1_->begin()+last_node_count,splits_->begin()+last_node_count, aabb_min_->begin()+last_node_count, aabb_max_->begin()+last_node_count,cit+last_node_count )), sn ); // copy allocation info to host thrust::host_vector alloc_info = allocation_info_; if( last_node_count == alloc_info[NodeCount] ) { // no more nodes were split -> done break; } last_node_count=alloc_info[NodeCount]; // a node was un-splittable due to a lack of space if( alloc_info[OutOfSpace]==1 ) { resize_node_vectors(alloc_info[NodesAllocated]*2); alloc_info[OutOfSpace]=0; alloc_info[NodesAllocated]*=2; allocation_info_=alloc_info; } #ifdef PRINT_DEBUG_TIMING cudaDeviceSynchronize(); std::cout<<" node split:"< ci0(0); thrust::for_each( thrust::make_zip_iterator( thrust::make_tuple( ci0, index_x_->begin(), index_y_->begin(), index_z_->begin()) ), thrust::make_zip_iterator( thrust::make_tuple( ci0+points_->size(), index_x_->end(), index_y_->end(), index_z_->end()) ),sno ); #ifdef PRINT_DEBUG_TIMING cudaDeviceSynchronize(); std::cout<<" set new owners:"< friend class KDTreeCuda3dIndex; protected: //! takes the partitioned nodes, and sets the left-/right info of leaf nodes, as well as the AABBs void update_leftright_and_aabb( const thrust::device_vector& x, const thrust::device_vector& y,const thrust::device_vector& z, const thrust::device_vector& ix, const thrust::device_vector& iy,const thrust::device_vector& iz, const thrust::device_vector& owners, thrust::device_vector& splits, thrust::device_vector& aabbMin,thrust::device_vector& aabbMax) { thrust::device_vector* labelsUnique=tmp_owners_; thrust::device_vector* countsUnique=tmp_index_; // assume: points of each node are continuous in the array // find which nodes are here, and where each node's points begin and end int unique_labels = thrust::unique_by_key_copy( owners.begin(), owners.end(), thrust::counting_iterator(0), labelsUnique->begin(), countsUnique->begin()).first - labelsUnique->begin(); // update the info cuda::kd_tree_builder_detail::SetLeftAndRightAndAABB s; s.maxPoints=x.size(); s.nElements=unique_labels; s.nodes=thrust::raw_pointer_cast(&(splits[0])); s.counts=thrust::raw_pointer_cast(&( (*countsUnique)[0])); s.labels=thrust::raw_pointer_cast(&( (*labelsUnique)[0])); s.x=thrust::raw_pointer_cast(&x[0]); s.y=thrust::raw_pointer_cast(&y[0]); s.z=thrust::raw_pointer_cast(&z[0]); s.ix=thrust::raw_pointer_cast(&ix[0]); s.iy=thrust::raw_pointer_cast(&iy[0]); s.iz=thrust::raw_pointer_cast(&iz[0]); s.aabbMin=thrust::raw_pointer_cast(&aabbMin[0]); s.aabbMax=thrust::raw_pointer_cast(&aabbMax[0]); thrust::counting_iterator it(0); thrust::for_each(it, it+unique_labels, s); } //! Separates the left and right children of each node into continuous parts of the array. //! More specifically, it seperates children with even and odd node indices because nodes are always //! allocated in pairs -> child1==child2+1 -> child1 even and child2 odd, or vice-versa. //! Since the split operation is stable, this results in continuous partitions //! for all the single nodes. //! (basically the split primitive according to sengupta et al) //! about twice as fast as thrust::partition void separate_left_and_right_children( thrust::device_vector& key_in, thrust::device_vector& val_in, thrust::device_vector& key_out, thrust::device_vector& val_out, thrust::device_vector& left_right_marks, bool scatter_val_out=true ) { thrust::device_vector* f_tmp = &val_out; thrust::device_vector* addr_tmp = tmp_misc_; thrust::exclusive_scan( /*thrust::make_transform_iterator(*/ left_right_marks.begin() /*,cuda::kd_tree_builder_detail::IsEven*/ /*())*/, /*thrust::make_transform_iterator(*/ left_right_marks.end() /*,cuda::kd_tree_builder_detail::IsEven*/ /*())*/, f_tmp->begin() ); cuda::kd_tree_builder_detail::set_addr3 sa; sa.val_=thrust::raw_pointer_cast(&left_right_marks[0]); sa.f_=thrust::raw_pointer_cast(&(*f_tmp)[0]); sa.npoints_=key_in.size(); thrust::counting_iterator it(0); thrust::transform(it, it+val_in.size(), addr_tmp->begin(), sa); thrust::scatter(key_in.begin(), key_in.end(), addr_tmp->begin(), key_out.begin()); if( scatter_val_out ) thrust::scatter(val_in.begin(), val_in.end(), addr_tmp->begin(), val_out.begin()); } //! allocates additional space in all the node-related vectors. //! new_size elements will be added to all vectors. void resize_node_vectors( size_t new_size ) { size_t add = new_size - child1_->size(); child1_->insert(child1_->end(), add, -1); parent_->insert(parent_->end(), add, -1); cuda::kd_tree_builder_detail::SplitInfo s; s.left=0; s.right=0; splits_->insert(splits_->end(), add, s); float4 f; aabb_min_->insert(aabb_min_->end(), add, f); aabb_max_->insert(aabb_max_->end(), add, f); } const thrust::device_vector* points_; // tree data, those are stored per-node //! left child of each node. (right child==left child + 1, due to the alloc mechanism) //! child1_[node]==-1 if node is a leaf node thrust::device_vector* child1_; //! parent node of each node thrust::device_vector* parent_; //! split info (dim/value or left/right pointers) thrust::device_vector* splits_; //! min aabb value of each node thrust::device_vector* aabb_min_; //! max aabb value of each node thrust::device_vector* aabb_max_; enum AllocationInfo { NodeCount=0, NodesAllocated=1, OutOfSpace=2 }; // those were put into a single vector of 3 elements so that only one mem transfer will be needed for all three of them // thrust::device_vector out_of_space_; // thrust::device_vector node_count_; // thrust::device_vector nodes_allocated_; thrust::device_vector allocation_info_; int max_leaf_size_; // coordinate values of the points thrust::device_vector* points_x_, * points_y_, * points_z_; // indices thrust::device_vector* index_x_, * index_y_, * index_z_; // owner node thrust::device_vector* owners_x_, * owners_y_, * owners_z_; // contains info about whether a point was partitioned to the left or right child after a split thrust::device_vector* leftright_x_, * leftright_y_, * leftright_z_; thrust::device_vector* tmp_index_, * tmp_owners_, * tmp_misc_; bool delete_node_info_; }; } // namespace flann #endifflann-1.8.4-src/src/cpp/flann/algorithms/all_indices.h0000644000000000000000000001521712075346130021353 0ustar rootroot/*********************************************************************** * Software License Agreement (BSD License) * * Copyright 2008-2009 Marius Muja (mariusm@cs.ubc.ca). All rights reserved. * Copyright 2008-2009 David G. Lowe (lowe@cs.ubc.ca). All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #ifndef FLANN_ALL_INDICES_H_ #define FLANN_ALL_INDICES_H_ #include "flann/general.h" #include "flann/algorithms/nn_index.h" #include "flann/algorithms/kdtree_index.h" #include "flann/algorithms/kdtree_single_index.h" #include "flann/algorithms/kmeans_index.h" #include "flann/algorithms/composite_index.h" #include "flann/algorithms/linear_index.h" #include "flann/algorithms/hierarchical_clustering_index.h" #include "flann/algorithms/lsh_index.h" #include "flann/algorithms/autotuned_index.h" #ifdef FLANN_USE_CUDA #include "flann/algorithms/kdtree_cuda_3d_index.h" #endif namespace flann { /** * enable_if sfinae helper */ template struct enable_if{}; template struct enable_if { typedef T type; }; /** * disable_if sfinae helper */ template struct disable_if{ typedef T type; }; template struct disable_if { }; /** * Check if two type are the same */ template struct same_type { enum {value = false}; }; template struct same_type { enum {value = true}; }; #define HAS_MEMBER(member) \ template \ struct member { \ typedef char No; \ typedef long Yes; \ template static Yes test( typename C::member* ); \ template static No test( ... ); \ enum { value = sizeof (test(0))==sizeof(Yes) }; \ }; HAS_MEMBER(needs_kdtree_distance) HAS_MEMBER(needs_vector_space_distance) HAS_MEMBER(is_kdtree_distance) HAS_MEMBER(is_vector_space_distance) struct DummyDistance { typedef float ElementType; typedef float ResultType; template ResultType operator()(Iterator1 a, Iterator2 b, size_t size, ResultType /*worst_dist*/ = -1) const { return ResultType(0); } template inline ResultType accum_dist(const U& a, const V& b, int) const { return ResultType(0); } }; /** * Checks if an index and a distance can be used together */ template