<commit_before>
//
// This source file is part of appleseed.
// Visit http://appleseedhq.net/ for additional information and resources.
//
// This software is released under the MIT license.
//
// Copyright (c) 2010-2012 Francois Beaune, Jupiter Jazz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

// Interface header.
#include "triangletree.h"

// appleseed.renderer headers.
#include "renderer/global/globallogger.h"
#include "renderer/kernel/shading/shadingpoint.h"
#include "renderer/kernel/tessellation/statictessellation.h"
#include "renderer/modeling/object/iregion.h"
#include "renderer/modeling/object/object.h"
#include "renderer/modeling/object/regionkit.h"
#include "renderer/modeling/object/triangle.h"
#include "renderer/modeling/scene/assembly.h"
#include "renderer/modeling/scene/containers.h"
#include "renderer/modeling/scene/objectinstance.h"

// appleseed.foundation headers.
#include "foundation/math/area.h"
#include "foundation/math/permutation.h"
#include "foundation/platform/timer.h"

// Standard headers.
#include <cassert>

using namespace foundation;
using namespace std;

namespace renderer
{

//
// Specializations.
//

typedef bvh::SAHPartitioner<
        TriangleTree
    > TriangleTreePartitioner;

typedef bvh::Builder<
        TriangleTree,
        TriangleTreePartitioner
    > TriangleTreeBuilder;

typedef bvh::TreeStatistics<
        TriangleTree,
        TriangleTreeBuilder
    > TriangleTreeStatistics;

//
// TriangleTree class implementation.
//

TriangleTree::Arguments::Arguments(
    const UniqueID                  triangle_tree_uid,
    const GAABB3&                   bbox,
    const Assembly&                 assembly,
    const RegionInfoVector& regions)
  : m_triangle_tree_uid(triangle_tree_uid)
  , m_bbox(bbox)
  , m_assembly(assembly)
  , m_regions(regions)
{
}

TriangleTree::TriangleTree(const Arguments& arguments)
  : m_triangle_tree_uid(arguments.m_triangle_tree_uid)
{
    // Collect triangles intersecting the bounding box of this tree.
    collect_triangles(arguments);

    // Log a progress message.
    RENDERER_LOG_INFO(
        "building triangle bvh #" FMT_UNIQUE_ID " (%s %s)...",
        arguments.m_triangle_tree_uid,
        pretty_int(size()).c_str(),
        plural(size(), "triangle").c_str());

    // Build the triangle tree.
    TriangleTreePartitioner partitioner(TriangleTreeMaxLeafSize);
    TriangleTreeBuilder builder;
    builder.build<DefaultWallclockTimer>(*this, partitioner);

    // Reorder the triangles according to the BVH ordering.
    vector<size_t> item_ordering(builder.get_item_ordering());
    assert(m_triangles.size() == item_ordering.size());
    large_item_reorder(
        &m_triangles[0],
        &item_ordering[0],
        m_triangles.size());

    // Collect and print triangle tree statistics.
    TriangleTreeStatistics tree_stats(*this, builder);
    RENDERER_LOG_DEBUG(
        "triangle bvh #" FMT_UNIQUE_ID " statistics:",
        arguments.m_triangle_tree_uid);
    tree_stats.print(global_logger());
}

TriangleTree::~TriangleTree()
{
    RENDERER_LOG_INFO(
        "deleting triangle bvh tree #" FMT_UNIQUE_ID "...",
        m_triangle_tree_uid);
}

void TriangleTree::collect_triangles(const Arguments& arguments)
{
    const size_t region_count = arguments.m_regions.size();
    for (size_t region_index = 0; region_index < region_count; ++region_index)
    {
        // Fetch the region info.
        const RegionInfo& region_info = arguments.m_regions[region_index];

        // Retrieve the object instance and its transformation.
        const ObjectInstance* object_instance =
            arguments.m_assembly.object_instances().get_by_index(
                region_info.get_object_instance_index());
        assert(object_instance);
        const Transformd& transform = object_instance->get_transform();

        // Retrieve the object.
        Object& object = object_instance->get_object();

        // Retrieve the region kit of the object.
        Access<RegionKit> region_kit(&object.get_region_kit());

        // Retrieve the region.
        const IRegion* region = (*region_kit)[region_info.get_region_index()];

        // Retrieve the tessellation of the region.
        Access<StaticTriangleTess> tess(&region->get_static_triangle_tess());

        // Collect all triangles of the region that intersect the bounding box of the tree.
        const size_t triangle_count = tess->m_primitives.size();
        for (size_t triangle_index = 0; triangle_index < triangle_count; ++triangle_index)
        {
            // Fetch the triangle.
            const Triangle& triangle = tess->m_primitives[triangle_index];

            // Retrieve object space vertices of the triangle.
            const GVector3& v0_os = tess->m_vertices[triangle.m_v0];
            const GVector3& v1_os = tess->m_vertices[triangle.m_v1];
            const GVector3& v2_os = tess->m_vertices[triangle.m_v2];

            // Transform triangle vertices to assembly space.
            const GVector3 v0 = transform.transform_point_to_parent(v0_os);
            const GVector3 v1 = transform.transform_point_to_parent(v1_os);
            const GVector3 v2 = transform.transform_point_to_parent(v2_os);

            // Calculate the (square of the) area of this triangle.
            const GScalar triangle_square_area = square_area(v0, v1, v2);

            // Ignore degenerate triangles.
            if (triangle_square_area == GScalar(0.0))
                continue;

            // Insert this triangle into the root leaf if it intersects the bounding box of the tree.
            if (intersect(arguments.m_bbox, v0, v1, v2))
            {
                const TriangleKey triangle_key(
                    region_info.get_object_instance_index(),
                    region_info.get_region_index(),
                    triangle_index);

                GAABB3 triangle_bbox;
                triangle_bbox.invalidate();
                triangle_bbox.insert(v0);
                triangle_bbox.insert(v1);
                triangle_bbox.insert(v2);

                insert(triangle_key, triangle_bbox);

                const GTriangleType triangle(v0, v1, v2);

                m_triangles.push_back(triangle);
            }
        }
    }
}

//
// TriangleTreeFactory class implementation.
//

TriangleTreeFactory::TriangleTreeFactory(
    const TriangleTree::Arguments&  arguments)
  : m_arguments(arguments)
{
}

auto_ptr<TriangleTree> TriangleTreeFactory::create()
{
    return auto_ptr<TriangleTree>(new TriangleTree(m_arguments));
}

//
// Utility class to convert a triangle to the desired precision if necessary,
// but avoid any work (in particular, no copy) if the source triangle already
// has the desired precision and can be used in-place.
//

namespace
{
    template <bool CompatibleTypes> struct TriangleReaderImpl;

    // Compatible types: no conversion or copy.
    template <> struct TriangleReaderImpl<true>
    {
        const TriangleType& m_triangle;

        explicit TriangleReaderImpl(const TriangleType& triangle)
          : m_triangle(triangle)
        {
        }
    };

    // Incompatible types: perform a conversion.
    template <> struct TriangleReaderImpl<false>
    {
        const TriangleType m_triangle;

        explicit TriangleReaderImpl(const GTriangleType& triangle)
          : m_triangle(triangle)
        {
        }
    };

    typedef TriangleReaderImpl<
                sizeof(GTriangleType::ValueType) == sizeof(TriangleType::ValueType)
            > TriangleReader;
}

//
// TriangleLeafVisitor class implementation.
//

bool TriangleLeafVisitor::visit(
    const vector<TriangleKey>&      items,
    const vector<GAABB3>&           bboxes,
    const size_t                    begin,
    const size_t                    end,
    const ShadingRay::RayType&      ray,
    const ShadingRay::RayInfoType&  ray_info,
    const double                    tmin,
    const double                    tmax,
    double&                         distance)
{
    // Sequentially intersect all triangles of this leaf.
    for (size_t i = begin; i < end; ++i)
    {
        const TriangleReader reader(m_tree.m_triangles[i]);

        // Intersect the triangle.
        double t, u, v;
        if (reader.m_triangle.intersect(m_shading_point.m_ray, t, u, v))
        {
            m_hit_triangle_index = i;
            m_shading_point.m_ray.m_tmax = t;
            m_shading_point.m_bary[0] = u;
            m_shading_point.m_bary[1] = v;
        }
    }

    // Continue traversal.
    distance = m_shading_point.m_ray.m_tmax;
    return true;
}

void TriangleLeafVisitor::read_hit_triangle_data() const
{
    if (m_hit_triangle_index != ~0)
    {
        // Record a hit.
        m_shading_point.m_hit = true;

        // Copy the triangle key.
        const TriangleKey& triangle_key = m_tree.m_items[m_hit_triangle_index];
        m_shading_point.m_object_instance_index = triangle_key.get_object_instance_index();
        m_shading_point.m_region_index = triangle_key.get_region_index();
        m_shading_point.m_triangle_index = triangle_key.get_triangle_index();

        // Compute and store the support plane of the hit triangle.
        const TriangleReader reader(m_tree.m_triangles[m_hit_triangle_index]);
        m_shading_point.m_triangle_support_plane.initialize(reader.m_triangle);
    }
}

//
// TriangleLeafProbeVisitor class implementation.
//

bool TriangleLeafProbeVisitor::visit(
    const vector<TriangleKey>&      items,
    const vector<GAABB3>&           bboxes,
    const size_t                    begin,
    const size_t                    end,
    const ShadingRay::RayType&      ray,
    const ShadingRay::RayInfoType&  ray_info,
    const double                    tmin,
    const double                    tmax,
    double&                         distance)
{
    // Sequentially intersect all triangles of this leaf.
    for (size_t i = begin; i < end; ++i)
    {
        const TriangleReader reader(m_tree.m_triangles[i]);

        // Intersect the triangle.
        if (reader.m_triangle.intersect(ray))
        {
            m_hit = true;
            return false;
        }
    }

    // Continue traversal.
    distance = ray.m_tmax;
    return true;
}

}   // namespace renderer
<commit_msg>properly handle empty triangle trees.<commit_after>
//
// This source file is part of appleseed.
// Visit http://appleseedhq.net/ for additional information and resources.
//
// This software is released under the MIT license.
//
// Copyright (c) 2010-2012 Francois Beaune, Jupiter Jazz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

// Interface header.
#include "triangletree.h"

// appleseed.renderer headers.
#include "renderer/global/globallogger.h"
#include "renderer/kernel/shading/shadingpoint.h"
#include "renderer/kernel/tessellation/statictessellation.h"
#include "renderer/modeling/object/iregion.h"
#include "renderer/modeling/object/object.h"
#include "renderer/modeling/object/regionkit.h"
#include "renderer/modeling/object/triangle.h"
#include "renderer/modeling/scene/assembly.h"
#include "renderer/modeling/scene/containers.h"
#include "renderer/modeling/scene/objectinstance.h"

// appleseed.foundation headers.
#include "foundation/math/area.h"
#include "foundation/math/permutation.h"
#include "foundation/platform/timer.h"

// Standard headers.
#include <cassert>

using namespace foundation;
using namespace std;

