/**
   @author Shin'ichiro Nakaoka
*/

#include "VRMLToSGConverter.h"
#include "SceneLight.h"
#include "Triangulator.h"
#include "PolygonMeshTriangulator.h"
#include "MeshNormalGenerator.h"
#include "MeshGenerator.h"
#include "ImageIO.h"
#include "Exception.h"
#include "DaeParser.h"
#include "STLSceneLoader.h"
#include "NullOut.h"
#include <boost/format.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/algorithm/string.hpp>

using namespace std;
using namespace cnoid;
using boost::format;

namespace {
const double PI = 3.14159265358979323846;
}

namespace cnoid {

class VRMLToSGConverterImpl
{
public:
    VRMLToSGConverter* self;

    ostream* os_;
    ostream& os() { return *os_; }

    bool isTriangulationEnabled;
    bool isNormalGenerationEnabled;

    vector<int> removedFaceIndices;
    vector<int> removedFaceVertexIndices;

    PolygonMeshTriangulator polygonMeshTriangulator;

    Triangulator<SgVertexArray> triangulator;
    vector<int> polygon;

    vector<int> newColorPosToOrgColorPosMap;

    MeshNormalGenerator normalGenerator;
    MeshGenerator meshGenerator;

    ImageIO imageIO;

    VRMLMaterialPtr defaultMaterial;

    typedef map<VRMLNodePtr, SgNodePtr> VRMLNodeToSgNodeMap;
    VRMLNodeToSgNodeMap vrmlNodeToSgNodeMap;

    typedef map<VRMLGeometryPtr, SgMeshPtr> VRMLGeometryToSgMeshMap;
    VRMLGeometryToSgMeshMap vrmlGeometryToSgMeshMap;

    typedef map<VRMLGeometryPtr, SgPlotPtr> VRMLGeometryToSgPlotMap;
    VRMLGeometryToSgPlotMap vrmlGeometryToSgPlotMap;

    typedef map<VRMLMaterialPtr, SgMaterialPtr> VRMLMaterialToSgMaterialMap;
    VRMLMaterialToSgMaterialMap vrmlMaterialToSgMaterialMap;

    typedef map<VRMLTexturePtr, SgTexturePtr> VRMLTextureToSgTextureMap;
    VRMLTextureToSgTextureMap vrmlTextureToSgTextureMap;

    typedef map<VRMLTextureTransformPtr, SgTextureTransformPtr> VRMLTextureTransformToSgTextureTransformMap;
    VRMLTextureTransformToSgTextureTransformMap vrmlTextureTransformToSgTextureTransformMap;

    typedef map<string, SgImagePtr> ImagePathToSgImageMap;
    ImagePathToSgImageMap imagePathToSgImageMap;

    enum BoxFaceID { NO_FACE, LEFT_FACE, TOP_FACE, FRONT_FACE, BOTTOM_FACE, RIGHT_FACE, BACK_FACE };

    VRMLToSGConverterImpl(VRMLToSGConverter* self);
    void putMessage(const std::string& message);
    SgNode* convertNode(VRMLNode* vnode);
    SgNode* convertGroupNode(AbstractVRMLGroup* vgroup);
    pair<SgNode*, SgGroup*> createTransformNodeSet(VRMLTransform* vt);
    SgNode* convertShapeNode(VRMLShape* vshape);
    SgMeshPtr createMeshFromIndexedFaceSet(VRMLIndexedFaceSet* vface);
    bool setIndicesForPerTriangleData(SgIndexArray& indices, int dataSize);
    bool convertIndicesForTriangles(SgIndexArray& indices, const MFInt32& orgIndices, const bool perVertex, const bool ccw);
    SgPolygonMeshPtr createPolygonMeshFromIndexedFaceSet(VRMLIndexedFaceSet* vface);
    void setIndicesForPerPolygonData(SgIndexArray& indices, int dataSize, const MFInt32& orgCoordIndices);
    void convertIndicesForPolygons(
        SgIndexArray& indices, const MFInt32& orgIndices, const MFInt32& orgCoordIndices, const bool perVertex, const bool ccw);
    void setDefaultTextureCoordinateForIndexedFaceSet(const SgMeshPtr& mesh);
    BoxFaceID faceOfBoxTriangle(const Vector3f& v0, const Vector3f& v1, const Vector3f& v2);
    void setDefaultTextureCoordinateForBox(const SgMeshPtr& mesh);
    double calcPolarAngle(const Vector3f& point);
    int findTexCoordPoint(const SgTexCoordArray& texCoord, const Vector2f& point);
    void setDefaultTextureCoordinateForSphere(const SgMeshPtr& mesh);
    void setDefaultTextureCoordinateForCylinder(const SgMeshPtr& mesh);
    void setDefaultTextureCoordinateForCone(const SgMeshPtr& mesh);
    SgMeshPtr createMeshFromElevationGrid(VRMLElevationGrid* grid);
    void setDefaultTextureCoordinateForElevationGrid(const SgMeshPtr& mesh, const VRMLElevationGrid* grid);
    SgMeshPtr createMeshFromExtrusion(VRMLExtrusion* extrusion);
    void setDefaultTextureCoordinateForExtrusion(const SgMeshPtr& mesh, const VRMLExtrusion* extrusion);
    SgMaterial* createMaterial(VRMLMaterial* vm);
    SgTexture* createTexture(VRMLTexture* vt);
    SgNode* convertLineSet(VRMLIndexedLineSet* vLineSet);
    SgTextureTransform* createTextureTransform(VRMLTextureTransform* tt);
    SgNode* convertLightNode(VRMLLight* vnode);
    void setLightCommonProperties(SgLight* light, VRMLLight* vlight);
    SgNode* createPointLight(VRMLPointLight* vlight);
    SgSpotLight* createSpotLight(VRMLSpotLight* vlight);
    SgDirectionalLight* createDirectionalLight(VRMLDirectionalLight* vlight);
    SgNode* readAnotherFormatFile(VRMLAnotherFormatFile* anotherFormat);
};
}

VRMLToSGConverter::VRMLToSGConverter()
{
    impl = new VRMLToSGConverterImpl(this);
}

VRMLToSGConverterImpl::VRMLToSGConverterImpl(VRMLToSGConverter* self)
    : self(self)
{
    os_ = &nullout();
    isTriangulationEnabled = true;
    isNormalGenerationEnabled = true;
    imageIO.setUpsideDown(true);
    defaultMaterial = new VRMLMaterial();
}

VRMLToSGConverter::~VRMLToSGConverter()
{
    delete impl;
}

void VRMLToSGConverter::setMessageSink(std::ostream& os)
{
    impl->os_ = &os;
}

void VRMLToSGConverter::setTriangulationEnabled(bool on)
{
    impl->isTriangulationEnabled = on;
}

void VRMLToSGConverter::setDivisionNumber(int divisionNumber)
{
    impl->meshGenerator.setDivisionNumber(divisionNumber);
}

int VRMLToSGConverter::divisionNumber() const
{
    return impl->meshGenerator.divisionNumber();
}

void VRMLToSGConverter::setNormalGenerationEnabled(bool on, bool doOverwrite)
{
    impl->isNormalGenerationEnabled = on;
    impl->normalGenerator.setOverwritingEnabled(doOverwrite);
}

void VRMLToSGConverter::setMinCreaseAngle(double angle)
{
    impl->normalGenerator.setMinCreaseAngle(angle);
}

void VRMLToSGConverter::setMaxCreaseAngle(double angle)
{
    impl->normalGenerator.setMaxCreaseAngle(angle);
}

void VRMLToSGConverter::clearConvertedNodeMap()
{
    impl->vrmlNodeToSgNodeMap.clear();
    impl->vrmlGeometryToSgMeshMap.clear();
    impl->vrmlGeometryToSgPlotMap.clear();
    impl->vrmlMaterialToSgMaterialMap.clear();
    impl->vrmlTextureToSgTextureMap.clear();
    impl->vrmlTextureTransformToSgTextureTransformMap.clear();
    impl->imagePathToSgImageMap.clear();
}

SgNodePtr VRMLToSGConverter::convert(VRMLNodePtr vrmlNode)
{
    if(vrmlNode){
        return impl->convertNode(vrmlNode.get());
    }
    return 0;
}

void VRMLToSGConverterImpl::putMessage(const std::string& message)
{
    os() << message << endl;

    /*
      if(!self->sigMessage.empty()){
      self->sigMessage(message + "\n" );
      }
    */
}

SgNode* VRMLToSGConverterImpl::convertNode(VRMLNode* vnode)
{
    SgNode* node = 0;

    VRMLNodeToSgNodeMap::iterator p = vrmlNodeToSgNodeMap.find(vnode);
    if(p != vrmlNodeToSgNodeMap.end()){
        node = p->second.get();

    } else {
        if(VRMLProtoInstance* protoInstance = dynamic_cast<VRMLProtoInstance*>(vnode)){
            vnode = protoInstance->actualNode.get();
        }

        if(vnode){
            if(AbstractVRMLGroup* group = dynamic_cast<AbstractVRMLGroup*>(vnode)){
                node = convertGroupNode(group);
            } else if(VRMLShape* shape = dynamic_cast<VRMLShape*>(vnode)){
                node = convertShapeNode(shape);
            } else if(VRMLLight* light = dynamic_cast<VRMLLight*>(vnode)){
                node = convertLightNode(light);
            } else if(VRMLAnotherFormatFile* anotherFormat = dynamic_cast<VRMLAnotherFormatFile*>(vnode)){
                node = readAnotherFormatFile(anotherFormat);
            }
            if(node){
                node->setName(vnode->defName);
                vrmlNodeToSgNodeMap[vnode] = node;
            }
        }
    }

    return node;
}

SgNode* VRMLToSGConverterImpl::convertGroupNode(AbstractVRMLGroup* vgroup)
{
    SgNode* top;
    SgGroup* group;

    if(VRMLTransform* transform = dynamic_cast<VRMLTransform*>(vgroup)){
        boost::tuples::tie(top, group) = createTransformNodeSet(transform);
    } else {
        group = new SgGroup;
        top = group;
    }

    int num = vgroup->countChildren();
    for(int i=0; i < num; i++){
        SgNode* child = convertNode(vgroup->getChild(i));
        if(child){
            group->addChild(child);
        }
    }

    if(group->numChildren() == 0){
        delete top;
        top = 0;
    }

    return top;
}

pair<SgNode*, SgGroup*> VRMLToSGConverterImpl::createTransformNodeSet(VRMLTransform* vt)
{
    const Translation3d C(vt->center);
    const AngleAxisd& R = vt->rotation;
    const Translation3d T(vt->translation);
    SgPosTransform* transform = new SgPosTransform;

    if(vt->scale.isOnes()){
        // no scaling
        transform->setTransform(T * C * R * C.inverse());
        return make_pair(transform, transform);
    } else {
        SgScaleTransform* scale = new SgScaleTransform;
        scale->setScale(vt->scale);
        transform->addChild(scale);
        if(vt->center.isZero() && !vt->scaleOrientation.angle()){
            transform->setTransform(T * R);
            return make_pair(transform, scale);
        } else {
            SgPosTransform* transform2 = new SgPosTransform;
            const AngleAxisd& SR = vt->scaleOrientation;
            Affine3d S;
            S.linear() = vt->scale.asDiagonal();
            S.translation().setZero();
            transform->setTransform(T * C * R * SR);
            transform2->setTransform(SR.inverse() * C.inverse());
            scale->addChild(transform2);
            return make_pair(transform, transform2);
        }
    }
}

