1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
/*
 * Generates optimized (0, m, 2)-nets, as described in the paper:
 *      Ahmed and Wonka: "Optimizing Dyadic Nets"
 *
 * Compilation:
 *      g++ -O3 -march=core2 -msse3 -no-pie -o net-optimize-pointers net-optimize-pointers.cpp
 * Execution example:
 *      ./net-optimize-pointers -q v -s 0.5 -n 1000 1024
 * This produces 1024 points, optimizes them using Gaussian BN, up to 1000 iterations.
 *
 * 2020-06-07: First created by Abdalla Ahmed.
 * 2021-05-04: Revised by Abdalla Ahmed for inclusion with the paper.
 */

#include <stdlib.h>
#include <stdio.h>
#include <vector>
#include <cmath>
#include <time.h>
#include <string>
#include <cstring>
#include <signal.h>
#include <algorithm>
#include <stdint.h>

extern "C" {
#include "getopt.h"
}

struct Point {
    int x, y;
};
typedef std::vector<Point> Points;
//typedef std::vector<uint32_t> List;                                             // A linear list of uint32_t integers
typedef std::vector<int> List;    // jonas: I had to change from uint32_t to int to be able to compile                                         
typedef std::string String;

const double dhex = 1.07456993182354;                                           // sqrt(2/sqrt(3)).
static bool interuptFlag = false;

inline void shuffle(List &list) {                                               // Populate a randomly ordered list.
    uint32_t N = list.size();
    for (uint32_t i = 0; i < N; i++) list[i] = i;
    for (uint32_t i = N - 1; i > 0; i--) {                                      // Iterate down the list, and swap each place with another place down, inclusive of same place.
        uint32_t r = rand() % (i + 1);                                          // (i + 1) means that the same place is included.
        std::swap(list[i], list[r]);
    }
}

class Net {
private:
    int N;                                                                      // The number of points, must be a power of 2.
    int half;                                                                   // Shorthand for N / 2.
    int m;                                                                      // log_2(N).
    bool useOwen;                                                               // If the net is a sequence, optimization needs to be restricted to Own's scrambling.
    Points p;                                                                   // List of points
    std::vector<List> q;                                                        // Back reference from points to strata
    int main;                                                                   // Index of stratification used for neighbor search.
    int width, widthBits;                                                       // Number of strata in a row of main, which is always row-major.
    int height, heightBits;                                                     // Number of strata in a column of main.
    int y2x;                                                                    // Used if the main strata are not square, when m is odd.
    std::vector<double> kernel;                                                 // Gaussian kernel for void-and-cluster optimization.
    double sigma, sigmaSq2Inv;                                                  // SD of Gaussian kernel.
    double rfSq;                                                                // Square of target conflict radius.
    double conflictRadiusFactor;
    uint32_t filterRange;                                                       // Range of Gaussian filter.
    int range;                                                                  // Neighborhood to consider in cluster optimization.
    void init();
    List netSort();                                                             // Return a (0, 1)-sequence.
    uint32_t p2q(uint32_t i, uint32_t k);                                       // Retrieve the stratum of point i in the k'th stratification
    void swap(uint32_t k, uint32_t stratum);
    uint32_t torroidalDistance(uint32_t x1, uint32_t x2);
    uint32_t getConflictRadiusSq(uint32_t i);                                   // Conflict radius of the indexed point
    int maxConflict(uint32_t i, int k);                                         // Swap the i'th point width the candidate in the k'th stratification to maximize conflict radius. Return 1 if swap is accepted, 0 otherwise.
    int minConflict(uint32_t i, int k);
    double g(int dx);                                                           // Gaussian, accounting for periods
    double cluster(uint32_t i);                                                 // Compute the Gaussian-weighted distance of other points at the point location in a given stratum, for void-and-cluster filtering.
    int minCluster(uint32_t i, int k);                                          // Swap the i'th point width the candidate in the k'th stratification to reduce the cluster tightness. Return 1 if swap is accepted, 0 otherwise.
    String outputPath;                                                          // Path for output files; e.g. a sub-folder under /tmp
    int (Net::*energyFunction) (uint32_t i, int k);                             // A pointer to a function for energy-based swapping.
    int optimize(int i);                                                        // Make all permissible swaps to optimize relative to a given point index, using the designated energy function, and return the number of applied swaps.
public:
    Net(int pointCount, String path = "");                                      // Create an N-points binary net, where N is a power of 2.
    Net(char *pbmFileName, String path = "");                                   // Generate the net from stored flags
    void printText(String fileName = "net.txt");                                // Print a .txt file to stdout.
    void printEPS(String fileName = "net.eps");                                 // Print a paginated EPS file showing elementary intervals.
    void printFlags(String fileName = "net.pbm");                               // Print a bitmap of flags.
    void printComplement(String fileName = "complement.txt");
    void setSigma(double v);
    void setRf(double v);
    void setRange(int v);                                                       // Set neighborhood for cluster optimization; default is n2/2.
    double conflictRadius();                                                    // Global conflict radius.
    void optimize(std::string seq, int iterations);
    void upsample();
};