namespace renderer
{

//
// Specializations.
//

typedef bvh::SAHPartitioner<
        TriangleTree
    > TriangleTreePartitioner;

typedef bvh::Builder<
        TriangleTree,
        TriangleTreePartitioner
    > TriangleTreeBuilder;

typedef bvh::TreeStatistics<
        TriangleTree,
        TriangleTreeBuilder
    > TriangleTreeStatistics;

//
// TriangleTree class implementation.
//

TriangleTree::Arguments::Arguments(
    const UniqueID                  triangle_tree_uid,
    const GAABB3&                   bbox,
    const Assembly&                 assembly,
    const RegionInfoVector& regions)
  : m_triangle_tree_uid(triangle_tree_uid)
  , m_bbox(bbox)
  , m_assembly(assembly)
  , m_regions(regions)
{
}

TriangleTree::TriangleTree(const Arguments& arguments)
  : m_triangle_tree_uid(arguments.m_triangle_tree_uid)
{
    // Collect triangles intersecting the bounding box of this tree.
    collect_triangles(arguments);

    // Log a progress message.
    RENDERER_LOG_INFO(
        "building triangle bvh #" FMT_UNIQUE_ID " (%s %s)...",
        arguments.m_triangle_tree_uid,
        pretty_int(size()).c_str(),
        plural(size(), "triangle").c_str());

    // Build the triangle tree.
    TriangleTreePartitioner partitioner(TriangleTreeMaxLeafSize);
    TriangleTreeBuilder builder;
    builder.build<DefaultWallclockTimer>(*this, partitioner);

    // Reorder the triangles according to the BVH ordering.
    if (!m_triangles.empty())
    {
        vector<size_t> item_ordering(builder.get_item_ordering());
        assert(m_triangles.size() == item_ordering.size());
        large_item_reorder(
            &m_triangles[0],
            &item_ordering[0],
            m_triangles.size());
    }

    // Collect and print triangle tree statistics.
    TriangleTreeStatistics tree_stats(*this, builder);
    RENDERER_LOG_DEBUG(
        "triangle bvh #" FMT_UNIQUE_ID " statistics:",
        arguments.m_triangle_tree_uid);
    tree_stats.print(global_logger());
}

TriangleTree::~TriangleTree()
{
    RENDERER_LOG_INFO(
        "deleting triangle bvh tree #" FMT_UNIQUE_ID "...",
        m_triangle_tree_uid);
}

void TriangleTree::collect_triangles(const Arguments& arguments)
{
    const size_t region_count = arguments.m_regions.size();
    for (size_t region_index = 0; region_index < region_count; ++region_index)
    {
        // Fetch the region info.
        const RegionInfo& region_info = arguments.m_regions[region_index];

        // Retrieve the object instance and its transformation.
        const ObjectInstance* object_instance =
            arguments.m_assembly.object_instances().get_by_index(
                region_info.get_object_instance_index());
        assert(object_instance);
        const Transformd& transform = object_instance->get_transform();

        // Retrieve the object.
        Object& object = object_instance->get_object();

        // Retrieve the region kit of the object.
        Access<RegionKit> region_kit(&object.get_region_kit());

        // Retrieve the region.
        const IRegion* region = (*region_kit)[region_info.get_region_index()];

        // Retrieve the tessellation of the region.
        Access<StaticTriangleTess> tess(&region->get_static_triangle_tess());

        // Collect all triangles of the region that intersect the bounding box of the tree.
        const size_t triangle_count = tess->m_primitives.size();
        for (size_t triangle_index = 0; triangle_index < triangle_count; ++triangle_index)
        {
            // Fetch the triangle.
            const Triangle& triangle = tess->m_primitives[triangle_index];

            // Retrieve object space vertices of the triangle.
            const GVector3& v0_os = tess->m_vertices[triangle.m_v0];
            const GVector3& v1_os = tess->m_vertices[triangle.m_v1];
            const GVector3& v2_os = tess->m_vertices[triangle.m_v2];

            // Transform triangle vertices to assembly space.
            const GVector3 v0 = transform.transform_point_to_parent(v0_os);
            const GVector3 v1 = transform.transform_point_to_parent(v1_os);
            const GVector3 v2 = transform.transform_point_to_parent(v2_os);

            // Calculate the (square of the) area of this triangle.
            const GScalar triangle_square_area = square_area(v0, v1, v2);

            // Ignore degenerate triangles.
            if (triangle_square_area == GScalar(0.0))
                continue;

            // Insert this triangle into the root leaf if it intersects the bounding box of the tree.
            if (intersect(arguments.m_bbox, v0, v1, v2))
            {
                const TriangleKey triangle_key(
                    region_info.get_object_instance_index(),
                    region_info.get_region_index(),
                    triangle_index);

                GAABB3 triangle_bbox;
                triangle_bbox.invalidate();
                triangle_bbox.insert(v0);
                triangle_bbox.insert(v1);
                triangle_bbox.insert(v2);

                insert(triangle_key, triangle_bbox);

                const GTriangleType triangle(v0, v1, v2);
                m_triangles.push_back(triangle);
            }
        }
    }
}

//
// TriangleTreeFactory class implementation.
//

TriangleTreeFactory::TriangleTreeFactory(
    const TriangleTree::Arguments&  arguments)
  : m_arguments(arguments)
{
}

auto_ptr<TriangleTree> TriangleTreeFactory::create()
{
    return auto_ptr<TriangleTree>(new TriangleTree(m_arguments));
}

//
// Utility class to convert a triangle to the desired precision if necessary,
// but avoid any work (in particular, no copy) if the source triangle already
// has the desired precision and can be used in-place.
//

namespace
{
    template <bool CompatibleTypes> struct TriangleReaderImpl;

    // Compatible types: no conversion or copy.
    template <> struct TriangleReaderImpl<true>
    {
        const TriangleType& m_triangle;

        explicit TriangleReaderImpl(const TriangleType& triangle)
          : m_triangle(triangle)
        {
        }
    };

    // Incompatible types: perform a conversion.
    template <> struct TriangleReaderImpl<false>
    {
        const TriangleType m_triangle;

        explicit TriangleReaderImpl(const GTriangleType& triangle)
          : m_triangle(triangle)
        {
        }
    };

    typedef TriangleReaderImpl<
                sizeof(GTriangleType::ValueType) == sizeof(TriangleType::ValueType)
            > TriangleReader;
}

//
// TriangleLeafVisitor class implementation.
//

bool TriangleLeafVisitor::visit(
    const vector<TriangleKey>&      items,
    const vector<GAABB3>&           bboxes,
    const size_t                    begin,
    const size_t                    end,
    const ShadingRay::RayType&      ray,
    const ShadingRay::RayInfoType&  ray_info,
    const double                    tmin,
    const double                    tmax,
    double&                         distance)
{
    // Sequentially intersect all triangles of this leaf.
    for (size_t i = begin; i < end; ++i)
    {
        const TriangleReader reader(m_tree.m_triangles[i]);

        // Intersect the triangle.
        double t, u, v;
        if (reader.m_triangle.intersect(m_shading_point.m_ray, t, u, v))
        {
            m_hit_triangle_index = i;
            m_shading_point.m_ray.m_tmax = t;
            m_shading_point.m_bary[0] = u;
            m_shading_point.m_bary[1] = v;
        }
    }

    // Continue traversal.
    distance = m_shading_point.m_ray.m_tmax;
    return true;
}

void TriangleLeafVisitor::read_hit_triangle_data() const
{
    if (m_hit_triangle_index != ~0)
    {
        // Record a hit.
        m_shading_point.m_hit = true;

        // Copy the triangle key.
        const TriangleKey& triangle_key = m_tree.m_items[m_hit_triangle_index];
        m_shading_point.m_object_instance_index = triangle_key.get_object_instance_index();
        m_shading_point.m_region_index = triangle_key.get_region_index();
        m_shading_point.m_triangle_index = triangle_key.get_triangle_index();

        // Compute and store the support plane of the hit triangle.
        const TriangleReader reader(m_tree.m_triangles[m_hit_triangle_index]);
        m_shading_point.m_triangle_support_plane.initialize(reader.m_triangle);
    }
}

//
// TriangleLeafProbeVisitor class implementation.
//

bool TriangleLeafProbeVisitor::visit(
    const vector<TriangleKey>&      items,
    const vector<GAABB3>&           bboxes,
    const size_t                    begin,
    const size_t                    end,
    const ShadingRay::RayType&      ray,
    const ShadingRay::RayInfoType&  ray_info,
    const double                    tmin,
    const double                    tmax,
    double&                         distance)
{
    // Sequentially intersect all triangles of this leaf.
    for (size_t i = begin; i < end; ++i)
    {
        const TriangleReader reader(m_tree.m_triangles[i]);

        // Intersect the triangle.
        if (reader.m_triangle.intersect(ray))
        {
            m_hit = true;
            return false;
        }
    }

    // Continue traversal.
    distance = ray.m_tmax;
    return true;
}

}   // namespace renderer
<|endoftext|>


<commit_before>//
// Copyright © 2021 Arm Ltd and Contributors. All rights reserved.
// SPDX-License-Identifier: MIT
//

#pragma once

#include "IgnoreUnused.hpp"
#include "Optimization.hpp"

#include <armnn/utility/PolymorphicDowncast.hpp>

namespace armnn
{
namespace optimizations
{

class RedirectMembersToConstantInputsImpl
{
public:
    /// Search for layers with ConstantLayers as inputs. If the inputs are constant redirect the layers member
    /// variable for ConstTensors (e.g. m_weights) to the data stored in the ConstantLayer it is connected to.
    void Run(Graph& graph, Layer& layer) const
    {
        IgnoreUnused(graph);

        switch (layer.GetType())
        {
            case LayerType::BatchNormalization:
                break;
            case LayerType::Convolution2d:
                break;
            case LayerType::DepthwiseConvolution2d:
                break;
            case LayerType::DetectionPostProcess:
                break;
            case LayerType::FullyConnected:
                RedirectWeightsAndBiases<FullyConnectedLayer>(&layer);
                break;
            case LayerType::Lstm:
                break;
            case LayerType::TransposeConvolution2d:
                break;
            default:
                break;
        }
    }

protected:
    RedirectMembersToConstantInputsImpl()  = default;
    ~RedirectMembersToConstantInputsImpl() = default;

private:
    template <typename LayerT>
    static LayerT* RedirectWeightsAndBiases(Layer* layer)
    {
        LayerT* layerPtr = PolymorphicDowncast<LayerT*>(layer);

        // Loop through input slots to check for constant weights and biases layers.
        // Weights index = 1, Biases index = 2.
        for (unsigned int inputSlotIndex = 1; inputSlotIndex != layerPtr->GetNumInputSlots(); ++inputSlotIndex)
        {
            OutputSlot* outputSlot = layerPtr->GetInputSlot(inputSlotIndex).GetConnectedOutputSlot();
            if (outputSlot->GetOwningLayer().GetType() == LayerType::Constant)
            {
                // Get constant layer and redirect base layer member variables.
                ConstantLayer& constantLayer = dynamic_cast<ConstantLayer&>(outputSlot->GetOwningLayer());
                if (inputSlotIndex == 1)
                {
                    layerPtr->m_Weight = constantLayer.m_LayerOutput;
                }
                else if (inputSlotIndex == 2)
                {
                    layerPtr->m_Bias = constantLayer.m_LayerOutput;
                }
            }
        }

        return layerPtr;
    }
};

using RedirectMembersToConstantInputs = OptimizeForType<FullyConnectedLayer, RedirectMembersToConstantInputsImpl>;

} // namespace optimizations
} // namespace armnn
<commit_msg>IVGCVSW-6331 Fixup include directive in RedirectMembersToConstantInputs<commit_after>//
// Copyright © 2021 Arm Ltd and Contributors. All rights reserved.
// SPDX-License-Identifier: MIT
//

#pragma once

#include "Optimization.hpp"

#include <armnn/utility/IgnoreUnused.hpp>
#include <armnn/utility/PolymorphicDowncast.hpp>

