Nat computeSum(Nat n, Str progressText) {
	Nat sum = 0;
	for (Nat i = 1; i <= n; i++) {
		sum += i;
		print("${progressText}: at ${i}, sum ${sum}");
		sleep(1 s);
	}
	return sum;
}

Word fibonacci(Nat n) {
	if (n < 2)
		return n;
	else
		return fibonacci(n - 1) + fibonacci(n - 2);
}

thread FibA;
thread FibB;

Word fibonacciA(Nat n) on FibA {
	return fibonacci(n);
}

Word fibonacciB(Nat n) on FibB {
	return fibonacci(n);
}

class Data {
	Nat value;

	init() {}

	init(Data other) {
		init { value = other.value; }
		print("Copied data!");
	}
}

thread MyThread;

Data modifyData(Data toModify) on MyThread {
	toModify.value = 10;
	return toModify;
}

class ActorData on MyThread {
	Nat value;

	void set(Nat newValue) {
		value = newValue;
	}

	void add(Nat toAdd) {
		value = value + toAdd;
	}
}

ActorData modifyData(ActorData toModify) on MyThread {
	toModify.value = 10;
	return toModify;
}

thread MyOtherThread;

void addData(ActorData toUpdate, Nat toAdd) on MyOtherThread {
	toUpdate.set(toUpdate.value + toAdd);
}

void addData2(ActorData toUpdate, Nat toAdd) on MyOtherThread {
	toUpdate.add(toAdd);
}

void main() {
	{
		print("-- Spawn ---");

		Future<Nat> sum3 = spawn computeSum(3, "To 3");
		Nat sum5 = computeSum(5, "To 5");
		print("Sum of 1 to 3 is: ${sum3.result}");
		print("Sum of 1 to 5 is: ${sum5}");
	}

	{
		print("-- Threads, same OS thread --");

		Moment start;
		Future<Word> value1 = spawn fibonacci(40);
		Future<Word> value2 = spawn fibonacci(41);
		print("Finished fibonacci(40)=${value1.result}");
		print("Finished fibonacci(41)=${value2.result}");
		Moment end;
		print("Total time: ${end - start}");
	}

	{
		print("-- Threads, different OS threads --");

		Moment start;
		Future<Word> value1 = spawn fibonacciA(40);
		Future<Word> value2 = spawn fibonacciB(41);
		print("Finished fibonacci(40)=${value1.result}");
		print("Finished fibonacci(41)=${value2.result}");
		Moment end;
		print("Total time: ${end - start}");
	}

	{
		print("-- Sharing data --");

		Data data;
		Future<Data> modify = spawn modifyData(data);
		data.value = 20;
		Data modified = modify.result();
		print("After modifications: ${data.value}");
		print("Returned object: ${modified.value}");
		print("Are they the same? ${data is modified}");
	}

	{
		print("-- Sharing data with actors --");

		ActorData data;
		Future<ActorData> modify = spawn modifyData(data);
		data.set(20);
		ActorData modified = modify.result();
		print("After modifications: ${data.value}");
		print("Returned object: ${modified.value}");
		print("Are they the same? ${data is modified}");
	}

	{
		print("-- Thread-safe operations, incorrect --");

		ActorData data;
		Future<void> modify = spawn addData(data, 20);
		for (Nat i = 0; i < 10; i++) {
			data.set(data.value + 10);
		}
		modify.result();
		print("Final result: ${data.value}");
	}

	{
		print("-- Thread-safe operations, correct --");

		ActorData data;
		Future<void> modify = spawn addData2(data, 20);
		for (Nat i = 0; i < 10; i++) {
			data.add(10);
		}
		modify.result();
		print("Final result: ${data.value}");
	}
}
