<commit_before>// -*- mode: c++; coding: utf-8 -*-
#pragma once
#ifndef IOSTR_HPP_
#define IOSTR_HPP_

#include <cstring>
#include <cerrno>
#include <cassert>
#include <cstdarg>
#include <ctime>

#include <stdexcept>
#include <iostream>
#include <sstream>
#include <fstream>

#include <string>
#include <vector>
#include <map>
#include <unordered_map>
#include <set>
#include <algorithm>

/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////
// prior declaration

namespace wtl {
template <class T> extern
std::string str_join(const T& v,
                     const std::string& sep=",",
                     const unsigned int digits=std::cout.precision(),
                     const bool fixed=false);
}

/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////
// global operator<< for containers

template <class T> inline
std::ostream& operator<< (std::ostream& ost, const std::vector<T>& v) {
    return ost << '[' << wtl::str_join(v, ", ", ost.precision()) << ']';
}

template <> inline
std::ostream& operator<< (std::ostream& ost, const std::vector<std::string>& v) {
    if (v.empty()) {return ost << "[]";}
    return ost << "['" << wtl::str_join(v, "', '") << "']";
}

template <class T> inline
std::ostream& operator<< (std::ostream& ost, const std::set<T>& v) {
    return ost << "set([" << wtl::str_join(v, ", ", ost.precision()) << "])";
}

template <class F, class S> inline
std::ostream& operator<< (std::ostream& ost, const std::pair<F, S>& p) {
    return ost << '(' << std::get<0>(p)
               << ": " << std::get<1>(p) << ')';
}

namespace wtl { namespace detail {
    template <class Map> inline
    std::ostream& operator_ost_map(std::ostream& ost, const Map& m) {
        ost << '{';
        if (!m.empty()) {
            auto it(begin(m));
            ost << std::get<0>(*it) << ": " << std::get<1>(*it);
            while (++it != end(m)) {
                ost << ", " << std::get<0>(*it) << ": " << std::get<1>(*it);
            }
        }
        return ost << '}';
    }
}} // namespace

// map
template <class Key, class T, class Comp> inline
std::ostream& operator<< (std::ostream& ost, const std::map<Key, T, Comp>& m) {
    return wtl::detail::operator_ost_map(ost, m);
}

// unordered_map
template <class Key, class T, class Hash> inline
std::ostream& operator<< (std::ostream& ost, const std::unordered_map<Key, T, Hash>& m) {
    return wtl::detail::operator_ost_map(ost, m);
}

/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////
namespace wtl {
/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////

struct identity {
    template<class T>
    constexpr T operator()(T&& x) const noexcept {
        return std::forward<T>(x);
    }
};

template <class Iter, class Func=identity> inline
std::ostream& ost_join(std::ostream& ost, Iter begin_, const Iter end_,
                       const std::string& sep=",", Func func=Func()) {
    if (begin_ == end_) return ost;
    ost << func(*begin_);
    while (++begin_ != end_) {ost << sep << func(*begin_);}
    return ost;
}

template <class T, class Func=identity> inline
std::ostream& ost_join(std::ostream& ost, const T& v,
                       const std::string& sep=",", Func func=Func()) {
    return ost_join(ost, begin(v), end(v), sep, func);
}

template <class Iter, class Func=identity> inline
std::string oss_join(Iter begin_, const Iter end_, const std::string& sep=",", Func func=Func()) {
    std::ostringstream oss;
    ost_join(oss, begin_, end_, sep, func);
    return oss.str();
}

template <class T, class Func=identity> inline
std::string oss_join(const T& v, const std::string& sep=",", Func func=Func()) {
    std::ostringstream oss;
    ost_join(oss, v, sep, func);
    return oss.str();
}

template <class Iter> inline
std::string str_join(Iter begin_, const Iter end_, const std::string& sep=",",
                     const unsigned int digits=std::cout.precision(), const bool fixed=false) {
    if (begin_ == end_) return "";
    std::ostringstream oss;
    oss.precision(digits);
    if (fixed) {oss << std::fixed;}
    oss << *begin_;
    while (++begin_ != end_) {oss << sep << *begin_;}
    return oss.str();
}

template <class T> inline
std::string str_join(const T& v, const std::string& sep,
                     const unsigned int digits, const bool fixed) {
    return str_join(begin(v), end(v), sep, digits, fixed);
}

template <class T> inline
std::string str_matrix(const T& m, const std::string& sep=",", const unsigned int digits=std::cout.precision()) {
    std::string s;
    for (const auto& row: m) {
        s += str_join(row, sep, digits);
        s += '\n';
    }
    return s;
}

template <class T> inline
std::string str_map(const T& m, const unsigned int digits=std::cout.precision()) {
    std::ostringstream oss;
    oss.precision(digits);
    oss << m;
    return oss.str();
}

template <int N, class Iter> inline
std::string str_pairs(Iter begin_, const Iter end_, const std::string& sep=",", const unsigned int digits=std::cout.precision()) {
    if (begin_ == end_) return "";
    std::ostringstream oss;
    oss.precision(digits);
    oss << std::get<N>(*begin_);
    while (++begin_ != end_) {oss << sep << std::get<N>(*begin_);}
    return oss.str();
}
template <int N, class Map> inline
std::string str_pairs(const Map& m, const std::string& sep=",", const unsigned int digits=std::cout.precision()) {
    return str_pairs<N>(begin(m), end(m), sep, digits);
}

template <class Map> inline
std::string str_pair_rows(const Map& m, const std::string& sep=",", const unsigned int digits=std::cout.precision()) {
    std::string s = str_pairs<0>(m, sep, digits);
    s += '\n';
    s += str_pairs<1>(m, sep, digits);
    return s += '\n';
}

template <class Map> inline
std::string str_pair_cols(const Map& m, const std::string& sep=",", const unsigned int digits=std::cout.precision()) {
    std::ostringstream oss;
    oss.precision(digits);
    for (const auto& p: m) {
        oss << std::get<0>(p) << sep << std::get<1>(p) << '\n';
    }
    return oss.str();
}

/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////
// std::string manipulation

inline std::vector<std::string> split_algorithm(const std::string& src, const std::string& delimiter=" \t\n") {
    std::vector<std::string> dst;
    for (auto delim_pos=begin(src), start=delim_pos; delim_pos!=end(src); ) {
        delim_pos = std::find_first_of(start, end(src), begin(delimiter), end(delimiter));
        dst.push_back(std::string(start, delim_pos));
        ++(start = delim_pos);
    }
    return dst;
}

inline std::vector<std::string>
split(const std::string& src, const std::string& delimiter=" \t\n") {
    std::vector<std::string> dst;
    size_t start = 0, offset = 0;
    while (true) {
        offset = src.find_first_of(delimiter, start);
        offset -= start;
        dst.push_back(src.substr(start, offset));
        start += offset;
        if (start == src.npos) break;
        ++start;
    }
    return dst;
}

inline std::string rstrip(std::string src, const std::string& chars=" ") {
    size_t pos(src.find_last_not_of(chars));
    if (pos == std::string::npos) {
        src.clear();
    } else {
        src.erase(++pos);
    }
    return src;
}

inline std::string lstrip(std::string src, const std::string& chars=" ") {
    const size_t pos(src.find_first_not_of(chars));
    if (pos == std::string::npos) {
        src.clear();
    } else {
        src.erase(0, pos);
    }
    return src;
}

inline std::string strip(std::string src, const std::string& chars=" ") {
    return rstrip(lstrip(src, chars), chars);
}

inline bool startswith(const std::string& full, const std::string& sub) {
    const size_t full_size = full.size();
    const size_t sub_size = sub.size();
    if (full_size > sub_size) {
        return full.compare(0, sub_size, sub) == 0;
    } else {
        return false;
    }
}

inline bool endswith(const std::string& full, const std::string& sub) {
    const size_t full_size = full.size();
    const size_t sub_size = sub.size();
    if (full_size > sub_size) {
        return full.compare(full_size - sub_size, sub_size, sub) == 0;
    } else {
        return false;
    }
}

inline std::string replace_all(const std::string& patt, const std::string& repl, const std::string& src) {
    std::string result;
    std::string::size_type pos_before(0);
    std::string::size_type pos(0);
    std::string::size_type len(patt.size());
    while ((pos = src.find(patt, pos)) != std::string::npos) {
        result.append(src, pos_before, pos-pos_before);
        result.append(repl);
        pos += len;
        pos_before = pos;
    }
    result.append(src, pos_before, src.size()-pos_before);
    return result;
}

/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////

inline std::string strprintf(const char* const format, ...) {
    assert(format);
    va_list args;
    std::string buffer;
    va_start(args, format);
    const int length = std::vsnprintf(nullptr, 0, format, args) ;
    va_end(args);
    if (length < 0) throw std::runtime_error(format);
    buffer.resize(length + 1);
    va_start(args, format);
    const int result = std::vsnprintf(&buffer[0], length + 1, format, args);
    va_end(args);
    if (result < 0) throw std::runtime_error(format);
    buffer.pop_back();
    return buffer;
}

/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////

// default is the same as ctime(): Thu Aug 23 14:55:02 2001
// which is equivalent to "%a %b %d %T %Y"
inline std::string strftime(const std::string& format="%c") {
    char cstr[80];
    const time_t rawtime = time(nullptr);
    const struct tm* t = localtime(&rawtime);
    std::strftime(cstr, sizeof(cstr), format.c_str(), t);
    return std::string(cstr);
}
inline std::string iso8601date(const std::string& sep="-") {
    std::ostringstream oss;
    oss << "%Y" << sep << "%m" << sep << "%d";
    return strftime(oss.str());
}
inline std::string iso8601time(const std::string& sep=":") {
    std::ostringstream oss;
    oss << "%H" << sep << "%M" << sep << "%S";
    return strftime(oss.str());
}
inline std::string iso8601datetime() {return strftime("%FT%T%z");}

/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////
// fstream wrapper for binary mode and exceptions
// boost::serialization requires binary mode

class Fin: public std::ifstream {
  public:
    explicit Fin(const std::string& filepath,
                 const std::ios::openmode mode=std::ios::in):
        std::ifstream(filepath.c_str(), mode | std::ios::binary)
    {exceptions(std::ios::badbit);}

    std::string readline(const char delimiter='\n') {
        std::string buffer;
        std::getline(*this, buffer, delimiter);
        return buffer;
    }

    std::vector<std::string> readlines(const char delimiter='\n') {
        std::vector<std::string> lines;
        std::string buffer;
        while (std::getline(*this, buffer, delimiter)) {
            lines.push_back(buffer);
        }
        return lines;
    }

    std::string read(const char delimiter='\0') {return readline(delimiter);}
};

class Fout: public std::ofstream {
  public:
    explicit Fout(const std::string& filepath,
                  const std::ios::openmode mode=std::ios::out):
        std::ofstream(filepath.c_str(), mode | std::ios::binary)
    {exceptions(std::ios::failbit);}

    template <class Iter>
    Fout& writelines(Iter begin_, const Iter end_, const char sep='\n') {
        if (begin_ == end_) return *this;
        *this << *begin_;
        while (++begin_ != end_) {*this << sep << *begin_;}
        return *this;
    }

    template <class V>
    Fout& writelines(const V& lines, const char sep='\n') {
        return writelines(begin(lines), end(lines), sep);
    }
};

inline std::vector<std::pair<std::string, std::string> >
read_ini(const std::string& filename) {
    std::vector<std::string> lines = Fin(filename).readlines();
    std::vector<std::pair<std::string, std::string> > dst;
    dst.reserve(lines.size());
    for (auto line_: lines) {
        line_ = strip(line_);
        if (startswith(line_, "[")) {continue;} // TODO
        auto pair_ = split(line_, ":="); // TODO
        if (pair_.size() < 2) {continue;}
        dst.emplace_back(rstrip(pair_[0]), lstrip(pair_[1]));
    }
    return dst;
}

/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////
} // namespace wtl
/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////

