text: /*
   * LuaTables++
   * Copyright (c) 2013-2018 Martin Felis <martin@fyxs.org>.
   * All rights reserved.
   *
   * Permission is hereby granted, free of charge, to any person obtaining
   * a copy of this software and associated documentation files (the
   * "Software"), to deal in the Software without restriction, including
   * without limitation the rights to use, copy, modify, merge, publish,
   * distribute, sublicense, and/or sell copies of the Software, and to
   * permit persons to whom the Software is furnished to do so, subject to
   * the following conditions:
   *
   * The above copyright notice and this permission notice shall be
   * included in all copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
   * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
   * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
   * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
   * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
   * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
   * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   */
  
  #include "LuaTest/luatables.h"
  
  #include <assert.h>
  #include <iostream>
  #include <cstdlib>
  #include <vector>
  #include <sstream>
  #include <cmath>
  
  extern "C"
  {
     #include <lua.h>
     #include <lauxlib.h>
     #include <lualib.h>
  }
  
  #include <stdio.h>  /* defines FILENAME_MAX */
  
  #if defined(WIN32) || defined (_WIN32)
    #include <direct.h>
    #define get_current_dir _getcwd
    #define DIRECTORY_SEPARATOR "\\"
  #elif defined(linux) || defined (__linux) || defined(__linux__) || defined(__APPLE__)
    #include <unistd.h>
    #define get_current_dir getcwd
    #define DIRECTORY_SEPARATOR "/"
  #else
    #error Platform not supported!
  #endif
  
  using namespace std;
  
  std::string get_file_directory (const char* filename)
  {
    string name (filename);
    string result = name.substr(0, name.find_last_of (DIRECTORY_SEPARATOR) + 1);
  
    if (result == "") {
      result = "./";
    }
    #if defined (WIN32) || defined (_WIN32)
    else if (result.substr(1,2) != ":\\") {
      result = string(".\\") + result;
    }
    #else
    else if (result.substr(0,
                           string(DIRECTORY_SEPARATOR).size()) != DIRECTORY_SEPARATOR  &&
             result[0] != '.') {
      result = string("./") + result;
    }
    #endif
  
    return result;
  }
  
  // char encoded serialize function that is available in plaintext in
  // utils/serialize.lua. Converted using lua auto.lua serialize.lua
  const char serialize_lua[] = {
  
    0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x73, 0x6c,
    0x69, 0x73, 0x74, 0x20, 0x28, 0x74, 0x29, 0x0a,
    0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x69, 0x74, 0x65, 0x6d, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x3d,
    0x20, 0x30, 0x0a,
    0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x20, 0x3d,
    0x20, 0x6e, 0x69, 0x6c, 0x0a,
    0x09, 0x66, 0x6f, 0x72, 0x20, 0x6b, 0x2c, 0x76, 0x20, 0x69, 0x6e, 0x20, 0x70, 0x61, 0x69, 0x72, 0x73, 0x28,
    0x74, 0x29, 0x20, 0x64, 0x6f, 0x0a,
    0x09, 0x09, 0x69, 0x74, 0x65, 0x6d, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x3d, 0x20, 0x69, 0x74, 0x65, 0x6d,
    0x63, 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x2b, 0x20, 0x31, 0x0a,
    0x09, 0x09, 0x69, 0x66, 0x20, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x20, 0x3d, 0x3d, 0x20,
    0x6e, 0x69, 0x6c, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
    0x09, 0x09, 0x09, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x20, 0x3d, 0x20, 0x74, 0x79, 0x70,
    0x65, 0x28, 0x76, 0x29, 0x0a,
    0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a,
    0x09, 0x09, 0x69, 0x66, 0x20, 0x74, 0x79, 0x70, 0x65, 0x28, 0x76, 0x29, 0x20, 0x7e, 0x3d, 0x20, 0x6c, 0x61,
    0x73, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x20, 0x6f, 0x72, 0x20, 0x28, 0x74, 0x79, 0x70, 0x65, 0x28, 0x76,
    0x29, 0x20, 0x7e, 0x3d, 0x20, 0x22, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x22, 0x20, 0x61, 0x6e, 0x64, 0x20,
    0x74, 0x79, 0x70, 0x65, 0x28, 0x76, 0x29, 0x20, 0x7e, 0x3d, 0x20, 0x22, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72,
    0x22, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x79, 0x70, 0x65, 0x28, 0x76, 0x29, 0x20, 0x7e, 0x3d, 0x20, 0x22,
    0x62, 0x6f, 0x6f, 0x6c, 0x65, 0x61, 0x6e, 0x22, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x79, 0x70, 0x65, 0x28,
    0x76, 0x29, 0x20, 0x7e, 0x3d, 0x20, 0x22, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x22, 0x29, 0x20, 0x74, 0x68, 0x65,
    0x6e, 0x0a,
    0x09, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x0a,
    0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a,
    0x09, 0x0a,
    0x09, 0x09, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x20, 0x3d, 0x20, 0x74, 0x79, 0x70, 0x65,
    0x28, 0x76, 0x29, 0x0a,
    0x09, 0x65, 0x6e, 0x64, 0x0a,
    0x09, 0x69, 0x66, 0x20, 0x69, 0x74, 0x65, 0x6d, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x7e, 0x3d, 0x20, 0x23,
    0x74, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
    0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x0a,
    0x09, 0x65, 0x6e, 0x64, 0x0a,
    0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x72, 0x75, 0x65, 0x0a,
    0x65, 0x6e, 0x64, 0x0a,
    0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6d, 0x70,
    0x5f, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x20, 0x28, 0x61, 0x2c, 0x20,
    0x62, 0x29, 0x0a,
    0x09, 0x69, 0x66, 0x20, 0x28, 0x74, 0x79, 0x70, 0x65, 0x28, 0x61, 0x29, 0x20, 0x3d, 0x3d, 0x20, 0x22, 0x73,
    0x74, 0x72, 0x69, 0x6e, 0x67, 0x22, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x79, 0x70, 0x65, 0x28, 0x62, 0x29,
    0x20, 0x3d, 0x3d, 0x20, 0x22, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x22, 0x20, 0x6f, 0x72, 0x20, 0x74, 0x79,
    0x70, 0x65, 0x28, 0x61, 0x29, 0x20, 0x3d, 0x3d, 0x20, 0x22, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x22, 0x20,
    0x61, 0x6e, 0x64, 0x20, 0x74, 0x79, 0x70, 0x65, 0x28, 0x62, 0x29, 0x20, 0x3d, 0x3d, 0x20, 0x22, 0x6e, 0x75,
    0x6d, 0x62, 0x65, 0x72, 0x22, 0x29, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
    0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x61, 0x20, 0x3c, 0x20, 0x62, 0x0a,
    0x09, 0x65, 0x6c, 0x73, 0x65, 0x0a,
    0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x74, 0x79, 0x70, 0x65, 0x28, 0x61, 0x29, 0x20, 0x3c,
    0x20, 0x74, 0x79, 0x70, 0x65, 0x28, 0x62, 0x29, 0x0a,
    0x09, 0x65, 0x6e, 0x64, 0x0a,
    0x65, 0x6e, 0x64, 0x0a,
    0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x67, 0x65, 0x6e,
    0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x20, 0x28,
    0x74, 0x29, 0x0a,
    0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x64,
    0x69, 0x63, 0x65, 0x73, 0x20, 0x3d, 0x20, 0x7b, 0x7d, 0x0a,
    0x09, 0x66, 0x6f, 0x72, 0x20, 0x6b, 0x20, 0x69, 0x6e, 0x20, 0x70, 0x61, 0x69, 0x72, 0x73, 0x20, 0x28, 0x74,
    0x29, 0x20, 0x64, 0x6f, 0x0a,
    0x09, 0x09, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x20, 0x28, 0x6f, 0x72,
    0x64, 0x65, 0x72, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x2c, 0x20, 0x6b, 0x29, 0x0a,
    0x09, 0x65, 0x6e, 0x64, 0x0a,
    0x09, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x73, 0x6f, 0x72, 0x74, 0x20, 0x28, 0x6f, 0x72, 0x64, 0x65, 0x72,
    0x65, 0x64, 0x5f, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x2c, 0x20, 0x63, 0x6d, 0x70, 0x5f, 0x61, 0x6c,
    0x70, 0x68, 0x61, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x29, 0x0a,
    0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x64, 0x5f, 0x69, 0x6e,
    0x64, 0x69, 0x63, 0x65, 0x73, 0x0a,
    0x65, 0x6e, 0x64, 0x0a,
    0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x72, 0x64,
    0x65, 0x72, 0x65, 0x64, 0x5f, 0x6e, 0x65, 0x78, 0x74, 0x20, 0x28, 0x74, 0x2c, 0x20, 0x73, 0x74, 0x61, 0x74,
    0x65, 0x29, 0x0a,
    0x09, 0x69, 0x66, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x6e, 0x69, 0x6c, 0x20, 0x74,
    0x68, 0x65, 0x6e, 0x0a,
    0x09, 0x09, 0x74, 0x2e, 0x5f, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63,
    0x65, 0x73, 0x20, 0x3d, 0x20, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x64, 0x5f, 0x69,
    0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x20, 0x28, 0x74, 0x29, 0x0a,
    0x09, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x3d, 0x20, 0x74, 0x2e, 0x5f, 0x5f,
    0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x5b, 0x31, 0x5d, 0x0a,
    0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6b, 0x65, 0x79, 0x2c, 0x20, 0x74, 0x5b, 0x6b, 0x65,
    0x79, 0x5d, 0x0a,
    0x09, 0x65, 0x6e, 0x64, 0x0a,
    0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x3d, 0x20, 0x6e, 0x69, 0x6c, 0x0a,
    0x09, 0x66, 0x6f, 0x72, 0x20, 0x69, 0x20, 0x3d, 0x20, 0x31, 0x2c, 0x20, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x2e,
    0x67, 0x65, 0x74, 0x6e, 0x28, 0x74, 0x2e, 0x5f, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x64, 0x49, 0x6e,
    0x64, 0x69, 0x63, 0x65, 0x73, 0x29, 0x20, 0x64, 0x6f, 0x0a,
    0x09, 0x09, 0x69, 0x66, 0x20, 0x74, 0x2e, 0x5f, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x64, 0x49, 0x6e,
    0x64, 0x69, 0x63, 0x65, 0x73, 0x5b, 0x69, 0x5d, 0x20, 0x3d, 0x3d, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x20,
    0x74, 0x68, 0x65, 0x6e, 0x0a,
    0x09, 0x09, 0x09, 0x6b, 0x65, 0x79, 0x20, 0x3d, 0x20, 0x74, 0x2e, 0x5f, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72,
    0x65, 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x5b, 0x69, 0x20, 0x2b, 0x20, 0x31, 0x5d, 0x0a,
    0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a,
    0x09, 0x65, 0x6e, 0x64, 0x0a,
    0x09, 0x69, 0x66, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
    0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6b, 0x65, 0x79, 0x2c, 0x20, 0x74, 0x5b, 0x6b, 0x65,
    0x79, 0x5d, 0x0a,
    0x09, 0x65, 0x6e, 0x64, 0x0a,
    0x09, 0x74, 0x2e, 0x5f, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x64, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65,
    0x73, 0x20, 0x3d, 0x20, 0x6e, 0x69, 0x6c, 0x0a,
    0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x0a,
    0x65, 0x6e, 0x64, 0x0a,
    0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x72, 0x64,
    0x65, 0x72, 0x65, 0x64, 0x5f, 0x70, 0x61, 0x69, 0x72, 0x73, 0x20, 0x28, 0x74, 0x29, 0x0a,
    0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x64, 0x5f, 0x6e, 0x65,
    0x78, 0x74, 0x2c, 0x20, 0x74, 0x2c, 0x20, 0x6e, 0x69, 0x6c, 0x0a,
    0x65, 0x6e, 0x64, 0x0a,
    0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65,
    0x20, 0x28, 0x6f, 0x2c, 0x20, 0x74, 0x61, 0x62, 0x73, 0x2c, 0x20, 0x73, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x29, 0x0a,
    0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x22,
    0x22, 0x0a,
    0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x70, 0x61, 0x69, 0x72, 0x73, 0x5f, 0x66, 0x75, 0x6e, 0x63, 0x20,
    0x3d, 0x20, 0x70, 0x61, 0x69, 0x72, 0x73, 0x0a,
    0x09, 0x69, 0x66, 0x20, 0x73, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
    0x09, 0x09, 0x70, 0x61, 0x69, 0x72, 0x73, 0x5f, 0x66, 0x75, 0x6e, 0x63, 0x20, 0x3d, 0x20, 0x6f, 0x72, 0x64,
    0x65, 0x72, 0x65, 0x64, 0x5f, 0x70, 0x61, 0x69, 0x72, 0x73, 0x0a,
    0x09, 0x65, 0x6e, 0x64, 0x0a,
    0x20, 0x20, 0x0a,
    0x20, 0x20, 0x69, 0x66, 0x20, 0x74, 0x61, 0x62, 0x73, 0x20, 0x3d, 0x3d, 0x20, 0x6e, 0x69, 0x6c, 0x20, 0x74,
    0x68, 0x65, 0x6e, 0x0a,
    0x20, 0x20, 0x20, 0x20, 0x74, 0x61, 0x62, 0x73, 0x20, 0x3d, 0x20, 0x22, 0x22, 0x0a,
    0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a,
    0x20, 0x20, 0x69, 0x66, 0x20, 0x74, 0x79, 0x70, 0x65, 0x28, 0x6f, 0x29, 0x20, 0x3d, 0x3d, 0x20, 0x22, 0x6e,
    0x75, 0x6d, 0x62, 0x65, 0x72, 0x22, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
    0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c,
    0x74, 0x20, 0x2e, 0x2e, 0x20, 0x74, 0x6f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x28, 0x6f, 0x29, 0x0a,
    0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x69, 0x66, 0x20, 0x74, 0x79, 0x70, 0x65, 0x28, 0x6f, 0x29, 0x20, 0x3d,
    0x3d, 0x20, 0x22, 0x62, 0x6f, 0x6f, 0x6c, 0x65, 0x61, 0x6e, 0x22, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
    0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c,
    0x74, 0x20, 0x2e, 0x2e, 0x20, 0x74, 0x6f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x28, 0x6f, 0x29, 0x0a,
    0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x69, 0x66, 0x20, 0x74, 0x79, 0x70, 0x65, 0x28, 0x6f, 0x29, 0x20, 0x3d,
    0x3d, 0x20, 0x22, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x22, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
    0x09, 0x09, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20,
    0x2e, 0x2e, 0x20, 0x22, 0x5c, 0x22, 0x22, 0x20, 0x2e, 0x2e, 0x20, 0x6f, 0x20, 0x2e, 0x2e, 0x20, 0x22, 0x5c,
    0x22, 0x22, 0x0a,
    0x09, 0x65, 0x6c, 0x73, 0x65, 0x69, 0x66, 0x20, 0x74, 0x79, 0x70, 0x65, 0x28, 0x6f, 0x29, 0x20, 0x3d, 0x3d,
    0x20, 0x22, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x22, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x69, 0x73, 0x6c, 0x69, 0x73,
    0x74, 0x28, 0x6f, 0x29, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
    0x09, 0x09, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20,
    0x2e, 0x2e, 0x20, 0x22, 0x7b, 0x22, 0x0a,
    0x09, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x77, 0x61, 0x73, 0x5f, 0x73,
    0x75, 0x62, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x3d, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x0a,
    0x09, 0x09, 0x66, 0x6f, 0x72, 0x20, 0x69, 0x2c, 0x76, 0x20, 0x69, 0x6e, 0x20, 0x69, 0x70, 0x61, 0x69, 0x72,
    0x73, 0x28, 0x6f, 0x29, 0x20, 0x64, 0x6f, 0x0a,
    0x09, 0x09, 0x09, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x77, 0x61, 0x73, 0x5f, 0x73, 0x75, 0x62, 0x74, 0x61, 0x62,
    0x6c, 0x65, 0x20, 0x3d, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x0a,
    0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x74, 0x79, 0x70, 0x65, 0x28, 0x76, 0x29, 0x20, 0x3d, 0x3d, 0x20, 0x22,
    0x74, 0x61, 0x62, 0x6c, 0x65, 0x22, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
    0x09, 0x09, 0x09, 0x09, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x77, 0x61, 0x73, 0x5f, 0x73, 0x75, 0x62, 0x74, 0x61,
    0x62, 0x6c, 0x65, 0x20, 0x3d, 0x20, 0x74, 0x72, 0x75, 0x65, 0x0a,
    0x09, 0x09, 0x09, 0x09, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c,
    0x74, 0x20, 0x2e, 0x2e, 0x20, 0x22, 0x5c, 0x6e, 0x22, 0x20, 0x2e, 0x2e, 0x20, 0x74, 0x61, 0x62, 0x73, 0x20,
    0x2e, 0x2e, 0x20, 0x22, 0x20, 0x20, 0x22, 0x20, 0x2e, 0x2e, 0x20, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69,
    0x7a, 0x65, 0x20, 0x28, 0x76, 0x2c, 0x20, 0x74, 0x61, 0x62, 0x73, 0x20, 0x2e, 0x2e, 0x20, 0x22, 0x20, 0x20,
    0x22, 0x2c, 0x20, 0x73, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x29, 0x20, 0x2e, 0x2e, 0x20, 0x22, 0x2c, 0x22, 0x0a,
    0x09, 0x09, 0x09, 0x65, 0x6c, 0x73, 0x65, 0x69, 0x66, 0x20, 0x74, 0x79, 0x70, 0x65, 0x28, 0x76, 0x29, 0x20,
    0x3d, 0x3d, 0x20, 0x22, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x22, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
    0x09, 0x09, 0x09, 0x09, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c,
    0x74, 0x20, 0x2e, 0x2e, 0x20, 0x22, 0x20, 0x5c, 0x22, 0x22, 0x20, 0x2e, 0x2e, 0x20, 0x76, 0x20, 0x2e, 0x2e,
    0x20, 0x22, 0x5c, 0x22, 0x2c, 0x22, 0x0a,
    0x09, 0x09, 0x09, 0x65, 0x6c, 0x73, 0x65, 0x0a,
    0x09, 0x09, 0x09, 0x09, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c,
    0x74, 0x20, 0x2e, 0x2e, 0x20, 0x22, 0x20, 0x22, 0x20, 0x2e, 0x2e, 0x20, 0x74, 0x6f, 0x73, 0x74, 0x72, 0x69,
    0x6e, 0x67, 0x20, 0x28, 0x76, 0x29, 0x20, 0x2e, 0x2e, 0x20, 0x22, 0x2c, 0x22, 0x0a,
    0x09, 0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a,
    0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a,
    0x09, 0x09, 0x69, 0x66, 0x20, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x77, 0x61, 0x73, 0x5f, 0x73, 0x75, 0x62, 0x74,
    0x61, 0x62, 0x6c, 0x65, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
    0x09, 0x09, 0x09, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74,
    0x20, 0x2e, 0x2e, 0x20, 0x22, 0x5c, 0x6e, 0x22, 0x20, 0x2e, 0x2e, 0x20, 0x74, 0x61, 0x62, 0x73, 0x0a,
    0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a,
    0x09, 0x09, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20,
    0x2e, 0x2e, 0x20, 0x22, 0x7d, 0x22, 0x0a,
    0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x69, 0x66, 0x20, 0x74, 0x79, 0x70, 0x65, 0x28, 0x6f, 0x29, 0x20, 0x3d,
    0x3d, 0x20, 0x22, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x22, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
    0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x6f, 0x2e, 0x64, 0x6f, 0x6e, 0x74, 0x5f, 0x73, 0x65, 0x72, 0x69, 0x61,
    0x6c, 0x69, 0x7a, 0x65, 0x5f, 0x6d, 0x65, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x22, 0x7b, 0x7d, 0x22, 0x0a,
    0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a,
    0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c,
    0x74, 0x20, 0x2e, 0x2e, 0x20, 0x22, 0x7b, 0x5c, 0x6e, 0x22, 0x0a,
    0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x6b, 0x2c, 0x76, 0x20, 0x69, 0x6e, 0x20, 0x70, 0x61, 0x69,
    0x72, 0x73, 0x5f, 0x66, 0x75, 0x6e, 0x63, 0x28, 0x6f, 0x29, 0x20, 0x64, 0x6f, 0x0a,
    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x74, 0x79, 0x70, 0x65, 0x28, 0x76, 0x29, 0x20, 0x7e,
    0x3d, 0x20, 0x22, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2d, 0x2d, 0x20, 0x6d, 0x61, 0x6b, 0x65, 0x20, 0x73, 0x75,
    0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x65, 0x64, 0x20, 0x6b,
    0x65, 0x79, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x6c, 0x79, 0x20, 0x61,
    0x72, 0x65, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x69, 0x66, 0x69, 0x65, 0x64, 0x0a,
    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x74, 0x79, 0x70, 0x65, 0x28, 0x6b, 0x29,
    0x20, 0x3d, 0x3d, 0x20, 0x22, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x22, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
    0x09, 0x09, 0x09, 0x09, 0x20, 0x20, 0x69, 0x66, 0x20, 0x74, 0x79, 0x70, 0x65, 0x28, 0x76, 0x29, 0x20, 0x3d,
    0x3d, 0x20, 0x22, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x22, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a,
    0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x73,
    0x75, 0x6c, 0x74, 0x20, 0x2e, 0x2e, 0x20, 0x74, 0x61, 0x62, 0x73, 0x20, 0x2e, 0x2e, 0x20, 0x22, 0x20, 0x20,
    0x5b, 0x22, 0x20, 0x2e, 0x2e, 0x20, 0x74, 0x6f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x28, 0x6b, 0x29, 0x20,
    0x2e, 0x2e, 0x20, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22, 0x20, 0x2e, 0x2e, 0x20, 0x74, 0x6f, 0x73, 0x74, 0x72,
    0x69, 0x6e, 0x67, 0x28, 0x76, 0x29, 0x20, 0x2e, 0x2e, 0x20, 0x22, 0x2c, 0x5c, 0x6e, 0x22, 0x0a,
    0x09, 0x09, 0x09, 0x09, 0x09, 0x65, 0x6c, 0x73, 0x65, 0x0a,
    0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x73,
    0x75, 0x6c, 0x74, 0x20, 0x2e, 0x2e, 0x20, 0x74, 0x61, 0x62, 0x73, 0x20, 0x2e, 0x2e, 0x20, 0x22, 0x20, 0x20,
    0x5b, 0x22, 0x20, 0x2e, 0x2e, 0x20, 0x74, 0x6f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x28, 0x6b, 0x29, 0x20,
    0x2e, 0x2e, 0x20, 0x22, 0x5d, 0x20, 0x3d, 0x20, 0x22, 0x20, 0x2e, 0x2e, 0x20, 0x73, 0x65, 0x72, 0x69, 0x61,
    0x6c, 0x69, 0x7a, 0x65, 0x20, 0x28, 0x76, 0x2c, 0x20, 0x74, 0x61, 0x62, 0x73, 0x20, 0x2e, 0x2e, 0x20, 0x22,
    0x20, 0x20, 0x22, 0x2c, 0x20, 0x73, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x29, 0x20, 0x2e, 0x2e, 0x20, 0x22, 0x2c,
    0x5c, 0x6e, 0x22, 0x0a,
    0x09, 0x09, 0x09, 0x09, 0x09, 0x65, 0x6e, 0x64, 0x0a,
    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a,
    0x09, 0x09, 0x09, 0x09, 0x09, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x73, 0x75,
    0x6c, 0x74, 0x20, 0x2e, 0x2e, 0x20, 0x74, 0x61, 0x62, 0x73, 0x20, 0x2e, 0x2e, 0x20, 0x22, 0x20, 0x20, 0x22,
    0x20, 0x2e, 0x2e, 0x20, 0x74, 0x6f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x28, 0x6b, 0x29, 0x20, 0x2e,
    0x2e, 0x20, 0x22, 0x20, 0x3d, 0x20, 0x22, 0x20, 0x2e, 0x2e, 0x20, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69,
    0x7a, 0x65, 0x28, 0x76, 0x2c, 0x20, 0x74, 0x61, 0x62, 0x73, 0x20, 0x2e, 0x2e, 0x20, 0x22, 0x20, 0x20, 0x22,
    0x2c, 0x20, 0x73, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x29, 0x20, 0x2e, 0x2e, 0x20, 0x22, 0x2c, 0x5c, 0x6e, 0x22, 0x0a,
    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a,
    0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a,
    0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a,
    0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c,
    0x74, 0x20, 0x2e, 0x2e, 0x20, 0x74, 0x61, 0x62, 0x73, 0x20, 0x2e, 0x2e, 0x20, 0x22, 0x7d, 0x22, 0x0a,
    0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a,
    0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x20, 0x28, 0x22, 0x6e, 0x6f, 0x74, 0x20, 0x73, 0x65,
    0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x69, 0x6e, 0x67, 0x20, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x20, 0x6f, 0x66,
    0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x22, 0x20, 0x2e, 0x2e, 0x20, 0x74, 0x79, 0x70, 0x65, 0x28, 0x6f, 0x29,
    0x20, 0x29, 0x0a,
    0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a,
    0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x0a,
    0x65, 0x6e, 0x64, 0x0a,
    0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x0a,
  };
  
  //
  // Lua Helper Functions
  //
  void bail(lua_State *L, const char *msg)
  {
    std::ostringstream errormsg;
    errormsg << msg << lua_tostring(L, -1) << endl;
    throw RigidBodyDynamics::Errors::RBDLFileParseError(errormsg.str());
  }
  
  void stack_print (const char *file, int line, lua_State *L)
  {
    cout << file << ":" << line << ": stack size: " << lua_gettop(L) << endl;;
    for (int i = 1; i < lua_gettop(L) + 1; i++) {
      cout << file << ":" << line << ": ";
      cout << i << ": ";
      if (lua_istable (L, i)) {
        cout << " table: " << lua_topointer (L, i) << endl;
      } else if (lua_isnumber (L, i)) {
        cout << " number: " << lua_tonumber (L, i) << endl;
      } else if (lua_isuserdata (L, i)) {
        void* userdata = (void*) lua_touserdata (L, i);
        cout << " userdata (" << userdata << ")" << endl;
      } else if (lua_isstring (L, i)) {
        cout << " string: " << lua_tostring(L, i) << endl;
      } else if (lua_isfunction (L, i)) {
        cout << " function" << endl;
      } else if (lua_isnil (L, i)) {
        cout << " nil" << endl;
      } else {
        cout << " unknown: " << lua_typename (L, lua_type (L, i)) << endl;
      }
    }
  }
  
  void l_push_LuaKey (lua_State *L, const LuaKey &key)
  {
    if (key.type == LuaKey::Integer) {
      lua_pushnumber (L, key.int_value);
    } else {
      lua_pushstring(L, key.string_value.c_str());
    }
  }
  
  bool query_key_stack (lua_State *L, std::vector<LuaKey> key_stack)
  {
    for (int i = key_stack.size() - 1; i >= 0; i--) {
      // get the global value when the result of a lua expression was not
      // pushed onto the stack via the return statement.
      if (lua_gettop(L) == 0) {
        lua_getglobal (L, key_stack[key_stack.size() - 1].string_value.c_str());
  
        if (lua_isnil(L, -1)) {
          return false;
        }
  
        continue;
      }
  
      l_push_LuaKey (L, key_stack[i]);
  
      lua_gettable (L, -2);
  
      // return if key is not found
      if (lua_isnil(L, -1)) {
        return false;
      }
    }
  
    return true;
  }
  
  void create_key_stack (lua_State *L, std::vector<LuaKey> key_stack)
  {
    for (int i = key_stack.size() - 1; i > 0; i--) {
      // get the global value when the result of a lua expression was not
      // pushed onto the stack via the return statement.
      if (lua_gettop(L) == 0) {
        lua_getglobal (L, key_stack[key_stack.size() - 1].string_value.c_str());
  
        if (lua_isnil(L, -1)) {
          lua_pop(L, 1);
          lua_newtable(L);
          lua_pushvalue(L, -1);
          lua_setglobal(L, key_stack[key_stack.size() - 1].string_value.c_str());
        }
  
        continue;
      }
  
      l_push_LuaKey (L, key_stack[i]);
  
      lua_pushvalue (L, -1);
      lua_gettable (L, -3);
  
      if (lua_isnil(L, -1)) {
        // parent, key, nil
        lua_pop(L, 1);  // parent, key
        lua_newtable(L); // parent, key, table
        lua_insert(L, -2); // parent, table, key
        lua_pushvalue(L, -2); // parent, table, key, table
        lua_settable (L, -4); // parent, table
      }
    }
  }
  
  //
  // LuaTableNode
  //
  std::vector<LuaKey> LuaTableNode::getKeyStack()
  {
    std::vector<LuaKey> result;
  
    const LuaTableNode *node_ptr = this;
  
    do {
      result.push_back (node_ptr->key);
      node_ptr = node_ptr->parent;
    } while (node_ptr != NULL);
  
    return result;
  }
  
  std::string LuaTableNode::keyStackToString()
  {
    std::vector<LuaKey> key_stack = getKeyStack();
  
    ostringstream result_stream ("");
    for (int i = key_stack.size() - 1; i >= 0; i--) {
      if (key_stack[i].type == LuaKey::String) {
        result_stream << "[\"" << key_stack[i].string_value << "\"]";
      } else {
        result_stream << "[" << key_stack[i].int_value << "]";
      }
    }
  
    return result_stream.str();
  }
  
  bool LuaTableNode::stackQueryValue()
  {
    luaTable->pushRef();
  
    lua_State *L = luaTable->L;
    stackTop = lua_gettop(L);
  
    std::vector<LuaKey> key_stack = getKeyStack();
  
    return query_key_stack (L, key_stack);
  }
  
  void LuaTableNode::stackCreateValue()
  {
    luaTable->pushRef();
  
    lua_State *L = luaTable->L;
    stackTop = lua_gettop(L);
  
    std::vector<LuaKey> key_stack = getKeyStack();
  
    create_key_stack (L, key_stack);
  }
  
  LuaTable LuaTableNode::stackQueryTable()
  {
    luaTable->pushRef();
  
    lua_State *L = luaTable->L;
    stackTop = lua_gettop(L);
  
    std::vector<LuaKey> key_stack = getKeyStack();
  
    if (!query_key_stack (L, key_stack)) {
      std::ostringstream errormsg;
      errormsg << "Error: could not query table " << key << "." << std::endl;
      throw RigidBodyDynamics::Errors::RBDLError(errormsg.str());
    }
  
    return LuaTable::fromLuaState (L);
  }
  
  LuaTable LuaTableNode::stackCreateLuaTable()
  {
    luaTable->pushRef();
  
    lua_State *L = luaTable->L;
    stackTop = lua_gettop(L);
  
    std::vector<LuaKey> key_stack = getKeyStack();
  
    create_key_stack (L, key_stack);
  
    // create new table for the CustomType
    lua_newtable(luaTable->L);	// parent, CustomTable
    // add table of CustomType to the parent
    stackPushKey(); // parent, CustomTable, key
    lua_pushvalue(luaTable->L, -2); // parent, CustomTable, key, CustomTable
    lua_settable(luaTable->L, -4);
  
    LuaTable result;
    result.luaStateRef = luaTable->luaStateRef->acquire();
    lua_pushvalue (result.luaStateRef->L, -1);
    result.luaRef = luaL_ref (result.luaStateRef->L, LUA_REGISTRYINDEX);
  
    lua_pop (luaTable->luaStateRef->L, 2);
  
    return result;
  }
  
  void LuaTableNode::stackPushKey()
  {
    l_push_LuaKey (luaTable->L, key);
  }
  
  void LuaTableNode::stackRestore()
  {
    lua_pop (luaTable->L, lua_gettop(luaTable->L) - stackTop);
  
    luaTable->popRef();
  }
  
  bool LuaTableNode::exists()
  {
    bool result = true;
  
    if (!stackQueryValue()) {
      result = false;
    }
  
    stackRestore();
  
    return result;
  }
  
  void LuaTableNode::remove()
  {
    if (stackQueryValue()) {
      lua_pop(luaTable->L, 1);
  
      if (lua_gettop(luaTable->L) != 0) {
        l_push_LuaKey (luaTable->L, key);
        lua_pushnil (luaTable->L);
        lua_settable (luaTable->L, -3);
      } else {
        lua_pushnil (luaTable->L);
        lua_setglobal (luaTable->L, key.string_value.c_str());
      }
    }
  
    stackRestore();
  }
  
  size_t LuaTableNode::length()
  {
    size_t result = 0;
  
    if (stackQueryValue()) {
      #if LUA_VERSION_NUM == 501
      result = lua_objlen(luaTable->L, -1);
      #elif LUA_VERSION_NUM >= 502
      result = lua_rawlen(luaTable->L, -1);
      #endif
    }
  
    stackRestore();
  
    return result;
  }
  
  std::vector<LuaKey> LuaTableNode::keys()
  {
    std::vector<LuaKey> result;
  
    if (stackQueryValue()) {
      // loop over all keys
      lua_pushnil(luaTable->L);
      while (lua_next(luaTable->L, -2) != 0) {
        if (lua_isnumber(luaTable->L, -2)) {
          double number = lua_tonumber (luaTable->L, -2);
          double frac;
          if (modf (number, &frac) == 0) {
            LuaKey key (static_cast<int>(number));
            result.push_back (key);
          }
        } else if (lua_isstring (luaTable->L, -2)) {
          LuaKey key (lua_tostring(luaTable->L, -2));
          result.push_back (key);
        } else {
          cerr << "Warning: invalid LuaKey type for key " << 				lua_typename(luaTable->L,
               lua_type(luaTable->L, -2)) << "!" << endl;
        }
  
        lua_pop(luaTable->L, 1);
      }
    }
  
    stackRestore();
  
    return result;
  }
  
  
  template<> bool LuaTableNode::getDefault<bool>(const bool &default_value)
  {
    bool result = default_value;
  
    if (stackQueryValue()) {
      result = lua_toboolean (luaTable->L, -1);
    }
  
    stackRestore();
  
    return result;
  }
  
  template<> float LuaTableNode::getDefault<float>(const float &default_value)
  {
    float result = default_value;
  
    if (stackQueryValue()) {
      result = static_cast<float>(lua_tonumber (luaTable->L, -1));
    }
  
    stackRestore();
  
    return result;
  }
  
  template<> double LuaTableNode::getDefault<double>(const double &default_value)
  {
    double result = default_value;
  
    if (stackQueryValue()) {
      result = lua_tonumber (luaTable->L, -1);
    }
  
    stackRestore();
  
    return result;
  }
  
  template<> std::string LuaTableNode::getDefault<std::string>
  (const std::string &default_value)
  {
    std::string result = default_value;
  
    if (stackQueryValue() && lua_isstring(luaTable->L, -1)) {
      result = lua_tostring (luaTable->L, -1);
    }
  
    stackRestore();
  
    return result;
  }
  
  template<> void LuaTableNode::set<bool>(const bool &value)
  {
    stackCreateValue();
  
    l_push_LuaKey (luaTable->L, key);
    lua_pushboolean(luaTable->L, value);
    // stack: parent, key, value
    lua_settable (luaTable->L, -3);
  
    stackRestore();
  }
  
  template<> void LuaTableNode::set<float>(const float &value)
  {
    stackCreateValue();
  
    l_push_LuaKey (luaTable->L, key);
    lua_pushnumber(luaTable->L, static_cast<double>(value));
    // stack: parent, key, value
    lua_settable (luaTable->L, -3);
  
    stackRestore();
  }
  
  template<> void LuaTableNode::set<double>(const double &value)
  {
    stackCreateValue();
  
    l_push_LuaKey (luaTable->L, key);
    lua_pushnumber(luaTable->L, value);
    // stack: parent, key, value
    lua_settable (luaTable->L, -3);
  
    stackRestore();
  }
  
  template<> void LuaTableNode::set<std::string>(const std::string &value)
  {
    stackCreateValue();
  
    l_push_LuaKey (luaTable->L, key);
    lua_pushstring(luaTable->L, value.c_str());
    // stack: parent, key, value
    lua_settable (luaTable->L, -3);
  
    stackRestore();
  }
  
  //
  // LuaTable
  //
  LuaTable::LuaTable (const LuaTable &other) :
    filename (other.filename),
    referencesGlobal (other.referencesGlobal)
  {
    if (other.luaStateRef) {
      luaStateRef = other.luaStateRef->acquire();
  
      if (!referencesGlobal) {
        lua_rawgeti(luaStateRef->L, LUA_REGISTRYINDEX, other.luaRef);
        luaRef = luaL_ref (luaStateRef->L, LUA_REGISTRYINDEX);
      }
    }
  }
  
  LuaTable& LuaTable::operator= (const LuaTable &other)
  {
    if (&other != this) {
      if (luaStateRef) {
        // cleanup any existing reference
        luaL_unref (luaStateRef->L, LUA_REGISTRYINDEX, luaRef);
  
        // if this is the last, delete the Lua state
        int ref_count = luaStateRef->release();
  
        if (ref_count == 0) {
          if (luaStateRef->freeOnZeroRefs) {
            lua_close (luaStateRef->L);
          }
          delete luaStateRef;
          luaStateRef = NULL;
        }
      }
  
      filename = other.filename;
      luaStateRef = other.luaStateRef;
      referencesGlobal = other.referencesGlobal;
  
      if (other.luaStateRef) {
        luaStateRef = other.luaStateRef->acquire();
  
        if (!referencesGlobal) {
          lua_rawgeti(luaStateRef->L, LUA_REGISTRYINDEX, other.luaRef);
          luaRef = luaL_ref (luaStateRef->L, LUA_REGISTRYINDEX);
        }
      }
    }
  
    return *this;
  }
  
  LuaTable::~LuaTable()
  {
    if (luaRef != -1) {
      luaL_unref (luaStateRef->L, LUA_REGISTRYINDEX, luaRef);
    }
  
    if (luaStateRef) {
      int ref_count = luaStateRef->release();
  
      if (ref_count == 0) {
        if (luaStateRef->freeOnZeroRefs) {
          lua_close (luaStateRef->L);
        }
        delete luaStateRef;
        luaStateRef = NULL;
      }
    }
  }
  
  int LuaTable::length()
  {
    pushRef();
  
    if ((lua_gettop(L) == 0) || (lua_type (L, -1) != LUA_TTABLE)) {
      throw RigidBodyDynamics::Errors::RBDLError("Error: cannot query table length. No table on stack!\n");
    }
    size_t result = 0;
  
    #if LUA_VERSION_NUM == 501
    result = lua_objlen(L, -1);
    #elif LUA_VERSION_NUM >= 502
    result = lua_rawlen(L, -1);
    #endif
  
    popRef();
  
    return result;
  }
  
  void LuaTable::pushRef()
  {
    assert (luaStateRef);
    assert (luaStateRef->L);
  
    if (!referencesGlobal) {
      lua_rawgeti(luaStateRef->L, LUA_REGISTRYINDEX, luaRef);
    }
    L = luaStateRef->L;
  }
  
  void LuaTable::popRef()
  {
    if (!referencesGlobal) {
      lua_pop (luaStateRef->L, 1);
    }
    L = NULL;
  }
  
  LuaTable LuaTable::fromFile (const char* _filename)
  {
    LuaTable result;
  
    result.filename = _filename;
    result.luaStateRef = new LuaStateRef();
    result.luaStateRef->L = luaL_newstate();
    result.luaStateRef->count = 1;
    luaL_openlibs (result.luaStateRef->L);
  
    // Add the directory of _filename to package.path
    result.addSearchPath(get_file_directory (_filename).c_str());
  
    // run the file we
    if (luaL_dofile (result.luaStateRef->L, _filename)) {
      bail (result.luaStateRef->L, "Error running file: ");
    }
  
    result.luaRef = luaL_ref (result.luaStateRef->L, LUA_REGISTRYINDEX);
  
    return result;
  }
  
  LuaTable LuaTable::fromLuaExpression (const char* lua_expr)
  {
    LuaTable result;
  
    result.luaStateRef = new LuaStateRef();
    result.luaStateRef->L = luaL_newstate();
    result.luaStateRef->count = 1;
    luaL_openlibs (result.luaStateRef->L);
  
    if (luaL_loadstring (result.luaStateRef->L, lua_expr)) {
      bail (result.luaStateRef->L, "Error compiling expression!");
    }
  
    if (lua_pcall (result.luaStateRef->L, 0, LUA_MULTRET, 0)) {
      bail (result.luaStateRef->L, "Error running expression!");
    }
  
    if (lua_gettop(result.luaStateRef->L) != 0) {
      result.luaRef = luaL_ref (result.luaStateRef->L, LUA_REGISTRYINDEX);
    } else {
      result.referencesGlobal = true;
    }
  
    return result;
  }
  
  LuaTable LuaTable::fromLuaState (lua_State* L)
  {
    LuaTable result;
  
    result.luaStateRef = new LuaStateRef();
    result.luaStateRef->L = L;
    result.luaStateRef->count = 1;
    result.luaStateRef->freeOnZeroRefs = false;
  
    lua_pushvalue (result.luaStateRef->L, -1);
    result.luaRef = luaL_ref (result.luaStateRef->L, LUA_REGISTRYINDEX);
  
    return result;
  }
  
  void LuaTable::addSearchPath(const char* path)
  {
    if (luaStateRef->L == NULL) {
      throw RigidBodyDynamics::Errors::RBDLError("Error: Cannot add search path: Lua state is not initialized!\n");
    }
  
    lua_getglobal(luaStateRef->L, "package");
    lua_getfield (luaStateRef->L, -1, "path");
    if (lua_type(luaStateRef->L, -1) != LUA_TSTRING) {
      throw RigidBodyDynamics::Errors::RBDLError("Error: could not get package.path!\n");
    }
  
    string package_path = lua_tostring (luaStateRef->L, -1);
    package_path = package_path + string (";") + string(path) + "?.lua;";
  
    lua_pushstring(luaStateRef->L, package_path.c_str());
    lua_setfield (luaStateRef->L, -3, "path");
  
    lua_pop(luaStateRef->L, 2);
  }
  
  std::string LuaTable::serialize()
  {
    pushRef();
  
    std::string result;
  
    int current_top = lua_gettop(L);
    if (lua_gettop(L) != 0) {
      if (luaL_loadstring(L, serialize_lua)) {
        bail (L, "Error loading serialization function: ");
      }
  
      if (lua_pcall(L, 0, 0, 0)) {
        bail (L, "Error compiling serialization function: " );
      }
  
      lua_getglobal (L, "serialize");
      assert (lua_isfunction (L, -1));
      lua_pushvalue (L, -2);
      if (lua_pcall (L, 1, 1, 0)) {
        bail (L, "Error while serializing: ");
      }
      result = string("return ") + lua_tostring (L, -1);
    } else {
      throw RigidBodyDynamics::Errors::RBDLError("Cannot serialize global Lua state!");
    }
  
    lua_pop (L, lua_gettop(L) - current_top);
  
    popRef();
  
    return result;
  }
  
  std::string LuaTable::orderedSerialize()
  {
    pushRef();
  
    std::string result;
  
    int current_top = lua_gettop(L);
    if (lua_gettop(L) != 0) {
      if (luaL_loadstring(L, serialize_lua)) {
        bail (L, "Error loading serialization function: ");
      }
  
      if (lua_pcall(L, 0, 0, 0)) {
        bail (L, "Error compiling serialization function: " );
      }
  
      lua_getglobal (L, "serialize");
      assert (lua_isfunction (L, -1));
      lua_pushvalue (L, -2);
      lua_pushstring (L, "");
      lua_pushboolean (L, true);
      if (lua_pcall (L, 3, 1, 0)) {
        bail (L, "Error while serializing: ");
      }
      result = string("return ") + lua_tostring (L, -1);
    } else {
      throw RigidBodyDynamics::Errors::RBDLError("Cannot serialize global Lua state!\n");
    }
  
    lua_pop (L, lua_gettop(L) - current_top);
  
    popRef();
  
    return result;
  }
  