Skip to content
Snippets Groups Projects
Select Git revision
  • f44d6ebf8d2fad238b2c24d871238ba217de8873
  • master default protected
  • alphashapes
  • quadMeshingTools
  • cygwin_conv_path
  • macos_arm64
  • add-transfiniteautomatic-to-geo
  • patch_releases_4_10
  • HierarchicalHDiv
  • isuruf-master-patch-63355
  • hyperbolic
  • hexdom
  • hxt_update
  • jf
  • 1618-pythonocc-and-gmsh-api-integration
  • octreeSizeField
  • hexbl
  • alignIrregularVertices
  • getEdges
  • patch_releases_4_8
  • isuruf-master-patch-51992
  • gmsh_4_11_0
  • gmsh_4_10_5
  • gmsh_4_10_4
  • gmsh_4_10_3
  • gmsh_4_10_2
  • gmsh_4_10_1
  • gmsh_4_10_0
  • gmsh_4_9_5
  • gmsh_4_9_4
  • gmsh_4_9_3
  • gmsh_4_9_2
  • gmsh_4_9_1
  • gmsh_4_9_0
  • gmsh_4_8_4
  • gmsh_4_8_3
  • gmsh_4_8_2
  • gmsh_4_8_1
  • gmsh_4_8_0
  • gmsh_4_7_1
  • gmsh_4_7_0
41 results

meshGFaceTransfinite.cpp