#endif /* IOSTR_HPP_ */
<commit_msg>Add operator<< for std::unordered_set<commit_after>// -*- mode: c++; coding: utf-8 -*-
#pragma once
#ifndef IOSTR_HPP_
#define IOSTR_HPP_

#include <cstring>
#include <cerrno>
#include <cassert>
#include <cstdarg>
#include <ctime>

#include <stdexcept>
#include <iostream>
#include <sstream>
#include <fstream>

#include <string>
#include <vector>
#include <map>
#include <unordered_map>
#include <set>
#include <unordered_set>
#include <algorithm>

/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////
// prior declaration

namespace wtl {
template <class T> extern
std::string str_join(const T& v,
                     const std::string& sep=",",
                     const unsigned int digits=std::cout.precision(),
                     const bool fixed=false);
}

/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////
// global operator<< for containers

template <class T> inline
std::ostream& operator<< (std::ostream& ost, const std::vector<T>& v) {
    return ost << '[' << wtl::str_join(v, ", ", ost.precision()) << ']';
}

template <> inline
std::ostream& operator<< (std::ostream& ost, const std::vector<std::string>& v) {
    if (v.empty()) {return ost << "[]";}
    return ost << "['" << wtl::str_join(v, "', '") << "']";
}

template <class T> inline
std::ostream& operator<< (std::ostream& ost, const std::set<T>& v) {
    return ost << "set([" << wtl::str_join(v, ", ", ost.precision()) << "])";
}

template <class T> inline
std::ostream& operator<< (std::ostream& ost, const std::unordered_set<T>& v) {
    return ost << "set([" << wtl::str_join(v, ", ", ost.precision()) << "])";
}

template <class F, class S> inline
std::ostream& operator<< (std::ostream& ost, const std::pair<F, S>& p) {
    return ost << '(' << std::get<0>(p)
               << ": " << std::get<1>(p) << ')';
}

namespace wtl { namespace detail {
    template <class Map> inline
    std::ostream& operator_ost_map(std::ostream& ost, const Map& m) {
        ost << '{';
        if (!m.empty()) {
            auto it(begin(m));
            ost << std::get<0>(*it) << ": " << std::get<1>(*it);
            while (++it != end(m)) {
                ost << ", " << std::get<0>(*it) << ": " << std::get<1>(*it);
            }
        }
        return ost << '}';
    }
}} // namespace

// map
template <class Key, class T, class Comp> inline
std::ostream& operator<< (std::ostream& ost, const std::map<Key, T, Comp>& m) {
    return wtl::detail::operator_ost_map(ost, m);
}

// unordered_map
template <class Key, class T, class Hash> inline
std::ostream& operator<< (std::ostream& ost, const std::unordered_map<Key, T, Hash>& m) {
    return wtl::detail::operator_ost_map(ost, m);
}

/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////
namespace wtl {
/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////

struct identity {
    template<class T>
    constexpr T operator()(T&& x) const noexcept {
        return std::forward<T>(x);
    }
};

template <class Iter, class Func=identity> inline
std::ostream& ost_join(std::ostream& ost, Iter begin_, const Iter end_,
                       const std::string& sep=",", Func func=Func()) {
    if (begin_ == end_) return ost;
    ost << func(*begin_);
    while (++begin_ != end_) {ost << sep << func(*begin_);}
    return ost;
}

template <class T, class Func=identity> inline
std::ostream& ost_join(std::ostream& ost, const T& v,
                       const std::string& sep=",", Func func=Func()) {
    return ost_join(ost, begin(v), end(v), sep, func);
}

template <class Iter, class Func=identity> inline
std::string oss_join(Iter begin_, const Iter end_, const std::string& sep=",", Func func=Func()) {
    std::ostringstream oss;
    ost_join(oss, begin_, end_, sep, func);
    return oss.str();
}

template <class T, class Func=identity> inline
std::string oss_join(const T& v, const std::string& sep=",", Func func=Func()) {
    std::ostringstream oss;
    ost_join(oss, v, sep, func);
    return oss.str();
}

template <class Iter> inline
std::string str_join(Iter begin_, const Iter end_, const std::string& sep=",",
                     const unsigned int digits=std::cout.precision(), const bool fixed=false) {
    if (begin_ == end_) return "";
    std::ostringstream oss;
    oss.precision(digits);
    if (fixed) {oss << std::fixed;}
    oss << *begin_;
    while (++begin_ != end_) {oss << sep << *begin_;}
    return oss.str();
}

template <class T> inline
std::string str_join(const T& v, const std::string& sep,
                     const unsigned int digits, const bool fixed) {
    return str_join(begin(v), end(v), sep, digits, fixed);
}

template <class T> inline
std::string str_matrix(const T& m, const std::string& sep=",", const unsigned int digits=std::cout.precision()) {
    std::string s;
    for (const auto& row: m) {
        s += str_join(row, sep, digits);
        s += '\n';
    }
    return s;
}

template <class T> inline
std::string str_map(const T& m, const unsigned int digits=std::cout.precision()) {
    std::ostringstream oss;
    oss.precision(digits);
    oss << m;
    return oss.str();
}

template <int N, class Iter> inline
std::string str_pairs(Iter begin_, const Iter end_, const std::string& sep=",", const unsigned int digits=std::cout.precision()) {
    if (begin_ == end_) return "";
    std::ostringstream oss;
    oss.precision(digits);
    oss << std::get<N>(*begin_);
    while (++begin_ != end_) {oss << sep << std::get<N>(*begin_);}
    return oss.str();
}
template <int N, class Map> inline
std::string str_pairs(const Map& m, const std::string& sep=",", const unsigned int digits=std::cout.precision()) {
    return str_pairs<N>(begin(m), end(m), sep, digits);
}

template <class Map> inline
std::string str_pair_rows(const Map& m, const std::string& sep=",", const unsigned int digits=std::cout.precision()) {
    std::string s = str_pairs<0>(m, sep, digits);
    s += '\n';
    s += str_pairs<1>(m, sep, digits);
    return s += '\n';
}

template <class Map> inline
std::string str_pair_cols(const Map& m, const std::string& sep=",", const unsigned int digits=std::cout.precision()) {
    std::ostringstream oss;
    oss.precision(digits);
    for (const auto& p: m) {
        oss << std::get<0>(p) << sep << std::get<1>(p) << '\n';
    }
    return oss.str();
}

/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////
// std::string manipulation

inline std::vector<std::string> split_algorithm(const std::string& src, const std::string& delimiter=" \t\n") {
    std::vector<std::string> dst;
    for (auto delim_pos=begin(src), start=delim_pos; delim_pos!=end(src); ) {
        delim_pos = std::find_first_of(start, end(src), begin(delimiter), end(delimiter));
        dst.push_back(std::string(start, delim_pos));
        ++(start = delim_pos);
    }
    return dst;
}

inline std::vector<std::string>
split(const std::string& src, const std::string& delimiter=" \t\n") {
    std::vector<std::string> dst;
    size_t start = 0, offset = 0;
    while (true) {
        offset = src.find_first_of(delimiter, start);
        offset -= start;
        dst.push_back(src.substr(start, offset));
        start += offset;
        if (start == src.npos) break;
        ++start;
    }
    return dst;
}

inline std::string rstrip(std::string src, const std::string& chars=" ") {
    size_t pos(src.find_last_not_of(chars));
    if (pos == std::string::npos) {
        src.clear();
    } else {
        src.erase(++pos);
    }
    return src;
}

inline std::string lstrip(std::string src, const std::string& chars=" ") {
    const size_t pos(src.find_first_not_of(chars));
    if (pos == std::string::npos) {
        src.clear();
    } else {
        src.erase(0, pos);
    }
    return src;
}

inline std::string strip(std::string src, const std::string& chars=" ") {
    return rstrip(lstrip(src, chars), chars);
}

inline bool startswith(const std::string& full, const std::string& sub) {
    const size_t full_size = full.size();
    const size_t sub_size = sub.size();
    if (full_size > sub_size) {
        return full.compare(0, sub_size, sub) == 0;
    } else {
        return false;
    }
}

inline bool endswith(const std::string& full, const std::string& sub) {
    const size_t full_size = full.size();
    const size_t sub_size = sub.size();
    if (full_size > sub_size) {
        return full.compare(full_size - sub_size, sub_size, sub) == 0;
    } else {
        return false;
    }
}

inline std::string replace_all(const std::string& patt, const std::string& repl, const std::string& src) {
    std::string result;
    std::string::size_type pos_before(0);
    std::string::size_type pos(0);
    std::string::size_type len(patt.size());
    while ((pos = src.find(patt, pos)) != std::string::npos) {
        result.append(src, pos_before, pos-pos_before);
        result.append(repl);
        pos += len;
        pos_before = pos;
    }
    result.append(src, pos_before, src.size()-pos_before);
    return result;
}

/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////

inline std::string strprintf(const char* const format, ...) {
    assert(format);
    va_list args;
    std::string buffer;
    va_start(args, format);
    const int length = std::vsnprintf(nullptr, 0, format, args) ;
    va_end(args);
    if (length < 0) throw std::runtime_error(format);
    buffer.resize(length + 1);
    va_start(args, format);
    const int result = std::vsnprintf(&buffer[0], length + 1, format, args);
    va_end(args);
    if (result < 0) throw std::runtime_error(format);
    buffer.pop_back();
    return buffer;
}

/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////

// default is the same as ctime(): Thu Aug 23 14:55:02 2001
// which is equivalent to "%a %b %d %T %Y"
inline std::string strftime(const std::string& format="%c") {
    char cstr[80];
    const time_t rawtime = time(nullptr);
    const struct tm* t = localtime(&rawtime);
    std::strftime(cstr, sizeof(cstr), format.c_str(), t);
    return std::string(cstr);
}
inline std::string iso8601date(const std::string& sep="-") {
    std::ostringstream oss;
    oss << "%Y" << sep << "%m" << sep << "%d";
    return strftime(oss.str());
}
inline std::string iso8601time(const std::string& sep=":") {
    std::ostringstream oss;
    oss << "%H" << sep << "%M" << sep << "%S";
    return strftime(oss.str());
}
inline std::string iso8601datetime() {return strftime("%FT%T%z");}

/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////
// fstream wrapper for binary mode and exceptions
// boost::serialization requires binary mode

class Fin: public std::ifstream {
  public:
    explicit Fin(const std::string& filepath,
                 const std::ios::openmode mode=std::ios::in):
        std::ifstream(filepath.c_str(), mode | std::ios::binary)
    {exceptions(std::ios::badbit);}

    std::string readline(const char delimiter='\n') {
        std::string buffer;
        std::getline(*this, buffer, delimiter);
        return buffer;
    }

    std::vector<std::string> readlines(const char delimiter='\n') {
        std::vector<std::string> lines;
        std::string buffer;
        while (std::getline(*this, buffer, delimiter)) {
            lines.push_back(buffer);
        }
        return lines;
    }

    std::string read(const char delimiter='\0') {return readline(delimiter);}
};

class Fout: public std::ofstream {
  public:
    explicit Fout(const std::string& filepath,
                  const std::ios::openmode mode=std::ios::out):
        std::ofstream(filepath.c_str(), mode | std::ios::binary)
    {exceptions(std::ios::failbit);}