namespace armnn
{
namespace optimizations
{

class RedirectMembersToConstantInputsImpl
{
public:
    /// Search for layers with ConstantLayers as inputs. If the inputs are constant redirect the layers member
    /// variable for ConstTensors (e.g. m_weights) to the data stored in the ConstantLayer it is connected to.
    void Run(Graph& graph, Layer& layer) const
    {
        IgnoreUnused(graph);

        switch (layer.GetType())
        {
            case LayerType::BatchNormalization:
                break;
            case LayerType::Convolution2d:
                break;
            case LayerType::DepthwiseConvolution2d:
                break;
            case LayerType::DetectionPostProcess:
                break;
            case LayerType::FullyConnected:
                RedirectWeightsAndBiases<FullyConnectedLayer>(&layer);
                break;
            case LayerType::Lstm:
                break;
            case LayerType::TransposeConvolution2d:
                break;
            default:
                break;
        }
    }

protected:
    RedirectMembersToConstantInputsImpl()  = default;
    ~RedirectMembersToConstantInputsImpl() = default;

private:
    template <typename LayerT>
    static LayerT* RedirectWeightsAndBiases(Layer* layer)
    {
        LayerT* layerPtr = PolymorphicDowncast<LayerT*>(layer);

        // Loop through input slots to check for constant weights and biases layers.
        // Weights index = 1, Biases index = 2.
        for (unsigned int inputSlotIndex = 1; inputSlotIndex != layerPtr->GetNumInputSlots(); ++inputSlotIndex)
        {
            OutputSlot* outputSlot = layerPtr->GetInputSlot(inputSlotIndex).GetConnectedOutputSlot();
            if (outputSlot->GetOwningLayer().GetType() == LayerType::Constant)
            {
                // Get constant layer and redirect base layer member variables.
                ConstantLayer& constantLayer = dynamic_cast<ConstantLayer&>(outputSlot->GetOwningLayer());
                if (inputSlotIndex == 1)
                {
                    layerPtr->m_Weight = constantLayer.m_LayerOutput;
                }
                else if (inputSlotIndex == 2)
                {
                    layerPtr->m_Bias = constantLayer.m_LayerOutput;
                }
            }
        }

        return layerPtr;
    }
};

using RedirectMembersToConstantInputs = OptimizeForType<FullyConnectedLayer, RedirectMembersToConstantInputsImpl>;

} // namespace optimizations
} // namespace armnn
<|endoftext|>


<commit_before>// doosabin_regression.cpp

// Includes
#include <algorithm>
#include <fstream>
#include <iostream>
#include <memory>
#include <string>
#include <utility>

#include "ceres/ceres.h"
#include "gflags/gflags.h"
#include "glog/logging.h"

#include <Eigen/Dense>

#include "Math/linalg.h"
#include "Ceres/compose_cost_functions.h"

#include "doosabin.h"

#include "surface.h"
#include "ceres_surface.h"

#include "doosabin_regression.pb.h"

// DooSabinSurface
class DooSabinSurface : public Surface {
 public:
  typedef doosabin::Surface<double> Surface;

  explicit DooSabinSurface(const Surface* surface)
    : surface_(surface)
  {}

  #define EVALUATE(M) \
  virtual void M(double* r, const int p, const double* u, \
                 const double* const* X) const { \
    const Eigen::Map<const Eigen::Vector2d> _u(u); \
    const linalg::MatrixOfColumnPointers<double> _X( \
      X, 3, surface_->patch_vertex_indices(p).size()); \
    Eigen::Map<Eigen::Vector3d> _r(r); \
    surface_->M(p, _u, _X, &_r); \
  }
  EVALUATE(M);
  EVALUATE(Mu);
  EVALUATE(Mv);
  EVALUATE(Muu);
  EVALUATE(Muv);
  EVALUATE(Mvv);
  #undef EVALUATE

  // `X` is only used by `surface_` to infer the geometry dimensions.
  #define EVALUATE_X(M) \
  virtual void M(double* r, const int p, const double* u) const { \
    const Eigen::Map<const Eigen::Vector2d> _u(u); \
    const Eigen::Matrix<double, 3, 0> X; \
    Eigen::Map<Eigen::VectorXd> _r( \
      r, 3 * 3 * surface_->patch_vertex_indices(p).size()); \
    surface_->M(p, _u, X, &_r); \
  }
  EVALUATE_X(Mx);
  EVALUATE_X(Mux);
  EVALUATE_X(Mvx);
  #undef EVALUATE_X

  virtual int number_of_vertices() const {
    return surface_->number_of_vertices();
  }

  virtual int number_of_faces() const {
    return surface_->number_of_faces();
  }

  virtual int number_of_patches() const {
    return surface_->number_of_patches();
  }

  virtual const std::vector<int>& patch_vertex_indices(const int p) const {
    return surface_->patch_vertex_indices(p);
  }

  virtual const std::vector<int>& adjacent_patch_indices(const int p) const {
    return surface_->adjacent_patch_indices(p);
  }

 private:
  const Surface* surface_;
};

// LoadProblemFromFile
bool LoadProblemFromFile(const std::string& input_path,
                         Eigen::MatrixXd* Y,
                         std::vector<int>* raw_face_array,
                         Eigen::MatrixXd* X,
                         Eigen::VectorXi* p,
                         Eigen::MatrixXd* U) {
  doosabin_regression::Problem problem;
  {
    std::fstream input(input_path, std::ios::in | std::ios::binary);
    if (!input) {
      LOG(ERROR) << "File \"" << input_path << "\" not found.";
      return false;
    } else if (!problem.ParseFromIstream(&input)) {
      LOG(ERROR) << "Failed to parse input problem.";
      return false;
    }
  }

  #define LOAD_MATRIX(SRC, DST, DIM) { \
    auto& _SRC = problem.SRC(); \
    CHECK_EQ(_SRC.size() % DIM, 0); \
    DST->resize(DIM, _SRC.size() / DIM); \
    std::copy(_SRC.data(), _SRC.data() + _SRC.size(), DST->data()); \
  }

  #define LOAD_VECTOR(SRC, DST) { \
    auto& _SRC = problem.SRC(); \
    DST->resize(_SRC.size()); \
    std::copy(_SRC.data(), _SRC.data() + _SRC.size(), DST->data()); \
  }

  LOAD_MATRIX(y_3, Y, 3);
  LOAD_VECTOR(t, raw_face_array);
  LOAD_MATRIX(x_3, X, 3);
  LOAD_VECTOR(p, p);
  LOAD_MATRIX(u_2, U, 2);

  #undef LOAD_MATRIX
  #undef LOAD_VECTOR

  return true;
}

// UpdateProblemToFile
bool UpdateProblemToFile(const std::string& input_path,
                         const std::string& output_path,
                         const Eigen::MatrixXd& X,
                         const Eigen::VectorXi& p,
                         const Eigen::MatrixXd& U) {
  doosabin_regression::Problem problem;
  {
    std::fstream input(input_path, std::ios::in | std::ios::binary);
    if (!input) {
      LOG(ERROR) << "File \"" << input_path << "\" not found.";
      return false;
    } else if (!problem.ParseFromIstream(&input)) {
      LOG(ERROR) << "Failed to parse input problem.";
      return false;
    }
  }

  std::copy(X.data(), X.data() + X.rows() * X.cols(),
            problem.mutable_x_3()->begin());
  std::copy(p.data(), p.data() + p.size(),
            problem.mutable_p()->begin());
  std::copy(U.data(), U.data() + U.rows() * U.cols(),
            problem.mutable_u_2()->begin());

  {
    std::fstream output(output_path, std::ios::out |
                                     std::ios::trunc |
                                     std::ios::binary);
    if (!problem.SerializeToOstream(&output)) {
      LOG(ERROR) << "Failed to write to \"" << output_path << "\"";
      return false;
    }
  }

  return true;
}

// PositionErrorFunctor
class PositionErrorFunctor {
 public:
  template <typename M>
  PositionErrorFunctor(const M& m0, const double& sqrt_w = 1.0)
    : m0_(m0), sqrt_w_(sqrt_w)
  {}

  template <typename T>
  bool operator()(const T* m, T* e) const {
    e[0] = T(sqrt_w_) * (T(m0_[0]) - m[0]);
    e[1] = T(sqrt_w_) * (T(m0_[1]) - m[1]);
    e[2] = T(sqrt_w_) * (T(m0_[2]) - m[2]);
    return true;
  }

 private:
  Eigen::Vector3d m0_;
  const double sqrt_w_;
};

// PairwiseErrorFunctor
class PairwiseErrorFunctor {
 public:
  PairwiseErrorFunctor(const double& sqrt_w = 1.0)
    : sqrt_w_(sqrt_w)
  {}

  template <typename T>
  bool operator()(const T* x0, const T* x1, T* e) const {
    e[0] = T(sqrt_w_) * (x1[0] - x0[0]);
    e[1] = T(sqrt_w_) * (x1[1] - x0[1]);
    e[2] = T(sqrt_w_) * (x1[2] - x0[2]);
    return true;
  }

 private:
  const double sqrt_w_;
};

// main
DEFINE_int32(max_num_iterations, 1000, "Maximum number of iterations.");
DEFINE_double(function_tolerance, 0.0,
  "Minimizer terminates when "
  "(new_cost - old_cost) < function_tolerance * old_cost");
DEFINE_double(gradient_tolerance, 0.0,
  "Minimizer terminates when "
  "max_i |gradient_i| < gradient_tolerance * max_i|initial_gradient_i|");
DEFINE_double(parameter_tolerance, 0.0,
  "Minimizer terminates when "
  "|step|_2 <= parameter_tolerance * ( |x|_2 +  parameter_tolerance)");
DEFINE_double(min_trust_region_radius, 1e-9,
  "Minimizer terminates when the trust region radius becomes smaller than "
  "this value");

DEFINE_int32(num_threads, 1, "Number of threads to use for Jacobian evaluation.");
DEFINE_int32(num_linear_solver_threads, 1, "Number of threads to use for the linear solver.");

