diff --git a/source/simple/support/algorithm/utils.hpp b/source/simple/support/algorithm/utils.hpp
index 2da935599bc402c0b613df9e3f6c847ac5c87bb9..f231a1366918c0eeadc48c34344459e050930ff8 100644
--- a/source/simple/support/algorithm/utils.hpp
+++ b/source/simple/support/algorithm/utils.hpp
@@ -20,7 +20,7 @@ namespace simple::support
 	{
 		using std::begin;
 		using std::end;
-		return make_range(begin(container), end(container));
+		return range{begin(container), end(container)};
 	}
 
 	template <typename Container>
@@ -32,7 +32,7 @@ namespace simple::support
 	{
 		using std::rbegin;
 		using std::rend;
-		return make_range(rbegin(container), rend(container));
+		return range{rbegin(container), rend(container)};
 	}
 
 	template <typename Container>
diff --git a/source/simple/support/algorithm/variance.hpp b/source/simple/support/algorithm/variance.hpp
index 60cdf3c54a6c519a41b0a5a69a2c4fa9a8c21aa2..b1e7902be87faac77a3a4175a76c2ff0ab1d14a0 100644
--- a/source/simple/support/algorithm/variance.hpp
+++ b/source/simple/support/algorithm/variance.hpp
@@ -1,6 +1,7 @@
 #ifndef SIMPLE_SUPPORT_ALGORITHM_VARIANCE_HPP
 #define SIMPLE_SUPPORT_ALGORITHM_VARIANCE_HPP
-#include "iterator"
+#include "../range.hpp"
+#include <iterator>
 
 namespace simple::support
 {
@@ -30,7 +31,7 @@ namespace simple::support
 	{
 		using std::begin;
 		using std::end;
-		return make_range( begin(range), variance(begin(range), end(range), bop) );
+		return support::range{ begin(range), variance(begin(range), end(range), bop) };
 	}
 
 	template <typename Range>
@@ -38,7 +39,7 @@ namespace simple::support
 	{
 		using std::begin;
 		using std::end;
-		return make_range( begin(range), variance(begin(range), end(range)) );
+		return support::range{ begin(range), variance(begin(range), end(range)) };
 	}
 
 } // namespace simple::support
diff --git a/source/simple/support/range.hpp b/source/simple/support/range.hpp
index 2c5c9c4e6d969f2d5e4a2541207ea5cb41296d79..b92a208ffbf2df3783f3b8ec92d30de056c0908c 100644
--- a/source/simple/support/range.hpp
+++ b/source/simple/support/range.hpp
@@ -3,8 +3,7 @@
 
 #include <algorithm>
 #include <type_traits>
-#include <istream>
-#include <ostream>
+#include <cassert>
 
 #include "array.hpp"
 #include "array_operators.hpp"
@@ -25,26 +24,25 @@ namespace simple::support
 	: public std::true_type
 	{};
 
