/***************************************************************************

    Konami 005289 - SCC sound as used in Bubblesystem

    This file is pieced together by Bryan McPhail from a combination of
    Namco Sound, Amuse by Cab, Nemesis schematics and whoever first
    figured out SCC!

    The 005289 is a 2 channel sound generator. Each channel gets its
    waveform from a prom (4 bits wide).

    (From Nemesis schematics)

    Address lines A0-A4 of the prom run to the 005289, giving 32 bytes
    per waveform.  Address lines A5-A7 of the prom run to PA5-PA7 of
    the AY8910 control port A, giving 8 different waveforms. PA0-PA3
    of the AY8910 control volume.

    The second channel is the same as above except port B is used.

    The 005289 has 12 address inputs and 4 control inputs: LD1, LD2, TG1, TG2.
    It has no data bus, so data values written don't matter.
    When LD1 or LD2 is asserted, the 12 bit value on the address bus is
    latched. Each of the two channels has its own latch.
    When TG1 or TG2 is asserted, the frequency of the respective channel is
    set to the previously latched value.

    The 005289 itself is nothing but an address generator. Digital to analog
    conversion, volume control and mixing of the channels is all done
    externally via resistor networks and 4066 switches and is only implemented
    here for convenience.

    Ported from MAME SVN 27/05/2014

***************************************************************************/

#include "burnint.h"
#include "k005289.h"

// is this an actual hardware limit? or just an arbitrary divider
// to bring the output frequency down to a reasonable value for MAME?
#define CLOCK_DIVIDER 32

static const UINT8 *sound_prom;
static INT32 rate;

static INT16 *mixer_table;
static INT16 *mixer_lookup;
static INT16 *mixer_buffer;
static double gain[2];
static INT32  output_dir[2];

static UINT32 counter[2];
static UINT16 frequency[2];
static UINT16 freq_latch[2];
static UINT16 waveform[2];
static UINT8 volume[2];

/* build a table to divide by the number of voices */
static void make_mixer_table(INT32 voices)
{
	INT32 count = voices * 128;
	INT32 i;
	INT32 ngain = 16;

	/* allocate memory */
	mixer_table = (INT16*)BurnMalloc(256 * voices * sizeof(INT16));

	/* find the middle of the table */
	mixer_lookup = mixer_table + (128 * voices);

	/* fill in the table - 16 bit case */
	for (i = 0; i < count; i++)
	{
		INT32 val = i * ngain * 16 / voices;
		if (val > 32767) val = 32767;
		mixer_lookup[ i] = val;
		mixer_lookup[-i] = -val;
	}
}

void K005289Reset()
{
#if defined FBA_DEBUG
	if (!DebugSnd_K005289Initted) bprintf(PRINT_ERROR, _T("K005289Reset called without init\n"));
#endif

	/* reset all the voices */
	for (INT32 i = 0; i < 2; i++)
	{
		counter[i] = 0;
		frequency[i] = 0;
		freq_latch[i] = 0;
		waveform[i] = i * 0x100;
		volume[i] = 0;
	}
}

void K005289Init(INT32 clock, UINT8 *prom)
{
	/* get stream channels */
	rate = ((clock / CLOCK_DIVIDER) * 100) / nBurnFPS;

	/* allocate a pair of buffers to mix into - 1 frame's worth should be more than enough */
	mixer_buffer = (INT16*)BurnMalloc(rate * sizeof(INT16));

	/* build the mixer table */
	make_mixer_table(2);

	sound_prom = prom;

	DebugSnd_K005289Initted = 1;
}

void K005289SetRoute(INT32 nIndex, double nVolume, INT32 nRouteDir)
{
#if defined FBA_DEBUG
	if (!DebugSnd_K005289Initted) bprintf(PRINT_ERROR, _T("K005289SetRoute called without init\n"));
#endif

	gain[nIndex] = nVolume;
	output_dir[nIndex] = nRouteDir;
}

void K005289Exit()
{
#if defined FBA_DEBUG
	if (!DebugSnd_K005289Initted) bprintf(PRINT_ERROR, _T("K005289Exit called without init\n"));
#endif

	if (!DebugSnd_K005289Initted) return;

	BurnFree (mixer_buffer);
	BurnFree (mixer_table);
	mixer_buffer = NULL;
	mixer_table = NULL;

	DebugSnd_K005289Initted = 0;
}

void K005289Update(INT16 *buffer, INT32 samples)
{
#if defined FBA_DEBUG
	if (!DebugSnd_K005289Initted) bprintf(PRINT_ERROR, _T("K005289Update called without init\n"));
#endif

	INT16 *mix;
	INT32 i,v,f;

	/* zap the contents of the mixer buffer */
	memset(mixer_buffer, 0, rate * sizeof(INT16));

	v=volume[0];
	f=frequency[0];
	if (v && f)
	{
		const UINT8 *w = sound_prom + waveform[0];
		INT32 c = counter[0];

		mix = mixer_buffer;

		/* add our contribution */
		for (i = 0; i < rate; i++)
		{
			INT32 offs;

			c += CLOCK_DIVIDER;
			offs = (c / f) & 0x1f;
			*mix++ += ((w[offs] & 0x0f) - 8) * v;
		}

		/* update the counter for this voice */
		counter[0] = c % (f * 0x20);
	}

	v=volume[1];
	f=frequency

	if (v && f)
	{
		const UINT8 *w = sound_prom + waveform[1];
		INT32 c = counter[1];

		mix = mixer_buffer;

		/* add our contribution */
		for (i = 0; i < rate; i++)
		{
			INT32 offs;

			c += CLOCK_DIVIDER;
			offs = (c / f) & 0x1f;
			*mix++ += ((w[offs] & 0x0f) - 8) * v;
		}

		/* update the counter for this voice */
		counter[1] = c % (f * 0x20);
	}

	/* mix it down */
	mix = mixer_buffer;
	for (i = 0; i < samples; i++)
	{
		INT32 nLeftSample = mixer_lookup[mix[(i * rate) / nBurnSoundLen]];
		INT32 nRightSample = nLeftSample;

		if ((output_dir[BURN_SND_K005289_ROUTE_1] & BURN_SND_ROUTE_LEFT) == BURN_SND_ROUTE_LEFT) {
			nLeftSample = (INT32)(nLeftSample * gain[BURN_SND_K005289_ROUTE_1]);
		}
		if ((output_dir[BURN_SND_K005289_ROUTE_1] & BURN_SND_ROUTE_RIGHT) == BURN_SND_ROUTE_RIGHT) {
			nRightSample = (INT32)(nRightSample * gain[BURN_SND_K005289_ROUTE_1]);
		}

		nLeftSample = BURN_SND_CLIP(nLeftSample);
		nRightSample = BURN_SND_CLIP(nRightSample);

		buffer[0] = BURN_SND_CLIP(buffer[0] + nLeftSample);
		buffer[1] = BURN_SND_CLIP(buffer[1] + nRightSample);
		buffer+=2;
	}
}

void K005289ControlAWrite(UINT8 data)
{
#if defined FBA_DEBUG
	if (!DebugSnd_K005289Initted) bprintf(PRINT_ERROR, _T("K005289ControlAWrite called without init\n"));
#endif

	volume[0] = data & 0xf;
	waveform[0] = data & 0xe0;
}

void K005289ControlBWrite(UINT8 data)
{
#if defined FBA_DEBUG
	if (!DebugSnd_K005289Initted) bprintf(PRINT_ERROR, _T("K005289ControlBWrite called without init\n"));
#endif

	volume[1] = data & 0xf;
	waveform[1] = (data & 0xe0) + 0x100;
}

void K005289Ld1Write(INT32 offset)
{
#if defined FBA_DEBUG
	if (!DebugSnd_K005289Initted) bprintf(PRINT_ERROR, _T("K005289Ld1 called without init\n"));
#endif

	offset &= 0xfff;
	freq_latch[0] = 0xfff - offset;
}

void K005289Ld2Write(INT32 offset)
{
#if defined FBA_DEBUG
	if (!DebugSnd_K005289Initted) bprintf(PRINT_ERROR, _T("K005289Ld2 called without init\n"));
#endif

	offset &= 0xfff;
	freq_latch[1] = 0xfff - offset;
}

void K005289Tg1Write()
{
#if defined FBA_DEBUG
	if (!DebugSnd_K005289Initted) bprintf(PRINT_ERROR, _T("K005289Tg1 called without init\n"));
#endif

	frequency[0] = freq_latch[0];
}

void K005289Tg2Write()
{
#if defined FBA_DEBUG
	if (!DebugSnd_K005289Initted) bprintf(PRINT_ERROR, _T("K005289Tg2 called without init\n"));
#endif

	frequency[1] = freq_latch[1];
}

void K005289Scan(INT32 nAction, INT32 *)
{
#if defined FBA_DEBUG
	if (!DebugSnd_K005289Initted) bprintf(PRINT_ERROR, _T("K005289Scan called without init\n"));
#endif

	if (nAction & ACB_DRIVER_DATA)
	{
		for (INT32 i = 0; i < 2; i++) {
			SCAN_VAR(counter[i]);
			SCAN_VAR(frequency[i]);
			SCAN_VAR(freq_latch[i]);
			SCAN_VAR(waveform[i]);
			SCAN_VAR(volume[i]);
		}
	}
}


static void make_mixer_table(INT32 voices)
{
	INT32 count = voices * 128;
	INT32 i;
	INT32 ngain = 16;

	/* allocate memory */
	mixer_table = (INT16*)BurnMalloc(256 * voices * sizeof(INT16));

	/* find the middle of the table */
	mixer_lookup = mixer_table + (128 * voices);

	/* fill in the table - 16 bit case */
	for (i = 0; i < count; i++)
	{
		INT32 val = i * ngain * 16 / voices;
		if (val > 32767) val = 32767;
		mixer_lookup[ i] = val;
		mixer_lookup[-i] = -val;
	}
}

/***************************************************************************

    Konami 005289 - SCC sound as used in Bubblesystem

    This file is pieced together by Bryan McPhail from a combination of
    Namco Sound, Amuse by Cab, Nemesis schematics and whoever first
    figured out SCC!

    The 005289 is a 2 channel sound generator. Each channel gets its
    waveform from a prom (4 bits wide).

    (From Nemesis schematics)

    Address lines A0-A4 of the prom run to the 005289, giving 32 bytes
    per waveform.  Address lines A5-A7 of the prom run to PA5-PA7 of
    the AY8910 control port A, giving 8 different waveforms. PA0-PA3
    of the AY8910 control volume.

    The second channel is the same as above except port B is used.

    The 005289 has 12 address inputs and 4 control inputs: LD1, LD2, TG1, TG2.
    It has no data bus, so data values written don't matter.
    When LD1 or LD2 is asserted, the 12 bit value on the address bus is
    latched. Each of the two channels has its own latch.
    When TG1 or TG2 is asserted, the frequency of the respective channel is
    set to the previously latched value.

    The 005289 itself is nothing but an address generator. Digital to analog
    conversion, volume control and mixing of the channels is all done
    externally via resistor networks and 4066 switches and is only implemented
    here for convenience.

    Ported from MAME SVN 27/05/2014

***************************************************************************/

#include "burnint.h"
#include "k005289.h"

// is this an actual hardware limit? or just an arbitrary divider
// to bring the output frequency down to a reasonable value for MAME?
#define CLOCK_DIVIDER 32

static const UINT8 *sound_prom;
static INT32 rate;

static INT16 *mixer_table;
static INT16 *mixer_lookup;
static INT16 *mixer_buffer;
static double gain[2];
static INT32  output_dir[2];

static UINT32 counter[2];
static UINT16 frequency[2];
static UINT16 freq_latch[2];
static UINT16 waveform[2];
static UINT8 volume[2];

/* build a table to divide by the number of voices */

void K005289Reset()
{
#if defined FBA_DEBUG
	if (!DebugSnd_K005289Initted) bprintf(PRINT_ERROR, _T("K005289Reset called without init\n"));
#endif

	/* reset all the voices */
	for (INT32 i = 0; i < 2; i++)
	{
		counter[i] = 0;
		frequency[i] = 0;
		freq_latch[i] = 0;
		waveform[i] = i * 0x100;
		volume[i] = 0;
	}
}