    template <class Iter>
    Fout& writelines(Iter begin_, const Iter end_, const char sep='\n') {
        if (begin_ == end_) return *this;
        *this << *begin_;
        while (++begin_ != end_) {*this << sep << *begin_;}
        return *this;
    }

    template <class V>
    Fout& writelines(const V& lines, const char sep='\n') {
        return writelines(begin(lines), end(lines), sep);
    }
};

inline std::vector<std::pair<std::string, std::string> >
read_ini(const std::string& filename) {
    std::vector<std::string> lines = Fin(filename).readlines();
    std::vector<std::pair<std::string, std::string> > dst;
    dst.reserve(lines.size());
    for (auto line_: lines) {
        line_ = strip(line_);
        if (startswith(line_, "[")) {continue;} // TODO
        auto pair_ = split(line_, ":="); // TODO
        if (pair_.size() < 2) {continue;}
        dst.emplace_back(rstrip(pair_[0]), lstrip(pair_[1]));
    }
    return dst;
}

/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////
} // namespace wtl
/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////

#endif /* IOSTR_HPP_ */
<|endoftext|>


<commit_before>#include "infer.hpp"
#include "ast.hpp"
#include "package.hpp"

#include <sstream>

namespace type {

  template<class Func>
  static void iter_rows(mono self, Func func) {
    assert(self.kind() == kind::row());
    self.match
      ([&](const app& self) {
        const auto e = extension::unpack(self);
        func(e.attr, e.head);
        iter_rows(e.tail, func);
      }, [](const mono& ) { });
  }

  struct let {
    const ::list<ast::bind> defs;
    const ast::expr body;

    static symbol fix() { return "__fix__"; }

    // rewrite let as non-recursive let + fix **when defining functions**
    static let rewrite(const ast::let& self) {
      using ::list;
      const list<ast::bind> defs = map(self.defs, [](ast::bind self) {
          return self.value.match<ast::bind>
          ([&](const ast::abs& abs) {
            return ast::bind{self.name,
                             ast::app{ast::var{fix()},
                                      ast::abs{self.name >>= list<ast::abs::arg>(),
                                               self.value}
                                      >>= list<ast::expr>()}};
          },
            [&](const ast::expr& expr) { return self; });
        });

      return {defs, *self.body};
    }
  };

  // rewrite app as nested unary applications
  static ast::app rewrite(const ast::app& self) {
    const ast::expr& init = *self.func;
    return foldl(init, self.args, [](ast::expr func, ast::expr arg) {
        return ast::app(func, arg >>= list<ast::expr>());
      }).cast<ast::app>();
  }

  // reconstruct actual type from reified type: ... -> (type 'a) yields 'a
  // note: t must be substituted
  static mono reconstruct(ref<state> s, mono t) {
    // std::clog << "reconstructing: " << s->generalize(t) << std::endl;

    if(auto self = t.get<app>()) {
      // std::clog << "ctor: " << s->generalize((*self)->ctor) << std::endl;

      if((*self)->ctor == ty) {
        return (*self)->arg;
      }
    }

    auto from = s->fresh();
    auto to = s->fresh();

    s->unify(from >>= to, t);
    return reconstruct(s, s->substitute(to));
  }

  // obtain constructor for a monotype
  static cst constructor(mono t) {
    if(auto self = t.get<app>()) {
      return constructor((*self)->ctor);
    }

    if(auto self = t.get<cst>()) {
      return *self;
    }

    // TODO restrictive?
    throw error("constructor must be a constant");
  }

  template<class T>
  static mono infer(const ref<T>& s, const T& self) {
    throw std::logic_error("infer unimplemented: " + tool::type_name(typeid(T)));
  }

  // var
  static mono infer(const ref<state>& s, const ast::var& self) {
    try {
      return s->instantiate(s->vars->find(self.name));
    } catch(std::out_of_range& e) {
      throw error("unbound variable " + tool::quote(self.name.get()));
    }
  }

  // abs
  static mono infer(const ref<state>& s, const ast::abs& self) {
    // function scope
    const auto sub = scope(s);

    // construct function type
    const mono result = s->fresh();

    const mono res = foldr(result, self.args, [&](ast::abs::arg arg, mono tail) {
        const mono sig = arg.match<mono>([&](symbol self) {
            // untyped arguments: trivial signature
            const mono a = sub->fresh();
            return a;
          },
          [&](ast::abs::typed self) {
            // obtain reified type from annoatation
            const mono t = infer(s, self.type);

            // extract actual type from reified
            const mono r = reconstruct(s, s->substitute(t));

            // obtain type constructor from type
              const cst c = constructor(r);

              try {
                // fetch associated signature, if any
                const auto sig = s->sigs->at(c);

                // instantiate signature
                return sub->instantiate(sig);

              } catch(std::out_of_range&) {
                throw error("unknown signature " + tool::quote(c->name.get()));
              }
          });

        const mono outer = s->fresh();
        const mono inner = sub->fresh();

        sub->unify(outer >>= inner, sig);
        sub->def(arg.name(), outer);

        // std::clog << "inner: " << sub->vars->find(arg.name()) << std::endl;

        return outer >>= tail;
      });

      // infer lambda body with augmented environment
      s->unify(result, infer(sub, *self.body));

      return res;
    }

  // app
  static mono infer(const ref<state>& s, const ast::app& self) {
    // normalize application as unary
    const ast::app rw = rewrite(self);
    assert(size(rw.args) == 1);

    // infer func/arg types for application
    const auto with_inferred = [&](auto cont) {
      const mono func = infer(s, *rw.func);
      const mono arg = infer(s, rw.args->head);

      return cont(func, arg);
    };

    // obtain inner type from a type with associated signature
    const auto inner_type = [&](mono t) {
      // obtain type constructor from argument type
      const cst c = constructor(s->substitute(t));

      try {
        auto sig = s->sigs->at(c);

        const mono inner = s->fresh();
        s->unify(t >>= inner, s->instantiate(sig));

        return inner;
      } catch(std::out_of_range&) {
        throw error("unknown signature");
      }
    };

    // check if application works with given func/arg type
    const auto check = [&](mono func, mono arg) {
      const mono ret = s->fresh();
      s->unify(func , arg >>= ret);
      return ret;
    };

    // TODO find a less stupid way of trying all cases?
    try {
      // normal case
      return with_inferred([&](mono func, mono arg) {
          return check(func, arg);
        });

    } catch(error& e) {

      try {
        // open func type and retry
        return with_inferred([&](mono func, mono arg) {
            const mono inner_func = inner_type(func);
            return check(inner_func, arg);
          });
      }
      catch(error& ) {  }

      try {
        // open arg type and retry
        return with_inferred([&](mono func, mono arg) {
            const mono inner_arg = inner_type(arg);
            return check(func, inner_arg);
          });
      }
      catch(error& ) {  }

      try {
        // open both func and arg types and retry
        return with_inferred([&](mono func, mono arg) {
            const mono inner_func = inner_type(func);
            const mono inner_arg = inner_type(arg);
            return check(inner_func, inner_arg);
          });
      }
      catch(error& ) {  }

      throw e;
    }

  }

  // non-recursive let
  static mono infer(const ref<state>& s, const let& self) {
    auto sub = scope(s);

    for(ast::bind def : self.defs) {
      sub->vars->locals.emplace(def.name, s->generalize(infer(s, def.value)));
    }

    return infer(sub, self.body);
  }

  // recursive let
  static mono infer(const ref<state>& s, const ast::let& self) {
    auto sub = scope(s);

    const mono a = sub->fresh();
    sub->def(let::fix(), (a >>= a) >>= a);

    return infer(sub, let::rewrite(self));
  }

  // sel
  static mono infer(const ref<state>& s, const ast::sel& self) {
    const mono tail = s->fresh(kind::row());
    const mono head = s->fresh(kind::term());

    const mono row = ext(self.name)(head)(tail);
    return record(row) >>= head;
  }

  // record
  static mono infer(const ref<state>& s, const ast::record& self) {
    const mono init = empty;
    const mono row = foldr(init, self.attrs, [&](ast::record::attr attr, mono tail) {
        return ext(attr.name)(infer(s, attr.value))(tail);
      });

    return record(row);
  }

  // make
  static mono infer(const ref<state>& s, const ast::make& self) {
    // get signature type
    const poly sig = s->vars->find(self.type);

    auto sub = scope(s);

    const mono outer = s->fresh();
    const mono inner = sub->fresh();

    // instantiate signature at sub level prevents generalization of
    // contravariant side (variables only appearing in the covariant side will
    // be generalized)
    s->unify(ty(outer) >>= ty(inner), sub->instantiate(sig));

    // vanilla covariant type
    const poly reference = sub->generalize(inner);

    // build provided type
    const mono init = empty;
    const mono provided =
      record(foldr(init, self.attrs,
                   [&](const ast::record::attr attr, mono tail) {
                     return row(attr.name, infer(sub, attr.value)) |= tail;
                   }));

    // now also unify inner with provided type
    s->unify(inner, provided);

    // now generalize the provided type
    const poly gen = sub->generalize(inner);

    // generalization check: quantified variables in reference/gen should
    // substitute to the same variables
    std::set<var> quantified;
    for(const var& v : gen.forall) {
      assert(sub->substitute(v) == v);
      quantified.insert(v);
    }

    // make sure all reference quantified references substitute to quantified
    // variables
    for(const var& v : reference.forall) {
      const mono vs = sub->substitute(v);
      if(auto u = vs.get<var>()) {
        auto it = quantified.find(*u);
        if(it != quantified.end()) {
          continue;
        }
      }

      std::stringstream ss;
      logger(ss) << "failed to generalize " << gen
                 << " as " << reference;

      throw error(ss.str());
    }

    return outer;
  }

  // cond
  static mono infer(const ref<state>& s, const ast::cond& self) {
    const mono test = infer(s, *self.test);
    s->unify(test, boolean);

    const mono conseq = infer(s, *self.conseq);
    const mono alt = infer(s, *self.alt);

    const mono result = s->fresh();
    s->unify(result, conseq);
    s->unify(result, alt);

    return result;
  }

  // def
  static mono infer(const ref<state>& s, const ast::def& self) {
    const mono value =
      infer(s, ast::let(ast::bind{self.name, *self.value} >>= list<ast::bind>(),
                        ast::var{self.name}));
    try {
      s->def(self.name, value);
      return io(unit);
    } catch(std::runtime_error& e) {
      throw error(e.what());
    }
  }

  // use
  static mono infer(const ref<state>& s, const ast::use& self) {
    // infer value type
    const mono value = infer(s, *self.env);

    // make sure value type is a record
    const mono row = s->fresh(kind::row());
    s->unify(value, record(row));

    auto sub = scope(s);

    // fill sub scope with record contents
    iter_rows(s->substitute(row), [&](symbol attr, mono t) {
        // TODO generalization issue?
        sub->def(attr, t);
      });

    return infer(sub, *self.body);
  }

  // import
  static mono infer(const ref<state>& s, const ast::import& self) {
    auto it = s->vars->locals.find(self.package);
    if(it != s->vars->locals.end()) {
      throw error("variable " + tool::quote(self.package.get()) + " already defined");
    }

    const package pkg = package::import(self.package);

    s->vars->locals.emplace(self.package, pkg.sig());
    return io(unit);
  }

  // lit
  static mono infer(const ref<state>&, const ast::lit<::boolean>& self) {
    return boolean;
  }

  static mono infer(const ref<state>&, const ast::lit<::integer>& self) {
    return integer;
  }

  static mono infer(const ref<state>&, const ast::lit<::real>& self) {
    return real;
  }

