1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
//! This module defines traits that can be implemented by all vector representations
//! operations then only need to be defined between each vector representation and this trait(s)
//! instead of between all combinations of vector reperesentation
//! i.e. only N implementations instead of N*N
//! with GAT i could bring that down to 1

/// implement this on types that can be indexed into that have a size known at compile time
///
/// mutability: just implement this twice, with &E &mut E as T
///
/// unsafety: calling .i(x) with x < N must successfully return T
///
/// unsafety: .i(x) and .i(y) return different objects when x != y.
/// i.e. they do not alias.
pub unsafe trait ConstIndex<T, const N: usize> {
	fn i(self, index: usize) -> T;
}

use crate::Vector;
unsafe impl<'a, T, const N: usize> ConstIndex<&'a T, N> for &'a Vector<T, N> {
	fn i(self, index: usize) -> &'a T { &self.inner[index] }
}

// lets just hope for the optimizer. only added this for templatemetamath to be usable.
// could think about changing the Add impls so &Vector returning T is accepted
// and only impl ConstIndex for Copy types
unsafe impl<'a, T, const N: usize> ConstIndex<T, N> for Vector<T, N>
where
	T: Copy,
{
	fn i(self, index: usize) -> T { *&self.inner[index] }
}

unsafe impl<'a, T, const N: usize> ConstIndex<&'a mut T, N> for &'a mut Vector<T, N> {
	fn i(self, index: usize) -> &'a mut T { &mut self.inner[index] }
}

use crate::VectorView;
unsafe impl<'a, T, const M: usize, const N: usize> ConstIndex<&'a T, M>
	for VectorView<'a, T, M, N>
{
	fn i(self, index: usize) -> &'a T {
		let row = &self.matrix[index];
		&row[self.row]
	}
}

unsafe impl<'a, T, const M: usize, const N: usize> ConstIndex<VectorView<'a, T, M, N>, N>
	for crate::TransposedMatrixView<'a, T, M, N>
{
	fn i(self, index: usize) -> VectorView<'a, T, M, N> {
		debug_assert!(index <= M);
		VectorView {
			row: index,
			matrix: self.matrix,
		}
	}
}

pub struct ConstIterator<T, C: ConstIndex<T, N>, const N: usize> {
	pub(crate) pos: usize,
	pub(crate) content: C,
	pub(crate) marker: core::marker::PhantomData<T>,
}

impl<T, C: ConstIndex<T, N> + Copy, const N: usize> Iterator for ConstIterator<T, C, N> {
	type Item = T;
	fn next(&mut self) -> Option<T> {
		if self.pos < N {
			let ret = self.content.i(self.pos);
			self.pos += 1;
			Some(ret)
		} else {
			None
		}
	}
}

/* im reasonably sure this could be implemented with GAT
 * right now it tells me that T and N are unconstrained, because C is not generic over them
 * this is exactly what GAT provides
impl<C, T, const N: usize> IntoIterator for C
where
	C: ConstIndex<T, N> + Copy,
{
	type Item = T;
	type IntoIter = ConstIterator<T, Self, N>;

	fn into_iter(self) -> Self::IntoIter { ConstIterator { pos: 0, content: self, marker: Default::default() }
}
*/

impl<C, T, const N: usize> From<C> for ConstIterator<T, C, N>
where
	C: ConstIndex<T, N>,
{
	fn from(content: C) -> Self {
		Self {
			pos: 0,
			content,
			marker: Default::default(),
		}
	}
}

pub struct ConstIteratorMut<'a, T, C, const N: usize> {
	pos: usize,
	content: *mut C,
	marker: core::marker::PhantomData<&'a mut T>,
}

impl<'a, T: 'a, C: 'a, const N: usize> Iterator for ConstIteratorMut<'a, T, C, N>
where
	&'a mut C: ConstIndex<&'a mut T, N>,
{
	type Item = &'a mut T;
	fn next(&mut self) -> Option<&'a mut T> {
		if self.pos < N {
			// this is to work around lifetime issues (but its like legit)
			// we can't just do this the direct way with self.content being &'a mut C
			// because then content.i(x) would return &'a mut T
			// but we need &a mut T, i.e. living as long/not outliving as &mut self
			// can't express that concept though cause its an anonymous lifetime
			// and changing that would break the iterator api.
			// the problem that would be occuring is that calling .next() twice and storing
			// the result might return the same reference twice leading to mutable
			// aliasing. we can guarantee that not to happen though, because the unsafe
			// trait ConstIndex provides the method .i(x) which is guaranteed to not alias for
			// different indices. as we increment the index on each iteration we never
			// alias.
			//
			// additionally there are tests that are run with miri just to make sure
			let content: &mut C = unsafe { core::mem::transmute(self.content) };
			let ret = content.i(self.pos);
			self.pos += 1;
			Some(ret)
		} else {
			None
		}
	}
}

impl<'a, T, C: 'a, const N: usize> From<&'a mut C> for ConstIteratorMut<'a, T, C, N>
where
	&'a mut C: ConstIndex<&'a mut T, N>,
{
	fn from(content: &'a mut C) -> Self {
		Self {
			pos: 0,
			content: content as *mut _,
			marker: Default::default(),
		}
	}
}

#[test]
fn const_iter() {
	use crate::Vector;
	use rand::{thread_rng, Rng};
	let mut rng = thread_rng();
	let a: Vector<f32, 40> = rng.gen();
	let iter: ConstIterator<&f32, _, 40> = ConstIterator {
		pos: 0,
		content: &a,
		marker: Default::default(),
	};

	let _s: f32 = iter.sum();
}