int main(int argc, char** argv) {
 GOOGLE_PROTOBUF_VERIFY_VERSION;

  google::ParseCommandLineFlags(&argc, &argv, true);
  google::InitGoogleLogging(argv[0]);

  if (argc < 4) {
    LOG(ERROR) << "Usage: " << argv[0] << " input_path lambda output_path";
    return -1;
  }

  // Load the problem data ...
  Eigen::MatrixXd Y;
  std::vector<int> raw_face_array;
  Eigen::MatrixXd X;
  Eigen::VectorXi p;
  Eigen::MatrixXd U;
  if (!LoadProblemFromFile(argv[1], &Y, &raw_face_array, &X, &p, &U)) {
    return -1;
  }

  // ... and check consistency of dimensions.
  typedef Eigen::DenseIndex Index;
  const Index num_data_points = Y.cols();
  CHECK_EQ(num_data_points, p.size());
  CHECK_EQ(num_data_points, U.cols());

  doosabin::GeneralMesh T(std::move(raw_face_array));
  doosabin::Surface<double> surface(T);
  DooSabinSurface doosabin_surface(&surface);
  CHECK_EQ(surface.number_of_vertices(), X.cols());

  // Set lambda.
  const double lambda = atof(argv[2]);

  // Setup `problem`.
  ceres::Problem problem;

  // Encode patch indices into the preimage positions.
  for (Index i = 0; i < num_data_points; ++i) {
    EncodePatchIndexInPlace(U.data() + 2 * i, p[i]);
  }

  // Add error residuals.
  std::vector<double*> parameter_blocks;
  parameter_blocks.reserve(1 + surface.number_of_vertices());
  parameter_blocks.push_back(nullptr); // `u`.
  for (Index i = 0; i < X.cols(); ++i) {
    parameter_blocks.push_back(X.data() + 3 * i);
  }

  for (Index i = 0; i < num_data_points; ++i) {
    parameter_blocks[0] = U.data() + 2 * i;

    auto position_error = new ceres_utility::GeneralCostFunctionComposition(
      new ceres::AutoDiffCostFunction<PositionErrorFunctor, 3, 3>(
        new PositionErrorFunctor(Y.col(i), 1.0 / sqrt(num_data_points))));
    position_error->AddInputCostFunction(
      new SurfacePositionCostFunction(&doosabin_surface),
      parameter_blocks);
    position_error->Finalise();

    problem.AddResidualBlock(position_error, nullptr, parameter_blocks);
  }

  // Add regularisation residuals.
  std::set<std::pair<int, int>> edges;
  for (auto& half_edge : T.iterate_half_edges()) {
    int i = half_edge.first, j = half_edge.second;
    if (i > j) {
      std::swap(i, j);
    }
    auto full_edge = std::make_pair(i, j);
    if (edges.count(full_edge)) {
      continue;
    }

    problem.AddResidualBlock(new ceres::AutoDiffCostFunction<PairwiseErrorFunctor, 3, 3, 3>(
      new PairwiseErrorFunctor(sqrt(lambda))),
      nullptr,
      X.data() + 3 * i,
      X.data() + 3 * j);

    edges.insert(full_edge);
  }

  typedef doosabin::SurfaceWalker<double> DooSabinWalker;
  DooSabinWalker walker(&surface);
  for (Index i = 0; i < num_data_points; ++i) {
    problem.SetParameterization(
      U.data() + 2 * i,
      new PreimageLocalParameterisation<DooSabinWalker, Eigen::MatrixXd>(
        &walker, &X));
  }

  // Initialise the solver options.
  std::cout << "Solver options:" << std::endl;
  ceres::Solver::Options options;

  options.num_threads = FLAGS_num_threads;
  options.num_linear_solver_threads = FLAGS_num_linear_solver_threads;

  // Disable auto-scaling and set LM to be additive (instead of multiplicative).
  options.min_lm_diagonal = 1.0;
  options.max_lm_diagonal = 1.0;
  options.jacobi_scaling = false;

  options.minimizer_progress_to_stdout = FLAGS_v >= 1;

  // Termination criteria.
  options.max_num_iterations = FLAGS_max_num_iterations;
  std::cout << " max_num_iterations: " << options.max_num_iterations <<
               std::endl;
  options.max_num_consecutive_invalid_steps = FLAGS_max_num_iterations;

  options.function_tolerance = FLAGS_function_tolerance;
  options.gradient_tolerance = FLAGS_gradient_tolerance;
  options.parameter_tolerance = FLAGS_parameter_tolerance;
  options.min_trust_region_radius = FLAGS_min_trust_region_radius;

  // Solver selection.
  options.dynamic_sparsity = true;

  // `update_state_every_iteration` is required by
  // `PreimageLocalParameterisation` instances.
  options.update_state_every_iteration = true;

  // Solve.
  std::cout << "Solving ..." << std::endl;
  ceres::Solver::Summary summary;
  ceres::Solve(options, &problem, &summary);

  std::cout << summary.FullReport() << std::endl;

  // Decode patch indices.
  for (Index i = 0; i < num_data_points; ++i) {
    p[i] = DecodePatchIndexInPlace(U.data() + 2 * i);
  }

  // Save.
  if (!UpdateProblemToFile(argv[1], argv[3], X, p, U)) {
    return -1;
  }
  std::cout << "Output: " << argv[3] << std::endl;

  return 0;
}
<commit_msg>Minor (mostly cosmetic) changes to src/doosabin_regression.cpp<commit_after>// doosabin_regression.cpp

// Includes
#include <algorithm>
#include <fstream>
#include <iostream>
#include <memory>
#include <string>
#include <utility>

#include "ceres/ceres.h"
#include "gflags/gflags.h"
#include "glog/logging.h"

#include <Eigen/Dense>

#include "Math/linalg.h"
#include "Ceres/compose_cost_functions.h"

#include "doosabin.h"

#include "ceres_surface.h"
#include "surface.h"

#include "doosabin_regression.pb.h"

// DooSabinSurface
class DooSabinSurface : public Surface {
 public:
  typedef doosabin::Surface<double> Surface;

  explicit DooSabinSurface(const Surface* surface)
    : surface_(surface)
  {}

  #define EVALUATE(M) \
  virtual void M(double* r, const int p, const double* u, \
                 const double* const* X) const { \
    const Eigen::Map<const Eigen::Vector2d> _u(u); \
    const linalg::MatrixOfColumnPointers<double> _X( \
      X, 3, surface_->patch_vertex_indices(p).size()); \
    Eigen::Map<Eigen::Vector3d> _r(r); \
    surface_->M(p, _u, _X, &_r); \
  }
  EVALUATE(M);
  EVALUATE(Mu);
  EVALUATE(Mv);
  EVALUATE(Muu);
  EVALUATE(Muv);
  EVALUATE(Mvv);
  #undef EVALUATE

  // `X` is only used by `surface_` to infer the geometry dimensions.
  #define EVALUATE_X(M) \
  virtual void M(double* r, const int p, const double* u) const { \
    const Eigen::Map<const Eigen::Vector2d> _u(u); \
    const Eigen::Matrix<double, 3, 0> X; \
    Eigen::Map<Eigen::VectorXd> _r( \
      r, 3 * 3 * surface_->patch_vertex_indices(p).size()); \
    surface_->M(p, _u, X, &_r); \
  }
  EVALUATE_X(Mx);
  EVALUATE_X(Mux);
  EVALUATE_X(Mvx);
  #undef EVALUATE_X

  virtual int number_of_vertices() const {
    return surface_->number_of_vertices();
  }

  virtual int number_of_faces() const {
    return surface_->number_of_faces();
  }

  virtual int number_of_patches() const {
    return surface_->number_of_patches();
  }

  virtual const std::vector<int>& patch_vertex_indices(const int p) const {
    return surface_->patch_vertex_indices(p);
  }

  virtual const std::vector<int>& adjacent_patch_indices(const int p) const {
    return surface_->adjacent_patch_indices(p);
  }

 private:
  const Surface* surface_;
};

// LoadProblemFromFile
bool LoadProblemFromFile(const std::string& input_path,
                         Eigen::MatrixXd* Y,
                         std::vector<int>* raw_face_array,
                         Eigen::MatrixXd* X,
                         Eigen::VectorXi* p,
                         Eigen::MatrixXd* U) {
  doosabin_regression::Problem problem;
  {
    std::fstream input(input_path, std::ios::in | std::ios::binary);
    if (!input) {
      LOG(ERROR) << "File \"" << input_path << "\" not found.";
      return false;
    } else if (!problem.ParseFromIstream(&input)) {
      LOG(ERROR) << "Failed to parse input problem.";
      return false;
    }
  }

  #define LOAD_MATRIX(SRC, DST, DIM) { \
    auto& _SRC = problem.SRC(); \
    CHECK_EQ(_SRC.size() % DIM, 0); \
    DST->resize(DIM, _SRC.size() / DIM); \
    std::copy(_SRC.data(), _SRC.data() + _SRC.size(), DST->data()); \
  }

  #define LOAD_VECTOR(SRC, DST) { \
    auto& _SRC = problem.SRC(); \
    DST->resize(_SRC.size()); \
    std::copy(_SRC.data(), _SRC.data() + _SRC.size(), DST->data()); \
  }

  LOAD_MATRIX(y_3, Y, 3);
  LOAD_VECTOR(t, raw_face_array);
  LOAD_MATRIX(x_3, X, 3);
  LOAD_VECTOR(p, p);
  LOAD_MATRIX(u_2, U, 2);

  #undef LOAD_MATRIX
  #undef LOAD_VECTOR

  return true;
}

// UpdateProblemToFile
bool UpdateProblemToFile(const std::string& input_path,
                         const std::string& output_path,
                         const Eigen::MatrixXd& X,
                         const Eigen::VectorXi& p,
                         const Eigen::MatrixXd& U) {
  doosabin_regression::Problem problem;
  {
    std::fstream input(input_path, std::ios::in | std::ios::binary);
    if (!input) {
      LOG(ERROR) << "File \"" << input_path << "\" not found.";
      return false;
    } else if (!problem.ParseFromIstream(&input)) {
      LOG(ERROR) << "Failed to parse input problem.";
      return false;
    }
  }

  std::copy(X.data(), X.data() + X.rows() * X.cols(),
            problem.mutable_x_3()->begin());
  std::copy(p.data(), p.data() + p.size(),
            problem.mutable_p()->begin());
  std::copy(U.data(), U.data() + U.rows() * U.cols(),
            problem.mutable_u_2()->begin());

  {
    std::fstream output(output_path, std::ios::out |
                                     std::ios::trunc |
                                     std::ios::binary);
    if (!problem.SerializeToOstream(&output)) {
      LOG(ERROR) << "Failed to write to \"" << output_path << "\"";
      return false;
    }
  }

  return true;
}

// PositionErrorFunctor
class PositionErrorFunctor {
 public:
  template <typename M>
  PositionErrorFunctor(const M& m0, const double& sqrt_w = 1.0)
    : m0_(m0), sqrt_w_(sqrt_w)
  {}

  template <typename T>
  bool operator()(const T* m, T* e) const {
    e[0] = T(sqrt_w_) * (T(m0_[0]) - m[0]);
    e[1] = T(sqrt_w_) * (T(m0_[1]) - m[1]);
    e[2] = T(sqrt_w_) * (T(m0_[2]) - m[2]);
    return true;
  }

 private:
  Eigen::Vector3d m0_;
  const double sqrt_w_;
};

// PairwiseErrorFunctor
class PairwiseErrorFunctor {
 public:
  PairwiseErrorFunctor(const double& sqrt_w = 1.0)
    : sqrt_w_(sqrt_w)
  {}

  template <typename T>
  bool operator()(const T* x0, const T* x1, T* e) const {
    e[0] = T(sqrt_w_) * (x1[0] - x0[0]);
    e[1] = T(sqrt_w_) * (x1[1] - x0[1]);
    e[2] = T(sqrt_w_) * (x1[2] - x0[2]);
    return true;
  }

 private:
  const double sqrt_w_;
};

// main
DEFINE_int32(max_num_iterations, 1000, "Maximum number of iterations.");
DEFINE_double(function_tolerance, 0.0,
  "Minimizer terminates when "
  "(new_cost - old_cost) < function_tolerance * old_cost");
DEFINE_double(gradient_tolerance, 0.0,
  "Minimizer terminates when "
  "max_i |gradient_i| < gradient_tolerance * max_i|initial_gradient_i|");
DEFINE_double(parameter_tolerance, 0.0,
  "Minimizer terminates when "
  "|step|_2 <= parameter_tolerance * ( |x|_2 +  parameter_tolerance)");
DEFINE_double(min_trust_region_radius, 1e-9,
  "Minimizer terminates when the trust region radius becomes smaller than "
  "this value");

DEFINE_int32(num_threads, 1,
  "Number of threads to use for Jacobian evaluation.");
DEFINE_int32(num_linear_solver_threads, 1,
  "Number of threads to use for the linear solver.");