SgNode* VRMLToSGConverterImpl::convertShapeNode(VRMLShape* vshape)
{
    SgNode* converted = 0;

    VRMLGeometry* vrmlGeometry = dynamic_node_cast<VRMLGeometry>(vshape->geometry).get();
    if(vrmlGeometry){
        SgMeshPtr mesh;
        VRMLGeometryToSgMeshMap::iterator p = vrmlGeometryToSgMeshMap.find(vrmlGeometry);
        if(p != vrmlGeometryToSgMeshMap.end()){
            mesh = p->second;
        } else {
            if(VRMLIndexedFaceSet* faceSet = dynamic_cast<VRMLIndexedFaceSet*>(vrmlGeometry)){
                if(isTriangulationEnabled){
                    SgPolygonMeshPtr polygonMesh = createPolygonMeshFromIndexedFaceSet(faceSet);
                    mesh = polygonMeshTriangulator.triangulate(*polygonMesh);
                    const string& errorMessage = polygonMeshTriangulator.errorMessage();
                    if(!errorMessage.empty()){
                        string message;
                        if(faceSet->defName.empty()){
                            message = "Error of an IndexedFaceSet node: \n";
                        } else {
                            message = str(format("Error of IndexedFaceSet node \"%1%\": \n") % faceSet->defName);
                        }
                        putMessage(message + errorMessage);
                    }
                } else {
                    mesh = createMeshFromIndexedFaceSet(faceSet);
                }
                if(mesh && isNormalGenerationEnabled){
                    normalGenerator.generateNormals(mesh, faceSet->creaseAngle);
                }

            } else if(VRMLBox* box = dynamic_cast<VRMLBox*>(vrmlGeometry)){
                mesh = meshGenerator.generateBox(
                    Vector3(box->size[0], box->size[1], box->size[2]));

            } else if(VRMLSphere* sphere = dynamic_cast<VRMLSphere*>(vrmlGeometry)){
                mesh = meshGenerator.generateSphere(sphere->radius);

            } else if(VRMLCylinder* cylinder = dynamic_cast<VRMLCylinder*>(vrmlGeometry)){
                mesh = meshGenerator.generateCylinder(
                    cylinder->radius, cylinder->height, cylinder->bottom, cylinder->side, cylinder->top);

            } else if(VRMLCone* cone = dynamic_cast<VRMLCone*>(vrmlGeometry)){
                mesh = meshGenerator.generateCone(
                    cone->bottomRadius, cone->height, cone->bottom, cone->side);

            } else if(VRMLElevationGrid* elevationGrid = dynamic_cast<VRMLElevationGrid*>(vrmlGeometry)){
                mesh = createMeshFromElevationGrid(elevationGrid);

            } else if(VRMLExtrusion* extrusion = dynamic_cast<VRMLExtrusion*>(vrmlGeometry)){
                mesh = createMeshFromExtrusion(extrusion);

            } else if(VRMLIndexedLineSet* lineSet = dynamic_cast<VRMLIndexedLineSet*>(vrmlGeometry)){
                converted = convertLineSet(lineSet);

            } else {
                putMessage("Unsupported VRML node type is used.");
            }

            if(mesh){
                vrmlGeometryToSgMeshMap[vrmlGeometry] = mesh;
            }
        }
        if(mesh){
            SgShape* shape = new SgShape;
            converted = shape;
            shape->setMesh(mesh);

            VRMLMaterial* vm;
            if(vshape->appearance && vshape->appearance->material){
                vm = vshape->appearance->material.get();
            } else {
                vm = defaultMaterial.get();
            }
            VRMLMaterialToSgMaterialMap::iterator p = vrmlMaterialToSgMaterialMap.find(vm);
            if(p != vrmlMaterialToSgMaterialMap.end()){
                shape->setMaterial(p->second);
            } else {
                shape->setMaterial(createMaterial(vm));
                vrmlMaterialToSgMaterialMap[vm] = shape->material();
            }

            if(vshape->appearance && vshape->appearance->texture){

                SgTextureTransformPtr textureTransform;
                if(vshape->appearance->textureTransform){
                    VRMLTextureTransform* vtt = vshape->appearance->textureTransform.get();
                    VRMLTextureTransformToSgTextureTransformMap::iterator pp =
                        vrmlTextureTransformToSgTextureTransformMap.find(vtt);
                    if(pp != vrmlTextureTransformToSgTextureTransformMap.end()){
                        textureTransform = pp->second;
                    } else {
                        textureTransform = createTextureTransform(vtt);
                        vrmlTextureTransformToSgTextureTransformMap[vtt] = textureTransform;
                    }
                }

                SgTexturePtr texture;
                VRMLTexture* vt = vshape->appearance->texture.get();
                VRMLTextureToSgTextureMap::iterator p = vrmlTextureToSgTextureMap.find(vt);
                if(p != vrmlTextureToSgTextureMap.end()){
                    if(p->second->textureTransform() == textureTransform){
                        texture = p->second;
                    }
                }
                if(!texture){
                    texture = createTexture(vt);
                    vrmlTextureToSgTextureMap[vt] = texture;
                }
                if(texture){
                    texture->setTextureTransform(textureTransform);
                    shape->setTexture(texture);

                    if(!mesh->texCoords()){
                        if(VRMLIndexedFaceSet* faceSet = dynamic_cast<VRMLIndexedFaceSet*>(vrmlGeometry)){
                            setDefaultTextureCoordinateForIndexedFaceSet(mesh);
                        } else if(VRMLBox* box = dynamic_cast<VRMLBox*>(vrmlGeometry)){
                            setDefaultTextureCoordinateForBox(mesh);
                        } else if(VRMLSphere* sphere = dynamic_cast<VRMLSphere*>(vrmlGeometry)){
                            setDefaultTextureCoordinateForSphere(mesh);
                        } else if(VRMLCylinder* cylinder = dynamic_cast<VRMLCylinder*>(vrmlGeometry)){
                            setDefaultTextureCoordinateForCylinder(mesh);
                        } else if(VRMLCone* cone = dynamic_cast<VRMLCone*>(vrmlGeometry)){
                            setDefaultTextureCoordinateForCone(mesh);
                        } else if(VRMLElevationGrid* elevationGrid = dynamic_cast<VRMLElevationGrid*>(vrmlGeometry)){
                            setDefaultTextureCoordinateForElevationGrid(mesh, elevationGrid);
                        } else if(VRMLExtrusion* extrusion = dynamic_cast<VRMLExtrusion*>(vrmlGeometry)){
                            setDefaultTextureCoordinateForExtrusion(mesh, extrusion);
                        }
                    }
                }
            }
        }
    }

    return converted;
}

SgMeshPtr VRMLToSGConverterImpl::createMeshFromIndexedFaceSet(VRMLIndexedFaceSet* vface)
{
    if(!vface->coord || vface->coord->point.empty() || vface->coordIndex.empty()){
        return SgMeshPtr(); // null
    }

    SgMeshPtr mesh = new SgMesh;
    mesh->setSolid(vface->solid);
    mesh->setVertices(new SgVertexArray(vface->coord->point));

    removedFaceIndices.clear();
    removedFaceVertexIndices.clear();

    const MFInt32& orgCoordIndices = vface->coordIndex;
    const bool ccw = vface->ccw;

    const int orgCoordIndicesSize = orgCoordIndices.size();
    SgIndexArray& triangleVertices = mesh->triangleVertices();
    triangleVertices.reserve((orgCoordIndicesSize + 1) * 3 / 4);
    int faceIndex = 0;
    int faceVertexIndex = 0;
    int firstVertexIndex = 0;
    int numFaceVertices = 0;
    for(int i=0; i < orgCoordIndicesSize; ++i){
        int index = orgCoordIndices[i];
        if(index >= 0){
            ++numFaceVertices;
        } else {
            if(numFaceVertices == 3) { // Triangle ?
                if(ccw){
                    triangleVertices.push_back(orgCoordIndices[firstVertexIndex]);
                    triangleVertices.push_back(orgCoordIndices[firstVertexIndex + 1]);
                    triangleVertices.push_back(orgCoordIndices[firstVertexIndex + 2]);
                } else { // flip
                    triangleVertices.push_back(orgCoordIndices[firstVertexIndex + 2]);
                    triangleVertices.push_back(orgCoordIndices[firstVertexIndex + 1]);
                    triangleVertices.push_back(orgCoordIndices[firstVertexIndex]);
                }
                faceVertexIndex += 3;
            } else {
                removedFaceIndices.push_back(faceIndex);
                for(int j=0; j < numFaceVertices; ++j){
                    removedFaceVertexIndices.push_back(faceVertexIndex++);
                }
            }
            firstVertexIndex = i + 1; // next position
            numFaceVertices = 0;
            ++faceVertexIndex;
            ++faceIndex;
        }
    }

    if(!removedFaceIndices.empty()){
        if(vface->defName.empty()){
            putMessage(str(format("An IndexedFaceSet node contains %1% non-triangle polygon(s).")
                           % removedFaceIndices.size()));
        } else {
            putMessage(str(format("IndexedFaceSet node \"%1%\" contains %2% non-triangle polygon(s).")
                           % vface->defName % removedFaceIndices.size()));
        }
    }

    if(vface->normal && !vface->normal->vector.empty()){
        bool converted;
        if(vface->normalIndex.empty() && !vface->normalPerVertex){
            converted = setIndicesForPerTriangleData(mesh->normalIndices(), vface->normal->vector.size());
        } else {
            converted = convertIndicesForTriangles(mesh->normalIndices(), vface->normalIndex, vface->normalPerVertex, ccw);
        }
        if(converted){
            mesh->setNormals(new SgNormalArray(vface->normal->vector));
        } else {
            putMessage("The normalIndex field of an IndexedFaceSet node contains illegal data.");
        }
    }

    if(vface->color && !vface->color->color.empty()){
        bool converted;
        if(vface->colorIndex.empty() && !vface->colorPerVertex){
            converted = setIndicesForPerTriangleData(mesh->colorIndices(), vface->color->color.size());
        } else {
            converted = convertIndicesForTriangles(mesh->colorIndices(), vface->colorIndex, vface->colorPerVertex, ccw);
        }
        if(converted){
            mesh->setColors(new SgColorArray(vface->color->color));
        } else {
            putMessage("The colorIndex field of an IndexedFaceSet node contains illegal data.");
        }
    }

    if(vface->texCoord && !vface->texCoord->point.empty()){
        if(convertIndicesForTriangles(mesh->texCoordIndices(), vface->texCoordIndex, true, ccw)){
            mesh->setTexCoords(new SgTexCoordArray(vface->texCoord->point));
        } else {
            putMessage("The texCoordIndex field of an IndexedFaceSet node contains illegal data.");
        }
    }

    mesh->updateBoundingBox();

    return mesh;
}

bool VRMLToSGConverterImpl::setIndicesForPerTriangleData(SgIndexArray& indices, int dataSize)
{
    const int numIndices = (dataSize - removedFaceIndices.size()) * 3;
    if(numIndices > 0){
        indices.reserve(numIndices);
    }
    int indexToSkip = removedFaceIndices.empty() ? std::numeric_limits<int>::min() : removedFaceIndices.front();
    size_t nextIndexToSkipIndex = 1;
    size_t indexInOrgCoordIndices = 0;
    for(int i=0; i < dataSize; ++i){
        if(i == indexToSkip){
            if(nextIndexToSkipIndex < removedFaceIndices.size()){
                indexToSkip = removedFaceIndices[nextIndexToSkipIndex++];
            }
        } else {
            indices.push_back(i);
            indices.push_back(i);
            indices.push_back(i);
        }
    }
    return true;
}

bool VRMLToSGConverterImpl::convertIndicesForTriangles
(SgIndexArray& indices, const MFInt32& orgIndices, const bool perVertex, const bool ccw)
{
    bool converted = true;

    if(!orgIndices.empty()){
        vector<int>* indicesToSkip;
        if(perVertex){
            indicesToSkip = &removedFaceVertexIndices;
            indices.reserve((orgIndices.size() + 1) * 3 / 4);
        } else {
            indicesToSkip = &removedFaceIndices;
            indices.reserve(orgIndices.size() * 3);
        }
        int indexToSkip = indicesToSkip->empty() ? std::numeric_limits<int>::min() : indicesToSkip->front();
        size_t nextIndexToSkipIndex = 1;
        int numFaceVertices = 0;
        int firstVertexIndex = 0;
        for(int i=0; i < orgIndices.size(); ++i){
            if(i == indexToSkip){
                if(nextIndexToSkipIndex < indicesToSkip->size()){
                    indexToSkip = (*indicesToSkip)[nextIndexToSkipIndex++];
                }
            } else {
                const int index = orgIndices[i];
                if(perVertex){
                    if(index >= 0){
                        ++numFaceVertices;
                    } else {
                        if(numFaceVertices != 3){
                            converted = false;
                            break;
                        }
                        if(ccw){
                            indices.push_back(orgIndices[firstVertexIndex]);
                            indices.push_back(orgIndices[firstVertexIndex + 1]);
                            indices.push_back(orgIndices[firstVertexIndex + 2]);
                        } else {
                            indices.push_back(orgIndices[firstVertexIndex + 2]);
                            indices.push_back(orgIndices[firstVertexIndex + 1]);
                            indices.push_back(orgIndices[firstVertexIndex]);
                        }
                        firstVertexIndex = i + 1;
                        numFaceVertices = 0;
                    }
                } else { // not perVertex
                    if(index < 0){
                        converted = false;
                        break;
                    }
                    indices.push_back(index);
                    indices.push_back(index);
                    indices.push_back(index);
                }
            }
        }
        if(!converted){
            indices.clear();
        }
    }

    return converted;
}