  struct infer_visitor {
    using type = mono;

    template<class T>
    mono operator()(const T& self, const ref<state>& s) const {
      return infer(s, self);
    }

  };

  mono infer(const ref<state>& s, const ast::expr& self) {
    return self.visit(infer_visitor(), s);
  }

}
<commit_msg>cleaner + fixed bug<commit_after>#include "infer.hpp"
#include "ast.hpp"
#include "package.hpp"

#include <sstream>

namespace type {

  // try to open type `self` from signatures in `s`
  static mono open(const ref<state>& s, const mono& self);

  template<class Func>
  static void iter_rows(mono self, Func func) {
    assert(self.kind() == kind::row());
    self.match
      ([&](const app& self) {
        const auto e = extension::unpack(self);
        func(e.attr, e.head);
        iter_rows(e.tail, func);
      }, [](const mono& ) { });
  }

  struct let {
    const ::list<ast::bind> defs;
    const ast::expr body;

    static symbol fix() { return "__fix__"; }

    // rewrite let as non-recursive let + fix **when defining functions**
    static let rewrite(const ast::let& self) {
      using ::list;
      const list<ast::bind> defs = map(self.defs, [](ast::bind self) {
          return self.value.match<ast::bind>
          ([&](const ast::abs& abs) {
            return ast::bind{self.name,
                             ast::app{ast::var{fix()},
                                      ast::abs{self.name >>= list<ast::abs::arg>(),
                                               self.value}
                                      >>= list<ast::expr>()}};
          },
            [&](const ast::expr& expr) { return self; });
        });

      return {defs, *self.body};
    }
  };

  // rewrite app as nested unary applications
  static ast::app rewrite(const ast::app& self) {
    const ast::expr& init = *self.func;
    return foldl(init, self.args, [](ast::expr func, ast::expr arg) {
        return ast::app(func, arg >>= list<ast::expr>());
      }).cast<ast::app>();
  }

  // reconstruct actual type from reified type: ... -> (type 'a) yields 'a
  // note: t must be substituted
  static mono reconstruct(ref<state> s, mono t) {
    // std::clog << "reconstructing: " << s->generalize(t) << std::endl;

    if(auto self = t.get<app>()) {
      // std::clog << "ctor: " << s->generalize((*self)->ctor) << std::endl;

      if((*self)->ctor == ty) {
        return (*self)->arg;
      }
    }

    auto from = s->fresh();
    auto to = s->fresh();

    s->unify(from >>= to, t);
    return reconstruct(s, s->substitute(to));
  }

  // obtain constructor for a monotype
  static cst constructor(mono t) {
    if(auto self = t.get<app>()) {
      return constructor((*self)->ctor);
    }

    if(auto self = t.get<cst>()) {
      return *self;
    }

    // TODO restrictive?
    throw error("constructor must be a constant");
  }

  // try to open type `self` from signatures in `s`
  static mono open(const ref<state>& s, const mono& self) {
    // obtain type constructor from argument type
    const cst c = constructor(s->substitute(self));

    try {
      auto sig = s->sigs->at(c);

      const mono inner = s->fresh();
      s->unify(self >>= inner, s->instantiate(sig));

      return inner;
    } catch(std::out_of_range&) {
      std::stringstream ss;
      ss << "constructor " << tool::quote(c->name.get()) << " has not associated signature";
      throw error(ss.str());
    }
  }

  template<class T>
  static mono infer(const ref<T>& s, const T& self) {
    throw std::logic_error("infer unimplemented: " + tool::type_name(typeid(T)));
  }

  // var
  static mono infer(const ref<state>& s, const ast::var& self) {
    try {
      return s->instantiate(s->vars->find(self.name));
    } catch(std::out_of_range& e) {
      throw error("unbound variable " + tool::quote(self.name.get()));
    }
  }

  // abs
  static mono infer(const ref<state>& s, const ast::abs& self) {
    // function scope
    const auto sub = scope(s);

    // construct function type
    const mono result = s->fresh();

    const mono res = foldr(result, self.args, [&](ast::abs::arg arg, mono tail) {
        const mono type = arg.match<mono>([&](symbol self) {
            return s->fresh();
          },
          [&](ast::abs::typed self) {
            // obtain reified type from annoatation
            const mono reified = infer(s, self.type);

            // TODO do we need gen/inst here?

            // extract concrete type from reified type
            const mono concrete = reconstruct(s, s->substitute(reified));

            return concrete;
          });

        sub->def(arg.name(), type);
        return type >>= tail;
      });

    // infer lambda body with augmented environment
    s->unify(result, infer(sub, *self.body));

    return res;
  }

  // app
  static mono infer(const ref<state>& s, const ast::app& self) {
    // normalize application as unary
    const ast::app rw = rewrite(self);
    assert(size(rw.args) == 1);

    // infer func/arg types for application
    const auto with_inferred = [&](auto cont) {
      const mono func = infer(s, *rw.func);
      const mono arg = infer(s, rw.args->head);

      return cont(func, arg);
    };

    // check if application works with given func/arg type
    const auto check = [&](mono func, mono arg) {
      const mono ret = s->fresh();
      s->unify(func , arg >>= ret);
      return ret;
    };

    // TODO find a less stupid way of trying all cases?
    try {
      // normal case
      return with_inferred([&](mono func, mono arg) {
          return check(func, arg);
        });

    } catch(error& e) {

      try {
        // open func type and retry
        return with_inferred([&](mono func, mono arg) {
            return check(open(s, func), arg);
          });
      }
      catch(error& ) {  }

      try {
        // open arg type and retry
        return with_inferred([&](mono func, mono arg) {
            return check(func, open(s, arg));
          });
      }
      catch(error& ) {  }

      try {
        // open both func and arg types and retry
        return with_inferred([&](mono func, mono arg) {
            return check(open(s, func), open(s, arg));
          });
      }
      catch(error& ) {  }

      throw e;
    }

  }

  // non-recursive let
  static mono infer(const ref<state>& s, const let& self) {
    auto sub = scope(s);

    for(ast::bind def : self.defs) {
      sub->vars->locals.emplace(def.name, s->generalize(infer(s, def.value)));
    }

    return infer(sub, self.body);
  }

  // recursive let
  static mono infer(const ref<state>& s, const ast::let& self) {
    auto sub = scope(s);

    const mono a = sub->fresh();
    sub->def(let::fix(), (a >>= a) >>= a);

    return infer(sub, let::rewrite(self));
  }

  // sel
  static mono infer(const ref<state>& s, const ast::sel& self) {
    const mono tail = s->fresh(kind::row());
    const mono head = s->fresh(kind::term());

    const mono row = ext(self.name)(head)(tail);
    return record(row) >>= head;
  }

  // record
  static mono infer(const ref<state>& s, const ast::record& self) {
    const mono init = empty;
    const mono row = foldr(init, self.attrs, [&](ast::record::attr attr, mono tail) {
        return ext(attr.name)(infer(s, attr.value))(tail);
      });

    return record(row);
  }

  // make
  static mono infer(const ref<state>& s, const ast::make& self) {
    // get signature type
    const poly sig = s->vars->find(self.type);

    auto sub = scope(s);

    const mono outer = s->fresh();
    const mono inner = sub->fresh();

    // instantiate signature at sub level prevents generalization of
    // contravariant side (variables only appearing in the covariant side will
    // be generalized)
    s->unify(ty(outer) >>= ty(inner), sub->instantiate(sig));

    // vanilla covariant type
    const poly reference = sub->generalize(inner);

    // build provided type
    const mono init = empty;
    const mono provided =
      record(foldr(init, self.attrs,
                   [&](const ast::record::attr attr, mono tail) {
                     return row(attr.name, infer(sub, attr.value)) |= tail;
                   }));

    // now also unify inner with provided type
    s->unify(inner, provided);

    // now generalize the provided type
    const poly gen = sub->generalize(inner);

    // generalization check: quantified variables in reference/gen should
    // substitute to the same variables
    std::set<var> quantified;
    for(const var& v : gen.forall) {
      assert(sub->substitute(v) == v);
      quantified.insert(v);
    }

    // make sure all reference quantified references substitute to quantified
    // variables
    for(const var& v : reference.forall) {
      const mono vs = sub->substitute(v);
      if(auto u = vs.get<var>()) {
        auto it = quantified.find(*u);
        if(it != quantified.end()) {
          continue;
        }
      }

      std::stringstream ss;
      logger(ss) << "failed to generalize " << gen
                 << " as " << reference;

      throw error(ss.str());
    }

    return outer;
  }

  // cond
  static mono infer(const ref<state>& s, const ast::cond& self) {
    const mono test = infer(s, *self.test);
    s->unify(test, boolean);

    const mono conseq = infer(s, *self.conseq);
    const mono alt = infer(s, *self.alt);

    const mono result = s->fresh();
    s->unify(result, conseq);
    s->unify(result, alt);

    return result;
  }

  // def
  static mono infer(const ref<state>& s, const ast::def& self) {
    const mono value =
      infer(s, ast::let(ast::bind{self.name, *self.value} >>= list<ast::bind>(),
                        ast::var{self.name}));
    try {
      s->def(self.name, value);
      return io(unit);
    } catch(std::runtime_error& e) {
      throw error(e.what());
    }
  }

  // use
  static mono infer(const ref<state>& s, const ast::use& self) {
    // infer value type
    const mono value = infer(s, *self.env);

    // make sure value type is a record
    const mono row = s->fresh(kind::row());
    s->unify(value, record(row));

    auto sub = scope(s);

    // fill sub scope with record contents
    iter_rows(s->substitute(row), [&](symbol attr, mono t) {
        // TODO generalization issue?
        sub->def(attr, t);
      });

    return infer(sub, *self.body);
  }

  // import
  static mono infer(const ref<state>& s, const ast::import& self) {
    auto it = s->vars->locals.find(self.package);
    if(it != s->vars->locals.end()) {
      throw error("variable " + tool::quote(self.package.get()) + " already defined");
    }

    const package pkg = package::import(self.package);

    s->vars->locals.emplace(self.package, pkg.sig());
    return io(unit);
  }

  // lit
  static mono infer(const ref<state>&, const ast::lit<::boolean>& self) {
    return boolean;
  }

  static mono infer(const ref<state>&, const ast::lit<::integer>& self) {
    return integer;
  }

  static mono infer(const ref<state>&, const ast::lit<::real>& self) {
    return real;
  }

  struct infer_visitor {
    using type = mono;

    template<class T>
    mono operator()(const T& self, const ref<state>& s) const {
      return infer(s, self);
    }

  };

  mono infer(const ref<state>& s, const ast::expr& self) {
    return self.visit(infer_visitor(), s);
  }

}
<|endoftext|>


<commit_before>/*********************************************************************
** Author: Zach Colbert
** Date: 30 November 2017
** Description: Library class
*********************************************************************/

/*********************************************************************
** Includes
*********************************************************************/

#include "Library.hpp"

/*********************************************************************
** Description:
*********************************************************************/
Library::Library()
{
    // Initialize the relative date of the library
    currentDate = 0;

}

/*********************************************************************
** Description:
*********************************************************************/
void Library::addBook(Book* newBook)
{
    // vector::push_back will add Book* to the end of the vector
    holdings.push_back(newBook);
}

/*********************************************************************
** Description:
*********************************************************************/
void Library::addPatron(Patron* newPatron)
{
    // vector::push_back will add Patron* to the end of the vector
    members.push_back(newPatron);
}