int main(int argc, char** argv) {
 GOOGLE_PROTOBUF_VERIFY_VERSION;

  google::ParseCommandLineFlags(&argc, &argv, true);
  google::InitGoogleLogging(argv[0]);

  if (argc < 4) {
    LOG(ERROR) << "Usage: " << argv[0] << " input_path lambda output_path";
    return -1;
  }

  // Load the problem data ...
  Eigen::MatrixXd Y;
  std::vector<int> raw_face_array;
  Eigen::MatrixXd X;
  Eigen::VectorXi p;
  Eigen::MatrixXd U;
  if (!LoadProblemFromFile(argv[1], &Y, &raw_face_array, &X, &p, &U)) {
    return -1;
  }

  // ... and check consistency of dimensions.
  typedef Eigen::DenseIndex Index;
  const Index num_data_points = Y.cols();
  CHECK_GT(num_data_points, 0);
  CHECK_EQ(num_data_points, p.size());
  CHECK_EQ(num_data_points, U.cols());

  doosabin::GeneralMesh T(std::move(raw_face_array));
  CHECK_GT(T.number_of_faces(), 0);
  doosabin::Surface<double> surface(T);
  DooSabinSurface doosabin_surface(&surface);
  CHECK_EQ(surface.number_of_vertices(), X.cols());

  // `lambda` is the regularisation weight.
  const double lambda = atof(argv[2]);

  // Setup `problem`.
  ceres::Problem problem;

  // Encode patch indices into the preimage positions.
  for (Index i = 0; i < num_data_points; ++i) {
    EncodePatchIndexInPlace(U.data() + 2 * i, p[i]);
  }

  // Add error residuals.
  std::vector<double*> parameter_blocks;
  parameter_blocks.reserve(1 + surface.number_of_vertices());
  parameter_blocks.push_back(nullptr); // `u`.
  for (Index i = 0; i < X.cols(); ++i) {
    parameter_blocks.push_back(X.data() + 3 * i);
  }

  std::unique_ptr<ceres::CostFunction> surface_position(
    new SurfacePositionCostFunction(&doosabin_surface));

  for (Index i = 0; i < num_data_points; ++i) {
    parameter_blocks[0] = U.data() + 2 * i;

    auto position_error = new ceres_utility::GeneralCostFunctionComposition(
      new ceres::AutoDiffCostFunction<PositionErrorFunctor, 3, 3>(
        new PositionErrorFunctor(Y.col(i), 1.0 / sqrt(num_data_points))));
    position_error->AddInputCostFunction(surface_position.get(),
                                         parameter_blocks,
                                         false);
    position_error->Finalise();

    problem.AddResidualBlock(position_error, nullptr, parameter_blocks);
  }

  // Add regularisation residuals.
  // Note: `problem` takes ownership of `pairwise_error`.
  auto pairwise_error =
    new ceres::AutoDiffCostFunction<PairwiseErrorFunctor, 3, 3, 3>(
      new PairwiseErrorFunctor(sqrt(lambda)));

  std::set<std::pair<int, int>> full_edges;
  for (std::pair<int, int> e : T.iterate_half_edges()) {
    if (e.first > e.second) {
      std::swap(e.first, e.second);
    }
    if (!full_edges.count(e)) {
      problem.AddResidualBlock(pairwise_error,
                               nullptr,
                               X.data() + 3 * e.first,
                               X.data() + 3 * e.second);
      full_edges.insert(e);
    }
  }

  // Set preimage parameterisations so that they are updated using
  // `doosabin::SurfaceWalker<double>` and NOT Euclidean addition.
  // Note: `problem` takes ownership of `local_parameterisation`.
  typedef doosabin::SurfaceWalker<double> DooSabinWalker;
  DooSabinWalker walker(&surface);
  auto local_parameterisation =
    new PreimageLocalParameterisation<DooSabinWalker, Eigen::MatrixXd>(
      &walker, &X);

  for (Index i = 0; i < num_data_points; ++i) {
    problem.SetParameterization(U.data() + 2 * i, local_parameterisation);
  }

  // Initialise the solver options.
  std::cout << "Solver options:" << std::endl;
  ceres::Solver::Options options;

  options.num_threads = FLAGS_num_threads;
  options.num_linear_solver_threads = FLAGS_num_linear_solver_threads;

  // Disable auto-scaling and set LM to be additive (instead of multiplicative).
  options.min_lm_diagonal = 1.0;
  options.max_lm_diagonal = 1.0;
  options.jacobi_scaling = false;

  options.minimizer_progress_to_stdout = FLAGS_v >= 1;

  // Termination criteria.
  options.max_num_iterations = FLAGS_max_num_iterations;
  std::cout << " max_num_iterations: " << options.max_num_iterations <<
               std::endl;
  options.max_num_consecutive_invalid_steps = FLAGS_max_num_iterations;

  options.function_tolerance = FLAGS_function_tolerance;
  options.gradient_tolerance = FLAGS_gradient_tolerance;
  options.parameter_tolerance = FLAGS_parameter_tolerance;
  options.min_trust_region_radius = FLAGS_min_trust_region_radius;

  // Solver selection.
  options.dynamic_sparsity = true;

  // `update_state_every_iteration` is required by
  // `PreimageLocalParameterisation` instances.
  options.update_state_every_iteration = true;

  // Solve.
  std::cout << "Solving ..." << std::endl;
  ceres::Solver::Summary summary;
  ceres::Solve(options, &problem, &summary);

  std::cout << summary.FullReport() << std::endl;

  // Decode patch indices.
  for (Index i = 0; i < num_data_points; ++i) {
    p[i] = DecodePatchIndexInPlace(U.data() + 2 * i);
  }

  // Save.
  if (!UpdateProblemToFile(argv[1], argv[3], X, p, U)) {
    return -1;
  }
  std::cout << "Output: " << argv[3] << std::endl;

  return 0;
}
<|endoftext|>