SgPolygonMeshPtr VRMLToSGConverterImpl::createPolygonMeshFromIndexedFaceSet(VRMLIndexedFaceSet* vface)
{
    if(!vface->coord || vface->coord->point.empty() || vface->coordIndex.empty()){
        return SgPolygonMeshPtr(); // null
    }

    SgPolygonMeshPtr mesh = new SgPolygonMesh;
    mesh->setSolid(vface->solid);
    mesh->setVertices(new SgVertexArray(vface->coord->point));

    const MFInt32& orgCoordIndices = vface->coordIndex;
    const bool ccw = vface->ccw;

    {
        SgIndexArray& polygonVertices = mesh->polygonVertices();
        if(ccw){
            polygonVertices = orgCoordIndices;
        } else {
            polygonVertices.reserve(orgCoordIndices.size());
            int firstVertexIndex = 0;
            int numFaceVertices = 0;
            for(size_t i=0; i < orgCoordIndices.size(); ++i){
                int index = orgCoordIndices[i];
                if(index >= 0){
                    ++numFaceVertices;
                } else {
                    while(--numFaceVertices >= 0){
                        polygonVertices.push_back(orgCoordIndices[firstVertexIndex + numFaceVertices]);
                    }
                    polygonVertices.push_back(-1);
                    firstVertexIndex = i + 1;
                    numFaceVertices = 0;
                }
            }
        }
    }

    if(vface->normal && !vface->normal->vector.empty()){
        mesh->setNormals(new SgNormalArray(vface->normal->vector));
        if(vface->normalIndex.empty()){
            if(!vface->normalPerVertex){
                setIndicesForPerPolygonData(mesh->normalIndices(), mesh->normals()->size(), orgCoordIndices);
            }
        } else {
            convertIndicesForPolygons(mesh->normalIndices(), vface->normalIndex, orgCoordIndices, vface->normalPerVertex, ccw);
        }
    }

    if(vface->color && !vface->color->color.empty()){
        mesh->setColors(new SgColorArray(vface->color->color));
        if(vface->colorIndex.empty()){
            if(!vface->colorPerVertex){
                setIndicesForPerPolygonData(mesh->colorIndices(), mesh->colors()->size(), orgCoordIndices);
            }
        } else {
            convertIndicesForPolygons(mesh->colorIndices(), vface->colorIndex, orgCoordIndices, vface->colorPerVertex, ccw);
        }
    }

    if(vface->texCoord && !vface->texCoord->point.empty()){
        mesh->setTexCoords(new SgTexCoordArray(vface->texCoord->point));
        if(!vface->texCoordIndex.empty()){
            convertIndicesForPolygons(mesh->texCoordIndices(), vface->texCoordIndex, orgCoordIndices, true, ccw);
        }
    }

    // The bounding box doesn't have to be updated because this polygon mesh is immediately converted into the triagnle mesh

    return mesh;
}

void VRMLToSGConverterImpl::setIndicesForPerPolygonData(SgIndexArray& indices, int dataSize, const MFInt32& orgCoordIndices)
{
    indices.reserve(orgCoordIndices.size());
    size_t indexInOrgCoordIndices = 0;
    for(int i=0; i < dataSize; ++i){
        while(indexInOrgCoordIndices < orgCoordIndices.size()){
            const int orgCoordIndex = orgCoordIndices[indexInOrgCoordIndices++];
            if(orgCoordIndex < 0){
                break;
            }
            indices.push_back(i);
        }
        indices.push_back(-1);
    }
}

void VRMLToSGConverterImpl::convertIndicesForPolygons
(SgIndexArray& indices, const MFInt32& orgIndices, const MFInt32& orgCoordIndices, const bool perVertex, const bool ccw)
{
    if(perVertex){
        if(ccw){
            indices = orgIndices;
            return;
        }
        indices.reserve(orgIndices.size());
        int firstVertexIndex = 0;
        int numFaceVertices = 0;
        for(size_t i=0; i < orgIndices.size(); ++i){
            if(orgIndices[i] >= 0){
                ++numFaceVertices;
            } else {
                while(--numFaceVertices >= 0){
                    indices.push_back(orgIndices[firstVertexIndex + numFaceVertices]);
                }
                indices.push_back(-1);
                firstVertexIndex = i + 1;
                numFaceVertices = 0;
            }
        }
    } else {
        indices.reserve(orgCoordIndices.size());
        size_t indexInOrgCoordIndices = 0;
        for(size_t i=0; i < orgIndices.size(); ++i){
            const int index = orgIndices[i];
            if(index >= 0){
                while(indexInOrgCoordIndices < orgCoordIndices.size()){
                    const int orgCoordIndex = orgCoordIndices[indexInOrgCoordIndices++];
                    if(orgCoordIndex < 0){
                        break;
                    }
                    indices.push_back(index);
                }
                indices.push_back(-1);
            }
        }
    }
}

void VRMLToSGConverterImpl::setDefaultTextureCoordinateForIndexedFaceSet(const SgMeshPtr& mesh)
{
    const SgVertexArray& vertices = *mesh->vertices();

    const Vector3f& v0 = vertices[0];
    Vector3f max = v0;
    Vector3f min = v0;

    const int n = vertices.size();
    for(int i=1; i < n; ++i){
        const Vector3f& vi = vertices[i];
        for(int j=0; j < 3; ++j){
            float vij = vi[j];
            if(vij > max[j]){
                max[j] = vij;
            } else if(vij < min[j]){
                min[j] = vij;
            }
        }
    }

    int s,t;
    const Vector3f size = max - min;
    if(size.x() >= size.y()){
        if(size.x() >= size.z()){
            s = 0;
            t = (size.y() >= size.z()) ? 1 : 2;
        } else {
            s = 2;
            t = 0;
        }
    } else {
        if(size.y() >= size.z()){
            s = 1;
            t = (size.x() >= size.z()) ? 0 : 2;
        } else {
            s = 2;
            t = 1;
        }
    }
    const float ratio = size[t] / size[s];

    mesh->setTexCoords(new SgTexCoordArray());
    SgTexCoordArray& texCoords = *mesh->texCoords();
    texCoords.resize(n);
    for(int i=0; i < n; ++i){
        texCoords[i] <<
            (vertices[i][s] - min[s]) / size[s],
            (vertices[i][t] - min[t]) / size[t] * ratio;
    }

    // Is this really necessary for rendering?
    mesh->texCoordIndices() = mesh->triangleVertices();
}

/**
   \todo The face correcpondence relationship obtained from the primitive mesh creation
   (SgMesh::setBox function) should be used to obtain which face the given one is.
*/
VRMLToSGConverterImpl::BoxFaceID VRMLToSGConverterImpl::faceOfBoxTriangle
(const Vector3f& v0, const Vector3f& v1, const Vector3f& v2)
{
    if(v0.x() <= 0.0 && v1.x() <= 0.0 && v2.x() <= 0.0 ) return LEFT_FACE;
    if(v0.x() > 0.0 && v1.x() > 0.0 && v2.x() > 0.0 ) return RIGHT_FACE;
    if(v0.y() <= 0.0 && v1.y() <= 0.0 && v2.y() <= 0.0 ) return BOTTOM_FACE;
    if(v0.y() > 0.0 && v1.y() > 0.0 && v2.y() > 0.0 ) return TOP_FACE;
    if(v0.z() <= 0.0 && v1.z() <= 0.0 && v2.z() <= 0.0 ) return BACK_FACE;
    if(v0.z() > 0.0 && v1.z() > 0.0 && v2.z() > 0.0 ) return FRONT_FACE;

    return NO_FACE;

    /**
       \todo use the algorithm like the following code to support arbitrary box meshs
       whose number of vertices is more than eight.
    */
    /*
      int faceNormalAxis = 0;
      SgFloat minNorm = std::numeric_limits<SgFloat>::max();
      for(int i=0; i < 3; ++i){
      const SgFloat d1 = (v0[i] - v1[i]);
      const SgFloat d2 = (v0[i] - v2[i]);
      const SgFloat n = d1 * d1 + d2 * d2;
      if(n < minNorm){
      minNorm = n;
      faceNormalAxis = i;
      }
      }
      bool isFront = (v0[faceNormalAxis] > 0.0);

      return (faceNormalAxis, isFront);
    */
}

void VRMLToSGConverterImpl::setDefaultTextureCoordinateForBox(const SgMeshPtr& mesh)
{
    mesh->setTexCoords(new SgTexCoordArray());
    SgTexCoordArray& texCoords = *mesh->texCoords();
    texCoords.resize(4);
    texCoords[0] << 0.0, 0.0;
    texCoords[1] << 1.0, 0.0;
    texCoords[2] << 0.0, 1.0;
    texCoords[3] << 1.0, 1.0;

    SgIndexArray& texCoordIndices = mesh->texCoordIndices();
    texCoordIndices.clear();
    texCoordIndices.reserve(36);
    const SgVertexArray& vertices = *mesh->vertices();
    const SgIndexArray& triangles = mesh->triangleVertices();

    for(int i=0; i < 12; ++i){
        const Vector3f* v[3] = {
            &vertices[triangles[i*3]],
            &vertices[triangles[i*3+1]],
            &vertices[triangles[i*3+2]] };

        switch(faceOfBoxTriangle(*v[0], *v[1], *v[2])){
        case LEFT_FACE:
            for(int j=0; j < 3; ++j){
                if(v[j]->y() > 0.0 && v[j]->z() > 0.0) texCoordIndices.push_back(3);
                else if(v[j]->y() >  0.0 && v[j]->z() <= 0.0) texCoordIndices.push_back(2);
                else if(v[j]->y() <= 0.0 && v[j]->z() > 0.0) texCoordIndices.push_back(1);
                else if(v[j]->y() <= 0.0 && v[j]->z() <= 0.0) texCoordIndices.push_back(0);
            }
            break;
        case RIGHT_FACE:
            for(int j=0; j < 3; ++j){
                if(v[j]->y() > 0.0 && v[j]->z() > 0.0) texCoordIndices.push_back(2);
                else if(v[j]->y() >  0.0 && v[j]->z() <= 0.0) texCoordIndices.push_back(3);
                else if(v[j]->y() <= 0.0 && v[j]->z() > 0.0) texCoordIndices.push_back(0);
                else if(v[j]->y() <= 0.0 && v[j]->z() <= 0.0) texCoordIndices.push_back(1);
            }
            break;
        case BOTTOM_FACE:
            for(int j=0; j < 3; ++j){
                if(v[j]->z() > 0.0 && v[j]->x() > 0.0) texCoordIndices.push_back(3);
                else if(v[j]->z() >  0.0 && v[j]->x() <= 0.0) texCoordIndices.push_back(2);
                else if(v[j]->z() <= 0.0 && v[j]->x()  > 0.0) texCoordIndices.push_back(1);
                else if(v[j]->z() <= 0.0 && v[j]->x() <= 0.0) texCoordIndices.push_back(0);
            }
            break;
        case TOP_FACE:
            for(int j=0; j < 3; ++j){
                if(v[j]->z() > 0.0 && v[j]->x() > 0.0) texCoordIndices.push_back(1);
                else if(v[j]->z() >  0.0 && v[j]->x() <= 0.0) texCoordIndices.push_back(0);
                else if(v[j]->z() <= 0.0 && v[j]->x() >  0.0) texCoordIndices.push_back(3);
                else if(v[j]->z() <= 0.0 && v[j]->x() <= 0.0) texCoordIndices.push_back(2);
            }
            break;
        case BACK_FACE:
            for(int j=0; j < 3; ++j){
                if(v[j]->y() > 0.0 && v[j]->x() > 0.0) texCoordIndices.push_back(2);
                else if(v[j]->y() >  0.0 && v[j]->x() <= 0.0) texCoordIndices.push_back(3);
                else if(v[j]->y() <= 0.0 && v[j]->x() >  0.0) texCoordIndices.push_back(0);
                else if(v[j]->y() <= 0.0 && v[j]->x() <= 0.0) texCoordIndices.push_back(1);
            }
            break;
        case FRONT_FACE:
            for(int j=0; j < 3; ++j){
                if(v[j]->y() > 0.0 && v[j]->x() > 0.0) texCoordIndices.push_back(3);
                else if(v[j]->y() >  0.0 && v[j]->x() <= 0.0) texCoordIndices.push_back(2);
                else if(v[j]->y() <= 0.0 && v[j]->x() >  0.0) texCoordIndices.push_back(1);
                else if(v[j]->y() <= 0.0 && v[j]->x() <= 0.0) texCoordIndices.push_back(0);
            }
            break;
        default:
            break;
        }
    }
}

double VRMLToSGConverterImpl::calcPolarAngle(const Vector3f& point)
{
    double angle = atan2(point[2], point[0]);
    if(angle>=0){
        angle = 1.5 * PI - angle;
    } else if(-0.5 * PI < angle){
        angle = -angle + 1.5 * PI;
    } else {
        angle = -angle - 0.5 * PI;
    }
    return angle;
}

/**
   \todo Check if the use of this inefficient function is rellay necessary.
*/
int VRMLToSGConverterImpl::findTexCoordPoint(const SgTexCoordArray& texCoords, const Vector2f& point)
{
    for(size_t i=0; i < texCoords.size(); ++i){
        if(texCoords[i].isApprox(point)){
            return i;
        }
    }
    return -1;
}