/*********************************************************************
** Description:
*********************************************************************/
std::string Library::checkOutBook(std::string pID, std::string bID)
{
    // if the specified Book is not in the Library, return "book not found"

    Book* chkBook = getBook(bID);

    if (chkBook == nullptr)
    {
        return "book not found";
    }

    // if the specified Patron is not in the Library, return "patron not found"

    Patron* chkPatron = getPatron(pID);

    if(chkPatron == nullptr)
    {
        return "patron not found";
    }

    // if the specified Book is already checked out, return "book already checked out"
    if ((*chkBook).getCheckedOutBy() != nullptr)
    {
        return "book already checked out";
    }

    // if the specified Book is on hold by another Patron, return "book on hold by other patron"
    if ((*chkBook).getRequestedBy() != nullptr)
    {
        return "book on hold by other patron";
    }

    // otherwise update the Book's checkedOutBy, dateCheckedOut and Location; if the Book was on hold for this Patron, update requestedBy; update the Patron's checkedOutBooks; return "check out successful"
    (*chkBook).setCheckedOutBy(chkPatron);
    (*chkBook).setDateCheckedOut(currentDate);
    (*chkBook).setLocation(CHECKED_OUT);

    if ((*chkBook).getRequestedBy() == (*chkBook).getCheckedOutBy())
    {
        (*chkBook).setRequestedBy(nullptr);
    }

    (*chkPatron).addBook(chkBook);

    return "check out successful";

}

/*********************************************************************
** Description:
*********************************************************************/
std::string Library::returnBook(std::string bID)
{
    // if the specified Book is not in the Library, return "book not found"
    Book* chkBook = getBook(bID);

    if (chkBook == nullptr)
    {
        return "book not found";
    }

    // if the Book is not checked out, return "book already in library"
    if ((*chkBook).getCheckedOutBy() == nullptr)
    {
        return "book already in library";
    }

    // update the Patron's checkedOutBooks; update the Book's location depending on whether another Patron has requested it; update the Book's checkedOutBy; return "return successful"
    (*(*chkBook).getCheckedOutBy()).removeBook(chkBook);

    // If the book is not on hold, location goes to ON_SHELF
    if ((*chkBook).getRequestedBy() == nullptr)
    {
        (*chkBook).setLocation(ON_SHELF);
    }
    // Otherwise, it is on hold and goes to ON_HOLD_SHELF
    else
    {
        (*chkBook).setLocation(ON_HOLD_SHELF);
    }

    (*chkBook).setCheckedOutBy(nullptr);

    return "return successful";

}

/*********************************************************************
** Description:
*********************************************************************/
std::string Library::requestBook(std::string pID, std::string bID)
{
    // if the specified Book is not in the Library, return "book not found"
    Book* chkBook = getBook(bID);

    if (chkBook == nullptr)
    {
        return "book not found";
    }

    // if the specified Patron is not in the Library, return "patron not found"
    Patron* chkPatron = getPatron(pID);

    if (chkPatron == nullptr)
    {
        return "patron not found";
    }

    // if the specified Book is already requested, return "book already on hold"
    if ((*chkBook).getRequestedBy() != nullptr)
    {
        return "book already on hold";
    }

    // update the Book's requestedBy; if the Book is on the shelf, update its location to on hold; return "request successful"
    (*chkBook).setRequestedBy(chkPatron);

    if ((*chkBook).getLocation() == ON_SHELF)
    {
        (*chkBook).setLocation(ON_HOLD_SHELF);
    }

}

/*********************************************************************
** Description:
*********************************************************************/
std::string Library::payFine(std::string pID, double payment)
{
    // takes as a parameter the amount that is being paid (not the negative of that amount)
    if (payment < 0)
    {
        return "invalid payment amount";
    }

    // if the specified Patron is not in the Library, return "patron not found"
    Patron* chkPatron = getPatron(pID);

    if (chkPatron == nullptr)
    {
        return "patron not found";
    }

    // use amendFine to update the Patron's fine; return "payment successful"
    (*chkPatron).amendFine(payment);

    return "payment successful";

}

/*********************************************************************
** Description:
*********************************************************************/
void Library::incrementCurrentDate()
{
    currentDate++;
}

// Description
Patron* Library::getPatron(std::string pID)
{
    bool patronFound = false;
    int patronIndex = -1;

    for (Patron* chkPatron : members)
    {
        patronIndex++;
        if ((*chkPatron).getIdNum() == pID)
        {
            patronFound = true;
            break;
        }
    }

    if (patronFound == false)
    {
        return nullptr;
    }
    else
    {
        return members[patronIndex];
    }
}

// Description
Book* Library::getBook(std::string bID)
{
    bool bookFound = false;
    int bookIndex = -1;

    for (Book* chkBook : holdings)
    {
        bookIndex++;
        if ((*chkBook).getIdCode() == bID)
        {
            bookFound = true;
            break;
        }
    }

    if (bookFound == false)
    {
        return nullptr;
    }
    else
    {
        return holdings[bookIndex];
    }
}<commit_msg>Second draft<commit_after>/*********************************************************************
** Author: Zach Colbert
** Date: 30 November 2017
** Description: Library class
*********************************************************************/

/*********************************************************************
** Includes
*********************************************************************/

#include "Library.hpp"

/*********************************************************************
** Description:
*********************************************************************/
Library::Library()
{
    // Initialize the relative date of the library
    currentDate = 0;

}

/*********************************************************************
** Description:
*********************************************************************/
void Library::addBook(Book* newBook)
{
    // vector::push_back will add Book* to the end of the vector
    holdings.push_back(newBook);
}

/*********************************************************************
** Description:
*********************************************************************/
void Library::addPatron(Patron* newPatron)
{
    // vector::push_back will add Patron* to the end of the vector
    members.push_back(newPatron);
}

/*********************************************************************
** Description:
*********************************************************************/
std::string Library::checkOutBook(std::string pID, std::string bID)
{
    // if the specified Book is not in the Library, return "book not found"

    Book* chkBook = getBook(bID);

    if (chkBook == nullptr)
    {
        return "book not found";
    }

    // if the specified Patron is not in the Library, return "patron not found"

    Patron* chkPatron = getPatron(pID);

    if(chkPatron == nullptr)
    {
        return "patron not found";
    }

    // if the specified Book is already checked out, return "book already checked out"
    if ((*chkBook).getCheckedOutBy() != nullptr)
    {
        return "book already checked out";
    }

    // if the specified Book is on hold by another Patron, return "book on hold by other patron"
    if ((*chkBook).getRequestedBy() != nullptr)
    {
        return "book on hold by other patron";
    }

    // otherwise update the Book's checkedOutBy, dateCheckedOut and Location; if the Book was on hold for this Patron, update requestedBy; update the Patron's checkedOutBooks; return "check out successful"
    (*chkBook).setCheckedOutBy(chkPatron);
    (*chkBook).setDateCheckedOut(currentDate);
    (*chkBook).setLocation(CHECKED_OUT);

    if ((*chkBook).getRequestedBy() == (*chkBook).getCheckedOutBy())
    {
        (*chkBook).setRequestedBy(nullptr);
    }

    (*chkPatron).addBook(chkBook);

    return "check out successful";

}

/*********************************************************************
** Description:
*********************************************************************/
std::string Library::returnBook(std::string bID)
{
    // if the specified Book is not in the Library, return "book not found"
    Book* chkBook = getBook(bID);

    if (chkBook == nullptr)
    {
        return "book not found";
    }

    // if the Book is not checked out, return "book already in library"
    if ((*chkBook).getCheckedOutBy() == nullptr)
    {
        return "book already in library";
    }

    // update the Patron's checkedOutBooks; update the Book's location depending on whether another Patron has requested it; update the Book's checkedOutBy; return "return successful"
    (*(*chkBook).getCheckedOutBy()).removeBook(chkBook);

    // If the book is not on hold, location goes to ON_SHELF
    if ((*chkBook).getRequestedBy() == nullptr)
    {
        (*chkBook).setLocation(ON_SHELF);
    }
    // Otherwise, it is on hold and goes to ON_HOLD_SHELF
    else
    {
        (*chkBook).setLocation(ON_HOLD_SHELF);
    }

    (*chkBook).setCheckedOutBy(nullptr);

    return "return successful";

}

/*********************************************************************
** Description:
*********************************************************************/
std::string Library::requestBook(std::string pID, std::string bID)
{
    // if the specified Book is not in the Library, return "book not found"
    Book* chkBook = getBook(bID);

    if (chkBook == nullptr)
    {
        return "book not found";
    }

    // if the specified Patron is not in the Library, return "patron not found"
    Patron* chkPatron = getPatron(pID);

    if (chkPatron == nullptr)
    {
        return "patron not found";
    }

    // if the specified Book is already requested, return "book already on hold"
    if ((*chkBook).getRequestedBy() != nullptr)
    {
        return "book already on hold";
    }

    // update the Book's requestedBy; if the Book is on the shelf, update its location to on hold; return "request successful"
    (*chkBook).setRequestedBy(chkPatron);

    if ((*chkBook).getLocation() == ON_SHELF)
    {
        (*chkBook).setLocation(ON_HOLD_SHELF);
    }

}

/*********************************************************************
** Description:
*********************************************************************/
std::string Library::payFine(std::string pID, double payment)
{
    // takes as a parameter the amount that is being paid (not the negative of that amount)
    if (payment < 0)
    {
        return "invalid payment amount";
    }

    // if the specified Patron is not in the Library, return "patron not found"
    Patron* chkPatron = getPatron(pID);

    if (chkPatron == nullptr)
    {
        return "patron not found";
    }

    // use amendFine to update the Patron's fine; return "payment successful"
    (*chkPatron).amendFine(-1.0 * payment);

    return "payment successful";

}

/*********************************************************************
** Description:
*********************************************************************/
void Library::incrementCurrentDate()
{
    currentDate++;
}

// Description
Patron* Library::getPatron(std::string pID)
{
    bool patronFound = false;
    int patronIndex = -1;

    for (Patron* chkPatron : members)
    {
        patronIndex++;
        if ((*chkPatron).getIdNum() == pID)
        {
            patronFound = true;
            break;
        }
    }

    if (patronFound == false)
    {
        return nullptr;
    }
    else
    {
        return members[patronIndex];
    }
}

// Description
Book* Library::getBook(std::string bID)
{
    bool bookFound = false;
    int bookIndex = -1;

    for (Book* chkBook : holdings)
    {
        bookIndex++;
        if ((*chkBook).getIdCode() == bID)
        {
            bookFound = true;
            break;
        }
    }

    if (bookFound == false)
    {
        return nullptr;
    }
    else
    {
        return holdings[bookIndex];
    }
}<|endoftext|>


<commit_before>/*
 * SuiPageData.cpp
 *
 *  Created on: Nov 7, 2013
 *      Author: crush
 */

#include "SuiPageData.h"
#include "SuiCommand.h"
#include "server/zone/ZoneClientSession.h"
#include "server/zone/packets/ui/SuiCreatePageMessage.h"
#include "server/zone/packets/ui/SuiUpdatePageMessage.h"
#include "server/zone/objects/player/PlayerObject.h"

void SuiPageData::setProperty(const String& widget, const String& property, const UnicodeString& value) {
	SuiCommand* command = new SuiCommand(SuiCommand::SCT_setProperty);
	command->addWideParameter(value);
	command->addNarrowParameter(widget);
	command->addNarrowParameter(property);

	commands.add(command);
}

void SuiPageData::addDataItem(const String& widget, const String& property, const UnicodeString& value) {
	SuiCommand* command = new SuiCommand(SuiCommand::SCT_addDataItem);
	command->addWideParameter(value);
	command->addNarrowParameter(widget);
	command->addNarrowParameter(property);

	commands.add(command);
}

