diff --git a/PendulumC++/GmshSocket.h b/PendulumC++/GmshSocket.h
new file mode 100644
index 0000000000000000000000000000000000000000..4f6129ff8ceac515033b6df462ddb48665a4b8e2
--- /dev/null
+++ b/PendulumC++/GmshSocket.h
@@ -0,0 +1,473 @@
+// Gmsh - Copyright (C) 1997-2018 C. Geuzaine, J.-F. Remacle
+//
+// 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 _GMSH_SOCKET_H_
+#define _GMSH_SOCKET_H_
+
+//#include "GmshConfig.h"
+
+#include <string>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if defined(_AIX)
+#include <strings.h>
+#endif
+
+#if !defined(WIN32) || defined(__CYGWIN__)
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <netdb.h>
+#if defined(HAVE_NO_SOCKLEN_T)
+typedef int socklen_t;
+#endif
+#else
+#include <winsock.h>
+#include <process.h>
+typedef int socklen_t;
+#endif
+
+class GmshSocket{
+ public:
+  // types of messages that can be exchanged (never use values greater
+  // that 65535: if we receive a type > 65535 we assume that we
+  // receive data from a machine with a different byte ordering, and
+  // we swap the bytes in the payload)
+  enum MessageType{
+    GMSH_START               = 1,
+    GMSH_STOP                = 2,
+    GMSH_INFO                = 10,
+    GMSH_WARNING             = 11,
+    GMSH_ERROR               = 12,
+    GMSH_PROGRESS            = 13,
+    GMSH_MERGE_FILE          = 20,
+    GMSH_PARSE_STRING        = 21,
+    GMSH_VERTEX_ARRAY        = 22,
+    GMSH_PARAMETER           = 23,
+    GMSH_PARAMETER_QUERY     = 24,
+    GMSH_PARAMETER_QUERY_ALL = 25,
+    GMSH_PARAMETER_QUERY_END = 26,
+    GMSH_CONNECT             = 27,
+    GMSH_OLPARSE             = 28,
+    GMSH_PARAMETER_NOT_FOUND = 29,
+    GMSH_SPEED_TEST          = 30,
+    GMSH_PARAMETER_CLEAR     = 31,
+    GMSH_PARAMETER_UPDATE    = 32,
+    GMSH_OPEN_PROJECT        = 33,
+    GMSH_CLIENT_CHANGED      = 34,
+    GMSH_PARAMETER_WITHOUT_CHOICES = 35,
+    GMSH_PARAMETER_QUERY_WITHOUT_CHOICES = 36,
+    GMSH_OPTION_1            = 100,
+    GMSH_OPTION_2            = 101,
+    GMSH_OPTION_3            = 102,
+    GMSH_OPTION_4            = 103,
+    GMSH_OPTION_5            = 104};
+ protected:
+  // the socket descriptor
+  int _sock;
+  // the socket name
+  std::string _sockname;
+  // statistics
+  unsigned long int _sent, _received;
+  // send some data over the socket
+  int _SendData(const void *buffer, int bytes)
+  {
+    const char *buf = (const char *)buffer;
+    long int sofar = 0;
+    long int remaining = bytes;
+    do {
+      long int len = send(_sock, buf + sofar, remaining, 0);
+      if(len < 0) return -1; // error
+      sofar += len;
+      remaining -= len;
+    } while(remaining > 0);
+    _sent += bytes;
+    return bytes;
+  }
+  // receive some data over the socket
+  int _ReceiveData(void *buffer, int bytes)
+  {
+    char *buf = (char *)buffer;
+    long int sofar = 0;
+    long int remaining = bytes;
+    do {
+      long int len = recv(_sock, buf + sofar, remaining, 0);
+      if(len == 0) break; // we're done!
+      if(len < 0) return -1; // error
+      sofar += len;
+      remaining -= len;
+    } while(remaining > 0);
+    _received += bytes;
+    return bytes;
+  }
+  // utility function to swap bytes in an array
+  void _SwapBytes(char *array, int size, int n)
+  {
+    char *x = new char[size];
+    for(int i = 0; i < n; i++) {
+      char *a = &array[i * size];
+      memcpy(x, a, size);
+      for(int c = 0; c < size; c++)
+        a[size - 1 - c] = x[c];
+    }
+    delete [] x;
+  }
+  // sleep for some milliseconds
+  void _Sleep(int ms)
+  {
+#if !defined(WIN32) || defined(__CYGWIN__)
+    usleep(1000 * ms);
+#else
+    Sleep(ms);
+#endif
+  }
+ public:
+  GmshSocket() : _sock(0), _sent(0), _received(0)
+  {
+#if defined(WIN32) && !defined(__CYGWIN__)
+    WSADATA wsaData;
+    WSAStartup(MAKEWORD(2, 2), &wsaData);
+#endif
+  }
+  ~GmshSocket()
+  {
+#if defined(WIN32) && !defined(__CYGWIN__)
+    WSACleanup();
+#endif
+  }
+  // Wait for some data to read on the socket (if seconds and microseconds == 0
+  // we check for available data and return immediately, i.e., we do
+  // polling). Returns 1 when data is available, 0 when nothing happened before
+  // the time delay, -1 on error.
+  int Select(int seconds, int microseconds, int socket=-1)
+  {
+    int s = (socket < 0) ? _sock : socket;
+    struct timeval tv;
+    tv.tv_sec = seconds;
+    tv.tv_usec = microseconds;
+    fd_set rfds;
+    FD_ZERO(&rfds);
+    FD_SET(s, &rfds);
+    // select checks all IO descriptors between 0 and its first arg, minus 1;
+    // hence the +1 below
+    return select(s + 1, &rfds, NULL, NULL, &tv);
+  }
+  void SendMessage(int type, int length, const void *msg)
+  {
+    // send header (type + length)
+    _SendData(&type, sizeof(int));
+    _SendData(&length, sizeof(int));
+    // send body
+    _SendData(msg, length);
+  }
+  void SendString(int type, const char *str)
+  {
+    SendMessage(type, strlen(str), str);
+  }
+  void Info(const char *str){ SendString(GMSH_INFO, str); }
+  void Warning(const char *str){ SendString(GMSH_WARNING, str); }
+  void Error(const char *str){ SendString(GMSH_ERROR, str); }
+  void Progress(const char *str){ SendString(GMSH_PROGRESS, str); }
+  void MergeFile(const char *str){ SendString(GMSH_MERGE_FILE, str); }
+  void OpenProject(const char *str){ SendString(GMSH_OPEN_PROJECT, str); }
+  void ParseString(const char *str){ SendString(GMSH_PARSE_STRING, str); }
+  void SpeedTest(const char *str){ SendString(GMSH_SPEED_TEST, str); }
+  void Option(int num, const char *str)
+  {
+    if(num < 1) num = 1;
+    if(num > 5) num = 5;
+    SendString(GMSH_OPTION_1 + num - 1, str);
+  }
+  int ReceiveHeader(int *type, int *len, int *swap)
+  {
+    *swap = 0;
+    if(_ReceiveData(type, sizeof(int)) > 0){
+      if(*type > 65535){
+        // the data comes from a machine with different endianness and
+        // we must swap the bytes
+        *swap = 1;
+        _SwapBytes((char*)type, sizeof(int), 1);
+      }
+      if(_ReceiveData(len, sizeof(int)) > 0){
+        if(*swap) _SwapBytes((char*)len, sizeof(int), 1);
+        return 1;
+      }
+    }
+    return 0;
+  }
+  int ReceiveMessage(int len, void *buffer)
+  {
+    if(_ReceiveData(buffer, len) == len) return 1;
+    return 0;
+  }
+  // str should be allocated with size (len+1)
+  int ReceiveString(int len, char *str)
+  {
+    if(_ReceiveData(str, len) == len) {
+      str[len] = '\0';
+      return 1;
+    }
+    return 0;
+  }
+  void CloseSocket(int s)
+  {
+#if !defined(WIN32) || defined(__CYGWIN__)
+    close(s);
+#else
+    closesocket(s);
+#endif
+  }
+  void ShutdownSocket(int s)
+  {
+#if !defined(WIN32) || defined(__CYGWIN__)
+    shutdown(s, SHUT_RDWR);
+#endif
+  }
+  unsigned long int SentBytes(){ return _sent; }
+  unsigned long int ReceivedBytes(){ return _received; }
+};
+
+class GmshClient : public GmshSocket {
+ public:
+  GmshClient() : GmshSocket() {}
+  ~GmshClient(){}
+  int Connect(const char *sockname)
+  {
+    if(strstr(sockname, "/") || strstr(sockname, "\\") || !strstr(sockname, ":")){
+#if !defined(WIN32) || defined(__CYGWIN__)
+      // UNIX socket (testing ":" is not enough with Windows paths)
+      _sock = socket(PF_UNIX, SOCK_STREAM, 0);
+      if(_sock < 0) return -1;
+      // try to connect socket to given name
+      struct sockaddr_un addr_un;
+      memset((char *) &addr_un, 0, sizeof(addr_un));
+      addr_un.sun_family = AF_UNIX;
+      strcpy(addr_un.sun_path, sockname);
+      for(int tries = 0; tries < 5; tries++) {
+        if(connect(_sock, (struct sockaddr *)&addr_un, sizeof(addr_un)) >= 0)
+          return _sock;
+        _Sleep(100);
+      }
+#else
+      return -1; // Unix sockets are not available on Windows
+#endif
+    }
+    else{
+      // TCP/IP socket
+      _sock = socket(AF_INET, SOCK_STREAM, 0);
+      if(_sock < 0) return -1;
+      char one = 1;
+      // disable Nagle's algorithm (very slow for many small messages)
+      setsockopt(_sock, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
+      // try to connect socket to host:port
+      const char *port = strstr(sockname, ":");
+      int portno = atoi(port + 1);
+      int remotelen = strlen(sockname) - strlen(port);
+      char remote[256];
+      if(remotelen > 0)
+        strncpy(remote, sockname, remotelen);
+      remote[remotelen] = '\0';
+      struct hostent *server;
+      if(!(server = gethostbyname(remote))){
+        CloseSocket(_sock);
+        return -3; // no such host
+      }
+      struct sockaddr_in addr_in;
+      memset((char *) &addr_in, 0, sizeof(addr_in));
+      addr_in.sin_family = AF_INET;
+      memcpy((char *)&addr_in.sin_addr.s_addr, (char *)server->h_addr, server->h_length);
+      addr_in.sin_port = htons(portno);
+      for(int tries = 0; tries < 5; tries++) {
+        if(connect(_sock, (struct sockaddr *)&addr_in, sizeof(addr_in)) >= 0){
+          return _sock;
+	}
+        _Sleep(100);
+      }
+    }
+    CloseSocket(_sock);
+    return -2; // couldn't connect
+  }
+  void Start()
+  {
+    char tmp[256];
+#if !defined(WIN32) || defined(__CYGWIN__)
+    sprintf(tmp, "%d", getpid());
+#else
+    sprintf(tmp, "%d", _getpid());
+#endif
+    SendString(GMSH_START, tmp);
+  }
+  void Stop(){ SendString(GMSH_STOP, "Goodbye!"); }
+  void Disconnect(){ CloseSocket(_sock); }
+};
+
+class GmshServer : public GmshSocket{
+ private:
+  int _portno;
+ public:
+  GmshServer() : GmshSocket(), _portno(-1) {}
+  virtual ~GmshServer(){}
+  virtual int NonBlockingSystemCall(const std::string &exe, const std::string &args) = 0;
+  virtual int NonBlockingWait(double waitint, double timeout, int socket=-1) = 0;
+  // start the client by launching "exe args" (args is supposed to contain
+  // '%s' where the socket name should appear)
+  int Start(const std::string &exe, const std::string &args, const std::string &sockname,
+            double timeout)
+  {
+    _sockname = sockname;
+    int tmpsock;
+    if(strstr(_sockname.c_str(), "/") || strstr(_sockname.c_str(), "\\") ||
+       !strstr(_sockname.c_str(), ":")){
+      // UNIX socket (testing ":" is not enough with Windows paths)
+      _portno = -1;
+#if !defined(WIN32) || defined(__CYGWIN__)
+      // delete the file if it already exists
+      unlink(_sockname.c_str());
+      // create a socket
+      tmpsock = socket(PF_UNIX, SOCK_STREAM, 0);
+      if(tmpsock < 0) throw "Couldn't create socket";
+      // bind the socket to its name
+      struct sockaddr_un addr_un;
+      memset((char *) &addr_un, 0, sizeof(addr_un));
+      strcpy(addr_un.sun_path, _sockname.c_str());
+      addr_un.sun_family = AF_UNIX;
+      if(bind(tmpsock, (struct sockaddr *)&addr_un, sizeof(addr_un)) < 0){
+        CloseSocket(tmpsock);
+        throw "Couldn't bind socket to name";
+      }
+      // change permissions on the socket name in case it has to be rm'd later
+      chmod(_sockname.c_str(), 0666);
+#else
+      throw "Unix sockets not available on Windows";
+#endif
+    }
+    else{
+      // TCP/IP socket: valid names are either explicit ("hostname:12345")
+      // or implicit ("hostname:", "hostname: ", "hostname:0") in which case
+      // the system attributes at random an available port
+      const char *port = strstr(_sockname.c_str(), ":");
+      _portno = atoi(port + 1);
+      // create a socket
+      tmpsock = socket(AF_INET, SOCK_STREAM, 0);
+      char one = 1;
+      setsockopt(tmpsock, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
+
+#if !defined(WIN32) || defined(__CYGWIN__)
+      if(tmpsock < 0)
+#else
+      if(tmpsock == (int)INVALID_SOCKET)
+#endif
+        throw "Couldn't create socket";
+      // bind the socket to its name
+      struct sockaddr_in addr_in;
+      memset((char *) &addr_in, 0, sizeof(addr_in));
+      addr_in.sin_family = AF_INET;
+      addr_in.sin_addr.s_addr = INADDR_ANY;
+      addr_in.sin_port = htons(_portno); // random assign if _portno == 0
+      if(bind(tmpsock, (struct sockaddr *)&addr_in, sizeof(addr_in)) < 0){
+        CloseSocket(tmpsock);
+        throw "Couldn't bind socket to name";
+      }
+      if(!_portno){ // retrieve name if randomly assigned port
+        socklen_t addrlen = sizeof(addr_in);
+        getsockname(tmpsock, (struct sockaddr *)&addr_in, &addrlen);
+        _portno = ntohs(addr_in.sin_port);
+	int pos = _sockname.find(':'); // remove trailing ' ' or '0'
+        char tmp[256];
+	sprintf(tmp, "%s:%d", _sockname.substr(0, pos).c_str(), _portno);
+        _sockname.assign(tmp);
+      }
+    }
+
+    if(exe.size() || args.size()){
+      char s[1024];
+      sprintf(s, args.c_str(), _sockname.c_str());
+      NonBlockingSystemCall(exe, s); // starts the solver
+    }
+    else{
+      timeout = 0.; // no command launched: don't set a timeout
+    }
+
+    // listen on socket (queue up to 20 connections before having
+    // them automatically rejected)
+    if(listen(tmpsock, 20)){
+      CloseSocket(tmpsock);
+      throw "Socket listen failed";
+    }
+
+    // wait until we get data
+    int ret = NonBlockingWait(0.001, timeout, tmpsock);
+    if(ret){
+      CloseSocket(tmpsock);
+      if(ret == 2){
+        throw "Socket listening timeout";
+      }
+      else{
+        return -1; // stopped listening
+      }
+    }
+
+    // accept connection request
+    if(_portno < 0){
+#if !defined(WIN32) || defined(__CYGWIN__)
+      struct sockaddr_un from_un;
+      socklen_t len = sizeof(from_un);
+      _sock = accept(tmpsock, (struct sockaddr *)&from_un, &len);
+#endif
+    }
+    else{
+      struct sockaddr_in from_in;
+      socklen_t len = sizeof(from_in);
+      _sock = accept(tmpsock, (struct sockaddr *)&from_in, &len);
+      char one = 1;
+      setsockopt(_sock, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
+    }
+    CloseSocket(tmpsock);
+    if(_sock < 0)
+      throw "Socket accept failed";
+    return _sock;
+  }
+  int Shutdown()
+  {
+#if !defined(WIN32) || defined(__CYGWIN__)
+    if(_portno < 0)
+      unlink(_sockname.c_str());
+#endif
+    ShutdownSocket(_sock);
+    CloseSocket(_sock);
+    return 0;
+  }
+};
+
+#endif
diff --git a/PendulumC++/onelab.h b/PendulumC++/onelab.h
new file mode 100644
index 0000000000000000000000000000000000000000..1bf5c359882eb58674393bdc6fd7841a4649c6b6
--- /dev/null
+++ b/PendulumC++/onelab.h
@@ -0,0 +1,1578 @@
+// 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;
+    }
+  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>()) 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;
+#else
+      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();
+    }
+  };
+
+}
+
+#endif
diff --git a/PendulumC++/pend.cpp b/PendulumC++/pend.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c83ab795963c93001f9669460ae119c869f989d6
--- /dev/null
+++ b/PendulumC++/pend.cpp
@@ -0,0 +1,202 @@
+// 1) compile with "c++ pend.cpp -o pend.exe"
+// 2) launch "gmsh pend.exe"
+
+#include <math.h>
+#include <stdio.h>
+#include "onelab.h"
+
+void exportMsh(const std::string &path, double le1, double le2)
+{
+  FILE *mshFile = fopen((path + "pend.msh").c_str(),"w");
+  if(!mshFile) return;
+  fprintf(mshFile, "$MeshFormat\n2.2 0 8\n$EndMeshFormat\n");
+  fprintf(mshFile, "$Nodes\n3\n1 0 0 0\n2 0 %f 0\n3 0 %f 0\n$EndNodes\n",
+          -le1, -le1-le2);
+  fprintf(mshFile, "$Elements\n3\n1 1 2 0 1 1 2\n2 1 2 0 1 2 3\n3 15 2 0 2 3\n"
+          "$EndElements\n");
+  fclose(mshFile);
+}
+
+void exportMshOpt(const std::string &path)
+{
+  FILE *optFile = fopen((path + "pend.msh.opt").c_str(), "w");
+  if(!optFile) return;
+  fprintf(optFile, "n = PostProcessing.NbViews - 1;\n");
+  fprintf(optFile, "If(n >= 0)\nView[n].ShowScale = 0;\nView[n].VectorType = 5;\n");
+  fprintf(optFile, "View[n].ExternalView = 0;\nView[n].DisplacementFactor = 1 ;\n");
+  fprintf(optFile, "View[n].PointType = 1;\nView[n].PointSize = 5;\n");
+  fprintf(optFile, "View[n].LineWidth = 2;\nEndIf\n");
+  fclose(optFile);
+}
+
+void exportIter(const std::string &path, int iter, double t, double x1, double y1,
+                double x2, double y2)
+{
+  FILE *mshFile = fopen((path + "pend.msh").c_str(), "a");
+  if(!mshFile) return;
+  fprintf(mshFile, "$NodeData\n1\n\"motion\"\n1\n\t%f\n3\n\t%d\n3\n", t, iter);
+  fprintf(mshFile, "\t3\n\t1 0 0 0\n\t2 %f %f 0\n\t3 %f %f 0\n$EndNodeData\n",
+          x1, y1, x2, y2);
+  fclose(mshFile);
+}
+
+double defineNumber(onelab::client *c, const std::string &name, double value,
+                    const std::map<std::string, std::string> &attributes)
+{
+  std::vector<onelab::number> ns;
+  c->get(ns, name);
+  if(ns.empty()){ // define new parameter
+    onelab::number n(name, value);
+    if(attributes.size()) n.setAttributes(attributes);
+    c->set(n);
+    return value;
+  }
+  // return value from server
+  return ns[0].getValue();
+}
+
+void setNumber(onelab::client *c, const std::string &name, double value,
+               double min=0, double max=0, bool visible=true)
+{
+  onelab::number n(name, value);
+  n.setMin(min);
+  n.setMax(max);
+  n.setVisible(visible);
+  c->set(n);
+}
+
+void addNumberChoice(onelab::client *c, const std::string &name, double choice)
+{
+  std::vector<onelab::number> ns;
+  c->get(ns, name);
+  if(ns.size()){
+    std::vector<double> choices = ns[0].getChoices();
+    choices.push_back(choice);
+    ns[0].setChoices(choices);
+    c->set(ns[0]);
+  }
+}
+
+int main(int argc, char **argv)
+{
+  std::string name, address;
+  for(int i = 0; i < argc; i++){
+    if(std::string(argv[i]) == "-onelab" && i + 2 < argc){
+      name = std::string(argv[i + 1]);
+      address = std::string(argv[i + 2]);
+    }
+  }
+
+  if(name.empty() || address.empty()) return 1;
+
+  onelab::remoteNetworkClient *c = new onelab::remoteNetworkClient(name, address);
+
+  std::string action;
+  std::vector<onelab::string> ns;
+  c->get(ns, name + "/Action");
+  if(ns.size()) action = ns[0].getValue();
+
+  std::string path(argv[0]);
+  int islash = (int)path.find_last_of("/\\");
+  if(islash > 0)
+    path = path.substr(0, islash + 1);
+  else
+    path = "";
+
+  double g = 9.8; // acceleration of gravity
+  double m = 0.3; // mass of pendulum balls
+
+  std::map<std::string, std::string> attr;
+
+  double l = defineNumber(c, "Geom/arm length [m]", 1.0, attr);
+  double time = defineNumber(c, "Dyna/time [s]", 0., attr);
+  double dt = defineNumber(c, "Dyna/time step [s]", 0.001, attr);
+  double tmax = defineNumber(c, "Dyna/max time [s]", 20, attr);
+  double refresh = defineNumber(c, "Dyna/refresh interval [s]", 0.05, attr);
+  attr["Highlight"] = "Pink";
+  double theta0 = defineNumber(c, "Init/initial theta angle [deg]", 10, attr);
+  double phi0 = defineNumber(c, "Init/initial phi angle [deg]", 180, attr);
+
+  // we're done if we are not in the compute phase
+  if(action != "compute"){
+    delete c;
+    return 0;
+  }
+
+  double l1 = l;
+  double l2 = l;
+  double m1 = m;
+  double m2 = m;
+  double theta = theta0 / 180.*M_PI;
+  double phi = phi0 / 180.*M_PI;
+  double theta_dot = 0.0;
+  double phi_dot = 0.0;
+  double refr = 0.0;
+  int iter = 0;
+  time = 0.0;
+
+  while (time < tmax){
+    double delta = phi - theta;
+    double sdelta = sin(delta);
+    double cdelta = cos(delta);
+    double theta_dot_dot = ( m2*l1*(theta_dot*theta_dot)*sdelta*cdelta
+                             + m2*g*sin(phi)*cdelta
+                             + m2*l2*(phi_dot*phi_dot)*sdelta
+                             - (m1+m2)*g*sin(theta) );
+    theta_dot_dot /= ( (m1+m2)*l1 - m2*l1*(cdelta*cdelta) );
+
+    double phi_dot_dot = ( -m2*l2*(phi_dot*phi_dot)*sdelta*cdelta
+                           + (m1+m2)*(g*sin(theta)*cdelta
+                                      - l1*(theta_dot*theta_dot)*sdelta
+                                      - g*sin(phi)) );
+    phi_dot_dot /= ( (m1+m2)*l2 - m2*l2*(cdelta*cdelta) );
+
+    theta_dot += theta_dot_dot*dt;
+    phi_dot += phi_dot_dot*dt;
+    theta += theta_dot*dt;
+    phi += phi_dot*dt;
+
+    double x1 =  l1*sin(theta);
+    double y1 = -l1*cos(theta);
+    double x2 =  l1*sin(theta) + l2*sin(phi);
+    double y2 = -l1*cos(theta) - l2*cos(phi);
+
+    time += dt;
+    refr += dt;
+
+    exportMshOpt(path);
+
+    if(refr >= refresh){
+      refr = 0;
+      setNumber(c, name + "/Progress", time, 0, tmax, false);
+      setNumber(c, "Dyna/time [s]", time);
+      setNumber(c, "Solu/phi", phi);
+      addNumberChoice(c, "Solu/phi", phi);
+      setNumber(c, "Solu/theta",  theta);
+      addNumberChoice(c, "Solu/theta", theta);
+      setNumber(c, "Solu/phi dot", phi_dot);
+      addNumberChoice(c, "Solu/phi dot", phi_dot);
+      setNumber(c, "Solu/theta dot", theta_dot);
+      addNumberChoice(c, "Solu/theta dot", theta_dot);
+
+      // ask Gmsh to refresh
+      onelab::string s("Gmsh/Action", "refresh");
+      c->set(s);
+
+      // stop if we are asked to (by Gmsh)
+      c->get(ns, name + "/Action");
+      if(ns.size() && ns[0].getValue() == "stop") break;
+
+      exportMsh(path, l1, l2);
+      exportIter(path, iter, time, x1, y1+l1, x2, y2+l1+l2);
+      c->sendMergeFileRequest(path + "pend.msh");
+      iter += 1;
+    }
+  }
+
+  setNumber(c, name + "/Progress", 0, 0, tmax, false);
+
+  delete c;
+
+  return 0;
+}
diff --git a/PendulumPython/pend.py b/PendulumPython/pend.py
new file mode 100644
index 0000000000000000000000000000000000000000..58878103beba235febfe4470c383a74333862ba8
--- /dev/null
+++ b/PendulumPython/pend.py
@@ -0,0 +1,151 @@
+#!/usr/bin/env python
+#coding=utf-8
+
+# 1) Launch "gmsh pend.py", or open "pend.py" with Gmsh's File->Open menu
+# 2) Click on "Run" in the left Gmsh panel
+# 3) there is no number 3... :-)
+
+# To interact with ONELAB, the script must first import the onelab.py module:
+import onelab
+import math, os
+
+# The script then creates a ONELAB client with:
+c = onelab.client(__file__)
+
+# Creating the client connects the script to the onelab server, through a
+# socket. The __file__ argument is a python variable. It tells ONELAB in which
+# directory the script being executed is located.
+
+def exportMsh(le1,le2):
+   mshFile = open(c.getPath("pend.msh"), 'w')
+   mshFile.write('$MeshFormat\n2.2 0 8\n$EndMeshFormat\n')
+   mshFile.write('$Nodes\n3\n1 0 0 0\n2 0 %s 0\n3 0 %s 0\n$EndNodes\n' %(-le1, -le1-le2))
+   mshFile.write('$Elements\n3\n1 1 2 0 1 1 2\n2 1 2 0 1 2 3\n3 15 2 0 2 3\n$EndElements\n')
+   mshFile.close()
+
+def exportMshOpt():
+   optFile = open(c.getPath("pend.msh.opt"),'w')
+   optFile.write('n = PostProcessing.NbViews - 1;\n')
+   optFile.write('If(n >= 0)\nView[n].ShowScale = 0;\nView[n].VectorType = 5;\n')
+   optFile.write('View[n].ExternalView = 0;\nView[n].DisplacementFactor = 1 ;\n')
+   optFile.write('View[n].PointType = 1;\nView[n].PointSize = 5;\n')
+   optFile.write('View[n].LineWidth = 2;\nEndIf\n')
+   optFile.close()
+
+def exportIter(iter,t,x1,y1,x2,y2):
+   mshFile = open(c.getPath("pend.msh"),'a')
+   mshFile.write('$NodeData\n1\n"motion"\n1\n\t%f\n3\n\t%d\n3\n' % (t, iter))
+   mshFile.write('\t3\n\t1 0 0 0\n\t2 %f %f 0\n\t3 %f %f 0\n$EndNodeData\n' %(x1,y1,x2,y2))
+   mshFile.close()
+
+g = 9.8	# acceleration of gravity
+m = 0.3 # mass of pendulum balls
+
+# New ONELAB variables can then be defined using defineNumber, e.g.:
+l = c.defineNumber('Geom/arm length [m]', value=1.0)
+time = c.defineNumber('Dyna/time [s]', value=0.0)
+dt = c.defineNumber('Dyna/time step [s]', value=0.001)
+tmax = c.defineNumber('Dyna/max time [s]', value=20)
+refresh = c.defineNumber('Dyna/refresh interval [s]', value=0.1)
+theta0 = c.defineNumber('Init/initial theta angle [deg]', value=10, 
+                         attributes={'Highlight':'Pink'})
+phi0 = c.defineNumber('Init/initial phi angle [deg]', value=180,
+                       attributes={'Highlight':'Pink'})
+
+# When the script is run, if the parameter Geom/arm length [m] has not been
+# previously defined, it takes the value (1.0) provided in defineNumber and is
+# sent to the ONELAB server. The "/" character in the variable name is
+# interpreted as a path separator, and results in the creation of a sub-tree in
+# the graphical user interface. If the script is re-run later, the value will be
+# updated using the value from the server (unless it is labeled as readOnly: see
+# below). When Gmsh runs a ONELAB client, the client can be run in two modes:
+# c.action=='check' to check the coherence of the ONELAB database and make
+# adjustments if necessary, and c.action=='compute' to perform the actual
+# computation. For instance, in 'check' mode, the double pendulum client simply
+# defines the ONELAB variables it wants to share with the server, then exits:
+if c.action == 'check' :
+   exit(0)
+
+# In 'compute' mode, the code enters a loop and performs the actual computation.
+   
+l1 = l;
+l2 = l;
+m1 = m;
+m2 = m;
+theta = theta0 / 180.*math.pi;
+phi = phi0 / 180.*math.pi;
+theta_dot = 0.0
+phi_dot = 0.0
+refr = 0.0
+iter = 0
+time = 0.0
+
+while (time < tmax):
+   delta = phi - theta
+   sdelta = math.sin(delta)
+   cdelta = math.cos(delta)
+   theta_dot_dot = ( m2*l1*(theta_dot**2.0)*sdelta*cdelta
+                     + m2*g*math.sin(phi)*cdelta
+                     + m2*l2*(phi_dot**2.0)*sdelta
+                     - (m1+m2)*g*math.sin(theta) )
+   theta_dot_dot /= ( (m1+m2)*l1 - m2*l1*(cdelta)**2.0 )
+   
+   phi_dot_dot = ( -m2*l2*(phi_dot**2.0)*sdelta*cdelta
+                    + (m1+m2)*(g*math.sin(theta)*cdelta
+                               - l1*(theta_dot**2.0)*sdelta
+                               - g*math.sin(phi)) )
+   phi_dot_dot /= ( (m1+m2)*l2 - m2*l2*(cdelta)**2.0 )
+   
+   theta_dot = theta_dot + theta_dot_dot*dt
+   phi_dot = phi_dot + phi_dot_dot*dt
+
+   theta = theta + theta_dot*dt
+   phi = phi + phi_dot*dt
+
+   x1 =  l1*math.sin(theta)
+   y1 = -l1*math.cos(theta)
+   x2 =  l1*math.sin(theta) + l2*math.sin(phi)
+   y2 = -l1*math.cos(theta) - l2*math.cos(phi)
+
+   time += dt
+   refr += dt
+
+   exportMshOpt()
+
+   if refr >= refresh:
+      refr = 0
+      # During the computation the script can directly set a value in the ONELAB
+      # database with setNumber:
+      c.setNumber(c.name + '/Progress', value=time, min=0, max=tmax, visible=0)
+      c.setNumber('Dyna/time [s]', value=time)
+      c.setNumber('Solu/phi', value=phi)
+      c.addNumberChoice('Solu/phi', phi)
+      c.setNumber('Solu/theta', value=theta)
+      c.addNumberChoice('Solu/theta', theta)
+      c.setNumber('Solu/phi dot', value=phi_dot)
+      c.addNumberChoice('Solu/phi dot', phi_dot)
+      c.setNumber('Solu/theta dot', value=theta_dot)
+      c.addNumberChoice('Solu/theta dot', theta_dot)
+
+      # It can also ask the server to refresh...
+      c.setString('Gmsh/Action', value='refresh')
+
+      # or to stop...
+      if(c.getString(c.name + '/Action') == 'stop'):
+         break;
+
+      exportMsh(l1, l2)
+      exportIter(iter, time, x1, y1+l1, x2, y2+l1+l2)
+
+      # or to read a file:
+      c.mergeFile(c.checkPath('pend.msh'))
+      iter += 1
+
+      # The check path function (c.checkPath) builds the pathname of a file
+      # named pend.msh located in the same directory as the script under
+      # execution, and then checks on whether the pathname exists on disk. If
+      # not, an error message is issued. Use the regular path function
+      # (c.getPath) to build a pathname without checking on the presence on disk
+      # of the file.
+
+c.setNumber(c.name + '/Progress', value=0)