void VRMLToSGConverterImpl::setDefaultTextureCoordinateForSphere(const SgMeshPtr& mesh)
{
    const SgMesh::Sphere& sphere = mesh->primitive<SgMesh::Sphere>();
    const SgVertexArray& vertices = *mesh->vertices();
    const SgIndexArray& triangles = mesh->triangleVertices();

    mesh->setTexCoords(new SgTexCoordArray());
    SgTexCoordArray& texCoords = *mesh->texCoords();
    SgIndexArray& texCoordIndices = mesh->texCoordIndices();
    texCoordIndices.clear();

    Vector2f texPoint;
    int texIndex = 0;
    const int numTriangles = mesh->numTriangles();
    for(size_t i=0; i < numTriangles; ++i){
        const Vector3f* point[3];
        bool over = false;
        double s[3] = { 0.0, 0.0, 0.0 };
        const SgMesh::TriangleRef triangle = mesh->triangle(i);
        for(int j=0; j < 3; ++j){
            point[j] = &vertices[triangle[j]];
            s[j] = calcPolarAngle(*point[j]) / 2.0 / PI;
            if(s[j] > 0.5){
                over = true;
            }
        }
        for(int j=0; j < 3; ++j){
            if(over && s[j] < 1.0e-6){
                s[j] = 1.0;
            }
            texPoint << s[j], 1.0 - acos(point[j]->y() / sphere.radius) / PI;

            int k = findTexCoordPoint(texCoords, texPoint);
            if(k >= 0){
                texCoordIndices.push_back(k);
            } else {
                texCoords.push_back(texPoint);
                texCoordIndices.push_back(texIndex++);
            }
        }
    }
}

void VRMLToSGConverterImpl::setDefaultTextureCoordinateForCylinder(const SgMeshPtr& mesh)
{
    const SgVertexArray& vertices = *mesh->vertices();
    const SgIndexArray& triangles = mesh->triangleVertices();
    mesh->setTexCoords(new SgTexCoordArray());
    SgTexCoordArray& texCoords = *mesh->texCoords();
    SgIndexArray& texCoordIndices = mesh->texCoordIndices();
    texCoordIndices.clear();

    Vector2f texPoint(0.5f, 0.5f); // center of top(bottom) index=0

    texCoords.push_back(texPoint);
    int texIndex = 1;
    const int numTriangles = mesh->numTriangles();
    for(int i=0; i < numTriangles; ++i){
        Vector3f point[3];
        bool notside = true;
        int center = -1;
        SgMesh::TriangleRef triangle = mesh->triangle(i);
        for(int j=0; j < 3; ++j){
            point[j] = vertices[triangle[j]];
            if(j > 0){
                if(point[0][1] != point[j][1]){
                    notside = false;
                }
            }
            if(point[j][0] == 0.0 && point[j][2] == 0.0){
                center = j;
            }
        }
        if(!notside){         //side
            bool over=false;
            Vector3f s(0.0, 0.0, 0.0);
            for(int j=0; j < 3; ++j){
                s[j] = calcPolarAngle(point[j]) / 2.0 / PI;
                if(s[j] > 0.5){
                    over = true;
                }
            }
            for(int j=0; j < 3; ++j){
                if(over && s[j] < 1.0e-6){
                    s[j] = 1.0;
                }
                texPoint[0] = s[j];
                if(point[j][1] > 0.0){
                    texPoint[1] = 1.0;
                } else {
                    texPoint[1] = 0.0;
                }
                const int k = findTexCoordPoint(texCoords, texPoint);
                if(k >= 0){
                    texCoordIndices.push_back(k);
                }else{
                    texCoords.push_back(texPoint);
                    texCoordIndices.push_back(texIndex++);
                }
            }
        } else {              // top / bottom
            for(int j=0; j < 3; ++j){
                if(j!=center){
                    const double angle = atan2(point[j][2], point[j][0]);
                    texPoint[0] = 0.5 + 0.5 * cos(angle);
                    if(point[0][1] > 0.0){  //top
                        texPoint[1] = 0.5 - 0.5 * sin(angle);
                    } else {               //bottom
                        texPoint[1] = 0.5 + 0.5 * sin(angle);
                    }
                    const int k = findTexCoordPoint(texCoords, texPoint);
                    if(k != -1){
                        texCoordIndices.push_back(k);
                    }else{
                        texCoords.push_back(texPoint);
                        texCoordIndices.push_back(texIndex++);
                    }
                }else{
                    texCoordIndices.push_back(0);
                }
            }
        }
    }
}

void VRMLToSGConverterImpl::setDefaultTextureCoordinateForCone(const SgMeshPtr& mesh)
{
    mesh->setTexCoords(new SgTexCoordArray());
    SgTexCoordArray& texCoords = *mesh->texCoords();
    SgIndexArray& texCoordIndices = mesh->texCoordIndices();
    texCoordIndices.clear();

    Vector2f texPoint(0.5, 0.5); //center of bottom index=0
    texCoords.push_back(texPoint);

    const SgVertexArray& vertices = *mesh->vertices();

    int texIndex = 1;
    const int numTriangles = mesh->numTriangles();
    for(size_t i=0; i < numTriangles; ++i){
        Vector3f point[3];
        int top = -1;
        int center = -1;
        SgMesh::TriangleRef triangle = mesh->triangle(i);
        for(int j=0; j < 3; ++j){
            point[j] = vertices[triangle[j]];
            if(point[j][1] > 0.0){
                top = j;
            }
            if(point[j][0] == 0.0 && point[j][2] == 0.0){
                center = j;
            }
        }
        if(top >= 0){ //side
            Vector3f s(0.0f, 0.0f, 0.0f);
            int pre = -1;
            for(int j=0; j < 3; ++j){
                if(j != top){
                    s[j] = calcPolarAngle(point[j]) / 2.0 / PI;
                    if(pre != -1){
                        if(s[pre] > 0.5 && s[j] < 1.0e-6){
                            s[j] = 1.0;
                        }
                    }
                    pre = j;
                }
            }
            for(int j=0; j < 3; ++j){
                if(j != top){
                    texPoint << s[j], 0.0;
                } else {
                    texPoint << (s[0] + s[1] + s[2]) / 2.0, 1.0;
                }
                const int k = findTexCoordPoint(texCoords, texPoint);
                if(k != -1){
                    texCoordIndices.push_back(k);
                } else {
                    texCoords.push_back(texPoint);
                    texCoordIndices.push_back(texIndex++);
                }
            }
        } else { // bottom
            for(int j=0; j < 3; ++j){
                if(j != center){
                    const double angle = atan2(point[j][2], point[j][0]);
                    texPoint << 0.5 + 0.5 * cos(angle), 0.5 + 0.5 * sin(angle);
                    const int k = findTexCoordPoint(texCoords, texPoint);
                    if(k != -1){
                        texCoordIndices.push_back(k);
                    } else {
                        texCoords.push_back(texPoint);
                        texCoordIndices.push_back(texIndex++);
                    }
                } else {
                    texCoordIndices.push_back(0);
                }
            }
        }
    }
}

SgMeshPtr VRMLToSGConverterImpl::createMeshFromElevationGrid(VRMLElevationGrid* grid)
{
    if(grid->xDimension * grid->zDimension != static_cast<SFInt32>(grid->height.size())){
        putMessage("A VRML ElevationGrid node has illegal parameters.");
        return SgMeshPtr();
    }

    SgMeshPtr mesh = new SgMesh;
    mesh->setVertices(new SgVertexArray());
    SgVertexArray& vertices = *mesh->vertices();
    vertices.reserve(grid->zDimension * grid->xDimension);

    for(int z=0; z < grid->zDimension; z++){
        for(int x=0; x < grid->xDimension; x++){
            vertices.push_back(Vector3f(x * grid->xSpacing, grid->height[z * grid->xDimension + x], z * grid->zSpacing));
        }
    }

    mesh->reserveNumTriangles((grid->zDimension - 1) * (grid->xDimension - 1) * 2);

    for(int z=0; z < grid->zDimension - 1; ++z){
        const int current = z * grid->xDimension;
        const int next = (z + 1) * grid->xDimension;
        for(int x=0; x < grid->xDimension - 1; ++x){
            if(grid->ccw){
                mesh->addTriangle( x + current, x + next, (x + 1) + next);
                mesh->addTriangle( x + current, (x + 1) + next, (x + 1) + current);
            }else{
                mesh->addTriangle( x + current, (x + 1) + next, x + next);
                mesh->addTriangle( x + current, (x + 1) + current, (x + 1) + next);
            }
        }
    }

    mesh->setSolid(grid->solid);

    if(isNormalGenerationEnabled){
        normalGenerator.generateNormals(mesh, grid->creaseAngle);
    }

    if(grid->color){
        const MFColor& orgColors = grid->color->color;
        if(!orgColors.empty()){
            if(!grid->colorPerVertex){
                mesh->setColors(new SgColorArray());
                SgColorArray& colors = *mesh->colors();
                const int n = orgColors.size();
                colors.reserve(n * 2);
                for(int i=0; i < n; ++i){
                    const SFColor& c = orgColors[i];
                    for(int j=0; j < 6; ++j){
                        colors.push_back(c);
                    }
                }
            }
        }
    }

    if(grid->texCoord){
        const MFVec2s& point = grid->texCoord->point;
        const int n = point.size();
        mesh->setTexCoords(new SgTexCoordArray());
        SgTexCoordArray& texCoords = *mesh->texCoords();
        texCoords.resize(n);
        for(int i=0; i < n; ++i){
            texCoords[i] = point[i];
        }
        mesh->texCoordIndices() = mesh->triangleVertices();
    }

    mesh->updateBoundingBox();

    return mesh;
}

void VRMLToSGConverterImpl::setDefaultTextureCoordinateForElevationGrid(const SgMeshPtr& mesh, const VRMLElevationGrid* grid)
{
    float xmax = grid->xSpacing * (grid->xDimension - 1);
    float zmax = grid->zSpacing * (grid->zDimension - 1);

    mesh->setTexCoords(new SgTexCoordArray());
    SgTexCoordArray& texCoords = *mesh->texCoords();
    const SgVertexArray& vertices = *mesh->vertices();
    const int numTriangles = mesh->numTriangles();

    for(size_t i=0; i < vertices.size(); ++i){
        const Vector3f& v = vertices[i];
        texCoords.push_back(Vector2f(v.x() / xmax, v.z() / zmax));
    }
    mesh->texCoordIndices() = mesh->triangleVertices();
}