<commit_before>/*
 * libMaia - maiaObject.cpp
 * Copyright (c) 2003 Frerich Raabe <raabe@kde.org> and
 *                    Ian Reinhart Geiser <geiseri@kde.org>
 * Copyright (c) 2007 Sebastian Wiedenroth <wiedi@frubar.net>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "maiaObject.h"

MaiaObject::MaiaObject(QObject* parent) : QObject(parent){
	QDomImplementation::setInvalidDataPolicy(QDomImplementation::DropInvalidChars);
}

QDomElement MaiaObject::toXml(QVariant arg) {

	//dummy document
	QDomDocument doc;
	//value element, we need this in each case
	QDomElement tagValue = doc.createElement("value");

	switch(arg.type()) {
	case QVariant::String: {

		QDomElement tagString = doc.createElement("string");
		QDomText textString = doc.createTextNode(arg.toString());

		tagValue.appendChild(tagString);
		tagString.appendChild(textString);

		return tagValue;

	} case QVariant::Int: {

		QDomElement tagInt = doc.createElement("int");
		QDomText textInt = doc.createTextNode(QString::number(arg.toInt()));

		tagValue.appendChild(tagInt);
		tagInt.appendChild(textInt);

		return tagValue;

	} case QVariant::Double: {

		QDomElement tagDouble = doc.createElement("double");
		QDomText textDouble = doc.createTextNode(QString::number(arg.toDouble()));

		tagValue.appendChild(tagDouble);
		tagDouble.appendChild(textDouble);

		return tagValue;

	} case QVariant::Bool: {

		QString textValue = arg.toBool() ? "true" : "false";

		QDomElement tag = doc.createElement("boolean");
		QDomText text = doc.createTextNode(textValue);

		tagValue.appendChild(tag);
		tag.appendChild(text);

		return tagValue;

	} case QVariant::ByteArray: {

		QString textValue = arg.toByteArray().toBase64();

		QDomElement tag = doc.createElement("base64");
		QDomText text = doc.createTextNode(textValue);

		tagValue.appendChild(tag);
		tag.appendChild(text);

		return tagValue;

	} case QVariant::DateTime: {

		QString textValue = arg.toDateTime().toString("yyyyMMddThh:mm:ss");

		QDomElement tag = doc.createElement("datetime.iso8601");
		QDomText text = doc.createTextNode(textValue);

		tagValue.appendChild(tag);
		tag.appendChild(text);

		return tagValue;

	} case QVariant::List: {

		QDomElement tagArray = doc.createElement("array");
		QDomElement tagData = doc.createElement("data");
		tagArray.appendChild(tagData);
		tagValue.appendChild(tagArray);

		const QList<QVariant> args = arg.toList();
		for(int i = 0; i < args.size(); ++i) {
			tagData.appendChild(toXml(args.at(i)));
		}

		return tagValue;

	} case QVariant::Map: {

		QDomElement tagStruct = doc.createElement("struct");
		QDomElement member;
		QDomElement name;

		tagValue.appendChild(tagStruct);

		QMap<QString, QVariant> map = arg.toMap();
		QMapIterator<QString, QVariant> i(map);
		while(i.hasNext()) {
			i.next();

			member = doc.createElement("member");
			name = doc.createElement("name");

			// (key) -> name -> member -> struct
			tagStruct.appendChild(member);
			member.appendChild(name);
			name.appendChild(doc.createTextNode(i.key()));

			// add variables by recursion
			member.appendChild(toXml(i.value()));
		}

		return tagValue;

	} default:
		qDebug() << "Failed to marshal unknown variant type: " << arg.type() << endl;
	}
	return QDomElement(); //QString::null;
}

QVariant MaiaObject::fromXml(const QDomElement &elem) {
	if(elem.tagName().toLower() != "value") {
		return QVariant();
	}

	const QDomElement typeElement = elem.firstChild().toElement();
	const QString typeName = typeElement.tagName().toLower();

	if(typeName == "string")
		return QVariant(typeElement.text());
	else if(typeName == "i4" || typeName == "int")
		return QVariant(typeElement.text().toInt());
	else if(typeName == "double")
		return QVariant(typeElement.text().toDouble());
	else if (typeName == "boolean") {
		if(typeElement.text().toLower() == "true" || typeElement.text() == "1")
			return QVariant(true);
		else
			return QVariant(false);
	} else if(typeName == "base64")
		return QVariant(QByteArray::fromBase64( typeElement.text().toLatin1()));
	else if(typeName == "datetime" || typeName == "datetime.iso8601")
		return QVariant(QDateTime::fromString(typeElement.text(), "yyyyMMddThh:mm:ss"));
	else if ( typeName == "array" ) {
		QList<QVariant> values;
		QDomNode valueNode = typeElement.firstChild().firstChild();
		while(!valueNode.isNull()) {
			values << fromXml(valueNode.toElement());
			valueNode = valueNode.nextSibling();
		}
		return QVariant(values);
	}
	else if ( typeName == "struct" ) {
		QMap<QString, QVariant> map;
		QDomNode memberNode = typeElement.firstChild();
		while(!memberNode.isNull())	{
			const QString key = memberNode.toElement().elementsByTagName("name").item(0).toElement().text();
			const QVariant data = fromXml(memberNode.toElement().elementsByTagName("value").item(0).toElement());
			map[key] = data;
			memberNode = memberNode.nextSibling();
		}
		return QVariant(map);
	} else {
		qDebug() << "Cannot demarshal unknown type " << typeElement.tagName().toLower();
	}
	return QVariant();
}

QString MaiaObject::prepareCall(QString method, QList<QVariant> args) {

	QDomDocument doc;

	QDomProcessingInstruction header = doc.createProcessingInstruction( "xml", QString("version=\"1.0\" encoding=\"UTF-8\"" ));
	doc.appendChild(header);

	QDomElement methodCall = doc.createElement("methodCall");
	QDomElement methodName = doc.createElement("methodName");
	QDomElement params = doc.createElement("params");
	QDomElement param;

	doc.appendChild(methodCall);
	methodCall.appendChild(methodName);
	methodName.appendChild(doc.createTextNode(method));

	methodCall.appendChild(params);

	for(int i = 0; i < args.size(); ++i) {
		param = doc.createElement("param");
		param.appendChild(toXml(args.at(i)));
		params.appendChild(param);
	}

	return doc.toString();
}

QString MaiaObject::prepareResponse(QVariant arg) {

	QDomDocument doc;

	QDomProcessingInstruction header = doc.createProcessingInstruction( "xml", QString("version=\"1.0\" encoding=\"UTF-8\"" ));
	doc.appendChild(header);

	QDomElement methodResponse = doc.createElement("methodResponse");
	QDomElement params = doc.createElement("params");
	QDomElement param;

	doc.appendChild(methodResponse);

	methodResponse.appendChild(params);

	if(!arg.isNull()) {
		param = doc.createElement("param");
		param.appendChild(toXml(arg));
		params.appendChild(param);
	}
	return doc.toString();
}

void MaiaObject::parseResponse(QString response, QNetworkReply* reply) {
	QDomDocument doc;
	QVariant arg;
	QString errorMsg;
	int errorLine;
	int errorColumn;
	if(!doc.setContent(response, &errorMsg, &errorLine, &errorColumn)) {
		emit fault(-32700, QString("parse error: response not well formed at line %1: %2").arg(errorLine).arg(errorMsg), reply);
		delete this;
		return;
	}
	if(doc.documentElement().firstChild().toElement().tagName().toLower() == "params") {
		QDomNode paramNode = doc.documentElement().firstChild().firstChild();
		if(!paramNode.isNull()) {
			arg = fromXml( paramNode.firstChild().toElement() );
		}
		emit aresponse(arg, reply);
	} else if(doc.documentElement().firstChild().toElement().tagName().toLower() == "fault") {
		const QVariant errorVariant = fromXml(doc.documentElement().firstChild().firstChild().toElement());
		emit fault(errorVariant.toMap() [ "faultCode" ].toInt(),
		           errorVariant.toMap() [ "faultString" ].toString(),
		           reply);
	} else {
		emit fault(-32600,
		           tr("parse error: invalid xml-rpc. not conforming to spec."),
		           reply);
	}
	delete this;
	return;
}

<commit_msg>compatibility with ws-xmlrpc (untested) - thanks to Martin Scheffler<commit_after>/*
 * libMaia - maiaObject.cpp
 * Copyright (c) 2003 Frerich Raabe <raabe@kde.org> and
 *                    Ian Reinhart Geiser <geiseri@kde.org>
 * Copyright (c) 2007 Sebastian Wiedenroth <wiedi@frubar.net>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "maiaObject.h"

MaiaObject::MaiaObject(QObject* parent) : QObject(parent){
	QDomImplementation::setInvalidDataPolicy(QDomImplementation::DropInvalidChars);
}

QDomElement MaiaObject::toXml(QVariant arg) {

	//dummy document
	QDomDocument doc;
	//value element, we need this in each case
	QDomElement tagValue = doc.createElement("value");

	switch(arg.type()) {
	case QVariant::String: {

		QDomElement tagString = doc.createElement("string");
		QDomText textString = doc.createTextNode(arg.toString());

		tagValue.appendChild(tagString);
		tagString.appendChild(textString);

		return tagValue;

	} case QVariant::Int: {

		QDomElement tagInt = doc.createElement("int");
		QDomText textInt = doc.createTextNode(QString::number(arg.toInt()));

		tagValue.appendChild(tagInt);
		tagInt.appendChild(textInt);

		return tagValue;

	} case QVariant::Double: {

		QDomElement tagDouble = doc.createElement("double");
		QDomText textDouble = doc.createTextNode(QString::number(arg.toDouble()));

		tagValue.appendChild(tagDouble);
		tagDouble.appendChild(textDouble);

		return tagValue;

	} case QVariant::Bool: {

		QString textValue = arg.toBool() ? "true" : "false";

		QDomElement tag = doc.createElement("boolean");
		QDomText text = doc.createTextNode(textValue);

		tagValue.appendChild(tag);
		tag.appendChild(text);

		return tagValue;

	} case QVariant::ByteArray: {

		QString textValue = arg.toByteArray().toBase64();

		QDomElement tag = doc.createElement("base64");
		QDomText text = doc.createTextNode(textValue);

		tagValue.appendChild(tag);
		tag.appendChild(text);

		return tagValue;

	} case QVariant::DateTime: {

		QString textValue = arg.toDateTime().toString("yyyyMMddThh:mm:ss");

		QDomElement tag = doc.createElement("datetime.iso8601");
		QDomText text = doc.createTextNode(textValue);

		tagValue.appendChild(tag);
		tag.appendChild(text);

		return tagValue;

	} case QVariant::List: {

		QDomElement tagArray = doc.createElement("array");
		QDomElement tagData = doc.createElement("data");
		tagArray.appendChild(tagData);
		tagValue.appendChild(tagArray);

		const QList<QVariant> args = arg.toList();
		for(int i = 0; i < args.size(); ++i) {
			tagData.appendChild(toXml(args.at(i)));
		}

		return tagValue;

	} case QVariant::Map: {

		QDomElement tagStruct = doc.createElement("struct");
		QDomElement member;
		QDomElement name;

		tagValue.appendChild(tagStruct);

		QMap<QString, QVariant> map = arg.toMap();
		QMapIterator<QString, QVariant> i(map);
		while(i.hasNext()) {
			i.next();

			member = doc.createElement("member");
			name = doc.createElement("name");

			// (key) -> name -> member -> struct
			tagStruct.appendChild(member);
			member.appendChild(name);
			name.appendChild(doc.createTextNode(i.key()));

			// add variables by recursion
			member.appendChild(toXml(i.value()));
		}

		return tagValue;

	} default:
		qDebug() << "Failed to marshal unknown variant type: " << arg.type() << endl;
	}
	return QDomElement(); //QString::null;
}

QVariant MaiaObject::fromXml(const QDomElement &elem) {
	if(elem.tagName().toLower() != "value") {
		return QVariant();
	}

	// If no type is indicated, the type is string.
	if(!elem.firstChild().isElement()) {
		return QVariant(elem.text());
	}

	const QDomElement typeElement = elem.firstChild().toElement();
	const QString typeName = typeElement.tagName().toLower();

	if(typeName == "string")
		return QVariant(typeElement.text());
	else if(typeName == "i4" || typeName == "int")
		return QVariant(typeElement.text().toInt());
	else if(typeName == "double")
		return QVariant(typeElement.text().toDouble());
	else if (typeName == "boolean") {
		if(typeElement.text().toLower() == "true" || typeElement.text() == "1")
			return QVariant(true);
		else
			return QVariant(false);
	} else if(typeName == "base64")
		return QVariant(QByteArray::fromBase64( typeElement.text().toLatin1()));
	else if(typeName == "datetime" || typeName == "datetime.iso8601")
		return QVariant(QDateTime::fromString(typeElement.text(), "yyyyMMddThh:mm:ss"));
	else if ( typeName == "array" ) {
		QList<QVariant> values;
		QDomNode valueNode = typeElement.firstChild().firstChild();
		while(!valueNode.isNull()) {
			values << fromXml(valueNode.toElement());
			valueNode = valueNode.nextSibling();
		}
		return QVariant(values);
	}
	else if ( typeName == "struct" ) {
		QMap<QString, QVariant> map;
		QDomNode memberNode = typeElement.firstChild();
		while(!memberNode.isNull())	{
			const QString key = memberNode.toElement().elementsByTagName("name").item(0).toElement().text();
			const QVariant data = fromXml(memberNode.toElement().elementsByTagName("value").item(0).toElement());
			map[key] = data;
			memberNode = memberNode.nextSibling();
		}
		return QVariant(map);
	} else {
		qDebug() << "Cannot demarshal unknown type " << typeElement.tagName().toLower();
	}
	return QVariant();
}

QString MaiaObject::prepareCall(QString method, QList<QVariant> args) {

	QDomDocument doc;

	QDomProcessingInstruction header = doc.createProcessingInstruction( "xml", QString("version=\"1.0\" encoding=\"UTF-8\"" ));
	doc.appendChild(header);

	QDomElement methodCall = doc.createElement("methodCall");
	QDomElement methodName = doc.createElement("methodName");
	QDomElement params = doc.createElement("params");
	QDomElement param;

	doc.appendChild(methodCall);
	methodCall.appendChild(methodName);
	methodName.appendChild(doc.createTextNode(method));

	methodCall.appendChild(params);

	for(int i = 0; i < args.size(); ++i) {
		param = doc.createElement("param");
		param.appendChild(toXml(args.at(i)));
		params.appendChild(param);
	}

	return doc.toString();
}

QString MaiaObject::prepareResponse(QVariant arg) {

	QDomDocument doc;

	QDomProcessingInstruction header = doc.createProcessingInstruction( "xml", QString("version=\"1.0\" encoding=\"UTF-8\"" ));
	doc.appendChild(header);

	QDomElement methodResponse = doc.createElement("methodResponse");
	QDomElement params = doc.createElement("params");
	QDomElement param;

	doc.appendChild(methodResponse);

	methodResponse.appendChild(params);

	if(!arg.isNull()) {
		param = doc.createElement("param");
		param.appendChild(toXml(arg));
		params.appendChild(param);
	}
	return doc.toString();
}

void MaiaObject::parseResponse(QString response, QNetworkReply* reply) {
	QDomDocument doc;
	QVariant arg;
	QString errorMsg;
	int errorLine;
	int errorColumn;
	if(!doc.setContent(response, &errorMsg, &errorLine, &errorColumn)) {
		emit fault(-32700, QString("parse error: response not well formed at line %1: %2").arg(errorLine).arg(errorMsg), reply);
		delete this;
		return;
	}
	if(doc.documentElement().firstChild().toElement().tagName().toLower() == "params") {
		QDomNode paramNode = doc.documentElement().firstChild().firstChild();
		if(!paramNode.isNull()) {
			arg = fromXml( paramNode.firstChild().toElement() );
		}
		emit aresponse(arg, reply);
	} else if(doc.documentElement().firstChild().toElement().tagName().toLower() == "fault") {
		const QVariant errorVariant = fromXml(doc.documentElement().firstChild().firstChild().toElement());
		emit fault(errorVariant.toMap() [ "faultCode" ].toInt(),
		           errorVariant.toMap() [ "faultString" ].toString(),
		           reply);
	} else {
		emit fault(-32600,
		           tr("parse error: invalid xml-rpc. not conforming to spec."),
		           reply);
	}
	delete this;
	return;
}

<|endoftext|>


<commit_before>//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//

#include <assert.h>
#include <string.h>

#include "locale.hpp"

#include "unicode/decimfmt.h"
#include "unicode/numfmt.h"
#include "unicode/ulocdata.h"

// invariant character definitions used by ICU
#define chCurrencySign                ((UChar)0x00A4) // international currency
#define chSpace                       ((UChar)0x0020) // space
#define chSpace2                      ((UChar)0x00A0) // space
#define chPatternDigit                ((UChar)0x0023) // '#'
#define chPatternSeparator            ((UChar)0x003B) // ';'
#define chPatternMinus                ((UChar)0x002D) // '-'
#define chPercent                     ((UChar)0x0025) // '%'
#define chOpenParen                   ((UChar)0x0028) // '('
#define chCloseParen                  ((UChar)0x0029) // ')'

#define ARRAY_LENGTH(array) (sizeof(array)/sizeof(array[0]))

// Enum that corresponds to managed enum CultureData.LocaleNumberData.
// The numeric values of the enum members match their Win32 counterparts.
enum LocaleNumberData : int32_t
{
	LanguageId = 0x00000001,
	MeasurementSystem = 0x0000000D,
	FractionalDigitsCount = 0x00000011,
	NegativeNumberFormat = 0x00001010,
	MonetaryFractionalDigitsCount = 0x00000019,
	PositiveMonetaryNumberFormat = 0x0000001B,
	NegativeMonetaryNumberFormat = 0x0000001C,
	CalendarType = 0x00001009,
	FirstWeekOfYear = 0x0000100D,
	ReadingLayout = 0x00000070,
	NegativePercentFormat = 0x00000074,
	PositivePercentFormat = 0x00000075,
	Digit = 0x00000010,
	Monetary = 0x00000018
};

/*
Function:
NormalizePattern

Returns a numeric string pattern in a format that we can match against the appropriate managed pattern.
*/
UnicodeString* NormalizePattern(const UnicodeString *srcPattern, UnicodeString *destPattern, bool isNegative)
{
	// A srcPattern example: "#,##0.00 C;(#,##0.00 C)" but where C is the international currency symbol (chCurrencySign)
	// The positive pattern comes first, then an optional negative pattern separated by a semicolon
	// A destPattern example: "(C n)" where C represents the currency symbol, and n is the number
	destPattern->remove();

	int iStart = 0;
	int iEnd = srcPattern->length() - 1;
	int32_t iNegativePatternStart = srcPattern->indexOf(chPatternSeparator);
	if (iNegativePatternStart >= 0)
	{
		if (isNegative)
		{
			iStart = iNegativePatternStart + 1;
		}
		else
		{
			iEnd = iNegativePatternStart - 1;
		}
	}

	bool minusAdded = false;
	bool digitAdded = false;
	bool currencyAdded = false;
	bool spaceAdded = false;

	for (int i = iStart; i <= iEnd; i++)
	{
		UChar32 ch = srcPattern->char32At(i);
		switch (ch)
		{
		case chPatternDigit:
			if (!digitAdded)
			{
				digitAdded = true;
				destPattern->append('n');
			}
			break;

		case chCurrencySign:
			if (!currencyAdded)
			{
				currencyAdded = true;
				destPattern->append('C');
			}
			break;

		case chSpace:
		case chSpace2:
			if (!spaceAdded)
			{
				spaceAdded = true;
				destPattern->append(chSpace);
			}
			else
			{
				assert(false);
			}
			break;

		case chPatternMinus:
		case chOpenParen:
		case chCloseParen:
			minusAdded = true;
			destPattern->append(ch);
			break;

		case chPercent:
			destPattern->append(ch);
			break;
		}
	}

	// if there is no negative subpattern, the convention is to prefix the minus sign
	if (isNegative && !minusAdded)
	{
		destPattern->insert(0, chPatternMinus);
	}

	return destPattern;
}