void SuiPageData::clearDataSource(const String& dataSource) {
	SuiCommand* command = new SuiCommand(SuiCommand::SCT_clearDataSource);
	command->addNarrowParameter(dataSource);

	commands.add(command);
}

void SuiPageData::addDataSourceContainer(const String& parent, const String& name) {
	SuiCommand* command = new SuiCommand(SuiCommand::SCT_addDataSourceContainer);
	command->addNarrowParameter(parent);
	command->addNarrowParameter(name);

	commands.add(command);
}

void SuiPageData::clearDataSourceContainer(const String& dataSourceContainer) {
	SuiCommand* command = new SuiCommand(SuiCommand::SCT_clearDataSourceContainer);
	command->addNarrowParameter(dataSourceContainer);

	commands.add(command);
}

void SuiPageData::addChildWidget(const String& parent, const String& type, const String& name) {
	SuiCommand* command = new SuiCommand(SuiCommand::SCT_addChildWidget);
	command->addNarrowParameter(parent);
	command->addNarrowParameter(type);
	command->addNarrowParameter(name);

	commands.add(command);
}

void SuiPageData::subscribeToEvent(const byte& eventType, const String& parent, const String& callback) {
	if (callbacks.contains(eventType)) {
		warning("SuiPageData::addCommand attempt to add duplicate SCT_subscribeToEvent command.");
		return;
	}

	SuiCommand* command = new SuiCommand(SuiCommand::SCT_subscribeToEvent);
	command->addNarrowParameter(parent);

    StringBuffer eventTypeBuffer;
    eventTypeBuffer.append((char)eventType);

    command->addNarrowParameter(eventTypeBuffer.toString());
	command->addNarrowParameter(callback);

	callbacks.put(eventType, command);
	commands.add(command);
}

void SuiPageData::subscribeToPropertyForEvent(const byte& eventType, const String& widget, const String& property) {
	Reference<SuiCommand*> command = callbacks.get(eventType);

	if (command == NULL) {
		warning("Attempted to add properties for event without subscribing a callback first.");
		return;
	}

	command->addNarrowParameter(widget);
	command->addNarrowParameter(property);
}

bool SuiPageData::toBinaryStream(ObjectOutputStream* stream) {
	stream->writeInt(id);
	rootPage.toBinaryStream(stream);
	stream->writeInt(commands.size());

	for (int i = 0; i < commands.size(); ++i)
		commands.get(i)->toBinaryStream(stream);

	stream->writeLong(targetNetworkId);
	stream->writeFloat(forceCloseDistance);
	stream->writeLong(unknownNetworkId);

	return true;
}

bool SuiPageData::parseFromBinaryStream(ObjectInputStream* stream) {
	id = stream->readInt();
	rootPage.parseFromBinaryStream(stream);

	int size = stream->readInt();

	for (int i = 0; i < size; ++i) {
		SuiCommand* command = new SuiCommand();
		command->parseFromBinaryStream(stream);

		if (command->getCommandType() == SuiCommand::SCT_subscribeToEvent) {
			//callbacks.put(); //TODO: Register subscribes again...
		}

		commands.add(command);
	}

	targetNetworkId = stream->readLong();
	forceCloseDistance = stream->readFloat();
	unknownNetworkId = stream->readLong();

	return true;
}

void SuiPageData::sendTo(CreatureObject* creo) {
	PlayerObject* playerObject = creo->getPlayerObject();

	if (playerObject != NULL) {
		creo->getClient()->sendMessage(new SuiCreatePageMessage(this));
	}
}

void SuiPageData::sendUpdateTo(CreatureObject* creo) {
	PlayerObject* playerObject = creo->getPlayerObject();

	if (playerObject != NULL) {
		creo->getClient()->sendMessage(new SuiUpdatePageMessage(this));
	}
}
<commit_msg>[Fixed] stability issue<commit_after>/*
 * SuiPageData.cpp
 *
 *  Created on: Nov 7, 2013
 *      Author: crush
 */

#include "SuiPageData.h"
#include "SuiCommand.h"
#include "server/zone/ZoneClientSession.h"
#include "server/zone/packets/ui/SuiCreatePageMessage.h"
#include "server/zone/packets/ui/SuiUpdatePageMessage.h"
#include "server/zone/objects/player/PlayerObject.h"

void SuiPageData::setProperty(const String& widget, const String& property, const UnicodeString& value) {
	SuiCommand* command = new SuiCommand(SuiCommand::SCT_setProperty);
	command->addWideParameter(value);
	command->addNarrowParameter(widget);
	command->addNarrowParameter(property);

	commands.add(command);
}

void SuiPageData::addDataItem(const String& widget, const String& property, const UnicodeString& value) {
	SuiCommand* command = new SuiCommand(SuiCommand::SCT_addDataItem);
	command->addWideParameter(value);
	command->addNarrowParameter(widget);
	command->addNarrowParameter(property);

	commands.add(command);
}

void SuiPageData::clearDataSource(const String& dataSource) {
	SuiCommand* command = new SuiCommand(SuiCommand::SCT_clearDataSource);
	command->addNarrowParameter(dataSource);

	commands.add(command);
}

void SuiPageData::addDataSourceContainer(const String& parent, const String& name) {
	SuiCommand* command = new SuiCommand(SuiCommand::SCT_addDataSourceContainer);
	command->addNarrowParameter(parent);
	command->addNarrowParameter(name);

	commands.add(command);
}

void SuiPageData::clearDataSourceContainer(const String& dataSourceContainer) {
	SuiCommand* command = new SuiCommand(SuiCommand::SCT_clearDataSourceContainer);
	command->addNarrowParameter(dataSourceContainer);

	commands.add(command);
}

void SuiPageData::addChildWidget(const String& parent, const String& type, const String& name) {
	SuiCommand* command = new SuiCommand(SuiCommand::SCT_addChildWidget);
	command->addNarrowParameter(parent);
	command->addNarrowParameter(type);
	command->addNarrowParameter(name);

	commands.add(command);
}

void SuiPageData::subscribeToEvent(const byte& eventType, const String& parent, const String& callback) {
	if (callbacks.contains(eventType)) {
		warning("SuiPageData::addCommand attempt to add duplicate SCT_subscribeToEvent command.");
		return;
	}

	SuiCommand* command = new SuiCommand(SuiCommand::SCT_subscribeToEvent);
	command->addNarrowParameter(parent);

    StringBuffer eventTypeBuffer;
    eventTypeBuffer.append((char)eventType);

    command->addNarrowParameter(eventTypeBuffer.toString());
	command->addNarrowParameter(callback);

	callbacks.put(eventType, command);
	commands.add(command);
}

void SuiPageData::subscribeToPropertyForEvent(const byte& eventType, const String& widget, const String& property) {
	Reference<SuiCommand*> command = callbacks.get(eventType);

	if (command == NULL) {
		warning("Attempted to add properties for event without subscribing a callback first.");
		return;
	}

	command->addNarrowParameter(widget);
	command->addNarrowParameter(property);
}

bool SuiPageData::toBinaryStream(ObjectOutputStream* stream) {
	stream->writeInt(id);
	rootPage.toBinaryStream(stream);
	stream->writeInt(commands.size());

	for (int i = 0; i < commands.size(); ++i)
		commands.get(i)->toBinaryStream(stream);

	stream->writeLong(targetNetworkId);
	stream->writeFloat(forceCloseDistance);
	stream->writeLong(unknownNetworkId);

	return true;
}

bool SuiPageData::parseFromBinaryStream(ObjectInputStream* stream) {
	id = stream->readInt();
	rootPage.parseFromBinaryStream(stream);

	int size = stream->readInt();

	for (int i = 0; i < size; ++i) {
		SuiCommand* command = new SuiCommand();
		command->parseFromBinaryStream(stream);

		if (command->getCommandType() == SuiCommand::SCT_subscribeToEvent) {
			//callbacks.put(); //TODO: Register subscribes again...
		}

		commands.add(command);
	}

	targetNetworkId = stream->readLong();
	forceCloseDistance = stream->readFloat();
	unknownNetworkId = stream->readLong();

	return true;
}

void SuiPageData::sendTo(CreatureObject* creo) {
	PlayerObject* playerObject = creo->getPlayerObject();

	if (playerObject != NULL) {
		creo->getClient()->sendMessage(new SuiCreatePageMessage(this));
	}
}

void SuiPageData::sendUpdateTo(CreatureObject* creo) {
	PlayerObject* playerObject = creo->getPlayerObject();
	ZoneClientSession* client = creo->getClient();

	if (playerObject != NULL && client != NULL) {
		client->sendMessage(new SuiUpdatePageMessage(this));
	}
}
<|endoftext|>