void K005289Init(INT32 clock, UINT8 *prom)
{
	/* get stream channels */
	rate = ((clock / CLOCK_DIVIDER) * 100) / nBurnFPS;

	/* allocate a pair of buffers to mix into - 1 frame's worth should be more than enough */
	mixer_buffer = (INT16*)BurnMalloc(rate * sizeof(INT16));

	/* build the mixer table */
	make_mixer_table(2);

	sound_prom = prom;

	DebugSnd_K005289Initted = 1;
}

void K005289SetRoute(INT32 nIndex, double nVolume, INT32 nRouteDir)
{
#if defined FBA_DEBUG
	if (!DebugSnd_K005289Initted) bprintf(PRINT_ERROR, _T("K005289SetRoute called without init\n"));
#endif

	gain[nIndex] = nVolume;
	output_dir[nIndex] = nRouteDir;
}

void K005289Exit()
{
#if defined FBA_DEBUG
	if (!DebugSnd_K005289Initted) bprintf(PRINT_ERROR, _T("K005289Exit called without init\n"));
#endif

	if (!DebugSnd_K005289Initted) return;

	BurnFree (mixer_buffer);
	BurnFree (mixer_table);
	mixer_buffer = NULL;
	mixer_table = NULL;

	DebugSnd_K005289Initted = 0;
}

void K005289Update(INT16 *buffer, INT32 samples)
{
#if defined FBA_DEBUG
	if (!DebugSnd_K005289Initted) bprintf(PRINT_ERROR, _T("K005289Update called without init\n"));
#endif

	INT16 *mix;
	INT32 i,v,f;

	/* zap the contents of the mixer buffer */
	memset(mixer_buffer, 0, rate * sizeof(INT16));

	v=volume[0];
	f=frequency[0];
	if (v && f)
	{
		const UINT8 *w = sound_prom + waveform[0];
		INT32 c = counter[0];

		mix = mixer_buffer;

		/* add our contribution */
		for (i = 0; i < rate; i++)
		{
			INT32 offs;

			c += CLOCK_DIVIDER;
			offs = (c / f) & 0x1f;
			*mix++ += ((w[offs] & 0x0f) - 8) * v;
		}

		/* update the counter for this voice */
		counter[0] = c % (f * 0x20);
	}

	v=volume[1];
	f=frequency[1];
	if (v && f)
	{
		const UINT8 *w = sound_prom + waveform[1];
		INT32 c = counter[1];

		mix = mixer_buffer;

		/* add our contribution */
		for (i = 0; i < rate; i++)
		{
			INT32 offs;

			c += CLOCK_DIVIDER;
			offs = (c / f) & 0x1f;
			*mix++ += ((w[offs] & 0x0f) - 8) * v;
		}

		/* update the counter for this voice */
		counter[1] = c % (f * 0x20);
	}

	/* mix it down */
	mix = mixer_buffer;
	for (i = 0; i < samples; i++)
	{
		INT32 nLeftSample = mixer_lookup[mix[(i * rate) / nBurnSoundLen]];
		INT32 nRightSample = nLeftSample;

		if ((output_dir[BURN_SND_K005289_ROUTE_1] & BURN_SND_ROUTE_LEFT) == BURN_SND_ROUTE_LEFT) {
			nLeftSample = (INT32)(nLeftSample * gain[BURN_SND_K005289_ROUTE_1]);
		}
		if ((output_dir[BURN_SND_K005289_ROUTE_1] & BURN_SND_ROUTE_RIGHT) == BURN_SND_ROUTE_RIGHT) {
			nRightSample = (INT32)(nRightSample * gain[BURN_SND_K005289_ROUTE_1]);
		}

		nLeftSample = BURN_SND_CLIP(nLeftSample);
		nRightSample = BURN_SND_CLIP(nRightSample);

		buffer[0] = BURN_SND_CLIP(buffer[0] + nLeftSample);
		buffer[1] = BURN_SND_CLIP(buffer[1] + nRightSample);
		buffer+=2;
	}
}

void K005289ControlAWrite(UINT8 data)
{
#if defined FBA_DEBUG
	if (!DebugSnd_K005289Initted) bprintf(PRINT_ERROR, _T("K005289ControlAWrite called without init\n"));
#endif

	volume[0] = data & 0xf;
	waveform[0] = data & 0xe0;
}

void K005289ControlBWrite(UINT8 data)
{
#if defined FBA_DEBUG
	if (!DebugSnd_K005289Initted) bprintf(PRINT_ERROR, _T("K005289ControlBWrite called without init\n"));
#endif

	volume[1] = data & 0xf;
	waveform[1] = (data & 0xe0) + 0x100;
}

void K005289Ld1Write(INT32 offset)
{
#if defined FBA_DEBUG
	if (!DebugSnd_K005289Initted) bprintf(PRINT_ERROR, _T("K005289Ld1 called without init\n"));
#endif

	offset &= 0xfff;
	freq_latch[0] = 0xfff - offset;
}

void K005289Ld2Write(INT32 offset)
{
#if defined FBA_DEBUG
	if (!DebugSnd_K005289Initted) bprintf(PRINT_ERROR, _T("K005289Ld2 called without init\n"));
#endif

	offset &= 0xfff;
	freq_latch[1] = 0xfff - offset;
}

void K005289Tg1Write()
{
#if defined FBA_DEBUG
	if (!DebugSnd_K005289Initted) bprintf(PRINT_ERROR, _T("K005289Tg1 called without init\n"));
#endif

	frequency[0] = freq_latch[0];
}

void K005289Tg2Write()
{
#if defined FBA_DEBUG
	if (!DebugSnd_K005289Initted) bprintf(PRINT_ERROR, _T("K005289Tg2 called without init\n"));
#endif

	frequency[1] = freq_latch[1];
}

void K005289Scan(INT32 nAction, INT32 *)
{
#if defined FBA_DEBUG
	if (!DebugSnd_K005289Initted) bprintf(PRINT_ERROR, _T("K005289Scan called without init\n"));
#endif

	if (nAction & ACB_DRIVER_DATA)
	{
		for (INT32 i = 0; i < 2; i++) {
			SCAN_VAR(counter[i]);
			SCAN_VAR(frequency[i]);
			SCAN_VAR(freq_latch[i]);
			SCAN_VAR(waveform[i]);
			SCAN_VAR(volume[i]);
		}
	}
}


GorIVAlgTask::GorIVAlgTask(const QByteArray &inputSeq)
    : SecStructPredictTask(inputSeq)

/**
 * UGENE - Integrated Bioinformatics Tools.
 *
 */

#include "GorIVAlgTask.h"

#include <QFile>

#include <U2Core/Counter.h>
#include <U2Core/U2SafePoints.h>

#include <U2View/SecStructPredictUtils.h>

#include "gor.h"

namespace U2 {

const QString GorIVAlgTask::taskName = "GORIV";
QMutex GorIVAlgTask::runLock;

{
    GCOUNTER(cvar, "GorIVAlgTask");
}

void GorIVAlgTask::run() {
    output.resize(sequence.size());
    sequence.push_front(' ');

    QFile seqDb(":gor4//datafiles//New_KS.267.seq");
    if (!seqDb.open(QIODevice::ReadOnly)) {
        stateInfo.setError(SecStructPredictTask::tr("seq database not found"));
        return;
    }

    QFile strucDb(":gor4//datafiles//New_KS.267.obs");
    if (!strucDb.open(QIODevice::ReadOnly)) {
        stateInfo.setError(SecStructPredictTask::tr("observed structures database not found"));
        return;
    }

    // TODO: get rid of this limit
    const int MAXRES = 10000;
    if (sequence.size() > MAXRES) {
        stateInfo.setError(SecStructPredictTask::tr("sequence is too long, max seq size is 10000"));
        return;
    }

    QMutexLocker runLocker(&runLock);
    runGORIV(seqDb, strucDb, sequence.data(), sequence.size() - 1, output.data(), stateInfo);
    CHECK_OP(stateInfo, );
    results = SecStructPredictUtils::saveAlgorithmResultsAsAnnotations(output, GORIV_ANNOTATION_NAME);
}

}  // namespace U2


/**
 * UGENE - Integrated Bioinformatics Tools.
 *
 */

#include "GorIVAlgTask.h"

#include <QFile>

#include <U2Core/Counter.h>
#include <U2Core/U2SafePoints.h>

#include <U2View/SecStructPredictUtils.h>

#include "gor.h"

namespace U2 {

const QString GorIVAlgTask::taskName = "GORIV";
QMutex GorIVAlgTask::runLock;

GorIVAlgTask::GorIVAlgTask(const QByteArray &inputSeq)
    : SecStructPredictTask(inputSeq)

void GorIVAlgTask::run() {
    output.resize(sequence.size());
    sequence.push_front(' ');

    QFile seqDb(":gor4//datafiles//New_KS.267.seq");
    if (!seqDb.open(QIODevice::ReadOnly)) {
        stateInfo.setError(SecStructPredictTask::tr("seq database not found"));
        return;
    }

    QFile strucDb(":gor4//datafiles//New_KS.267.obs");
    if (!strucDb.open(QIODevice::ReadOnly)) {
        stateInfo.setError(SecStructPredictTask::tr("observed structures database not found"));
        return;
    }

    // TODO: get rid of this limit
    const int MAXRES = 10000;
    if (sequence.size() > MAXRES) {
        stateInfo.setError(SecStructPredictTask::tr("sequence is too long, max seq size is 10000"));
        return;
    }

    QMutexLocker runLocker(&runLock);
    runGORIV(seqDb, strucDb, sequence.data(), sequence.size() - 1, output.data(), stateInfo);
    CHECK_OP(stateInfo, );
    results = SecStructPredictUtils::saveAlgorithmResultsAsAnnotations(output, GORIV_ANNOTATION_NAME);
}

}  // namespace U2


/**
 * UGENE - Integrated Bioinformatics Tools.
 *
 */

#include "GorIVAlgTask.h"

#include <QFile>

#include <U2Core/Counter.h>
#include <U2Core/U2SafePoints.h>

#include <U2View/SecStructPredictUtils.h>

#include "gor.h"

namespace U2 {

const QString GorIVAlgTask::taskName = "GORIV";
QMutex GorIVAlgTask::runLock;

GorIVAlgTask::GorIVAlgTask(const QByteArray &inputSeq)
    : SecStructPredictTask(inputSeq) {
    GCOUNTER(cvar, "GorIVAl

}

void GorIVAlgTask::run() {
    output.resize(sequence.size());
    sequence.push_front(' ');

    QFile seqDb(":gor4//datafiles//New_KS.267.seq");
    if (!seqDb.open(QIODevice::ReadOnly)) {
        stateInfo.setError(SecStructPredictTask::tr("seq database not found"));
        return;
    }

    QFile strucDb(":gor4//datafiles//New_KS.267.obs");
    if (!strucDb.open(QIODevice::ReadOnly)) {
        stateInfo.setError(SecStructPredictTask::tr("observed structures database not found"));
        return;
    }

    // TODO: get rid of this limit
    const int MAXRES = 10000;
    if (sequence.size() > MAXRES) {
        stateInfo.setError(SecStructPredictTask::tr("sequence is too long, max seq size is 10000"));
        return;
    }

    QMutexLocker runLocker(&runLock);
    runGORIV(seqDb, strucDb, sequence.data(), sequence.size() - 1, output.data(), stateInfo);
    CHECK_OP(stateInfo, );
    results = SecStructPredictUtils::saveAlgorithmResultsAsAnnotations(output, GORIV_ANNOTATION_NAME);
}

}  // namespace U2


/*
 *
 */

#include "config.h"
#include "ArrayAllocationProfile.h"

#include "JSCInlines.h"

namespace JSC {

{
    // This is awkwardly racy but totally sound even when executed concurrently. The
    // worst cases go something like this:
    // - Two threads race to execute this code; one of them succeeds in updating the
    //   m_currentIndexingType and the other either updates it again, or sees a null
    //   m_lastArray; if it updates it again then at worst it will cause the profile
    //   to "forget" some array. That's still sound, since we don't promise that
    //   this profile is a reflection of any kind of truth.
    // - A concurrent thread reads m_lastArray, but that array is now dead. While
    //   it's possible for that array to no longer be reachable, it cannot actually
    //   be freed, since we require the GC to wait until all concurrent JITing

