5 Commits 3864208ad1 ... 77f7e97b95

Auteur SHA1 Bericht Datum
  namark 77f7e97b95 DCT unit test. 6 maanden geleden
  namark 4060a807cc This is also used to format random test seed regardless of debug header. 6 maanden geleden
  namark 61133b530b Now I have generic copy, 6 maanden geleden
  namark b8a5dc0c99 Fixed out_bits assignment operator not destroying the instance assigned to. 6 maanden geleden
  namark 0c6d5d56dd all this for measly std::byte -_- 6 maanden geleden
5 gewijzigde bestanden met toevoegingen van 157 en 14 verwijderingen
  1. 20 2
      source/simple/compress/bits.hpp
  2. 6 6
      source/simple/compress/iterator.hpp
  3. 3 3
      source/simple/compress/lz77.hpp
  4. 128 0
      unit_tests/dct.cpp
  5. 0 3
      unit_tests/fft.cpp

+ 20 - 2
source/simple/compress/bits.hpp

@@ -1,7 +1,7 @@
 #ifndef SIMPLE_COMPRESS_BITS_HPP
 #define SIMPLE_COMPRESS_BITS_HPP
 
-#include <type_traits> // std::enable_if_t std::is_integral_v std::is_same_v std::make_unsigned_t std::underlying_type_t std::is_unsigned_v
+#include <type_traits> // std::enable_if_t std::is_integral_v std::is_same_v std::make_unsigned_t std::underlying_type_t std::is_unsigned_v std::is_enum_v std::is_enum_v
 #include <cstddef> // std::size_t
 #include <limits> // std::numeric_limits
 #include <iterator> // std::iterator_traits
@@ -24,6 +24,11 @@ namespace simple::compress
 	}
 
 	template <typename T,
+		std::enable_if_t<std::is_enum_v<T>>* = nullptr>
+	constexpr std::size_t bit_count(const T& x) noexcept
+	{ return bit_count(static_cast<std::underlying_type_t<T>>(x)); }
+
+	template <typename T,
 		std::enable_if_t<std::is_integral_v<T>>* = nullptr>
 	constexpr std::size_t bit_offset(const T&) noexcept
 	{
@@ -34,6 +39,11 @@ namespace simple::compress
 	}
 
 	template <typename T,
+		std::enable_if_t<std::is_enum_v<T>>* = nullptr>
+	constexpr std::size_t bit_offset(const T& x) noexcept
+	{ return bit_offset(static_cast<std::underlying_type_t<T>>(x)); }
+
+	template <typename T,
 		std::enable_if_t<std::is_integral_v<T>>* = nullptr>
 	constexpr auto get_bits(const T& t) noexcept
 	{
@@ -43,6 +53,11 @@ namespace simple::compress
 			return static_cast<std::make_unsigned_t<T>>(t);
 	}
 
+	template <typename T,
+		std::enable_if_t<std::is_enum_v<T>>* = nullptr>
+	constexpr auto get_bits(const T& t) noexcept
+	{ return get_bits(static_cast<std::underlying_type_t<T>>(t)); }
+
 	template <typename It>
 	struct bit_iterator
 	{
@@ -93,7 +108,10 @@ namespace simple::compress
 			static_assert(type_width < read_width, "not implemented");
 		}
 
-		to = bits;
+		if constexpr (std::is_enum_v<T>)
+			to = static_cast<T>(bits);
+		else
+			to = bits;
 		return it;
 	}
 

+ 6 - 6
source/simple/compress/iterator.hpp

@@ -41,14 +41,14 @@ namespace simple::compress
 		out_bits& operator=(out_bits&) = delete;
 		out_bits& operator=(out_bits&& other)
 		{
-			out = other.out;
-			value = other.value;
-			bit_index = other.bit_index;
-			other.bit_index = 0;
+			std::swap(out, other.out);
+			std::swap(value, other.value);
+			std::swap(bit_index, other.bit_index);
+			return *this;
 		}
 
 		out_bits& operator++() { return *this; }
