475 lines
15 KiB
C++
475 lines
15 KiB
C++
#ifndef _vector_n_
|
|
#define _vector_n_
|
|
|
|
#include "iostream"
|
|
#include <vector>
|
|
#include <random>
|
|
|
|
#include <initializer_list>
|
|
|
|
#include "./utils/random.h"
|
|
#include <cstdint>
|
|
#include <type_traits>
|
|
#include <stdexcept>
|
|
#include <cmath>
|
|
|
|
namespace utils{
|
|
//######################################
|
|
//# VECTOR TYPE #
|
|
//# Backed by std::vector<T> #
|
|
//######################################
|
|
template<typename T>
|
|
class Vector{
|
|
public:
|
|
using value_type = T;
|
|
std::vector<T> v;
|
|
|
|
// Default construtor utils::Vd vector;
|
|
Vector() = default;
|
|
|
|
// Construtor utils::Vd vector(2, 3.15);
|
|
// Prevent implicit conversions like Vector<double> v = 5;
|
|
explicit Vector(uint64_t size, T value = T()) {
|
|
v.resize(size, value);
|
|
}
|
|
|
|
// Construct from a braced list: utils::Vf v{1,2,3};
|
|
Vector(std::initializer_list<T> init) : v(init) {}
|
|
|
|
|
|
|
|
|
|
|
|
void random(const uint64_t size, const T& lower, const T& higher){
|
|
|
|
v.resize(size, T{0});
|
|
|
|
// Copy data row by row
|
|
for (uint64_t i = 0; i < size; ++i) {
|
|
v[i] = utils::random(lower, higher);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//##########################################################
|
|
//# VECTOR: --- basic properties --- #
|
|
//##########################################################
|
|
// vector.clear();
|
|
void clear() noexcept {v.clear();}
|
|
|
|
// vector.push_back(2);
|
|
void push_back(const T& val) { v.push_back(val); }
|
|
|
|
// vector[2] = 2;
|
|
T& operator[](uint64_t idx) { return v[idx]; }
|
|
// a = vector[2];
|
|
const T& operator[](uint64_t idx) const { return v[idx]; }
|
|
|
|
|
|
// Assign from a braced list after default construction:
|
|
Vector& operator=(std::initializer_list<T> init) {
|
|
v = init;
|
|
return *this;
|
|
}
|
|
|
|
// vector.size();
|
|
uint64_t size() const noexcept { return v.size(); }
|
|
|
|
void resize(uint64_t new_size, const T& value = T()) {
|
|
v.resize(new_size, value);
|
|
}
|
|
|
|
|
|
T* data() noexcept { return v.data(); }
|
|
const T* data() const noexcept { return v.data(); }
|
|
|
|
//###########################################
|
|
//# VECTOR: == and != #
|
|
//###########################################
|
|
bool operator==(const Vector<T>& a) const {
|
|
if (v.size() != a.size()) {
|
|
return false;
|
|
}
|
|
const static T eps = static_cast<T>(1e-6); // tweak if you like
|
|
for (uint64_t i = 0; i < v.size(); ++i) {
|
|
if (std::fabs(v[i] - a[i]) > eps) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool operator!=(const Vector<T>& a) const { return !(*this == a); }
|
|
|
|
//##################################################
|
|
//# VECTOR: nearly_equal_vec #
|
|
//##################################################
|
|
|
|
bool nearly_equal_vec(const Vector<T>& a, double tol=1e-12) const {
|
|
if (a.size() != v.size()) return false;
|
|
for (uint64_t i=0;i<a.size();++i) {
|
|
if (std::fabs(a[i]-v[i])>tol) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//##################################################
|
|
//# VECTOR: Scalar Addition #
|
|
//##################################################
|
|
template <typename U, typename = typename std::enable_if<std::is_convertible<U, T>::value>::type>
|
|
void inplace_add(const U a){
|
|
const T a_hat = static_cast<T>(a);
|
|
const uint64_t n = v.size();
|
|
for (uint64_t i = 0; i < n; ++i){
|
|
v[i] += a_hat;
|
|
}
|
|
}
|
|
template <typename U, typename = typename std::enable_if<std::is_convertible<U, T>::value>::type>
|
|
Vector<T> add(const U a) const{
|
|
Vector<T> result = *this;
|
|
result.inplace_add(a);
|
|
return result;
|
|
}
|
|
template <typename U, typename = typename std::enable_if<std::is_convertible<U, T>::value>::type>
|
|
Vector<T> operator+(const U a) const {
|
|
return add(a);
|
|
}
|
|
template <typename U, typename = typename std::enable_if<std::is_convertible<U, T>::value>::type>
|
|
friend Vector<T> operator+(U a, const Vector<T>& b) {
|
|
return b + a;
|
|
}
|
|
template <typename U, typename = typename std::enable_if<std::is_convertible<U, T>::value>::type>
|
|
Vector<T>& operator+=(const U a) {
|
|
inplace_add(a);
|
|
return *this;
|
|
}
|
|
//##################################################
|
|
//# VECTOR: Vector Addition #
|
|
//##################################################
|
|
void inplace_add(const Vector<T>& a){
|
|
if (a.size() != v.size()){
|
|
throw std::runtime_error("utill:Vector.inplace_add -> Dimensions does not fit");
|
|
}
|
|
const uint64_t n = v.size();
|
|
for (uint64_t i = 0; i < n; ++i){
|
|
v[i] += a[i];
|
|
}
|
|
}
|
|
Vector<T> add(const Vector<T>& a) const{
|
|
Vector<T> result = *this;
|
|
result.inplace_add(a);
|
|
return result;
|
|
}
|
|
Vector<T> operator+(const Vector<T>& a) const {
|
|
return add(a);
|
|
}
|
|
Vector<T>& operator+=(const Vector<T>& a) {
|
|
inplace_add(a);
|
|
return *this;
|
|
}
|
|
//##################################################
|
|
//# VECTOR: Scalar Subtract #
|
|
//##################################################
|
|
template <typename U, typename = typename std::enable_if<std::is_convertible<U, T>::value>::type>
|
|
void inplace_subtract(const U a){
|
|
const T a_hat = static_cast<T>(a);
|
|
const uint64_t n = v.size();
|
|
for (uint64_t i = 0; i < n; ++i){
|
|
v[i] -= a_hat;
|
|
}
|
|
}
|
|
template <typename U, typename = typename std::enable_if<std::is_convertible<U, T>::value>::type>
|
|
Vector<T> subtract(const U a) const{
|
|
Vector<T> result = *this;
|
|
result.inplace_subtract(a);
|
|
return result;
|
|
}
|
|
template <typename U, typename = typename std::enable_if<std::is_convertible<U, T>::value>::type>
|
|
Vector<T> operator-(const U a) const {
|
|
return subtract(a);
|
|
}
|
|
template <typename U, typename = typename std::enable_if<std::is_convertible<U, T>::value>::type>
|
|
Vector<T>& operator-=(const U a) {
|
|
inplace_subtract(a);
|
|
return *this;
|
|
}
|
|
//##################################################
|
|
//# VECTOR: Vector Subtract #
|
|
//##################################################
|
|
void inplace_subtract(const Vector<T>& a){
|
|
if (a.size() != v.size()){
|
|
throw std::runtime_error("utill:Vector.inplace_subtract -> Dimensions does not fit");
|
|
}
|
|
const uint64_t n = v.size();
|
|
for (uint64_t i = 0; i < n; ++i){
|
|
v[i] -= a[i];
|
|
}
|
|
}
|
|
Vector<T> subtract(const Vector<T>& a) const{
|
|
Vector<T> result = *this;
|
|
result.inplace_subtract(a);
|
|
return result;
|
|
}
|
|
Vector<T> operator-(const Vector<T>& a) const {
|
|
return subtract(a);
|
|
}
|
|
Vector<T>& operator-=(const Vector<T>& a) {
|
|
inplace_subtract(a);
|
|
return *this;
|
|
}
|
|
//##################################################
|
|
//# VECTOR: Scalar Multiply #
|
|
//##################################################
|
|
template <typename U, typename = typename std::enable_if<std::is_convertible<U, T>::value>::type>
|
|
void inplace_multiply(const U a){
|
|
const T a_hat = static_cast<T>(a);
|
|
const uint64_t n = v.size();
|
|
for (uint64_t i = 0; i < n; ++i){
|
|
v[i] *= a_hat;
|
|
}
|
|
}
|
|
template <typename U, typename = typename std::enable_if<std::is_convertible<U, T>::value>::type>
|
|
Vector<T> multiply(const U a) const{
|
|
Vector<T> result = *this;
|
|
result.inplace_multiply(a);
|
|
return result;
|
|
}
|
|
template <typename U, typename = typename std::enable_if<std::is_convertible<U, T>::value>::type>
|
|
Vector<T> operator*(const U a) const {
|
|
return multiply(a);
|
|
}
|
|
template <typename U, typename = typename std::enable_if<std::is_convertible<U, T>::value>::type>
|
|
friend Vector<T> operator*(U a, const Vector<T>& b) {
|
|
return b * a;
|
|
}
|
|
template <typename U, typename = typename std::enable_if<std::is_convertible<U, T>::value>::type>
|
|
Vector<T>& operator*=(const U a) {
|
|
inplace_multiply(a);
|
|
return *this;
|
|
}
|
|
//##################################################
|
|
//# VECTOR: Vector Multiply #
|
|
//##################################################
|
|
void inplace_multiply(const Vector<T>& a){
|
|
if (a.size() != v.size()){
|
|
throw std::runtime_error("utill:Vector.inplace_multiply -> Dimensions does not fit");
|
|
}
|
|
const uint64_t n = v.size();
|
|
for (uint64_t i = 0; i < n; ++i){
|
|
v[i] *= a[i];
|
|
}
|
|
}
|
|
Vector<T> multiply(const Vector<T>& a) const{
|
|
Vector<T> result = *this;
|
|
result.inplace_multiply(a);
|
|
return result;
|
|
}
|
|
Vector<T> operator*(const Vector<T>& a) const {
|
|
return multiply(a);
|
|
}
|
|
Vector<T>& operator*=(const Vector<T>& a) {
|
|
inplace_multiply(a);
|
|
return *this;
|
|
}
|
|
//################################################
|
|
//# VECTOR: Scalar Divide #
|
|
//################################################
|
|
template <typename U, typename = typename std::enable_if<std::is_convertible<U, T>::value>::type>
|
|
void inplace_divide(const U a){
|
|
const T a_hat = static_cast<T>(a);
|
|
const uint64_t n = v.size();
|
|
for (uint64_t i = 0; i < n; ++i){
|
|
v[i] /= a_hat;
|
|
}
|
|
}
|
|
template <typename U, typename = typename std::enable_if<std::is_convertible<U, T>::value>::type>
|
|
Vector<T> divide(const U a) const{
|
|
Vector<T> result = *this;
|
|
result.inplace_divide(a);
|
|
return result;
|
|
}
|
|
template <typename U, typename = typename std::enable_if<std::is_convertible<U, T>::value>::type>
|
|
Vector<T> operator/(const U a) const {
|
|
return divide(a);
|
|
}
|
|
template <typename U, typename = typename std::enable_if<std::is_convertible<U, T>::value>::type>
|
|
Vector<T>& operator/=(const U a) {
|
|
inplace_divide(a);
|
|
return *this;
|
|
}
|
|
//##################################################
|
|
//# VECTOR: Vector Divide #
|
|
//##################################################
|
|
void inplace_divide(const Vector<T>& a){
|
|
if (a.size() != v.size()){
|
|
throw std::runtime_error("utill:Vector.inplace_divide -> Dimensions does not fit");
|
|
}
|
|
uint64_t n = a.size();
|
|
for (uint64_t i = 0; i < n; ++i){
|
|
v[i] /= a[i];
|
|
}
|
|
}
|
|
Vector<T> divide(const Vector<T>& a) const{
|
|
Vector<T> result = *this;
|
|
result.inplace_divide(a);
|
|
return result;
|
|
}
|
|
Vector<T> operator/(const Vector<T>& a) const {
|
|
return divide(a);
|
|
}
|
|
Vector<T>& operator/=(const Vector<T>& a) {
|
|
inplace_divide(a);
|
|
return *this;
|
|
}
|
|
//###############################################
|
|
//# VECTOR: Scalar Power #
|
|
//###############################################
|
|
template <typename U, typename = typename std::enable_if<std::is_convertible<U, T>::value>::type>
|
|
void inplace_power(const U a){
|
|
const uint64_t n = v.size();
|
|
for (uint64_t i = 0; i < n; ++i){
|
|
v[i] = static_cast<T>(std::pow(v[i], a));
|
|
}
|
|
}
|
|
template <typename U, typename = typename std::enable_if<std::is_convertible<U, T>::value>::type>
|
|
Vector<T> power(const U a) const{
|
|
Vector<T> result = *this;
|
|
result.inplace_power(a);
|
|
return result;
|
|
}
|
|
//###############################################
|
|
//# VECTOR: Vector Power #
|
|
//###############################################
|
|
void inplace_power(const Vector<T>& a){
|
|
if (a.size() != v.size()){
|
|
throw std::runtime_error("utill:Vector.inplace_power -> Dimensions does not fit");
|
|
}
|
|
uint64_t n = a.size();
|
|
for (uint64_t i = 0; i < n; ++i){
|
|
v[i] = static_cast<T>(std::pow(v[i], a[i]));
|
|
}
|
|
}
|
|
Vector<T> power(const Vector<T>& a) const{
|
|
Vector<T> result = *this;
|
|
result.inplace_power(a);
|
|
return result;
|
|
}
|
|
//################################################
|
|
//# VECTOR: Vector square #
|
|
//################################################
|
|
void inplace_sqrt(){
|
|
uint64_t n = v.size();
|
|
for (uint64_t i = 0; i < n; ++i){
|
|
v[i] = static_cast<T>(std::sqrt(v[i]));
|
|
}
|
|
}
|
|
Vector<T> sqrt() const{
|
|
Vector<T> result = *this;
|
|
result.inplace_sqrt();
|
|
return result;
|
|
}
|
|
//###################################################
|
|
//# VECTOR: Dot Product #
|
|
//###################################################
|
|
T dot(const Vector<T>& a)const {
|
|
if (a.size() != v.size()){
|
|
throw std::runtime_error("utill:Vector.dot -> Dimensions does not fit");
|
|
}
|
|
T result = T{0};
|
|
const uint64_t n = v.size();
|
|
for (uint64_t i = 0; i < n; ++i){
|
|
result += a[i]*v[i];
|
|
}
|
|
return result;
|
|
}
|
|
//############################################
|
|
//# VECTOR: Sum #
|
|
//############################################
|
|
T sum()const{
|
|
T result = T{0};
|
|
const uint64_t n = v.size();
|
|
for (uint64_t i = 0; i < n; ++i){
|
|
result += v[i];
|
|
}
|
|
return result;
|
|
}
|
|
//############################################
|
|
//# VECTOR: Norm #
|
|
//############################################
|
|
T norm() const{
|
|
return static_cast<T>(std::sqrt(this->dot(*this)));
|
|
}
|
|
//############################################
|
|
//# VECTOR: Normalize #
|
|
//############################################
|
|
void inplace_normalize() {
|
|
T norm = this->norm();
|
|
if (norm == T{0}){
|
|
throw std::runtime_error("utils::Vector.normalize -> zero norm");
|
|
}
|
|
this->inplace_divide(norm);
|
|
}
|
|
Vector<T> normalize() const{
|
|
Vector<T> result = *this;
|
|
result.inplace_normalize();
|
|
return result;
|
|
}
|
|
//######################################################
|
|
//# VECTOR: Support Functions #
|
|
//######################################################
|
|
inline friend std::ostream& operator << (std::ostream& out, const Vector& vec){
|
|
out << "[";
|
|
for (uint64_t i = 0; i < vec.v.size(); i++){
|
|
out << vec.v[i];
|
|
if (i != vec.v.size() - 1) {
|
|
out << ", ";
|
|
}
|
|
}
|
|
out << "]";
|
|
return out;
|
|
}
|
|
|
|
void print() const{
|
|
std::cout << *this << std::endl;
|
|
}
|
|
|
|
|
|
bool nearly_equal(const Vector<T>& a, T tol = static_cast<T>(1e-9)) const {
|
|
if (v.size() != a.size()){
|
|
return false;
|
|
}
|
|
for (uint64_t i = 0; i < v.size(); ++i){
|
|
T val1 = v[i];
|
|
T val2 = a[i];
|
|
if (std::is_floating_point<T>::value) {
|
|
if (std::fabs(val1 - val2) > tol) return false;
|
|
} else {
|
|
if (val1 != val2) return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
typedef Vector<int> Vi;
|
|
typedef Vector<float> Vf;
|
|
typedef Vector<double> Vd;
|
|
/*
|
|
if (std::is_floating_point<T>::value) {
|
|
if (std::fabs(a - b) > tol) return false;
|
|
} else {
|
|
if (a != b) return false;
|
|
}*/
|
|
} // namespace utils
|
|
|
|
#endif // _vector_n_
|