    JSArray* lastArray = m_lastArray;
    if (!lastArray)
        return;
    m_currentIndexingType = leastUpperBoundOfIndexingTypes(m_currentIndexingType, lastArray->indexingType());
    m_lastArray = 0;
}

} // namespace JSC


/*
 *
 */

#include "config.h"
#include "ArrayAllocationProfile.h"

#include "JSCInlines.h"

namespace JSC {

void ArrayAllocationProfile::updateIndexingType()
{
    // This is awkwardly racy but totally sound even when executed concurrently. The
    // worst cases go something like this:
    // - Two threads race to execute this code; one of them succeeds in updating the
    //   m_currentIndexingType and the other either updates it again, or sees a null
    //   m_lastArray; if it updates it again then at worst it will cause the profile
    //   to "forget" some array. That's still sound, since we don't promise that
    //   this profile is a reflection of any kind of truth.
    // - A concurrent thread reads m_lastArray, but that array is now dead. While
    //   it's possible for that array to no longer be reachable, it cannot actually
    //   be freed, since we require the GC to wait until all concurrent JITing

    JSArray

    if (!lastArray)
        return;
    m_currentIndexingType = leastUpperBoundOfIndexingTypes(m_currentIndexingType, lastArray->indexingType());
    m_lastArray = 0;
}

} // namespace JSC


void prepareAndExecute(const Arguments &args)

/*

*/

#include <NdbApi.hpp>

#include "adapter_global.h"
#include "js_wrapper_macros.h"
#include "Record.h"
#include "NativeMethodCall.h"
#include "NdbWrapperErrors.h"
#include "ScanOperation.h"

using namespace v8;

V8WrapperFn newScanOperation;
V8WrapperFn prepareAndExecute;
V8WrapperFn getOperationError;
V8WrapperFn scanNextResult;
V8WrapperFn scanFetchResults;
V8WrapperFn ScanOperation_close;
V8WrapperFn getNdbError;
V8WrapperFn ScanOp_readBlobResults;

class ScanOperationEnvelopeClass : public Envelope {
public:
  ScanOperationEnvelopeClass() : Envelope("ScanOperation") {
    addMethod("getNdbError", getNdbError<ScanOperation>);
    addMethod("prepareAndExecute", prepareAndExecute);
    addMethod("fetchResults", scanFetchResults);
    addMethod("nextResult", scanNextResult);
    addMethod("close", ScanOperation_close);
    addMethod("readBlobResults", ScanOp_readBlobResults);
  }
};

ScanOperationEnvelopeClass ScanOperationEnvelope;

Envelope * getScanOperationEnvelope() {
  return & ScanOperationEnvelope;
}

// Constructor wrapper
void newScanOperation(const Arguments &args) {
  EscapableHandleScope scope(args.GetIsolate());
  ScanOperation * s = new ScanOperation(args);
  Local<Value> wrapper = ScanOperationEnvelope.wrap(s);
  // freeFromGC: Disabled as it leads to segfaults during garbage collection
  args.GetReturnValue().Set(scope.Escape(wrapper));
}

// ASYNC

{
  EscapableHandleScope scope(args.GetIsolate());
  DEBUG_MARKER(UDEB_DEBUG);
  REQUIRE_ARGS_LENGTH(1);
  typedef NativeMethodCall_0_<int, ScanOperation> MCALL;
  MCALL * mcallptr = new MCALL(& ScanOperation::prepareAndExecute, args);
  mcallptr->errorHandler = getNdbErrorIfLessThanZero;
  mcallptr->runAsync();

  args.GetReturnValue().SetUndefined();
}

// ASYNC
void ScanOperation_close(const Arguments & args) {
  typedef NativeVoidMethodCall_0_<ScanOperation> NCALL;
  NCALL * ncallptr = new NCALL(& ScanOperation::close, args);
  ncallptr->runAsync();
  args.GetReturnValue().SetUndefined();
}

// IMMEDIATE
void scanNextResult(const Arguments & args) {
  DEBUG_MARKER(UDEB_DETAIL);
  EscapableHandleScope scope(args.GetIsolate());
  typedef NativeMethodCall_1_<int, ScanOperation, char *> MCALL;
  MCALL mcall(& ScanOperation::nextResult, args);
  mcall.run();
  args.GetReturnValue().Set(scope.Escape(mcall.jsReturnVal()));
}

// ASYNC; CALLBACK GETS (Null-Or-Error, Int)
void scanFetchResults(const Arguments & args) {
  DEBUG_MARKER(UDEB_DETAIL);
  REQUIRE_ARGS_LENGTH(3);
  typedef NativeMethodCall_2_<int, ScanOperation, char *, bool> MCALL;
  MCALL * ncallptr = new MCALL(& ScanOperation::fetchResults, args);
  ncallptr->errorHandler = getNdbErrorIfLessThanZero;
  ncallptr->runAsync();
  args.GetReturnValue().SetUndefined();
}

void ScanOp_readBlobResults(const Arguments & args) {
  ScanOperation * op = unwrapPointer<ScanOperation *>(args.Holder());
  op->readBlobResults(args);
}

#define WRAP_CONSTANT(TARGET, X) DEFINE_JS_INT(TARGET, #X, NdbScanOperation::X)

void ScanHelper_initOnLoad(Handle<Object> target) {
  Local<Object> scanObj = Object::New(Isolate::GetCurrent());
  Local<String> scanKey = NEW_SYMBOL("Scan");
  target->Set(scanKey, scanObj);

  DEFINE_JS_FUNCTION(scanObj, "create", newScanOperation);

  Local<Object> ScanHelper = Object::New(Isolate::GetCurrent());
  Local<Object> ScanFlags =  Object::New(Isolate::GetCurrent());

  scanObj->Set(NEW_SYMBOL("helper"), ScanHelper);
  scanObj->Set(NEW_SYMBOL("flags"), ScanFlags);

  WRAP_CONSTANT(ScanFlags, SF_TupScan);
  WRAP_CONSTANT(ScanFlags, SF_DiskScan);
  WRAP_CONSTANT(ScanFlags, SF_OrderBy);
  WRAP_CONSTANT(ScanFlags, SF_OrderByFull);
  WRAP_CONSTANT(ScanFlags, SF_Descending);
  WRAP_CONSTANT(ScanFlags, SF_ReadRangeNo);
  WRAP_CONSTANT(ScanFlags, SF_MultiRange);
  WRAP_CONSTANT(ScanFlags, SF_KeyInfo);

  DEFINE_JS_INT(ScanHelper, "table_record", SCAN_TABLE_RECORD);
  DEFINE_JS_INT(ScanHelper, "index_record", SCAN_INDEX_RECORD);
  DEFINE_JS_INT(ScanHelper, "lock_mode", SCAN_LOCK_MODE);
  DEFINE_JS_INT(ScanHelper, "bounds", SCAN_BOUNDS);
  DEFINE_JS_INT(ScanHelper, "flags", SCAN_OPTION_FLAGS);
  DEFINE_JS_INT(ScanHelper, "batch_size", SCAN_OPTION_BATCH_SIZE);
  DEFINE_JS_INT(ScanHelper, "parallel", SCAN_OPTION_PARALLELISM);
  DEFINE_JS_INT(ScanHelper, "filter_code", SCAN_FILTER_CODE);
}


/*

*/

#include <NdbApi.hpp>

#include "adapter_global.h"
#include "js_wrapper_macros.h"
#include "Record.h"
#include "NativeMethodCall.h"
#include "NdbWrapperErrors.h"
#include "ScanOperation.h"

using namespace v8;

V8WrapperFn newScanOperation;
V8WrapperFn prepareAndExecute;
V8WrapperFn getOperationError;
V8WrapperFn scanNextResult;
V8WrapperFn scanFetchResults;
V8WrapperFn ScanOperation_close;
V8WrapperFn getNdbError;
V8WrapperFn ScanOp_readBlobResults;

class ScanOperationEnvelopeClass : public Envelope {
public:
  ScanOperationEnvelopeClass() : Envelope("ScanOperation") {
    addMethod("getNdbError", getNdbError<ScanOperation>);
    addMethod("prepareAndExecute", prepareAndExecute);
    addMethod("fetchResults", scanFetchResults);
    addMethod("nextResult", scanNextResult);
    addMethod("close", ScanOperation_close);
    addMethod("readBlobResults", ScanOp_readBlobResults);
  }
};

ScanOperationEnvelopeClass ScanOperationEnvelope;

Envelope * getScanOperationEnvelope()

// Constructor wrapper
void newScanOperation(const Arguments &args) {
  EscapableHandleScope scope(args.GetIsolate());
  ScanOperation * s = new ScanOperation(args);
  Local<Value> wrapper = ScanOperationEnvelope.wrap(s);
  // freeFromGC: Disabled as it leads to segfaults during garbage collection
  args.GetReturnValue().Set(scope.Escape(wrapper));
}

// ASYNC
void prepareAndExecute(const Arguments &args) {
  EscapableHandleScope scope(args.GetIsolate());
  DEBUG_MARKER(UDEB_DEBUG);
  REQUIRE_ARGS_LENGTH(1);
  typedef NativeMethodCall_0_<int, ScanOperation> MCALL;
  MCALL * mcallptr = new MCALL(& ScanOperation::prepareAndExecute, args);
  mcallptr->errorHandler = getNdbErrorIfLessThanZero;
  mcallptr->runAsync();

  args.GetReturnValue().SetUndefined();
}

// ASYNC
void ScanOperation_close(const Arguments & args) {
  typedef NativeVoidMethodCall_0_<ScanOperation> NCALL;
  NCALL * ncallptr = new NCALL(& ScanOperation::close, args);
  ncallptr->runAsync();
  args.GetReturnValue().SetUndefined();
}

// IMMEDIATE
void scanNextResult(const Arguments & args) {
  DEBUG_MARKER(UDEB_DETAIL);
  EscapableHandleScope scope(args.GetIsolate());
  typedef NativeMethodCall_1_<int, ScanOperation, char *> MCALL;
  MCALL mcall(& ScanOperation::nextResult, args);
  mcall.run();
  args.GetReturnValue().Set(scope.Escape(mcall.jsReturnVal()));
}

// ASYNC; CALLBACK GETS (Null-Or-Error, Int)
void scanFetchResults(const Arguments & args) {
  DEBUG_MARKER(UDEB_DETAIL);
  REQUIRE_ARGS_LENGTH(3);
  typedef NativeMethodCall_2_<int, ScanOperation, char *, bool> MCALL;
  MCALL * ncallptr = new MCALL(& ScanOperation::fetchResults, args);
  ncallptr->errorHandler = getNdbErrorIfLessThanZero;
  ncallptr->runAsync();
  args.GetReturnValue().SetUndefined();
}

void ScanOp_readBlobResults(const Arguments & args) {
  ScanOperation * op = unwrapPointer<ScanOperation *>(args.Holder());
  op->readBlobResults(args);
}

#define WRAP_CONSTANT(TARGET, X) DEFINE_JS_INT(TARGET, #X, NdbScanOperation::X)

void ScanHelper_initOnLoad(Handle<Object> target) {
  Local<Object> scanObj = Object::New(Isolate::GetCurrent());
  Local<String> scanKey = NEW_SYMBOL("Scan");
  target->Set(scanKey, scanObj);

  DEFINE_JS_FUNCTION(scanObj, "create", newScanOperation);

  Local<Object> ScanHelper = Object::New(Isolate::GetCurrent());
  Local<Object> ScanFlags =  Object::New(Isolate::GetCurrent());

  scanObj->Set(NEW_SYMBOL("helper"), ScanHelper);
  scanObj->Set(NEW_SYMBOL("flags"), ScanFlags);

  WRAP_CONSTANT(ScanFlags, SF_TupScan);
  WRAP_CONSTANT(ScanFlags, SF_DiskScan);
  WRAP_CONSTANT(ScanFlags, SF_OrderBy);
  WRAP_CONSTANT(ScanFlags, SF_OrderByFull);
  WRAP_CONSTANT(ScanFlags, SF_Descending);
  WRAP_CONSTANT(ScanFlags, SF_ReadRangeNo);
  WRAP_CONSTANT(ScanFlags, SF_MultiRange);
  WRAP_CONSTANT(ScanFlags, SF_KeyInfo);

  DEFINE_JS_INT(ScanHelper, "table_record", SCAN_TABLE_RECORD);
  DEFINE_JS_INT(ScanHelper, "index_record", SCAN_INDEX_RECORD);
  DEFINE_JS_INT(ScanHelper, "lock_mode", SCAN_LOCK_MODE);
  DEFINE_JS_INT(ScanHelper, "bounds", SCAN_BOUNDS);
  DEFINE_JS_INT(ScanHelper, "flags", SCAN_OPTION_FLAGS);
  DEFINE_JS_INT(ScanHelper, "batch_size", SCAN_OPTION_BATCH_SIZE);
  DEFINE_JS_INT(ScanHelper, "parallel", SCAN_OPTION_PARALLELISM);
  DEFINE_JS_INT(ScanHelper, "filter_code", SCAN_FILTER_CODE);
}


= new MCALL(& ScanOperation::prepareAndExecute, args);

/*

*/

#include <NdbApi.hpp>

#include "adapter_global.h"
#include "js_wrapper_macros.h"
#include "Record.h"
#include "NativeMethodCall.h"
#include "NdbWrapperErrors.h"
#include "ScanOperation.h"

using namespace v8;

V8WrapperFn newScanOperation;
V8WrapperFn prepareAndExecute;
V8WrapperFn getOperationError;
V8WrapperFn scanNextResult;
V8WrapperFn scanFetchResults;
V8WrapperFn ScanOperation_close;
V8WrapperFn getNdbError;
V8WrapperFn ScanOp_readBlobResults;

class ScanOperationEnvelopeClass : public Envelope {
public:
  ScanOperationEnvelopeClass() : Envelope("ScanOperation") {
    addMethod("getNdbError", getNdbError<ScanOperation>);
    addMethod("prepareAndExecute", prepareAndExecute);
    addMethod("fetchResults", scanFetchResults);
    addMethod("nextResult", scanNextResult);
    addMethod("close", ScanOperation_close);
    addMethod("readBlobResults", ScanOp_readBlobResults);
  }
};

ScanOperationEnvelopeClass ScanOperationEnvelope;

Envelope * getScanOperationEnvelope() {
  return & ScanOperationEnvelope;
}

// Constructor wrapper
void newScanOperation(const Arguments &args) {
  EscapableHandleScope scope(args.GetIsolate());
  ScanOperation * s = new ScanOperation(args);
  Local<Value> wrapper = ScanOperationEnvelope.wrap(s);
  // freeFromGC: Disabled as it leads to segfaults during garbage collection
  args.GetReturnValue().Set(scope.Escape(wrapper));
}

// ASYNC
void prepareAndExecute(const Arguments &args) {
  EscapableHandleScope scope(args.GetIsolate());
  DEBUG_MARKER(UDEB_DEBUG);
  REQUIRE_ARGS_LENGTH(1);
  typedef NativeMethodCall_0_<int, ScanOperation> MCALL;
  MCALL * mcallptr

  mcallptr->errorHandler = getNdbErrorIfLessThanZero;
  mcallptr->runAsync();

  args.GetReturnValue().SetUndefined();
}

// ASYNC
void ScanOperation_close(const Arguments & args) {
  typedef NativeVoidMethodCall_0_<ScanOperation> NCALL;
  NCALL * ncallptr = new NCALL(& ScanOperation::close, args);
  ncallptr->runAsync();
  args.GetReturnValue().SetUndefined();
}

// IMMEDIATE
void scanNextResult(const Arguments & args) {
  DEBUG_MARKER(UDEB_DETAIL);
  EscapableHandleScope scope(args.GetIsolate());
  typedef NativeMethodCall_1_<int, ScanOperation, char *> MCALL;
  MCALL mcall(& ScanOperation::nextResult, args);
  mcall.run();
  args.GetReturnValue().Set(scope.Escape(mcall.jsReturnVal()));
}

// ASYNC; CALLBACK GETS (Null-Or-Error, Int)
void scanFetchResults(const Arguments & args) {
  DEBUG_MARKER(UDEB_DETAIL);
  REQUIRE_ARGS_LENGTH(3);
  typedef NativeMethodCall_2_<int, ScanOperation, char *, bool> MCALL;
  MCALL * ncallptr = new MCALL(& ScanOperation::fetchResults, args);
  ncallptr->errorHandler = getNdbErrorIfLessThanZero;
  ncallptr->runAsync();
  args.GetReturnValue().SetUndefined();
}

void ScanOp_readBlobResults(const Arguments & args) {
  ScanOperation * op = unwrapPointer<ScanOperation *>(args.Holder());
  op->readBlobResults(args);
}

#define WRAP_CONSTANT(TARGET, X) DEFINE_JS_INT(TARGET, #X, NdbScanOperation::X)

void ScanHelper_initOnLoad(Handle<Object> target) {
  Local<Object> scanObj = Object::New(Isolate::GetCurrent());
  Local<String> scanKey = NEW_SYMBOL("Scan");
  target->Set(scanKey, scanObj);

  DEFINE_JS_FUNCTION(scanObj, "create", newScanOperation);

  Local<Object> ScanHelper = Object::New(Isolate::GetCurrent());
  Local<Object> ScanFlags =  Object::New(Isolate::GetCurrent());

  scanObj->Set(NEW_SYMBOL("helper"), ScanHelper);
  scanObj->Set(NEW_SYMBOL("flags"), ScanFlags);

  WRAP_CONSTANT(ScanFlags, SF_TupScan);
  WRAP_CONSTANT(ScanFlags, SF_DiskScan);
  WRAP_CONSTANT(ScanFlags, SF_OrderBy);
  WRAP_CONSTANT(ScanFlags, SF_OrderByFull);
  WRAP_CONSTANT(ScanFlags, SF_Descending);
  WRAP_CONSTANT(ScanFlags, SF_ReadRangeNo);
  WRAP_CONSTANT(ScanFlags, SF_MultiRange);
  WRAP_CONSTANT(ScanFlags, SF_KeyInfo);

  DEFINE_JS_INT(ScanHelper, "table_record", SCAN_TABLE_RECORD);
  DEFINE_JS_INT(ScanHelper, "index_record", SCAN_INDEX_RECORD);
  DEFINE_JS_INT(ScanHelper, "lock_mode", SCAN_LOCK_MODE);
  DEFINE_JS_INT(ScanHelper, "bounds", SCAN_BOUNDS);
  DEFINE_JS_INT(ScanHelper, "flags", SCAN_OPTION_FLAGS);
  DEFINE_JS_INT(ScanHelper, "batch_size", SCAN_OPTION_BATCH_SIZE);
  DEFINE_JS_INT(ScanHelper, "parallel", SCAN_OPTION_PARALLELISM);
  DEFINE_JS_INT(ScanHelper, "filter_code", SCAN_FILTER_CODE);
}


void prepareAndExecute(const Arguments &args) {
  EscapableHandleScope scope(args.GetIsolate());
  DEBUG_MARKER(UDEB_DEBUG);
  REQUIRE_ARGS_LENGTH(1);
  typedef NativeMethodCall_0_<int, ScanOperation> MCALL;
  MCALL * mcallptr = new MCALL(& ScanOperation::prepareAndExecute, args);
  mcallptr->errorHandler = getNdbErrorIfLessThanZero;
  mcallptr->runAsync();

  args.GetReturnValue().SetUndefined();
}

// ASYNC
void ScanOperation_close(const Arguments & args) {
  typedef NativeVoidMethodCall_0_<ScanOperation> NCALL;
  NCALL * ncallptr = new NCALL(& ScanOperation::close, args);
  ncallptr->runAsync();
  args.GetReturnValue().SetUndefined();
}

// IMMEDIATE
void scanNextResult(const Arguments & args) {
  DEBUG_MARKER(UDEB_DETAIL);
  EscapableHandleScope scope(args.GetIsolate());
  typedef NativeMethodCall_1_<int, ScanOperation, char *> MCALL;
  MCALL mcall(& ScanOperation::nextResult, args);
  mcall.run();
  args.GetReturnValue().Set(scope.Escape(mcall.jsReturnVal()));
}

// ASYNC; CALLBACK GETS (Null-Or-Error, Int)
void scanFetchResults(const Arguments & args) {
  DEBUG_MARKER(UDEB_DETAIL);
  REQUIRE_ARGS_LENGTH(3);
  typedef NativeMethodCall_2_<int, ScanOperation, char *, bool> MCALL;
  MCALL * ncallptr = new MCALL(& ScanOperation::fetchResults, args);
  ncallptr->errorHandler = getNdbErrorIfLessThanZero;
  ncallptr->runAsync();
  args.GetReturnValue().SetUndefined();
}

void ScanOp_readBlobResults(const Arguments & args) {
  ScanOperation * op = unwrapPointer<ScanOperation *>(args.Holder());
  op->readBlobResults(args);
}

#define WRAP_CONSTANT(TARGET, X) DEFINE_JS_INT(TARGET, #X, NdbScanOperation::X)

void ScanHelper_initOnLoad(Handle<Object> target) {
  Local<Object> scanObj = Object::New(Isolate::GetCurrent());
  Local<String> scanKey = NEW_SYMBOL("Scan");
  target->Set(scanKey, scanObj);

  DEFINE_JS_FUNCTION(scanObj, "create", newScanOperation);

  Local<Object> ScanHelper = Object::New(Isolate::GetCurrent());
  Local<Object> ScanFlags =  Object::New(Isolate::GetCurrent());

  scanObj->Set(NEW_SYMBOL("helper"), ScanHelper);
  scanObj->Set(NEW_SYMBOL("flags"), ScanFlags);

  WRAP_CONSTANT(ScanFlags, SF_TupScan);
  WRAP_CONSTANT(ScanFlags, SF_DiskScan);
  WRAP_CONSTANT(ScanFlags, SF_OrderBy);
  WRAP_CONSTANT(ScanFlags, SF_OrderByFull);
  WRAP_CONSTANT(ScanFlags, SF_Descending);
  WRAP_CONSTANT(ScanFlags, SF_ReadRangeNo);
  WRAP_CONSTANT(ScanFlags, SF_MultiRange);
  WRAP_CONSTANT(ScanFlags, SF_KeyInfo);

  DEFINE_JS_INT(ScanHelper, "table_record", SCAN_TABLE_RECORD);
  DEFINE_JS_INT(ScanHelper, "index_record", SCAN_INDEX_RECORD);
  DEFINE_JS_INT(ScanHelper, "lock_mode", SCAN_LOCK_MODE);
  DEFINE_JS_INT(ScanHelper, "bounds", SCAN_BOUNDS);
  DEFINE_JS_INT(ScanHelper, "flags", SCAN_OPTION_FLAGS);
  DEFINE_JS_INT(ScanHelper, "batch_size", SCAN_OPTION_BATCH_SIZE);
  DEFINE_JS_INT(ScanHelper, "parallel", SCAN_OPTION_PARALLELISM);
  DEFINE_JS_INT(ScanHelper, "filter_code", SCAN_FILTER_CODE);
}


std::string base64_decode(std::string const& encoded_string)

// This file is part of libigl, a simple c++ geometry processing library.

#include "serialize_xml.h"
#include "../STR.h"
#include "../serialize.h"
#include "XMLSerializable.h"

#include <iterator>
#include <limits>
#include <iomanip>

namespace igl
{
  namespace xml
  {
    template <typename T>
    IGL_INLINE void serialize_xml(
      const T& obj,
      const std::string& filename)
    {
      serialize_xml(obj,"object",filename,false,true);
    }

