<commit_before>#pragma once
// blockBinaryNumber.hpp: parameterized blocked binary number
//
// Copyright (C) 2017-2020 Stillwater Supercomputing, Inc.
//
// This file is part of the universal numbers project, which is released under an MIT Open Source license.
#include <iostream>
#include <string>
#include <sstream>

// compiler specific operators
#if defined(__clang__)
/* Clang/LLVM. ---------------------------------------------- */


#elif defined(__ICC) || defined(__INTEL_COMPILER)
/* Intel ICC/ICPC. ------------------------------------------ */


#elif defined(__GNUC__) || defined(__GNUG__)
/* GNU GCC/G++. --------------------------------------------- */


#elif defined(__HP_cc) || defined(__HP_aCC)
/* Hewlett-Packard C/aC++. ---------------------------------- */

#elif defined(__IBMC__) || defined(__IBMCPP__)
/* IBM XL C/C++. -------------------------------------------- */

#elif defined(_MSC_VER)
/* Microsoft Visual Studio. --------------------------------- */


#elif defined(__PGI)
/* Portland Group PGCC/PGCPP. ------------------------------- */

#elif defined(__SUNPRO_C) || defined(__SUNPRO_CC)
/* Oracle Solaris Studio. ----------------------------------- */

#endif

namespace sw {
namespace unum {


	// forward references
	template<size_t nbits, typename StorageBlockType> class blockBinaryNumber;
	template<size_t nbits, typename StorageBlockType> blockBinaryNumber<nbits, StorageBlockType> twosComplement(const blockBinaryNumber<nbits, StorageBlockType>&);

	// generate the 2's complement of the block binary number
	template<size_t nbits, typename StorageBlockType>
	blockBinaryNumber<nbits, StorageBlockType> twosComplement(const blockBinaryNumber<nbits, StorageBlockType>& orig) {
		blockBinaryNumber<nbits, StorageBlockType> twosC;
		blockBinaryNumber<nbits, StorageBlockType> plusOne;
		plusOne = 1;
		twosC += plusOne;
		return twosC;
	}

/*
NOTES

for block arithmetic, we need to manage a carry bit.
This disqualifies using uint64_t as a block type as we can't catch the overflow condition
in the same way as the other native types, uint8_t, uint16_t, uint32_t.

We could use a sint64_t and then convert to uint64_t and observe the MSB. Very different 
logic though.
*/

// a block-based 2's complement binary number
template<size_t nbits, typename StorageBlockType = uint8_t>
class blockBinaryNumber {
public:
	static constexpr size_t bitsInByte = 8;
	static constexpr size_t bitsInBlock = sizeof(StorageBlockType) * bitsInByte;
	static_assert(bitsInBlock <= 32, "storage unit for block arithmetic needs to be <= uint32_t");

	static constexpr size_t nrUnits = 1 + ((nbits - 1) / bitsInBlock);
	static constexpr uint64_t storageMask = (0xFFFFFFFFFFFFFFFF >> (64 - bitsInBlock));
	static constexpr StorageBlockType maxBlockValue = (StorageBlockType(1) << bitsInBlock) - 1;

	static constexpr size_t MSU = nrUnits - 1; // Most Significant Unit
	static constexpr size_t MSU_MASK = (StorageBlockType(0xFFFFFFFFFFFFFFFFul) >> (nrUnits * bitsInBlock - nbits));

	// constructors
	blockBinaryNumber() { setzero(); }

	blockBinaryNumber(const blockBinaryNumber&) = default;
	blockBinaryNumber(blockBinaryNumber&&) = default;

	blockBinaryNumber& operator=(const blockBinaryNumber&) = default;
	blockBinaryNumber& operator=(blockBinaryNumber&&) = default;

	// initializer for long long
	blockBinaryNumber(const long long initial_value) { *this = initial_value; }

	blockBinaryNumber& operator=(long long rhs) {
		for (unsigned i = 0; i < nrUnits; ++i) {
			block[i] = rhs & storageMask;
			rhs >>= bitsInBlock;
		}
		// enforce precondition for fast comparison by properly nulling bits that are outside of nbits
		block[MSU] &= MSU_MASK;
		return *this;
	}