Blame
  • Forked from gmsh / gmsh
    Source project has a limited visibility.
    onelab.h 61.21 KiB
    // ONELAB - Copyright (C) 2011-2019 Universite de Liege - Universite catholique
    // de Louvain
    //
    // 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, and/or sell copies of the
    // Software, and to permit persons to whom the Software is furnished to do so,
    // provided that the above copyright notice(s) and this permission notice appear
    // in all copies of the Software and that both the above copyright notice(s) and
    // this permission notice appear in supporting documentation.
    //
    // 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 OF THIRD PARTY
    // RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS
    // NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL
    // DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
    // PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
    // ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
    // SOFTWARE.
    
    #ifndef _ONELAB_H_
    #define _ONELAB_H_
    
    #include <time.h>
    #include <stdio.h>
    #include <string>
    #include <vector>
    #include <set>
    #include <map>
    #include <iostream>
    #include <algorithm>
    #include <sstream>
    
    #if __cplusplus >= 201103L
    #include <mutex>
    #endif
    
    #include "GmshSocket.h"
    
    #define HAVE_PICOJSON
    #if defined(HAVE_PICOJSON)
    #include "picojson.h"
    #endif
    
    namespace onelab {
    
      // The base parameter class.
      class parameter {
      private:
        // the name of the parameter, including its '/'-separated path in the
        // parameter hierarchy. Parameters or subpaths can start with numbers to
        // force their relative ordering (such numbers are automatically hidden in
        // the interface). All strings in onelab are supposed to be UTF8-encoded.
        std::string _name;
        // the parameter label: if provided it serves as a better way to display the
        // parameter in the interface
        std::string _label;
        // a help string
        std::string _help;
        // map of clients that use this parameter, associated with a "changed" flag
        // (set to 0 if the client has already been run with the current value of
        // the parameter; set to defaultChangedValue() when the value of a parameter
        // has been changed; values between 1 and defaultChangedValue() can be used
        // to "modulate" the degree of change, e.g. to change the action to be
        // performed depending on the "severity" of the change)
        std::map<std::string, int> _clients;
        // flag indicating what the "changed" value should be set to when a
        // parameter is updated to a different value (if set to 0, the parameter
        // will appear as never being changed)
        int _changedValue;
        // should the parameter be visible in the interface?
        bool _visible;
        // sould the paramete be "read-only" (not settable by the user)
        bool _readOnly;
    
      protected:
        // optional additional attributes
        std::map<std::string, std::string> _attributes;
    
      public:
        parameter(const std::string &name = "", const std::string &label = "",
                  const std::string &help = "")
          : _name(name), _label(label), _help(help), _visible(true),
            _readOnly(false)
        {
          _changedValue = defaultChangedValue();
        }
        virtual ~parameter() {}
        void setName(const std::string &name) { _name = name; }
        void setLabel(const std::string &label) { _label = label; }
        void setHelp(const std::string &help) { _help = help; }
        void setChanged(int changed, const std::string &client = "")
        {
          if(client.size()) {
            std::map<std::string, int>::iterator it = _clients.find(client);
            if(it != _clients.end()) it->second = changed;
          }
          else {
            for(std::map<std::string, int>::iterator it = _clients.begin();
                it != _clients.end(); it++)
              it->second = changed;
          }
        }
        void setChangedValue(int value) { _changedValue = value; }
        void setNeverChanged(bool never)
        {
          _changedValue = never ? 0 : defaultChangedValue();
        }
        void setVisible(bool visible) { _visible = visible; }
        void setReadOnly(bool readOnly) { _readOnly = readOnly; }
        void setAttribute(const std::string &key, const std::string &value)
        {
          _attributes[key] = value;
        }
        void setAttributes(const std::map<std::string, std::string> &attributes)
        {
          _attributes = attributes;
        }
        void setClients(const std::map<std::string, int> &clients)
        {
          _clients = clients;
        }
        void addClient(const std::string &client, int changed)
        {
          if(_clients.find(client) == _clients.end()) _clients[client] = changed;
        }
        void addClients(const std::map<std::string, int> &clients)
        {
          _clients.insert(clients.begin(), clients.end());
        }
        bool hasClient(const std::string &client)
        {
          return (_clients.find(client) != _clients.end());
        }
        int getNumClients() { return (int)_clients.size(); };
        virtual std::string getType() const = 0;
        const std::string &getName() const { return _name; }
        const std::string &getLabel() const { return _label; }
        const std::string &getHelp() const { return _help; }
        std::string getPath() const
        {
          std::string::size_type last = _name.find_last_of('/');
          return _name.substr(0, last);
        }
        std::string getShortName() const
        {
          std::string units = getAttribute("Units");
          if(_label.size()) {
            if(units.empty())
              return _label;
            else
              return _label + " [" + units + "]";
          }
          std::string s = _name;
          // remove path
          std::string::size_type last = _name.find_last_of('/');
          if(last != std::string::npos) s = _name.substr(last + 1);
          // remove starting white space
          while(s.size() && s[0] == ' ') s = s.substr(1);
          // remove starting braces: can be used to order parameters 'from the end',
          // as the ASCII code is after numbers and letters
          while(s.size() && (s[0] == '}' || s[0] == '{')) s = s.substr(1);
          // remove starting numbers: can be used to order parameters 'from the
          // start'
          while(s.size() && s[0] >= '0' && s[0] <= '9') s = s.substr(1);
          if(units.empty())
            return s;
          else
            return s + " [" + units + "]";
        }
        int getChanged(const std::string &client = "") const
        {
          if(client.size()) {
            std::map<std::string, int>::const_iterator it = _clients.find(client);
            if(it != _clients.end())
              return it->second;
            else
              return 0;
          }
          else {
            int changed = 0;
            for(std::map<std::string, int>::const_iterator it = _clients.begin();
                it != _clients.end(); it++) {
              changed = std::max(changed, it->second);
            }
            return changed;
          }
        }
        int getChangedValue() const { return _changedValue; }
        bool getNeverChanged() const { return _changedValue ? false : true; }
        bool getVisible() const { return _visible; }
        bool getReadOnly() const { return _readOnly; }
        std::string getAttribute(const std::string &key) const
        {
          std::map<std::string, std::string>::const_iterator it =
            _attributes.find(key);
          if(it != _attributes.end()) return it->second;
          return "";
        }
        const std::map<std::string, std::string> &getAttributes() const
        {
          return _attributes;
        }
        const std::map<std::string, int> &getClients() const { return _clients; }
        static char charSep() { return '\0'; }
        static double maxNumber() { return 1e200; }
        static std::string version() { return "1.3"; }
        static int defaultChangedValue() { return 31; }
        static std::string getNextToken(const std::string &msg,
                                        std::string::size_type &first,
                                        char separator = charSep())
        {
          if(first == std::string::npos) return "";
          std::string::size_type last = msg.find_first_of(separator, first);
          std::string next("");
          if(last == std::string::npos) {
            next = msg.substr(first);
            first = last;
          }
          else if(first == last) {
            next = "";
            first = last + 1;
          }
          else {
            next = msg.substr(first, last - first);
            first = last + 1;
          }
          return next;
        }
        static std::vector<std::string> split(const std::string &msg,
                                              char separator = charSep())
        {
          std::vector<std::string> out;
          std::string::size_type first = 0;
          while(first != std::string::npos)
            out.push_back(getNextToken(msg, first, separator));
          return out;
        }
        static std::string trim(const std::string &str,
                                const std::string &whitespace = " \t\n")
        {
          std::string::size_type strBegin = str.find_first_not_of(whitespace);
          if(strBegin == std::string::npos) return ""; // no content
          std::string::size_type strEnd = str.find_last_not_of(whitespace);
          std::string::size_type strRange = strEnd - strBegin + 1;
          return str.substr(strBegin, strRange);
        }
        std::string sanitize(const std::string &in) const
        {
          std::string out(in);
          for(unsigned int i = 0; i < in.size(); i++)
            if(out[i] == charSep()) out[i] = ' ';
          return out;
        }
        virtual std::string toChar() const
        {
          std::ostringstream sstream;
          sstream << version() << charSep() << getType() << charSep()
                  << sanitize(getName()) << charSep() << sanitize(getLabel())
                  << charSep() << sanitize(getHelp()) << charSep()
                  << getChangedValue() << charSep() << (getVisible() ? 1 : 0)
                  << charSep() << (getReadOnly() ? 1 : 0) << charSep()
                  << _attributes.size() << charSep();
          for(std::map<std::string, std::string>::const_iterator it =
                _attributes.begin();
              it != _attributes.end(); it++)
            sstream << sanitize(it->first) << charSep() << sanitize(it->second)
                    << charSep();
          sstream << getClients().size() << charSep();
          for(std::map<std::string, int>::const_iterator it = getClients().begin();
              it != getClients().end(); it++)
            sstream << sanitize(it->first) << charSep() << (it->second ? 1 : 0)
                    << charSep();
          return sstream.str();
        }
        virtual std::string::size_type fromChar(const std::string &msg)
        {
          std::string::size_type pos = 0;
          if(getNextToken(msg, pos) != version()) return 0;
          if(getNextToken(msg, pos) != getType()) return 0;
          setName(getNextToken(msg, pos));
          setLabel(getNextToken(msg, pos));
          setHelp(getNextToken(msg, pos));
          setChangedValue(atoi(getNextToken(msg, pos).c_str()));
          setVisible(atoi(getNextToken(msg, pos).c_str()));
          setReadOnly(atoi(getNextToken(msg, pos).c_str()));
          int numAttributes = atoi(getNextToken(msg, pos).c_str());
          for(int i = 0; i < numAttributes; i++) {
            std::string key(getNextToken(msg, pos));
            setAttribute(key, getNextToken(msg, pos));
          }
          int numClients = atoi(getNextToken(msg, pos).c_str());
          for(int i = 0; i < numClients; i++) {
            std::string client(getNextToken(msg, pos));
            int changed = atoi(getNextToken(msg, pos).c_str());
            addClient(client, changed);
          }
          return pos;
        }
        static void getInfoFromChar(const std::string &msg, std::string &version,
                                    std::string &type, std::string &name)
        {
          std::string::size_type first = 0;
          version = getNextToken(msg, first);
          type = getNextToken(msg, first);
          name = getNextToken(msg, first);
        }
        static bool fromFile(std::vector<std::string> &msg, FILE *fp)
        {
          msg.clear();
          char tmp[1000];
          if(!fgets(tmp, sizeof(tmp), fp)) return false; // first line is comment
          while(!feof(fp)) {
            int numc = 0;
            if(!fscanf(fp, "%d ", &numc)) break; // space is important
            if(!numc) break;
            msg.push_back("");
            for(int i = 0; i < numc; i++) msg.back() += fgetc(fp);
            if(!fgets(tmp, sizeof(tmp), fp)) break; // end of line
          }
          return true;
        }
        static bool toFile(const std::vector<std::string> &msg, FILE *fp,
                           const std::string &creator)
        {
          time_t now;
          time(&now);
          fprintf(fp, "ONELAB database created by %s on %s", creator.c_str(),
                  ctime(&now));
          for(unsigned int i = 0; i < msg.size(); i++) {
            fprintf(fp, "%d ", (int)msg[i].size());
            for(unsigned int j = 0; j < msg[i].size(); j++) fputc(msg[i][j], fp);
            fputc('\n', fp);
          }
          return true;
        }
        std::string sanitizeJSON(const std::string &in) const
        {
          // FIXME: replace \n with \\n, \t with \\t, etc.
          return in;
        }
        virtual std::string toJSON() const
        {
          std::ostringstream sstream;
          sstream << "\"type\":\"" << getType() << "\""
                  << ", \"name\":\"" << sanitizeJSON(getName()) << "\"";
          if(getLabel().size())
            sstream << ", \"label\":\"" << sanitizeJSON(getLabel()) << "\"";
          if(getHelp().size())
            sstream << ", \"help\":\"" << sanitizeJSON(getHelp()) << "\"";
          sstream << ", \"changedValue\":" << getChangedValue()
                  << ", \"visible\":" << (getVisible() ? "true" : "false")
                  << ", \"readOnly\":" << (getReadOnly() ? "true" : "false");
          if(_attributes.size()) {
            sstream << ", \"attributes\":{ ";
            for(std::map<std::string, std::string>::const_iterator it =
                  _attributes.begin();
                it != _attributes.end(); it++) {
              if(it != _attributes.begin()) sstream << ", ";
              sstream << "\"" << sanitizeJSON(it->first) << "\":\""
                      << sanitizeJSON(it->second) << "\"";
            }
            sstream << " }";
          }
          if(getClients().size()) {
            sstream << ", \"clients\":{ ";
            for(std::map<std::string, int>::const_iterator it =
                  getClients().begin();
                it != getClients().end(); it++) {
              if(it != getClients().begin()) sstream << ", ";
              sstream << "\"" << sanitizeJSON(it->first) << "\":" << it->second;
            }
            sstream << " }";
          }
          return sstream.str();
        }
    #if defined(HAVE_PICOJSON)
        virtual bool fromJSON(const picojson::value::object &par)
        {
          for(picojson::value::object::const_iterator it = par.begin();
              it != par.end(); ++it) {
            if(it->first == "name") {
              if(!it->second.is<std::string>()) return false;
              setName(it->second.get<std::string>());
            }
            else if(it->first == "label") {
              if(!it->second.is<std::string>()) return false;
              setLabel(it->second.get<std::string>());
            }
            else if(it->first == "help") {
              if(!it->second.is<std::string>()) return false;
              setHelp(it->second.get<std::string>());
            }
            else if(it->first == "changedValue") {
              if(!it->second.is<double>()) return false;
              setChangedValue((int)it->second.get<double>());
            }
            else if(it->first == "visible") {
              if(!it->second.is<bool>()) return false;
              setVisible(it->second.get<bool>());
            }
            else if(it->first == "readOnly") {
              if(!it->second.is<bool>()) return false;
              setReadOnly(it->second.get<bool>());
            }
            else if(it->first == "attributes") {
              if(!it->second.is<picojson::object>()) return false;
              const picojson::value::object &obj =
                it->second.get<picojson::object>();
              for(picojson::value::object::const_iterator i = obj.begin();
                  i != obj.end(); ++i) {
                std::string key(i->first);
                if(!i->second.is<std::string>()) return false;
                setAttribute(key, i->second.get<std::string>());
              }
            }
            else if(it->first == "clients") {
              if(!it->second.is<picojson::object>()) return false;
              const picojson::value::object &obj =
                it->second.get<picojson::object>();
              for(picojson::value::object::const_iterator i = obj.begin();
                  i != obj.end(); ++i) {
                std::string client(i->first);
                if(!i->second.is<double>()) return false;
                addClient(client, (int)i->second.get<double>());
              }
            }
          }
          return true;
        }
    #endif
      };
    
      class parameterLessThan {
      public:
        bool operator()(const parameter *p1, const parameter *p2) const
        {
          return p1->getName() < p2->getName();
        }
      };
    
      // The number class. Numbers are stored internally as double precision real
      // numbers.
      class number : public parameter {
      private:
        std::vector<double> _values, _choices;
        double _min, _max, _step;
        // when in a loop, indicates current index in the vector _choices; is -1
        // when not in a loop
        int _index;
        std::map<double, std::string> _valueLabels;
    
      public:
        number(const std::string &name = "", double value = 0.,
               const std::string &label = "", const std::string &help = "")
          : parameter(name, label, help), _values(std::vector<double>(1, value)),
            _min(-maxNumber()), _max(maxNumber()), _step(0.), _index(-1)
        {
        }
        number(const std::string &name, const std::vector<double> &values,
               const std::string &label = "", const std::string &help = "")
          : parameter(name, label, help), _values(values), _min(-maxNumber()),
            _max(maxNumber()), _step(0.), _index(-1)
        {
        }
        void setValue(double value)
        {
          _values.resize(1);
          _values[0] = value;
        }
        void setValues(const std::vector<double> &values) { _values = values; }
        void setMin(double min) { _min = min; }
        void setMax(double max) { _max = max; }
        void setStep(double step) { _step = step; }
        void setIndex(int index) { _index = index; }
        void setChoices(const std::vector<double> &choices) { _choices = choices; }
        void setChoiceLabels(const std::vector<std::string> &labels)
        {
          if(labels.size() != _choices.size()) return;
          if(_valueLabels.size()) _valueLabels.clear();
          for(unsigned int i = 0; i < _choices.size(); i++)
            _valueLabels[_choices[i]] = labels[i];
        }
        void setValueLabels(const std::map<double, std::string> &valueLabels)
        {
          _valueLabels = valueLabels;
        }
        void setValueLabel(double value, const std::string &label)
        {
          _valueLabels[value] = label;
        }
        std::string getType() const { return "number"; }
        double getValue() const
        {
          if(_values.empty()) return 0.;
          return _values[0];
        }
        const std::vector<double> &getValues() const { return _values; }
        unsigned int getNumValues() const { return _values.size(); }
        double getMin() const { return _min; }
        double getMax() const { return _max; }
        double getStep() const { return _step; }
        int getIndex() const { return _index; }
        const std::vector<double> &getChoices() const { return _choices; }
        const std::map<double, std::string> &getValueLabels() const
        {
          return _valueLabels;
        }
        std::string getValueLabel(double value) const
        {
          std::map<double, std::string>::const_iterator it =
            _valueLabels.find(value);
          if(it != _valueLabels.end()) return it->second;
          return "";
        }
        void update(const number &p)
        {
          addClients(p.getClients());
          setLabel(p.getLabel());
          setHelp(p.getHelp());
          setVisible(p.getVisible());
          setReadOnly(p.getReadOnly());
          setAttributes(p.getAttributes());
          bool changed = false;
          for(unsigned int i = 0; i < p.getValues().size(); i++) {
            if(p.getValues()[i] != getValues()[i]) {
              changed = true;
              break;
            }
          }
          if(changed) {
            setValues(p.getValues());
            setChanged(getChangedValue());
          }
          setMin(p.getMin());
          setMax(p.getMax());
          setStep(p.getStep());
          setIndex(p.getIndex());
          setChoices(p.getChoices());
          setValueLabels(p.getValueLabels());
          if(getNeverChanged()) setChanged(0);
        }
        std::string toChar() const
        {
          std::ostringstream sstream;
          sstream.precision(16);
          sstream << parameter::toChar() << _values.size() << charSep();
          for(unsigned int i = 0; i < _values.size(); i++)
            sstream << _values[i] << charSep();
          sstream << _min << charSep() << _max << charSep() << _step << charSep()
                  << _index << charSep() << _choices.size() << charSep();
          for(unsigned int i = 0; i < _choices.size(); i++)
            sstream << _choices[i] << charSep();
          sstream << _valueLabels.size() << charSep();
          for(std::map<double, std::string>::const_iterator it =
                _valueLabels.begin();
              it != _valueLabels.end(); it++) {
            sstream << it->first << charSep() << sanitize(it->second) << charSep();
          }
          return sstream.str();
        }
        std::string::size_type fromChar(const std::string &msg)
        {
          std::string::size_type pos = parameter::fromChar(msg);
          if(!pos) return 0;
          _values.resize(atoi(getNextToken(msg, pos).c_str()));
          for(unsigned int i = 0; i < _values.size(); i++)
            _values[i] = atof(getNextToken(msg, pos).c_str());
          setMin(atof(getNextToken(msg, pos).c_str()));
          setMax(atof(getNextToken(msg, pos).c_str()));
          setStep(atof(getNextToken(msg, pos).c_str()));
          setIndex(atoi(getNextToken(msg, pos).c_str()));
          _choices.resize(atoi(getNextToken(msg, pos).c_str()));
          for(unsigned int i = 0; i < _choices.size(); i++)
            _choices[i] = atof(getNextToken(msg, pos).c_str());
          int numValueLabels = atoi(getNextToken(msg, pos).c_str());
          for(int i = 0; i < numValueLabels; i++) {
            double value = atof(getNextToken(msg, pos).c_str());
            _valueLabels[value] = getNextToken(msg, pos);
          }
          return pos;
        }
        std::string toJSON() const
        {
          std::ostringstream sstream;
          sstream.precision(16);
          sstream << "{ " << parameter::toJSON() << ", \"values\":[ ";
          for(unsigned int i = 0; i < _values.size(); i++) {
            if(i) sstream << ", ";
            sstream << _values[i];
          }
          sstream << " ]"
                  << ", \"min\":" << _min << ", \"max\":" << _max
                  << ", \"step\":" << _step << ", \"index\":" << _index;
          if(_choices.size()) {
            sstream << ", \"choices\":[ ";
            for(unsigned int i = 0; i < _choices.size(); i++) {
              if(i) sstream << ", ";
              sstream << _choices[i];
            }
            sstream << " ]";
          }
          if(_valueLabels.size()) {
            sstream << ", \"valueLabels\":{ ";
            for(std::map<double, std::string>::const_iterator it =
                  _valueLabels.begin();
                it != _valueLabels.end(); it++) {
              if(it != _valueLabels.begin()) sstream << ", ";
              sstream << "\"" << sanitizeJSON(it->second) << "\":" << it->first;
            }
            sstream << " }";
          }
          sstream << " }";
          return sstream.str();
        }
        bool fromJSON(const std::string &json)
        {
    #if defined(HAVE_PICOJSON)
          picojson::value v;
          std::string err = picojson::parse(v, json);
          if(err.size()) return false;
          if(!v.is<picojson::object>()) return false;
          const picojson::value::object &par = v.get<picojson::object>();
          picojson::value::object::const_iterator it = par.find("type");
          if(it == par.end()) return false;
          if(it->second.to_str() == "number") {
            fromJSON(par);
            return true;
          }
    #endif
          return false;
        }
    #if defined(HAVE_PICOJSON)
        bool fromJSON(const picojson::value::object &par)
        {
          if(!parameter::fromJSON(par)) return false;
          for(picojson::value::object::const_iterator it = par.begin();
              it != par.end(); ++it) {
            if(it->first == "values") {
              if(!it->second.is<picojson::array>()) return false;
              const picojson::value::array &arr = it->second.get<picojson::array>();
              _values.resize(arr.size());
              for(unsigned int i = 0; i < arr.size(); i++) {
                if(!arr[i].is<double>()) return false;
                _values[i] = arr[i].get<double>();
              }
            }
            else if(it->first == "min") {
              if(!it->second.is<double>()) return false;
              setMin(it->second.get<double>());
            }
            else if(it->first == "max") {
              if(!it->second.is<double>()) return false;
              setMax(it->second.get<double>());
            }
            else if(it->first == "step") {
              if(!it->second.is<double>()) return false;
              setStep(it->second.get<double>());
            }
            else if(it->first == "index") {
              if(!it->second.is<double>()) return false;
              setIndex((int)it->second.get<double>());
            }
            else if(it->first == "choices") {
              if(!it->second.is<picojson::array>()) return false;
              const picojson::value::array &arr = it->second.get<picojson::array>();
              _choices.resize(arr.size());
              for(unsigned int i = 0; i < arr.size(); i++) {
                if(!arr[i].is<double>()) return false;
                _choices[i] = arr[i].get<double>();
              }
            }
            else if(it->first == "valueLabels") {
              if(!it->second.is<picojson::object>()) return false;
              const picojson::value::object &obj =
                it->second.get<picojson::object>();
              for(picojson::value::object::const_iterator i = obj.begin();
                  i != obj.end(); ++i) {
                if(!i->second.is<double>()) return false;
                _valueLabels[i->second.get<double>()] = i->first;
              }
            }
          }
          return true;
        }
    #endif
      };
    
      // The string class. A string has a mutable "kind", that can be changed at
      // runtime. Kinds leading to specific behavior in Gmsh are: "file".
      class string : public parameter {
      private:
        std::vector<std::string> _values, _choices;
        std::string _kind;
    
      public:
        string(const std::string &name = "", const std::string &value = "",
               const std::string &label = "", const std::string &help = "")
          : parameter(name, label, help),
            _values(std::vector<std::string>(1, value)), _kind("generic")
        {
        }
        string(const std::string &name, const std::vector<std::string> &values,
               const std::string &label = "", const std::string &help = "")
          : parameter(name, label, help), _values(values), _kind("generic")
        {
        }
        void setValue(const std::string &value)
        {
          _values.resize(1);
          _values[0] = value;
        }
        void setValues(const std::vector<std::string> &values) { _values = values; }
        void setKind(const std::string &kind) { _kind = kind; }
        void setChoices(const std::vector<std::string> &choices)
        {
          _choices = choices;
        }
        std::string getType() const { return "string"; }
        const std::string &getValue() const
        {
          static std::string n("");
          if(_values.empty()) return n;
          return _values[0];
        }
        const std::vector<std::string> &getValues() const { return _values; }
        unsigned int getNumValues() const { return _values.size(); }
        const std::string &getKind() const { return _kind; }
        const std::vector<std::string> &getChoices() const { return _choices; }
        void update(const string &p)
        {
          addClients(p.getClients());
          setLabel(p.getLabel());
          setHelp(p.getHelp());
          setVisible(p.getVisible());
          setReadOnly(p.getReadOnly());
          setAttributes(p.getAttributes());
          bool changed = false;
          for(unsigned int i = 0; i < p.getValues().size(); i++) {
            if(p.getValues()[i] != getValues()[i]) {
              changed = true;
              break;
            }
          }
          if(changed) {
            setValues(p.getValues());
            setChanged(getChangedValue());
          }
          if(p.getKind() != getKind()) {
            setKind(p.getKind());
            setChanged(getChangedValue());
          }
          setChoices(p.getChoices());
          if(getNeverChanged()) setChanged(0);
        }
        std::string toChar() const
        {
          std::ostringstream sstream;
          sstream << parameter::toChar() << _values.size() << charSep();
          for(unsigned int i = 0; i < _values.size(); i++)
            sstream << sanitize(_values[i]) << charSep();
          sstream << sanitize(_kind) << charSep() << _choices.size() << charSep();
          for(unsigned int i = 0; i < _choices.size(); i++)
            sstream << sanitize(_choices[i]) << charSep();
          return sstream.str();
        }
        std::string::size_type fromChar(const std::string &msg)
        {
          std::string::size_type pos = parameter::fromChar(msg);
          if(!pos) return 0;
          _values.resize(atoi(getNextToken(msg, pos).c_str()));
          for(unsigned int i = 0; i < _values.size(); i++)
            _values[i] = getNextToken(msg, pos);
          setKind(getNextToken(msg, pos));
          _choices.resize(atoi(getNextToken(msg, pos).c_str()));
          for(unsigned int i = 0; i < _choices.size(); i++)
            _choices[i] = getNextToken(msg, pos);
          return pos;
        }
        std::string toJSON() const
        {
          std::ostringstream sstream;
          sstream << "{ " << parameter::toJSON() << ", \"values\":[ ";
          for(unsigned int i = 0; i < _values.size(); i++) {
            if(i) sstream << ", ";
            sstream << "\"" << sanitizeJSON(_values[i]) << "\"";
          }
          sstream << " ] ";
          if(_kind.size())
            sstream << ", \"kind\":\"" << sanitizeJSON(_kind) << "\"";
          if(_choices.size()) {
            sstream << ", \"choices\":[ ";
            for(unsigned int i = 0; i < _choices.size(); i++) {
              if(i) sstream << ", ";
              sstream << "\"" << sanitizeJSON(_choices[i]) << "\"";
            }
            sstream << " ]";
          }
          sstream << " }";
          return sstream.str();
        }
        bool fromJSON(const std::string &json)
        {
    #if defined(HAVE_PICOJSON)
          picojson::value v;
          std::string err = picojson::parse(v, json);
          if(err.size()) return false;
          if(!v.is<picojson::object>()) return false;
          const picojson::value::object &par = v.get<picojson::object>();
          picojson::value::object::const_iterator it = par.find("type");
          if(it == par.end()) return false;
          if(it->second.to_str() == "string") {
            fromJSON(par);
            return true;
          }
    #endif
          return false;
        }
    #if defined(HAVE_PICOJSON)
        bool fromJSON(const picojson::value::object &par)
        {
          if(!parameter::fromJSON(par)) return false;
          for(picojson::value::object::const_iterator it = par.begin();
              it != par.end(); ++it) {
            if(it->first == "values") {
              if(!it->second.is<picojson::array>()) return false;
              const picojson::value::array &arr = it->second.get<picojson::array>();
              _values.resize(arr.size());
              for(unsigned int i = 0; i < arr.size(); i++) {
                if(!arr[i].is<std::string>()) return false;
                _values[i] = arr[i].get<std::string>();
              }
            }
            else if(it->first == "kind") {
              if(!it->second.is<std::string>()) return false;
              setKind(it->second.get<std::string>());
            }
            else if(it->first == "choices") {
              if(!it->second.is<picojson::array>()) return false;
              const picojson::value::array &arr = it->second.get<picojson::array>();
              _choices.resize(arr.size());
              for(unsigned int i = 0; i < arr.size(); i++) {
                if(!arr[i].is<std::string>()) return false;
                _choices[i] = arr[i].get<std::string>();
              }
            }
          }
          return true;
        }
    #endif
      };
    
      // The parameter space, i.e., the set of parameters stored and handled by the
      // onelab server.
      class parameterSpace {
      private:
        std::set<number *, parameterLessThan> _numbers;
        std::set<string *, parameterLessThan> _strings;
    #if __cplusplus >= 201103L
        std::mutex _mutex;
    #endif
    
        // delete a parameter from the parameter space
        template <class T>
        bool _clear(const std::string &name, const std::string &client,
                    std::set<T *, parameterLessThan> &ps)
        {
          if(name.empty() && client.size()) {
            std::vector<T *> toDelete;
            for(typename std::set<T *, parameterLessThan>::iterator it = ps.begin();
                it != ps.end();) {
              T *p = *it;
              if(p->hasClient(client)) {
                ps.erase(it++); // to avoid invalid iterator
                delete p;
              }
              else {
                it++;
              }
            }
          }
          else {
            T tmp(name);
            typename std::set<T *, parameterLessThan>::iterator it = ps.find(&tmp);
            if(it != ps.end()) {
              T *p = *it;
              if(client.empty() || p->hasClient(client)) {
                ps.erase(it);
                delete p;
                return true;
              }
            }
          }
          return false;
        }
        // set a parameter in the parameter space; if it already exists, update it
        // (adding new clients if necessary). This would need to be locked to avoid
        // race conditions when several clients try to set a parameter at the same
        // time.
        template <class T>
        bool _set(const T &p, const std::string &client,
                  std::set<T *, parameterLessThan> &ps)
        {
    #if __cplusplus >= 201103L
          _mutex.lock();
    #endif
          typename std::set<T *, parameterLessThan>::iterator it = ps.find((T *)&p);
          if(it != ps.end()) {
            (*it)->update(p);
            if(client.size())
              (*it)->addClient(client, parameter::defaultChangedValue());
          }
          else {
            T *newp = new T(p);
            if(client.size())
              newp->addClient(client, parameter::defaultChangedValue());
            ps.insert(newp);
          }
    #if __cplusplus >= 201103L
          _mutex.unlock();
    #endif
          return true;
        }
        // get the parameter matching the given name, or all the parameters in the
        // category if no name is given. If we find a given parameter by name, we
        // add the client requesting the parameter to the list of clients for this
        // parameter. This would also need to be locked.
        template <class T>
        bool _get(std::vector<T> &p, const std::string &name,
                  const std::string &client, std::set<T *, parameterLessThan> &ps)
        {
          p.clear();
          if(name.empty()) {
            for(typename std::set<T *, parameterLessThan>::iterator it = ps.begin();
                it != ps.end(); it++)
              p.push_back(**it);
          }
          else {
            T tmp(name);
            typename std::set<T *, parameterLessThan>::iterator it = ps.find(&tmp);
            if(it != ps.end()) {
              if(client.size()){
    #if __cplusplus >= 201103L
                _mutex.lock();
    #endif
                (*it)->addClient(client, parameter::defaultChangedValue());
    #if __cplusplus >= 201103L
                _mutex.unlock();
    #endif
              }
              p.push_back(**it);
            }
          }
          return true;
        }
        template <class T>
        T *_getPtr(std::string name, const std::string client,
                   std::set<T *, parameterLessThan> ps)
        {
          T tmp(name);
          typename std::set<T *, parameterLessThan>::iterator it = ps.find(&tmp);
          if(it != ps.end()) {
            if(client.size()){
    #if __cplusplus >= 201103L
              _mutex.lock();
    #endif
              (*it)->addClient(client, parameter::defaultChangedValue());
    #if __cplusplus >= 201103L
              _mutex.unlock();
    #endif
            }
            return *it;
          }
          return NULL;
        }
    
      public:
        parameterSpace() {}
        ~parameterSpace() { clear(); }
        void clear(const std::string &name = "", const std::string &client = "")
        {
          if(name.empty() && client.empty()) {
            std::set<parameter *, parameterLessThan> ps;
            getAllParameters(ps);
            for(std::set<parameter *, parameterLessThan>::iterator it = ps.begin();
                it != ps.end(); it++)
              delete *it;
            _numbers.clear();
            _strings.clear();
          }
          else {
            bool done = _clear(name, client, _numbers);
            if(!done) done = _clear(name, client, _strings);
          }
        }
        bool set(const number &p, const std::string &client = "")
        {
          return _set(p, client, _numbers);
        }
        bool set(const string &p, const std::string &client = "")
        {
          return _set(p, client, _strings);
        }
        bool get(std::vector<number> &ps, const std::string &name = "",
                 const std::string &client = "")
        {
          return _get(ps, name, client, _numbers);
        }
        bool get(std::vector<string> &ps, const std::string &name = "",
                 const std::string &client = "")
        {
          return _get(ps, name, client, _strings);
        }
        void getPtr(number **ptr, const std::string &name,
                    const std::string &client = "")
        {
          *ptr = _getPtr(name, client, _numbers);
        }
        void getPtr(string **ptr, const std::string &name,
                    const std::string &client = "")
        {
          *ptr = _getPtr(name, client, _strings);
        }
        void getAllParameters(std::set<parameter *, parameterLessThan> &ps) const
        {
          ps.insert(_numbers.begin(), _numbers.end());
          ps.insert(_strings.begin(), _strings.end());
        }
        unsigned int getNumParameters()
        {
          return (int)(_numbers.size() + _strings.size());
        }
        // check if at least one parameter depends on the given client
        bool hasClient(const std::string &client) const
        {
          std::set<parameter *, parameterLessThan> ps;
          getAllParameters(ps);
          for(std::set<parameter *, parameterLessThan>::iterator it = ps.begin();
              it != ps.end(); it++)
            if((*it)->hasClient(client)) return true;
          return false;
        }
        // check if some parameters have changed (optionnally only check the
        // parameters that depend on a given client)
        int getChanged(const std::string &client = "") const
        {
          std::set<parameter *, parameterLessThan> ps;
          getAllParameters(ps);
          int changed = 0;
          for(std::set<parameter *, parameterLessThan>::iterator it = ps.begin();
              it != ps.end(); it++) {
            changed = std::max(changed, (*it)->getChanged(client));
          }
          return changed;
        }
        // set the changed flag for all the parameters that depend on the given
        // client (or for all parameters if no client name is provided)
        void setChanged(int changed, const std::string &client = "")
        {
          std::set<parameter *, parameterLessThan> ps;
          getAllParameters(ps);
          for(std::set<parameter *, parameterLessThan>::iterator it = ps.begin();
              it != ps.end(); it++)
            (*it)->setChanged(changed, client);
        }
        void thresholdChanged(int threshold, const std::string &client = "")
        {
          std::set<parameter *, parameterLessThan> ps;
          getAllParameters(ps);
          for(std::set<parameter *, parameterLessThan>::iterator it = ps.begin();
              it != ps.end(); it++) {
            int changed = (*it)->getChanged(client);
            if(changed > threshold) (*it)->setChanged(threshold, client);
          }
        }
        // serialize the parameter space (optionally only serialize those parameters
        // that depend on the given client)
        std::vector<std::string> toChar(const std::string &client = "") const
        {
          std::vector<std::string> s;
          std::set<parameter *, parameterLessThan> ps;
          getAllParameters(ps);
          for(std::set<parameter *, parameterLessThan>::const_iterator it =
                ps.begin();
              it != ps.end(); it++)
            if(client.empty() || (*it)->hasClient(client)) {
              if((*it)->getAttribute("NotInDb") != "True")
                s.push_back((*it)->toChar());
            }
          return s;
        }
        // unserialize the parameter space
        bool fromChar(const std::vector<std::string> &msg,
                      const std::string &client = "")
        {
          for(unsigned int i = 0; i < msg.size(); i++) {
            std::string version, type, name;
            onelab::parameter::getInfoFromChar(msg[i], version, type, name);
            if(onelab::parameter::version() != version) return false;
            if(type == "number") {
              number p;
              p.fromChar(msg[i]);
              set(p, client);
            }
            else if(type == "string") {
              string p;
              p.fromChar(msg[i]);
              set(p, client);
            }
            else
              return false;
          }
          return true;
        }
        bool toJSON(std::string &json, const std::string &creator = "",
                    const std::string &client = "") const
        {
          time_t now;
          time(&now);
          std::string t(ctime(&now));
          t.resize(t.size() - 1);
          json.clear();
          json += "{ \"onelab\":{\n";
          json += "  \"creator\":\"" + creator + "\",\n";
          json += "  \"date\":\"" + t + "\",\n";
          json += "  \"version\":\"" + parameter::version() + "\",\n";
          json += "  \"parameters\":[\n";
          std::set<parameter *, parameterLessThan> ps;
          getAllParameters(ps);
          for(std::set<parameter *, parameterLessThan>::const_iterator it =
                ps.begin();
              it != ps.end(); it++) {
            if(it != ps.begin()) json += ",\n";
            if(client.empty() || (*it)->hasClient(client)) {
              if((*it)->getAttribute("NotInDb") != "True") {
                json += "    " + (*it)->toJSON();
              }
            }
          }
          json += "\n  ] }\n}\n";
          return true;
        }
        bool fromJSON(const std::string &json, const std::string &client = "")
        {
    #if defined(HAVE_PICOJSON)
          picojson::value v;
          std::string err = picojson::parse(v, json);
          if(err.size()) return false;
          if(v.is<picojson::object>()){ // onelab database or single parameter
            const picojson::value::object &obj = v.get<picojson::object>();
            picojson::value::object::const_iterator it = obj.find("onelab");
            if(it != obj.end()){ // onelab database
              if(!it->second.is<picojson::object>()) return false;
              const picojson::value::object &db = it->second.get<picojson::object>();
              for(picojson::value::object::const_iterator j = db.begin(); j != db.end(); ++j) {
                if(j->first == "version") {
                  if(!j->second.is<std::string>()) return false;
                  if(j->second.get<std::string>() != parameter::version())
                    return false;
                }
                else if(j->first == "parameters") {
                  if(!j->second.is<picojson::array>()) return false;
                  const picojson::value::array &arr = j->second.get<picojson::array>();
                  for(unsigned int k = 0; k < arr.size(); k++) {
                    if(!arr[k].is<picojson::object>()) return false;
                    const picojson::value::object &par = arr[k].get<picojson::object>();
                    if(!fromJSON(par, client)) return false;
                  }
                }
              }
              return true;
            }
            else{ // single parameter
              return fromJSON(obj, client);
            }
          }
          else if(v.is<picojson::array>()){ // array of parameters
            const picojson::value::array &arr = v.get<picojson::array>();
            for(unsigned int k = 0; k < arr.size(); k++) {
              if(!arr[k].is<picojson::object>()) return false;
              const picojson::value::object &par = arr[k].get<picojson::object>();
              if(!fromJSON(par, client)) return false;
            }
            return true;
          }
          else{
            return false;
          }
    #else
          return false;
    #endif
        }
    #if defined(HAVE_PICOJSON)
        bool fromJSON(const picojson::value::object &par, const std::string &client = "")
        {
          picojson::value::object::const_iterator it = par.find("type");
          if(it == par.end()) return false;
          if(it->second.to_str() == "number") {
            number p;
            p.fromJSON(par);
            set(p, client);
            return true;
          }
          else if(it->second.to_str() == "string") {
            string p;
            p.fromJSON(par);
            set(p, client);
            return true;
          }
          return false;
        }
    #endif
      };
    
      // The onelab client: a class that communicates with the onelab server. Each
      // client should be derived from this one. A client can be understood as "one
      // simulation step in a complex computation".
      class client {
      protected:
        // the name of the client
        std::string _name;
        // the id of the client, used to create a unique socket for this client
        int _id;
        // the index of the client in an external client list (if any)
        int _index;
    
      public:
        client(const std::string &name) : _name(name), _id(0), _index(-1) {}
        virtual ~client() {}
        std::string getName() { return _name; }
        void setId(int id) { _id = id; }
        int getId() { return _id; }
        void setIndex(int index) { _index = index; }
        int getIndex() { return _index; }
        virtual bool run() { return false; }
        virtual bool isNetworkClient() { return false; }
        virtual bool kill() { return false; }
        virtual void sendInfo(const std::string &msg)
        {
          std::cout << msg << std::endl;
        }
        virtual void sendWarning(const std::string &msg)
        {
          std::cerr << msg << std::endl;
        }
        virtual void sendError(const std::string &msg)
        {
          std::cerr << msg << std::endl;
        }
        virtual void sendProgress(const std::string &msg)
        {
          std::cout << msg << std::endl;
        }
        virtual void sendMergeFileRequest(const std::string &msg) {}
        virtual void sendOpenProjectRequest(const std::string &msg) {}
        virtual void sendParseStringRequest(const std::string &msg) {}
        virtual void sendVertexArray(const std::string &msg) {}
        virtual bool clear(const std::string &name) = 0;
        virtual bool set(const number &p) = 0;
        virtual bool set(const string &p) = 0;
        virtual bool get(std::vector<number> &ps, const std::string &name = "") = 0;
        virtual bool get(std::vector<string> &ps, const std::string &name = "") = 0;
        virtual bool setAndAppendChoices(const number &p) = 0;
        virtual bool setAndAppendChoices(const string &p) = 0;
        virtual bool getWithoutChoices(std::vector<number> &ps,
                                       const std::string &name = "") = 0;
        virtual bool getWithoutChoices(std::vector<string> &ps,
                                       const std::string &name = "") = 0;
        std::vector<std::string> toChar()
        {
          std::vector<std::string> out;
          std::vector<number> n;
          get(n);
          for(unsigned int i = 0; i < n.size(); i++) out.push_back(n[i].toChar());
          std::vector<string> s;
          get(s);
          for(unsigned int i = 0; i < s.size(); i++) out.push_back(s[i].toChar());
          return out;
        }
        bool fromChar(const std::vector<std::string> &msg)
        {
          for(unsigned int i = 0; i < msg.size(); i++) {
            std::string version, type, name;
            onelab::parameter::getInfoFromChar(msg[i], version, type, name);
            if(onelab::parameter::version() != version) return false;
            if(type == "number") {
              number p;
              p.fromChar(msg[i]);
              set(p);
            }
            else if(type == "string") {
              string p;
              p.fromChar(msg[i]);
              set(p);
            }
            else
              return false;
          }
          return true;
        }
        bool toFile(FILE *fp) { return parameter::toFile(toChar(), fp, getName()); }
        bool fromFile(FILE *fp)
        {
          std::vector<std::string> msg;
          if(parameter::fromFile(msg, fp)) return fromChar(msg);
          return false;
        }
      };
    
      // The onelab server: a singleton that stores the parameter space and
      // interacts with onelab clients.
      class server {
      private:
        // the unique server (singleton behaviour due to the "static" specifier)
        static server *_server;
        // the address of the server
        std::string _address;
        // the connected clients
        std::set<client *> _clients;
        // the parameter space
        parameterSpace _parameterSpace;
    
      public:
        server(const std::string &address = "") : _address(address) {}
        ~server() {}
        static server *instance(const std::string &address = "")
        {
          if(!_server) _server = new server(address);
          return _server;
        }
        static void setInstance(server *s) { _server = s; }
        void clear(const std::string &name = "", const std::string &client = "")
        {
          _parameterSpace.clear(name, client);
        }
        template <class T> bool set(const T &p, const std::string &client = "")
        {
          return _parameterSpace.set(p, client);
        }
        template <class T>
        bool get(std::vector<T> &ps, const std::string &name = "",
                 const std::string &client = "")
        {
          return _parameterSpace.get(ps, name, client);
        }
        typedef std::set<client *>::iterator citer;
        citer firstClient() { return _clients.begin(); }
        citer lastClient() { return _clients.end(); }
        int getNumClients() { return (int)_clients.size(); };
        citer findClient(const std::string &name)
        {
          for(citer it = _clients.begin(); it != _clients.end(); it++)
            if((*it)->getName() == name) return it;
          return _clients.end();
        }
        void registerClient(client *c)
        {
          _clients.insert(c);
          c->setId(_clients.size());
        }
        void unregisterClient(client *c) { _clients.erase(c); }
        void setChanged(int changed, const std::string &client = "")
        {
          _parameterSpace.setChanged(changed, client);
        }
        int getChanged(const std::string &client = "")
        {
          return _parameterSpace.getChanged(client);
        }
        void thresholdChanged(int value, const std::string &client = "")
        {
          _parameterSpace.thresholdChanged(value, client);
        }
        unsigned int getNumParameters()
        {
          return _parameterSpace.getNumParameters();
        }
        std::vector<std::string> toChar(const std::string &client = "")
        {
          return _parameterSpace.toChar(client);
        }
        bool fromChar(const std::vector<std::string> &msg,
                      const std::string &client = "")
        {
          return _parameterSpace.fromChar(msg, client);
        }
        bool toFile(FILE *fp, const std::string &client = "")
        {
          return parameter::toFile(toChar(client), fp, "onelab server");
        }
        bool fromFile(FILE *fp, const std::string &client = "")
        {
          std::vector<std::string> msg;
          if(parameter::fromFile(msg, fp)) return fromChar(msg, client);
          return false;
        }
        bool toJSON(std::string &json, const std::string &client = "")
        {
          return _parameterSpace.toJSON(json, client);
        }
        bool fromJSON(const std::string &json, const std::string &client = "")
        {
          return _parameterSpace.fromJSON(json, client);
        }
      };
    
      // A local client, which lives in the same memory space as the server.
      class localClient : public client {
      private:
        template <class T> bool _set(const T &p)
        {
          server::instance()->set(p, _name);
          return true;
        }
        template <class T>
        bool _get(std::vector<T> &ps, const std::string &name = "")
        {
          server::instance()->get(ps, name, _name);
          return true;
        }
    
      public:
        localClient(const std::string &name) : client(name)
        {
          server::instance()->registerClient(this);
        }
        virtual ~localClient() { server::instance()->unregisterClient(this); }
        virtual bool clear(const std::string &name = "")
        {
          server::instance()->clear(name);
          return true;
        }
        virtual bool set(const number &p) { return _set(p); }
        virtual bool set(const string &p) { return _set(p); }
        virtual bool get(std::vector<number> &ps, const std::string &name = "")
        {
          return _get(ps, name);
        }
        virtual bool get(std::vector<string> &ps, const std::string &name = "")
        {
          return _get(ps, name);
        }
        virtual bool setAndAppendChoices(const number &p)
        {
          std::vector<number> ps;
          _get(ps, _name);
          std::vector<double> choices;
          if(ps.size()) choices = ps[0].getChoices();
          choices.insert(choices.end(), p.getChoices().begin(),
                         p.getChoices().end());
          number p2(p);
          p2.setChoices(choices);
          return _set(p2);
        }
        virtual bool setAndAppendChoices(const string &p)
        {
          std::vector<string> ps;
          _get(ps, _name);
          std::vector<std::string> choices;
          if(ps.size()) choices = ps[0].getChoices();
          choices.insert(choices.end(), p.getChoices().begin(),
                         p.getChoices().end());
          string p2(p);
          p2.setChoices(choices);
          return _set(p2);
        }
        virtual bool getWithoutChoices(std::vector<number> &ps,
                                       const std::string &name = "")
        {
          bool ret = _get(ps, name);
          for(unsigned int i = 0; i < ps.size(); i++)
            ps[i].setChoices(std::vector<double>());
          return ret;
        }
        virtual bool getWithoutChoices(std::vector<string> &ps,
                                       const std::string &name = "")
        {
          bool ret = _get(ps, name);
          for(unsigned int i = 0; i < ps.size(); i++)
            ps[i].setChoices(std::vector<std::string>());
          return ret;
        }
      };
    
      // The local part of a network client.
      class localNetworkClient : public localClient {
      private:
        // executable of the client (including filesystem path, if necessary)
        std::string _executable;
        // treat the executable name as a full command line (will prevent the
        // escaping of the exe name, and will assume that the command line has been
        // correcly escaped)
        bool _treatExecutableAsFullCommandLine;
        // command to login to a remote host (if necessary)
        std::string _remoteLogin;
        // command line option to specify socket
        std::string _socketSwitch;
        // pid of the remote network client while it is running (-1 otherwise)
        int _pid;
        // underlying GmshServer
        GmshServer *_gmshServer;
    
      public:
        localNetworkClient(const std::string &name, const std::string &executable,
                           const std::string &remoteLogin = "",
                           bool treatExecutableAsFullCommandLine = false)
          : localClient(name), _executable(executable),
            _treatExecutableAsFullCommandLine(treatExecutableAsFullCommandLine),
            _remoteLogin(remoteLogin), _socketSwitch("-onelab"), _pid(-1),
            _gmshServer(0)
        {
        }
        virtual ~localNetworkClient() {}
        virtual bool isNetworkClient() { return true; }
        const std::string &getExecutable() { return _executable; }
        void setExecutable(const std::string &s) { _executable = s; }
        const std::string &getRemoteLogin() { return _remoteLogin; }
        bool treatExecutableAsFullCommandLine() const
        {
          return _treatExecutableAsFullCommandLine;
        }
        void setRemoteLogin(const std::string &s) { _remoteLogin = s; }
        const std::string &getSocketSwitch() { return _socketSwitch; }
        void setSocketSwitch(const std::string &s) { _socketSwitch = s; }
        int getPid() { return _pid; }
        void setPid(int pid) { _pid = pid; }
        GmshServer *getGmshServer() { return _gmshServer; }
        void setGmshServer(GmshServer *server) { _gmshServer = server; }
        virtual bool run() = 0;
        virtual bool kill() = 0;
      };
    
      // The remote part of a network client.
      class remoteNetworkClient : public client {
      private:
        // address (inet:port or unix socket) of the server
        std::string _serverAddress;
        // underlying GmshClient
        GmshClient *_gmshClient;
        // number of subclients
        int _numSubClients;
        template <class T> bool _set(const T &p, bool withChoices = true)
        {
          if(!_gmshClient) return false;
          std::string msg = p.toChar();
          _gmshClient->SendMessage(withChoices ?
                                     GmshSocket::GMSH_PARAMETER :
                                     GmshSocket::GMSH_PARAMETER_WITHOUT_CHOICES,
                                   msg.size(), &msg[0]);
          return true;
        }
        template <class T>
        bool _get(std::vector<T> &ps, const std::string &name = "",
                  bool withChoices = true)
        {
          ps.clear();
          if(!_gmshClient) return false;
          T p(name);
          std::string msg = p.toChar();
          if(name.size())
            _gmshClient->SendMessage(
              withChoices ? GmshSocket::GMSH_PARAMETER_QUERY :
                            GmshSocket::GMSH_PARAMETER_QUERY_WITHOUT_CHOICES,
              msg.size(), &msg[0]);
          else // get all parameters
            _gmshClient->SendMessage(GmshSocket::GMSH_PARAMETER_QUERY_ALL,
                                     msg.size(), &msg[0]);
    
          while(1) {
            // stop if we have no communications for 5 minutes
            int ret = _gmshClient->Select(500, 0);
            if(!ret) {
              _gmshClient->Info("Timeout: aborting remote get");
              return false;
            }
            else if(ret < 0) {
              _gmshClient->Error("Error on select: aborting remote get");
              return false;
            }
            int type, length, swap;
            if(!_gmshClient->ReceiveHeader(&type, &length, &swap)) {
              _gmshClient->Error(
                "Did not receive message header: aborting remote get");
              return false;
            }
            std::string msg(length, ' ');
            if(!_gmshClient->ReceiveMessage(length, &msg[0])) {
              _gmshClient->Error(
                "Did not receive message body: aborting remote get");
              return false;
            }
            if(type == GmshSocket::GMSH_PARAMETER) {
              T p;
              p.fromChar(msg);
              ps.push_back(p);
              return true;
            }
            if(type == GmshSocket::GMSH_PARAMETER_QUERY_ALL) {
              T p;
              p.fromChar(msg);
              ps.push_back(p);
              // do NOT return until all parameters have been downloaded
            }
            else if(type == GmshSocket::GMSH_PARAMETER_QUERY_END) {
              // all parameters have been sent
              return true;
            }
            else if(type == GmshSocket::GMSH_PARAMETER_NOT_FOUND) {
              // parameter not found
              return true;
            }
            else if(type == GmshSocket::GMSH_INFO) {
              return true;
            }
            else {
              _gmshClient->Error("Unknown message type: aborting remote get");
              return false;
            }
          }
          return true;
        }
    
      public:
        void waitOnSubClients()
        {
          if(!_gmshClient) return;
          while(_numSubClients > 0) {
            int ret = _gmshClient->Select(500, 0);
            if(!ret) {
              _gmshClient->Info("Timeout: aborting wait on subclients");
              return;
            }
            else if(ret < 0) {
              _gmshClient->Error("Error on select: aborting wait on subclients");
              return;
            }
            int type, length, swap;
            if(!_gmshClient->ReceiveHeader(&type, &length, &swap)) {
              _gmshClient->Error(
                "Did not receive message header: aborting wait on subclients");
              return;
            }
            std::string msg(length, ' ');
            if(!_gmshClient->ReceiveMessage(length, &msg[0])) {
              _gmshClient->Error(
                "Did not receive message body: aborting wait on subclients");
              return;
            }
            if(type == GmshSocket::GMSH_STOP) _numSubClients -= 1;
          }
        }
    
      public:
        remoteNetworkClient(const std::string &name,
                            const std::string &serverAddress)
          : client(name), _serverAddress(serverAddress), _numSubClients(0)
        {
          _gmshClient = new GmshClient();
          if(_gmshClient->Connect(_serverAddress.c_str()) < 0) {
            delete _gmshClient;
            _gmshClient = 0;
          }
          else {
            _gmshClient->Start();
          }
        }
        virtual ~remoteNetworkClient()
        {
          if(_gmshClient) {
            waitOnSubClients();
            _gmshClient->Stop();
            _gmshClient->Disconnect();
            delete _gmshClient;
            _gmshClient = 0;
          }
        }
        GmshClient *getGmshClient() { return _gmshClient; }
        virtual bool isNetworkClient() { return true; }
        virtual bool clear(const std::string &name = "")
        {
          if(!_gmshClient) return false;
          std::string msg = name;
          if(msg.empty()) msg = "*";
          _gmshClient->SendMessage(GmshSocket::GMSH_PARAMETER_CLEAR, msg.size(),
                                   &msg[0]);
          return true;
        }
        virtual bool set(const number &p) { return _set(p); }
        virtual bool set(const string &p) { return _set(p); }
        virtual bool get(std::vector<number> &ps, const std::string &name = "")
        {
          return _get(ps, name);
        }
        virtual bool get(std::vector<string> &ps, const std::string &name = "")
        {
          return _get(ps, name);
        }
        virtual bool setAndAppendChoices(const number &p) { return _set(p, false); }
        virtual bool setAndAppendChoices(const string &p) { return _set(p, false); }
        virtual bool getWithoutChoices(std::vector<number> &ps,
                                       const std::string &name = "")
        {
          return _get(ps, name, false);
        }
        virtual bool getWithoutChoices(std::vector<string> &ps,
                                       const std::string &name = "")
        {
          return _get(ps, name, false);
        }
        void sendInfo(const std::string &msg)
        {
          if(_gmshClient) _gmshClient->Info(msg.c_str());
        }
        void sendWarning(const std::string &msg)
        {
          if(_gmshClient) _gmshClient->Warning(msg.c_str());
        }
        void sendError(const std::string &msg)
        {
          if(_gmshClient) _gmshClient->Error(msg.c_str());
        }
        void sendProgress(const std::string &msg)
        {
          if(_gmshClient) _gmshClient->Progress(msg.c_str());
        }
        void sendMergeFileRequest(const std::string &msg)
        {
          if(_gmshClient) _gmshClient->MergeFile(msg.c_str());
        }
        void sendOpenProjectRequest(const std::string &msg)
        {
          if(_gmshClient) _gmshClient->OpenProject(msg.c_str());
        }
        void sendParseStringRequest(const std::string &msg)
        {
          if(_gmshClient) _gmshClient->ParseString(msg.c_str());
        }
        void runNonBlockingSubClient(const std::string &name,
                                     const std::string &command)
        {
    #if !defined(BUILD_IOS)
          if(!_gmshClient) {
            int res = system(command.c_str());
            if(res) {
              // report error
            }
            return;
          }
    #endif
          std::string msg = name + parameter::charSep() + command;
          _gmshClient->SendMessage(GmshSocket::GMSH_CONNECT, msg.size(), &msg[0]);
          _numSubClients += 1;
        }
        void runSubClient(const std::string &name, const std::string &command)
        {
          runNonBlockingSubClient(name, command);
          waitOnSubClients();
        }
      };
    
    } // namespace onelab
    
    #endif