<commit_before>
/*
 * Copyright (c) 2019, Open Source Robotics Foundation, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the Willow Garage, Inc. nor the names of its
 *       contributors may be used to endorse or promote products derived from
 *       this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <gtest/gtest.h>
#include <realtime_tools/realtime_server_goal_handle.h>
#include <actionlib/client/simple_action_client.h>
#include <actionlib/TwoIntsAction.h>
#include <chrono>
#include <memory>
#include <mutex>
#include <thread>

using realtime_tools::RealtimeServerGoalHandle;

const size_t ATTEMPTS = 10;
const std::chrono::milliseconds DELAY(250);

struct ActionCallback
{
  using GoalHandle = actionlib::ActionServer<actionlib::TwoIntsAction>::GoalHandle;
  bool have_handle_ = false;
  GoalHandle handle_;
  std::mutex mtx_;

  void goal_callback(GoalHandle handle)
  {
    std::unique_lock<std::mutex> lock(mtx_);
    handle_ = handle;
    handle_.setAccepted();
    have_handle_ = true;
  }

  void cancel_callback(GoalHandle handle)
  {
  }

  bool wait_for_handle()
  {
    for (size_t i = 0; i < ATTEMPTS; ++i) {
      ros::spinOnce();
      std::this_thread::sleep_for(DELAY);
      std::unique_lock<std::mutex> lock(mtx_);
      if (have_handle_) {
        break;
      }
    }
    std::unique_lock<std::mutex> lock(mtx_);
    return have_handle_;
  }
};

struct FeedbackCallback
{
  bool have_feedback_ = false;
  std::mutex mtx_;

  using FeedbackConstPtr = actionlib::ActionServer<actionlib::TwoIntsAction>::FeedbackConstPtr;
  void feedback_callback(const FeedbackConstPtr &)
  {
    std::unique_lock<std::mutex> lock(mtx_);
    have_feedback_ = true;
  }

  bool wait_for_feedback()
  {
    for (size_t i = 0; i < ATTEMPTS; ++i) {
      ros::spinOnce();
      std::this_thread::sleep_for(DELAY);
      std::unique_lock<std::mutex> lock(mtx_);
      if (have_feedback_) {
        break;
      }
    }
    std::unique_lock<std::mutex> lock(mtx_);
    return have_feedback_;
  }
};

std::shared_ptr<actionlib::SimpleActionClient<actionlib::TwoIntsAction>>
send_goal(
  const std::string & server_name,
  FeedbackCallback * cb = nullptr)
{
  std::shared_ptr<actionlib::SimpleActionClient<actionlib::TwoIntsAction>> ac;
  ac.reset(new actionlib::SimpleActionClient<actionlib::TwoIntsAction>(server_name, true));

  for (size_t i = 0; i < ATTEMPTS && !ac->isServerConnected(); ++i) {
    ros::spinOnce();
    std::this_thread::sleep_for(DELAY);
  }
  if (!ac->isServerConnected()) {
    ac.reset();
  } else {
    actionlib::TwoIntsGoal goal;
    goal.a = 2;
    goal.b = 3;
    ac->sendGoal(
      goal,
      actionlib::SimpleActionClient<actionlib::TwoIntsAction>::SimpleDoneCallback(),
      actionlib::SimpleActionClient<actionlib::TwoIntsAction>::SimpleActiveCallback(),
      boost::bind(&FeedbackCallback::feedback_callback, cb, _1));
  }
  return ac;
}

bool wait_for_result(std::shared_ptr<actionlib::SimpleActionClient<actionlib::TwoIntsAction>> ac)
{
  for (int i = 0; i < ATTEMPTS; ++i) {
    ros::spinOnce();
    if (ac->getState().isDone()) {
      return true;
    }
    std::this_thread::sleep_for(DELAY);
  }
  return false;
}

std::shared_ptr<actionlib::ActionServer<actionlib::TwoIntsAction>>
make_server(const std::string & server_name, ActionCallback & callbacks)
{
  ros::NodeHandle nh;
  std::shared_ptr<actionlib::ActionServer<actionlib::TwoIntsAction>> as;
  as.reset(new actionlib::ActionServer<actionlib::TwoIntsAction>(
      nh, server_name,
      boost::bind(&ActionCallback::goal_callback, &callbacks, _1),
      boost::bind(&ActionCallback::cancel_callback, &callbacks, _1), false));
  as->start();
  return as;
}

TEST(RealtimeServerGoalHandle, set_aborted)
{
  ActionCallback callbacks;
  const std::string server_name("set_aborted");
  auto as = make_server(server_name, callbacks);

  auto client = send_goal(server_name);
  ASSERT_NE(nullptr, client.get());

  ASSERT_TRUE(callbacks.wait_for_handle());
  RealtimeServerGoalHandle<actionlib::TwoIntsAction> rt_handle(callbacks.handle_);
  ASSERT_TRUE(rt_handle.valid());

  {
    actionlib::TwoIntsResult::Ptr result(new actionlib::TwoIntsResult);
    result->sum = 42;
    rt_handle.setAborted(result);
    rt_handle.runNonRealtime(ros::TimerEvent());
  }

  ASSERT_TRUE(wait_for_result(client));
  auto state = client->getState();
  EXPECT_EQ(state, actionlib::SimpleClientGoalState::ABORTED) << state.toString();
  auto result = client->getResult();
  ASSERT_NE(nullptr, result.get());
  EXPECT_EQ(42, result->sum);
}

TEST(RealtimeServerGoalHandle, set_canceled)
{
  ActionCallback callbacks;
  const std::string server_name("set_canceled");
  auto as = make_server(server_name, callbacks);

  auto client = send_goal(server_name);
  ASSERT_NE(nullptr, client.get());

  ASSERT_TRUE(callbacks.wait_for_handle());
  RealtimeServerGoalHandle<actionlib::TwoIntsAction> rt_handle(callbacks.handle_);
  ASSERT_TRUE(rt_handle.valid());

  // Cancel and wait for server to learn about that
  client->cancelGoal();
  for (size_t i = 0; i < ATTEMPTS; ++i) {
    actionlib_msgs::GoalStatus gs = callbacks.handle_.getGoalStatus();
    if (gs.status == actionlib_msgs::GoalStatus::PREEMPTING) {
      break;
    }
    ros::spinOnce();
    std::this_thread::sleep_for(DELAY);
  }
  ASSERT_EQ(callbacks.handle_.getGoalStatus().status, actionlib_msgs::GoalStatus::PREEMPTING);

  {
    actionlib::TwoIntsResult::Ptr result(new actionlib::TwoIntsResult);
    result->sum = 1234;
    rt_handle.setCanceled(result);
    rt_handle.runNonRealtime(ros::TimerEvent());
  }

  ASSERT_TRUE(wait_for_result(client));
  auto state = client->getState();
  EXPECT_EQ(state, actionlib::SimpleClientGoalState::PREEMPTED) << state.toString();
  auto result = client->getResult();
  ASSERT_NE(nullptr, result.get());
  EXPECT_EQ(1234, result->sum);
}

TEST(RealtimeServerGoalHandle, set_succeeded)
{
  ActionCallback callbacks;
  const std::string server_name("set_succeeded");
  auto as = make_server(server_name, callbacks);

  auto client = send_goal(server_name);
  ASSERT_NE(nullptr, client.get());

  ASSERT_TRUE(callbacks.wait_for_handle());
  RealtimeServerGoalHandle<actionlib::TwoIntsAction> rt_handle(callbacks.handle_);
  ASSERT_TRUE(rt_handle.valid());

  {
    actionlib::TwoIntsResult::Ptr result(new actionlib::TwoIntsResult);
    result->sum = 321;
    rt_handle.setSucceeded(result);
    rt_handle.runNonRealtime(ros::TimerEvent());
  }

  ASSERT_TRUE(wait_for_result(client));
  auto state = client->getState();
  EXPECT_EQ(state, actionlib::SimpleClientGoalState::SUCCEEDED) << state.toString();
  auto result = client->getResult();
  ASSERT_NE(nullptr, result.get());
  EXPECT_EQ(321, result->sum);
}

TEST(RealtimeServerGoalHandle, send_feedback)
{
  ActionCallback callbacks;
  FeedbackCallback client_callbacks;
  const std::string server_name("send_feedback");
  auto as = make_server(server_name, callbacks);

  auto client = send_goal(server_name, &client_callbacks);
  ASSERT_NE(nullptr, client.get());

  ASSERT_TRUE(callbacks.wait_for_handle());
  RealtimeServerGoalHandle<actionlib::TwoIntsAction> rt_handle(callbacks.handle_);
  ASSERT_TRUE(rt_handle.valid());

  {
    actionlib::TwoIntsFeedback::Ptr fb(new actionlib::TwoIntsFeedback());
    rt_handle.setFeedback(fb);
    rt_handle.runNonRealtime(ros::TimerEvent());
  }

  EXPECT_TRUE(client_callbacks.wait_for_feedback());
}

int main(int argc, char ** argv) {
  ::testing::InitGoogleTest(&argc, argv);
  ros::init(argc, argv, "realtime_server_goal_handle_tests");
  return RUN_ALL_TESTS();
}
<commit_msg>Remove unnecessary whitespace<commit_after>/*
 * Copyright (c) 2019, Open Source Robotics Foundation, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the Willow Garage, Inc. nor the names of its
 *       contributors may be used to endorse or promote products derived from
 *       this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <gtest/gtest.h>
#include <realtime_tools/realtime_server_goal_handle.h>
#include <actionlib/client/simple_action_client.h>
#include <actionlib/TwoIntsAction.h>
#include <chrono>
#include <memory>
#include <mutex>
#include <thread>

using realtime_tools::RealtimeServerGoalHandle;

const size_t ATTEMPTS = 10;
const std::chrono::milliseconds DELAY(250);

struct ActionCallback
{
  using GoalHandle = actionlib::ActionServer<actionlib::TwoIntsAction>::GoalHandle;
  bool have_handle_ = false;
  GoalHandle handle_;
  std::mutex mtx_;

  void goal_callback(GoalHandle handle)
  {
    std::unique_lock<std::mutex> lock(mtx_);
    handle_ = handle;
    handle_.setAccepted();
    have_handle_ = true;
  }

  void cancel_callback(GoalHandle handle)
  {
  }

  bool wait_for_handle()
  {
    for (size_t i = 0; i < ATTEMPTS; ++i) {
      ros::spinOnce();
      std::this_thread::sleep_for(DELAY);
      std::unique_lock<std::mutex> lock(mtx_);
      if (have_handle_) {
        break;
      }
    }
    std::unique_lock<std::mutex> lock(mtx_);
    return have_handle_;
  }
};

struct FeedbackCallback
{
  bool have_feedback_ = false;
  std::mutex mtx_;

  using FeedbackConstPtr = actionlib::ActionServer<actionlib::TwoIntsAction>::FeedbackConstPtr;
  void feedback_callback(const FeedbackConstPtr &)
  {
    std::unique_lock<std::mutex> lock(mtx_);
    have_feedback_ = true;
  }

  bool wait_for_feedback()
  {
    for (size_t i = 0; i < ATTEMPTS; ++i) {
      ros::spinOnce();
      std::this_thread::sleep_for(DELAY);
      std::unique_lock<std::mutex> lock(mtx_);
      if (have_feedback_) {
        break;
      }
    }
    std::unique_lock<std::mutex> lock(mtx_);
    return have_feedback_;
  }
};

std::shared_ptr<actionlib::SimpleActionClient<actionlib::TwoIntsAction>>
send_goal(
  const std::string & server_name,
  FeedbackCallback * cb = nullptr)
{
  std::shared_ptr<actionlib::SimpleActionClient<actionlib::TwoIntsAction>> ac;
  ac.reset(new actionlib::SimpleActionClient<actionlib::TwoIntsAction>(server_name, true));

  for (size_t i = 0; i < ATTEMPTS && !ac->isServerConnected(); ++i) {
    ros::spinOnce();
    std::this_thread::sleep_for(DELAY);
  }
  if (!ac->isServerConnected()) {
    ac.reset();
  } else {
    actionlib::TwoIntsGoal goal;
    goal.a = 2;
    goal.b = 3;
    ac->sendGoal(
      goal,
      actionlib::SimpleActionClient<actionlib::TwoIntsAction>::SimpleDoneCallback(),
      actionlib::SimpleActionClient<actionlib::TwoIntsAction>::SimpleActiveCallback(),
      boost::bind(&FeedbackCallback::feedback_callback, cb, _1));
  }
  return ac;
}

bool wait_for_result(std::shared_ptr<actionlib::SimpleActionClient<actionlib::TwoIntsAction>> ac)
{
  for (int i = 0; i < ATTEMPTS; ++i) {
    ros::spinOnce();
    if (ac->getState().isDone()) {
      return true;
    }
    std::this_thread::sleep_for(DELAY);
  }
  return false;
}

std::shared_ptr<actionlib::ActionServer<actionlib::TwoIntsAction>>
make_server(const std::string & server_name, ActionCallback & callbacks)
{
  ros::NodeHandle nh;
  std::shared_ptr<actionlib::ActionServer<actionlib::TwoIntsAction>> as;
  as.reset(new actionlib::ActionServer<actionlib::TwoIntsAction>(
      nh, server_name,
      boost::bind(&ActionCallback::goal_callback, &callbacks, _1),
      boost::bind(&ActionCallback::cancel_callback, &callbacks, _1), false));
  as->start();
  return as;
}

TEST(RealtimeServerGoalHandle, set_aborted)
{
  ActionCallback callbacks;
  const std::string server_name("set_aborted");
  auto as = make_server(server_name, callbacks);

  auto client = send_goal(server_name);
  ASSERT_NE(nullptr, client.get());

  ASSERT_TRUE(callbacks.wait_for_handle());
  RealtimeServerGoalHandle<actionlib::TwoIntsAction> rt_handle(callbacks.handle_);
  ASSERT_TRUE(rt_handle.valid());

  {
    actionlib::TwoIntsResult::Ptr result(new actionlib::TwoIntsResult);
    result->sum = 42;
    rt_handle.setAborted(result);
    rt_handle.runNonRealtime(ros::TimerEvent());
  }

  ASSERT_TRUE(wait_for_result(client));
  auto state = client->getState();
  EXPECT_EQ(state, actionlib::SimpleClientGoalState::ABORTED) << state.toString();
  auto result = client->getResult();
  ASSERT_NE(nullptr, result.get());
  EXPECT_EQ(42, result->sum);
}

TEST(RealtimeServerGoalHandle, set_canceled)
{
  ActionCallback callbacks;
  const std::string server_name("set_canceled");
  auto as = make_server(server_name, callbacks);

  auto client = send_goal(server_name);
  ASSERT_NE(nullptr, client.get());

  ASSERT_TRUE(callbacks.wait_for_handle());
  RealtimeServerGoalHandle<actionlib::TwoIntsAction> rt_handle(callbacks.handle_);
  ASSERT_TRUE(rt_handle.valid());

  // Cancel and wait for server to learn about that
  client->cancelGoal();
  for (size_t i = 0; i < ATTEMPTS; ++i) {
    actionlib_msgs::GoalStatus gs = callbacks.handle_.getGoalStatus();
    if (gs.status == actionlib_msgs::GoalStatus::PREEMPTING) {
      break;
    }
    ros::spinOnce();
    std::this_thread::sleep_for(DELAY);
  }
  ASSERT_EQ(callbacks.handle_.getGoalStatus().status, actionlib_msgs::GoalStatus::PREEMPTING);

  {
    actionlib::TwoIntsResult::Ptr result(new actionlib::TwoIntsResult);
    result->sum = 1234;
    rt_handle.setCanceled(result);
    rt_handle.runNonRealtime(ros::TimerEvent());
  }

  ASSERT_TRUE(wait_for_result(client));
  auto state = client->getState();
  EXPECT_EQ(state, actionlib::SimpleClientGoalState::PREEMPTED) << state.toString();
  auto result = client->getResult();
  ASSERT_NE(nullptr, result.get());
  EXPECT_EQ(1234, result->sum);
}

TEST(RealtimeServerGoalHandle, set_succeeded)
{
  ActionCallback callbacks;
  const std::string server_name("set_succeeded");
  auto as = make_server(server_name, callbacks);

  auto client = send_goal(server_name);
  ASSERT_NE(nullptr, client.get());

  ASSERT_TRUE(callbacks.wait_for_handle());
  RealtimeServerGoalHandle<actionlib::TwoIntsAction> rt_handle(callbacks.handle_);
  ASSERT_TRUE(rt_handle.valid());

  {
    actionlib::TwoIntsResult::Ptr result(new actionlib::TwoIntsResult);
    result->sum = 321;
    rt_handle.setSucceeded(result);
    rt_handle.runNonRealtime(ros::TimerEvent());
  }

  ASSERT_TRUE(wait_for_result(client));
  auto state = client->getState();
  EXPECT_EQ(state, actionlib::SimpleClientGoalState::SUCCEEDED) << state.toString();
  auto result = client->getResult();
  ASSERT_NE(nullptr, result.get());
  EXPECT_EQ(321, result->sum);
}

TEST(RealtimeServerGoalHandle, send_feedback)
{
  ActionCallback callbacks;
  FeedbackCallback client_callbacks;
  const std::string server_name("send_feedback");
  auto as = make_server(server_name, callbacks);

  auto client = send_goal(server_name, &client_callbacks);
  ASSERT_NE(nullptr, client.get());

  ASSERT_TRUE(callbacks.wait_for_handle());
  RealtimeServerGoalHandle<actionlib::TwoIntsAction> rt_handle(callbacks.handle_);
  ASSERT_TRUE(rt_handle.valid());

  {
    actionlib::TwoIntsFeedback::Ptr fb(new actionlib::TwoIntsFeedback());
    rt_handle.setFeedback(fb);
    rt_handle.runNonRealtime(ros::TimerEvent());
  }

  EXPECT_TRUE(client_callbacks.wait_for_feedback());
}

int main(int argc, char ** argv) {
  ::testing::InitGoogleTest(&argc, argv);
  ros::init(argc, argv, "realtime_server_goal_handle_tests");
  return RUN_ALL_TESTS();
}
<|endoftext|>


<commit_before>/*
 * ParallelPartitionCoarsening.cpp
 *
 *  Created on: 28.01.2014
 *      Author: cls
 */