Net::Net(int pointCount, String path) {
    outputPath = path;
    m = ceil(std::log2(pointCount));                                            // Round up to a power of 2.
    N = 1 << m;
    p.resize(N);
    List xlist = netSort();                                                     // Populate the y's with a (0, m, 1)-net
    for (int i = 0; i < N; i++) {
        p[i] = {xlist[i], i};
    }
    init();
}

Net::Net(char *pbmFileName, String path) {
    outputPath = path;
    FILE *pbmFile = fopen(pbmFileName, "r");
    fscanf(pbmFile, "P1 %d %d", &N, &m);
    N *= 2;
    if (N != (1 << m)) {
        fprintf(stderr, "Wrong flags table size\n");
        exit(1);
    }
    p.resize(N);
    List xlist(N), tmpList(N);                                                  // We maintain two buffers and swap them
    for (int i = 0; i < N; i++) xlist[i] = i;                                   // Initialize to a natural order
    for (uint32_t span = N; span > 1; span >>= 1) {                             // Size of sorted sub sets, starting at the whole set
        for (uint32_t slotNo = 0; slotNo < N; slotNo += 2) {                    // Iterate through pairs of slots
            uint32_t relevantBits = slotNo & (span - 1);                        // The set will be permuted in this range only
            uint32_t newSlot0 = slotNo - relevantBits + (relevantBits >> 1);    // Perform a bit rotate over the relevant bits; the least significant being 0;
            uint32_t newSlot1 = newSlot0 + (span >> 1);                         // This is the partial bit rotate for slotNo + 1
            char c;
            fscanf(pbmFile, " %c", &c);
            uint32_t toggle = c == '0' ? 0 : 1;
            tmpList[newSlot0] = xlist[slotNo ^ toggle];
            tmpList[newSlot1] = xlist[(slotNo + 1) ^ toggle];
        }
        std::swap(xlist, tmpList);
    }
    for (int i = 0; i < N; i++) {
        p[i] = {xlist[i], i};
    }
    init();
}

void Net::init() {
    half = N / 2;
    main = m / 2;                                                               // Main stratification used for neighbor search
    y2x = (m & 1) ? 1 : 2;                                                      // Aspect ratio of main strata
    widthBits = ((m + 1) / 2);
    heightBits = m - widthBits;
    width = 1 << widthBits;
    height = N >> widthBits;
    conflictRadiusFactor = 1 / (sqrt(N) * dhex);
    fprintf(
        stderr, "N = %d, m = %d, main is %d, width = %d,"
        " height = %d, y2x = %d\n",
        N, m, main, width, height, y2x
    );
    q.resize(m+1);
    for (int k = 0; k <= m; k++) {
        q[k].resize(N);
    }
    for (int k = 0; k <= m; k++) {
        for (int i = 0; i < N; i++) {
            q[k][p2q(i, k)] = i;                                                // Point strata to points
        }
    }
    //filterRange = half + 1;
    filterRange = std::max(1025,half+1);
    kernel.resize(filterRange);
    setSigma(1.5);
    setRange(0);
    useOwen = false;
}

void Net::swap(uint32_t k_ref, uint32_t stratum) {
    uint32_t i = q[k_ref][stratum];
    uint32_t j = q[k_ref][stratum ^ 1];
    std::swap(p[i].y, p[j].y);                                                  // We always keep the x unchanged
    for (int k = k_ref + 1; k <= m; k++) {                                      // Affected stratifications
        std::swap(q[k][ p2q(i,k) ], q[k][ p2q(j,k) ]);
    }
}

inline uint32_t Net::p2q(uint32_t i, uint32_t k) {
    uint32_t xbits = p[i].x >> k;                                               // Points are indexed by their x's
    uint32_t ybits = p[i].y >> (m - k);
    return (ybits << (m-k)) | xbits;
}

