Program Listing for File NDSize.hpp

Return to documentation for file (include/nix/NDSize.hpp)

// Copyright (c) 2013, German Neuroinformatics Node (G-Node)
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted under the terms of the BSD License. See
// LICENSE file in the root of the Project.

#ifndef NIX_PSIZE_H
#define NIX_PSIZE_H

#include <nix/Platform.hpp>
#include <nix/Exception.hpp>
#include <nix/types.hpp>

#include <cstdint>
#include <stdexcept>
#include <algorithm>
#include <initializer_list>
#include <iostream>
#include <vector>
#include <type_traits>


namespace nix {

// ndsize_t is declared in <nix/types.hpp>


#ifdef _MSC_VER
// This is a workaround for MVSC that spits out warnings for
// unsafe operations when using copy_n, fill_n and raw pointers
// To avoid this we implement the following functions in NDSize.cpp
// and disable the correspoding warning (C4996) for <algorithm>

NIXAPI void nd_fill(ndsize_t *data, size_t len, ndsize_t value);
NIXAPI void nd_fill(ndssize_t *data, size_t len, ndssize_t value);

NIXAPI void nd_copy(ndsize_t *source, size_t n, ndsize_t *dest);
NIXAPI void nd_copy(ndssize_t *source, size_t n, ndssize_t *dest);

#else

#define nd_fill std::fill_n
#define nd_copy std::copy_n

#endif

template<typename T>
class NDSizeBase {

public:

    typedef T        value_type;
    typedef T       *iterator;
    typedef const T *const_iterator;
    typedef T       &reference;
    typedef const T  const_reference;
    typedef T       *pointer;
    typedef size_t   difference_type;
    typedef size_t   size_type;

    NDSizeBase()
        : rank(0), dims(nullptr)
    {
    }


    explicit NDSizeBase(size_t rank)
        : rank(rank), dims(nullptr)
    {
        allocate();
    }


    explicit NDSizeBase(size_t rank, T fill_value)
        : rank(rank), dims(nullptr)
    {
        allocate();
        fill(fill_value);
    }

    template<typename U>
    NDSizeBase(std::initializer_list<U> args)
        : rank(args.size())
    {
        allocate();

        #ifdef _MSC_VER
        //std::transform triggers C4996, see nd_copy, nd_fill above
        std::vector<U> u = args;
        for (size_t i = 0; i < rank; i++) {
            dims[i] = static_cast<T>(u[i]);
        }
        #else
        std::transform(args.begin(), args.end(), dims,
                       [](const U& val) {
                           return static_cast<T>(val);
                       });
        #endif
    }

    template<typename U>
    NDSizeBase(const std::vector<U> &args)
        : rank(args.size())
    {
        allocate();

        #ifdef _MSC_VER
        //std::transform triggers C4996, see nd_copy, nd_fill above
        for (size_t i = 0; i < rank; i++) {
            dims[i] = static_cast<T>(args[i]);
        }
        #else
        std::transform(args.begin(), args.end(), dims,
                       [](const U& val) {
                           return static_cast<T>(val);
                       });
        #endif
    }

    //copy
    NDSizeBase(const NDSizeBase &other)
        : rank(other.rank), dims(nullptr)
    {
        allocate();
        nd_copy(other.dims, rank, dims);
    }

    //move (not tested due to: http://llvm.org/bugs/show_bug.cgi?id=12208)
    NDSizeBase(NDSizeBase &&other)
        : rank(other.rank), dims(other.dims)
    {
        other.dims = nullptr;
        other.rank = 0;
    }

    //copy and move assignment operator (not tested, see above)
    NDSizeBase& operator=(NDSizeBase other) {
        swap(other);
        return *this;
    }

    // safe bool of the future (i.e. C++11)
    explicit operator bool() const {
        return rank > 0;
    }

    T& operator[] (const size_t index) {
        const NDSizeBase *this_const = const_cast<const NDSizeBase*>(this);
        return const_cast<T&>(this_const->operator[](index));
    }


    const T& operator[] (const size_t index) const {
        if (index + 1 > rank) {
            throw std::out_of_range ("Index out of bounds");
        }
        return dims[index];
    }