/*
Function:
GetPattern

Determines the pattern from the decimalFormat and returns the matching pattern's index from patterns[].
Returns index -1 if no pattern is found.
*/
int GetPattern(DecimalFormat *decimalFormat, const char* patterns[], int patternsCount, bool isNegative)
{
	const int INVALID_FORMAT = -1;
	const int MAX_DOTNET_NUMERIC_PATTERN_LENGTH = 6; // example: "(C n)" plus terminator
	char charPattern[MAX_DOTNET_NUMERIC_PATTERN_LENGTH] = { 0 };

	UnicodeString icuPattern;
	decimalFormat->toPattern(icuPattern);

	UnicodeString normalizedPattern;
	NormalizePattern(&icuPattern, &normalizedPattern, isNegative);

	assert(normalizedPattern.length() > 0);
	assert(normalizedPattern.length() < MAX_DOTNET_NUMERIC_PATTERN_LENGTH);
	if (normalizedPattern.length() == 0 || normalizedPattern.length() >= MAX_DOTNET_NUMERIC_PATTERN_LENGTH)
	{
		return INVALID_FORMAT;
	}

	u_UCharsToChars(normalizedPattern.getTerminatedBuffer(), charPattern, normalizedPattern.length() + 1);

	for (int i = 0; i < patternsCount; i++)
	{
		if (strcmp(charPattern, patterns[i]) == 0)
		{
			return i;
		}
	};

	assert(false); // should have found a valid pattern
	return INVALID_FORMAT;
}

/*
Function:
GetCurrencyNegativePattern

Implementation of NumberFormatInfo.CurrencyNegativePattern.
Returns the pattern index.
*/
int GetCurrencyNegativePattern(const Locale &locale)
{
	const int DEFAULT_VALUE = 0;
	static const char* Patterns[] = { "(Cn)", "-Cn", "C-n", "Cn-", "(nC)", "-nC", "n-C", "nC-", "-n C", "-C n", "n C-", "C n-", "C -n", "n- C", "(C n)", "(n C)" };
	UErrorCode status = U_ZERO_ERROR;

	LocalPointer<NumberFormat> format(NumberFormat::createInstance(locale, UNUM_CURRENCY, status));
	if (U_FAILURE(status))
	{
		assert(false);
		return DEFAULT_VALUE;
	}

	int value = GetPattern(static_cast<DecimalFormat*>(format.getAlias()), Patterns, ARRAY_LENGTH(Patterns), true);
	return (value >= 0) ? value : DEFAULT_VALUE;
}

/*
Function:
GetCurrencyPositivePattern

Implementation of NumberFormatInfo.CurrencyPositivePattern.
Returns the pattern index.
*/
int GetCurrencyPositivePattern(const Locale &locale)
{
	const int DEFAULT_VALUE = 0;
	static const char* Patterns[] = { "Cn", "nC", "C n", "n C" };
	UErrorCode status = U_ZERO_ERROR;

	LocalPointer<NumberFormat> format(NumberFormat::createInstance(locale, UNUM_CURRENCY, status));
	if (U_FAILURE(status))
	{
		assert(false);
		return DEFAULT_VALUE;
	}

	int value = GetPattern(static_cast<DecimalFormat*>(format.getAlias()), Patterns, ARRAY_LENGTH(Patterns), false);
	return (value >= 0) ? value : DEFAULT_VALUE;
}

/*
Function:
GetNumberNegativePattern

Implementation of NumberFormatInfo.NumberNegativePattern.
Returns the pattern index.
*/
int GetNumberNegativePattern(const Locale &locale)
{
	const int DEFAULT_VALUE = 1;
	static const char* Patterns[] = { "(n)", "-n", "- n", "n-", "n -" };
	UErrorCode status = U_ZERO_ERROR;

	LocalPointer<NumberFormat> format(NumberFormat::createInstance(locale, UNUM_DECIMAL, status));
	if (U_FAILURE(status))
	{
		assert(false);
		return DEFAULT_VALUE;
	}

	int value = GetPattern(static_cast<DecimalFormat*>(format.getAlias()), Patterns, ARRAY_LENGTH(Patterns), true);
	return (value >= 0) ? value : DEFAULT_VALUE;
}

/*
Function:
GetPercentNegativePattern

Implementation of NumberFormatInfo.PercentNegativePattern.
Returns the pattern index.
*/
int GetPercentNegativePattern(const Locale &locale)
{
	const int DEFAULT_VALUE = 0;
	static const char* Patterns[] = { "-n %", "-n%", "-%n", "%-n", "%n-", "n-%", "n%-", "-% n", "n %-", "% n-", "% -n", "n- %" };
	UErrorCode status = U_ZERO_ERROR;

	LocalPointer<NumberFormat> format(NumberFormat::createInstance(locale, UNUM_PERCENT, status));
	if (U_FAILURE(status))
	{
		assert(false);
		return DEFAULT_VALUE;
	}

	int value = GetPattern(static_cast<DecimalFormat*>(format.getAlias()), Patterns, ARRAY_LENGTH(Patterns), true);
	return (value >= 0) ? value : DEFAULT_VALUE;
}

/*
Function:
GetPercentPositivePattern

Implementation of NumberFormatInfo.PercentPositivePattern.
Returns the pattern index.
*/
int GetPercentPositivePattern(const Locale &locale)
{
	const int DEFAULT_VALUE = 0;
	static const char* Patterns[] = { "n %", "n%", "%n", "% n" };
	UErrorCode status = U_ZERO_ERROR;

	LocalPointer<NumberFormat> format(NumberFormat::createInstance(locale, UNUM_PERCENT, status));
	if (U_FAILURE(status))
	{
		assert(false);
		return DEFAULT_VALUE;
	}

	int value = GetPattern(static_cast<DecimalFormat*>(format.getAlias()), Patterns, ARRAY_LENGTH(Patterns), false);
	return (value >= 0) ? value : DEFAULT_VALUE;
}

/*
Function:
GetMeasurementSystem

Obtains the measurement system for the local, determining if US or metric.
Returns 1 for US, 0 otherwise.
*/
UErrorCode GetMeasurementSystem(const char *localeId, int32_t *value)
{
	UErrorCode status = U_ZERO_ERROR;

	UMeasurementSystem measurementSystem = ulocdata_getMeasurementSystem(localeId, &status);
	if (U_SUCCESS(status))
	{
		*value = (measurementSystem == UMeasurementSystem::UMS_US) ? 1 : 0;
	}

	return status;
}

/*
PAL Function:
GetLocaleInfoInt

Obtains integer locale information
Returns 1 for success, 0 otherwise
*/
extern "C" int32_t GetLocaleInfoInt(const UChar* localeName, LocaleNumberData localeNumberData, int32_t* value)
{
	Locale locale = GetLocale(localeName);
	if (locale.isBogus())
	{
		return UErrorCodeToBool(U_ILLEGAL_ARGUMENT_ERROR);
	}

	UErrorCode status = U_ZERO_ERROR;

	switch (localeNumberData)
	{
	case LanguageId:
		*value = locale.getLCID();
		break;
	case MeasurementSystem:
		status = GetMeasurementSystem(locale.getName(), value);
		break;
	case FractionalDigitsCount:
	{
		UNumberFormat* numformat = unum_open(UNUM_DECIMAL, NULL, 0, locale.getName(), NULL, &status);
		if (U_SUCCESS(status))
		{
			*value = unum_getAttribute(numformat, UNUM_MAX_FRACTION_DIGITS);
			unum_close(numformat);
		}
		break;
	}
	case NegativeNumberFormat:
		*value = GetNumberNegativePattern(locale);
		break;
	case MonetaryFractionalDigitsCount:
	{
		UNumberFormat* numformat = unum_open(UNUM_CURRENCY, NULL, 0, locale.getName(), NULL, &status);
		if (U_SUCCESS(status))
		{
			*value = unum_getAttribute(numformat, UNUM_MAX_FRACTION_DIGITS);
			unum_close(numformat);
		}
		break;
	}
	case PositiveMonetaryNumberFormat:
		*value = GetCurrencyPositivePattern(locale);
		break;
	case NegativeMonetaryNumberFormat:
		*value = GetCurrencyNegativePattern(locale);
		break;
	case CalendarType:
		*value = 1; //TODO: implement
		break;
	case FirstWeekOfYear:
		*value = 0; //TODO: implement
		break;
	case ReadingLayout:
		*value = 0; //todo: implement
		break;
	case NegativePercentFormat:
		*value = GetPercentNegativePattern(locale);
		break;
	case PositivePercentFormat:
		*value = GetPercentPositivePattern(locale);
		break;
	default:
		status = U_UNSUPPORTED_ERROR;
		assert(false);
		break;
	}

	assert(status != U_BUFFER_OVERFLOW_ERROR);

	return UErrorCodeToBool(status);
}
<commit_msg>address code review feedback<commit_after>//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//

#include <assert.h>
#include <string.h>

#include "locale.hpp"

#include "unicode/decimfmt.h"
#include "unicode/numfmt.h"
#include "unicode/ulocdata.h"

// invariant character definitions used by ICU
#define chCurrencySign                ((UChar)0x00A4) // international currency
#define chSpace                       ((UChar)0x0020) // space
#define chSpace2                      ((UChar)0x00A0) // space
#define chPatternDigit                ((UChar)0x0023) // '#'
#define chPatternSeparator            ((UChar)0x003B) // ';'
#define chPatternMinus                ((UChar)0x002D) // '-'
#define chPercent                     ((UChar)0x0025) // '%'
#define chOpenParen                   ((UChar)0x0028) // '('
#define chCloseParen                  ((UChar)0x0029) // ')'

