/* Copyright (c) 2013 Microsoft Corporation. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Author: Leonardo de Moura */ #pragma once #include #include #include "util/list.h" #include "util/buffer.h" #include "util/pair.h" namespace lean { /** \brief Copy the values in the list \c l to the buffer \c r. */ template void to_buffer(list const & l, buffer & r) { for (T const & v : l) { r.push_back(v); } } /** \brief Copy the cells in the list \c l to the buffer \c r. */ template void to_buffer(list const & l, buffer::cell *> & r) { typename list::cell * it = l.raw(); while (it) { r.push_back(it); it = it->tail().raw(); } } /** \brief Create a list using the values in the cells from \c begin to \c end. */ template list cells_to_list(typename list::cell * const * begin, typename list::cell * const * end) { list r; auto it = end; while (it != begin) { --it; r = cons((*it)->head(), r); } return r; } /** \brief Create a list using the values in the cells from \c b. */ template list buffer_to_list(buffer::cell*> const & b) { return cells_to_list(b.begin(), b.end()); } /** \brief Return the reverse list. */ template list reverse(list const & l) { if (is_nil(l)) { return l; } else { buffer::cell *> tmp; to_buffer(l, tmp); std::reverse(tmp.begin(), tmp.end()); return buffer_to_list(tmp); } } /** \brief Return two lists \c l1 and \c l2 of approximately the same size s.t. append(l1, l2) == l */ template std::pair, list> split(list const & l) { if (is_nil(l)) { return mk_pair(l, l); } else if (is_nil(cdr(l))) { return mk_pair(l, list()); } else { buffer::cell*> tmp; to_buffer(l, tmp); unsigned mid = tmp.size() / 2; auto beg = tmp.begin(); lean_assert(beg + mid <= tmp.end()); return mk_pair(cells_to_list(beg, beg + mid), cells_to_list(beg + mid, tmp.end())); } } /** \brief Return two lists \c l1 and \c l2 of approximately the same size s.t. append(l1, reverse(l2)) == l */ template std::pair, list> split_reverse_second(list const & l) { if (is_nil(l)) { return mk_pair(l, l); } else if (is_nil(cdr(l))) { return mk_pair(l, list()); } else { buffer tmp; to_buffer(l, tmp); unsigned mid = tmp.size() / 2; auto beg = tmp.begin(); lean_assert(beg + mid <= tmp.end()); return mk_pair(to_list(beg, beg + mid), reverse_to_list(beg+mid, tmp.end())); } } /** \brief Append two lists */ template list append(list const & l1, list const & l2) { if (!l1) { return l2; } else if (!l2) { return l1; } else { buffer::cell*> tmp; list r = l2; to_buffer(l1, tmp); unsigned i = tmp.size(); while (i > 0) { --i; r = cons(tmp[i]->head(), r); } return r; } } /** \brief Given list (a_0, ..., a_k), return list (f(a_0), ..., f(a_k)). */ template list map2(list const & l, F && f) { static_assert(std::is_same::type, To>::value, "map: return type of f is not equal to input type"); if (is_nil(l)) { return list(); } else { buffer::cell*> tmp; to_buffer(l, tmp); unsigned i = tmp.size(); list r; while (i > 0) { --i; r = cons(f(tmp[i]->head()), r); } return r; } } /** \brief Given list (a_0, ..., a_k), return list (f(a_0), ..., f(a_k)). */ template list map(list const & l, F && f) { return map2(l, std::move(f)); } /** \brief Filter/Remove elements from the list that do not satisfy the given predicate. */ template list filter(list const & l, P && p) { if (is_nil(l)) { return l; } else { buffer::cell*> tmp; to_buffer(l, tmp); unsigned i = tmp.size(); while (i > 0) { --i; if (!p(tmp[i]->head())) { list r = tmp[i]->tail(); while (i > 0) { --i; if (p(tmp[i]->head())) r = cons(tmp[i]->head(), r); } return r; } } return l; // not element was removed } } /** \brief Remove the last element that satisfies \c p. */ template list remove_last(list const & l, P && p) { if (!is_nil(l)) { buffer::cell*> tmp; to_buffer(l, tmp); unsigned i = tmp.size(); while (i > 0) { --i; if (p(tmp[i]->head())) { list r = tmp[i]->tail(); while (i > 0) { --i; r = cons(tmp[i]->head(), r); } return r; } } } return l; } /** \brief Similar to \c map but \c f has signature bool f(T const & in, T & out) If \c out becomes part of the result iff \c f returns true. */ template list map_filter(list const & l, F && f) { if (is_nil(l)) { return l; } else { buffer::cell*> tmp; to_buffer(l, tmp); unsigned i = tmp.size(); list r; while (i > 0) { --i; T out; if (f(tmp[i]->head(), out)) r = cons(out, r); } return r; } } template list map_append(list const & l, F && f) { if (is_nil(l)) { return l; } else { buffer tmp; for (auto const & v : l) to_buffer(f(v), tmp); return to_list(tmp.begin(), tmp.end()); } } /** \brief Semantically equivalent to \c map, but it tries to reuse list cells. The elements are compared using the predicate \c eq. */ template> list map_reuse(list const & l, F && f, Eq const & eq = Eq()) { if (is_nil(l)) { return l; } else { buffer::cell*> tmp; to_buffer(l, tmp); auto it = tmp.end(); auto begin = tmp.begin(); while (it != begin) { --it; auto curr = *it; auto new_v = f(curr->head()); if (!eq(new_v, curr->head())) { list r(new_v, curr->tail()); while (it != begin) { --it; auto curr = *it; r = cons(f(curr->head()), r); } return r; } } return l; } } /** \brief Given list (a_0, ..., a_k), exec f(a_0); f(a_1); ... f(a_k). */ template void for_each(list const & l, F && f) { static_assert(std::is_same::type, void>::value, "for_each: return type of f is not void"); typedef typename list::cell cell; cell * it = l.raw(); while (it) { f(it->head()); it = it->tail().raw(); } } /** \brief Compare two lists using the binary predicate p. */ template bool compare(list const & l1, list const & l2, P && p) { static_assert(std::is_same::type, bool>::value, "compare: return type of f is not bool"); auto it1 = l1.begin(); auto it2 = l2.begin(); auto end1 = l1.end(); auto end2 = l2.end(); for (; it1 != end1 && it2 != end2; ++it1, ++it2) { if (!p(*it1, *it2)) return false; } return it1 == end1 && it2 == end2; } /** \brief Return the i-th element of the list */ template T const & get_ith(list const & l, unsigned idx) { return idx == 0 ? head(l) : get_ith(tail(l), idx - 1); } }