/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ /* * Main authors: * Christian Schulte * Guido Tack * * Copyright: * Christian Schulte, 2003 * Guido Tack, 2004 * * Last modified: * $Date: 2008-02-20 08:39:15 +0100 (Wed, 20 Feb 2008) $ by $Author: tack $ * $Revision: 6240 $ * * This file is part of Gecode, the generic constraint * development environment: * http://www.gecode.org * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * */ #include #include namespace Gecode { template class VarArray; template class VarArgArray; /** * \brief %Variable arrays * * %Variable arrays store variables. They are typically used * for storing the variables being part of a solution. * * Never use them for temporary purposes, use argument arrays * instead. * \ingroup TaskVar */ template class VarArray { protected: /// Number of variables (size) int used; /// Allocated size of the array int n; /// Array of variables Var* x; public: /// \name Constructors and initialization //@{ /// Default constructor (array of size 0) VarArray(void); /// Allocate array with \a m variables VarArray(Space*, int m); /// Initialize from variable argument array \a a (copy elements) VarArray(Space*,const VarArgArray&); /// Initialize from variable array \a a (share elements) VarArray(const VarArray& a); /// Initialize from variable array \a a (share elements) const VarArray& operator=(const VarArray& a); /// Destructor ~VarArray(void); //@} /// \name Array size //@{ /// Return size of array (number of elements) int size(void) const; /** * \brief Insert or remove (uninitialized!) elements at the end such * that size becomes \a m */ void resize(Space* home, int m); //@} /// \name Array elements //@{ /// Return variable at position \a i Var& operator[](int i); /// Return variable at position \a i const Var& operator[](int i) const; /// Insert a new element \a v at the end of the array (increase size by 1) void add(Space* home, const Var& v); //@} /// \name Cloning //@{ /** * \brief Update array to be a clone of array \a a * * If \a share is true, sharing is retained for all shared * data structures. Otherwise, for each of them an independent * copy is created. */ void update(Space*, bool share, VarArray& a); //@} private: static void* operator new(size_t); static void operator delete(void*,size_t); }; /** * \brief View arrays * * View arrays store views. They are typically used for storing the * views with which propagators and branchings compute. * \ingroup TaskActor */ template class ViewArray { private: /// Number of views (size) int n; /// Views View* x; /// Sort order for views class ViewLess { public: bool operator()(const View&, const View&); }; /// Sort \a n views \a x according to \a ViewLess static void sort(View* x, int n); public: /// \name Constructors and initialization //@{ /// Default constructor (array of size 0) ViewArray(void); /// Allocate array with \a m variables ViewArray(Space* home, int m); /// Initialize from view array \a a (share elements) ViewArray(const ViewArray& a); /// Initialize from specification \a spec with variables \a vars ViewArray(Space* home, const Reflection::VarMap& vars, Reflection::Arg* spec); /// Initialize from view array \a a (copy elements) ViewArray(Space* home, const ViewArray& a); /// Initialize from view array \a a (share elements) const ViewArray& operator=(const ViewArray& a); /** * \brief Initialize from variable argument array \a a (copy elements) * * Note that the view type \a View must provide a constructor * for the associated \a Var type. */ template ViewArray(Space* home, const VarArgArray& a) : n(a.size()) { // This may not be in the icc file (to satisfy the MS compiler) if (n>0) { x = static_cast(home->alloc(sizeof(View)*n)); for (int i = n; i--; ) x[i] = a[i]; } else { x = NULL; } } //@} /// \name Array size //@{ /// Return size of array (number of elements) int size(void) const; /// Decrease size of array (number of elements) void size(int n); //@} /// \name Array elements //@{ /// Return view at position \a i View& operator[](int i); /// Return view at position \a i const View& operator[](int i) const; //@} /// \name Dependencies //@{ /** * \brief Subscribe propagator \a p with propagation condition \a pc to variable * * In case \a process is false, the propagator is just subscribed but * not processed for execution (this must be used when creating * subscriptions during propagation). */ void subscribe(Space* home, Propagator* p, PropCond pc, bool process=true); /// Cancel subscription of propagator \a p with propagation condition \a pc to all views void cancel(Space* home, Propagator* p, PropCond pc); /// Subscribe advisor \a a to variable void subscribe(Space* home, Advisor* a); /// Cancel subscription of advisor \a a void cancel(Space* home, Advisor* a); //@} /// \name Cloning //@{ /** * \brief Update array to be a clone of array \a a * * If \a share is true, sharing is retained for all shared * data structures. Otherwise, for each of them an independent * copy is created. */ void update(Space*, bool share, ViewArray& a); //@} /// \name Moving elements //@{ /// Move assigned view from position 0 to position \a i (shift elements to the left) void move_fst(int i); /// Move assigned view from position \c size()-1 to position \a i (truncate array by one) void move_lst(int i); /** \brief Move view from position 0 to position \a i (shift elements to the left) * * Before moving, cancel subscription of propagator \a p with * propagation condition \a pc to view at position \a i. */ void move_fst(int i, Space* home, Propagator* p, PropCond pc); /** \brief Move view from position \c size()-1 to position \a i (truncate array by one) * * Before moving, cancel subscription of propagator \a p with * propagation condition \a pc to view at position \a i. */ void move_lst(int i, Space* home, Propagator* p, PropCond pc); /** \brief Move view from position 0 to position \a i (shift elements to the left) * * Before moving, cancel subscription of advisor \a a * to view at position \a i. */ void move_fst(int i, Space* home, Advisor* a); /** \brief Move view from position \c size()-1 to position \a i (truncate array by one) * * Before moving, cancel subscription of advisor \a a to view * at position \a i. */ void move_lst(int i, Space* home, Advisor* a); //@} /// \name Dropping elements //@{ /// Drop assigned views from positions 0 to \a i-1 from array void drop_fst(int i); /// Drop assigned views from positions \a i+1 to \c size()-1 from array void drop_lst(int i); /** \brief Drop views from positions 0 to \a i-1 from array * * Before moving, cancel subscription of propagator \a p with * propagation condition \a pc to views at positions 0 to \a i-1. */ void drop_fst(int i, Space* home, Propagator* p, PropCond pc); /** \brief Drop assigned views from positions \a i+1 to \c size()-1 from array * * Before moving, cancel subscription of propagator \a p with * propagation condition \a pc to views at positions \a i+1 to * \c size()-1. */ void drop_lst(int i, Space* home, Propagator* p, PropCond pc); /** \brief Drop views from positions 0 to \a i-1 from array * * Before moving, cancel subscription of advisor \a a to views at * positions 0 to \a i-1. */ void drop_fst(int i, Space* home, Advisor* a); /** \brief Drop assigned views from positions \a i+1 to \c size()-1 from array * * Before moving, cancel subscription of advisor \a a to views at * positions \a i+1 to \c size()-1. */ void drop_lst(int i, Space* home, Advisor* a); //@} /// \name View equality //@{ /** * \brief Test whether array has multiple occurence of the same view * * Note that assigned views are ignored. */ bool same(void) const; /** * \brief Test whether array contains a view being the same as \a y * * Note that assigned views are ignored. */ bool same(const View& y) const; /// Remove all duplicate views from array (changes element order) void unique(void); //@} /// \name View sharing //@{ /** * \brief Test whether array contains shared views * * Note that assigned views are ignored. */ bool shared(void) const; /** * \brief Test whether array contains a view being shared with \a y * * Note that assigned views are ignored. */ bool shared(const View& y) const; //@} /// \name Reflection //@{ Reflection::Arg* spec(const Space* home, Reflection::VarMap& m) const; //@} private: static void* operator new(size_t); static void operator delete(void*,size_t); }; /** * \brief Base-class for argument arrays * * Argument arrays are used as convenient mechanism of passing arguments * when calling functions as they combine both the size and the elements * of an array. For a small number of elements, memory is allocated by * creating an argument array object. Otherwise the memory is allocated * from the heap. * * This base-class is not to be used directly, use PrimArgArray for * argument arrays of primitive types and VarArgArray for argument * arrays storing variables. */ template class ArgArrayBase { protected: /// Number of elements int n; /// Element array T* a; /// How much elements are possible inside array static const int onstack_size = 16; /// In-array storage for elements T onstack[onstack_size]; /// Allocate memory for \a n elements T* allocate(int n); public: /// \name Constructors and initialization //@{ /// Allocate array with \a n elements ArgArrayBase(int n); /// Initialize from argument array \a a (copy elements) ArgArrayBase(const ArgArrayBase& a); /// Initialize from view array \a a (copy elements) const ArgArrayBase& operator=(const ArgArrayBase& a); //@} /// \name Array size //@{ /// Return size of array (number of elements) int size(void) const; //@} /// \name Array elements //@{ /// Return element at position \a i T& operator[](int i); /// Return element at position \a i const T& operator[](int i) const; //@} /// \name Destructor //@{ /// Destructor ~ArgArrayBase(void); //@} private: static void* operator new(size_t); static void operator delete(void*,size_t); }; /** * \brief Argument array for primtive types * * Argument arrays are used as convenient mechanism of passing arguments * when calling functions as they combine both the size and the elements * of an array. For a small number of elements, memory is allocated by * creating an argument array object. Otherwise the memory is allocated * from the heap. * * \ingroup TaskVar */ template class PrimArgArray : public ArgArrayBase { protected: using ArgArrayBase::a; public: using ArgArrayBase::size; /// \name Constructors and initialization //@{ /// Allocate array with \a n elements PrimArgArray(int n); /// Allocate array with \a n elements and initialize with \a e0, ... PrimArgArray(int n, T e0, ...); /// Allocate array with \a n elements and initialize with elements from array \a e PrimArgArray(int n, const T* e); /// Initialize from primitive argument array \a a (copy elements) PrimArgArray(const PrimArgArray& a); //@} }; /** * \brief Argument array for variables * * Argument arrays are used as convenient mechanism of passing arguments * when calling functions as they combine both the size and the elements * of an array. For a small number of elements, memory is allocated by * creating an argument array object. Otherwise the memory is allocated * from the heap. * * \ingroup TaskVar */ template class VarArgArray : public ArgArrayBase { protected: using ArgArrayBase::a; using ArgArrayBase::n; /// Sort order for variables class VarLess { public: bool operator()(const Var&, const Var&); }; public: using ArgArrayBase::size; /// \name Constructors and initialization //@{ /// Allocate array with \a n elements VarArgArray(int n); /// Initialize from variable argument array \a a (copy elements) VarArgArray(const VarArgArray& a); /// Initialize from variable array \a a (copy elements) VarArgArray(const VarArray& a); //@} /// \name Variable equality //@{ /** * \brief Test whether array contains same variable multiply * * Note that assigned variables are ignored. */ bool same(void) const; /** * \brief Test whether array contains variable \a y * * Note that assigned variables are ignored. */ bool same(const Var& y) const; /** * \brief Test whether all elements from array and \a y contains same variable multiply * * Note that assigned variables are ignored. */ bool same(const VarArgArray& y) const; //@} }; } /** * \brief Print array elements enclosed in curly brackets * \relates VarArray */ template std::ostream& operator<<(std::ostream& os, const Gecode::VarArray& x); /** * \brief Print array elements enclosed in curly brackets * \relates ViewArray */ template std::ostream& operator<<(std::ostream& os, const Gecode::ViewArray& x); /** * \brief Print array elements enclosed in curly brackets * \relates ArgArrayBase */ template std::ostream& operator<<(std::ostream& os, const Gecode::ArgArrayBase& x); namespace Gecode { /** \brief Traits of arrays in Gecode * * This class collects the traits of an array in Gecode. * The traits used are the following. * - typedef Type storage_type where \c Type is the type * of an appropriate storage type for this array. * - typedef Type value_type where \c Type is the type * of the elements of this array. * - typedef Type args_type where \c Type is the type * of the appropriate Args-array type (e.g., \c BoolVarArgs if \c A is * \c BoolVarArray). */ template class ArrayTraits {}; /* * Implementation * */ /* * Variable arrays * */ template forceinline VarArray::VarArray(void) : used(0), n(0), x(NULL) {} template forceinline VarArray::VarArray(Space* home, int n0) : used(n0), n(n0) { x = (n>0) ? static_cast(home->alloc(sizeof(Var)*n)) : NULL; } template forceinline VarArray::VarArray(const VarArray& a) { used = a.used; n = a.n; x = a.x; } template forceinline VarArray::~VarArray(void) { if (used != n) { // Array was allocated on the heap instead of the space Memory::free(x); } } template forceinline const VarArray& VarArray::operator=(const VarArray& a) { used = a.used; n = a.n; x = a.x; return *this; } template forceinline int VarArray::size(void) const { return used; } template forceinline void VarArray::resize(Space* home, int m) { int newsize; if (m(Memory::malloc(sizeof(Var)*newsize)); for (int i=used; i--;) new (&x[i]) Var(oldx[i]); if (used != n) Memory::free(oldx); else home->reuse(oldx, n); n = newsize; used = m; } template forceinline Var& VarArray::operator[](int i) { assert((i >= 0) && (i < size())); return x[i]; } template forceinline const Var& VarArray::operator[](int i) const { assert((i >= 0) && (i < size())); return x[i]; } template forceinline void VarArray::add(Space* home, const Var& v) { resize(home, used+1); new (&(*this)[used-1]) Var(v); } template forceinline void VarArray::update(Space* home, bool share, VarArray& a) { n = a.used; used = n; if (n > 0) { x = static_cast(home->alloc(sizeof(Var)*n)); for (int i = n; i--; ) x[i].update(home, share, a.x[i]); } else { x = NULL; } } template void* VarArray::operator new(size_t) { return NULL; } template void VarArray::operator delete(void*,size_t) { } /* * View arrays * */ template forceinline ViewArray::ViewArray(void) : n(0), x(NULL) {} template forceinline ViewArray::ViewArray(Space* home, int n0) : n(n0) { x = (n>0) ? static_cast(home->alloc(sizeof(View)*n)) : NULL; } template ViewArray::ViewArray(Space* home, const ViewArray& a) : n(a.size()) { if (n>0) { x = static_cast(home->alloc(sizeof(View)*n)); for (int i = n; i--; ) x[i] = a[i]; } else { x = NULL; } } template ViewArray::ViewArray(Space* home, const Reflection::VarMap& vars, Reflection::Arg* spec) { if (spec == NULL) { x = NULL; n = 0; return; } Reflection::ArrayArg* a = spec->toArray(); n = a->size(); x = n>0 ? static_cast(home->alloc(sizeof(View)*n)) : NULL; for (int i=n; i--;) x[i] = View(home, vars, (*a)[i]); } template forceinline ViewArray::ViewArray(const ViewArray& a) : n(a.n), x(a.x) {} template forceinline const ViewArray& ViewArray::operator=(const ViewArray& a) { n = a.n; x = a.x; return *this; } template forceinline int ViewArray::size(void) const { return n; } template forceinline void ViewArray::size(int n0) { n = n0; } template forceinline View& ViewArray::operator[](int i) { assert((i >= 0) && (i < size())); return x[i]; } template forceinline const View& ViewArray::operator[](int i) const { assert((i >= 0) && (i < size())); return x[i]; } template forceinline void ViewArray::move_fst(int i) { // move x[0] to x[i] assert(x[i].assigned()); x[i]=x[0]; x++; n--; } template forceinline void ViewArray::move_lst(int i) { // move x[n-1] to x[i] assert(x[i].assigned()); n--; x[i]=x[n]; } template forceinline void ViewArray::drop_fst(int i) { // Drop elements from 0..i-1 assert(i>=0); x += i; n -= i; } template forceinline void ViewArray::drop_lst(int i) { // Drop elements from i+1..n-1 assert(i forceinline void ViewArray::move_fst(int i, Space* home, Propagator* p, PropCond pc) { // Move x[0] to x[i] x[i].cancel(home,p,pc); x[i]=x[0]; x++; n--; } template forceinline void ViewArray::move_lst(int i, Space* home, Propagator* p, PropCond pc) { // Move x[n-1] to x[i] x[i].cancel(home,p,pc); n--; x[i]=x[n]; } template void ViewArray::drop_fst(int i, Space* home, Propagator* p, PropCond pc) { // Drop elements from 0..i-1 assert(i>=0); for (int j=i; j--; ) x[j].cancel(home,p,pc); x += i; n -= i; } template void ViewArray::drop_lst(int i, Space* home, Propagator* p, PropCond pc) { // Drop elements from i+1..n-1 assert(i forceinline void ViewArray::move_fst(int i, Space* home, Advisor* a) { // Move x[0] to x[i] x[i].cancel(home,a); x[i]=x[0]; x++; n--; } template forceinline void ViewArray::move_lst(int i, Space* home, Advisor* a) { // Move x[n-1] to x[i] x[i].cancel(home,a); n--; x[i]=x[n]; } template void ViewArray::drop_fst(int i, Space* home, Advisor* a) { // Drop elements from 0..i-1 assert(i>=0); for (int j=i; j--; ) x[j].cancel(home,a); x += i; n -= i; } template void ViewArray::drop_lst(int i, Space* home, Advisor* a) { // Drop elements from i+1..n-1 assert(i void ViewArray::update(Space* home, bool share, ViewArray& y) { n = y.n; if (n > 0) { x = static_cast(home->alloc(sizeof(View)*n)); for (int i = n; i--; ) x[i].update(home, share, y.x[i]); } else { x = NULL; } } template void ViewArray::subscribe(Space* home, Propagator* p, PropCond pc, bool process) { for (int i = n; i--; ) x[i].subscribe(home,p,pc,process); } template void ViewArray::cancel(Space* home, Propagator* p, PropCond pc) { for (int i = n; i--; ) x[i].cancel(home,p,pc); } template void ViewArray::subscribe(Space* home, Advisor* a) { for (int i = n; i--; ) x[i].subscribe(home,a); } template void ViewArray::cancel(Space* home, Advisor* a) { for (int i = n; i--; ) x[i].cancel(home,a); } template forceinline bool __before(const View& x, const View& y) { return before(x,y); } template forceinline bool ViewArray::ViewLess::operator()(const View& a, const View& b) { return __before(a,b); } template void ViewArray::sort(View* y, int m) { ViewLess vl; Support::quicksort(y,m,vl); } template forceinline bool __same(const View& x, const View& y) { return same(x,y); } template forceinline bool __shared(const View& x, const View& y) { return shared(x,y); } template bool ViewArray::same(void) const { if (n < 2) return false; GECODE_AUTOARRAY(View,y,n); for (int i = n; i--; ) y[i] = x[i]; sort(y,n); for (int i = n-1; i--; ) if (!y[i].assigned() && __same(y[i+1],y[i])) return true; return false; } template bool ViewArray::same(const View& y) const { if (y.assigned()) return false; for (int i = n; i--; ) if (__same(x[i],y)) return true; return false; } template void ViewArray::unique(void) { if (n < 2) return; sort(x,n); int j = 0; for (int i = 1; i bool ViewArray::shared(void) const { if (n < 2) return false; GECODE_AUTOARRAY(View,y,n); for (int i = n; i--; ) y[i] = x[i]; sort(y,n); for (int i = n-1; i--; ) if (!y[i].assigned() && __shared(y[i+1],y[i])) return true; return false; } template bool ViewArray::shared(const View& y) const { if (y.assigned()) return false; for (int i = n; i--; ) if (__shared(x[i],y)) return true; return false; } template Reflection::Arg* ViewArray::spec(const Space* home, Reflection::VarMap& m) const { Reflection::ArrayArg* s = Reflection::Arg::newArray(n); for (int i = 0; i void* ViewArray::operator new(size_t) { return NULL; } template void ViewArray::operator delete(void*,size_t) { } /* * Argument arrays: base class * */ template forceinline T* ArgArrayBase::allocate(int n) { return (n > onstack_size) ? Memory::bmalloc(static_cast(n)) : &onstack[0]; } template forceinline ArgArrayBase::ArgArrayBase(int n0) : n(n0), a(allocate(n0)) { for (int i=n; i--;) new (&a[i]) T(); } template inline ArgArrayBase::ArgArrayBase(const ArgArrayBase& aa) : n(aa.n), a(allocate(aa.n)) { for (int i = n; i--; ) new (&a[i]) T(aa.a[i]); } template forceinline ArgArrayBase::~ArgArrayBase(void) { for (int i=n; i--;) a[i].~T(); if (n > onstack_size) Memory::free(a); } template forceinline const ArgArrayBase& ArgArrayBase::operator=(const ArgArrayBase& aa) { if (&aa != this) { if (n > onstack_size) Memory::free(a); n = aa.n; a = allocate(aa.n); for (int i = n; i--; ) a[i] = aa.a[i]; } return *this; } template forceinline int ArgArrayBase::size(void) const { return n; } template forceinline T& ArgArrayBase::operator[](int i) { assert((i>=0) && (i < n)); return a[i]; } template forceinline const T& ArgArrayBase::operator[](int i) const { assert((i>=0) && (i < n)); return a[i]; } /* * Argument arrays for primitive types * */ template forceinline PrimArgArray::PrimArgArray(int n) : ArgArrayBase(n) {} template PrimArgArray::PrimArgArray(int n, T a0, ...) : ArgArrayBase(n) { va_list args; va_start(args, a0); a[0] = a0; for (int i = 1; i < n; i++) a[i] = va_arg(args,T); va_end(args); } template PrimArgArray::PrimArgArray(int n, const T* a0) : ArgArrayBase(n) { for (int i=n; i--; ) a[i] = a0[i]; } template forceinline PrimArgArray::PrimArgArray(const PrimArgArray& aa) : ArgArrayBase(aa) {} /* * Argument arrays for variables * */ template forceinline VarArgArray::VarArgArray(int n) : ArgArrayBase(n) {} template forceinline VarArgArray::VarArgArray(const VarArgArray& aa) : ArgArrayBase(aa) {} template inline VarArgArray::VarArgArray(const VarArray& x) : ArgArrayBase(x.size()) { for (int i = x.size(); i--; ) a[i] = x[i]; } template forceinline bool VarArgArray::VarLess::operator()(const Var& a, const Var& b) { return a.var() < b.var(); } template bool VarArgArray::same(void) const { if (n < 2) return false; GECODE_AUTOARRAY(Var,y,n); for (int i = n; i--; ) y[i] = a[i]; VarLess vl; Support::quicksort(y,n,vl); for (int i = n-1; i--; ) if (!y[i].assigned() && (y[i+1].var() == y[i].var())) return true; return false; } template bool VarArgArray::same(const VarArgArray& y) const { int m = n + y.n; if (m < 2) return false; GECODE_AUTOARRAY(Var,z,m); for (int i = n; i--; ) z[i] = a[i]; for (int i = y.n; i--; ) z[i+n] = y.a[i]; VarLess vl; Support::quicksort(z,m,vl); for (int i = m-1; i--; ) if (!z[i].assigned() && (z[i+1].var() == z[i].var())) return true; return false; } template bool VarArgArray::same(const Var& y) const { if (y.assigned()) return false; for (int i = n; i--; ) if (a[i].var() == y.var()) return true; return false; } /* * Interdependent code * */ template inline VarArray::VarArray(Space* home, const VarArgArray& a) : n(a.size()) { if (n>0) { x = static_cast(home->alloc(sizeof(Var)*n)); for (int i = n; i--; ) x[i] = a[i]; } else { x = NULL; } } } /* * Printing of arrays * */ template std::ostream& operator<<(std::ostream& os, const Gecode::VarArray& x) { os << '{'; if (x.size() > 0) { os << x[0]; for (int i=1; i std::ostream& operator<<(std::ostream& os, const Gecode::ViewArray& x) { os << '{'; if (x.size() > 0) { os << x[0]; for (int i=1; i std::ostream& operator<<(std::ostream& os, const Gecode::ArgArrayBase& x) { os << '{'; if (x.size() > 0) { os << x[0]; for (int i=1; i