#include "stdafx.h"
#include "Checked.h"
#include "Utils/Bitwise.h"

namespace storm {

	CheckedNat::CheckedNat() : val(0), ok(true) {}

	CheckedNat::CheckedNat(Nat from) : val(from), ok(true) {}

	CheckedNat::CheckedNat(Byte from) : val(from), ok(true) {}

	CheckedNat::CheckedNat(Int from) : val(from), ok(from >= 0) {}

	CheckedNat::CheckedNat(Word from) : val(Nat(from)), ok(from <= Nat(-1)) {}

	CheckedNat::CheckedNat(Long from) : val(Nat(from)), ok((from >= 0) && (from <= Nat(-1))) {}

	Maybe<Nat> CheckedNat::nat() const {
		if (ok)
			return Maybe<Nat>(val);
		else
			return Maybe<Nat>();
	}

	Bool CheckedNat::error() const {
		return !ok;
	}

	void CheckedNat::toS(StrBuf *to) const {
		if (ok)
			*to << val;
		else
			*to << S("<error>");
	}

	CheckedNat CheckedNat::operator +(CheckedNat other) const {
		if (error() || other.error())
			return CheckedNat(0, false);

		Nat out = val + other.val;
		Bool ok = (out >= val) && (out >= other.val);
		return CheckedNat(out, ok);
	}

	CheckedNat CheckedNat::operator -(CheckedNat other) const {
		if (error() || other.error())
			return CheckedNat(0, false);

		if (other.val > val)
			return CheckedNat(0, false);

		return CheckedNat(val - other.val);
	}

	CheckedNat CheckedNat::operator *(CheckedNat other) const {
		if (error() || other.error())
			return CheckedNat(0, false);

		// Note: Overflow is checked inside the constructor!
		return CheckedNat(Word(val) * Word(other.val));
	}

	CheckedNat CheckedNat::operator /(CheckedNat other) const {
		if (error() || other.error())
			return CheckedNat(0, false);

		return CheckedNat(val / other.val);
	}

	CheckedNat CheckedNat::operator <<(CheckedNat other) const {
		if (error() || other.error())
			return CheckedNat(0, false);

		Nat bits = sizeof(Nat) * CHAR_BIT;
		if (other.val == bits)
			return CheckedNat(0, val == 0);
		if (other.val > bits)
			return CheckedNat(0, false);

		Nat mask = Nat(-1) << (bits - other.val);
		return CheckedNat(val << other.val, (val & mask) == 0);
	}

	CheckedNat CheckedNat::operator >>(CheckedNat other) const {
		if (error() || other.error())
			return CheckedNat(0, false);

		return CheckedNat(val >> other.val, false);
	}


	CheckedWord::CheckedWord() : val(0), ok(true) {}

	CheckedWord::CheckedWord(Nat from) : val(from), ok(true) {}

	CheckedWord::CheckedWord(Byte from) : val(from), ok(true) {}

	CheckedWord::CheckedWord(Int from) : val(from), ok(from >= 0) {}

	CheckedWord::CheckedWord(Word from) : val(from), ok(true) {}

	CheckedWord::CheckedWord(Long from) : val(from), ok(from >= 0) {}

	CheckedWord::CheckedWord(CheckedNat from) : val(0), ok(!from.error()) {
		if (ok) {
			val = from.nat().value();
		}
	}

	Maybe<Word> CheckedWord::word() const {
		if (ok)
			return Maybe<Word>(val);
		else
			return Maybe<Word>();
	}

	Bool CheckedWord::error() const {
		return !ok;
	}

	void CheckedWord::toS(StrBuf *to) const {
		if (ok)
			*to << val;
		else
			*to << S("<error>");
	}

	CheckedWord CheckedWord::operator +(CheckedWord other) const {
		if (error() || other.error())
			return CheckedWord(0, false);

		Word out = val + other.val;
		Bool ok = (out >= val) && (out >= other.val);
		return CheckedWord(out, ok);
	}

	CheckedWord CheckedWord::operator -(CheckedWord other) const {
		if (error() || other.error())
			return CheckedWord(0, false);

		if (other.val > val)
			return CheckedWord(0, false);

		return CheckedWord(val - other.val);
	}

	CheckedWord CheckedWord::operator *(CheckedWord other) const {
		if (error() || other.error())
			return CheckedWord(0, false);

#if defined(X86) // The only 32-bit architecture we support!

		CheckedNat highA(val >> 32);
		CheckedNat lowA(val & 0xFFFFFFFF);
		CheckedNat highB(other.val >> 32);
		CheckedNat lowB(other.val & 0xFFFFFFFF);

		// We can create the output entirely using ourself and the multiplication in 'CheckedNat'!

		// First, do the high * high. If they are >= 1 we overflow.
		CheckedWord out = CheckedWord(highA * highB) << 64;
		// Then, add the two high * low.
		out = out + CheckedWord((highA * lowB) + (lowA * highB)) << 32;
		// Finally, the lowest bits. Note these *may* overflow, but we need to check overflow
		// for the final addition.
		out = out + (Word(lowA.nat().value()) * Word(lowB.nat().value()));

		return out;

#elif defined(X64) || defined(ARM64)

		Word out = 0;
		Bool ok = multiplyOverflow(val, other.val, out);

		return CheckedWord(out, ok);

#else
#error "Don't know if we are running on 32- or 64-bit!"
#endif
	}

	CheckedWord CheckedWord::operator /(CheckedWord other) const {
		if (error() || other.error())
			return CheckedWord(0, false);

		return CheckedWord(val / other.val);
	}

	CheckedWord CheckedWord::operator <<(CheckedWord other) const {
		if (error() || other.error())
			return CheckedWord(0, false);

		Nat bits = sizeof(Word) * CHAR_BIT;
		if (other.val == bits)
			return CheckedWord(0, val == 0);
		if (other.val > bits)
			return CheckedWord(0, false);

		Word mask = Word(-1) << (bits - other.val);
		return CheckedWord(val << other.val, (val & mask) == 0);
	}

	CheckedWord CheckedWord::operator >>(CheckedWord other) const {
		if (error() || other.error())
			return CheckedWord(0, false);

		return CheckedWord(val >> other.val, false);
	}

}
