diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c795b05 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..056ab5d --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "files.associations": { + "inttypes.h": "c", + "errno.h": "c" + } +} \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..fb71766 --- /dev/null +++ b/Makefile @@ -0,0 +1,12 @@ +CFLAGS=-g -Wall + +.PHONY := gen_data + +gen_data: build/gen_data.exe + +build/gen_data.exe: src/gen_data.c + mkdir -p build + gcc -o build/gen_data.exe src/gen_data.c $(CFLAGS) -O2 + +clean: + rm -r build \ No newline at end of file diff --git a/src/gen_data.c b/src/gen_data.c new file mode 100644 index 0000000..2002269 --- /dev/null +++ b/src/gen_data.c @@ -0,0 +1,256 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#define EARTH_RADIUS 6372.8 +#define ARRAY_LEN(x) (sizeof(x)/sizeof(x[0])) +#define strequal(a, b) !strcmp(a, b) + +typedef float f32; +typedef double f64; + +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; + +enum gen_method { + GEN_UNIFORM, + GEN_CLUSTER +}; + +static u8 rand_u8() { + return rand() & 0xFF; +} +static u16 rand_u16() { + return rand_u8() | (rand_u8() << 8); +} +static u32 rand_u32() { + return rand_u16() | (rand_u16() << 16); +} + +// https://stackoverflow.com/a/55766267 +static f64 rand_f64() { + u64 r53 = ((uint64_t)(rand_u32()) << 21) ^ (rand_u8() >> 2); + return (double)r53 / 9007199254740991.0; // 2^53 - 1 +} + +static f64 rand_f64_range(f64 from, f64 to) { + return rand_f64() * (to - from) + from; +} + +static u32 rand_u32_range(u32 from, u32 to) { + return rand_u32() % (to - from + 1) + from; +} + +static void write_json_f64(char *key, f64 value, FILE *stream) { + char buffer[256]; + int size = snprintf(buffer, sizeof(buffer), "\"%s\":%21.16f", key, value); + fwrite(buffer, sizeof(char), size, stream); +} + +static f64 sqaure_f64(f64 num) +{ + return num*num; +} + +static f64 radians_to_degrees(f64 degrees) +{ + return 0.01745329251994329577 * degrees; +} + +static f64 get_harvensine_distance(f64 X0, f64 Y0, f64 X1, f64 Y1, f64 earth_radius) +{ + f64 lat1 = Y0; + f64 lat2 = Y1; + f64 lon1 = X0; + f64 lon2 = X1; + + f64 dLat = radians_to_degrees(lat2 - lat1); + f64 dLon = radians_to_degrees(lon2 - lon1); + lat1 = radians_to_degrees(lat1); + lat2 = radians_to_degrees(lat2); + + f64 a = sqaure_f64(sin(dLat/2.0)) + cos(lat1)*cos(lat2)*sqaure_f64(sin(dLon/2)); + f64 c = 2.0*asin(sqrt(a)); + + return earth_radius * c; +} + +static size_t find_filename_ext(const char *filename, size_t length) { + for (size_t i = length-1; i >= 0; i--) { + if (filename[i] == '.') { + return i+1; + } + } + return -1; +} + +static char *replace_filename_ext(const char *filename, const char *ext) { + size_t length = strlen(filename); + char *new_filename = calloc(strlen(filename)+strlen(ext)+1, sizeof(char)); + strcpy(new_filename, filename); + size_t extension_idx = find_filename_ext(filename, length); + strcpy(new_filename+extension_idx, ext); + return new_filename; +} + +static bool does_ext_match(const char *filename, const char *ext) { + size_t ext_idx = find_filename_ext(filename, strlen(filename)); + if (ext_idx == -1) return false; + return strequal(filename + ext_idx, ext); +} + +static void write_json_start(FILE *f) { + fwrite("{\"pairs\":[\n", sizeof(char), 11, f); +} + +static void write_json_end(FILE *f) { + fwrite("]}", sizeof(char), 2, f); +} + +static void write_json_row(f64 x0, f64 y0, f64 x1, f64 y1, bool is_last, FILE *f) { + fwrite("\t{", sizeof(char), 2, f); + + write_json_f64("x0", x0, f); + fwrite(", ", sizeof(char), 2, f); + write_json_f64("y0", y0, f); + fwrite(", ", sizeof(char), 2, f); + write_json_f64("x1", x1, f); + fwrite(", ", sizeof(char), 2, f); + write_json_f64("y1", y1, f); + + fwrite("}", sizeof(char), 1, f); + if (!is_last) { + fwrite(",", sizeof(char), 1, f); + } + fwrite("\n", sizeof(char), 1, f); +} + +static void print_usage(char *program) { + fprintf(stderr, "Usage: %s \n", program); + fprintf(stderr, "Commands:\n"); + fprintf(stderr, "\tuniform [seed]\n"); + fprintf(stderr, "\tcluster [seed]\n"); +} + +int main(int argc, char **argv) { + if (argc < 2) { + print_usage(argv[0]); + return -1; + } + + u32 row_count; + char *json_output; + u32 seed = time(NULL); + char *method_name = argv[1]; + enum gen_method method; + if (strequal(method_name, "uniform")) { + if (argc < 4) { + print_usage(argv[0]); + return -1; + } + row_count = atoi(argv[2]); + json_output = argv[3]; + + if (argc >= 5) { + seed = atoi(argv[4]); + } + method = GEN_UNIFORM; + } else if (strequal(method_name, "cluster")) { + if (argc < 4) { + print_usage(argv[0]); + return -1; + } + row_count = atoi(argv[2]); + json_output = argv[3]; + + if (argc >= 5) { + seed = atoi(argv[4]); + } + method = GEN_CLUSTER; + } else { + print_usage(argv[0]); + return -1; + } + + if (!does_ext_match(json_output, "json")) { + fprintf(stderr, "ERROR: Expected json file to end with .json\n"); + return -1; + } + + FILE *json_file = fopen(json_output, "w"); + if (json_file == NULL) { + fprintf(stderr, "Failed to open file '%s' for writting: %s\n", json_output, strerror(errno)); + return -1; + } + + char *answer_output = replace_filename_ext(json_output, "bin"); + FILE *answer_file = fopen(answer_output, "w"); + if (json_file == NULL) { + fprintf(stderr, "Failed to open file '%s' for writting: %s\n", answer_output, strerror(errno)); + return -1; + } + + srand(seed); + f64 sum = 0; + if (method == GEN_UNIFORM) { + write_json_start(json_file); + for (size_t i = 0; i < row_count; i++) { + f64 x0 = rand_f64_range(-180, 180); + f64 y0 = rand_f64_range(-90, 90); + f64 x1 = rand_f64_range(-180, 180); + f64 y1 = rand_f64_range(-90, 90); + + f64 distance = get_harvensine_distance(x0, y0, x1, y1, EARTH_RADIUS); + sum += distance; + + write_json_row(x0, y0, x1, y1, i == row_count-1, json_file); + fwrite(&distance, sizeof(f64), 1, answer_file); + } + write_json_end(json_file); + } else if (method == GEN_CLUSTER) { + u32 cluster_count = 32; // TODO: Make this configurable + f64 cluster_x_range = 180.0 / 15.0; + f64 cluster_y_range = 90.0 / 15.0; + + f64 *clusters_x = malloc(cluster_count * sizeof(f64)); + f64 *clusters_y = malloc(cluster_count * sizeof(f64)); + for (size_t i = 0; i < cluster_count; i++) { + clusters_x[i] = rand_f64_range(-180, 180); + clusters_x[i] = rand_f64_range(-90, 90); + } + + write_json_start(json_file); + for (size_t i = 0; i < row_count; i++) { + u32 from_cluster = rand_u32_range(0, cluster_count-1); + u32 to_cluster = rand_u32_range(0, cluster_count-1); + + f64 x0 = clusters_x[from_cluster] + rand_f64_range(-cluster_x_range, cluster_x_range); + f64 y0 = clusters_y[from_cluster] + rand_f64_range(-cluster_y_range, cluster_y_range); + f64 x1 = clusters_x[to_cluster] + rand_f64_range(-cluster_x_range, cluster_x_range); + f64 y1 = clusters_y[to_cluster] + rand_f64_range(-cluster_y_range, cluster_y_range); + + f64 distance = get_harvensine_distance(x0, y0, x1, y1, EARTH_RADIUS); + sum += distance; + + write_json_row(x0, y0, x1, y1, i == row_count-1, json_file); + fwrite(&distance, sizeof(f64), 1, answer_file); + } + write_json_end(json_file); + } + + fwrite(&sum, sizeof(f64), 1, answer_file); + + printf("Used earth radius: %f\n", EARTH_RADIUS); + printf("Seed: %d\n", seed); + printf("Number of pairs: %d\n", row_count); + printf("Expected sum: %lf\n", sum); + + return 0; +} \ No newline at end of file