/*
 * This file contains a C demonstration of ATC.
 */

#include <stddef.h>
#include <stdbool.h>
#include <stdio.h>
#include <math.h>
#include <stdlib.h>

#include "atc.h"

int main() {
	
	printf("ATC version %s\n", ATC_VERSION);
	
	// Parameter set-up
	const char* original_file = "../data/test_data_4x8x16x32.raw";
	const char* compressed_file = "../data/tmp_compressed.atc";
	const char* decompressed_file = "../data/tmp_decompressed.raw";
	const uint8_t modes = 4;
	size_t sizes[4] = {4, 8, 16, 32}; // C doesn't allow a const variable to be used as length of a statically allocated array ...
	const double target_relative_error = 1e-3;
	atc_options options = ATC_OPTIONS_DEFAULT;
	options.error_metric = ATC_RELATIVE_ERROR;
	options.timings = true;
	
	// Compression
	atc_results compression_results = atc_compress_ff_results_options(original_file, ATC_DATA_TYPE_FLOAT32, modes, sizes, target_relative_error, compressed_file, &options);
	
	// Decompression
	atc_decompress_ff(compressed_file, decompressed_file);
	
	// Check results
	
	// Print compression timings to make sure that ATC moved all results correctly
	printf("Compression timings:\n");
	for (size_t i = 0; i < compression_results.timings_length; i++)
		printf("- %s: %f s\n", compression_results.timings[i].name, compression_results.timings[i].timing);
	
	// Print other results
	printf("Compressed file size: %zu\n", compression_results.compressed_size);
	printf("Compression factor: %f\n", compression_results.compression_factor);
	
	// Compute total tensor size
	size_t size = 1;
	for (size_t i = 0; i < modes; i++)
		size *= sizes[i];
	
	// Get original data
	FILE* original_file_ptr = fopen(original_file, "rb");
	float* original_data = (float*) malloc(size*sizeof(float));
	size_t elements_read = fread(original_data, sizeof(float), size, original_file_ptr);
	if (elements_read != size) {
		printf("Could not read enough elements from original data!");
		return 1;
	}
	fclose(original_file_ptr);
	
	// Get decompressed data
	FILE* decompressed_file_ptr = fopen(decompressed_file, "rb");
	float* decompressed_data = (float*) malloc(size*sizeof(float));
	elements_read = fread(decompressed_data, sizeof(float), size, decompressed_file_ptr);
	if (elements_read != size) {
		printf("Could not read enough elements from original data!");
		return 1;
	}
	fclose(decompressed_file_ptr);
	
	// Compute relative error explicitly and compare to reported relative error
	double sq_norm = 0;
	double sse = 0;
	for (size_t i = 0; i < size; i++) {
		sq_norm += pow(original_data[i], 2);
		sse += pow(decompressed_data[i] - original_data[i], 2);
	}
	double relative_error = sqrt(sse/sq_norm);
	printf("Target relative error: %f\n", target_relative_error);
	printf("Actual relative error: %f\n", relative_error);
	
	// Free resources
	free(decompressed_data);
	free(original_data);
	atc_destroy_results(&compression_results);
	
	return 0;
	
}