    NDSizeBase<T>& operator++() {
        std::for_each(begin(), end(), [](T &val) {
            val++;
        });
        return *this;
    }


    NDSizeBase<T> operator++(int) {
        NDSizeBase<T> snapshot(*this);
        operator++();
        return snapshot;
    }


    NDSizeBase<T>& operator+=(const NDSizeBase<T> &rhs) {
        if(size() != rhs.size()) {
            throw std::out_of_range (""); //fixme: use different exception
        }

        for (size_t i = 0; i < rank; i++) {
            dims[i] += rhs.dims[i];
        }

        return *this;
    }


    NDSizeBase<T>& operator+=(T val) {
        for (size_t i = 0; i < rank; i++) {
            dims[i] += val;
        }

        return *this;
    }


    NDSizeBase<T>& operator+=(int val) {
        return operator+=(static_cast<T>(val));
    }


    NDSizeBase<T>& operator--() {
        std::for_each(begin(), end(), [](T &val) {
            val--;
        });
        return *this;
    }


    NDSizeBase<T> operator--(int) {
        NDSizeBase<T> snapshot(*this);
        operator--();
        return snapshot;
    }


    NDSizeBase<T>& operator-=(const NDSizeBase<T> &rhs) {
        if(size() != rhs.size()) {
            throw std::out_of_range (""); //fixme: use different exception
        }

        for (size_t i = 0; i < rank; i++) {
            dims[i] -= rhs.dims[i];
        }

        return *this;
    }


    NDSizeBase<T>& operator-=(T val) {
        for (size_t i = 0; i < rank; i++) {
            dims[i] -= val;
        }

        return *this;
    }


    NDSizeBase<T>& operator-=(int val) {
        return operator-=(static_cast<T>(val));
    }


    void swap(NDSizeBase &other) {
        using std::swap;
        swap(dims, other.dims);
        rank = other.rank;
    }


    NDSizeBase<T>& operator*=(const NDSizeBase<T> &rhs) {
        if(size() != rhs.size()) {
            throw std::out_of_range (""); //fixme: use different exception
        }

        for (size_t i = 0; i < rank; i++) {
            dims[i] *= rhs.dims[i];
        }

        return *this;
    }


    NDSizeBase<T>& operator/=(const NDSizeBase<T> &rhs) {
        if(size() != rhs.size()) {
            throw std::out_of_range (""); //fixme: use different exception
        }

        for (size_t i = 0; i < rank; i++) {
            dims[i] /= rhs.dims[i];
        }

        return *this;
    }


    size_t size() const { return rank; }


    T nelms() const {
        T product = 1;
        //TODO: check for "overflow" in calculations
        std::for_each(begin(), end(), [&](T val) {
            product *= val;
        });

        return product;
    }


    T dot(const NDSizeBase<T> &other) const {
        if(size() != other.size()) {
            throw std::out_of_range ("Dimensions do not match"); //fixme: use different exception
        }

        T res  = 0;
        for (size_t i = 0; i < rank; i++) {
            res += dims[i] * other.dims[i];
        }

        return res;
    }


    T* data() { return dims; }


    const T* data() const {return dims; }


    void fill(T value) {
        nd_fill(dims, rank, value);
    }


    ~NDSizeBase() {
        delete[] dims;
    }


    //we are modelling a boost::Collection
    iterator begin() { return dims; }


    iterator end() { return dims + rank; }


    const_iterator begin() const { return dims; }


    const_iterator end() const { return dims + rank; }


    bool empty() const { return rank == 0; }

private:

    void allocate() {
        if (rank > 0) {
            dims = new T[rank];
        }
    }