    template <typename T>
    IGL_INLINE void serialize_xml(
      const T& obj,
      const std::string& objectName,
      const std::string& filename,
      bool binary,
      bool overwrite)
    {
      tinyxml2::XMLDocument* doc = new tinyxml2::XMLDocument();

      if(overwrite == false)
      {
        // Check if file exists
        tinyxml2::XMLError error = doc->LoadFile(filename.c_str());
        if(error != tinyxml2::XML_SUCCESS)
        {
          doc->Clear();
        }
      }

      tinyxml2::XMLElement* element = doc->FirstChildElement("serialization");
      if(element == NULL)
      {
        element = doc->NewElement("serialization");
        doc->InsertEndChild(element);
      }

      serialize_xml(obj,objectName,doc,element,binary);

      // Save
      tinyxml2::XMLError error = doc->SaveFile(filename.c_str());
      if(error != tinyxml2::XML_SUCCESS)
      {
        doc->PrintError();
      }

      delete doc;
    }

    template <typename T>
    IGL_INLINE void serialize_xml(
      const T& obj,
      const std::string& objectName,
      tinyxml2::XMLDocument* doc,
      tinyxml2::XMLElement* element,
      bool binary)
    {
      static_assert(
        serialization_xml::is_serializable<T>::value,
        "'igl::xml::serialize_xml': type is not serializable");

      std::string name(objectName);
      serialization_xml::encodeXMLElementName(name);

      tinyxml2::XMLElement* child = element->FirstChildElement(name.c_str());

      if(child != NULL)
        element->DeleteChild(child);

      child = doc->NewElement(name.c_str());
      element->InsertEndChild(child);

      if(binary)
      {
        std::vector<char> buffer;
        serialize(obj,name,buffer);
        std::string data =
          serialization_xml::base64_encode(
            reinterpret_cast<const unsigned char*>(
              buffer.data()),buffer.size());

        child->SetAttribute("binary",true);

        serialization_xml::serialize(data,doc,element,name);
      }
      else
      {
        serialization_xml::serialize(obj,doc,element,name);
      }
    }