List Net::netSort() {
    List list(N), tmpList(N);                                                   // We maintain two buffers and swap them
    for (int i = 0; i < N; i++) list[i] = i;                                    // Initialize to a natural order
    for (uint32_t span = N; span > 1; span >>= 1) {                             // Size of sorted sub sets, starting at the whole set
        for (uint32_t slotNo = 0; slotNo < N; slotNo += 2) {                    // Iterate through pairs of slots
            uint32_t relevantBits = slotNo & (span - 1);                        // The set will be permuted in this range only
            uint32_t newSlot0 = slotNo - relevantBits + (relevantBits >> 1);    // Perform a bit rotate over the relevant bits; the least significant being 0;
            uint32_t newSlot1 = newSlot0 + (span >> 1);                         // This is the partial bit rotate for slotNo + 1
            uint32_t toggle = rand() & 1;                                       // Randomly decide which slot maps to which. I tried extracting bits from a single random number, but it exhibited some correlation.
            tmpList[newSlot0] = list[slotNo ^ toggle];
            tmpList[newSlot1] = list[(slotNo + 1) ^ toggle];
        }
        std::swap(list, tmpList);
    }
    return list;
}

inline uint32_t Net::torroidalDistance(uint32_t x1, uint32_t x2) {
    if (x1 < x2) std::swap(x1, x2);
    uint32_t d = x1 - x2;
    if (d >= half) d = N - d;
    return d;
}

void Net::setSigma(double v) {
    sigma = v;
    double sigmaSqx2 = 2 * (v * v * N);
    sigmaSq2Inv = 1.0 / sigmaSqx2;
    fprintf(stderr, "Sigma = %f\n", v);
    for (int x = 0; x < filterRange; x++) {
        kernel[x] = exp(-(x*x) / sigmaSqx2);
    }
}

void Net::setRf(double v) {
    double rf = v * sqrt(N) * dhex;
    rfSq = rf * rf;
}

void Net::setRange(int v) {
    if (v > height / 2 || v <= 0) v = height / 2;
    range = v;
    fprintf(stderr,
        "Optimization neighborhood set to %d neighbor rows/columns\n", v
    );
}

uint32_t Net::getConflictRadiusSq(uint32_t i) {
    uint32_t min(N);
    uint32_t refStratum = p2q(i, main);
    uint32_t Yref = refStratum >> widthBits;
    uint32_t Xref = refStratum & (width - 1);
    for (int dY = -1; dY <= 1; dY++) {
        int Y = (Yref + dY + height) & (height - 1);
        for (int dX = -y2x; dX <= y2x; dX++) {
            if (dX == 0 && dY == 0) continue;
            int X = (Xref + dX + width) & (width - 1);
            int j = q[main][Y * width + X];                                     // Index of neighbor
            uint32_t dx = torroidalDistance(p[j].x, p[i].x);
            uint32_t dy = torroidalDistance(p[j].y, p[i].y);
            uint32_t rr = dx * dx + dy * dy;
            min = std::min(min, rr);
        }
    }
    return min;
}

double Net::conflictRadius() {
    uint32_t min(N);
    for (int i = 0; i < N; i++) {
        min = std::min(min, getConflictRadiusSq(i));
    }
    return conflictRadiusFactor * sqrt(min);
}

int Net::maxConflict(uint32_t i, int k) {
    int stratum = p2q(i, k);
    uint32_t j = q[k][stratum ^ 1];
    int rfSqCurrent = std::min(
        getConflictRadiusSq(i), getConflictRadiusSq(j)
    );
    swap(k, stratum);
    int rfSqNew = std::min(
        getConflictRadiusSq(i), getConflictRadiusSq(j)
    );
    if (rfSqNew > rfSqCurrent) {                                                // If the swap does improve the conflict radius accept it and return 1
        //fprintf(stderr, "[%2d %2d]\n", stratum, k);
        return 1;
    }
    swap(k, stratum);                                                           // Otherwise undo it;
    return 0;                                                                   //  and return 0.
}

int Net::minConflict(uint32_t i, int k) {
    int stratum = p2q(i, k);
    uint32_t j = q[k][stratum ^ 1];
    int rfSqCurrent = std::min(
        getConflictRadiusSq(i), getConflictRadiusSq(j)
    );
    swap(k, stratum);
    int rfSqNew = std::min(
        getConflictRadiusSq(i), getConflictRadiusSq(j)
    );
    if (rfSqNew < rfSqCurrent) {                                                // If the swap worsens the conflict radius accept it and return 1
        fprintf(stderr, "[%2d %2d]\n", stratum, k);
        return 1;
    }
    swap(k, stratum);                                                           // Otherwise undo it;
    return 0;                                                                   //  and return 0.
}