    size_t   rank;
    T *dims;
};


template<typename T>
NDSizeBase<T> operator-(NDSizeBase<T> lhs, const NDSizeBase<T> &rhs)
{
    lhs -= rhs;
    return lhs;
}


template<typename T>
NDSizeBase<T> operator+(NDSizeBase<T> lhs, const NDSizeBase<T> &rhs)
{
    lhs += rhs;
    return lhs;
}


template<typename T>
NDSizeBase<T> operator+(NDSizeBase<T> lhs, T rhs)
{
    lhs += rhs;
    return lhs;
}


template<typename T>
NDSizeBase<T> operator+(T lhs, const NDSizeBase<T> &rhs)
{
    return operator+(rhs, lhs);
}


template<typename T>
NDSizeBase<T> operator+(NDSizeBase<T> lhs, int rhs)
{
    lhs += rhs;
    return lhs;
}


template<typename T>
NDSizeBase<T> operator+(int lhs, const NDSizeBase<T> &rhs)
{
    return operator+(rhs, lhs);
}

template<typename T>
NDSizeBase<T> operator*(NDSizeBase<T> lhs, const NDSizeBase<T> &rhs)
{
    lhs *= rhs;
    return lhs;
}


template<typename T>
NDSizeBase<T> operator*(NDSizeBase<T> lhs, T rhs)
{
    lhs *= rhs;
    return lhs;
}


template<typename T>
NDSizeBase<T> operator*(T lhs, const NDSizeBase<T> &rhs)
{
    return operator*(rhs, lhs);
}


template<typename T>
NDSizeBase<T> operator/(NDSizeBase<T> lhs, const NDSizeBase<T> &rhs)
{
    lhs /= rhs;
    return lhs;
}


template<typename T>
NDSizeBase<T> operator/(NDSizeBase<T> lhs, T rhs)
{
    lhs /= rhs;
    return lhs;
}


template<typename T>
NDSizeBase<T> operator/(T lhs, const NDSizeBase<T> &rhs)
{
    return operator/(rhs, lhs);
}


template<typename T>
inline bool operator==(const NDSizeBase<T> &lhs, const NDSizeBase<T> &rhs)
{
    if (lhs.size() != rhs.size())
        return false;

    for (size_t i = 0; i < lhs.size(); i++) {
        if (lhs[i] != rhs[i])
            return false;
    }

    return true;
}


template<typename T>
inline bool operator!=(const NDSizeBase<T> &lhs, const NDSizeBase<T> &rhs)
{
    return !operator==(lhs, rhs);
}


template<typename T>
inline bool operator<(const NDSizeBase<T> &lhs, const NDSizeBase<T> &rhs)
{
    if (lhs.size() != rhs.size()) {
        throw IncompatibleDimensions("size must agree to compare",
                                     "NDSizeBase < NDSizeBase ");
    }

    const size_t size = lhs.size();
    for (size_t i = 0; i < size; i++) {
        if (lhs[i] >= rhs[i]) {
            return false;
        }
    }

    return true;
}

template<typename T>
inline bool operator<=(const NDSizeBase<T> &lhs, const NDSizeBase<T> &rhs)
{
    if (lhs.size() != rhs.size()) {
        throw IncompatibleDimensions("size must agree to compare",
                                     "NDSizeBase < NDSizeBase ");
    }

    const size_t size = lhs.size();
    for (size_t i = 0; i < size; i++) {
        if (lhs[i] > rhs[i]) {
            return false;
        }
    }

    return true;
}

template<typename T>
inline bool operator>(const NDSizeBase<T> &lhs, const NDSizeBase<T> &rhs)
{
    return !(lhs <= rhs);
}

template<typename T>
inline bool operator>=(const NDSizeBase<T> &lhs, const NDSizeBase<T> &rhs)
{
    return !(lhs < rhs);
}

template<typename T>
inline std::ostream& operator<<(std::ostream &os, const NDSizeBase<T> &ndsize)
{
  os << "NDSize {";
  for(size_t i = 0; i < ndsize.size(); i++) {
    if (i != 0) {
      os << ", ";
    }

    os << ndsize[i];
  }

  os << "}\n";
  return os;
}


typedef NDSizeBase<ndsize_t>  NDSize;

typedef NDSizeBase<ndssize_t> NDSSize;
#if 0 // __has_builtin(__builtin_add_overflow)
#define nix_safe_add __builtin_add_overflow
#else

template<typename T>
inline typename std::enable_if<std::is_unsigned<T>::value, bool>::type
nix_safe_add(T a, T b, T *out)
{
    *out = a + b;
    return *out < a;
}

#endif //__has_builtin

} // namespace nix

#endif // NIX_PSIZE_H