    template <typename T>
    IGL_INLINE void deserialize_xml(T& obj,const std::string& filename)
    {
      deserialize_xml(obj,"object",filename);
    }

    template <typename T>
    IGL_INLINE void deserialize_xml(T& obj,const std::string& objectName,const std::string& filename)
    {
      tinyxml2::XMLDocument* doc = new tinyxml2::XMLDocument();

      tinyxml2::XMLError error = doc->LoadFile(filename.c_str());
      if(error != tinyxml2::XML_SUCCESS)
      {
        std::cerr << "File not found!" << std::endl;
        doc->PrintError();
        doc = NULL;
      }
      else
      {
        tinyxml2::XMLElement* element = doc->FirstChildElement("serialization");
        if(element == NULL)
        {
          std::cerr << "Name of object not found! Initialized with default value." << std::endl;
          obj = T();
        }
        else
        {
          deserialize_xml(obj,objectName,doc,element);
        }

        delete doc;
      }
    }

    template <typename T>
    IGL_INLINE void deserialize_xml(T& obj,const std::string& objectName,const tinyxml2::XMLDocument* doc,const tinyxml2::XMLElement* element)
    {
      static_assert(serialization::is_serializable<T>::value,"'igl::xml::deserialize_xml': type is not deserializable");

      std::string name(objectName);
      serialization_xml::encodeXMLElementName(name);

      const tinyxml2::XMLElement* child = element->FirstChildElement(name.c_str());
      if(child != NULL)
      {
        bool isBinary = false;
        const tinyxml2::XMLAttribute* attr = child->FindAttribute("binary");
        if(attr != NULL)
        {
          std::string code;
          serialization_xml::deserialize(code,doc,element,name);
          std::string decoded = serialization_xml::base64_decode(code);

          std::vector<char> buffer;
          std::copy(decoded.c_str(),decoded.c_str()+decoded.length(),std::back_inserter(buffer));

          deserialize(obj,name,buffer);
        }
        else
        {
          serialization_xml::deserialize(obj,doc,element,name);
        }
      }
    }

    namespace serialization_xml
    {
      // fundamental types

      template <typename T>
      IGL_INLINE typename std::enable_if<std::is_fundamental<T>::value>::type serialize(const T& obj,tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element,const std::string& name)
      {
        tinyxml2::XMLElement* child = getElement(doc,element,name.c_str());
        child->SetAttribute("val",obj);
      }

      template <typename T>
      IGL_INLINE typename std::enable_if<std::is_fundamental<T>::value>::type deserialize(T& obj,const tinyxml2::XMLDocument* doc,const tinyxml2::XMLElement* element,const std::string& name)
      {
        const tinyxml2::XMLElement* child = element->FirstChildElement(name.c_str());
        if(child != NULL)
        {
          getAttribute(child->Attribute("val"),obj);
        }
        else
        {
          obj = T();
        }
      }

      IGL_INLINE void serialize(const std::string& obj,tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element,const std::string& name)
      {
        tinyxml2::XMLElement* child = getElement(doc,element,name.c_str());
        child->SetAttribute("val",obj.c_str());
      }

      IGL_INLINE void deserialize(std::string& obj,const tinyxml2::XMLDocument* doc,const tinyxml2::XMLElement* element,const std::string& name)
      {
        const tinyxml2::XMLElement* child = element->FirstChildElement(name.c_str());
        if(child != NULL)
        {
          getAttribute(child->Attribute("val"),obj);
        }
        else
        {
          obj = std::string("");
        }
      }

      // Serializable

      template <typename T>
      IGL_INLINE typename std::enable_if<std::is_base_of<XMLSerializableBase,T>::value>::type serialize(const T& obj,tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element,const std::string& name)
      {
        // Serialize object implementing Serializable interface
        const XMLSerializableBase& object = dynamic_cast<const XMLSerializableBase&>(obj);

        tinyxml2::XMLElement* child = getElement(doc,element,name.c_str());
        object.Serialize(doc,child);
      }

      template <typename T>
      IGL_INLINE typename std::enable_if<std::is_base_of<XMLSerializableBase,T>::value>::type deserialize(T& obj,const tinyxml2::XMLDocument* doc,const tinyxml2::XMLElement* element,const std::string& name)
      {
        const tinyxml2::XMLElement* child = element->FirstChildElement(name.c_str());

        if(child != NULL)
        {
          obj.Deserialize(doc,child);
        }
        else
        {
          obj = T();
        }
      }

      // STL containers

      template <typename T1,typename T2>
      IGL_INLINE void serialize(const std::pair<T1,T2>& obj,tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element,const std::string& name)
      {
        tinyxml2::XMLElement* pair = getElement(doc,element,name.c_str());
        serialize(obj.first,doc,pair,"first");
        serialize(obj.second,doc,pair,"second");
      }

      template <typename T1,typename T2>
      IGL_INLINE void deserialize(std::pair<T1,T2>& obj,const tinyxml2::XMLDocument* doc,const tinyxml2::XMLElement* element,const std::string& name)
      {
        const tinyxml2::XMLElement* child = element->FirstChildElement(name.c_str());
        if(child != NULL)
        {
          deserialize(obj.first,doc,child,"first");
          deserialize(obj.second,doc,child,"second");
        }
        else
        {
          obj.first = T1();
          obj.second = T2();
        }
      }

      template <typename T1,typename T2>
      IGL_INLINE void serialize(const std::vector<T1,T2>& obj,tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element,const std::string& name)
      {
        tinyxml2::XMLElement* vector = getElement(doc,element,name.c_str());
        vector->SetAttribute("size",(unsigned int)obj.size());

        std::stringstream num;
        for(unsigned int i=0;i<obj.size();i++)
        {
          num.str("");
          num << "value" << i;
          serialize(obj[i],doc,vector,num.str());
        }
      }

      template <typename T1,typename T2>
      IGL_INLINE void deserialize(std::vector<T1,T2>& obj,const tinyxml2::XMLDocument* doc,const tinyxml2::XMLElement* element,const std::string& name)
      {
        obj.clear();

        const tinyxml2::XMLElement* child = element->FirstChildElement(name.c_str());
        if(child != NULL)
        {
          unsigned int size = child->UnsignedAttribute("size");
          obj.resize(size);

          std::stringstream num;
          for(unsigned int i=0;i<size;i++)
          {
            num.str("");
            num << "value" << i;

            deserialize(obj[i],doc,child,num.str());
          }
        }
        else
        {
          obj.clear();
        }
      }

      template <typename T>
      IGL_INLINE void serialize(const std::set<T>& obj,tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element,const std::string& name)
      {
        tinyxml2::XMLElement* set = getElement(doc,element,name.c_str());
        set->SetAttribute("size",(unsigned int)obj.size());

        std::stringstream num;
        typename std::set<T>::iterator iter = obj.begin();
        for(int i=0;iter!=obj.end();iter++,i++)
        {
          num.str("");
          num << "value" << i;
          serialize((T)*iter,doc,set,num.str());
        }
      }

      template <typename T>
      IGL_INLINE void deserialize(std::set<T>& obj,const tinyxml2::XMLDocument* doc,const tinyxml2::XMLElement* element,const std::string& name)
      {
        obj.clear();

        const tinyxml2::XMLElement* child = element->FirstChildElement(name.c_str());
        if(child != NULL)
        {
          unsigned int size = child->UnsignedAttribute("size");

          std::stringstream num;
          typename std::set<T>::iterator iter = obj.begin();
          for(int i=0;i<size;i++)
          {
            num.str("");
            num << "value" << i;

            T val;
            deserialize(val,doc,child,num.str());
            obj.insert(val);
          }
        }
        else
        {
          obj.clear();
        }
      }

      template <typename T1,typename T2>
      IGL_INLINE void serialize(const std::map<T1,T2>& obj,tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element,const std::string& name)
      {
        tinyxml2::XMLElement* map = getElement(doc,element,name.c_str());
        map->SetAttribute("size",(unsigned int)obj.size());

        std::stringstream num;
        typename std::map<T1,T2>::const_iterator iter = obj.cbegin();
        for(int i=0;iter!=obj.end();iter++,i++)
        {
          num.str("");
          num << "value" << i;
          serialize((std::pair<T1,T2>)*iter,doc,map,num.str());
        }
      }

      template <typename T1,typename T2>
      IGL_INLINE void deserialize(std::map<T1,T2>& obj,const tinyxml2::XMLDocument* doc,const tinyxml2::XMLElement* element,const std::string& name)
      {
        obj.clear();

        const tinyxml2::XMLElement* child = element->FirstChildElement(name.c_str());
        if(child != NULL)
        {
          unsigned int size = child->UnsignedAttribute("size");

          std::stringstream num;
          typename std::map<T1,T2>::iterator iter = obj.begin();
          for(int i=0;i<size;i++)
          {
            num.str("");
            num << "value" << i;

            std::pair<T1,T2> pair;
            deserialize(pair,doc,child,num.str());
            obj.insert(pair);
          }
        }
        else
        {
          obj.clear();
        }
      }

      template<typename T,int R,int C,int P,int MR,int MC>
      IGL_INLINE void serialize(
        const Eigen::Matrix<T,R,C,P,MR,MC>& obj,
        const std::string& name,
        const std::function<std::string(const T &) >& to_string,
        tinyxml2::XMLDocument* doc,
        tinyxml2::XMLElement* element)
      {
        tinyxml2::XMLElement* matrix = getElement(doc,element,name.c_str());

        const unsigned int rows = obj.rows();
        const unsigned int cols = obj.cols();

        matrix->SetAttribute("rows",rows);
        matrix->SetAttribute("cols",cols);

        std::stringstream ms;
        ms << "\n";
        for(unsigned int r=0;r<rows;r++)
        {
          for(unsigned int c=0;c<cols;c++)
          {
            ms << to_string(obj(r,c)) << ",";
          }
          ms << "\n";
        }

        std::string mString = ms.str();
        if(mString.size() > 1)
          mString[mString.size()-2] = '\0';

        matrix->SetAttribute("matrix",mString.c_str());
      }