inline double Net::g(int dx) {
    double sum(0.0);
    for (int x = dx; x < filterRange; x += N) {
        sum += kernel[x];
    }
    for (int x = N - dx; x < filterRange; x += N) {
        sum += kernel[x];
    }
    return sum;
}

double Net::cluster(uint32_t i) {
    double sum(0.0);
    uint32_t refStratum = p2q(i, main);
    uint32_t Yref = refStratum >> widthBits;
    uint32_t Xref = refStratum & (width - 1);
    for (int dY = -range; dY < range; dY++) {
        int Y = (Yref + dY + height) & (height - 1);
        for (int dX = -y2x * range; dX < y2x * range; dX++) {
            if (dX == 0 && dY == 0) continue;
            int X = (Xref + dX + width) & (width - 1);
            int j = q[main][Y * width + X];                                     // Index of neighbor
            uint32_t dx = torroidalDistance(p[j].x, p[i].x);
            uint32_t dy = torroidalDistance(p[j].y, p[i].y);
            sum += g(dx) * g(dy);
        }
    }
    return sum;
}


int Net::minCluster(uint32_t i, int k) {
    int stratum = p2q(i, k);
    uint32_t j = q[k][stratum ^ 1];
    double sumCurrent = cluster(i) + cluster(j);
    swap(k, stratum);
    double sumNew = cluster(i) + cluster(j);
    if (sumNew < sumCurrent) {                                                  // If the swap does improve cluster tightness accept it and return 1.
        return 1;
    }
    swap(k, stratum);                                                           // Otherwise undo it;
    return 0;                                                                   //  and return 0.
}

int Net::optimize(int i) {
    int swapCount(0);
    if (useOwen) {
        swapCount += (this->*energyFunction)(i, 0);
        swapCount += (this->*energyFunction)(i, m-1);
        return swapCount;
    }
    for (int k = main; k >= 0; k--) {                                           // Vertical strata, we start in the middle stratifications, which are more significant
        swapCount += (this->*energyFunction)(i, k);
    }
    for (int k = main; k < m; k++) {                                            // horizontal strata, ~
        swapCount += (this->*energyFunction)(i, k);
    }
    return swapCount;
}

void Net::optimize(std::string seq, int iterations) {
    List order(N);
    for (int iteration = 0; iteration < iterations; iteration++) {
        fprintf(stderr, "Iteration %4d:\n", iteration);
        int totalSwapCount(0);
        for (int k = 0; k < seq.length(); k++) {
            switch (seq[k]) {
                case 'f': energyFunction = &Net::maxConflict; break;
                case 'v': energyFunction = &Net::minCluster; break;
                case 'F': energyFunction = &Net::minConflict; break;
                default:
                    fprintf(stderr, "Error: Unknown optimization option.");
                    exit(1);
            }
            shuffle(order);
            int swapCount(0);
            for (int i = 0; i < N && !interuptFlag; i++) {
                swapCount += optimize(order[i]);
            }
            fprintf(
                stderr,
                "  Performed %4d '%c' swaps."
                " Current conflict radius is %0.5f\n",
                swapCount, seq[k], conflictRadius()
            );
            totalSwapCount += swapCount;
        }
        if (interuptFlag || totalSwapCount == 0) break;
    }
}

void Net::printText(String fileName) {                                          // Generate a text printout
    char fullFileName[100];
    std::strcpy(fullFileName, (outputPath + fileName).c_str());
    FILE *file = fopen(fullFileName, "w");
    if (!file) {
        fprintf(stderr, "Failed to open %s\n", fullFileName);
        exit(1);
    }
    fprintf(file, "%d\n", N);
    double res = 1.0 / N;
    for (int stratum = 0; stratum < N; stratum++) {
        int i = q[main][stratum];                                               // We will use main stratification for ordering
        fprintf(file, "%0.9f %0.9f\n", res * p[i].x, res * p[i].y);
    }
    fclose(file);
}