SgMeshPtr VRMLToSGConverterImpl::createMeshFromExtrusion(VRMLExtrusion* extrusion)
{
    bool isClosed = false;
    const int numSpine = extrusion->spine.size();
    const int numcross = extrusion->crossSection.size();
    if(extrusion->spine[0][0] == extrusion->spine[numSpine - 1][0] &&
       extrusion->spine[0][1] == extrusion->spine[numSpine - 1][1] &&
       extrusion->spine[0][2] == extrusion->spine[numSpine - 1][2] ){
        isClosed = true;
    }
    bool crossSectionisClosed = false;
    if(extrusion->crossSection[0][0] == extrusion->crossSection[numcross - 1][0] &&
       extrusion->crossSection[0][1] == extrusion->crossSection[numcross - 1][1] ){
        crossSectionisClosed = true;
    }

    SgMeshPtr mesh = new SgMesh;
    mesh->setVertices(new SgVertexArray());
    SgVertexArray& vertices = *mesh->vertices();
    vertices.reserve(numSpine*numcross);

    SFVec3f preZaxis(SFVec3f::Zero());
    int definedZaxis = -1;
    std::vector<SFVec3f> Yaxisarray;
    std::vector<SFVec3f> Zaxisarray;
    if(numSpine > 2){
        for(int i=0; i < numSpine; ++i){
            SFVec3f Yaxis, Zaxis;
            if(i == 0){
                if(isClosed){
                    const SFVec3f& spine1 = extrusion->spine[numSpine - 2];
                    const SFVec3f& spine2 = extrusion->spine[0];
                    const SFVec3f& spine3 = extrusion->spine[1];
                    Yaxis = spine3 - spine1;
                    Zaxis = (spine3 - spine2).cross(spine1 - spine2);
                } else {
                    const SFVec3f& spine1 = extrusion->spine[0];
                    const SFVec3f& spine2 = extrusion->spine[1];
                    const SFVec3f& spine3 = extrusion->spine[2];
                    Yaxis = spine2 - spine1;
                    Zaxis = (spine3 - spine2).cross(spine1 - spine2);
                }
            } else if(i == numSpine - 1){
                if(isClosed){
                    const SFVec3f& spine1 = extrusion->spine[numSpine - 2];
                    const SFVec3f& spine2 = extrusion->spine[0];
                    const SFVec3f& spine3 = extrusion->spine[1];
                    Yaxis = spine3 - spine1;
                    Zaxis = (spine3 - spine2).cross(spine1 - spine2);
                } else {
                    const SFVec3f& spine1 = extrusion->spine[numSpine - 3];
                    const SFVec3f& spine2 = extrusion->spine[numSpine - 2];
                    const SFVec3f& spine3 = extrusion->spine[numSpine - 1];
                    Yaxis = spine3 - spine2;
                    Zaxis = (spine3 - spine2).cross(spine1 - spine2);
                }
            } else {
                const SFVec3f& spine1 = extrusion->spine[i - 1];
                const SFVec3f& spine2 = extrusion->spine[i];
                const SFVec3f& spine3 = extrusion->spine[i + 1];
                Yaxis = spine3 - spine1;
                Zaxis = (spine3-spine2).cross(spine1-spine2);
            }
            if(!Zaxis.norm()){
                if(definedZaxis != -1)
                    Zaxis = preZaxis;
            } else {
                if(definedZaxis == -1){
                    definedZaxis = i;
                }
                preZaxis = Zaxis;
            }
            Yaxisarray.push_back(Yaxis);
            Zaxisarray.push_back(Zaxis);
        }
    } else {
        const SFVec3f Yaxis(extrusion->spine[1] - extrusion->spine[0]);
        Yaxisarray.push_back(Yaxis);
        Yaxisarray.push_back(Yaxis);
    }
    for(int i=0; i < numSpine; ++i){
        Eigen::Matrix3d Scp;
        SFVec3f y = Yaxisarray[i].normalized();
        if(definedZaxis == -1){
            SFRotation R(acos(y[1]), SFRotation::Vector3(y[2], 0.0, -y[0]));
            Scp = R.toRotationMatrix();
        } else {
            if(i < definedZaxis){
                Zaxisarray[i] = Zaxisarray[definedZaxis];
            }
            if(i && (Zaxisarray[i].dot(Zaxisarray[i - 1]) < 0.0)){
                Zaxisarray[i] *= -1.0;
            }
            SFVec3f z = Zaxisarray[i].normalized();
            SFVec3f x = y.cross(z);
            Scp << x, y, z;
        }

        const SFVec3f& spine = extrusion->spine[i];
        SFVec3f scale;
        if(extrusion->scale.size() == 1){
            scale << extrusion->scale[0][0], 0.0, extrusion->scale[0][1];
        } else {
            scale << extrusion->scale[i][0], 0.0, extrusion->scale[i][1];
        }
        SFRotation o;
        if(extrusion->orientation.size() == 1){
            o = extrusion->orientation[0];
        } else {
            o = extrusion->orientation[i];
        }

        for(int j=0; j < numcross; ++j){
            const SFVec3f crossSection(extrusion->crossSection[j][0], 0.0, extrusion->crossSection[j][1]);
            const SFVec3f v1(crossSection[0] * scale[0], 0.0, crossSection[2] * scale[2]);
            vertices.push_back((Scp * o.toRotationMatrix() * v1 + spine).cast<float>());
        }
    }

    for(int i=0; i < numSpine - 1 ; ++i){
        const int upper = i * numcross;
        const int lower = (i + 1) * numcross;
        for(int j=0; j < numcross - 1; ++j) {
            if(extrusion->ccw){
                mesh->addTriangle(j + upper, j + lower, (j + 1) + lower);
                mesh->addTriangle(j + upper, (j + 1) + lower, j + 1 + upper);
            } else {
                // upward convex triangle
                mesh->addTriangle(j + upper, (j + 1) + lower, j + lower);
                // downward convex triangle
                mesh->addTriangle(j + upper, (j + 1) + upper, j + 1 + lower);
            }
        }
    }

    int j = 0;
    if(crossSectionisClosed){
        j = 1;
    }
    if(extrusion->beginCap && !isClosed){
        triangulator.setVertices(vertices);
        polygon.clear();
        for(int i=0; i < numcross - j; ++i){
            polygon.push_back(i);
        }
        triangulator.apply(polygon);
        const vector<int>& triangles = triangulator.triangles();
        for(size_t i=0; i < triangles.size(); i += 3){
            if(extrusion->ccw){
                mesh->addTriangle(polygon[triangles[i]], polygon[triangles[i+1]], polygon[triangles[i+2]]);
            } else {
                mesh->addTriangle(polygon[triangles[i]], polygon[triangles[i+2]], polygon[triangles[i+1]]);
            }
        }
    }

    if(extrusion->endCap && !isClosed){
        triangulator.setVertices(vertices);
        polygon.clear();
        for(int i=0; i < numcross - j; ++i){
            polygon.push_back(numcross * (numSpine - 1) + i);
        }
        triangulator.apply(polygon);
        const vector<int>& triangles = triangulator.triangles();
        for(size_t i=0; i < triangles.size(); i +=3){
            if(extrusion->ccw){
                mesh->addTriangle(polygon[triangles[i]], polygon[triangles[i+2]], polygon[triangles[i+1]]);
            } else {
                mesh->addTriangle(polygon[triangles[i]], polygon[triangles[i+1]], polygon[triangles[i+2]]);
            }
        }
    }

    mesh->setSolid(extrusion->solid);

    if(isNormalGenerationEnabled){
        normalGenerator.generateNormals(mesh, extrusion->creaseAngle);
    }

    mesh->updateBoundingBox();

    return mesh;
}

void VRMLToSGConverterImpl::setDefaultTextureCoordinateForExtrusion(const SgMeshPtr& mesh, const VRMLExtrusion* extrusion)
{
    const int numSpine = extrusion->spine.size();
    const int numcross = extrusion->crossSection.size();

    mesh->setTexCoords(new SgTexCoordArray());
    SgTexCoordArray& texCoords = *mesh->texCoords();

    vector<double> s;
    vector<double> t;
    double slen = 0.0;
    s.push_back(0.0);
    for(size_t i=1; i < extrusion->crossSection.size(); ++i){
        double x = extrusion->crossSection[i][0] - extrusion->crossSection[i-1][0];
        double z = extrusion->crossSection[i][1] - extrusion->crossSection[i-1][1];
        slen += sqrt(x*x + z*z);
        s.push_back(slen);
    }
    double tlen = 0.0;
    t.push_back(0.0);
    for(size_t i=1; i < extrusion->spine.size(); ++i){
        double x = extrusion->spine[i][0] - extrusion->spine[i-1][0];
        double y = extrusion->spine[i][1] - extrusion->spine[i-1][1];
        double z = extrusion->spine[i][2] - extrusion->spine[i-1][2];
        tlen += sqrt(x*x + y*y + z*z);
        t.push_back(tlen);
    }
    for(size_t i=0; i < extrusion->spine.size(); ++i){
        Vector2f point;
        point[1] = t[i] / tlen;
        for(size_t j=0; j < extrusion->crossSection.size(); ++j){
            point[0] = s[j] / slen;
            texCoords.push_back(point);
        }
    }

    SgIndexArray& texCoordIndices = mesh->texCoordIndices();
    texCoordIndices.clear();
    const int endOfSpineVertices = (numSpine - 1) * (numcross - 1) * 2 * 3;
    texCoordIndices.resize(endOfSpineVertices);
    const SgIndexArray& triangleVertices = mesh->triangleVertices();
    copy(triangleVertices.begin(), triangleVertices.begin() + endOfSpineVertices, texCoordIndices.begin());

    int endOfBeginCapVertices = endOfSpineVertices;
    const int endOfSpineTexCoords = texCoords.size();

    if(extrusion->beginCap){
        if(extrusion->endCap){
            endOfBeginCapVertices += (triangleVertices.size() - endOfSpineVertices) / 2;
        } else {
            endOfBeginCapVertices = triangleVertices.size();
        }
        double xmin, xmax;
        double zmin, zmax;
        xmin = xmax = extrusion->crossSection[0][0];
        zmin = zmax = extrusion->crossSection[0][1];
        for(size_t i=1; i < extrusion->crossSection.size(); ++i){
            xmax = std::max(xmax, extrusion->crossSection[i][0]);
            xmin = std::min(xmin, extrusion->crossSection[i][0]);
            zmax = std::max(zmax, extrusion->crossSection[i][1]);
            zmin = std::min(xmin, extrusion->crossSection[i][1]);
        }
        float xsize = xmax - xmin;
        float zsize = zmax - zmin;
        for(int i=0; i < numcross; ++i){
            Vector2f point;
            point[0] = (extrusion->crossSection[i][0] - xmin) / xsize;
            point[1] = (extrusion->crossSection[i][1] - zmin) / zsize;
            texCoords.push_back(point);
        }
        for(int i = endOfSpineVertices; i < endOfBeginCapVertices; ++i){
            texCoordIndices.push_back(triangleVertices[i] + endOfSpineTexCoords);
        }
    }

    if(extrusion->endCap){
        double xmax, xmin;
        double zmax, zmin;
        xmin = xmax = extrusion->crossSection[0][0];
        zmin = zmax = extrusion->crossSection[0][1];
        for(size_t i=1; i < extrusion->crossSection.size(); ++i){
            xmax = std::max(xmax, extrusion->crossSection[i][0]);
            xmin = std::min(xmin, extrusion->crossSection[i][0]);
            zmax = std::max(zmax, extrusion->crossSection[i][1]);
            zmin = std::min(xmin, extrusion->crossSection[i][1]);
        }
        double xsize = xmax - xmin;
        double zsize = zmax - zmin;
        for(size_t i=0; i < extrusion->crossSection.size(); ++i){
            Vector2f point;
            point[0] = (extrusion->crossSection[i][0] - xmin) / xsize;
            point[1] = (extrusion->crossSection[i][1] - zmin) / zsize;
            texCoords.push_back(point);
        }
        const int offset = texCoords.size() - endOfSpineTexCoords;
        for(size_t i = endOfBeginCapVertices; i < triangleVertices.size(); ++i){
            texCoordIndices.push_back(triangleVertices[i] + offset);
        }
    }
}

SgMaterial* VRMLToSGConverterImpl::createMaterial(VRMLMaterial* vm)
{
    SgMaterial* material = new SgMaterial;
    material->setDiffuseColor(vm->diffuseColor);
    material->setAmbientIntensity(vm->ambientIntensity);
    material->setEmissiveColor(vm->emissiveColor);
    material->setSpecularColor(vm->specularColor);
    material->setShininess(vm->shininess);
    material->setTransparency(vm->transparency);
    return material;
}

SgTextureTransform* VRMLToSGConverterImpl::createTextureTransform(VRMLTextureTransform* tt)
{
    SgTextureTransform* textureTransform = new SgTextureTransform;
    textureTransform->setCenter(tt->center);
    textureTransform->setRotation(tt->rotation * 180.0 / PI);
    textureTransform->setScale(tt->scale);
    textureTransform->setTranslation(tt->translation);
    return textureTransform;
}

SgTexture* VRMLToSGConverterImpl::createTexture(VRMLTexture* vt)
{
    SgTexture* texture = 0;

    VRMLImageTexturePtr imageTextureNode = dynamic_node_cast<VRMLImageTexture>(vt);
    if(imageTextureNode){
        SgImagePtr image;
        SgImagePtr imageForLoading;
        const MFString& urls = imageTextureNode->url;
        for(size_t i=0; i < urls.size(); ++i){
            const string& url = urls[i];
            if(!url.empty()){
                ImagePathToSgImageMap::iterator p = imagePathToSgImageMap.find(url);
                if(p != imagePathToSgImageMap.end()){
                    image = p->second;
                    break;
                } else {
                    try {
                        if(!imageForLoading){
                            imageForLoading = new SgImage;
                        }
                        imageIO.load(imageForLoading->image(), url);
                        image = imageForLoading;
                        imagePathToSgImageMap[url] = image;
                        break;
                    } catch(const exception_base& ex){
                        putMessage(*boost::get_error_info<error_info_message>(ex));
                    }
                }
            }
        }
        if(image){
            texture = new SgTexture;
            texture->setImage(image);
            texture->setRepeat(imageTextureNode->repeatS, imageTextureNode->repeatT);
        }

    } else if(VRMLPixelTexturePtr pixelTextureNode =  dynamic_node_cast<VRMLPixelTexture>(vt)){

        const int width = pixelTextureNode->image.width;
        const int height = pixelTextureNode->image.height;
        const int nc = pixelTextureNode->image.numComponents;

        if(width > 0 && height > 0 && nc > 0){
            texture = new SgTexture;
            SgImage* image = new SgImage;
            image->setSize(width, height, nc);

            // copy the pixels in the upside-down way
            std::vector<unsigned char>& src = pixelTextureNode->image.pixels;
            unsigned char* dest = image->pixels();
            for(int i=0; i<height; ++i){
                int ii = height - i - 1;
                for(int j=0; j < width; ++j){
                    for(int l=0; l < nc; ++l)
                    dest[(i * width + j) * nc + l] = src[(ii * width +j) * nc + l];
                }
            }
            texture->setImage(image);
            texture->setRepeat(pixelTextureNode->repeatS, pixelTextureNode->repeatT);
        }
    } else {
        putMessage("MovieTextureNode is not supported");
    }

    return texture;
}