      // Eigen types
      template<typename T,int R,int C,int P,int MR,int MC>
      IGL_INLINE void serialize(
        const Eigen::Matrix<T,R,C,P,MR,MC>& obj,
        tinyxml2::XMLDocument* doc,
        tinyxml2::XMLElement* element,
        const std::string& name)
      {
        const std::function<std::string(const T &) > to_string =
          [](const T & v)->std::string
          {
            return
              STR(std::setprecision(std::numeric_limits<double>::digits10+2)<<v);
          };
        serialize(obj,name,to_string,doc,element);
      }
      template<typename T,int R,int C,int P,int MR,int MC>
      IGL_INLINE void deserialize(
        const tinyxml2::XMLDocument* doc,
        const tinyxml2::XMLElement* element,
        const std::string& name,
        const std::function<void(const std::string &,T &)> & from_string,
        Eigen::Matrix<T,R,C,P,MR,MC>& obj)
      {
        const tinyxml2::XMLElement* child = element->FirstChildElement(name.c_str());
        bool initialized = false;
        if(child != NULL)
        {
          const unsigned int rows = child->UnsignedAttribute("rows");
          const unsigned int cols = child->UnsignedAttribute("cols");

          if(rows > 0 && cols > 0)
          {
            obj.resize(rows,cols);

            const tinyxml2::XMLAttribute* attribute = child->FindAttribute("matrix");
            if(attribute != NULL)
            {
              std::string matTemp;
              getAttribute(attribute->Value(),matTemp);

              std::string line,srows,scols;
              std::stringstream mats;
              mats << matTemp;

              int r=0;
              std::string val;
              // for each line
              getline(mats,line);
              while(getline(mats,line))
              {
                // get current line
                std::stringstream liness(line);

                for(unsigned int c=0;c<cols-1;c++)
                {
                  // split line
                  getline(liness,val,',');

                  // push pack the data if any
                  if(!val.empty())
                  {
                    from_string(val,obj.coeffRef(r,c));
                  }
                }

                getline(liness,val);
                from_string(val,obj.coeffRef(r,cols-1));

                r++;
              }
              initialized = true;
            }
          }
        }

        if(!initialized)
        {
          obj = Eigen::Matrix<T,R,C,P,MR,MC>();
        }
      }

      template<typename T,int R,int C,int P,int MR,int MC>
      IGL_INLINE void deserialize(
        Eigen::Matrix<T,R,C,P,MR,MC>& obj,
        const tinyxml2::XMLDocument* doc,
        const tinyxml2::XMLElement* element,
        const std::string& name)
      {
        const std::function<void(const std::string &,T &)> & from_string =
          [](const std::string & s,T & v)
          {
            getAttribute(s.c_str(),v);
          };
        deserialize(doc,element,name,from_string,obj);
      }

      template<typename T,int P,typename I>
      IGL_INLINE void serialize(const Eigen::SparseMatrix<T,P,I>& obj,tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element,const std::string& name)
      {
        tinyxml2::XMLElement* matrix = getElement(doc,element,name.c_str());

        const unsigned int rows = obj.rows();
        const unsigned int cols = obj.cols();

        matrix->SetAttribute("rows",rows);
        matrix->SetAttribute("cols",cols);

        char buffer[200];
        std::stringstream ms;
        ms << "\n";
        for(int k=0;k<obj.outerSize();++k)
        {
          for(typename Eigen::SparseMatrix<T,P,I>::InnerIterator it(obj,k);it;++it)
          {
            tinyxml2::XMLUtil::ToStr(it.value(),buffer,200);
            ms << it.row() << "," << it.col() << "," << buffer << "\n";
          }
        }

        std::string mString = ms.str();
        if(mString.size() > 0)
          mString[mString.size()-1] = '\0';

        matrix->SetAttribute("matrix",mString.c_str());
      }

      template<typename T,int P,typename I>
      IGL_INLINE void deserialize(Eigen::SparseMatrix<T,P,I>& obj,const tinyxml2::XMLDocument* doc,const tinyxml2::XMLElement* element,const std::string& name)
      {
        const tinyxml2::XMLElement* child = element->FirstChildElement(name.c_str());
        bool initialized = false;
        if(child != NULL)
        {
          const unsigned int rows = child->UnsignedAttribute("rows");
          const unsigned int cols = child->UnsignedAttribute("cols");

          if(rows > 0 && cols > 0)
          {
            obj.resize(rows,cols);
            obj.setZero();

            const tinyxml2::XMLAttribute* attribute = child->FindAttribute("matrix");
            if(attribute != NULL)
            {
              std::string matTemp;
              getAttribute(attribute->Value(),matTemp);

              std::string line,srows,scols;
              std::stringstream mats;
              mats << matTemp;

              std::vector<Eigen::Triplet<T,I> > triplets;
              int r=0;
              std::string val;

              // for each line
              getline(mats,line);
              while(getline(mats,line))
              {
                // get current line
                std::stringstream liness(line);

                // row
                getline(liness,val,',');
                int row = atoi(val.c_str());
                // col
                getline(liness,val,',');
                int col = atoi(val.c_str());
                // val
                getline(liness,val);
                T value;
                getAttribute(val.c_str(),value);

                triplets.push_back(Eigen::Triplet<T,I>(row,col,value));

                r++;
              }

              obj.setFromTriplets(triplets.begin(),triplets.end());
              initialized = true;
            }
          }
        }

        if(!initialized)
        {
          obj = Eigen::SparseMatrix<T,P,I>();
        }
      }

      // pointers

      template <typename T>
      IGL_INLINE typename std::enable_if<std::is_pointer<T>::value>::type serialize(const T& obj,tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element,const std::string& name)
      {
        tinyxml2::XMLElement* pointer = getElement(doc,element,name.c_str());

        bool isNullPtr = (obj == NULL);

        pointer->SetAttribute("isNullPtr",isNullPtr);

        if(isNullPtr == false)
          serialization_xml::serialize(*obj,doc,element,name);
      }

      template <typename T>
      IGL_INLINE typename std::enable_if<std::is_pointer<T>::value>::type deserialize(T& obj,const tinyxml2::XMLDocument* doc,const tinyxml2::XMLElement* element,const std::string& name)
      {
        const tinyxml2::XMLElement* child = element->FirstChildElement(name.c_str());
        if(child != NULL)
        {
          bool isNullPtr = child->BoolAttribute("isNullPtr");

          if(isNullPtr)
          {
            if(obj != NULL)
            {
              std::cout << "deserialization: possible memory leak for '" << typeid(obj).name() << "'" << std::endl;
              obj = NULL;
            }
          }
          else
          {
            if(obj != NULL)
              std::cout << "deserialization: possible memory leak for '" << typeid(obj).name() << "'" << std::endl;

            obj = new typename std::remove_pointer<T>::type();

            serialization_xml::deserialize(*obj,doc,element,name);
          }
        }
      }

      // helper functions

      IGL_INLINE tinyxml2::XMLElement* getElement(tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element,const std::string& name)
      {
        tinyxml2::XMLElement* child = element->FirstChildElement(name.c_str());
        if(child == NULL)
        {
          child = doc->NewElement(name.c_str());
          element->InsertEndChild(child);
        }
        return child;
      }

      IGL_INLINE void getAttribute(const char* src,bool& dest)
      {
        tinyxml2::XMLUtil::ToBool(src,&dest);
      }

      IGL_INLINE void getAttribute(const char* src,char& dest)
      {
        dest = (char)atoi(src);
      }

      IGL_INLINE void getAttribute(const char* src,std::string& dest)
      {
        dest = src;
      }

      IGL_INLINE void getAttribute(const char* src,float& dest)
      {
        tinyxml2::XMLUtil::ToFloat(src,&dest);
      }

      IGL_INLINE void getAttribute(const char* src,double& dest)
      {
        tinyxml2::XMLUtil::ToDouble(src,&dest);
      }

      template<typename T>
      IGL_INLINE typename std::enable_if<std::is_integral<T>::value && std::is_unsigned<T>::value>::type getAttribute(const char* src,T& dest)
      {
        unsigned int val;
        tinyxml2::XMLUtil::ToUnsigned(src,&val);
        dest = (T)val;
      }

      template<typename T>
      IGL_INLINE typename std::enable_if<std::is_integral<T>::value && !std::is_unsigned<T>::value>::type getAttribute(const char* src,T& dest)
      {
        int val;
        tinyxml2::XMLUtil::ToInt(src,&val);
        dest = (T)val;
      }

      // tinyXML2 related stuff
      static const int numForbiddenChars = 8;
      static const char forbiddenChars[] ={' ','/','~','#','&','>','<','='};

      IGL_INLINE void replaceSubString(std::string& str,const std::string& search,const std::string& replace)
      {
        size_t pos = 0;
        while((pos = str.find(search,pos)) != std::string::npos)
        {
          str.replace(pos,search.length(),replace);
          pos += replace.length();
        }
      }

      IGL_INLINE void encodeXMLElementName(std::string& name)
      {
        // must not start with a digit
        if(isdigit(*name.begin()))
        {
          name = ":::" + name;
        }

        std::stringstream stream;
        for(int i=0;i<numForbiddenChars;i++)
        {
          std::string search;
          search = forbiddenChars[i];
          std::stringstream replaces;
          replaces << ":" << (int)forbiddenChars[i];
          std::string replace = replaces.str();

          replaceSubString(name,search,replace);
        }
      }

      IGL_INLINE void decodeXMLElementName(std::string& name)
      {
        if(name.find("::",0) == 0)
          name.replace(0,3,"");

        for(auto chr : forbiddenChars)
        {
          std::stringstream searchs;
          searchs << ":" << (int)chr;
          std::string search = searchs.str();
          std::string replace;
          replace = chr;

          replaceSubString(name,search,replace);
        }
      }

     /*

        Ren� Nyffenegger example@nnthink.com
        */

      static const std::string base64_chars =
        "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
        "abcdefghijklmnopqrstuvwxyz"
        "0123456789+/";

      static inline bool is_base64(unsigned char c) {
        return (isalnum(c) || (c == '+') || (c == '/'));
      }

      std::string base64_encode(unsigned char const* bytes_to_encode,unsigned int in_len)
      {
        std::string ret;
        int i = 0;
        int j = 0;
        unsigned char char_array_3[3];
        unsigned char char_array_4[4];

        while(in_len--) {
          char_array_3[i++] = *(bytes_to_encode++);
          if(i == 3) {
            char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
            char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
            char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
            char_array_4[3] = char_array_3[2] & 0x3f;

            for(i = 0; (i <4) ; i++)
              ret += base64_chars[char_array_4[i]];

            i = 0;
          }
        }

        if(i)
        {
          for(j = i; j < 3; j++)
            char_array_3[j] = '\0';

          char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
          char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
          char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
          char_array_4[3] = char_array_3[2] & 0x3f;

          for(j = 0; (j < i + 1); j++)
            ret += base64_chars[char_array_4[j]];

          while((i++ < 3))
            ret += '=';
        }

        return ret;
      }

{
        int in_len = encoded_string.size();
        int i = 0;
        int j = 0;
        int in_ = 0;
        unsigned char char_array_4[4],char_array_3[3];
        std::string ret;

        // construct fast lookup table
        // added by Christian Sch�ller (example@nnthink.com)
        int charLookup[200];
        for(int i=0;i<(int)(base64_chars.length());i++)
          charLookup[(int)base64_chars[i]] = i;

        while(in_len-- && (encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
          char_array_4[i++] = encoded_string[in_]; in_++;
          if(i ==4) {
            for(i = 0; i <4; i++)
              char_array_4[i] = charLookup[char_array_4[i]]; // new fast lookup

            char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
            char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
            char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];

            for(i = 0; (i < 3); i++)
              ret += char_array_3[i];

            i = 0;
          }
        }

        if(i) {
          for(j = i; j <4; j++)
            char_array_4[j] = 0;

          for(j = 0; j <4; j++)
            char_array_4[j] = base64_chars.find(char_array_4[j]);

          char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
          char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
          char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];

          for(j = 0; (j < i - 1);
            j++) ret += char_array_3[j];
        }

        return ret;
      }
    }
  }
}

#ifdef IGL_STATIC_LIBRARY
// Explicit template instantiation
template void igl::xml::serialize_xml<std::vector<float, std::allocator<float> > >(std::vector<float, std::allocator<float> > const&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, bool, bool);
template void igl::xml::deserialize_xml<std::vector<float, std::allocator<float> > >(std::vector<float, std::allocator<float> >&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&);
#endif


{
        unsigned int val;
        tinyxml2::XMLUtil::ToUnsigned(src,&val);
        dest = (T)val;
      }

// This file is part of libigl, a simple c++ geometry processing library.

#include "serialize_xml.h"
#include "../STR.h"
#include "../serialize.h"
#include "XMLSerializable.h"

#include <iterator>
#include <limits>
#include <iomanip>

namespace igl
{
  namespace xml
  {
    template <typename T>
    IGL_INLINE void serialize_xml(
      const T& obj,
      const std::string& filename)
    {
      serialize_xml(obj,"object",filename,false,true);
    }