	// arithmetic operators
	blockBinaryNumber& operator+=(const blockBinaryNumber& rhs) {
		bool carry = false;
		for (unsigned i = 0; i < nrUnits; ++i) {
			// cast up so we can test for overflow
			uint64_t l = uint64_t(block[i]);
			uint64_t r = uint64_t(rhs.block[i]);
			uint64_t s = l + r + (carry ? uint64_t(1) : uint64_t(0));
			carry = (s > maxBlockValue ? true : false);
			block[i] = StorageBlockType(s);
		}
		// enforce precondition for fast comparison by properly nulling bits that are outside of nbits
		block[MSU] &= MSU_MASK;
		return *this;
	}
	blockBinaryNumber& operator-=(const blockBinaryNumber& rhs) {
		return *this;
	}
	blockBinaryNumber& operator*=(const blockBinaryNumber& rhs) {
		return *this;
	}
	blockBinaryNumber& operator/=(const blockBinaryNumber& rhs) {
		return *this;
	}
	blockBinaryNumber& operator%=(const blockBinaryNumber& rhs) {
		return *this;
	}
	// shift left operator
	blockBinaryNumber& operator<<=(long bitsToShift) {
		signed blockShift = 0;
		if (bitsToShift >= bitsInBlock) {
			blockShift = bitsToShift / bitsInBlock;
			for (signed i = signed(MSU); i >= blockShift; --i) {
				block[i] = block[i - blockShift];
			}
			std::cout << "after block shift: " << to_binary(*this, true) << std::endl;

			for (signed i = blockShift - 1; i >= 0; --i) {
				block[i] = StorageBlockType(0);
			}
			std::cout << "after null: " << to_binary(*this, true) << std::endl;

						// adjust the shift
			bitsToShift -= (long long)(blockShift * bitsInBlock);
			if (bitsToShift == 0) return *this;
		}
		// construct the mask for the upper bits in the block that need to move to the higher word
		StorageBlockType mask = 0xFFFFFFFFFFFFFFFF << (bitsInBlock - bitsToShift);
		for (unsigned i = MSU; i > 0; --i) {
			block[i] <<= bitsToShift;
			// mix in the bits from the right
			StorageBlockType bits = (mask & block[i - 1]);
			block[i] |= (bits >> (bitsInBlock - bitsToShift));
		}
		block[0] <<= bitsToShift;
		return *this;
	}
	// shift right operator
	blockBinaryNumber& operator>>=(long long bitsToShift) {
		size_t blockShift = 0;
		if (bitsToShift >= bitsInBlock) {
			blockShift = bitsToShift / bitsInBlock;
			for (size_t i = 0; i <= MSU - blockShift; ++i) {
				block[i] = block[i + blockShift];
			}		
//			std::cout << "after block shift: " << to_binary(*this, true) << std::endl;

			for (size_t i = MSU - blockShift + 1; i <= MSU; ++i) {
				block[i] = StorageBlockType(0);
			}
//			std::cout << "after null: " << to_binary(*this, true) << std::endl;

			// adjust the shift
			bitsToShift -= (long long)(blockShift * bitsInBlock);
			if (bitsToShift == 0) return *this;
		}
		StorageBlockType mask = 0xFFFFFFFFFFFFFFFF >> (64 - bitsInBlock);
		mask >>= (bitsInBlock - bitsToShift); // this is a mask for the lower bits in the block that need to move to the lower word
		for (unsigned i = 0; i < MSU; ++i) {
			block[i] >>= bitsToShift;
			// mix in the bits from the left
			StorageBlockType bits = (mask & block[i + 1]);
			block[i] |= (bits << (bitsInBlock - bitsToShift));<commit_msg>adding negative shifts to left and right shift operators<commit_after>#pragma once
// blockBinaryNumber.hpp: parameterized blocked binary number
//
// Copyright (C) 2017-2020 Stillwater Supercomputing, Inc.
//
// This file is part of the universal numbers project, which is released under an MIT Open Source license.
#include <iostream>
#include <string>
#include <sstream>

// compiler specific operators
#if defined(__clang__)
/* Clang/LLVM. ---------------------------------------------- */


#elif defined(__ICC) || defined(__INTEL_COMPILER)
/* Intel ICC/ICPC. ------------------------------------------ */


#elif defined(__GNUC__) || defined(__GNUG__)
/* GNU GCC/G++. --------------------------------------------- */


#elif defined(__HP_cc) || defined(__HP_aCC)
/* Hewlett-Packard C/aC++. ---------------------------------- */

#elif defined(__IBMC__) || defined(__IBMCPP__)
/* IBM XL C/C++. -------------------------------------------- */

#elif defined(_MSC_VER)
/* Microsoft Visual Studio. --------------------------------- */


#elif defined(__PGI)
/* Portland Group PGCC/PGCPP. ------------------------------- */

#elif defined(__SUNPRO_C) || defined(__SUNPRO_CC)
/* Oracle Solaris Studio. ----------------------------------- */

#endif

namespace sw {
namespace unum {


	// forward references
	template<size_t nbits, typename StorageBlockType> class blockBinaryNumber;
	template<size_t nbits, typename StorageBlockType> blockBinaryNumber<nbits, StorageBlockType> twosComplement(const blockBinaryNumber<nbits, StorageBlockType>&);

	// generate the 2's complement of the block binary number
	template<size_t nbits, typename StorageBlockType>
	blockBinaryNumber<nbits, StorageBlockType> twosComplement(const blockBinaryNumber<nbits, StorageBlockType>& orig) {
		blockBinaryNumber<nbits, StorageBlockType> twosC;
		blockBinaryNumber<nbits, StorageBlockType> plusOne;
		plusOne = 1;
		twosC += plusOne;
		return twosC;
	}

/*
NOTES

for block arithmetic, we need to manage a carry bit.
This disqualifies using uint64_t as a block type as we can't catch the overflow condition
in the same way as the other native types, uint8_t, uint16_t, uint32_t.

We could use a sint64_t and then convert to uint64_t and observe the MSB. Very different 
logic though.
*/

// a block-based 2's complement binary number
template<size_t nbits, typename StorageBlockType = uint8_t>
class blockBinaryNumber {
public:
	static constexpr size_t bitsInByte = 8;
	static constexpr size_t bitsInBlock = sizeof(StorageBlockType) * bitsInByte;
	static_assert(bitsInBlock <= 32, "storage unit for block arithmetic needs to be <= uint32_t");

	static constexpr size_t nrUnits = 1 + ((nbits - 1) / bitsInBlock);
	static constexpr uint64_t storageMask = (0xFFFFFFFFFFFFFFFF >> (64 - bitsInBlock));
	static constexpr StorageBlockType maxBlockValue = (uint64_t(1) << bitsInBlock) - 1;

	static constexpr size_t MSU = nrUnits - 1; // Most Significant Unit
	static constexpr size_t MSU_MASK = (StorageBlockType(0xFFFFFFFFFFFFFFFFul) >> (nrUnits * bitsInBlock - nbits));

	// constructors
	blockBinaryNumber() { setzero(); }

	blockBinaryNumber(const blockBinaryNumber&) = default;
	blockBinaryNumber(blockBinaryNumber&&) = default;

	blockBinaryNumber& operator=(const blockBinaryNumber&) = default;
	blockBinaryNumber& operator=(blockBinaryNumber&&) = default;

	// initializer for long long
	blockBinaryNumber(const long long initial_value) { *this = initial_value; }

	blockBinaryNumber& operator=(long long rhs) {
		for (unsigned i = 0; i < nrUnits; ++i) {
			block[i] = rhs & storageMask;
			rhs >>= bitsInBlock;
		}
		// enforce precondition for fast comparison by properly nulling bits that are outside of nbits
		block[MSU] &= MSU_MASK;
		return *this;
	}

	// arithmetic operators
	blockBinaryNumber& operator+=(const blockBinaryNumber& rhs) {
		bool carry = false;
		for (unsigned i = 0; i < nrUnits; ++i) {
			// cast up so we can test for overflow
			uint64_t l = uint64_t(block[i]);
			uint64_t r = uint64_t(rhs.block[i]);
			uint64_t s = l + r + (carry ? uint64_t(1) : uint64_t(0));
			carry = (s > maxBlockValue ? true : false);
			block[i] = StorageBlockType(s);
		}
		// enforce precondition for fast comparison by properly nulling bits that are outside of nbits
		block[MSU] &= MSU_MASK;
		return *this;
	}
	blockBinaryNumber& operator-=(const blockBinaryNumber& rhs) {
		return *this;
	}
	blockBinaryNumber& operator*=(const blockBinaryNumber& rhs) {
		return *this;
	}
	blockBinaryNumber& operator/=(const blockBinaryNumber& rhs) {
		return *this;
	}
	blockBinaryNumber& operator%=(const blockBinaryNumber& rhs) {
		return *this;
	}
	// shift left operator
	blockBinaryNumber& operator<<=(long bitsToShift) {
		if (bitsToShift < 0) return operator>>=(-bitsToShift);
		signed blockShift = 0;
		if (bitsToShift >= bitsInBlock) {
			blockShift = bitsToShift / bitsInBlock;
			for (signed i = signed(MSU); i >= blockShift; --i) {
				block[i] = block[i - blockShift];
			}
			std::cout << "after block shift: " << to_binary(*this, true) << std::endl;

			for (signed i = blockShift - 1; i >= 0; --i) {
				block[i] = StorageBlockType(0);
			}
			std::cout << "after null: " << to_binary(*this, true) << std::endl;

						// adjust the shift
			bitsToShift -= (long)(blockShift * bitsInBlock);
			if (bitsToShift == 0) return *this;
		}
		// construct the mask for the upper bits in the block that need to move to the higher word
		StorageBlockType mask = 0xFFFFFFFFFFFFFFFF << (bitsInBlock - bitsToShift);
		for (unsigned i = MSU; i > 0; --i) {
			block[i] <<= bitsToShift;
			// mix in the bits from the right
			StorageBlockType bits = (mask & block[i - 1]);
			block[i] |= (bits >> (bitsInBlock - bitsToShift));
		}
		block[0] <<= bitsToShift;
		return *this;
	}
	// shift right operator
	blockBinaryNumber& operator>>=(long bitsToShift) {
		if (bitsToShift < 0) return operator<<=(-bitsToShift);
		size_t blockShift = 0;
		if (bitsToShift >= bitsInBlock) {
			blockShift = bitsToShift / bitsInBlock;
			for (size_t i = 0; i <= MSU - blockShift; ++i) {
				block[i] = block[i + blockShift];
			}		
//			std::cout << "after block shift: " << to_binary(*this, true) << std::endl;

			for (size_t i = MSU - blockShift + 1; i <= MSU; ++i) {
				block[i] = StorageBlockType(0);
			}
//			std::cout << "after null: " << to_binary(*this, true) << std::endl;

			// adjust the shift
			bitsToShift -= (long)(blockShift * bitsInBlock);
			if (bitsToShift == 0) return *this;
		}
		StorageBlockType mask = 0xFFFFFFFFFFFFFFFF >> (64 - bitsInBlock);
		mask >>= (bitsInBlock - bitsToShift); // this is a mask for the lower bits in the block that need to move to the lower word
		for (unsigned i = 0; i < MSU; ++i) {
			block[i] >>= bitsToShift;
			// mix in the bits from the left
			StorageBlockType bits = (mask & block[i + 1]);
			block[i] |= (bits << (bitsInBlock - bitsToShift));
{'fraction_non_alphanumeric': 0.16330734147417977, 'fraction_numerical': 0.011475651022509931, 'mean_word_length': 3.30766793409379, 'pattern_counts': {'":': 0, '<': 119, '<?xml version=': 0, '>': 75, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 8, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5316878', 'n_tokens_mistral': 4626, 'n_tokens_neox': 4178, 'n_words': 1286}