diff --git a/haversine_02/.gitignore b/haversine_02/.gitignore new file mode 100644 index 0000000..1a704fb --- /dev/null +++ b/haversine_02/.gitignore @@ -0,0 +1,3 @@ +count_and_distances +pairs.json +main diff --git a/haversine_02/.vscode/launch.json b/haversine_02/.vscode/launch.json new file mode 100644 index 0000000..3e66803 --- /dev/null +++ b/haversine_02/.vscode/launch.json @@ -0,0 +1,25 @@ +{ + "configurations": [ + { + "name": "Debug clustered generator", + "type": "cppdbg", + "request": "launch", + "cwd": "${workspaceFolder}", + "program": "${workspaceFolder}/main", + "args": [ + "--cluster", + "10" + ] + }, + { + "name": "Debug uniform generator", + "type": "cppdbg", + "request": "launch", + "cwd": "${workspaceFolder}", + "program": "${workspaceFolder}/main", + "args": [ + "10" + ] + } + ] +} \ No newline at end of file diff --git a/haversine_02/aliases.h b/haversine_02/aliases.h new file mode 100644 index 0000000..d7e7807 --- /dev/null +++ b/haversine_02/aliases.h @@ -0,0 +1,26 @@ +#ifndef ALIASES_H +#define ALIASES_H + +#include + +#define u8 uint8_t +#define u16 uint16_t +#define u32 uint32_t +#define u64 uint64_t + +#define i8 int8_t +#define i16 int16_t +#define i32 int32_t +#define i64 int64_t + +#define f32 float +#define f64 double + +#define INTERNAL static +#define PERSISTENT static + +#ifdef __cplusplus +#define CLASS_MEMBER static +#endif // __cplusplus + +#endif // !ALIASES_H diff --git a/haversine_02/argparser.cpp b/haversine_02/argparser.cpp new file mode 100644 index 0000000..2481c9d --- /dev/null +++ b/haversine_02/argparser.cpp @@ -0,0 +1,60 @@ +#include "argparser.h" +#include +#include +#include +#include + +void print_usage(); + +bool strequals(const char *str1, const char *str2); + +ParseResult parse_args(i32 argc, char *argv[]) { + GeneratorArgs args = {}; + + if (argc < 2) { + print_usage(); + + return {PARSE_STATUS_FAILED, args}; + } + + bool seeded = false; + bool positional_complete = false; + + for (i32 i = 1; i < argc; ++i) { + if (strequals(argv[i], "-h") || strequals(argv[i], "--help")) { + print_usage(); + + return {PARSE_STATUS_HELP, {}}; + } else if (strequals(argv[i], "--seed")) { + if (i + 1 < argc) { + args.seed = strtoul(argv[++i], NULL, 10); + seeded = true; + } + } else if (strequals(argv[i], "--cluster")) { + args.clustered = true; + } else { + args.count = strtoull(argv[i], NULL, 10); + positional_complete = true; + } + } + + if (!seeded) { + args.seed = time(NULL); + } + + if (!positional_complete) { + print_usage(); + + return {PARSE_STATUS_FAILED, args}; + } + + return {PARSE_STATUS_SUCCEEDED, args}; +} + +void print_usage() { + printf("Usage: main [-h] [--seed SEED] [--cluster] COUNT\n"); +} + +bool strequals(const char *str1, const char *str2) { + return strcmp(str1, str2) == 0; +} diff --git a/haversine_02/argparser.h b/haversine_02/argparser.h new file mode 100644 index 0000000..4c6acc0 --- /dev/null +++ b/haversine_02/argparser.h @@ -0,0 +1,25 @@ +#ifndef ARGPARSER_H +#define ARGPARSER_H + +#include "aliases.h" + +struct GeneratorArgs { + u32 seed; + bool clustered; + u64 count; +}; + +enum ParseStatus { + PARSE_STATUS_FAILED, + PARSE_STATUS_HELP, + PARSE_STATUS_SUCCEEDED, +}; + +struct ParseResult { + ParseStatus status; + GeneratorArgs args; +}; + +ParseResult parse_args(i32 argc, char *argv[]); + +#endif // !ARGPARSER_H diff --git a/haversine_02/build b/haversine_02/build new file mode 100755 index 0000000..4caafa8 --- /dev/null +++ b/haversine_02/build @@ -0,0 +1,10 @@ +#!/usr/bin/bash + +CC=clang++ +CFLAGS="-g -Wall -Wextra" +SRC=*.cpp +OUT=main + +set -x + +$CC $CFLAGS $SRC -o $OUT diff --git a/haversine_02/generator.cpp b/haversine_02/generator.cpp new file mode 100644 index 0000000..81c27bf --- /dev/null +++ b/haversine_02/generator.cpp @@ -0,0 +1,115 @@ +#include "generator.h" +#include "aliases.h" +#include "point_types.h" +#include +#include + +#define X_MIN -180.0 +#define X_MAX 180.0 +#define Y_MIN -90.0 +#define Y_MAX 90.0 + +f64 generate_random_double(f64 min, f64 max); +Point generate_random_point(f64 x_min, f64 x_max, f64 y_min, f64 y_max); +PointPair generate_random_pair(f64 x_min, f64 x_max, f64 y_min, f64 y_max); + +void fill_pairs_array(PairArray *pairs, bool clustered) { + if (clustered) { + u64 digit_count = (u64)log10(pairs->count) + 1; + + u64 cluster_count = 0; + + if (digit_count > 2) { + cluster_count = digit_count * 8; + } else { + cluster_count = digit_count; + } + + Point clusters[cluster_count]; + f64 radii[cluster_count]; + + u64 pairs_per_cluster = pairs->count / cluster_count; + u64 generated_pairs = 0; + u64 pairs_to_generate = 0; + + for (u64 i = 0; i < cluster_count; ++i) { + clusters[i] = { + generate_random_double(X_MIN, X_MAX), + generate_random_double(Y_MIN, Y_MAX), + }; + + radii[i] = generate_random_double(0.0, (digit_count - 1) * 40.0); + + f64 cluster_x_min = clusters[i].x - radii[i]; + if (cluster_x_min < X_MIN) { + cluster_x_min = X_MIN; + } + + f64 cluster_x_max = clusters[i].x + radii[i]; + if (cluster_x_max > X_MAX) { + cluster_x_max = X_MAX; + } + + f64 cluster_y_min = clusters[i].y - radii[i]; + if (cluster_y_min < Y_MIN) { + cluster_y_min = Y_MIN; + } + + f64 cluster_y_max = clusters[i].y + radii[i]; + if (cluster_y_max > Y_MAX) { + cluster_y_max = Y_MAX; + } + + pairs_to_generate = 0; + + if (generated_pairs + pairs_per_cluster < pairs->count) { + pairs_to_generate = pairs_per_cluster; + } else { + pairs_to_generate = pairs->count - generated_pairs; + } + + for (u64 i = 0; i < pairs_to_generate; ++i) { + // clang-format off + pairs->pairs[generated_pairs + i] = generate_random_pair( + cluster_x_min, + cluster_x_max, + cluster_y_min, + cluster_y_max + ); + // clang-format on + } + + generated_pairs += pairs_to_generate; + } + } else { + for (u64 i = 0; i < pairs->count; ++i) { + pairs->pairs[i] = generate_random_pair(X_MIN, X_MAX, Y_MIN, Y_MAX); + } + } +} + +f64 generate_random_double(f64 min, f64 max) { + u32 num = rand(); + + f64 result = min + (((f64)num / (f64)RAND_MAX) * (max - min)); + + return result; +} + +Point generate_random_point(f64 x_min, f64 x_max, f64 y_min, f64 y_max) { + Point p = { + generate_random_double(x_min, x_max), + generate_random_double(y_min, y_max), + }; + + return p; +} + +PointPair generate_random_pair(f64 x_min, f64 x_max, f64 y_min, f64 y_max) { + PointPair pair = { + generate_random_point(x_min, x_max, y_min, y_max), + generate_random_point(x_min, x_max, y_min, y_max), + }; + + return pair; +} diff --git a/haversine_02/generator.h b/haversine_02/generator.h new file mode 100644 index 0000000..6f32a50 --- /dev/null +++ b/haversine_02/generator.h @@ -0,0 +1,8 @@ +#ifndef GENERATOR_H +#define GENERATOR_H + +#include "point_types.h" + +void fill_pairs_array(PairArray *pairs, bool clustered); + +#endif // !GENERATOR_H diff --git a/haversine_02/haversine.cpp b/haversine_02/haversine.cpp new file mode 100644 index 0000000..2f4484e --- /dev/null +++ b/haversine_02/haversine.cpp @@ -0,0 +1,30 @@ +#include "haversine.h" +#include "aliases.h" +#include "point_types.h" +#include + +#define PI 3.14159265358979323845 +#define SQUARE(X) ((X) * (X)) + +f64 radians(f64 degrees); + +f64 haversine_of_degrees(const PointPair &pair, f64 radius) { + f64 x0 = pair.p1.x; + f64 y0 = pair.p1.y; + f64 x1 = pair.p2.x; + f64 y1 = pair.p2.y; + + f64 dy = radians(y1 - y0); + f64 dx = radians(x1 - x0); + y0 = radians(y0); + y1 = radians(y1); + + f64 root_term = + SQUARE(sin(dy / 2.0)) + cos(y0) * cos(y1) * SQUARE(sin(dx / 2.0)); + + f64 result = 2.0 * radius * asin(sqrt(root_term)); + + return result; +} + +f64 radians(f64 degrees) { return (degrees * PI) / 180.0; } diff --git a/haversine_02/haversine.h b/haversine_02/haversine.h new file mode 100644 index 0000000..0e7099a --- /dev/null +++ b/haversine_02/haversine.h @@ -0,0 +1,9 @@ +#ifndef HAVERSINE_H +#define HAVERSINE_H + +#include "aliases.h" +#include "point_types.h" + +f64 haversine_of_degrees(const PointPair &pair, f64 radius); + +#endif // !HAVERSINE_H diff --git a/haversine_02/main.cpp b/haversine_02/main.cpp new file mode 100644 index 0000000..d9e975c --- /dev/null +++ b/haversine_02/main.cpp @@ -0,0 +1,51 @@ +#include "aliases.h" +#include "argparser.h" +#include "generator.h" +#include "haversine.h" +#include "point_types.h" +#include +#include +#include + +#define EARTH_RADIUS_KM 6371.0 + +i32 main(i32 argc, char *argv[]) { + ParseResult result = parse_args(argc, argv); + + if (result.status == PARSE_STATUS_FAILED) { + return -1; + } else if (result.status == PARSE_STATUS_HELP) { + return 0; + } + + srand(result.args.seed); + + PairArray arr = {result.args.count, NULL}; + arr.pairs = (PointPair *)malloc(arr.count * sizeof(PointPair)); + + fill_pairs_array(&arr, result.args.clustered); + + write_pairs_to_json(arr, "pairs.json"); + + FILE *fp = fopen("count_and_distances", "w"); + + if (fp) { + fwrite(&(arr.count), sizeof(arr.count), 1, fp); + + i64 sum = 0; + for (u64 i = 0; i < arr.count; ++i) { + f64 distance = haversine_of_degrees(arr.pairs[i], EARTH_RADIUS_KM); + + fwrite(&distance, sizeof(f64), 1, fp); + + sum += distance; + } + printf("%ld\n", sum / arr.count); + + fclose(fp); + } + + free(arr.pairs); + + return 0; +} diff --git a/haversine_02/point_types.cpp b/haversine_02/point_types.cpp new file mode 100644 index 0000000..f6f105d --- /dev/null +++ b/haversine_02/point_types.cpp @@ -0,0 +1,75 @@ +#include "point_types.h" +#include +#include + +bool compare_point(const Point &p1, const Point &p2); +bool compare_pair(const PointPair &pair1, const PointPair &pair2); + +void write_pairs_to_binary(const PairArray &arr, const char *filename) { + FILE *fp = fopen(filename, "w"); + + if (fp) { + fwrite(&arr, sizeof(arr.count), 1, fp); + + u64 total_size = sizeof(*(arr.pairs)) * arr.count; + + fwrite(arr.pairs, total_size, 1, fp); + + fclose(fp); + } +} + +void read_pairs_from_binary(PairArray &arr, const char *filename) { + FILE *fp = fopen(filename, "r"); + + if (fp) { + fread(&(arr.count), sizeof(arr.count), 1, fp); + + arr.pairs = (PointPair *)malloc(arr.count * sizeof(PointPair)); + + fread(arr.pairs, sizeof(PointPair), arr.count, fp); + + fclose(fp); + } +} + +void write_pairs_to_json(const PairArray &arr, const char *filename) { + FILE *fp = fopen(filename, "w"); + + if (fp) { + fprintf(fp, "{\n\t\"pairs\": [\n"); + + for (u64 i = 0; i < arr.count; ++i) { + PointPair pair = arr.pairs[i]; + fprintf(fp, "\t\t{\"x0\": %f, \"y0\": %f, \"x1\": %f, \"y1\": %f}%s\n", + pair.p1.x, pair.p1.y, pair.p2.x, pair.p2.y, + i + 1 < arr.count ? "," : ""); + } + + fprintf(fp, "\t]\n}\n"); + + fclose(fp); + } +} + +bool compare_pair_array(const PairArray &arr1, const PairArray &arr2) { + if (arr1.count != arr2.count) { + return false; + } + + for (u64 i = 0; i < arr1.count; ++i) { + if (!compare_pair(arr1.pairs[i], arr2.pairs[i])) { + return false; + } + } + + return true; +} + +bool compare_point(const Point &p1, const Point &p2) { + return p1.x == p2.x && p1.y == p2.y; +} + +bool compare_pair(const PointPair &pair1, const PointPair &pair2) { + return compare_point(pair1.p1, pair2.p1) && compare_point(pair1.p2, pair2.p2); +} diff --git a/haversine_02/point_types.h b/haversine_02/point_types.h new file mode 100644 index 0000000..df2d183 --- /dev/null +++ b/haversine_02/point_types.h @@ -0,0 +1,27 @@ +#ifndef POINT_H +#define POINT_H + +#include "aliases.h" +#include + +struct Point { + f64 x; + f64 y; +}; + +struct PointPair { + Point p1; + Point p2; +}; + +struct PairArray { + u64 count; + PointPair *pairs; +}; + +void write_pairs_to_binary(const PairArray &arr, const char *filename); +void read_pairs_from_binary(PairArray &arr, const char *filename); +void write_pairs_to_json(const PairArray &arr, const char *filename); +bool compare_pair_array(const PairArray &arr1, const PairArray &arr2); + +#endif // !POINT_H