VM2D 1.14
Vortex methods for 2D flows simulation
Loading...
Searching...
No Matches
knnCPU.cpp
Go to the documentation of this file.
1/*--------------------------------*- VM2D -*-----------------*---------------*\
2| ## ## ## ## #### ##### | | Version 1.14 |
3| ## ## ### ### ## ## ## ## | VM2D: Vortex Method | 2026/03/06 |
4| ## ## ## # ## ## ## ## | for 2D Flow Simulation *----------------*
5| #### ## ## ## ## ## | Open Source Code |
6| ## ## ## ###### ##### | https://www.github.com/vortexmethods/VM2D |
7| |
8| Copyright (C) 2017-2026 I. Marchevsky, K. Sokol, E. Ryatina, A. Kolganova |
9*-----------------------------------------------------------------------------*
10| File name: knnCPU.cpp |
11| Info: Source code of VM2D |
12| |
13| This file is part of VM2D. |
14| VM2D is free software: you can redistribute it and/or modify it |
15| under the terms of the GNU General Public License as published by |
16| the Free Software Foundation, either version 3 of the License, or |
17| (at your option) any later version. |
18| |
19| VM2D is distributed in the hope that it will be useful, but WITHOUT |
20| ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
21| FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
22| for more details. |
23| |
24| You should have received a copy of the GNU General Public License |
25| along with VM2D. If not, see <http://www.gnu.org/licenses/>. |
26\*---------------------------------------------------------------------------*/
27
41#include "knnCPU.h"
42#include "Gpudefs.h"
43#include <algorithm>
44#include <omp.h>
45
46namespace knnNew
47{
48
49 const int twoPowCodeLengthVar = (1 << codeLength);
50
51
52
53 //"Разрежение" двоичного представления беззнакового целого, вставляя по одному нулику между всеми битами
54 unsigned int ExpandBits(unsigned int v)
55 {
56 // вставит 1 нуль
57 v = (v | (v << 8)) & 0x00FF00FF; // 00000000`00000000`abcdefgh`ijklmnop
58 // | 00000000`abcdefgh`ijklmnop`00000000
59 // = 00000000`abcdefgh`XXXXXXXX`ijklmnop
60 // & 00000000`11111111`00000000`11111111
61 // = 00000000`abcdefgh`00000000`ijklmnop
62
63 v = (v | (v << 4)) & 0x0F0F0F0F; // 00000000`abcdefgh`00000000`ijklmnop
64 // | 0000abcd`efgh0000`0000ijkl`mnop0000
65 // = 0000abcd`XXXXefgh`0000ijkl`XXXXmnop
66 // & 00001111`00001111`00001111`00001111
67 // = 0000abcd`0000efgh`0000ijkl`0000mnop
68
69 v = (v | (v << 2)) & 0x33333333; // 0000abcd`0000efgh`0000ijkl`0000mnop
70 // | 00abcd00`00efgh00`00ijkl00`00mnop00
71 // = 00abXXcd`00efXXgh`00ijXXkl`00mnXXop
72 // & 00110011`00110011`00110011`00110011
73 // = 00ab00cd`00ef00gh`00ij00kl`00mn00op
74
75 v = (v | (v << 1)) & 0x55555555; // 00ab00cd`00ef00gh`00ij00kl`00mn00op
76 // | 0ab00cd0`0ef00gh0`0ij00kl0`0mn00op0
77 // = 0aXb0cXd`0eXf0gXh`0iXj0kXl`0mXn0oXp
78 // & 01010101`01010101`01010101`01010101
79 // = 0a0b0c0d`0e0f0g0h`0i0j0k0l`0m0n0o0p
80 return v;
81 }
82
83
84 //Мортоновский код для пары из чисел типа double
85 //Исходное число - строго в диапазоне [0, 1 / (1.0 + 1/(2^codeLength - 1) ))
86 unsigned int Morton2D(const Point2D& r)
87 {
88 const Point2D& rscale = twoPowCodeLengthVar * r;
89 const unsigned int& xx = ExpandBits((unsigned int)(rscale[0]));
90 const unsigned int& yy = ExpandBits((unsigned int)(rscale[1]));
91 return yy | (xx << 1);
92 }
93
94
96 void RSort_Parallel(TParticleCode* m, TParticleCode* m_temp, unsigned int n, unsigned int* s)
97 {
98 if (n == 0)
99 return;
100 // Количество задействованных потоков
101 unsigned char threads = omp_get_max_threads();
102 //std::cout << "threads = " << (int)threads << std::endl;
103
104#pragma omp parallel num_threads(threads)
105 {
107 TParticleCode* dest = m_temp;
108 unsigned int l = omp_get_thread_num();
109 unsigned int div = n / omp_get_num_threads();
110 unsigned int mod = n % omp_get_num_threads();
111 unsigned int left_index = l < mod ? (div + (mod == 0 ? 0 : 1)) * l : n - (omp_get_num_threads() - l) * div;
112 unsigned int right_index = left_index + div - (mod > l ? 0 : 1);
113
114 for (unsigned int digit = 0; digit < sizeof(m->key); ++digit)
115 {
116 unsigned int s_sum[256] = { 0 };
117 unsigned int s0[256] = { 0 };
118 unsigned char* b1 = (unsigned char*)&source[right_index].key;
119 unsigned char* b2 = (unsigned char*)&source[left_index].key;
120 while (b1 >= b2)
121 {
122 ++s0[*(b1 + digit)];
123 b1 -= sizeof(TParticleCode);
124 }
125 for (unsigned int i = 0; i < 256; i++)
126 {
127 s[i + 256 * l] = s0[i];
128 }
129
130#pragma omp barrier
131 for (unsigned int j = 0; j < threads; j++)
132 {
133 for (unsigned int i = 0; i < 256; i++)
134 {
135 s_sum[i] += s[i + 256 * j];
136 if (j < l)
137 {
138 s0[i] += s[i + 256 * j];
139 }
140 }
141 }
142
143 for (unsigned int i = 1; i < 256; ++i)
144 {
145 s_sum[i] += s_sum[i - 1];
146 s0[i] += s_sum[i - 1];
147 }
148 unsigned char* b = (unsigned char*)&source[right_index].key + digit;
149 TParticleCode* v1 = &source[right_index];
150 TParticleCode* v2 = &source[left_index];
151 while (v1 >= v2)
152 {
153 dest[--s0[*b]] = *v1--;
154 b -= sizeof(TParticleCode);
155 }
156#pragma omp barrier
157 std::swap(source, dest);
158 }
159 }
160
161 // Если ключ структуры однобайтовый, просто копируем в исходный массив
162 if (sizeof(m->key) == 1)
163 {
164 memcpy(m, m_temp, n * sizeof(TParticleCode));
165 }
166 }
167
168
169 inline size_t BinSearch(const std::vector<std::pair<double, size_t>>& currentNN, double x, int low, int high)
170 {
171 int mid = -1;
172
173 if (x > currentNN[high].first)
174 return high + 1;
175
176 while (low <= high) {
177 mid = (low + high) / 2;
178
179 if (currentNN[mid].first == x)
180 return mid + 1; //выход из цикла
181
182 if (currentNN[mid].first < x)
183 low = mid + 1;
184 else
185 high = mid - 1;
186 }
187 return mid;
188 }
189
190 void newSort(std::vector<std::pair<double, size_t>>& mass, std::vector<std::pair<double, size_t>>& dstKeys) {
191 const size_t k = mass.size();
192 size_t cnt = 0;
193
194 for (size_t i = 0; i < k; ++i)
195 {
196 double elem = mass[i].first;
197
198 cnt = 0;
199 for (size_t j = 0; j < k; ++j)
200 cnt += (mass[j].first < elem);
201
202 //Это для того, чтобы сортировка была устойчивой? Без этого никак?
203 //&&&
204 //for (size_t j = 0; j < i; ++j)
205 // cnt += (mass[j].first == elem);
206
207 dstKeys[cnt] = mass[i];
208 }
209 mass.swap(dstKeys);
210 }
211
213 int iii,
214 std::vector<std::pair<double, size_t>>& currentNN, const std::vector<std::pair<double, size_t>>& candidateNN,
215 std::vector<size_t>& loc,
216 std::vector<size_t>& counter,
217 std::vector<size_t>& offset,
218 std::vector<size_t>& counterScan,
219 std::vector<std::pair<double, size_t>>& updateNN
220 )
221 {
222 const size_t k = candidateNN.size() / 2; //количество ближайших соседей
223
224 for (size_t j = 0; j < 2 * k; ++j)
225 loc[j] = BinSearch(currentNN, candidateNN[j].first, 0, (int)k - 1);
226
227 for (size_t j = 0; j < 2 * k; ++j)
228 counter[j] = j & 1;
229
230 for (size_t j = 0; j < 2 * k; ++j)
231 {
232 if ((loc[j] > 0) && ((loc[j] == k) || (candidateNN[j].second == currentNN[loc[j] - 1].second)))
233 offset[j] = k + 1;
234 else
235 offset[j] = counter[2 * loc[j]]++;
236 }
237
238 counterScan[0] = 0;
239 for (size_t j = 1; j < 2 * k; ++j)
240 counterScan[j] = counterScan[j - 1] + counter[j - 1];
241
242 size_t index;
243 for (size_t j = 0; j < k; ++j)
244 {
245 index = counterScan[2 * j + 1];
246 if (index < k)
247 updateNN[index] = currentNN[j];
248 }
249
250 for (size_t j = 0; j < 2 * k; ++j)
251 {
252 if (2 * loc[j] < 2 * k)
253 {
254 index = counterScan[2 * loc[j]] + offset[j];
255 if (index < k)
256 updateNN[index] = candidateNN[j];
257 }
258 }
259
260 currentNN.swap(updateNN);
261 }
262
263
264 std::pair<bool, bool> calcCheck(const Vortex2D& vtxi, const Vortex2D& vtxk, double maxG, double cSP, double cRBP, double epsCol, int type, double& d2)
265 {
266 bool flagExit = false;
267 bool check = false;
268
269 //линейное увеличение радиуса коллапса
270 double mnog = std::max(1.0, /* 2.0 * */ (vtxi.r()[0] - cRBP) / cSP);
271 double r2test = (epsCol * mnog) * (epsCol * mnog);
272
273 if (type == 1)
274 r2test *= 4.0; //Увеличение радиуса коллапса в 2 раза для коллапса вихрей разных знаков
275
276 d2 = (vtxi.r() - vtxk.r()).length2();
277
278 if (d2 > r2test)
279 {
280 flagExit = true;
281 return { true, false };
282 }
283
284 const double gi = vtxi.g();
285 const double gk = vtxk.g();
286
287 if (d2 < r2test)
288 {
289 switch (type)
290 {
291 case 0:
292 check = (fabs(gi * gk) != 0.0) && (fabs(gi + gk) < (mnog * mnog) * maxG);
293 break;
294 case 1:
295 check = (gi * gk < 0.0);
296 break;
297 case 2:
298 check = (gi * gk > 0.0) && (fabs(gi + gk) < (mnog * mnog) * maxG);
299 break;
300 }
301 }//if r2 < r2_test
302
303 return { false, check };
304 }
305
306}
307
308void WakekNNnewForCollaps(const std::vector<Vortex2D>& vtx, const size_t k, std::vector<std::vector<std::pair<double, size_t>>>& initdist,
309 double cSP, double cRBP, double maxG, double epsCol, int type)
310{
311 using namespace knnNew;
312
313 double preTime = -omp_get_wtime();
314
315 const size_t n = vtx.size();
316
318 auto xx = std::minmax_element(vtx.begin(), vtx.end(), [](const Vortex2D& P1, const Vortex2D& P2) {return P1.r()[0] < P2.r()[0]; });
319 auto yy = std::minmax_element(vtx.begin(), vtx.end(), [](const Vortex2D& P1, const Vortex2D& P2) {return P1.r()[1] < P2.r()[1]; });
320
321 double scale = std::max(xx.second->r()[0] - xx.first->r()[0], yy.second->r()[1] - yy.first->r()[1]);
322
323 //сортировка массива (data) по мортоновским кодам
324 std::vector<unsigned int> s(256 * omp_get_max_threads());
325
326 std::vector<TParticleCode> mcdata(vtx.size());
327 std::vector<TParticleCode> mcdata_temp(vtx.size());
328
329 //std::vector<unsigned int> iq;
330
331 //Сдвигаем все точки
332 const int nSdvig = 5;
333 double tStart[nSdvig], tFinish[nSdvig];
334 double tm[nSdvig][5];
335
336 preTime += omp_get_wtime();
337
338 std::pair<double, size_t> zeroPair = std::make_pair<double, size_t>(0.0, 0);
339 std::pair<double, size_t> minusOnePair = std::make_pair<double, size_t>(-1.0, 0);
340
341 std::vector<std::pair<double, size_t>> dist(2 * k, { -1.0, 0 });
342 std::vector<size_t> loc(2 * k);
343 std::vector<size_t> counter(2 * k, 0);
344 std::vector<size_t> offset(2 * k, 0);
345 std::vector<size_t> counterScan(2 * k, 0);
346 std::vector<std::pair<double, size_t>> dstKeys1(2 * k, zeroPair);
347
348 std::vector<std::pair<double, size_t>> updateNN(k, zeroPair);
349 std::vector<std::pair<double, size_t>> dstKeys(k, zeroPair);
350
351 //14-05-2024
352 for (size_t sdvig = 0; sdvig < nSdvig /*5*/; ++sdvig)
353 {
354 tStart[sdvig] = omp_get_wtime();
355
356 const Point2D& lowLeft = { xx.first->r()[0], yy.first->r()[1] };
357 double* time = tm[sdvig];
358
359 //масштабирование координат вихрей в [0;0.75)^2, поиск их мортоновских кодов
360
361 time[0] = omp_get_wtime();
362#pragma omp parallel for
363 for (int i = 0; i < vtx.size(); ++i)
364 {
365 Point2D sh = (vtx[i].r() - lowLeft) * (0.75 / scale) + sdvig * Point2D{ 0.05, 0.05 };
366 mcdata[i].key = Morton2D(sh);
367 mcdata[i].originNumber = i;
368 }
369
370 time[1] = omp_get_wtime();
371
372 //сортировка единого массива (data и query) по мортоновским кодам
373 RSort_Parallel(mcdata.data(), mcdata_temp.data(), (int)mcdata.size(), s.data());
374
375 time[2] = omp_get_wtime();
376
377 for (size_t idx = 0; idx < 2 * k; ++idx)
378 {
379 dist[idx] = minusOnePair;
380 dstKeys1[idx] = zeroPair;
381 loc[idx] = counter[idx] = offset[idx] = counterScan[idx] = 0;
382 }
383
384 for (size_t idx = 0; idx < k; ++idx)
385 updateNN[idx] = dstKeys[idx] = zeroPair;
386
387 time[3] = omp_get_wtime();
388
389#pragma omp parallel for firstprivate(dist, loc, counter, offset, counterScan, updateNN, dstKeys, dstKeys1)
390 for (int i = 0; i < initdist.size(); ++i)
391 {
392 const Vortex2D& vtxi = vtx[mcdata[i].originNumber];
393
394 int cntr = 0;
395 int search = i - 1; // iq[i];
396 std::vector<std::pair<double, size_t>>& fillPosition = ((sdvig == 0) ? initdist[mcdata[i].originNumber] : dist);
397
398 while ((cntr < k) && (search >= 0))
399 {
400 const Vortex2D& vtxk = vtx[mcdata[search].originNumber];
401 bool flagExit, check;
402 double d2;
403 std::tie(flagExit, check) = calcCheck(vtxi, vtxk, maxG, cSP, cRBP, epsCol, type, d2);
404
405 if (flagExit)
406 break;
407
408 if ((mcdata[search].originNumber > mcdata[i].originNumber) && check)
409 fillPosition[cntr++] = { d2, mcdata[search].originNumber };
410
411 --search;
412 }
413
414 for (int w = cntr; w < k; ++w)
415 fillPosition[cntr++] = { 100000000.0, 0 };
416
417 search = i + 1;
418
419 while ((cntr < 2 * k) && (search < mcdata.size()))
420 {
421 const Vortex2D& vtxk = vtx[mcdata[search].originNumber];
422 bool flagExit, check;
423 double d2;
424 std::tie(flagExit, check) = calcCheck(vtxi, vtxk, maxG, cSP, cRBP, epsCol, type, d2);
425
426 if (flagExit)
427 break;
428
429 if ((mcdata[search].originNumber > mcdata[i].originNumber) && check)
430 fillPosition[cntr++] = { d2, mcdata[search].originNumber };
431
432 ++search;
433 }
434 for (int w = cntr; w < 2 * k; ++w)
435 fillPosition[cntr++] = { 100000000.0, 0 };
436
437 if (sdvig == 0)
438 {
439 newSort(initdist[mcdata[i].originNumber], dstKeys1);
440 initdist[mcdata[i].originNumber].resize(k);
441 }
442 else
443 {
444 newSort(dist, dstKeys1);
445 newMerge(mcdata[i].originNumber, initdist[mcdata[i].originNumber], dist, loc, counter, offset, counterScan, updateNN);
446 newSort(initdist[mcdata[i].originNumber], dstKeys);
447 }
448 }
449
450 time[4] = omp_get_wtime();
451
452 tFinish[sdvig] = omp_get_wtime();
453
454 }//for sdvig
455} // WakekNNnewForCollaps
456
457
458/*
459void WakekNNnewForEpsast(const std::vector<BH::PointsCopy>& vtx, const size_t k, std::vector<std::vector<std::pair<double, size_t>>>& initdist)
460{
461 using namespace knnNew;
462
463 double preTime = -omp_get_wtime();
464
465 const size_t n = vtx.size();
466
468 auto xx = std::minmax_element(vtx.begin(), vtx.end(), [](const Vortex2D& P1, const Vortex2D& P2) {return P1.r()[0] < P2.r()[0]; });
469 auto yy = std::minmax_element(vtx.begin(), vtx.end(), [](const Vortex2D& P1, const Vortex2D& P2) {return P1.r()[1] < P2.r()[1]; });
470
471 double scale = std::max(xx.second->r()[0] - xx.first->r()[0], yy.second->r()[1] - yy.first->r()[1]);
472
473 //сортировка массива (data) по мортоновским кодам
474 std::vector<unsigned int> s(256 * omp_get_max_threads());
475
476 std::vector<TParticleCode> mcdata(vtx.size());
477 std::vector<TParticleCode> mcdata_temp(vtx.size());
478
479 //std::vector<unsigned int> iq;
480
481 //Сдвигаем все точки
482 const int nSdvig = 5;
483 double tStart[nSdvig], tFinish[nSdvig];
484 double tm[nSdvig][5];
485
486 preTime += omp_get_wtime();
487
488 std::pair<double, size_t> zeroPair = std::make_pair<double, size_t>(0.0, 0);
489 std::pair<double, size_t> minusOnePair = std::make_pair<double, size_t>(-1.0, 0);
490
491 std::vector<std::pair<double, size_t>> dist(2 * k, { -1.0, 0 });
492 std::vector<size_t> loc(2 * k);
493 std::vector<size_t> counter(2 * k, 0);
494 std::vector<size_t> offset(2 * k, 0);
495 std::vector<size_t> counterScan(2 * k, 0);
496 std::vector<std::pair<double, size_t>> dstKeys1(2 * k, zeroPair);
497
498 std::vector<std::pair<double, size_t>> updateNN(k, zeroPair);
499 std::vector<std::pair<double, size_t>> dstKeys(k, zeroPair);
500
501 //14-05-2024
502 for (size_t sdvig = 0; sdvig < nSdvig; ++sdvig)
503 {
504 tStart[sdvig] = omp_get_wtime();
505
506 const Point2D& lowLeft = { xx.first->r()[0], yy.first->r()[1] };
507 double* time = tm[sdvig];
508
509 //масштабирование координат вихрей в [0;0.75)^2, поиск их мортоновских кодов
510
511 time[0] = omp_get_wtime();
512#pragma omp parallel for
513 for (int i = 0; i < vtx.size(); ++i)
514 {
515 Point2D sh = (vtx[i].r() - lowLeft) * (0.75 / scale) + sdvig * Point2D{ 0.05, 0.05 };
516 mcdata[i].key = Morton2D(sh);
517 mcdata[i].originNumber = i;
518 }
519
520 time[1] = omp_get_wtime();
521
522 //сортировка единого массива (data и query) по мортоновским кодам
523 RSort_Parallel(mcdata.data(), mcdata_temp.data(), (int)mcdata.size(), s.data());
524
525 time[2] = omp_get_wtime();
526
527 for (size_t idx = 0; idx < 2 * k; ++idx)
528 {
529 dist[idx] = minusOnePair;
530 dstKeys1[idx] = zeroPair;
531 loc[idx] = counter[idx] = offset[idx] = counterScan[idx] = 0;
532 }
533
534 for (size_t idx = 0; idx < k; ++idx)
535 updateNN[idx] = dstKeys[idx] = zeroPair;
536
537 time[3] = omp_get_wtime();
538
539#pragma omp parallel for firstprivate(dist, loc, counter, offset, counterScan, updateNN, dstKeys, dstKeys1)
540 for (int i = 0; i < initdist.size(); ++i)
541 {
542 const Vortex2D& vtxi = vtx[mcdata[i].originNumber];
543
544 int cntr = 0;
545 int search = i - 1; // iq[i];
546 std::vector<std::pair<double, size_t>>& fillPosition = ((sdvig == 0) ? initdist[mcdata[i].originNumber] : dist);
547
548 while ((cntr < k) && (search >= 0))
549 {
550 const Vortex2D& vtxk = vtx[mcdata[search].originNumber];
551 double d2 = (vtxi.r()-vtxk.r()).length2();
552 fillPosition[cntr++] = { d2, mcdata[search].originNumber };
553 --search;
554 }
555
556 for (int w = cntr; w < k; ++w)
557 fillPosition[cntr++] = { 100000000.0, 0 };
558
559 search = i + 1;
560
561 while ((cntr < 2 * k) && (search < mcdata.size()))
562 {
563 const Vortex2D& vtxk = vtx[mcdata[search].originNumber];
564 double d2 = (vtxi.r() - vtxk.r()).length2();
565 fillPosition[cntr++] = { d2, mcdata[search].originNumber };
566 ++search;
567 }
568 for (int w = cntr; w < 2 * k; ++w)
569 fillPosition[cntr++] = { 100000000.0, 0 };
570
571 if (sdvig == 0)
572 {
573 newSort(initdist[mcdata[i].originNumber], dstKeys1);
574 initdist[mcdata[i].originNumber].resize(k);
575 }
576 else
577 {
578 newSort(dist, dstKeys1);
579 newMerge(mcdata[i].originNumber, initdist[mcdata[i].originNumber], dist, loc, counter, offset, counterScan, updateNN);
580 newSort(initdist[mcdata[i].originNumber], dstKeys);
581 }
582 }
583
584 time[4] = omp_get_wtime();
585
586 tFinish[sdvig] = omp_get_wtime();
587
588 }//for sdvig
589
590 //for (size_t sd = 0; sd < nSdvig; ++sd)
591 // std::cout << "knn[" << sd << "] = " << tFinish[sd] - tStart[sd] << "sec. " << std::endl;
592
593} // WakekNNnewForEpsast
594*/
Описание констант и параметров для взаимодействия с графическим ускорителем
#define codeLength
Definition Gpudefs.h:101
Класс, опеделяющий двумерный вихревой элемент
Definition Vortex2D.h:59
HD Point2D & r()
Функция для доступа к радиус-вектору вихря
Definition Vortex2D.h:92
HD double & g()
Функция для доступа к циркуляции вихря
Definition Vortex2D.h:100
void WakekNNnewForCollaps(const std::vector< Vortex2D > &vtx, const size_t k, std::vector< std::vector< std::pair< double, size_t > > > &initdist, double cSP, double cRBP, double maxG, double epsCol, int type)
Definition knnCPU.cpp:308
knn CPU.
unsigned int ExpandBits(unsigned int v)
Definition knnCPU.cpp:54
void newMerge(int iii, std::vector< std::pair< double, size_t > > &currentNN, const std::vector< std::pair< double, size_t > > &candidateNN, std::vector< size_t > &loc, std::vector< size_t > &counter, std::vector< size_t > &offset, std::vector< size_t > &counterScan, std::vector< std::pair< double, size_t > > &updateNN)
Definition knnCPU.cpp:212
void RSort_Parallel(TParticleCode *m, TParticleCode *m_temp, unsigned int n, unsigned int *s)
Сортировка массива из мортоновский кодов
Definition knnCPU.cpp:96
unsigned int Morton2D(const Point2D &r)
Definition knnCPU.cpp:86
std::pair< bool, bool > calcCheck(const Vortex2D &vtxi, const Vortex2D &vtxk, double maxG, double cSP, double cRBP, double epsCol, int type, double &d2)
Definition knnCPU.cpp:264
void newSort(std::vector< std::pair< double, size_t > > &mass, std::vector< std::pair< double, size_t > > &dstKeys)
Definition knnCPU.cpp:190
const int twoPowCodeLengthVar
Definition knnCPU.cpp:49
size_t BinSearch(const std::vector< std::pair< double, size_t > > &currentNN, double x, int low, int high)
Definition knnCPU.cpp:169
unsigned int key
Мортоновский код частицы
Definition Point2D.h:57