void Net::printEPS(String fileName) {                                           // Generate a text printout
    char fullFileName[100];
    std::strcpy(fullFileName, (outputPath + fileName).c_str());
    FILE *file = fopen(fullFileName, "w");
    if (!file) {
        fprintf(stderr, "Failed to open %s\n", fullFileName);
        exit(1);
    }
    fprintf(
        file,
        "%%!PS-Adobe-3.0 EPSF-3.0\n"
        "%%%%BoundingBox: 0 0 4096 4096\n"
        "/N %d def\n"
        "/unit 1 N div def\n"
        "/depth N log 2 log div round cvi def\n"
        "/r 0.35 unit mul def\n"
        "/linewidth 0.15 unit mul def\n"
        "/p {\n"
        "    2 {linewidth 0.5 mul add exch} repeat\n"
        "    unit linewidth sub dup rectfill\n"
        "} def\n"
        "/plot {\n"
        "    /k exch def\n"
        "    4096 1 linewidth add div dup scale\n"
        "    linewidth 0.5 mul dup translate\n"
        "    2 setlinecap\n"
        "    gsave\n"
        "    0 setlinewidth 1 0.975 0.9375 setrgbcolor\n"
        "    %%0 1 N 1 sub {unit mul 0 moveto 0 1 rlineto} for stroke\n"
        "    %%0 1 N 1 sub {unit mul 0 exch moveto 1 0 rlineto} for stroke\n"
        "    1 0.8 0.5 setrgbcolor\n"
        "    linewidth setlinewidth\n"
        "    0 1 depth k sub bitshift N {\n"
        "        unit mul 0 exch moveto 1 0 rlineto\n"
        "    } for stroke\n"
        "    0 1 k bitshift N {\n"
        "        unit mul 0 moveto 0 1 rlineto\n"
        "    } for stroke\n"
        "    grestore\n"
        "    0.2 setgray\n"
        "    0 1 N 1 sub {\n"
        "        points exch get {unit mul} forall p\n"
        "    } for\n"
        "    showpage\n"
        "} def\n"
        "/points [\n",
        N
    );
    for (int stratum = 0; stratum < N; stratum++) {
        int i = q[main][stratum];                                               // We will use main stratification for ordering
        fprintf(file, "    [%4d %4d]\n", p[i].x, p[i].y);
    }
    fprintf(file, "] def\n0 1 depth {plot} for\n");
    fclose(file);
}

void Net::printFlags(String fileName) {                                         // Print a bitmap of flags
    char fullFileName[100];
    std::strcpy(fullFileName, (outputPath + fileName).c_str());
    FILE *file = fopen(fullFileName, "w");
    if (!file) {
        fprintf(stderr, "Failed to open %s\n", fullFileName);
        exit(1);
    }
    fprintf(file, "P1\n%d %d\n", N / 2, m);                                     // Print output file header
    for (int k = 0; k < m; k++) {
        for (int stratum = 0; stratum < N; stratum += 2) {
            int i = q[k][stratum];
            int j = q[k][stratum + 1];
            char flag = p[i].y < p[j].y ? '0' : '1';
            fprintf(file, "%c ", flag);
        }
        fprintf(file, "\n");
    }
    fclose(file);
}

void Net::printComplement(String fileName) {
    char fullFileName[100];
    std::strcpy(fullFileName, (outputPath + fileName).c_str());
    FILE *file = fopen(fullFileName, "w");
    if (!file) {
        fprintf(stderr, "Failed to open %s\n", fullFileName);
        exit(1);
    }
    List xlist(N), tmpList(N);                                                   // We maintain two buffers and swap them
    for (int i = 0; i < N; i++) xlist[i] = i;                                    // Initialize to a natural order
    for (int k = 0; k < m; k++) {
        int span = 1 << (m - k);
        for (uint32_t slotNo = 0; slotNo < N; slotNo += 2) {                    // Iterate through pairs of slots
            uint32_t relevantBits = slotNo & (span - 1);                        // The set will be permuted in this range only
            uint32_t newSlot0 = slotNo - relevantBits + (relevantBits >> 1);    // Perform a bit rotate over the relevant bits; the least significant being 0;
            uint32_t newSlot1 = newSlot0 + (span >> 1);                         // This is the partial bit rotate for slotNo + 1
            uint32_t toggle = p[q[k][slotNo]].y < p[q[k][slotNo + 1]].y ? 1 : 0;// Note that the decision is inverted
            tmpList[newSlot0] = xlist[slotNo ^ toggle];
            tmpList[newSlot1] = xlist[(slotNo + 1) ^ toggle];
        }
        std::swap(xlist, tmpList);
    }

    fprintf(file, "%d\n", N);
    double res = 1.0 / N;
    for (int y = 0; y < N; y++) {
        fprintf(file, "%0.9f %0.9f\n", res * (xlist[y] + 0.5), res * (y + 0.5));
    }
    fclose(file);
}