-	template <typename Type>
+	template <typename Type, typename Bounds = array<Type,2>>
 	struct range
 	{
 
-		array<Type, 2> bounds;
+		Bounds bounds;
 
 		using iterator = std::conditional_t<is_iterable<Type>{},
 			Type, void>;
 
-		constexpr static range limit()
+		constexpr static auto limit()
 		{
 			static_assert(std::numeric_limits<Type>::is_specialized);
-			return { std::numeric_limits<Type>::lowest(), std::numeric_limits<Type>::max() };
+			return support::range{ std::numeric_limits<Type>::lowest(), std::numeric_limits<Type>::max() };
 		}
 
-
-		constexpr Type& lower() noexcept { return bounds[0]; }
-		constexpr Type& upper() noexcept { return bounds[1]; }
-		constexpr const Type& lower() const noexcept { return bounds[0]; }
-		constexpr const Type& upper() const noexcept { return bounds[1]; }
+		constexpr auto& lower() noexcept { return bounds[0]; }
+		constexpr auto& upper() noexcept { return bounds[1]; }
+		constexpr const auto& lower() const noexcept { return bounds[0]; }
+		constexpr const auto& upper() const noexcept { return bounds[1]; }
 
 		constexpr bool operator ==(const range& other) const
 		{ return bounds == other.bounds; }
@@ -67,16 +65,16 @@ namespace simple::support
 		[[nodiscard]] constexpr auto rend() const
 		{ return std::make_reverse_iterator(lower()); }
 
-		template<typename OtherType>
+		template<typename OtherType, typename OtherBounds = Bounds>
 		[[nodiscard]] constexpr
-		range sub_range(range<OtherType> other) const
+		auto sub_range(range<OtherType, OtherBounds> other) const
 		{
 			// TODO: use unsigned for other type when it makes sense, to avoid overflow
 			clamp_in_place(other, {
 				OtherType{},
 				static_cast<OtherType>(upper() - lower())
 			});
-			return
+			return support::range
 			{
 				lower() + other.lower(),
 				lower() + other.upper()
@@ -110,9 +108,15 @@ namespace simple::support
 		}
 
 		[[nodiscard]]
-		constexpr range fixed() const
+		constexpr auto fixed() const
 		{
-			return range{*this}.fix();
+			using std::min;
+			using std::max;
+			return support::range
+			{
+				min(lower(), upper()),
+				max(lower(), upper())
+			};
 		}
 
 		[[nodiscard]]
@@ -137,6 +141,7 @@ namespace simple::support
 		constexpr bool intersects_upper(const ValueType& value) const
 		{ return lower() < value && value <= upper(); }
 
+		// TODO: make these more generic, compatible Type, any Bounds
 		constexpr bool contains(const range& other) const
 		{ return lower() < other.lower() && other.upper() < upper(); }
 
@@ -159,9 +164,15 @@ namespace simple::support
 			return *this;
 		}
 
-		constexpr range intersection(range other) const
+		constexpr auto intersection(const range& other) const
 		{
-			return other.intersect(*this);
+			using std::min;
+			using std::max;
+			return support::range
+			{
+				max(lower(), other.lower()),
+				min(upper(), other.upper())
+			};
 		}
 
 		template <typename Other, std::enable_if_t<
@@ -194,50 +205,76 @@ namespace simple::support
 	template <typename T> range(T lower, T upper) -> range<T>;
 
 	// TODO: generalize some of these to anything that has lower()/upper() bound iterface
-	template <typename Type, typename ValueType = Type, std::common_type_t<ValueType, Type>* = nullptr>
-	constexpr bool contains(const range<Type>& range, const ValueType& value)
+	// and implement the members based on these instead of these based on members
+	template <typename Type, typename Bounds = array<Type,2>, typename ValueType = Type, std::common_type_t<ValueType, Type>* = nullptr>
+	constexpr bool contains(const range<Type,Bounds>& range, const ValueType& value)
 	{ return range.contains(value); }
 
-	template <typename Type, typename ValueType, std::common_type_t<ValueType, Type>* = nullptr>
-	constexpr bool intersects(const range<Type>& range, const ValueType& value)
+	template <typename Type, typename ValueType, typename Bounds = array<Type,2>, std::common_type_t<ValueType, Type>* = nullptr>
+	constexpr bool intersects(const range<Type,Bounds>& range, const ValueType& value)
 	{ return range.intersects(value); }
 
-	template <typename Type, typename ValueType, std::common_type_t<ValueType, Type>* = nullptr>
-	constexpr bool intersects_lower(const range<Type>& range, const ValueType& value)
+	template <typename Type, typename ValueType, typename Bounds = array<Type,2>, std::common_type_t<ValueType, Type>* = nullptr>
+	constexpr bool intersects_lower(const range<Type,Bounds>& range, const ValueType& value)
 	{ return range.intersects_lower(value); }
 
-	template <typename Type, typename ValueType, std::common_type_t<ValueType, Type>* = nullptr>
-	constexpr bool intersects_upper(const range<Type>& range, const ValueType& value)
+	// TODO: here to enable implicit conversion when Type is specified, but this whole business is really dodgy
+	template <typename Type, typename ValueType = Type, std::common_type_t<ValueType, Type>* = nullptr>
+	constexpr bool intersects_lower(const non_deduced<range<Type>>& range, const non_deduced<ValueType>& value)
+	{ return intersects_lower(range, value); }
+
+	template <typename Type, typename ValueType, typename Bounds = array<Type,2>, std::common_type_t<ValueType, Type>* = nullptr>
+	constexpr bool intersects_upper(const range<Type,Bounds>& range, const ValueType& value)
 	{ return range.intersects_upper(value); }
 
 	template <typename Type>
 	constexpr bool contains(const range<Type>& one, const range<Type>& other)
 	{ return one.contains(other); }
 
+	template <typename Type, typename Bounds = array<Type,2>>
+	constexpr bool contains(const range<Type,Bounds>& one, const range<Type,Bounds>& other)
+	{ return one.contains(other); }
+
 	template <typename Type>
 	constexpr bool covers(const range<Type>& one, const range<Type>& other)
 	{ return one.covers(other); }
 
+	template <typename Type, typename Bounds = array<Type,2>>
+	constexpr bool covers(const range<Type,Bounds>& one, const range<Type,Bounds>& other)
+	{ return one.covers(other); }
+
 	template <typename Type>
 	constexpr bool overlaps(const range<Type>& one, const range<Type>& other)
 	{ return one.overlaps(other); }
 
+	template <typename Type, typename Bounds = array<Type,2>>
+	constexpr bool overlaps(const range<Type,Bounds>& one, const range<Type,Bounds>& other)
+	{ return one.overlaps(other); }
+
 	template <typename Type>
 	constexpr bool intersects(const range<Type>& one, const range<Type>& other)
 	{ return one.intersects(other); }
 
+	template <typename Type, typename Bounds = array<Type,2>>
+	constexpr bool intersects(const range<Type,Bounds>& one, const range<Type,Bounds>& other)
+	{ return one.intersects(other); }
+
 	template <typename Type>
-	constexpr range<Type> intersection(const range<Type>& one, range<Type> other)
+	constexpr auto intersection(const range<Type>& one, range<Type> other)
 	{ return one.intersection(other); }
 
-	template <typename Type>
+	template <typename Type, typename Bounds = array<Type,2>>
+	constexpr auto intersection(const range<Type,Bounds>& one, range<Type,Bounds> other)
+	{ return one.intersection(other); }
+
+	template <typename Type, typename Bounds = array<Type,2>>
 	[[nodiscard]]
-	constexpr auto reverse(const range<Type>& one)
+	constexpr auto reverse(const range<Type, Bounds>& one)
 	{ return one.reverse(); }
 
-	template <typename Type>
+	template <typename Type, typename Bounds = array<Type,2>>
 	constexpr
-	range<Type>& clamp_in_place(range<Type>& v, const range<Type>& hilo)
+	range<Type>& clamp_in_place(range<Type,Bounds>& v, const range<Type,Bounds>& hilo)
 	{
 		using std::clamp;
 		for(size_t i = 0; i < v.bounds.size(); ++i)
@@ -245,45 +282,76 @@ namespace simple::support
 		return v;
 	}
 
-	template <typename Type, typename RangeValueType = Type>
+	template <typename Type, typename RangeValueType = Type, typename RangeBounds = array<RangeValueType,2>>
 	constexpr
-	Type& clamp_in_place(Type& v, const range<RangeValueType>& hilo)
+	Type& clamp_in_place(Type& v, const range<RangeValueType, RangeBounds>& hilo)
 	{
 		using std::clamp;
 		v = clamp(v, hilo.lower(), hilo.upper());
 		return v;
 	}
 
+	// TODO: here to enable implicit conversion when Type is specified, but this whole business is really dodgy
 	template <typename Type, typename RangeValueType = Type>
 	constexpr
-	Type clamp(Type v, const range<RangeValueType>& hilo)
+	Type& clamp_in_place(non_deduced<Type>& v, const non_deduced<range<RangeValueType>>& hilo)
+	{
+		return clamp_in_place(v,hilo);
+	}
+
+	template <typename Type, typename RangeValueType = Type, typename RangeBounds = array<RangeValueType, 2>>
+	constexpr
+	Type clamp(Type v, const range<RangeValueType,RangeBounds>& hilo)
 	{
 		clamp_in_place(v, hilo);
 		return v;
 	}
 
-	template <typename Type>
+	template <typename Type, typename Bounds = array<Type,2>>
 	constexpr
-	range<Type> clamp(range<Type> v, const range<Type>& hilo)
+	range<Type> clamp(range<Type,Bounds> v, const range<Type,Bounds>& hilo)
 	{
 		clamp_in_place(v, hilo);
 		return v;
 	}
 
-	template<typename T>
-	constexpr range<T> make_range(T&& lower, T&& upper)
+	class range_operators_compatibility_tag {};
+
+	template <typename Type, typename Lower>
+	struct range_upper_bound
+	{
+		Type upper;
+
+		constexpr const Type& operator[](size_t i) const
+		{
+			assert(intersects_lower(range{size_t{},size()}, i));
+			if(i == 0)
+				return Lower::value;
+			else
+				return upper;
+		};
+
+		constexpr size_t size() const { return 2; }
+
+		constexpr bool operator==(range_upper_bound other)
+		{ return upper == other.upper; }
+		constexpr bool operator!=(range_upper_bound other)
+		{ return !(*this == other); }
+	};
+
+	template<typename Lower, typename T = typename Lower::value_type>
+	constexpr range<T, range_upper_bound<T, Lower>> make_range_upper(T&& upper)
 	{
-		return range<T>{std::forward<T>(lower), std::forward<T>(upper)};
+		return {std::forward<T>(upper)};
 	}
 
-	class range_operators_compatibility_tag {};
 
 } // namespace simple::support
 
 namespace simple
 {
-	template<typename T>
-	struct support::define_array_operators<support::range<T>>
+	template<typename T, typename B>
+	struct support::define_array_operators<support::range<T,B>>
 	{
 		constexpr static auto enabled_operators = array_operator::none;
 		constexpr static auto enabled_right_element_operators =
@@ -298,9 +366,9 @@ namespace simple
 		constexpr static auto enabled_left_element_operators = array_operator::none;
 
 		constexpr static size_t size = 2;
-		constexpr static array<T,size>& get_array(support::range<T>& r) noexcept
+		constexpr static auto& get_array(support::range<T,B>& r) noexcept
 		{ return r.bounds; }
-		constexpr static const array<T,size>& get_array(const support::range<T>& r) noexcept
+		constexpr static const auto& get_array(const support::range<T,B>& r) noexcept
 		{ return r.bounds; }
 
 		template <typename R, array_operator, typename, bool>
diff --git a/unit_tests/random.cpp b/unit_tests/random.cpp
index 6f0df6427bb9d4cf8e702866aa29406d573adae5..9069bddff5490ebd52a69bdaaf29d93e5cc83947 100644
--- a/unit_tests/random.cpp
+++ b/unit_tests/random.cpp
@@ -1,16 +1,17 @@
 // TODO: need a test file per header and include the header first, to detect missing includes
 
-#include <iostream>
-#include <sstream>
-#include <cassert>
-#include <random>
-#include <unordered_map>
 #include "simple/support/random/engine/tiny.hpp"
 #include "simple/support/random/distribution/naive.hpp"
 #include "simple/support/random/distribution/diagonal.hpp"
 #include "simple/support/misc.hpp"
 #include "simple/support/algorithm.hpp"
 
+#include <iostream>
+#include <sstream>
+#include <cassert>
+#include <random>
+#include <unordered_map>
+
 using namespace simple::support;
 using namespace random;
 using namespace distribution;
@@ -58,7 +59,7 @@ entropy_result get_entropy(PRNG prng)
 
 	unsigned char* bytes = reinterpret_cast<unsigned char*>(data.data());
 	const std::size_t byte_size = data.size() * sizeof(typename decltype(data)::value_type);
-	auto byte_range = make_range(+bytes, bytes + byte_size);
+	auto byte_range = range{+bytes, bytes + byte_size};
 
 	result.base = bit_entropy(data);
 	result.byte_base = bit_entropy(byte_range);
diff --git a/unit_tests/range.cpp b/unit_tests/range.cpp
index 31f6ccdbc08a94a75bb6cbe0950ef5442ee4b339..fa1da4fac1b4f46f200f012a8eb911be3225b5e3 100644
--- a/unit_tests/range.cpp
+++ b/unit_tests/range.cpp
@@ -1,7 +1,8 @@
+#include "simple/support/range.hpp"
+
 #include <cassert>
 #include <random>
 #include <iostream>
-#include "simple/support/range.hpp"
 
 using namespace simple::support;
 
@@ -38,8 +39,8 @@ void SetLikePredicates()
 	range r{-13,31};
 
 	assert(contains(r, 2));
-	assert(!contains(r, 45));
 	assert(!contains(r, r.lower()));
+	assert(!contains(r, 45));
 	assert(!contains(r, r.upper()));
 
 	assert(intersects(r, 12));
@@ -304,6 +305,40 @@ constexpr bool Constexprness()
 	return true;
 }
 
+void UpperRange()
+{
+	auto valid_upper_range = make_range_upper<std::integral_constant<int,-3>>(12);
+	assert(valid_upper_range.valid());
+	assert(( valid_upper_range + 3 == range{0,15} ));
+
+	struct { int x; } one_int;
+	static_assert(sizeof(valid_upper_range) == sizeof(one_int));
+
+	auto invalid_upper_range = make_range_upper<std::integral_constant<int,13>>(12);
+	assert(!invalid_upper_range.valid());
+	assert(( invalid_upper_range.fixed() == range{12,13} ));
+
+	// TODO: rest of const stuff should work
+
+}
+
+struct rango
+{
+	operator range<int> () { return range{1,2}; }
+};
+
+void ImplicitConversionSupport() // TODO: not sure about this...
+{
+	int a = 13;
+	clamp_in_place<int>(a, rango{});
+	assert(2 == a);
+	assert(not intersects_lower<int>(rango{},a));
+
+	int b = -1;
+	clamp_in_place<int>(b, rango{});
+	assert(intersects_lower<int>(rango{},b));
+}
+
 int main()
 {
 	Validity();
@@ -312,7 +347,8 @@ int main()
 	SubRange();
 	Limit();
 	Arithmetic();
-	Iterator();
 	static_assert(Constexprness());
+	UpperRange();
+	ImplicitConversionSupport();
 	return 0;
 }