-		out_bits operator++(int) { out_bits prev = *this; ++(*this); return prev; }
+		// move only can't have postincrement
 
 		class proxy
 		{
@@ -58,7 +58,7 @@ namespace simple::compress
 
 			proxy(It& out, Value& value, std::size_t& bit_index)
 				: out(out), value(value), bit_index(bit_index) {}
-			friend class out_bits;
+			friend struct out_bits;
 			public:
 			proxy() = delete;
 			proxy(const proxy&) = delete;

+ 3 - 3
source/simple/compress/lz77.hpp

@@ -13,6 +13,7 @@
 #include "simple/support/algorithm/utils.hpp" // support::distance
 #include "simple/support/algorithm/search.hpp" // support::search_prefix
 #include "simple/support/algorithm/mismatch.hpp" // support::mismatch
+#include "simple/support/algorithm/copy.hpp" // support::copy_n
 #include "simple/support/range.hpp" // support::range
 #include "simple/support/type_traits.hpp" // support::is_template_instance_v
 
@@ -214,9 +215,8 @@ namespace simple::compress
 
 				auto decoded = o - out_begin;
 				auto dict = decoded > ref_offset_max ? o - ref_offset_max : out_begin;
-				dict += position;
-				// NOTE: nothing from std seems to fit here, it's a copy but the end of the input can overlap output, when we are expanding a repeating pattern, closest is std::copy_n, but it does not guarantee order...
-				while(size --> 0) *o++ = *dict++;
+				assert(size <= (out_end - o));
+				o = support::copy_n(dict+position, size, o).second;
 			}
 			else
 			{

+ 128 - 0
unit_tests/dct.cpp

@@ -0,0 +1,128 @@
+#include "simple/compress/fft.hpp"
+#include "simple/support/random.hpp"
+
+#include <cassert>
+#include <vector>
+#include <cmath>
+#include <algorithm>
+#include <numeric>
+#include <complex>
+#include <random>
+#include <iostream>
+#include <iomanip>
+
+using namespace simple::compress;
+
+const float tau = 2*std::acos(-1);
+
+// DCT more widely used than DFT, as it has several practical advantages
+// the end result is the same size as input, instead of double the size
+// cosine being even function behaves better at endpoints of the window
+template <typename Waveform>
+void DCT_1(std::size_t size, Waveform waveform)
+{
+
+	std::vector<float> wave;
+	wave.resize(size);
+
+	std::iota(wave.begin(), wave.end(), 0);
+	std::transform(wave.begin(), wave.end(), wave.begin(), [&](auto x) { return x/size; });
+	std::transform(wave.begin(), wave.end(), wave.begin(), waveform);
+
+	const auto dft_size = 2*(size-1);
+
+	wave.resize(dft_size);
+	// symmetric extension of the waveform results in canceling of sine/imaginary components
+	std::copy(wave.rbegin()+size-1, wave.rend()-1, wave.begin() + size);
+
+	std::vector<std::complex<float>> frequencies;
+	frequencies.resize(dft_size);
+
+	fft(std::multiplies<>{}, std::polar(1.f,tau/wave.size()), wave, frequencies);
+	for(auto&& x : frequencies)
+		x = x / float(dft_size);
+
+	for(std::size_t i = 0; i != size; ++i)
+		assert(std::abs(frequencies[i].imag()) < 0.001);
+
+	for(auto&& x : frequencies)
+		x.imag(0);
+
+	// no idea how or why symmetric extension of the frequencies works to do the inverse... magic...
+	std::copy(frequencies.rbegin()+size-1, frequencies.rend()-1, frequencies.begin() + size);
+
+	std::vector<std::complex<float>> aaanback;
+	aaanback.resize(dft_size);
+	fft(std::multiplies<>{}, std::polar(1.f,tau/wave.size()), frequencies, aaanback);
+
+	for(std::size_t i = 0; i != size; ++i)
+		assert(std::abs(aaanback[i].real() - wave[i]) < 0.001);
+
+	wave.resize(size);
+	frequencies.resize(size);
+	aaanback.resize(size);
+
+#if defined SIMPLE_SUPPORT_DEBUG_HPP
+	if(size <= 33)
+	{
+		std::cerr << std::fixed;
+		std::cerr << std::setprecision(3);
+		simple::support::print('\n');
+
+		simple::support::print("IN : [ ");
+		for(auto&& x : wave)
+			simple::support::print(x, " ");
+		simple::support::print("]\n");
+
+		simple::support::print("DEC: [ ");
+		for(auto&& x : aaanback)
+			simple::support::print(x.real(), " ");
+		simple::support::print("]\n");
+
+		simple::support::print("ENC: [ ");
+		for(auto&& x : frequencies)
+			simple::support::print(x.real(), " ");
+		simple::support::print("]\n");
+	}
+#endif
+}
+
+int main()
+{
+
+	DCT_1(5, [&](auto x) { return std::sin(tau*x) + std::cos(tau*x*2); });
+	DCT_1(5, [&](auto x) { return std::sin(tau*x) + std::cos(tau*x); });
+	DCT_1(5, [&](auto x) { return std::sin(tau*x); });
+	DCT_1(5, [&](auto x) { return std::cos(tau*x); });
+	DCT_1(9, [&](auto x) { return std::sin(tau*x) + std::cos(tau*x*2); });
+	DCT_1(9, [&](auto x) { return std::sin(tau*x) + std::cos(tau*x); });
+	DCT_1(9, [&](auto x) { return std::sin(tau*x); });
+	DCT_1(9, [&](auto x) { return std::cos(tau*x); });
+	DCT_1(17, [&](auto x) { return std::sin(tau*x) + std::cos(tau*x*2); });
+	DCT_1(17, [&](auto x) { return std::sin(tau*x) + std::cos(tau*x); });
+	DCT_1(17, [&](auto x) { return std::sin(tau*x); });
+	DCT_1(17, [&](auto x) { return std::cos(tau*x); });
+	DCT_1(33, [&](auto x) { return std::sin(tau*x); });
+	DCT_1(33, [&](auto x) { return std::cos(tau*x); });
+	DCT_1(33, [&](auto x) { return std::sin(tau*x) + std::cos(tau*x*2); });
+	DCT_1(1025, [&](auto x) { return std::sin(tau*x) + std::cos(tau*x*2); });
+	DCT_1(1025, [&](auto x) { return std::sin(tau*x) + std::cos(tau*x); });
+	DCT_1(1025, [&](auto x) { return std::sin(tau*x); });
+	DCT_1(1025, [&](auto x) { return std::cos(tau*x); });
+	DCT_1(2049, [&](auto x) { return std::sin(tau*x) + std::cos(tau*x*2); });
+	DCT_1(2049, [&](auto x) { return std::sin(tau*x) + std::cos(tau*x); });
+	DCT_1(2049, [&](auto x) { return std::sin(tau*x); });
+	DCT_1(2049, [&](auto x) { return std::cos(tau*x); });
+
+	DCT_1(2049, [&](auto x) { return std::sin(tau*13*x) + std::cos(tau*345*x); });
+	DCT_1(2049, [&](auto x) { return std::sin(tau*134*x) + std::sin(tau*345*x) + std::cos(tau*789*x); });
+
+	auto seed = std::random_device{}();
+	std::cout << "DCT_1 random test seed: " << std::hex << std::showbase << seed << std::dec << std::endl;
+	simple::support::random::engine::tiny<unsigned long long> random{seed};
+	std::uniform_real_distribution<double> number{};
+
+	DCT_1(2049, [&](auto) { return number(random); });
+
+	return 0;
+}

+ 0 - 3
unit_tests/fft.cpp

@@ -9,10 +9,7 @@
 #include <complex>
 #include <random>
 #include <iostream>
-
-#if defined SIMPLE_SUPPORT_DEBUG_HPP
 #include <iomanip>
-#endif
 
 using namespace simple::compress;