#define ARRAY_LENGTH(array) (sizeof(array)/sizeof(array[0]))

// Enum that corresponds to managed enum CultureData.LocaleNumberData.
// The numeric values of the enum members match their Win32 counterparts.
enum LocaleNumberData : int32_t
{
	LanguageId = 0x00000001,
	MeasurementSystem = 0x0000000D,
	FractionalDigitsCount = 0x00000011,
	NegativeNumberFormat = 0x00001010,
	MonetaryFractionalDigitsCount = 0x00000019,
	PositiveMonetaryNumberFormat = 0x0000001B,
	NegativeMonetaryNumberFormat = 0x0000001C,
	CalendarType = 0x00001009,
	FirstWeekOfYear = 0x0000100D,
	ReadingLayout = 0x00000070,
	NegativePercentFormat = 0x00000074,
	PositivePercentFormat = 0x00000075,
	Digit = 0x00000010,
	Monetary = 0x00000018
};

/*
Function:
NormalizePattern

Returns a numeric string pattern in a format that we can match against the appropriate managed pattern.
*/
void NormalizePattern(const UnicodeString *srcPattern, UnicodeString *destPattern, bool isNegative)
{
	// A srcPattern example: "#,##0.00 C;(#,##0.00 C)" but where C is the international currency symbol (chCurrencySign)
	// The positive pattern comes first, then an optional negative pattern separated by a semicolon
	// A destPattern example: "(C n)" where C represents the currency symbol, and n is the number
	destPattern->remove();

	int iStart = 0;
	int iEnd = srcPattern->length() - 1;
	int32_t iNegativePatternStart = srcPattern->indexOf(chPatternSeparator);
	if (iNegativePatternStart >= 0)
	{
		if (isNegative)
		{
			iStart = iNegativePatternStart + 1;
		}
		else
		{
			iEnd = iNegativePatternStart - 1;
		}
	}

	bool minusAdded = false;
	bool digitAdded = false;
	bool currencyAdded = false;
	bool spaceAdded = false;

	for (int i = iStart; i <= iEnd; i++)
	{
		UChar ch = srcPattern->char32At(i);
		switch (ch)
		{
		case chPatternDigit:
			if (!digitAdded)
			{
				digitAdded = true;
				destPattern->append('n');
			}
			break;

		case chCurrencySign:
			if (!currencyAdded)
			{
				currencyAdded = true;
				destPattern->append('C');
			}
			break;

		case chSpace:
		case chSpace2:
			if (!spaceAdded)
			{
				spaceAdded = true;
				destPattern->append(chSpace);
			}
			else
			{
				assert(false);
			}
			break;

		case chPatternMinus:
		case chOpenParen:
		case chCloseParen:
			minusAdded = true;
			destPattern->append(ch);
			break;

		case chPercent:
			destPattern->append(ch);
			break;
		}
	}

	// if there is no negative subpattern, the convention is to prefix the minus sign
	if (isNegative && !minusAdded)
	{
		destPattern->insert(0, chPatternMinus);
	}
}

/*
Function:
GetPattern

Determines the pattern from the decimalFormat and returns the matching pattern's index from patterns[].
Returns index -1 if no pattern is found.
*/
int GetPattern(DecimalFormat *decimalFormat, const char* patterns[], int patternsCount, bool isNegative)
{
	const int INVALID_FORMAT = -1;
	const int MAX_DOTNET_NUMERIC_PATTERN_LENGTH = 6; // example: "(C n)" plus terminator
	char charPattern[MAX_DOTNET_NUMERIC_PATTERN_LENGTH] = { 0 };

	UnicodeString icuPattern;
	decimalFormat->toPattern(icuPattern);

	UnicodeString normalizedPattern;
	NormalizePattern(&icuPattern, &normalizedPattern, isNegative);

	assert(normalizedPattern.length() > 0);
	assert(normalizedPattern.length() < MAX_DOTNET_NUMERIC_PATTERN_LENGTH);
	if (normalizedPattern.length() == 0 || normalizedPattern.length() >= MAX_DOTNET_NUMERIC_PATTERN_LENGTH)
	{
		return INVALID_FORMAT;
	}

	u_UCharsToChars(normalizedPattern.getTerminatedBuffer(), charPattern, normalizedPattern.length() + 1);

	for (int i = 0; i < patternsCount; i++)
	{
		if (strcmp(charPattern, patterns[i]) == 0)
		{
			return i;
		}
	};

	assert(false); // should have found a valid pattern
	return INVALID_FORMAT;
}

/*
Function:
GetCurrencyNegativePattern

Implementation of NumberFormatInfo.CurrencyNegativePattern.
Returns the pattern index.
*/
int GetCurrencyNegativePattern(const Locale &locale)
{
	const int DEFAULT_VALUE = 0;
	static const char* Patterns[] = { "(Cn)", "-Cn", "C-n", "Cn-", "(nC)", "-nC", "n-C", "nC-", "-n C", "-C n", "n C-", "C n-", "C -n", "n- C", "(C n)", "(n C)" };
	UErrorCode status = U_ZERO_ERROR;

	LocalPointer<NumberFormat> format(NumberFormat::createInstance(locale, UNUM_CURRENCY, status));
	if (U_FAILURE(status))
	{
		assert(false);
		return DEFAULT_VALUE;
	}

	int value = GetPattern(static_cast<DecimalFormat*>(format.getAlias()), Patterns, ARRAY_LENGTH(Patterns), true);
	return (value >= 0) ? value : DEFAULT_VALUE;
}

/*
Function:
GetCurrencyPositivePattern

Implementation of NumberFormatInfo.CurrencyPositivePattern.
Returns the pattern index.
*/
int GetCurrencyPositivePattern(const Locale &locale)
{
	const int DEFAULT_VALUE = 0;
	static const char* Patterns[] = { "Cn", "nC", "C n", "n C" };
	UErrorCode status = U_ZERO_ERROR;

	LocalPointer<NumberFormat> format(NumberFormat::createInstance(locale, UNUM_CURRENCY, status));
	if (U_FAILURE(status))
	{
		assert(false);
		return DEFAULT_VALUE;
	}

	int value = GetPattern(static_cast<DecimalFormat*>(format.getAlias()), Patterns, ARRAY_LENGTH(Patterns), false);
	return (value >= 0) ? value : DEFAULT_VALUE;
}

/*
Function:
GetNumberNegativePattern

Implementation of NumberFormatInfo.NumberNegativePattern.
Returns the pattern index.
*/
int GetNumberNegativePattern(const Locale &locale)
{
	const int DEFAULT_VALUE = 1;
	static const char* Patterns[] = { "(n)", "-n", "- n", "n-", "n -" };
	UErrorCode status = U_ZERO_ERROR;

	LocalPointer<NumberFormat> format(NumberFormat::createInstance(locale, UNUM_DECIMAL, status));
	if (U_FAILURE(status))
	{
		assert(false);
		return DEFAULT_VALUE;
	}

	int value = GetPattern(static_cast<DecimalFormat*>(format.getAlias()), Patterns, ARRAY_LENGTH(Patterns), true);
	return (value >= 0) ? value : DEFAULT_VALUE;
}

/*
Function:
GetPercentNegativePattern

Implementation of NumberFormatInfo.PercentNegativePattern.
Returns the pattern index.
*/
int GetPercentNegativePattern(const Locale &locale)
{
	const int DEFAULT_VALUE = 0;
	static const char* Patterns[] = { "-n %", "-n%", "-%n", "%-n", "%n-", "n-%", "n%-", "-% n", "n %-", "% n-", "% -n", "n- %" };
	UErrorCode status = U_ZERO_ERROR;

	LocalPointer<NumberFormat> format(NumberFormat::createInstance(locale, UNUM_PERCENT, status));
	if (U_FAILURE(status))
	{
		assert(false);
		return DEFAULT_VALUE;
	}

	int value = GetPattern(static_cast<DecimalFormat*>(format.getAlias()), Patterns, ARRAY_LENGTH(Patterns), true);
	return (value >= 0) ? value : DEFAULT_VALUE;
}

/*
Function:
GetPercentPositivePattern

Implementation of NumberFormatInfo.PercentPositivePattern.
Returns the pattern index.
*/
int GetPercentPositivePattern(const Locale &locale)
{
	const int DEFAULT_VALUE = 0;
	static const char* Patterns[] = { "n %", "n%", "%n", "% n" };
	UErrorCode status = U_ZERO_ERROR;

	LocalPointer<NumberFormat> format(NumberFormat::createInstance(locale, UNUM_PERCENT, status));
	if (U_FAILURE(status))
	{
		assert(false);
		return DEFAULT_VALUE;
	}

	int value = GetPattern(static_cast<DecimalFormat*>(format.getAlias()), Patterns, ARRAY_LENGTH(Patterns), false);
	return (value >= 0) ? value : DEFAULT_VALUE;
}

/*
Function:
GetMeasurementSystem

Obtains the measurement system for the local, determining if US or metric.
Returns 1 for US, 0 otherwise.
*/
UErrorCode GetMeasurementSystem(const char *localeId, int32_t *value)
{
	UErrorCode status = U_ZERO_ERROR;

	UMeasurementSystem measurementSystem = ulocdata_getMeasurementSystem(localeId, &status);
	if (U_SUCCESS(status))
	{
		*value = (measurementSystem == UMeasurementSystem::UMS_US) ? 1 : 0;
	}

	return status;
}

/*
PAL Function:
GetLocaleInfoInt

Obtains integer locale information
Returns 1 for success, 0 otherwise
*/
extern "C" int32_t GetLocaleInfoInt(const UChar* localeName, LocaleNumberData localeNumberData, int32_t* value)
{
	Locale locale = GetLocale(localeName);
	if (locale.isBogus())
	{
		return UErrorCodeToBool(U_ILLEGAL_ARGUMENT_ERROR);
	}

	UErrorCode status = U_ZERO_ERROR;

	switch (localeNumberData)
	{
	case LanguageId:
		*value = locale.getLCID();
		break;
	case MeasurementSystem:
		status = GetMeasurementSystem(locale.getName(), value);
		break;
	case FractionalDigitsCount:
	{
		UNumberFormat* numformat = unum_open(UNUM_DECIMAL, NULL, 0, locale.getName(), NULL, &status);
		if (U_SUCCESS(status))
		{
			*value = unum_getAttribute(numformat, UNUM_MAX_FRACTION_DIGITS);
			unum_close(numformat);
		}
		break;
	}
	case NegativeNumberFormat:
		*value = GetNumberNegativePattern(locale);
		break;
	case MonetaryFractionalDigitsCount:
	{
		UNumberFormat* numformat = unum_open(UNUM_CURRENCY, NULL, 0, locale.getName(), NULL, &status);
		if (U_SUCCESS(status))
		{
			*value = unum_getAttribute(numformat, UNUM_MAX_FRACTION_DIGITS);
			unum_close(numformat);
		}
		break;
	}
	case PositiveMonetaryNumberFormat:
		*value = GetCurrencyPositivePattern(locale);
		break;
	case NegativeMonetaryNumberFormat:
		*value = GetCurrencyNegativePattern(locale);
		break;
	case CalendarType:
		*value = 1; //TODO: implement
		break;
	case FirstWeekOfYear:
		*value = 0; //TODO: implement
		break;
	case ReadingLayout:
		*value = 0; //todo: implement
		break;
	case NegativePercentFormat:
		*value = GetPercentNegativePattern(locale);
		break;
	case PositivePercentFormat:
		*value = GetPercentPositivePattern(locale);
		break;
	default:
		status = U_UNSUPPORTED_ERROR;
		assert(false);
		break;
	}

	assert(status != U_BUFFER_OVERFLOW_ERROR);

	return UErrorCodeToBool(status);
}
<|endoftext|>