| 121 | |
| 122 | template <class T, index_int N> |
| 123 | struct array |
| 124 | { |
| 125 | using value_type = T; |
| 126 | T d[N] = {{}}; |
| 127 | |
| 128 | constexpr array() = default; |
| 129 | |
| 130 | template <class... Ts, |
| 131 | MIGRAPHX_REQUIRES(sizeof...(Ts) == N and (is_convertible<Ts, T>{} and ...))> |
| 132 | constexpr array(Ts... xs) : d{static_cast<value_type>(xs)...} |
| 133 | { |
| 134 | } |
| 135 | |
| 136 | template <class U, MIGRAPHX_REQUIRES(is_convertible<U, T>{} and (N > 1))> |
| 137 | constexpr explicit array(U x) |
| 138 | { |
| 139 | fill(this->begin(), this->end(), x); |
| 140 | } |
| 141 | |
| 142 | constexpr T& operator[](index_int i) |
| 143 | { |
| 144 | MIGRAPHX_ASSERT(i < N); |
| 145 | return d[i]; |
| 146 | } |
| 147 | constexpr const T& operator[](index_int i) const |
| 148 | { |
| 149 | MIGRAPHX_ASSERT(i < N); |
| 150 | return d[i]; |
| 151 | } |
| 152 | |
| 153 | constexpr T& front() { return d[0]; } |
| 154 | constexpr const T& front() const { return d[0]; } |
| 155 | |
| 156 | constexpr T& back() { return d[N - 1]; } |
| 157 | constexpr const T& back() const { return d[N - 1]; } |
| 158 | |
| 159 | constexpr T* data() { return d; } |
| 160 | constexpr const T* data() const { return d; } |
| 161 | |
| 162 | constexpr index_constant<N> size() const { return {}; } |
| 163 | constexpr auto empty() const { return size() == _c<0>; } |
| 164 | |
| 165 | constexpr T* begin() { return d; } |
| 166 | constexpr const T* begin() const { return d; } |
| 167 | |
| 168 | constexpr T* end() { return d + size(); } |
| 169 | constexpr const T* end() const { return d + size(); } |
| 170 | |
| 171 | constexpr T dot(const array& x) const |
| 172 | { |
| 173 | auto r = x * (*this); |
| 174 | return r.reduce([](auto a, auto b) { return a + b; }, 0); |
| 175 | } |
| 176 | |
| 177 | constexpr T product() const |
| 178 | { |
| 179 | return reduce([](auto x, auto y) { return x * y; }, 1); |
| 180 | } |