SgNode* VRMLToSGConverterImpl::convertLineSet(VRMLIndexedLineSet* vLineSet)
{
    VRMLGeometryToSgPlotMap::iterator p = vrmlGeometryToSgPlotMap.find(vLineSet);
    if(p != vrmlGeometryToSgPlotMap.end()){
        return p->second.get();
    }

    if(!vLineSet->coord || vLineSet->coord->point.empty() || vLineSet->coordIndex.empty()){
        return 0;
    }

    SgLineSet* lineSet = new SgLineSet;
    lineSet->setVertices(new SgVertexArray(vLineSet->coord->point));

    const bool hasColors = (vLineSet->color && !vLineSet->color->color.empty());
    if(hasColors){
        newColorPosToOrgColorPosMap.clear();
    }
    const bool colorPerVertex = vLineSet->colorPerVertex;

    const MFInt32& coordIndex = vLineSet->coordIndex;
    int topPosition = 0;
    int polylineIndex = 0;
    for(size_t i=0; i < coordIndex.size(); ++i){
        const int index = coordIndex[i];
        if(index < 0){
            int n = i - topPosition;
            if(n >= 2){
                --n;
                for(int j=0; j < n; ++j){
                    const int v1 = topPosition + j;
                    const int v2 = topPosition + j + 1;
                    lineSet->addLine(coordIndex[v1], coordIndex[v2]);
                    if(hasColors){
                        if(colorPerVertex){
                            newColorPosToOrgColorPosMap.push_back(v1);
                            newColorPosToOrgColorPosMap.push_back(v2);
                        } else {
                            newColorPosToOrgColorPosMap.push_back(polylineIndex);
                            newColorPosToOrgColorPosMap.push_back(polylineIndex);
                        }
                    }
                }
            }
            topPosition = i + 1;
            ++polylineIndex;
        }
    }

    if(hasColors){
        lineSet->setColors(new SgColorArray(vLineSet->color->color));
        const MFInt32& orgColorIndices = vLineSet->colorIndex;
        SgIndexArray& colorIndices = lineSet->colorIndices();
        const SgIndexArray& lineVertices = lineSet->lineVertices();
        for(size_t i=0; i < lineVertices.size(); ++i){
            int orgPos = newColorPosToOrgColorPosMap[i];
            colorIndices.push_back(orgColorIndices[orgPos]);
        }
    }

    vrmlGeometryToSgPlotMap[vLineSet] = lineSet;
    return lineSet;
}

SgNode* VRMLToSGConverterImpl::convertLightNode(VRMLLight* vlight)
{
    if(VRMLPointLight* vPointLight = dynamic_cast<VRMLSpotLight*>(vlight)){
        return createPointLight(vPointLight);
    } else if(VRMLDirectionalLight* vDirectionalLight = dynamic_cast<VRMLDirectionalLight*>(vlight)){
        return createDirectionalLight(vDirectionalLight);
    }
}

void VRMLToSGConverterImpl::setLightCommonProperties(SgLight* light, VRMLLight* vlight)
{
    light->on(vlight->on);
    light->setColor(vlight->color);
    light->setIntensity(vlight->intensity);
    light->setAmbientIntensity(vlight->ambientIntensity);
}

SgNode* VRMLToSGConverterImpl::createPointLight(VRMLPointLight* vlight)
{
    SgPointLight* light;
    if(VRMLSpotLight* vSpotLight = dynamic_cast<VRMLSpotLight*>(vlight)){
        light = createSpotLight(vSpotLight);
    } else {
        light = new SgPointLight();
    }
    light->setConstantAttenuation(vlight->attenuation[0]);
    light->setLinearAttenuation(vlight->attenuation[1]);
    light->setQuadraticAttenuation(vlight->attenuation[2]);

    setLightCommonProperties(light, vlight);

    if(vlight->location == SFVec3f::Zero()){
        return light;
    } else {
        SgPosTransform* transform = new SgPosTransform;
        transform->setTranslation(vlight->location);
        transform->addChild(light);
        return transform;
    }
}

SgSpotLight* VRMLToSGConverterImpl::createSpotLight(VRMLSpotLight* vlight)
{
    SgSpotLight* light = new SgSpotLight();

    light->setDirection(vlight->direction);
    light->setBeamWidth(vlight->beamWidth);
    light->setCutOffAngle(vlight->cutOffAngle);

    return light;
}

SgDirectionalLight* VRMLToSGConverterImpl::createDirectionalLight(VRMLDirectionalLight* vlight)
{
    SgDirectionalLight* light = new SgDirectionalLight();
    light->setDirection(vlight->direction);
    setLightCommonProperties(light, vlight);
    return light;
}

SgNode* VRMLToSGConverterImpl::readAnotherFormatFile(VRMLAnotherFormatFile* anotherFormat)
{
    BOOST_ASSERT(!anotherFormat->url.empty());
    if (boost::algorithm::iends_with(anotherFormat->url, "dae")) {
        // In the case of dae, we have to create a Sg-object directly from dae-file by using the dae-parser.
        DaeParser parser(&os());
        return parser.createScene(anotherFormat->url);

    } else if (boost::algorithm::iends_with(anotherFormat->url, "stl")) {
        // In the case of stl, we have to create a Sg-object directly from stl-file by using the stl-parser.
        STLSceneLoader loader;
        return loader.load(anotherFormat->url);

    } else {
        // File format of the other is an error.
        BOOST_ASSERT(0);
        return 0;
    }
}


// Boost.Geometry (aka GGL, Generic Geometry Library)

// Copyright (c) 2014-2021, Oracle and/or its affiliates.

// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle

// Licensed under the Boost Software License version 1.0.
// http://www.boost.org/users/license.html

#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_HAS_VALID_SELF_TURNS_HPP
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_HAS_VALID_SELF_TURNS_HPP

#include <vector>

#include <boost/core/ignore_unused.hpp>
#include <boost/range/empty.hpp>

#include <boost/geometry/algorithms/detail/is_valid/is_acceptable_turn.hpp>
#include <boost/geometry/algorithms/detail/overlay/get_turn_info.hpp>
#include <boost/geometry/algorithms/detail/overlay/turn_info.hpp>
#include <boost/geometry/algorithms/detail/overlay/self_turn_points.hpp>
#include <boost/geometry/algorithms/validity_failure_type.hpp>

#include <boost/geometry/core/assert.hpp>
#include <boost/geometry/core/point_type.hpp>

#include <boost/geometry/policies/predicate_based_interrupt_policy.hpp>
#include <boost/geometry/policies/robustness/segment_ratio_type.hpp>
#include <boost/geometry/policies/robustness/get_rescale_policy.hpp>

namespace boost { namespace geometry
{

#ifndef DOXYGEN_NO_DETAIL
namespace detail { namespace is_valid
{

template
<
    typename Geometry,
    typename CSTag // TODO: remove
>
class has_valid_self_turns
{
private:
    typedef typename point_type<Geometry>::type point_type;

    typedef typename geometry::rescale_policy_type
        <
            point_type,
            CSTag
        >::type rescale_policy_type;

public:
    typedef detail::overlay::turn_info
        <
            point_type,
            typename segment_ratio_type
                <
                    point_type,
                    rescale_policy_type
                >::type
        > turn_type;

    // returns true if all turns are valid
    template <typename Turns, typename VisitPolicy, typename Strategy>
    static inline bool apply(Geometry const& geometry,
                             Turns& turns,
                             VisitPolicy& visitor,
                             Strategy const& strategy)
    {
        boost::ignore_unused(visitor);

        rescale_policy_type robust_policy
            = geometry::get_rescale_policy<rescale_policy_type>(geometry, strategy);

        detail::overlay::stateless_predicate_based_interrupt_policy
            <
                is_acceptable_turn<Geometry>
            > interrupt_policy;

        // Calculate self-turns, skipping adjacent segments
        detail::self_get_turn_points::self_turns
            <
                false, detail::overlay::assign_null_policy
            >(geometry, strategy, robust_policy, turns, interrupt_policy,
              0, true);

        if (interrupt_policy.has_intersections)
        {
            BOOST_GEOMETRY_ASSERT(! boost::empty(turns));
            return visitor.template apply<failure_self_intersections>(turns);
        }
        else
        {
            return visitor.template apply<no_failure>();
        }
    }

    // returns true if all turns are valid
    template <typename VisitPolicy, typename Strategy>
    static inline bool apply(Geometry const& geometry, VisitPolicy& visitor, Strategy const& strategy)
    {
        std::vector<turn_type> turns;
        return apply(geometry, turns, visitor, strategy);
    }
};

}} // namespace detail::is_valid
#endif // DOXYGEN_NO_DETAIL

}} // namespace boost::geometry

#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_HAS_VALID_SELF_TURNS_HPP


//==============================================================================
//         Copyright 2003 - 2011 LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2011 LRI    UMR 8623 CNRS/Univ Paris Sud XI
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================
#ifndef BOOST_SIMD_IEEE_FUNCTIONS_SIMD_COMMON_BITFLOATING_HPP_INCLUDED
#define BOOST_SIMD_IEEE_FUNCTIONS_SIMD_COMMON_BITFLOATING_HPP_INCLUDED
#include <boost/simd/ieee/functions/bitfloating.hpp>
#include <boost/simd/include/functions/simd/bitwise_cast.hpp>
#include <boost/simd/include/functions/simd/is_gez.hpp>
#include <boost/simd/include/functions/simd/if_else.hpp>
#include <boost/simd/include/functions/simd/minus.hpp>
#include <boost/simd/include/constants/signmask.hpp>
#include <boost/dispatch/meta/as_floating.hpp>

namespace boost { namespace simd { namespace ext
{
  BOOST_SIMD_FUNCTOR_IMPLEMENTATION( boost::simd::tag::bitfloating_, tag::cpu_
                            , (A0)(X)
                            , ((simd_<arithmetic_<A0>,X>))
                            )
  {

    typedef typename dispatch::meta::as_floating<A0>::type result_type;

    BOOST_SIMD_FUNCTOR_CALL(1)
    {
      typedef typename dispatch::meta::as_floating<A0>::type type;
      type r;
      A0 s = bitwise_cast<A0>(Signmask<type>());
      r = bitwise_cast<type>(select(is_gez(a0) , a0, s-a0));
      return r;
    }
  };

  BOOST_SIMD_FUNCTOR_IMPLEMENTATION( boost::simd::tag::bitfloating_, tag::cpu_
                            , (A0)(X)
                            , ((simd_<unsigned_<A0>,X>))
                            )
  {

    typedef typename dispatch::meta::as_floating<A0>::type result_type;

    BOOST_SIMD_FUNCTOR_CALL(1)
    {
      typedef typename dispatch::meta::as_floating<A0>::type type;
      return simd::bitwise_cast<type>(a0);
    }
  };
} } }

#endif


// Copyright (c) 2011-2016 The Bitcoin Core developers
// Copyright (c) 2019 Bitcoin Association
// Distributed under the Open BSV software license, see the accompanying file LICENSE.

#include "key.h"
#include "keystore.h"
#include "policy/policy.h"
#include "script/interpreter.h"
#include "script/ismine.h"
#include "script/script.h"
#include "script/script_error.h"
#include "script/sighashtype.h"
#include "script/sign.h"
#include "taskcancellation.h"
#include "test/test_bitcoin.h"
#include "uint256.h"
#include "chainparams.h"
#include "config.h"

#include <boost/test/unit_test.hpp>

typedef std::vector<uint8_t> valtype;

BOOST_FIXTURE_TEST_SUITE(multisig_tests, BasicTestingSetup)

CScript sign_multisig(CScript scriptPubKey, std::vector<CKey> keys,
                      CMutableTransaction mutableTransaction, int whichIn) {
    uint256 hash = SignatureHash(scriptPubKey, CTransaction(mutableTransaction),
                                 whichIn, SigHashType(), Amount(0));

    CScript result;
    // CHECKMULTISIG bug workaround
    result << OP_0;
    for (const CKey &key : keys) {
        std::vector<uint8_t> vchSig;
        BOOST_CHECK(key.Sign(hash, vchSig));
        vchSig.push_back(uint8_t(SIGHASH_ALL));
        result << vchSig;
    }
    return result;
}