#include "ParallelPartitionCoarsening.h"
#include <omp.h>
#include "../auxiliary/Timer.h"
#include "../auxiliary/Log.h"

namespace NetworKit {

std::pair<Graph, std::vector<node> > NetworKit::ParallelPartitionCoarsening::run(const Graph& G, const Partition& zeta) {

	Aux::Timer timer;
	timer.start();

	std::vector<node> subsetToSuperNode(zeta.upperBound(), none); // there is one supernode for each subset

	DEBUG("populate map subset -> supernode");
	node nextNodeId = 0;
	G.forNodes([&](node v){
		index c = zeta.subsetOf(v);
		if (subsetToSuperNode[c] == none) {
			subsetToSuperNode[c] = nextNodeId++;
		}
	});
	Graph Ginit(nextNodeId, true); // initial graph containing supernodes

	index z = G.upperNodeIdBound();
	std::vector<node> nodeToSuperNode(z, none);

	// set entries node -> supernode
	DEBUG("set entries node -> supernode");
	G.parallelForNodes([&](node v){
		nodeToSuperNode[v] = subsetToSuperNode[zeta.subsetOf(v)];
	});

	// make copies of initial graph
	count nThreads = omp_get_max_threads();
	std::vector<Graph> localGraphs(nThreads, Ginit); // thread-local graphs

	// iterate over edges of G and create edges in coarse graph or update edge and node weights in Gcon
	DEBUG("create edges in coarse graphs");
	G.parallelForWeightedEdges([&](node u, node v, edgeweight ew) {
		index t = omp_get_thread_num();

		node su = nodeToSuperNode[u];
		node sv = nodeToSuperNode[v];
		localGraphs.at(t).increaseWeight(su, sv, ew);

	});

	// DEBUG
	for (Graph G : localGraphs) {
		INFO("local graph: ", G.toString());
	}
	// DEBUG

	Aux::Timer timer2;
	timer2.start();
	// combine local graphs in parallel
	Graph Gcombined(Ginit.numberOfNodes(), true); //

	std::vector<count> numEdges(nThreads);

	// access internals of Graph to write adjacencies
	auto threadSafeIncreaseWeight = [&](node u, node v, edgeweight ew) {

		index vi = Gcombined.indexInOutEdgeArray(u, v);
		if (vi == none) {
			index t = omp_get_thread_num();
			if (u == v) {
				numEdges[t] += 2;
			} else {
				numEdges[t] += 1; // normal edges count half
			}
			Gcombined.outDeg[u]++;
			Gcombined.outEdges[u].push_back(v);
			Gcombined.outEdgeWeights[u].push_back(ew);
		} else {
			Gcombined.outEdgeWeights[u][vi] += ew;
		}

	};

	DEBUG("combining graphs");
	Gcombined.parallelForNodes([&](node u) {
		for (index l = 0; l < nThreads; ++l) {
			localGraphs.at(l).forEdgesOf(u, [&](node u, node v) {
				TRACE("increasing weight of (", u, v, ") to", localsGraphs.at(l).weight(u, v));
				threadSafeIncreaseWeight(u, v, localGraphs.at(l).weight(u, v));
			});
		}
	});

	// ensure consistency of data structure
	DEBUG("numEdges: ", numEdges);
	count twiceM = std::accumulate(numEdges.begin(), numEdges.end(), 0);
	assert (twiceM % 2 == 0);
	Gcombined.m = (twiceM / 2);

	assert (G.consistencyCheck());

	timer2.stop();
	INFO("combining coarse graphs took ", timer2.elapsedTag());

	timer.stop();
	INFO("parallel coarsening took ", timer.elapsedTag());

	return std::make_pair(Gcombined, nodeToSuperNode);

}

} /* namespace NetworKit */
<commit_msg>minor improvements<commit_after>/*
 * ParallelPartitionCoarsening.cpp
 *
 *  Created on: 28.01.2014
 *      Author: cls
 */

#include "ParallelPartitionCoarsening.h"
#include <omp.h>
#include "../auxiliary/Timer.h"
#include "../auxiliary/Log.h"

namespace NetworKit {

std::pair<Graph, std::vector<node> > NetworKit::ParallelPartitionCoarsening::run(const Graph& G, const Partition& zeta) {

	Aux::Timer timer;
	timer.start();

	std::vector<node> subsetToSuperNode(zeta.upperBound(), none); // there is one supernode for each subset

	DEBUG("populate map subset -> supernode");
	node nextNodeId = 0;
	G.forNodes([&](node v){
		index c = zeta.subsetOf(v);
		if (subsetToSuperNode[c] == none) {
			subsetToSuperNode[c] = nextNodeId++;
		}
	});
	Graph Ginit(nextNodeId, true); // initial graph containing supernodes

	index z = G.upperNodeIdBound();
	std::vector<node> nodeToSuperNode(z, none);

	// set entries node -> supernode
	DEBUG("set entries node -> supernode");
	G.parallelForNodes([&](node v){
		nodeToSuperNode[v] = subsetToSuperNode[zeta.subsetOf(v)];
	});

	// make copies of initial graph
	count nThreads = omp_get_max_threads();
	std::vector<Graph> localGraphs(nThreads, Ginit); // thread-local graphs

	// iterate over edges of G and create edges in coarse graph or update edge and node weights in Gcon
	DEBUG("create edges in coarse graphs");
	G.parallelForWeightedEdges([&](node u, node v, edgeweight ew) {
		index t = omp_get_thread_num();

		node su = nodeToSuperNode[u];
		node sv = nodeToSuperNode[v];
		localGraphs.at(t).increaseWeight(su, sv, ew);

	});

	// TODO: this will destroy any reasonable timing-results, consider replacing INFO() with DEBUG()
	// DEBUG
	for (Graph G : localGraphs) {
		INFO("local graph: ", G.toString());
	}
	// DEBUG

	Aux::Timer timer2;
	timer2.start();
	// combine local graphs in parallel
	Graph Gcombined(Ginit.numberOfNodes(), true); //

	std::vector<count> numEdges(nThreads);

	// access internals of Graph to write adjacencies
	auto threadSafeIncreaseWeight = [&](node u, node v, edgeweight ew) {

		index vi = Gcombined.indexInOutEdgeArray(u, v);
		if (vi == none) {
			index t = omp_get_thread_num();
			if (u == v) {
				numEdges[t] += 2;
			} else {
				numEdges[t] += 1; // normal edges count half
			}
			Gcombined.outDeg[u]++;
			Gcombined.outEdges[u].push_back(v);
			Gcombined.outEdgeWeights[u].push_back(ew);
		} else {
			Gcombined.outEdgeWeights[u][vi] += ew;
		}

	};

	DEBUG("combining graphs");
	Gcombined.parallelForNodes([&](node u) {
		for (index l = 0; l < nThreads; ++l) {
			localGraphs.at(l).forEdgesOf(u, [&](node u, node v) {
				TRACE("increasing weight of (", u, v, ") to", localGraphs.at(l).weight(u, v));
				threadSafeIncreaseWeight(u, v, localGraphs.at(l).weight(u, v));
			});
		}
	});

	// ensure consistency of data structure
	DEBUG("numEdges: ", numEdges);
	count twiceM = std::accumulate(numEdges.begin(), numEdges.end(), 0);
	assert (twiceM % 2 == 0);
	Gcombined.m = (twiceM / 2);

	assert (G.consistencyCheck());

	// stop both timers before printing
	timer2.stop();
	timer.stop();
	INFO("combining coarse graphs took ", timer2.elapsedTag());
	INFO("parallel coarsening took ", timer.elapsedTag());

	return {std::move(Gcombined), std::move(nodeToSuperNode)};

}

} /* namespace NetworKit */
<|endoftext|>