    template <typename T>
    IGL_INLINE void serialize_xml(
      const T& obj,
      const std::string& objectName,
      const std::string& filename,
      bool binary,
      bool overwrite)
    {
      tinyxml2::XMLDocument* doc = new tinyxml2::XMLDocument();

      if(overwrite == false)
      {
        // Check if file exists
        tinyxml2::XMLError error = doc->LoadFile(filename.c_str());
        if(error != tinyxml2::XML_SUCCESS)
        {
          doc->Clear();
        }
      }

      tinyxml2::XMLElement* element = doc->FirstChildElement("serialization");
      if(element == NULL)
      {
        element = doc->NewElement("serialization");
        doc->InsertEndChild(element);
      }

      serialize_xml(obj,objectName,doc,element,binary);

      // Save
      tinyxml2::XMLError error = doc->SaveFile(filename.c_str());
      if(error != tinyxml2::XML_SUCCESS)
      {
        doc->PrintError();
      }

      delete doc;
    }

    template <typename T>
    IGL_INLINE void serialize_xml(
      const T& obj,
      const std::string& objectName,
      tinyxml2::XMLDocument* doc,
      tinyxml2::XMLElement* element,
      bool binary)
    {
      static_assert(
        serialization_xml::is_serializable<T>::value,
        "'igl::xml::serialize_xml': type is not serializable");

      std::string name(objectName);
      serialization_xml::encodeXMLElementName(name);

      tinyxml2::XMLElement* child = element->FirstChildElement(name.c_str());

      if(child != NULL)
        element->DeleteChild(child);

      child = doc->NewElement(name.c_str());
      element->InsertEndChild(child);

      if(binary)
      {
        std::vector<char> buffer;
        serialize(obj,name,buffer);
        std::string data =
          serialization_xml::base64_encode(
            reinterpret_cast<const unsigned char*>(
              buffer.data()),buffer.size());

        child->SetAttribute("binary",true);

        serialization_xml::serialize(data,doc,element,name);
      }
      else
      {
        serialization_xml::serialize(obj,doc,element,name);
      }
    }

    template <typename T>
    IGL_INLINE void deserialize_xml(T& obj,const std::string& filename)
    {
      deserialize_xml(obj,"object",filename);
    }

    template <typename T>
    IGL_INLINE void deserialize_xml(T& obj,const std::string& objectName,const std::string& filename)
    {
      tinyxml2::XMLDocument* doc = new tinyxml2::XMLDocument();

      tinyxml2::XMLError error = doc->LoadFile(filename.c_str());
      if(error != tinyxml2::XML_SUCCESS)
      {
        std::cerr << "File not found!" << std::endl;
        doc->PrintError();
        doc = NULL;
      }
      else
      {
        tinyxml2::XMLElement* element = doc->FirstChildElement("serialization");
        if(element == NULL)
        {
          std::cerr << "Name of object not found! Initialized with default value." << std::endl;
          obj = T();
        }
        else
        {
          deserialize_xml(obj,objectName,doc,element);
        }

        delete doc;
      }
    }

    template <typename T>
    IGL_INLINE void deserialize_xml(T& obj,const std::string& objectName,const tinyxml2::XMLDocument* doc,const tinyxml2::XMLElement* element)
    {
      static_assert(serialization::is_serializable<T>::value,"'igl::xml::deserialize_xml': type is not deserializable");

      std::string name(objectName);
      serialization_xml::encodeXMLElementName(name);

      const tinyxml2::XMLElement* child = element->FirstChildElement(name.c_str());
      if(child != NULL)
      {
        bool isBinary = false;
        const tinyxml2::XMLAttribute* attr = child->FindAttribute("binary");
        if(attr != NULL)
        {
          std::string code;
          serialization_xml::deserialize(code,doc,element,name);
          std::string decoded = serialization_xml::base64_decode(code);

          std::vector<char> buffer;
          std::copy(decoded.c_str(),decoded.c_str()+decoded.length(),std::back_inserter(buffer));

          deserialize(obj,name,buffer);
        }
        else
        {
          serialization_xml::deserialize(obj,doc,element,name);
        }
      }
    }

    namespace serialization_xml
    {
      // fundamental types

      template <typename T>
      IGL_INLINE typename std::enable_if<std::is_fundamental<T>::value>::type serialize(const T& obj,tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element,const std::string& name)
      {
        tinyxml2::XMLElement* child = getElement(doc,element,name.c_str());
        child->SetAttribute("val",obj);
      }

      template <typename T>
      IGL_INLINE typename std::enable_if<std::is_fundamental<T>::value>::type deserialize(T& obj,const tinyxml2::XMLDocument* doc,const tinyxml2::XMLElement* element,const std::string& name)
      {
        const tinyxml2::XMLElement* child = element->FirstChildElement(name.c_str());
        if(child != NULL)
        {
          getAttribute(child->Attribute("val"),obj);
        }
        else
        {
          obj = T();
        }
      }

      IGL_INLINE void serialize(const std::string& obj,tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element,const std::string& name)
      {
        tinyxml2::XMLElement* child = getElement(doc,element,name.c_str());
        child->SetAttribute("val",obj.c_str());
      }

      IGL_INLINE void deserialize(std::string& obj,const tinyxml2::XMLDocument* doc,const tinyxml2::XMLElement* element,const std::string& name)
      {
        const tinyxml2::XMLElement* child = element->FirstChildElement(name.c_str());
        if(child != NULL)
        {
          getAttribute(child->Attribute("val"),obj);
        }
        else
        {
          obj = std::string("");
        }
      }

      // Serializable

      template <typename T>
      IGL_INLINE typename std::enable_if<std::is_base_of<XMLSerializableBase,T>::value>::type serialize(const T& obj,tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element,const std::string& name)
      {
        // Serialize object implementing Serializable interface
        const XMLSerializableBase& object = dynamic_cast<const XMLSerializableBase&>(obj);

        tinyxml2::XMLElement* child = getElement(doc,element,name.c_str());
        object.Serialize(doc,child);
      }

      template <typename T>
      IGL_INLINE typename std::enable_if<std::is_base_of<XMLSerializableBase,T>::value>::type deserialize(T& obj,const tinyxml2::XMLDocument* doc,const tinyxml2::XMLElement* element,const std::string& name)
      {
        const tinyxml2::XMLElement* child = element->FirstChildElement(name.c_str());

        if(child != NULL)
        {
          obj.Deserialize(doc,child);
        }
        else
        {
          obj = T();
        }
      }

      // STL containers

      template <typename T1,typename T2>
      IGL_INLINE void serialize(const std::pair<T1,T2>& obj,tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element,const std::string& name)
      {
        tinyxml2::XMLElement* pair = getElement(doc,element,name.c_str());
        serialize(obj.first,doc,pair,"first");
        serialize(obj.second,doc,pair,"second");
      }

      template <typename T1,typename T2>
      IGL_INLINE void deserialize(std::pair<T1,T2>& obj,const tinyxml2::XMLDocument* doc,const tinyxml2::XMLElement* element,const std::string& name)
      {
        const tinyxml2::XMLElement* child = element->FirstChildElement(name.c_str());
        if(child != NULL)
        {
          deserialize(obj.first,doc,child,"first");
          deserialize(obj.second,doc,child,"second");
        }
        else
        {
          obj.first = T1();
          obj.second = T2();
        }
      }

      template <typename T1,typename T2>
      IGL_INLINE void serialize(const std::vector<T1,T2>& obj,tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element,const std::string& name)
      {
        tinyxml2::XMLElement* vector = getElement(doc,element,name.c_str());
        vector->SetAttribute("size",(unsigned int)obj.size());

        std::stringstream num;
        for(unsigned int i=0;i<obj.size();i++)
        {
          num.str("");
          num << "value" << i;
          serialize(obj[i],doc,vector,num.str());
        }
      }

      template <typename T1,typename T2>
      IGL_INLINE void deserialize(std::vector<T1,T2>& obj,const tinyxml2::XMLDocument* doc,const tinyxml2::XMLElement* element,const std::string& name)
      {
        obj.clear();

        const tinyxml2::XMLElement* child = element->FirstChildElement(name.c_str());
        if(child != NULL)
        {
          unsigned int size = child->UnsignedAttribute("size");
          obj.resize(size);

          std::stringstream num;
          for(unsigned int i=0;i<size;i++)
          {
            num.str("");
            num << "value" << i;

            deserialize(obj[i],doc,child,num.str());
          }
        }
        else
        {
          obj.clear();
        }
      }

      template <typename T>
      IGL_INLINE void serialize(const std::set<T>& obj,tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element,const std::string& name)
      {
        tinyxml2::XMLElement* set = getElement(doc,element,name.c_str());
        set->SetAttribute("size",(unsigned int)obj.size());

        std::stringstream num;
        typename std::set<T>::iterator iter = obj.begin();
        for(int i=0;iter!=obj.end();iter++,i++)
        {
          num.str("");
          num << "value" << i;
          serialize((T)*iter,doc,set,num.str());
        }
      }

      template <typename T>
      IGL_INLINE void deserialize(std::set<T>& obj,const tinyxml2::XMLDocument* doc,const tinyxml2::XMLElement* element,const std::string& name)
      {
        obj.clear();

        const tinyxml2::XMLElement* child = element->FirstChildElement(name.c_str());
        if(child != NULL)
        {
          unsigned int size = child->UnsignedAttribute("size");

          std::stringstream num;
          typename std::set<T>::iterator iter = obj.begin();
          for(int i=0;i<size;i++)
          {
            num.str("");
            num << "value" << i;

            T val;
            deserialize(val,doc,child,num.str());
            obj.insert(val);
          }
        }
        else
        {
          obj.clear();
        }
      }

      template <typename T1,typename T2>
      IGL_INLINE void serialize(const std::map<T1,T2>& obj,tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element,const std::string& name)
      {
        tinyxml2::XMLElement* map = getElement(doc,element,name.c_str());
        map->SetAttribute("size",(unsigned int)obj.size());

        std::stringstream num;
        typename std::map<T1,T2>::const_iterator iter = obj.cbegin();
        for(int i=0;iter!=obj.end();iter++,i++)
        {
          num.str("");
          num << "value" << i;
          serialize((std::pair<T1,T2>)*iter,doc,map,num.str());
        }
      }

      template <typename T1,typename T2>
      IGL_INLINE void deserialize(std::map<T1,T2>& obj,const tinyxml2::XMLDocument* doc,const tinyxml2::XMLElement* element,const std::string& name)
      {
        obj.clear();

        const tinyxml2::XMLElement* child = element->FirstChildElement(name.c_str());
        if(child != NULL)
        {
          unsigned int size = child->UnsignedAttribute("size");

          std::stringstream num;
          typename std::map<T1,T2>::iterator iter = obj.begin();
          for(int i=0;i<size;i++)
          {
            num.str("");
            num << "value" << i;

            std::pair<T1,T2> pair;
            deserialize(pair,doc,child,num.str());
            obj.insert(pair);
          }
        }
        else
        {
          obj.clear();
        }
      }

      template<typename T,int R,int C,int P,int MR,int MC>
      IGL_INLINE void serialize(
        const Eigen::Matrix<T,R,C,P,MR,MC>& obj,
        const std::string& name,
        const std::function<std::string(const T &) >& to_string,
        tinyxml2::XMLDocument* doc,
        tinyxml2::XMLElement* element)
      {
        tinyxml2::XMLElement* matrix = getElement(doc,element,name.c_str());

        const unsigned int rows = obj.rows();
        const unsigned int cols = obj.cols();

        matrix->SetAttribute("rows",rows);
        matrix->SetAttribute("cols",cols);

        std::stringstream ms;
        ms << "\n";
        for(unsigned int r=0;r<rows;r++)
        {
          for(unsigned int c=0;c<cols;c++)
          {
            ms << to_string(obj(r,c)) << ",";
          }
          ms << "\n";
        }

        std::string mString = ms.str();
        if(mString.size() > 1)
          mString[mString.size()-2] = '\0';

        matrix->SetAttribute("matrix",mString.c_str());
      }