BOOST_AUTO_TEST_CASE(multisig_verify) {
    uint32_t flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC;

    ScriptError err;
    CKey key[4];
    Amount amount(0);
    for (int i = 0; i < 4; i++) {
        key[i].MakeNewKey(true);
    }

    CScript a_and_b;
    a_and_b << OP_2 << ToByteVector(key[0].GetPubKey())
            << ToByteVector(key[1].GetPubKey()) << OP_2 << OP_CHECKMULTISIG;

    CScript a_or_b;
    a_or_b << OP_1 << ToByteVector(key[0].GetPubKey())
           << ToByteVector(key[1].GetPubKey()) << OP_2 << OP_CHECKMULTISIG;

    CScript escrow;
    escrow << OP_2 << ToByteVector(key[0].GetPubKey())
           << ToByteVector(key[1].GetPubKey())
           << ToByteVector(key[2].GetPubKey()) << OP_3 << OP_CHECKMULTISIG;

    // Funding transaction
    CMutableTransaction txFrom;
    txFrom.vout.resize(3);
    txFrom.vout[0].scriptPubKey = a_and_b;
    txFrom.vout[1].scriptPubKey = a_or_b;
    txFrom.vout[2].scriptPubKey = escrow;

    // Spending transaction
    CMutableTransaction txTo[3];
    for (int i = 0; i < 3; i++) {
        txTo[i].vin.resize(1);
        txTo[i].vout.resize(1);
        txTo[i].vin[0].prevout = COutPoint(txFrom.GetId(), i);
        txTo[i].vout[0].nValue = Amount(1);
    }

    std::vector<CKey> keys;
    CScript s;

    // Test a AND b:
    keys.assign(1, key[0]);
    keys.push_back(key[1]);
    s = sign_multisig(a_and_b, keys, txTo[0], 0);
    auto source = task::CCancellationSource::Make();
    auto res =
        VerifyScript(
            testConfig, true,
            source->GetToken(),
            s,
            a_and_b,
            flags,
            MutableTransactionSignatureChecker(&txTo[0], 0, amount),
            &err);
    BOOST_CHECK(res.value());
    BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err));

    for (int i = 0; i < 4; i++) {
        keys.assign(1, key[i]);
        s = sign_multisig(a_and_b, keys, txTo[0], 0);
        res =
            VerifyScript(
                testConfig, true,
                source->GetToken(),
                s,
                a_and_b,
                flags,
                MutableTransactionSignatureChecker(&txTo[0], 0, amount),
                &err);
        BOOST_CHECK_MESSAGE(!res.value(), strprintf("a&b 1: %d", i));
        BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_INVALID_STACK_OPERATION,
                            ScriptErrorString(err));

        keys.assign(1, key[1]);
        keys.push_back(key[i]);
        s = sign_multisig(a_and_b, keys, txTo[0], 0);
        res =
            VerifyScript(
                testConfig, true,
                source->GetToken(),
                s,
                a_and_b,
                flags,
                MutableTransactionSignatureChecker(&txTo[0], 0, amount),
                &err);
        BOOST_CHECK_MESSAGE(!res.value(), strprintf("a&b 2: %d", i));
        BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE,
                            ScriptErrorString(err));
    }

    // Test a OR b:
    for (int i = 0; i < 4; i++) {
        keys.assign(1, key[i]);
        s = sign_multisig(a_or_b, keys, txTo[1], 0);
        if (i == 0 || i == 1) {
            res =
                VerifyScript(
                    testConfig, true,
                    source->GetToken(),
                    s,
                    a_or_b,
                    flags,
                    MutableTransactionSignatureChecker(&txTo[1], 0, amount),
                    &err);
            BOOST_CHECK_MESSAGE(res.value(), strprintf("a|b: %d", i));
            BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err));
        } else {
            res =
                VerifyScript(
                    testConfig, true,
                    source->GetToken(),
                    s,
                    a_or_b,
                    flags,
                    MutableTransactionSignatureChecker(&txTo[1], 0, amount),
                    &err);
            BOOST_CHECK_MESSAGE(!res.value(), strprintf("a|b: %d", i));
            BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE,
                                ScriptErrorString(err));
        }
    }
    s.clear();
    s << OP_0 << OP_1;
    res =
        VerifyScript(
            testConfig, true,
            source->GetToken(),
            s,
            a_or_b,
            flags,
            MutableTransactionSignatureChecker(&txTo[1], 0, amount),
            &err);
    BOOST_CHECK(!res.value());
    BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_SIG_DER, ScriptErrorString(err));

    for (int i = 0; i < 4; i++)
        for (int j = 0; j < 4; j++) {
            keys.assign(1, key[i]);
            keys.push_back(key[j]);
            s = sign_multisig(escrow, keys, txTo[2], 0);
            if (i < j && i < 3 && j < 3) {
                res =
                    VerifyScript(
                        testConfig, true,
                        source->GetToken(),
                        s,
                        escrow,
                        flags,
                        MutableTransactionSignatureChecker(&txTo[2], 0, amount),
                        &err);
                BOOST_CHECK_MESSAGE(res.value(), strprintf("escrow 1: %d %d", i, j));
                BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK,
                                    ScriptErrorString(err));
            } else {
                res =
                    VerifyScript(
                        testConfig, true,
                        source->GetToken(),
                        s,
                        escrow,
                        flags,
                        MutableTransactionSignatureChecker(&txTo[2], 0, amount),
                        &err);
                BOOST_CHECK_MESSAGE(
                    !res.value(),
                    strprintf("escrow 2: %d %d", i, j));
                BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE,
                                    ScriptErrorString(err));
            }
        }
}

BOOST_AUTO_TEST_CASE(multisig_IsStandard) {

    DummyConfig config(CBaseChainParams::MAIN);

    CKey key[4];
    for (int i = 0; i < 4; i++)
        key[i].MakeNewKey(true);

    txnouttype whichType;

    CScript a_and_b;
    a_and_b << OP_2 << ToByteVector(key[0].GetPubKey())
            << ToByteVector(key[1].GetPubKey()) << OP_2 << OP_CHECKMULTISIG;
    BOOST_CHECK(::IsStandard(config, a_and_b, 1, whichType));

    CScript a_or_b;
    a_or_b << OP_1 << ToByteVector(key[0].GetPubKey())
           << ToByteVector(key[1].GetPubKey()) << OP_2 << OP_CHECKMULTISIG;
    BOOST_CHECK(::IsStandard(config, a_or_b, 1, whichType));

    CScript escrow;
    escrow << OP_2 << ToByteVector(key[0].GetPubKey())
           << ToByteVector(key[1].GetPubKey())
           << ToByteVector(key[2].GetPubKey()) << OP_3 << OP_CHECKMULTISIG;
    BOOST_CHECK(::IsStandard(config, escrow, 1, whichType));

    CScript one_of_four;
    one_of_four << OP_1 << ToByteVector(key[0].GetPubKey())
                << ToByteVector(key[1].GetPubKey())
                << ToByteVector(key[2].GetPubKey())
                << ToByteVector(key[3].GetPubKey()) << OP_4 << OP_CHECKMULTISIG;
    BOOST_CHECK(!::IsStandard(config, one_of_four, 1, whichType));

    CScript malformed[6];
    malformed[0] << OP_3 << ToByteVector(key[0].GetPubKey())
                 << ToByteVector(key[1].GetPubKey()) << OP_2
                 << OP_CHECKMULTISIG;
    malformed[1] << OP_2 << ToByteVector(key[0].GetPubKey())
                 << ToByteVector(key[1].GetPubKey()) << OP_3
                 << OP_CHECKMULTISIG;
    malformed[2] << OP_0 << ToByteVector(key[0].GetPubKey())
                 << ToByteVector(key[1].GetPubKey()) << OP_2
                 << OP_CHECKMULTISIG;
    malformed[3] << OP_1 << ToByteVector(key[0].GetPubKey())
                 << ToByteVector(key[1].GetPubKey()) << OP_0
                 << OP_CHECKMULTISIG;
    malformed[4] << OP_1 << ToByteVector(key[0].GetPubKey())
                 << ToByteVector(key[1].GetPubKey()) << OP_CHECKMULTISIG;
    malformed[5] << OP_1 << ToByteVector(key[0].GetPubKey())
                 << ToByteVector(key[1].GetPubKey());

    for (int i = 0; i < 6; i++)
        BOOST_CHECK(!::IsStandard(config, malformed[i], 1, whichType));
}

BOOST_AUTO_TEST_CASE(multisig_Solver1) {
    // Tests Solver() that returns lists of keys that are required to satisfy a
    // ScriptPubKey
    //
    // Also tests IsMine() and ExtractDestination()
    //
    // Note: ExtractDestination for the multisignature transactions always
    // returns false for this release, even if you have one key that would
    // satisfy an (a|b) or 2-of-3 keys needed to spend an escrow transaction.
    //
    CBasicKeyStore keystore, emptykeystore, partialkeystore;
    CKey key[3];
    CTxDestination keyaddr[3];
    for (int i = 0; i < 3; i++) {
        key[i].MakeNewKey(true);
        keystore.AddKey(key[i]);
        keyaddr[i] = key[i].GetPubKey().GetID();
    }
    partialkeystore.AddKey(key[0]);

    { // P2PK
        std::vector<valtype> solutions;
        txnouttype whichType;
        CScript s;
        s << ToByteVector(key[0].GetPubKey()) << OP_CHECKSIG;
        for(bool genesisEnabled : {true, false}){
            BOOST_CHECK(Solver(s, genesisEnabled, whichType, solutions));
            BOOST_CHECK(solutions.size() == 1);
            CTxDestination addr;
            BOOST_CHECK(ExtractDestination(s, genesisEnabled, addr));
            BOOST_CHECK(addr == keyaddr[0]);
        }
        BOOST_CHECK(IsMine(keystore, s));
        BOOST_CHECK(!IsMine(emptykeystore, s));
    }
    { // P2PKH
        std::vector<valtype> solutions;
        txnouttype whichType;
        CScript s;
        s << OP_DUP << OP_HASH160 << ToByteVector(key[0].GetPubKey().GetID())
          << OP_EQUALVERIFY << OP_CHECKSIG;
        for(bool genesisEnabled : {true, false}){
            BOOST_CHECK(Solver(s, genesisEnabled, whichType, solutions));
            BOOST_CHECK(solutions.size() == 1);
            CTxDestination addr;
            BOOST_CHECK(ExtractDestination(s, genesisEnabled, addr));
            BOOST_CHECK(addr == keyaddr[0]);
        }
        BOOST_CHECK(!IsMine(emptykeystore, s));
        BOOST_CHECK(IsMine(keystore, s));
    }
    { // Data - has no address  - just test ExtractDestination() and IsMine() since we have Solver() dedicated tests in script_tests.cpp (script_Solver)
        std::vector<uint8_t> data(200, 1);
        std::vector<valtype> solutions;
        CScript opReturn = CScript() << OP_RETURN << data;
        CTxDestination addr;
        for(bool genesisEnabled : {true, false}){
            BOOST_CHECK(!ExtractDestination(opReturn, genesisEnabled, addr));
        }
        BOOST_CHECK(!IsMine(keystore, opReturn));
        CScript opFalseOpReturn = CScript() << OP_FALSE << OP_RETURN;
        for(bool genesisEnabled : {true, false}){
            BOOST_CHECK(!ExtractDestination(opReturn, genesisEnabled, addr));
        }
        BOOST_CHECK(!IsMine(keystore, opReturn));

    }
    { // Multisig
        std::vector<valtype> solutions;
        txnouttype whichType;
        CScript s;
        s << OP_2 << ToByteVector(key[0].GetPubKey())
          << ToByteVector(key[1].GetPubKey()) << OP_2 << OP_CHECKMULTISIG;
        for(bool genesisEnabled : {true, false}){
            BOOST_CHECK(Solver(s, genesisEnabled, whichType, solutions));
            BOOST_CHECK_EQUAL(solutions.size(), 4U);
            CTxDestination addr;
            BOOST_CHECK(!ExtractDestination(s, genesisEnabled, addr));
        }
        BOOST_CHECK(IsMine(keystore, s));
        BOOST_CHECK(!IsMine(emptykeystore, s));
        BOOST_CHECK(!IsMine(partialkeystore, s));
    }
    { // Multisig
        std::vector<valtype> solutions;
        txnouttype whichType;
        CScript s;
        s << OP_1 << ToByteVector(key[0].GetPubKey())
          << ToByteVector(key[1].GetPubKey()) << OP_2 << OP_CHECKMULTISIG;

        for(bool genesisEnabled : {true, false}){
            BOOST_CHECK(Solver(s, genesisEnabled, whichType, solutions));
            BOOST_CHECK_EQUAL(solutions.size(), 4U);

            std::vector<CTxDestination> addrs;
            int nRequired;
            BOOST_CHECK(ExtractDestinations(s, genesisEnabled, whichType, addrs, nRequired));
            BOOST_CHECK(addrs[0] == keyaddr[0]);
            BOOST_CHECK(addrs[1] == keyaddr[1]);
            BOOST_CHECK(nRequired == 1);

        }

        BOOST_CHECK(IsMine(keystore, s));
        BOOST_CHECK(!IsMine(emptykeystore, s));
        BOOST_CHECK(!IsMine(partialkeystore, s));
    }
    { // Multisig
        std::vector<valtype> solutions;
        txnouttype whichType;
        CScript s;
        s << OP_2 << ToByteVector(key[0].GetPubKey())
          << ToByteVector(key[1].GetPubKey())
          << ToByteVector(key[2].GetPubKey()) << OP_3 << OP_CHECKMULTISIG;
        BOOST_CHECK(Solver(s, true, whichType, solutions));
        BOOST_CHECK(solutions.size() == 5);
        BOOST_CHECK(Solver(s, false, whichType, solutions));
        BOOST_CHECK(solutions.size() == 5);

    }
}