void Net::upsample() {
    List xlist(N), tmpList(N);                                                  // We maintain two buffers and swap them
    for (int i = 0; i < N; i++) xlist[i] = i;                                   // Initialize to a natural order
    for (int k = 0; k < m; k++) {
        int span = 1 << (m - k);
        for (uint32_t slotNo = 0; slotNo < N; slotNo += 2) {                    // Iterate through pairs of slots
            uint32_t relevantBits = slotNo & (span - 1);                        // The set will be permuted in this range only
            uint32_t newSlot0 = slotNo - relevantBits + (relevantBits >> 1);    // Perform a bit rotate over the relevant bits; the least significant being 0;
            uint32_t newSlot1 = newSlot0 + (span >> 1);                         // This is the partial bit rotate for slotNo + 1
            uint32_t toggle = p[q[k][slotNo]].y < p[q[k][slotNo + 1]].y ? 1 : 0;// Note that the decision is inverted
            tmpList[newSlot0] = xlist[slotNo ^ toggle];
            tmpList[newSlot1] = xlist[(slotNo + 1) ^ toggle];
        }
        std::swap(xlist, tmpList);
    }
    p.resize(2 * N);
    for (int i = 0; i < N; i++) {
        p[    i] = {2 * p[i].x, 2 * p[i].y};
        p[N + i] = {2 * xlist[i] + 1, 2 * i + 1};
    }
    m++;
    N *= 2;
    init();
    useOwen = true;
}


static void signalHandler(int signalCode) {
    fprintf(stderr, "Aborting ...\n");
    interuptFlag = true;
}

const char *USAGE_MESSAGE = "Usage: %s [options] <point count>\n"
"Options:\n"
"    -l <flagsFileName>\n"
"    -n <number of iterations>\n"
"    -q <optimization letter sequence>\n"
"    -s <sigma for Gaussian filtering>\n"
"    -r <target conflict radius>\n"
"    -b <log2optimizationBlockSize>\n"
"    -R <Neighborhood size for cluster optimization>\n"
"Optimization sequences:\n"
"    f: conflict;\n"
"    F: worsens conflict;\n"
"    v: void-n-cluster;\n"
"    V: void-n-cluster with Poisson disk enhancement\n"
"    e: electrostatic\n"
"    j: 1D conflict\n"
"    c: Poisson Disk\n";

int main(int argc,char **argv) {
    srand(time(NULL));
    //srand48(time(NULL));
    int opt;                                                                    // For use by getopt, the command line options utility
    int iterations = 0;
    double sigma = 0.4;
    char *flagsFileName = NULL;
    std::string optimizationSequence = "f";
    double rf = 0.75;
    uint32_t bBits = 0;
    int range = 0;
    fprintf(
        stderr,
        "Generate a point set of size N, where N is a power of 2\n"
    );
    while ((opt = getopt(argc, argv, "l:n:q:s:r:b:R:")) != -1) {                        // Modify default settings with command line options
        switch (opt) {
            case 'l': flagsFileName = optarg; break;
            case 'n': iterations = atoi(optarg); break;
            case 'q': optimizationSequence = optarg; break;
            case 's': sigma = atof(optarg); break;
            case 'r': rf = atof(optarg); break;
            case 'b': bBits = atoi(optarg); break;
            case 'R': range = atoi(optarg); break;
            default: fprintf(stderr, USAGE_MESSAGE, argv[0]); exit(1);
        }
    }
    if (optind > argc - 1) {
        fprintf(stderr, USAGE_MESSAGE, argv[0]); exit(1);
    }
    signal(SIGINT, signalHandler);
    uint32_t pointCount = atoi(argv[optind]);
    Net *net;
    if (flagsFileName != NULL) {
        net = new Net(flagsFileName);
    }
    else {
        net = new Net(pointCount);
    }
    net->setSigma(sigma);
    net->setRf(rf);
    net->setRange(range);
    net->printEPS("initial.eps");
    fprintf(stderr, "Optimizing ..\n");
    clock_t t0 = clock();
    if (iterations) net->optimize(optimizationSequence, iterations);
    clock_t t1 = clock();
    double totalTime = (double)(t1 - t0) / CLOCKS_PER_SEC;
    fprintf(stderr, "\ndone! Total time = %.6fs\n", totalTime);
    net->printText();
    net->printComplement();
    net->printEPS();
    net->printFlags();
//     net->upsample();
//     net->optimize(optimizationSequence, iterations);
//     net->printText("union.txt");
    fprintf(stderr, "rf = %0.5f\n", net->conflictRadius());
}