      // Eigen types
      template<typename T,int R,int C,int P,int MR,int MC>
      IGL_INLINE void serialize(
        const Eigen::Matrix<T,R,C,P,MR,MC>& obj,
        tinyxml2::XMLDocument* doc,
        tinyxml2::XMLElement* element,
        const std::string& name)
      {
        const std::function<std::string(const T &) > to_string =
          [](const T & v)->std::string
          {
            return
              STR(std::setprecision(std::numeric_limits<double>::digits10+2)<<v);
          };
        serialize(obj,name,to_string,doc,element);
      }
      template<typename T,int R,int C,int P,int MR,int MC>
      IGL_INLINE void deserialize(
        const tinyxml2::XMLDocument* doc,
        const tinyxml2::XMLElement* element,
        const std::string& name,
        const std::function<void(const std::string &,T &)> & from_string,
        Eigen::Matrix<T,R,C,P,MR,MC>& obj)
      {
        const tinyxml2::XMLElement* child = element->FirstChildElement(name.c_str());
        bool initialized = false;
        if(child != NULL)
        {
          const unsigned int rows = child->UnsignedAttribute("rows");
          const unsigned int cols = child->UnsignedAttribute("cols");

          if(rows > 0 && cols > 0)
          {
            obj.resize(rows,cols);

            const tinyxml2::XMLAttribute* attribute = child->FindAttribute("matrix");
            if(attribute != NULL)
            {
              std::string matTemp;
              getAttribute(attribute->Value(),matTemp);

              std::string line,srows,scols;
              std::stringstream mats;
              mats << matTemp;

              int r=0;
              std::string val;
              // for each line
              getline(mats,line);
              while(getline(mats,line))
              {
                // get current line
                std::stringstream liness(line);

                for(unsigned int c=0;c<cols-1;c++)
                {
                  // split line
                  getline(liness,val,',');

                  // push pack the data if any
                  if(!val.empty())
                  {
                    from_string(val,obj.coeffRef(r,c));
                  }
                }

                getline(liness,val);
                from_string(val,obj.coeffRef(r,cols-1));

                r++;
              }
              initialized = true;
            }
          }
        }

        if(!initialized)
        {
          obj = Eigen::Matrix<T,R,C,P,MR,MC>();
        }
      }

      template<typename T,int R,int C,int P,int MR,int MC>
      IGL_INLINE void deserialize(
        Eigen::Matrix<T,R,C,P,MR,MC>& obj,
        const tinyxml2::XMLDocument* doc,
        const tinyxml2::XMLElement* element,
        const std::string& name)
      {
        const std::function<void(const std::string &,T &)> & from_string =
          [](const std::string & s,T & v)
          {
            getAttribute(s.c_str(),v);
          };
        deserialize(doc,element,name,from_string,obj);
      }

      template<typename T,int P,typename I>
      IGL_INLINE void serialize(const Eigen::SparseMatrix<T,P,I>& obj,tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element,const std::string& name)
      {
        tinyxml2::XMLElement* matrix = getElement(doc,element,name.c_str());

        const unsigned int rows = obj.rows();
        const unsigned int cols = obj.cols();

        matrix->SetAttribute("rows",rows);
        matrix->SetAttribute("cols",cols);

        char buffer[200];
        std::stringstream ms;
        ms << "\n";
        for(int k=0;k<obj.outerSize();++k)
        {
          for(typename Eigen::SparseMatrix<T,P,I>::InnerIterator it(obj,k);it;++it)
          {
            tinyxml2::XMLUtil::ToStr(it.value(),buffer,200);
            ms << it.row() << "," << it.col() << "," << buffer << "\n";
          }
        }

        std::string mString = ms.str();
        if(mString.size() > 0)
          mString[mString.size()-1] = '\0';

        matrix->SetAttribute("matrix",mString.c_str());
      }

      template<typename T,int P,typename I>
      IGL_INLINE void deserialize(Eigen::SparseMatrix<T,P,I>& obj,const tinyxml2::XMLDocument* doc,const tinyxml2::XMLElement* element,const std::string& name)
      {
        const tinyxml2::XMLElement* child = element->FirstChildElement(name.c_str());
        bool initialized = false;
        if(child != NULL)
        {
          const unsigned int rows = child->UnsignedAttribute("rows");
          const unsigned int cols = child->UnsignedAttribute("cols");

          if(rows > 0 && cols > 0)
          {
            obj.resize(rows,cols);
            obj.setZero();

            const tinyxml2::XMLAttribute* attribute = child->FindAttribute("matrix");
            if(attribute != NULL)
            {
              std::string matTemp;
              getAttribute(attribute->Value(),matTemp);

              std::string line,srows,scols;
              std::stringstream mats;
              mats << matTemp;

              std::vector<Eigen::Triplet<T,I> > triplets;
              int r=0;
              std::string val;

              // for each line
              getline(mats,line);
              while(getline(mats,line))
              {
                // get current line
                std::stringstream liness(line);

                // row
                getline(liness,val,',');
                int row = atoi(val.c_str());
                // col
                getline(liness,val,',');
                int col = atoi(val.c_str());
                // val
                getline(liness,val);
                T value;
                getAttribute(val.c_str(),value);

                triplets.push_back(Eigen::Triplet<T,I>(row,col,value));

                r++;
              }

              obj.setFromTriplets(triplets.begin(),triplets.end());
              initialized = true;
            }
          }
        }

        if(!initialized)
        {
          obj = Eigen::SparseMatrix<T,P,I>();
        }
      }

      // pointers

      template <typename T>
      IGL_INLINE typename std::enable_if<std::is_pointer<T>::value>::type serialize(const T& obj,tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element,const std::string& name)
      {
        tinyxml2::XMLElement* pointer = getElement(doc,element,name.c_str());

        bool isNullPtr = (obj == NULL);

        pointer->SetAttribute("isNullPtr",isNullPtr);

        if(isNullPtr == false)
          serialization_xml::serialize(*obj,doc,element,name);
      }

      template <typename T>
      IGL_INLINE typename std::enable_if<std::is_pointer<T>::value>::type deserialize(T& obj,const tinyxml2::XMLDocument* doc,const tinyxml2::XMLElement* element,const std::string& name)
      {
        const tinyxml2::XMLElement* child = element->FirstChildElement(name.c_str());
        if(child != NULL)
        {
          bool isNullPtr = child->BoolAttribute("isNullPtr");

          if(isNullPtr)
          {
            if(obj != NULL)
            {
              std::cout << "deserialization: possible memory leak for '" << typeid(obj).name() << "'" << std::endl;
              obj = NULL;
            }
          }
          else
          {
            if(obj != NULL)
              std::cout << "deserialization: possible memory leak for '" << typeid(obj).name() << "'" << std::endl;

            obj = new typename std::remove_pointer<T>::type();

            serialization_xml::deserialize(*obj,doc,element,name);
          }
        }
      }

      // helper functions

      IGL_INLINE tinyxml2::XMLElement* getElement(tinyxml2::XMLDocument* doc,tinyxml2::XMLElement* element,const std::string& name)
      {
        tinyxml2::XMLElement* child = element->FirstChildElement(name.c_str());
        if(child == NULL)
        {
          child = doc->NewElement(name.c_str());
          element->InsertEndChild(child);
        }
        return child;
      }

      IGL_INLINE void getAttribute(const char* src,bool& dest)
      {
        tinyxml2::XMLUtil::ToBool(src,&dest);
      }

      IGL_INLINE void getAttribute(const char* src,char& dest)
      {
        dest = (char)atoi(src);
      }

      IGL_INLINE void getAttribute(const char* src,std::string& dest)
      {
        dest = src;
      }

      IGL_INLINE void getAttribute(const char* src,float& dest)
      {
        tinyxml2::XMLUtil::ToFloat(src,&dest);
      }

      IGL_INLINE void getAttribute(const char* src,double& dest)
      {
        tinyxml2::XMLUtil::ToDouble(src,&dest);
      }

      template<typename T>
      IGL_INLINE typename std::enable_if<std::is_integral<T>::value && std::is_unsigned<T>::value>::type getAttribute(const char* src,T& dest)

      template<typename T>
      IGL_INLINE typename std::enable_if<std::is_integral<T>::value && !std::is_unsigned<T>::value>::type getAttribute(const char* src,T& dest)
      {
        int val;
        tinyxml2::XMLUtil::ToInt(src,&val);
        dest = (T)val;
      }

      // tinyXML2 related stuff
      static const int numForbiddenChars = 8;
      static const char forbiddenChars[] ={' ','/','~','#','&','>','<','='};

      IGL_INLINE void replaceSubString(std::string& str,const std::string& search,const std::string& replace)
      {
        size_t pos = 0;
        while((pos = str.find(search,pos)) != std::string::npos)
        {
          str.replace(pos,search.length(),replace);
          pos += replace.length();
        }
      }

      IGL_INLINE void encodeXMLElementName(std::string& name)
      {
        // must not start with a digit
        if(isdigit(*name.begin()))
        {
          name = ":::" + name;
        }

        std::stringstream stream;
        for(int i=0;i<numForbiddenChars;i++)
        {
          std::string search;
          search = forbiddenChars[i];
          std::stringstream replaces;
          replaces << ":" << (int)forbiddenChars[i];
          std::string replace = replaces.str();

          replaceSubString(name,search,replace);
        }
      }

      IGL_INLINE void decodeXMLElementName(std::string& name)
      {
        if(name.find("::",0) == 0)
          name.replace(0,3,"");

        for(auto chr : forbiddenChars)
        {
          std::stringstream searchs;
          searchs << ":" << (int)chr;
          std::string search = searchs.str();
          std::string replace;
          replace = chr;

          replaceSubString(name,search,replace);
        }
      }

     /*

        Ren� Nyffenegger example@nnthink.com
        */

      static const std::string base64_chars =
        "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
        "abcdefghijklmnopqrstuvwxyz"
        "0123456789+/";

      static inline bool is_base64(unsigned char c) {
        return (isalnum(c) || (c == '+') || (c == '/'));
      }

      std::string base64_encode(unsigned char const* bytes_to_encode,unsigned int in_len)
      {
        std::string ret;
        int i = 0;
        int j = 0;
        unsigned char char_array_3[3];
        unsigned char char_array_4[4];

        while(in_len--) {
          char_array_3[i++] = *(bytes_to_encode++);
          if(i == 3) {
            char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
            char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
            char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
            char_array_4[3] = char_array_3[2] & 0x3f;

            for(i = 0; (i <4) ; i++)
              ret += base64_chars[char_array_4[i]];

            i = 0;
          }
        }

        if(i)
        {
          for(j = i; j < 3; j++)
            char_array_3[j] = '\0';

          char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
          char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
          char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
          char_array_4[3] = char_array_3[2] & 0x3f;

          for(j = 0; (j < i + 1); j++)
            ret += base64_chars[char_array_4[j]];

          while((i++ < 3))
            ret += '=';
        }

        return ret;
      }

      std::string base64_decode(std::string const& encoded_string)
      {
        int in_len = encoded_string.size();
        int i = 0;
        int j = 0;
        int in_ = 0;
        unsigned char char_array_4[4],char_array_3[3];
        std::string ret;

        // construct fast lookup table
        // added by Christian Sch�ller (example@nnthink.com)
        int charLookup[200];
        for(int i=0;i<(int)(base64_chars.length());i++)
          charLookup[(int)base64_chars[i]] = i;

        while(in_len-- && (encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
          char_array_4[i++] = encoded_string[in_]; in_++;
          if(i ==4) {
            for(i = 0; i <4; i++)
              char_array_4[i] = charLookup[char_array_4[i]]; // new fast lookup

            char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
            char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
            char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];

            for(i = 0; (i < 3); i++)
              ret += char_array_3[i];

            i = 0;
          }
        }

        if(i) {
          for(j = i; j <4; j++)
            char_array_4[j] = 0;

          for(j = 0; j <4; j++)
            char_array_4[j] = base64_chars.find(char_array_4[j]);

          char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
          char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
          char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];

          for(j = 0; (j < i - 1);
            j++) ret += char_array_3[j];
        }

        return ret;
      }
    }
  }
}

#ifdef IGL_STATIC_LIBRARY
// Explicit template instantiation
template void igl::xml::serialize_xml<std::vector<float, std::allocator<float> > >(std::vector<float, std::allocator<float> > const&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, bool, bool);
template void igl::xml::deserialize_xml<std::vector<float, std::allocator<float> > >(std::vector<float, std::allocator<float> >&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&);
#endif