import random
import subprocess
import os
from textwrap import dedent
import time
import glob
import math

def make_sig(n):
	return "int func_{0}(int arg);".format(n)

def make_func(n):
	template = dedent("""
		int func_{0}(int arg) {{
			int count = func_{1}(arg) % 1000 + 3;
			if (count < 3) count = 3;
			for (int i = 0; i < count; i++) {{
				arg = (arg ^ {2}) * 33 + arg;
			}}
			return arg;
		}}
	""").strip()
	return template.format(n, n-1, random.randint(1, 2147483647))

def make_main_single(fn_count):
	sigs  = [make_sig(i) for i in range(fn_count + 1)]
	funcs = [make_func(i + 1) for i in range(fn_count)]
	template = dedent("""
		#include <stdio.h>
		#include <stdlib.h>

		{0}

		int func_0(int arg) {{
			return arg + 1;
		}}

		{1}

		int main(void) {{
			int r = rand();
			printf("%d\\n", func_{2}(r));
		}}
	""").strip()
	return template.format("\n".join(sigs), "\n\n".join(funcs), fn_count);

def compile_single(cc, fn_count):
	with open("main.c", "w") as file:
		file.write(make_main_single(fn_count) + "\n")
	start_time = time.time()
	subprocess.run([cc, "main.c", "-o", "program"]);
	elapsed = time.time() - start_time
	print(f"Took {elapsed} seconds")
	return elapsed

def make_file_partial(n_start, n_end):
	sigs  = [make_sig(i) for i in range(n_start - 1, n_end)]
	funcs = [make_func(i) for i in range(n_start, n_end)]
	return "\n".join(sigs) + "\n\n" + "\n\n".join(funcs)

def make_main_partial(fn_count):
	template = dedent("""
		#include <stdio.h>
		#include <stdlib.h>

		int func_0(int arg);
		int func_{0}(int arg);

		int func_0(int arg) {{
			return arg + 1;
		}}

		int main(void) {{
			int r = rand();
			printf("%d\\n", func_{0}(r));
		}}
	""").strip()
	return template.format(fn_count)

def compile_multiple(cc, fn_count, n_files):
	per_file = fn_count / (n_files - 1)
	file_names = []
	for i in range(n_files - 1):
		n_start = round(i * per_file) + 1
		n_end = round((i + 1) * per_file) + 1
		name = f"file_{i+1}.c"
		with open(name, "w") as file:
			file.write(make_file_partial(n_start, n_end) + "\n")
		file_names.append(name)
	with open("main.c", "w") as file:
		file.write(make_main_partial(fn_count) + "\n")
	file_names.append("main.c")
	start_time = time.time()
	for f in file_names:
		subprocess.run([cc, '-c', f, "-o", f[:-1] + "o"]);
	o_files = [f[:-1]+"o" for f in file_names]
	subprocess.run([cc, *o_files, "-o", "program"]);
	elapsed = time.time() - start_time
	print(f"Took {elapsed} seconds")
	return elapsed

fn_lines = len(make_func(1).splitlines()) + 2

for cc in ["gcc", "clang"]:
	for target_loc in [100, 1000, 10000, 100000, 1000000, 10000000]:
		for n_files in [1, 10, 100, 1000]:
			if n_files > target_loc / 10:
				continue

			fn_count = target_loc // fn_lines
			random.seed(1234)

			runs = []

			print(f"{cc}: {target_loc} LOC, {n_files} files")
			for i in range(5 if target_loc < 1000000 else 1):
				if n_files == 1:
					runs.append(compile_single(cc, fn_count))
				else:
					runs.append(compile_multiple(cc, fn_count, n_files))

			run = min(runs)
			print(f"{cc}: {target_loc} LOC, {n_files} files: {run} seconds")
			print("")

try:
	for f in glob.glob("*.c"):
		os.remove(f)
	for f in glob.glob("*.o"):
		os.remove(f)
	os.remove("program")
except:
	pass