BOOST_AUTO_TEST_CASE(multisig_Sign) {
    // Test SignSignature() (and therefore the version of Solver() that signs
    // transactions)
    CBasicKeyStore keystore;
    CKey key[4];
    for (int i = 0; i < 4; i++) {
        key[i].MakeNewKey(true);
        keystore.AddKey(key[i]);
    }

    CScript a_and_b;
    a_and_b << OP_2 << ToByteVector(key[0].GetPubKey())
            << ToByteVector(key[1].GetPubKey()) << OP_2 << OP_CHECKMULTISIG;

    CScript a_or_b;
    a_or_b << OP_1 << ToByteVector(key[0].GetPubKey())
           << ToByteVector(key[1].GetPubKey()) << OP_2 << OP_CHECKMULTISIG;

    CScript escrow;
    escrow << OP_2 << ToByteVector(key[0].GetPubKey())
           << ToByteVector(key[1].GetPubKey())
           << ToByteVector(key[2].GetPubKey()) << OP_3 << OP_CHECKMULTISIG;

    // Funding transaction
    CMutableTransaction txFrom;
    txFrom.vout.resize(3);
    txFrom.vout[0].scriptPubKey = a_and_b;
    txFrom.vout[1].scriptPubKey = a_or_b;
    txFrom.vout[2].scriptPubKey = escrow;

    // Spending transaction
    CMutableTransaction txTo[3];
    for (int i = 0; i < 3; i++) {
        txTo[i].vin.resize(1);
        txTo[i].vout.resize(1);
        txTo[i].vin[0].prevout = COutPoint(txFrom.GetId(), i);
        txTo[i].vout[0].nValue = Amount(1);
    }

    for (int i = 0; i < 3; i++) {
        BOOST_CHECK_MESSAGE(SignSignature(testConfig, keystore, true, true, CTransaction(txFrom),
                                          txTo[i], 0,
                                          SigHashType().withForkId()),
                            strprintf("SignSignature %d", i));
        BOOST_CHECK_MESSAGE(SignSignature(testConfig, keystore, true, false, CTransaction(txFrom),
                                          txTo[i], 0,
                                          SigHashType().withForkId()),
                            strprintf("SignSignature %d", i));
    }
}

BOOST_AUTO_TEST_SUITE_END()


/*-----------------------------------------------------------------------------+
Copyright (c) 2008-2009: Joachim Faulhaber
+------------------------------------------------------------------------------+
   Distributed under the Boost Software License, Version 1.0.
      (See accompanying file LICENCE.txt or copy at
           http://www.boost.org/LICENSE_1_0.txt)
+-----------------------------------------------------------------------------*/
#ifndef BOOST_ICL_TEST_TYPE_LISTS_HPP_JOFA_080916
#define BOOST_ICL_TEST_TYPE_LISTS_HPP_JOFA_080916

//#define BOOST_ICL_TEST_XINT
//#define BOOST_ICL_TEST_CHRONO

#include <boost/mpl/list.hpp>

// interval instance types
#include <boost/icl/gregorian.hpp>
#include <boost/icl/ptime.hpp>

#ifdef BOOST_ICL_TEST_XINT
#   include <boost/icl/xint.hpp>
#endif

#ifdef BOOST_ICL_TEST_CHRONO
#   define BOOST_CHRONO_EXTENSIONS
#   include <libs/icl/test/chrono/utility.hpp>

    namespace boch  = boost::chrono;
#endif

#include <boost/icl/rational.hpp>

#if(_MSC_VER < 1500 && defined(_DEBUG) ) // 1500 = MSVC-9.0
    typedef int boost_posix_time_ptime;
    typedef int boost_posix_time_duration;
    typedef int boost_gregorian_date;
    typedef int boost_gregorian_date_duration;
#else
    typedef boost::posix_time::ptime         boost_posix_time_ptime;
    typedef boost::posix_time::time_duration boost_posix_time_duration;
    typedef boost::gregorian::date           boost_gregorian_date;
    typedef boost::gregorian::date_duration  boost_gregorian_date_duration;
#endif

typedef ::boost::mpl::list<
     unsigned short, unsigned int, unsigned long
    ,short, int, long, long long
    ,float, double, long double
    ,boost::rational<int>
#ifdef BOOST_ICL_TEST_XINT
    ,boost::xint::integer
    ,boost::rational<boost::xint::integer>
#endif
#ifdef BOOST_ICL_TEST_CHRONO
    ,boch::duration<int>
    ,boch::duration<double>
    ,Now::time_point
    ,boch::time_point<Now, boch::duration<double> >
#endif
    ,boost_posix_time_ptime
    ,boost_posix_time_duration
    ,boost_gregorian_date
    ,boost_gregorian_date_duration
    ,int*
> bicremental_types;

#ifdef BOOST_ICL_TEST_CHRONO
    typedef boch::duration<long long, boost::ratio<1,113> >               duration_long2_113s;
    typedef boch::duration<int, boost::ratio<11,113> >                    duration_int_11_113s;
    typedef boch::duration<boost::rational<int>, boost::ratio<101,997> >  duration_rational_101_997s;
    typedef boch::time_point<Now, duration_int_11_113s >                  Now_time_int_11_113s;
    typedef boch::time_point<Now, boch::duration<double> >                Now_time_double;
    typedef boch::time_point<Now, boch::duration<boost::rational<int> > > Now_time_rational;
    typedef boch::time_point<Now, duration_rational_101_997s >            Now_time_rational_101_997s;

    typedef boch::duration<int>            bicremental_type_1;
    typedef boch::duration<double>         bicremental_type_2;
    typedef Now::time_point                bicremental_type_3;
    typedef Now_time_double                bicremental_type_4;
    typedef Now_time_rational              bicremental_type_5;
    typedef duration_long2_113s            bicremental_type_6;
    typedef duration_rational_101_997s     bicremental_type_7;
    typedef Now_time_rational_101_997s     bicremental_type_8;
#else
    typedef unsigned int             bicremental_type_1;
    typedef          int             bicremental_type_2;
    typedef          double          bicremental_type_3;
    typedef boost::rational<int>     bicremental_type_4;
    typedef boost_posix_time_ptime   bicremental_type_5;
    typedef          short           bicremental_type_6;
    typedef          float           bicremental_type_7;
    typedef          int*            bicremental_type_8;
#endif //BOOST_ICL_TEST_CHRONO

typedef ::boost::mpl::list<
     short, int, long, long long
    ,float, double, long double
    ,boost::rational<int>
#ifdef BOOST_ICL_TEST_XINT
    ,boost::xint::integer
    ,boost::rational<boost::xint::integer>
#endif
#ifdef BOOST_ICL_TEST_CHRONO
    ,boch::duration<int>
    ,boch::duration<float>
    ,Now::time_point
#endif
> signed_bicremental_types;

#ifdef BOOST_ICL_TEST_CHRONO
    typedef boch::duration<int>      signed_bicremental_type_1;
    typedef boch::duration<double>   signed_bicremental_type_2;
    typedef Now::time_point          signed_bicremental_type_3;
    typedef Now_time_double          signed_bicremental_type_4;
    typedef Now_time_rational        signed_bicremental_type_5;
#else
    typedef          int             signed_bicremental_type_1;
    typedef          double          signed_bicremental_type_2;
    typedef boost::rational<int>     signed_bicremental_type_3;
    typedef          short           signed_bicremental_type_4;
    typedef          float           signed_bicremental_type_5;
#endif //BOOST_ICL_TEST_CHRONO

//DBG short list for debugging
typedef ::boost::mpl::list<
    int
> debug_types;

typedef ::boost::mpl::list<
    float, double, long double
    ,boost::rational<int>
#ifdef BOOST_ICL_TEST_XINT
    ,boost::rational<boost::xint::integer>
#endif
#ifdef BOOST_ICL_TEST_CHRONO
    ,boch::duration<double>
    ,boch::time_point<Now, boch::duration<double> >
#endif
> bicremental_continuous_types;

#ifdef BOOST_ICL_TEST_CHRONO
    typedef boch::duration<double>   bicremental_continuous_type_1;
    typedef Now_time_double          bicremental_continuous_type_2;
    typedef Now_time_rational        bicremental_continuous_type_3;
#else
    typedef float                bicremental_continuous_type_1;
    typedef double               bicremental_continuous_type_2;
    typedef boost::rational<int> bicremental_continuous_type_3;
#endif // BOOST_ICL_TEST_CHRONO

typedef ::boost::mpl::list<
    unsigned short, unsigned int
    ,unsigned long, unsigned long long
    ,short, int, long, long long
#ifdef BOOST_ICL_TEST_XINT
    ,boost::xint::integer
#endif
> integral_types;

typedef int           integral_type_1;
typedef unsigned int  integral_type_2;
typedef short         integral_type_3;
typedef unsigned int  integral_type_4;

typedef ::boost::mpl::list<
    unsigned short, unsigned int
    ,unsigned long, unsigned long long
    ,short, int, long
#ifdef BOOST_ICL_TEST_XINT
    ,boost::xint::integer
#endif
#ifdef BOOST_ICL_TEST_CHRONO
    ,boch::duration<unsigned short>
    ,Now::time_point
#endif
    ,boost_posix_time_ptime
    ,boost_posix_time_duration
    ,boost_gregorian_date
    ,boost_gregorian_date_duration
    ,int*
> discrete_types;

#ifdef BOOST_ICL_TEST_CHRONO
    typedef boch::duration<int>           discrete_type_1;
    typedef duration_int_11_113s          discrete_type_2;
    typedef Now::time_point               discrete_type_3;
    typedef duration_long2_113s           discrete_type_4;
    typedef Now_time_int_11_113s          discrete_type_5;
    typedef short                         discrete_type_6;
    typedef int*                          discrete_type_7;
    typedef boost_posix_time_duration     discrete_type_8;
#else
    typedef int                           discrete_type_1;
    typedef boost_posix_time_ptime        discrete_type_2;
    typedef unsigned int                  discrete_type_3;
    typedef short                         discrete_type_4;
    typedef int*                          discrete_type_5;
    typedef boost_posix_time_duration     discrete_type_6;
    typedef boost_gregorian_date          discrete_type_7;
    typedef boost_gregorian_date_duration discrete_type_8;
#endif //BOOST_ICL_TEST_CHRONO

typedef ::boost::mpl::list<
     short, int, long
> signed_discrete_types;

#ifdef BOOST_ICL_TEST_CHRONO
    typedef Now::time_point          signed_discrete_type_1;
    typedef duration_long2_113s      signed_discrete_type_2;
    typedef Now_time_int_11_113s     signed_discrete_type_3;
#else
    typedef int                      signed_discrete_type_1;
    typedef short                    signed_discrete_type_2;
    typedef long                     signed_discrete_type_3;
#endif //BOOST_ICL_TEST_CHRONO

typedef ::boost::mpl::list<
    float, double, long double
    ,boost::rational<int>
#ifdef BOOST_ICL_TEST_XINT
    ,boost::rational<boost::xint::integer>
#endif
//JODO
//test_interval_map_shared.hpp(1190) : error C2440: 'initializing' : cannot convert from 'long double' to 'boost::chrono::duration<Rep>'
//#ifdef BOOST_ICL_TEST_CHRONO
//    ,boost::chrono::duration<long double>
//#endif
> numeric_continuous_types;

#ifdef BOOST_ICL_TEST_CHRONO
    typedef boch::duration<double>      numeric_continuous_type_1;
    typedef Now_time_double             numeric_continuous_type_2;
    typedef Now_time_rational           numeric_continuous_type_3;
    typedef duration_rational_101_997s  numeric_continuous_type_4;
#else
    typedef double                      numeric_continuous_type_1;
    typedef float                       numeric_continuous_type_2;
    typedef boost::rational<int>        numeric_continuous_type_3;
    typedef long double                 numeric_continuous_type_4;
#endif //BOOST_ICL_TEST_CHRONO

typedef ::boost::mpl::list<
    float, double, long double
    ,boost::rational<int>
#ifdef BOOST_ICL_TEST_XINT
    ,boost::rational<boost::xint::integer>
#endif
#ifdef BOOST_ICL_TEST_CHRONO
    ,boch::duration<double>
    ,boch::time_point<Now, boch::duration<double> >
#endif
    ,std::string
> continuous_types;

#ifdef BOOST_ICL_TEST_CHRONO
    typedef boch::duration<double> continuous_type_1;
    typedef Now_time_double        continuous_type_2;
    typedef Now_time_rational      continuous_type_3;
    typedef std::string            continuous_type_4;
#else
    typedef double               continuous_type_1;
    typedef float                continuous_type_2;
    typedef boost::rational<int> continuous_type_3;
    typedef std::string          continuous_type_4;
#endif //BOOST_ICL_TEST_CHRONO

typedef ::boost::mpl::list<
     unsigned short
    ,unsigned long
    ,unsigned long long
    ,short
    ,int
    ,long
    ,long long
    ,float
    ,double
    ,long double
    ,boost::rational<int>
#ifdef BOOST_ICL_TEST_XINT
    ,boost::xint::integer
#endif
#ifdef BOOST_ICL_TEST_CHRONO
    ,boch::duration<short>
    ,boch::duration<long double>
    ,Now::time_point
#endif
    ,boost_posix_time_ptime
    ,boost_posix_time_duration
    ,boost_gregorian_date
    ,boost_gregorian_date_duration
    ,int*
    ,std::string
> ordered_types;

#ifdef BOOST_ICL_TEST_CHRONO
    typedef boch::duration<int>      ordered_type_1;
    typedef boch::duration<double>   ordered_type_2;
    typedef Now::time_point          ordered_type_3;
    typedef Now_time_double          ordered_type_4;
    typedef Now_time_rational        ordered_type_5;
#else
    typedef int                      ordered_type_1;
    typedef std::string              ordered_type_2;
    typedef boost_posix_time_ptime   ordered_type_3;
    typedef boost::rational<int>     ordered_type_4;
    typedef double                   ordered_type_5;
#endif //BOOST_ICL_TEST_CHRONO

#endif