Skip to content
Snippets Groups Projects
Select Git revision
  • f778a1fd0aca0c532362682e470c5214343d30dc
  • master default protected
  • patches-4.14
  • steplayer
  • bl
  • pluginMeshQuality
  • fixBugsAmaury
  • hierarchical-basis
  • alphashapes
  • relaying
  • new_export_boris
  • oras_vs_osm
  • reassign_partitions
  • distributed_fwi
  • rename-classes
  • fix/fortran-api-example-t4
  • robust_partitions
  • reducing_files
  • fix_overlaps
  • 3115-issue-fix
  • 3023-Fillet2D-Update
  • gmsh_4_14_0
  • gmsh_4_13_1
  • gmsh_4_13_0
  • gmsh_4_12_2
  • gmsh_4_12_1
  • gmsh_4_12_0
  • gmsh_4_11_1
  • 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
41 results

MathEval.h

Blame
  • onelab.h 57.93 KiB
    // ONELAB - Copyright (C) 2011-2016 ULg-UCL
    //
    // 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.
    //
    // Please report all bugs and problems to the public mailing list
    // <gmsh@onelab.info>.
    
    #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>
    #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());
          if(p.getValue() != getValue()){ // FIXME change this
            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();
        }
    #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());
          if(p.getValue() != getValue()){ // FIXME: change this
            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();
        }
    #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;
        // 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)
        {
          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);
          }
          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()) (*it)->addClient(client, parameter::defaultChangedValue());
              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()) (*it)->addClient(client, parameter::defaultChangedValue());
            return *it;
          }
          return NULL;
        }
        void _getAllParameters(std::set<parameter*, parameterLessThan> &ps) const
        {
          ps.insert(_numbers.begin(), _numbers.end());
          ps.insert(_strings.begin(), _strings.end());
        }
      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;
        }
    #if defined(HAVE_PICOJSON)
        bool fromJSON(const std::string &json, const std::string &client="")
        {
          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 &obj = v.get<picojson::object>();
          for (picojson::value::object::const_iterator i = obj.begin(); i != obj.end(); ++i) {
            if(i->first == "onelab"){ // onelab database
              if(!i->second.is<picojson::object>()) return false;
              const picojson::value::object &db = i->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>();
                    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);
                    }
                    else if(it->second.to_str() == "string"){
                      string p; p.fromJSON(par); set(p, client);
                    }
                  }
                }
              }
            }
          }
          return true;
        }
    #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);
        }
    #if defined(HAVE_PICOJSON)
        bool fromJSON(const std::string &json, const std::string &client="")
        {
          return _parameterSpace.fromJSON(json, client);
        }
    #endif
      };
    
      // 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();
        }
      };
    
    }
    
    #endif