<commit_before>/*****************************************************************************************
 *                                                                                *
 * OpenSpace                                                                             *
 *                                                                                *
 * Copyright (c) 2014-2022                                                               *
 *                                                                                *
 * 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.                                         *
 ****************************************************************************************/

namespace {

/**
 * Load a navigation state from file. The file should be a lua file returning the
 * navigation state as a table formatted as a Navigation State, such as the output files
 * of saveNavigationState.
 */
[[codegen::luawrap]] void loadNavigationState(std::string cameraStateFilePath) {
    if (cameraStateFilePath.empty()) {
        throw ghoul::lua::LuaError("Filepath string is empty");
    }

    openspace::global::navigationHandler->loadNavigationState(cameraStateFilePath);
}

/**
 * Return the current navigation state as a lua table. The optional argument is the scene
 * graph node to use as reference frame. By default, the reference frame will picked based
 * on whether the orbital navigator is currently following the anchor node rotation. If it
 * is, the anchor will be chosen as reference frame. If not, the reference frame will be
 * set to the scene graph root.
 */
[[codegen::luawrap]] ghoul::Dictionary getNavigationState(
                                                         std::optional<std::string> frame)
{
    using namespace openspace;

    interaction::NavigationState state;
    if (frame.has_value()) {
        const SceneGraphNode* referenceFrame = sceneGraphNode(*frame);
        if (!referenceFrame) {
            throw ghoul::lua::LuaError(
                fmt::format("Could not find node '{}' as reference frame", *frame)
            );
        }
        state = global::navigationHandler->navigationState(*referenceFrame);
    }
    else {
        state = global::navigationHandler->navigationState();
    }

    ghoul::Dictionary res;
    res.setValue("Anchor", state.anchor);
    if (!state.aim.empty()) {
        res.setValue("Aim", state.aim);
    }
    if (!state.referenceFrame.empty()) {
        res.setValue("ReferenceFrame", state.referenceFrame);
    }
    res.setValue("ReferenceFrame", state.position);
    if (state.up.has_value()) {
        res.setValue("Up", *state.up);
    }
    if (state.yaw != 0) {
        res.setValue("Up", state.yaw);
    }
    if (state.pitch != 0) {
        res.setValue("Pitch", state.pitch);
    }
    return res;
}

// Set the navigation state. The argument must be a valid Navigation State.
[[codegen::luawrap]] void setNavigationState(ghoul::Dictionary navigationState) {
    using namespace openspace;

    documentation::TestResult r = documentation::testSpecification(
        interaction::NavigationState::Documentation(),
        navigationState
    );

    if (!r.success) {
        throw ghoul::lua::LuaError(
            fmt::format("Could not set camera state: {}", ghoul::to_string(r))
        );
    }

    global::navigationHandler->setNavigationStateNextFrame(
        interaction::NavigationState(navigationState)
    );
}

/**
 * Save the current navigation state to a file with the path given by the first argument.
 * The optional second argument is the scene graph node to use as reference frame. By
 * default, the reference frame will picked based on whether the orbital navigator is
 * currently following the anchor node rotation. If it is, the anchor will be chosen as
 * reference frame. If not, the reference frame will be set to the scene graph root.
 */
[[codegen::luawrap]] void saveNavigationState(std::string path, std::string frame = "") {
    if (path.empty()) {
        throw ghoul::lua::LuaError("Filepath string is empty");
    }
    openspace::global::navigationHandler->saveNavigationState(path, frame);
}

// Reset the camera direction to point at the anchor node.
[[codegen::luawrap]] void retargetAnchor() {
    openspace::global::navigationHandler->orbitalNavigator().startRetargetAnchor();
}

// Reset the camera direction to point at the aim node.
[[codegen::luawrap]] void retargetAim() {
    openspace::global::navigationHandler->orbitalNavigator().startRetargetAim();
}

// Picks the next node from the interesting nodes out of the profile and selects that. If
// the current anchor is not an interesting node, the first will be selected
[[codegen::luawrap]] void targetNextInterestingAnchor() {
    using namespace openspace;
    if (global::profile->markNodes.empty()) {
        LWARNINGC(
            "targetNextInterestingAnchor",
            "Profile does not define any interesting nodes"
        );
        return;
    }
    const std::vector<std::string>& markNodes = global::profile->markNodes;

    std::string currAnchor =
        global::navigationHandler->orbitalNavigator().anchorNode()->identifier();

    auto it = std::find(markNodes.begin(), markNodes.end(), currAnchor);
    if (it == markNodes.end() || ((it + 1) == markNodes.end())) {
        // We want to use the first node either if
        //  1. The current node is not an interesting node
        //  2. The current node is the last interesting node
        global::navigationHandler->orbitalNavigator().setFocusNode(markNodes.front());
    }
    else {
        // Otherwise we can just select the next one
        global::navigationHandler->orbitalNavigator().setFocusNode(*(it + 1));
    }
    global::navigationHandler->orbitalNavigator().startRetargetAnchor();
}

/**
 * Finds the input joystick with the given 'name' and binds the axis identified by the
 * second argument to be used as the type identified by the third argument. If
 * 'isInverted' is 'true', the axis value is inverted. 'joystickType' is if the joystick
 * behaves more like a joystick or a trigger, where the first is the default. If
 * 'isSticky' is 'true', the value is calculated relative to the previous value. If
 * 'sensitivity' is given then that value will affect the sensitivity of the axis together
 * with the global sensitivity.
 */
[[codegen::luawrap]] void bindJoystickAxis(std::string joystickName, int axis,
                                           std::string axisType,
                                           bool shouldInvert = false,
                                           std::string joystickType = "JoystickLike",
                                           bool isSticky = false,
                                           double sensitivity = 0.0)
{
    using namespace openspace;
    using JoystickCameraStates = interaction::JoystickCameraStates;
    global::navigationHandler->setJoystickAxisMapping(
        std::move(joystickName),
        axis,
        ghoul::from_string<JoystickCameraStates::AxisType>(axisType),
        JoystickCameraStates::AxisInvert(shouldInvert),
        ghoul::from_string<JoystickCameraStates::JoystickType>(joystickType),
        isSticky,
        sensitivity
    );
}

/**
 * Finds the input joystick with the given 'name' and binds the axis identified by the
 * second argument to be bound to the property identified by the third argument. 'min' and
 * 'max' is the minimum and the maximum allowed value for the given property and the axis
 * value is rescaled from [-1, 1] to [min, max], default is [0, 1]. If 'isInverted' is
 * 'true', the axis value is inverted. The last argument determines whether the property
 * change is going to be executed locally or remotely, where the latter is the default.
 */
[[codegen::luawrap]] void bindJoystickAxisProperty(std::string joystickName, int axis,
                                                   std::string propertyUri,
                                                   float min = 0.f, float max = 1.f,
                                                   bool shouldInvert = false,
                                                   bool isRemote = true)
{
    using namespace openspace;
    global::navigationHandler->setJoystickAxisMappingProperty(
        std::move(joystickName),
        axis,
        std::move(propertyUri),
        min,
        max,
        interaction::JoystickCameraStates::AxisInvert(shouldInvert),
        isRemote
    );
}

/**
 * Finds the input joystick with the given 'name' and returns the joystick axis
 * information for the passed axis. The information that is returned is the current axis
 * binding as a string, whether the values are inverted as bool, the joystick type as a
 * string, whether the axis is sticky as bool, the sensitivity as number, the property uri
 * bound to the axis as string (empty is type is not Property), the min and max values for
 * the property as numbers and whether the property change will be executed remotly as
 * bool.
 */
[[codegen::luawrap]]
std::tuple<std::string, bool, std::string, bool, double, std::string, float, float, bool>
joystickAxis(std::string joystickName, int axis)
{
    using namespace openspace;

    interaction::JoystickCameraStates::AxisInformation info =
        global::navigationHandler->joystickAxisMapping(joystickName, axis);

    return {
        ghoul::to_string(info.type),
        info.invert,
        ghoul::to_string(info.joystickType),
        info.isSticky,
        info.sensitivity,
        info.propertyUri,
        info.minValue,
        info.maxValue,
        info.isRemote
    };
}

/**
 * Finds the input joystick with the given 'name' and sets the deadzone for a particular
 * joystick axis, which means that any input less than this value is completely ignored.
 */
[[codegen::luawrap("setAxisDeadZone")]] void setJoystickAxisDeadZone(
                                                                 std::string joystickName,
                                                                                int axis,
                                                                           float deadzone)
{
    using namespace openspace;
    global::navigationHandler->setJoystickAxisDeadzone(joystickName, axis, deadzone);
}

/**
 * Returns the deadzone for the desired axis of the provided joystick.
 */
[[codegen::luawrap("axisDeadzone")]] float joystickAxisDeadzone(std::string joystickName,
                                                                int axis)
{
    float deadzone = openspace::global::navigationHandler->joystickAxisDeadzone(
        joystickName,
        axis
    );
    return deadzone;
}

/**
 * Finds the input joystick with the given 'name' and binds a Lua script given by the
 * third argument to be executed when the joystick button identified by the second
 * argument is triggered. The fifth argument determines when the script should be
 * executed, this defaults to 'Press', which means that the script is run when the user
 * presses the button. The fourth arguemnt is the documentation of the script in the third
 * argument. The sixth argument determines whether the command is going to be executable
 * locally or remotely, where the latter is the default.
 */
[[codegen::luawrap]] void bindJoystickButton(std::string joystickName, int button,
                                             std::string command,
                                             std::string documentation,
                                             std::string action = "Press",
                                             bool isRemote = true)
{
    using namespace openspace;
    interaction::JoystickAction act =
        ghoul::from_string<interaction::JoystickAction>(action);

    global::navigationHandler->bindJoystickButtonCommand(
        joystickName,
        button,
        command,
        act,
        interaction::JoystickCameraStates::ButtonCommandRemote(isRemote),
        documentation
    );
}

/**
 * Finds the input joystick with the given 'name' and removes all commands that are
 * currently bound to the button identified by the second argument.
 */
[[codegen::luawrap]] void clearJoystickButton(std::string joystickName, int button) {
    openspace::global::navigationHandler->clearJoystickButtonCommand(
        joystickName,
        button
    );
}

/**
 * Finds the input joystick with the given 'name' and returns the script that is currently
 * bound to be executed when the provided button is pressed.
 */
[[codegen::luawrap]] std::string joystickButton(std::string joystickName, int button) {
    using namespace openspace;
    const std::vector<std::string>& cmds =
        global::navigationHandler->joystickButtonCommand(joystickName, button);

    std::string cmd = std::accumulate(
        cmds.cbegin(),
        cmds.cend(),
        std::string(),
        [](const std::string& lhs, const std::string& rhs) {
            return lhs + ";" + rhs;
        }
    );
    return cmd;
}

// Directly adds to the global rotation of the camera.
[[codegen::luawrap]] void addGlobalRotation(double v1, double v2) {
    using namespace openspace;
    global::navigationHandler->orbitalNavigator().scriptStates().addGlobalRotation(
        glm::dvec2(v1, v2)
    );
}

// Directly adds to the local rotation of the camera.
[[codegen::luawrap]] void addLocalRotation(double v1, double v2) {
    using namespace openspace;
    global::navigationHandler->orbitalNavigator().scriptStates().addLocalRotation(
        glm::dvec2(v1, v2)
    );
}

// Directly adds to the truck movement of the camera.
[[codegen::luawrap]] void addTruckMovement(double v1, double v2) {
    using namespace openspace;
    global::navigationHandler->orbitalNavigator().scriptStates().addTruckMovement(
        glm::dvec2(v1, v2)
    );
}

// Directly adds to the local roll of the camera.
[[codegen::luawrap]] void addLocalRoll(double v1, double v2) {
    using namespace openspace;
    global::navigationHandler->orbitalNavigator().scriptStates().addLocalRoll(
        glm::dvec2(v1, v2)
    );
}

// Directly adds to the global roll of the camera.
[[codegen::luawrap]] void addGlobalRoll(double v1, double v2) {
    using namespace openspace;
    global::navigationHandler->orbitalNavigator().scriptStates().addGlobalRoll(
        glm::dvec2(v1, v2)
    );
}

/**
 * Immediately start applying the chosen IdleBehavior. If none is specified, use the one
 * set to default in the OrbitalNavigator.
 */
[[codegen::luawrap]] void triggerIdleBehavior(std::string choice = "") {
    using namespace openspace;
    try {
        global::navigationHandler->orbitalNavigator().triggerIdleBehavior(choice);
    }
    catch (ghoul::RuntimeError& e) {
        throw ghoul::lua::LuaError(e.message);
    }
}

/**
 * Return the complete list of connected joysticks
 */
[[codegen::luawrap]] std::vector<std::string> listAllJoysticks() {
    using namespace openspace;
    return global::navigationHandler->listAllJoysticks();
}

#include "navigationhandler_lua_codegen.cpp"

} // namespace
<commit_msg>Fix broken getNavigationState function<commit_after>/*****************************************************************************************
 *                                                                                *
 * OpenSpace                                                                             *
 *                                                                                *
 * Copyright (c) 2014-2022                                                               *
 *                                                                                *
 * 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.                                         *
 ****************************************************************************************/

namespace {

/**
 * Load a navigation state from file. The file should be a lua file returning the
 * navigation state as a table formatted as a Navigation State, such as the output files
 * of saveNavigationState.
 */
[[codegen::luawrap]] void loadNavigationState(std::string cameraStateFilePath) {
    if (cameraStateFilePath.empty()) {
        throw ghoul::lua::LuaError("Filepath string is empty");
    }

    openspace::global::navigationHandler->loadNavigationState(cameraStateFilePath);
}

/**
 * Return the current navigation state as a lua table. The optional argument is the scene
 * graph node to use as reference frame. By default, the reference frame will picked based
 * on whether the orbital navigator is currently following the anchor node rotation. If it
 * is, the anchor will be chosen as reference frame. If not, the reference frame will be
 * set to the scene graph root.
 */
[[codegen::luawrap]] ghoul::Dictionary getNavigationState(
                                                         std::optional<std::string> frame)
{
    using namespace openspace;

    interaction::NavigationState state;
    if (frame.has_value()) {
        const SceneGraphNode* referenceFrame = sceneGraphNode(*frame);
        if (!referenceFrame) {
            throw ghoul::lua::LuaError(
                fmt::format("Could not find node '{}' as reference frame", *frame)
            );
        }
        state = global::navigationHandler->navigationState(*referenceFrame);
    }
    else {
        state = global::navigationHandler->navigationState();
    }

    return state.dictionary();
}

// Set the navigation state. The argument must be a valid Navigation State.
[[codegen::luawrap]] void setNavigationState(ghoul::Dictionary navigationState) {
    using namespace openspace;

    documentation::TestResult r = documentation::testSpecification(
        interaction::NavigationState::Documentation(),
        navigationState
    );

    if (!r.success) {
        throw ghoul::lua::LuaError(
            fmt::format("Could not set camera state: {}", ghoul::to_string(r))
        );
    }

    global::navigationHandler->setNavigationStateNextFrame(
        interaction::NavigationState(navigationState)
    );
}

/**
 * Save the current navigation state to a file with the path given by the first argument.
 * The optional second argument is the scene graph node to use as reference frame. By
 * default, the reference frame will picked based on whether the orbital navigator is
 * currently following the anchor node rotation. If it is, the anchor will be chosen as
 * reference frame. If not, the reference frame will be set to the scene graph root.
 */
[[codegen::luawrap]] void saveNavigationState(std::string path, std::string frame = "") {
    if (path.empty()) {
        throw ghoul::lua::LuaError("Filepath string is empty");
    }
    openspace::global::navigationHandler->saveNavigationState(path, frame);
}

// Reset the camera direction to point at the anchor node.
[[codegen::luawrap]] void retargetAnchor() {
    openspace::global::navigationHandler->orbitalNavigator().startRetargetAnchor();
}

// Reset the camera direction to point at the aim node.
[[codegen::luawrap]] void retargetAim() {
    openspace::global::navigationHandler->orbitalNavigator().startRetargetAim();
}

// Picks the next node from the interesting nodes out of the profile and selects that. If
// the current anchor is not an interesting node, the first will be selected
[[codegen::luawrap]] void targetNextInterestingAnchor() {
    using namespace openspace;
    if (global::profile->markNodes.empty()) {
        LWARNINGC(
            "targetNextInterestingAnchor",
            "Profile does not define any interesting nodes"
        );
        return;
    }
    const std::vector<std::string>& markNodes = global::profile->markNodes;

    std::string currAnchor =
        global::navigationHandler->orbitalNavigator().anchorNode()->identifier();

    auto it = std::find(markNodes.begin(), markNodes.end(), currAnchor);
    if (it == markNodes.end() || ((it + 1) == markNodes.end())) {
        // We want to use the first node either if
        //  1. The current node is not an interesting node
        //  2. The current node is the last interesting node
        global::navigationHandler->orbitalNavigator().setFocusNode(markNodes.front());
    }
    else {
        // Otherwise we can just select the next one
        global::navigationHandler->orbitalNavigator().setFocusNode(*(it + 1));
    }
    global::navigationHandler->orbitalNavigator().startRetargetAnchor();
}

/**
 * Finds the input joystick with the given 'name' and binds the axis identified by the
 * second argument to be used as the type identified by the third argument. If
 * 'isInverted' is 'true', the axis value is inverted. 'joystickType' is if the joystick
 * behaves more like a joystick or a trigger, where the first is the default. If
 * 'isSticky' is 'true', the value is calculated relative to the previous value. If
 * 'sensitivity' is given then that value will affect the sensitivity of the axis together
 * with the global sensitivity.
 */
[[codegen::luawrap]] void bindJoystickAxis(std::string joystickName, int axis,
                                           std::string axisType,
                                           bool shouldInvert = false,
                                           std::string joystickType = "JoystickLike",
                                           bool isSticky = false,
                                           double sensitivity = 0.0)
{
    using namespace openspace;
    using JoystickCameraStates = interaction::JoystickCameraStates;
    global::navigationHandler->setJoystickAxisMapping(
        std::move(joystickName),
        axis,
        ghoul::from_string<JoystickCameraStates::AxisType>(axisType),
        JoystickCameraStates::AxisInvert(shouldInvert),
        ghoul::from_string<JoystickCameraStates::JoystickType>(joystickType),
        isSticky,
        sensitivity
    );
}

/**
 * Finds the input joystick with the given 'name' and binds the axis identified by the
 * second argument to be bound to the property identified by the third argument. 'min' and
 * 'max' is the minimum and the maximum allowed value for the given property and the axis
 * value is rescaled from [-1, 1] to [min, max], default is [0, 1]. If 'isInverted' is
 * 'true', the axis value is inverted. The last argument determines whether the property
 * change is going to be executed locally or remotely, where the latter is the default.
 */
[[codegen::luawrap]] void bindJoystickAxisProperty(std::string joystickName, int axis,
                                                   std::string propertyUri,
                                                   float min = 0.f, float max = 1.f,
                                                   bool shouldInvert = false,
                                                   bool isRemote = true)
{
    using namespace openspace;
    global::navigationHandler->setJoystickAxisMappingProperty(
        std::move(joystickName),
        axis,
        std::move(propertyUri),
        min,
        max,
        interaction::JoystickCameraStates::AxisInvert(shouldInvert),
        isRemote
    );
}

/**
 * Finds the input joystick with the given 'name' and returns the joystick axis
 * information for the passed axis. The information that is returned is the current axis
 * binding as a string, whether the values are inverted as bool, the joystick type as a
 * string, whether the axis is sticky as bool, the sensitivity as number, the property uri
 * bound to the axis as string (empty is type is not Property), the min and max values for
 * the property as numbers and whether the property change will be executed remotly as
 * bool.
 */
[[codegen::luawrap]]
std::tuple<std::string, bool, std::string, bool, double, std::string, float, float, bool>
joystickAxis(std::string joystickName, int axis)
{
    using namespace openspace;

    interaction::JoystickCameraStates::AxisInformation info =
        global::navigationHandler->joystickAxisMapping(joystickName, axis);

    return {
        ghoul::to_string(info.type),
        info.invert,
        ghoul::to_string(info.joystickType),
        info.isSticky,
        info.sensitivity,
        info.propertyUri,
        info.minValue,
        info.maxValue,
        info.isRemote
    };
}

/**
 * Finds the input joystick with the given 'name' and sets the deadzone for a particular
 * joystick axis, which means that any input less than this value is completely ignored.
 */
[[codegen::luawrap("setAxisDeadZone")]] void setJoystickAxisDeadZone(
                                                                 std::string joystickName,
                                                                                int axis,
                                                                           float deadzone)
{
    using namespace openspace;
    global::navigationHandler->setJoystickAxisDeadzone(joystickName, axis, deadzone);
}

/**
 * Returns the deadzone for the desired axis of the provided joystick.
 */
[[codegen::luawrap("axisDeadzone")]] float joystickAxisDeadzone(std::string joystickName,
                                                                int axis)
{
    float deadzone = openspace::global::navigationHandler->joystickAxisDeadzone(
        joystickName,
        axis
    );
    return deadzone;
}

/**
 * Finds the input joystick with the given 'name' and binds a Lua script given by the
 * third argument to be executed when the joystick button identified by the second
 * argument is triggered. The fifth argument determines when the script should be
 * executed, this defaults to 'Press', which means that the script is run when the user
 * presses the button. The fourth arguemnt is the documentation of the script in the third
 * argument. The sixth argument determines whether the command is going to be executable
 * locally or remotely, where the latter is the default.
 */
[[codegen::luawrap]] void bindJoystickButton(std::string joystickName, int button,
                                             std::string command,
                                             std::string documentation,
                                             std::string action = "Press",
                                             bool isRemote = true)
{
    using namespace openspace;
    interaction::JoystickAction act =
        ghoul::from_string<interaction::JoystickAction>(action);

    global::navigationHandler->bindJoystickButtonCommand(
        joystickName,
        button,
        command,
        act,
        interaction::JoystickCameraStates::ButtonCommandRemote(isRemote),
        documentation
    );
}

/**
 * Finds the input joystick with the given 'name' and removes all commands that are
 * currently bound to the button identified by the second argument.
 */
[[codegen::luawrap]] void clearJoystickButton(std::string joystickName, int button) {
    openspace::global::navigationHandler->clearJoystickButtonCommand(
        joystickName,
        button
    );
}

/**
 * Finds the input joystick with the given 'name' and returns the script that is currently
 * bound to be executed when the provided button is pressed.
 */
[[codegen::luawrap]] std::string joystickButton(std::string joystickName, int button) {
    using namespace openspace;
    const std::vector<std::string>& cmds =
        global::navigationHandler->joystickButtonCommand(joystickName, button);

    std::string cmd = std::accumulate(
        cmds.cbegin(),
        cmds.cend(),
        std::string(),
        [](const std::string& lhs, const std::string& rhs) {
            return lhs + ";" + rhs;
        }
    );
    return cmd;
}

// Directly adds to the global rotation of the camera.
[[codegen::luawrap]] void addGlobalRotation(double v1, double v2) {
    using namespace openspace;
    global::navigationHandler->orbitalNavigator().scriptStates().addGlobalRotation(
        glm::dvec2(v1, v2)
    );
}

// Directly adds to the local rotation of the camera.
[[codegen::luawrap]] void addLocalRotation(double v1, double v2) {
    using namespace openspace;
    global::navigationHandler->orbitalNavigator().scriptStates().addLocalRotation(
        glm::dvec2(v1, v2)
    );
}

// Directly adds to the truck movement of the camera.
[[codegen::luawrap]] void addTruckMovement(double v1, double v2) {
    using namespace openspace;
    global::navigationHandler->orbitalNavigator().scriptStates().addTruckMovement(
        glm::dvec2(v1, v2)
    );
}

// Directly adds to the local roll of the camera.
[[codegen::luawrap]] void addLocalRoll(double v1, double v2) {
    using namespace openspace;
    global::navigationHandler->orbitalNavigator().scriptStates().addLocalRoll(
        glm::dvec2(v1, v2)
    );
}

// Directly adds to the global roll of the camera.
[[codegen::luawrap]] void addGlobalRoll(double v1, double v2) {
    using namespace openspace;
    global::navigationHandler->orbitalNavigator().scriptStates().addGlobalRoll(
        glm::dvec2(v1, v2)
    );
}

/**
 * Immediately start applying the chosen IdleBehavior. If none is specified, use the one
 * set to default in the OrbitalNavigator.
 */
[[codegen::luawrap]] void triggerIdleBehavior(std::string choice = "") {
    using namespace openspace;
    try {
        global::navigationHandler->orbitalNavigator().triggerIdleBehavior(choice);
    }
    catch (ghoul::RuntimeError& e) {
        throw ghoul::lua::LuaError(e.message);
    }
}

/**
 * Return the complete list of connected joysticks
 */
[[codegen::luawrap]] std::vector<std::string> listAllJoysticks() {
    using namespace openspace;
    return global::navigationHandler->listAllJoysticks();
}

#include "navigationhandler_lua_codegen.cpp"

} // namespace
<|endoftext|>


<commit_before>// Copyright (c) 2011-2012 Zeex
// All rights reserved.
//
// 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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 <amx/amx.h>

#include "amx.h"
#include "amxerror.h"
#include "compiler.h"
#include "crashdetect.h"
#include "fileutils.h"
#include "hook.h"
#include "logprintf.h"
#include "os.h"
#include "plugincommon.h"
#include "version.h"

extern "C" int AMXAPI amx_Error(AMX *amx, cell index, int error) {
	if (error != AMX_ERR_NONE) {
		CrashDetect::Get(amx)->HandleExecError(index, error);
	}
	return AMX_ERR_NONE;
}

static int AMXAPI AmxCallback(AMX *amx, cell index, cell *result, cell *params) {
	return CrashDetect::Get(amx)->DoAmxCallback(index, result, params);
}

static int AMXAPI AmxExec(AMX *amx, cell *retval, int index) {
	if (amx->flags & AMX_FLAG_BROWSE) {
		return amx_Exec(amx, retval, index);
	}
	return CrashDetect::Get(amx)->DoAmxExec(retval, index);
}

PLUGIN_EXPORT unsigned int PLUGIN_CALL Supports() {
	return SUPPORTS_VERSION | SUPPORTS_AMX_NATIVES;
}

PLUGIN_EXPORT bool PLUGIN_CALL Load(void **ppData) {
	void **exports = reinterpret_cast<void**>(ppData[PLUGIN_DATA_AMX_EXPORTS]);
	::logprintf = (logprintf_t)ppData[PLUGIN_DATA_LOGPRINTF];

	void *amx_Exec_ptr = exports[PLUGIN_AMX_EXPORT_Exec];
	void *amx_Exec_sub = Hook::GetTargetAddress(reinterpret_cast<unsigned char*>(amx_Exec_ptr));

	if (amx_Exec_sub == 0) {
		new Hook(amx_Exec_ptr, (void*)AmxExec);
	} else {
		std::string module = fileutils::GetFileName(os::GetModulePathFromAddr(amx_Exec_sub));
		if (!module.empty()) {
			logprintf("  Warning: Runtime error detection will not work during this run because ");
			logprintf("           %s has been loaded before CrashDetect.", module.c_str());
		}
	}

	os::SetExceptionHandler(CrashDetect::OnException);
	os::SetInterruptHandler(CrashDetect::OnInterrupt);

	logprintf("  crashdetect v"PROJECT_VERSION_STRING" is OK.");
	return true;
}

PLUGIN_EXPORT int PLUGIN_CALL AmxLoad(AMX *amx) {
	int error = CrashDetect::Create(amx)->Load();
	if (error == AMX_ERR_NONE) {
		amx_SetCallback(amx, AmxCallback);
	}
	return error;
}

PLUGIN_EXPORT int PLUGIN_CALL AmxUnload(AMX *amx) {
	int error = CrashDetect::Get(amx)->Unload();
	CrashDetect::Destroy(amx);
	return error;
}
<commit_msg>Change message<commit_after>// Copyright (c) 2011-2012 Zeex
// All rights reserved.
//
// 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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 <amx/amx.h>

#include "amx.h"
#include "amxerror.h"
#include "compiler.h"
#include "crashdetect.h"
#include "fileutils.h"
#include "hook.h"
#include "logprintf.h"
#include "os.h"
#include "plugincommon.h"
#include "version.h"

extern "C" int AMXAPI amx_Error(AMX *amx, cell index, int error) {
	if (error != AMX_ERR_NONE) {
		CrashDetect::Get(amx)->HandleExecError(index, error);
	}
	return AMX_ERR_NONE;
}

static int AMXAPI AmxCallback(AMX *amx, cell index, cell *result, cell *params) {
	return CrashDetect::Get(amx)->DoAmxCallback(index, result, params);
}

static int AMXAPI AmxExec(AMX *amx, cell *retval, int index) {
	if (amx->flags & AMX_FLAG_BROWSE) {
		return amx_Exec(amx, retval, index);
	}
	return CrashDetect::Get(amx)->DoAmxExec(retval, index);
}

PLUGIN_EXPORT unsigned int PLUGIN_CALL Supports() {
	return SUPPORTS_VERSION | SUPPORTS_AMX_NATIVES;
}

PLUGIN_EXPORT bool PLUGIN_CALL Load(void **ppData) {
	void **exports = reinterpret_cast<void**>(ppData[PLUGIN_DATA_AMX_EXPORTS]);
	::logprintf = (logprintf_t)ppData[PLUGIN_DATA_LOGPRINTF];

	void *amx_Exec_ptr = exports[PLUGIN_AMX_EXPORT_Exec];
	void *amx_Exec_sub = Hook::GetTargetAddress(reinterpret_cast<unsigned char*>(amx_Exec_ptr));

	if (amx_Exec_sub == 0) {
		new Hook(amx_Exec_ptr, (void*)AmxExec);
	} else {
		std::string module = fileutils::GetFileName(os::GetModulePathFromAddr(amx_Exec_sub));
		if (!module.empty()) {
			logprintf("  In order for runtime error detection to work crashdetect must be loaded before %s.",module.c_str());
		}
	}

	os::SetExceptionHandler(CrashDetect::OnException);
	os::SetInterruptHandler(CrashDetect::OnInterrupt);

	logprintf("  crashdetect v"PROJECT_VERSION_STRING" is OK.");
	return true;
}

PLUGIN_EXPORT int PLUGIN_CALL AmxLoad(AMX *amx) {
	int error = CrashDetect::Create(amx)->Load();
	if (error == AMX_ERR_NONE) {
		amx_SetCallback(amx, AmxCallback);
	}
	return error;
}

PLUGIN_EXPORT int PLUGIN_CALL AmxUnload(AMX *amx) {
	int error = CrashDetect::Get(amx)->Unload();
	CrashDetect::Destroy(amx);
	return error;
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2015 OpenALPR Technology, Inc.
 * Open source Automated License Plate Recognition [http://www.openalpr.com]
 *
 * This file is part of OpenALPR.
 *
 * OpenALPR is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License
 * version 3 as published by the Free Software Foundation
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include "postprocess.h"

using namespace std;

namespace alpr
{

  PostProcess::PostProcess(Config* config)
  {
    this->config = config;

    stringstream filename;
    filename << config->getPostProcessRuntimeDir() << "/" << config->country << ".patterns";

    std::ifstream infile(filename.str().c_str());

    string region, pattern;
    while (infile >> region >> pattern)
    {
      RegexRule* rule = new RegexRule(region, pattern, config->postProcessRegexLetters, config->postProcessRegexNumbers);
      //cout << "REGION: " << region << " PATTERN: " << pattern << endl;

      if (rules.find(region) == rules.end())
      {
        vector<RegexRule*> newRule;
        newRule.push_back(rule);
        rules[region] = newRule;
      }
      else
      {
        vector<RegexRule*> oldRule = rules[region];
        oldRule.push_back(rule);
        rules[region] = oldRule;
      }
    }

    //vector<RegexRule> test = rules["base"];
    //for (int i = 0; i < test.size(); i++)
    //  cout << "Rule: " << test[i].regex << endl;
  }

  PostProcess::~PostProcess()
  {
    // TODO: Delete all entries in rules vector
    map<string, vector<RegexRule*> >::iterator iter;

    for (iter = rules.begin(); iter != rules.end(); ++iter)
    {
      for (int i = 0; i < iter->second.size(); i++)
      {
        delete iter->second[i];
      }
    }
  }

  void PostProcess::addLetter(string letter, int line_index, int charposition, float score)
  {
    if (score < config->postProcessMinConfidence)
      return;

    insertLetter(letter, line_index, charposition, score);

    if (score < config->postProcessConfidenceSkipLevel)
    {
      float adjustedScore = abs(config->postProcessConfidenceSkipLevel - score) + config->postProcessMinConfidence;
      insertLetter(SKIP_CHAR, line_index, charposition, adjustedScore );
    }

    //if (letter == '0')
    //{
    //  insertLetter('O', charposition, score - 0.5);
    //}
  }

  void PostProcess::insertLetter(string letter, int line_index, int charposition, float score)
  {
    score = score - config->postProcessMinConfidence;

    int existingIndex = -1;
    if (letters.size() < charposition + 1)
    {
      for (int i = letters.size(); i < charposition + 1; i++)
      {
        vector<Letter> tmp;
        letters.push_back(tmp);
      }
    }

    for (int i = 0; i < letters[charposition].size(); i++)
    {
      if (letters[charposition][i].letter == letter &&
          letters[charposition][i].line_index == line_index &&
          letters[charposition][i].charposition == charposition)
      {
        existingIndex = i;
        break;
      }
    }

    if (existingIndex == -1)
    {
      Letter newLetter;
      newLetter.line_index = line_index;
      newLetter.charposition = charposition;
      newLetter.letter = letter;
      newLetter.occurences = 1;
      newLetter.totalscore = score;
      letters[charposition].push_back(newLetter);
    }
    else
    {
      letters[charposition][existingIndex].occurences = letters[charposition][existingIndex].occurences + 1;
      letters[charposition][existingIndex].totalscore = letters[charposition][existingIndex].totalscore + score;
    }
  }

  void PostProcess::clear()
  {
    for (int i = 0; i < letters.size(); i++)
    {
      letters[i].clear();
    }
    letters.resize(0);

    unknownCharPositions.clear();
    unknownCharPositions.resize(0);
    allPossibilities.clear();
    allPossibilitiesLetters.clear();
    //allPossibilities.resize(0);

    bestChars = "";
    matchesTemplate = false;
  }

  void PostProcess::analyze(string templateregion, int topn)
  {
    timespec startTime;
    getTimeMonotonic(&startTime);

    // Get a list of missing positions
    for (int i = letters.size() -1; i >= 0; i--)
    {
      if (letters[i].size() == 0)
      {
        unknownCharPositions.push_back(i);
      }
    }

    if (letters.size() == 0)
      return;

    // Sort the letters as they are
    for (int i = 0; i < letters.size(); i++)
    {
      if (letters[i].size() > 0)
        std::stable_sort(letters[i].begin(), letters[i].end(), letterCompare);
    }

    if (this->config->debugPostProcess)
    {
      // Print all letters
      for (int i = 0; i < letters.size(); i++)
      {
        for (int j = 0; j < letters[i].size(); j++)
          cout << "PostProcess Line " << letters[i][j].line_index << " Letter: " << letters[i][j].charposition << " " << letters[i][j].letter << " -- score: " << letters[i][j].totalscore << " -- occurences: " << letters[i][j].occurences << endl;
      }
    }

    timespec permutationStartTime;
    getTimeMonotonic(&permutationStartTime);

    findAllPermutations(templateregion, topn);

    if (config->debugTiming)
    {
      timespec permutationEndTime;
      getTimeMonotonic(&permutationEndTime);
      cout << " -- PostProcess Permutation Time: " << diffclock(permutationStartTime, permutationEndTime) << "ms." << endl;
    }

    if (allPossibilities.size() > 0)
    {

      bestChars = allPossibilities[0].letters;
      for (int z = 0; z < allPossibilities.size(); z++)
      {
        if (allPossibilities[z].matchesTemplate)
        {
          bestChars = allPossibilities[z].letters;
          break;
        }
      }

      // Now adjust the confidence scores to a percentage value
      float maxPercentScore = calculateMaxConfidenceScore();
      float highestRelativeScore = (float) allPossibilities[0].totalscore;

      for (int i = 0; i < allPossibilities.size(); i++)
      {
        allPossibilities[i].totalscore = maxPercentScore * (allPossibilities[i].totalscore / highestRelativeScore);
      }
    }

    if (this->config->debugPostProcess)
    {
      // Print top words
      for (int i = 0; i < allPossibilities.size(); i++)
      {
        cout << "Top " << topn << " Possibilities: " << allPossibilities[i].letters << " :\t" << allPossibilities[i].totalscore;
        if (allPossibilities[i].letters == bestChars)
          cout << " <--- ";
        cout << endl;
      }
      cout << allPossibilities.size() << " total permutations" << endl;
    }

    if (config->debugTiming)
    {
      timespec endTime;
      getTimeMonotonic(&endTime);
      cout << "PostProcess Time: " << diffclock(startTime, endTime) << "ms." << endl;
    }

    if (this->config->debugPostProcess)
      cout << "PostProcess Analysis Complete: " << bestChars << " -- MATCH: " << matchesTemplate << endl;
  }

  bool PostProcess::regionIsValid(std::string templateregion)
  {
    return rules.find(templateregion) != rules.end();
  }

  float PostProcess::calculateMaxConfidenceScore()
  {
    // Take the best score for each char position and average it.

    float totalScore = 0;
    int numScores = 0;
    // Get a list of missing positions
    for (int i = 0; i < letters.size(); i++)
    {
      if (letters[i].size() > 0)
      {
        totalScore += (letters[i][0].totalscore / letters[i][0].occurences) + config->postProcessMinConfidence;
        numScores++;
      }
    }

    if (numScores == 0)
      return 0;

    return totalScore / ((float) numScores);
  }

  const vector<PPResult> PostProcess::getResults()
  {
    return this->allPossibilities;
  }

  struct PermutationCompare {
    bool operator() (pair<float,vector<int> > &a, pair<float,vector<int> > &b)
    {
      return (a.first < b.first);
    }
  };

  void PostProcess::findAllPermutations(string templateregion, int topn) {

    // use a priority queue to process permutations in highest scoring order
    priority_queue<pair<float,vector<int> >, vector<pair<float,vector<int> > >, PermutationCompare> permutations;
    set<float> permutationHashes;

    // push the first word onto the queue
    float totalscore = 0;
    for (int i=0; i<letters.size(); i++)
    {
      if (letters[i].size() > 0)
        totalscore += letters[i][0].totalscore;
    }
    vector<int> v(letters.size());
    permutations.push(make_pair(totalscore, v));

    int consecutiveNonMatches = 0;
    while (permutations.size() > 0)
    {
      // get the top permutation and analyze
      pair<float, vector<int> > topPermutation = permutations.top();
      if (analyzePermutation(topPermutation.second, templateregion, topn) == true)
        consecutiveNonMatches = 0;
      else
        consecutiveNonMatches += 1;
      permutations.pop();

      if (allPossibilities.size() >= topn || consecutiveNonMatches >= 10)
        break;

      // add child permutations to queue
      for (int i=0; i<letters.size(); i++)
      {
        // no more permutations with this letter
        if (topPermutation.second[i]+1 >= letters[i].size())
          continue;

        pair<float, vector<int> > childPermutation = topPermutation;
        childPermutation.first -= letters[i][topPermutation.second[i]].totalscore - letters[i][topPermutation.second[i] + 1].totalscore;
        childPermutation.second[i] += 1;

        // ignore permutations that have already been visited (assume that score is a good hash for permutation)
        if (permutationHashes.end() != permutationHashes.find(childPermutation.first))
          continue;

        permutations.push(childPermutation);
        permutationHashes.insert(childPermutation.first);
      }
    }
  }

  bool PostProcess::analyzePermutation(vector<int> letterIndices, string templateregion, int topn)
  {
    PPResult possibility;
    possibility.letters = "";
    possibility.totalscore = 0;
    possibility.matchesTemplate = false;
    int plate_char_length = 0;

    int last_line = 0;
    for (int i = 0; i < letters.size(); i++)
    {
      if (letters[i].size() == 0)
        continue;

      Letter letter = letters[i][letterIndices[i]];

      // Add a "\n" on new lines
      if (letter.line_index != last_line)
      {
        possibility.letters = possibility.letters + "\n";
      }
      last_line = letter.line_index;

      if (letter.letter != SKIP_CHAR)
      {
        possibility.letters = possibility.letters + letter.letter;
        possibility.letter_details.push_back(letter);
        plate_char_length += 1;
      }
      possibility.totalscore = possibility.totalscore + letter.totalscore;
    }

    // ignore plates that don't fit the length requirements
    if (plate_char_length < config->postProcessMinCharacters ||
      plate_char_length > config->postProcessMaxCharacters)
      return false;

    // Apply templates
    if (templateregion != "")
    {
      vector<RegexRule*> regionRules = rules[templateregion];

      for (int i = 0; i < regionRules.size(); i++)
      {
        possibility.matchesTemplate = regionRules[i]->match(possibility.letters);
        if (possibility.matchesTemplate)
        {
          possibility.letters = regionRules[i]->filterSkips(possibility.letters);
          break;
        }
      }
    }

    // ignore duplicate words
    if (allPossibilitiesLetters.end() != allPossibilitiesLetters.find(possibility.letters))
      return false;

    // If mustMatchPattern is toggled in the config and a template is provided,
    // only include this result if there is a pattern match
    if (!config->mustMatchPattern || templateregion.size() == 0 ||
        (config->mustMatchPattern && possibility.matchesTemplate))
    {
      allPossibilities.push_back(possibility);
      allPossibilitiesLetters.insert(possibility.letters);
      return true;
    }

    return false;
  }

  std::vector<string> PostProcess::getPatterns() {
    vector<string> v;
    for(map<string,std::vector<RegexRule*> >::iterator it = rules.begin(); it != rules.end(); ++it) {
      v.push_back(it->first);
    }

    return v;
  }

  bool letterCompare( const Letter &left, const Letter &right )
  {
    if (left.totalscore < right.totalscore)
      return false;
    return true;
  }

}<commit_msg>Fixed issue with must_match_pattern not returning enough values<commit_after>/*
 * Copyright (c) 2015 OpenALPR Technology, Inc.
 * Open source Automated License Plate Recognition [http://www.openalpr.com]
 *
 * This file is part of OpenALPR.
 *
 * OpenALPR is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License
 * version 3 as published by the Free Software Foundation
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include "postprocess.h"

using namespace std;

namespace alpr
{

  PostProcess::PostProcess(Config* config)
  {
    this->config = config;

    stringstream filename;
    filename << config->getPostProcessRuntimeDir() << "/" << config->country << ".patterns";

    std::ifstream infile(filename.str().c_str());

    string region, pattern;
    while (infile >> region >> pattern)
    {
      RegexRule* rule = new RegexRule(region, pattern, config->postProcessRegexLetters, config->postProcessRegexNumbers);
      //cout << "REGION: " << region << " PATTERN: " << pattern << endl;

      if (rules.find(region) == rules.end())
      {
        vector<RegexRule*> newRule;
        newRule.push_back(rule);
        rules[region] = newRule;
      }
      else
      {
        vector<RegexRule*> oldRule = rules[region];
        oldRule.push_back(rule);
        rules[region] = oldRule;
      }
    }

    //vector<RegexRule> test = rules["base"];
    //for (int i = 0; i < test.size(); i++)
    //  cout << "Rule: " << test[i].regex << endl;
  }

  PostProcess::~PostProcess()
  {
    // TODO: Delete all entries in rules vector
    map<string, vector<RegexRule*> >::iterator iter;

    for (iter = rules.begin(); iter != rules.end(); ++iter)
    {
      for (int i = 0; i < iter->second.size(); i++)
      {
        delete iter->second[i];
      }
    }
  }

  void PostProcess::addLetter(string letter, int line_index, int charposition, float score)
  {
    if (score < config->postProcessMinConfidence)
      return;

    insertLetter(letter, line_index, charposition, score);

    if (score < config->postProcessConfidenceSkipLevel)
    {
      float adjustedScore = abs(config->postProcessConfidenceSkipLevel - score) + config->postProcessMinConfidence;
      insertLetter(SKIP_CHAR, line_index, charposition, adjustedScore );
    }

    //if (letter == '0')
    //{
    //  insertLetter('O', charposition, score - 0.5);
    //}
  }

  void PostProcess::insertLetter(string letter, int line_index, int charposition, float score)
  {
    score = score - config->postProcessMinConfidence;

    int existingIndex = -1;
    if (letters.size() < charposition + 1)
    {
      for (int i = letters.size(); i < charposition + 1; i++)
      {
        vector<Letter> tmp;
        letters.push_back(tmp);
      }
    }

    for (int i = 0; i < letters[charposition].size(); i++)
    {
      if (letters[charposition][i].letter == letter &&
          letters[charposition][i].line_index == line_index &&
          letters[charposition][i].charposition == charposition)
      {
        existingIndex = i;
        break;
      }
    }

    if (existingIndex == -1)
    {
      Letter newLetter;
      newLetter.line_index = line_index;
      newLetter.charposition = charposition;
      newLetter.letter = letter;
      newLetter.occurences = 1;
      newLetter.totalscore = score;
      letters[charposition].push_back(newLetter);
    }
    else
    {
      letters[charposition][existingIndex].occurences = letters[charposition][existingIndex].occurences + 1;
      letters[charposition][existingIndex].totalscore = letters[charposition][existingIndex].totalscore + score;
    }
  }

  void PostProcess::clear()
  {
    for (int i = 0; i < letters.size(); i++)
    {
      letters[i].clear();
    }
    letters.resize(0);

    unknownCharPositions.clear();
    unknownCharPositions.resize(0);
    allPossibilities.clear();
    allPossibilitiesLetters.clear();
    //allPossibilities.resize(0);

    bestChars = "";
    matchesTemplate = false;
  }

  void PostProcess::analyze(string templateregion, int topn)
  {
    timespec startTime;
    getTimeMonotonic(&startTime);

    // Get a list of missing positions
    for (int i = letters.size() -1; i >= 0; i--)
    {
      if (letters[i].size() == 0)
      {
        unknownCharPositions.push_back(i);
      }
    }

    if (letters.size() == 0)
      return;

    // Sort the letters as they are
    for (int i = 0; i < letters.size(); i++)
    {
      if (letters[i].size() > 0)
        std::stable_sort(letters[i].begin(), letters[i].end(), letterCompare);
    }

    if (this->config->debugPostProcess)
    {
      // Print all letters
      for (int i = 0; i < letters.size(); i++)
      {
        for (int j = 0; j < letters[i].size(); j++)
          cout << "PostProcess Line " << letters[i][j].line_index << " Letter: " << letters[i][j].charposition << " " << letters[i][j].letter << " -- score: " << letters[i][j].totalscore << " -- occurences: " << letters[i][j].occurences << endl;
      }
    }

    timespec permutationStartTime;
    getTimeMonotonic(&permutationStartTime);

    findAllPermutations(templateregion, topn);

    if (config->debugTiming)
    {
      timespec permutationEndTime;
      getTimeMonotonic(&permutationEndTime);
      cout << " -- PostProcess Permutation Time: " << diffclock(permutationStartTime, permutationEndTime) << "ms." << endl;
    }

    if (allPossibilities.size() > 0)
    {

      bestChars = allPossibilities[0].letters;
      for (int z = 0; z < allPossibilities.size(); z++)
      {
        if (allPossibilities[z].matchesTemplate)
        {
          bestChars = allPossibilities[z].letters;
          break;
        }
      }

      // Now adjust the confidence scores to a percentage value
      float maxPercentScore = calculateMaxConfidenceScore();
      float highestRelativeScore = (float) allPossibilities[0].totalscore;

      for (int i = 0; i < allPossibilities.size(); i++)
      {
        allPossibilities[i].totalscore = maxPercentScore * (allPossibilities[i].totalscore / highestRelativeScore);
      }
    }

    if (this->config->debugPostProcess)
    {
      // Print top words
      for (int i = 0; i < allPossibilities.size(); i++)
      {
        cout << "Top " << topn << " Possibilities: " << allPossibilities[i].letters << " :\t" << allPossibilities[i].totalscore;
        if (allPossibilities[i].letters == bestChars)
          cout << " <--- ";
        cout << endl;
      }
      cout << allPossibilities.size() << " total permutations" << endl;
    }

    if (config->debugTiming)
    {
      timespec endTime;
      getTimeMonotonic(&endTime);
      cout << "PostProcess Time: " << diffclock(startTime, endTime) << "ms." << endl;
    }

    if (this->config->debugPostProcess)
      cout << "PostProcess Analysis Complete: " << bestChars << " -- MATCH: " << matchesTemplate << endl;
  }

  bool PostProcess::regionIsValid(std::string templateregion)
  {
    return rules.find(templateregion) != rules.end();
  }

  float PostProcess::calculateMaxConfidenceScore()
  {
    // Take the best score for each char position and average it.

    float totalScore = 0;
    int numScores = 0;
    // Get a list of missing positions
    for (int i = 0; i < letters.size(); i++)
    {
      if (letters[i].size() > 0)
      {
        totalScore += (letters[i][0].totalscore / letters[i][0].occurences) + config->postProcessMinConfidence;
        numScores++;
      }
    }

    if (numScores == 0)
      return 0;

    return totalScore / ((float) numScores);
  }

  const vector<PPResult> PostProcess::getResults()
  {
    return this->allPossibilities;
  }

  struct PermutationCompare {
    bool operator() (pair<float,vector<int> > &a, pair<float,vector<int> > &b)
    {
      return (a.first < b.first);
    }
  };

  void PostProcess::findAllPermutations(string templateregion, int topn) {

    // use a priority queue to process permutations in highest scoring order
    priority_queue<pair<float,vector<int> >, vector<pair<float,vector<int> > >, PermutationCompare> permutations;
    set<float> permutationHashes;

    // push the first word onto the queue
    float totalscore = 0;
    for (int i=0; i<letters.size(); i++)
    {
      if (letters[i].size() > 0)
        totalscore += letters[i][0].totalscore;
    }
    vector<int> v(letters.size());
    permutations.push(make_pair(totalscore, v));

    int consecutiveNonMatches = 0;
    while (permutations.size() > 0)
    {
      // get the top permutation and analyze
      pair<float, vector<int> > topPermutation = permutations.top();
      if (analyzePermutation(topPermutation.second, templateregion, topn) == true)
        consecutiveNonMatches = 0;
      else
        consecutiveNonMatches += 1;
      permutations.pop();

      if (allPossibilities.size() >= topn || consecutiveNonMatches >= (topn*2))
        break;

      // add child permutations to queue
      for (int i=0; i<letters.size(); i++)
      {
        // no more permutations with this letter
        if (topPermutation.second[i]+1 >= letters[i].size())
          continue;

        pair<float, vector<int> > childPermutation = topPermutation;
        childPermutation.first -= letters[i][topPermutation.second[i]].totalscore - letters[i][topPermutation.second[i] + 1].totalscore;
        childPermutation.second[i] += 1;

        // ignore permutations that have already been visited (assume that score is a good hash for permutation)
        if (permutationHashes.end() != permutationHashes.find(childPermutation.first))
          continue;

        permutations.push(childPermutation);
        permutationHashes.insert(childPermutation.first);
      }
    }
  }

  bool PostProcess::analyzePermutation(vector<int> letterIndices, string templateregion, int topn)
  {
    PPResult possibility;
    possibility.letters = "";
    possibility.totalscore = 0;
    possibility.matchesTemplate = false;
    int plate_char_length = 0;

    int last_line = 0;
    for (int i = 0; i < letters.size(); i++)
    {
      if (letters[i].size() == 0)
        continue;

      Letter letter = letters[i][letterIndices[i]];

      // Add a "\n" on new lines
      if (letter.line_index != last_line)
      {
        possibility.letters = possibility.letters + "\n";
      }
      last_line = letter.line_index;

      if (letter.letter != SKIP_CHAR)
      {
        possibility.letters = possibility.letters + letter.letter;
        possibility.letter_details.push_back(letter);
        plate_char_length += 1;
      }
      possibility.totalscore = possibility.totalscore + letter.totalscore;
    }

    // ignore plates that don't fit the length requirements
    if (plate_char_length < config->postProcessMinCharacters ||
      plate_char_length > config->postProcessMaxCharacters)
      return false;

    // Apply templates
    if (templateregion != "")
    {
      vector<RegexRule*> regionRules = rules[templateregion];

      for (int i = 0; i < regionRules.size(); i++)
      {
        possibility.matchesTemplate = regionRules[i]->match(possibility.letters);
        if (possibility.matchesTemplate)
        {
          possibility.letters = regionRules[i]->filterSkips(possibility.letters);
          break;
        }
      }
    }

    // ignore duplicate words
    if (allPossibilitiesLetters.end() != allPossibilitiesLetters.find(possibility.letters))
      return false;

    // If mustMatchPattern is toggled in the config and a template is provided,
    // only include this result if there is a pattern match
    if (!config->mustMatchPattern || templateregion.size() == 0 ||
        (config->mustMatchPattern && possibility.matchesTemplate))
    {
      allPossibilities.push_back(possibility);
      allPossibilitiesLetters.insert(possibility.letters);
      return true;
    }

    return false;
  }

  std::vector<string> PostProcess::getPatterns() {
    vector<string> v;
    for(map<string,std::vector<RegexRule*> >::iterator it = rules.begin(); it != rules.end(); ++it) {
      v.push_back(it->first);
    }

    return v;
  }

  bool letterCompare( const Letter &left, const Letter &right )
  {
    if (left.totalscore < right.totalscore)
      return false;
    return true;
  }

}<|endoftext|>


<commit_before>/**
 * \file prastar.cc
 *
 *
 *
 * \author Seth Lemons
 * \date 2008-11-19
 */

#include <assert.h>
#include <math.h>
#include <errno.h>

#include <vector>
#include <limits>

extern "C" {
#include "lockfree/include/atomic.h"
}

#include "util/timer.h"
#include "util/mutex.h"
#include "util/msg_buffer.h"
#include "prastar.h"
#include "projection.h"
#include "search.h"
#include "state.h"

using namespace std;

PRAStar::PRAStarThread::PRAStarThread(PRAStar *p, vector<PRAStarThread *> *threads, CompletionCounter* cc)
	: p(p),
	  threads(threads),
	  cc(cc),
	  q_empty(true)
{
	time_spinning = 0;
	out_qs.resize(threads->size(), NULL);
        completed = false;
}

PRAStar::PRAStarThread::~PRAStarThread(void)
{
	 vector<MsgBuffer<State*> *>::iterator i;
	for (i = out_qs.begin(); i != out_qs.end(); i++)
		if (*i)
			delete *i;
}

vector<State*> *PRAStar::PRAStarThread::get_queue(void)
{
	return &q;
}

Mutex *PRAStar::PRAStarThread::get_mutex(void)
{
	return &mutex;
}

void PRAStar::PRAStarThread::post_send(void *t)
{

	PRAStarThread *thr = (PRAStarThread*) t;
	if (thr->completed) {
		thr->cc->uncomplete();
		thr->completed = false;
	}
	thr->q_empty = false;
}

bool PRAStar::PRAStarThread::flush_sends(void)
{
	unsigned int i;
	bool has_sends = false;
	for (i = 0; i < threads->size(); i += 1) {
		if (!out_qs[i])
			continue;
		if (out_qs[i]) {
			out_qs[i]->try_flush();
			if (!out_qs[i]->is_empty())
				has_sends = true;
		}
	}

	return has_sends;
}

/**
 * Flush the queue
 */
void PRAStar::PRAStarThread::flush_receives(bool has_sends)
{
	// wait for either completion or more nodes to expand
	if (open.empty())
		mutex.lock();
	else if (!mutex.try_lock())
		return;

	if (q_empty && !has_sends) {
		if (!open.empty()) {
			mutex.unlock();
			return;
		}
		completed = true;
		cc->complete();

		// busy wait
		mutex.unlock();
		while (q_empty && !cc->is_complete() && !p->is_done())
			;
		mutex.lock();

		// we are done, just return
		if (cc->is_complete()) {
			assert(q_empty);
			mutex.unlock();
			return;
		}
	}

	// got some stuff on the queue, lets do duplicate detection
	// and add stuff to the open list
	for (unsigned int i = 0; i < q.size(); i += 1) {
		State *c = q[i];
		if (c->get_f() >= p->bound.read()) {
			delete c;
			continue;
		}
		State *dup = closed.lookup(c);
		if (dup){
			if (dup->get_g() > c->get_g()) {
				dup->update(c->get_parent(),
					    c->get_c(),
					    c->get_g());
				if (dup->is_open())
					open.see_update(dup);
				else
					open.add(dup);
			}
			delete c;
		}
		else{
			open.add(c);
			closed.add(c);
		}
	}
	q.clear();
	q_empty = true;
	mutex.unlock();
}

void PRAStar::PRAStarThread::send_state(State *c)
{
/*
	unsigned long hash = p->project->project(c);
*/
	unsigned long hash =
		p->use_abstraction
		? p->project->project(c)
		: c->hash();
	unsigned int dest_tid = threads->at(hash % p->n_threads)->get_id();
	bool self_add = dest_tid == this->get_id();

	assert (p->n_threads != 1 || self_add);

	if (self_add) {
		State *dup = closed.lookup(c);
		if (dup){
			if (dup->get_g() > c->get_g()) {
				dup->update(c->get_parent(),
					    c->get_c(),
					    c->get_g());
				if (dup->is_open())
					open.see_update(dup);
				else
					open.add(dup);
			}
			delete c;
		}
		else{
			open.add(c);
			closed.add(c);
		}
		return;
	}

	// not a self add
	//
	// Do a buffered send, in which case we can just sit on a lock
	// because there is no work to do anyway.
	if (!out_qs[dest_tid]) {
		Mutex *lk = threads->at(dest_tid)->get_mutex();
		vector<State*> *qu = threads->at(dest_tid)->get_queue();
		out_qs[dest_tid] =
			new MsgBuffer<State*>(lk, qu,
					      post_send,
					      threads->at(dest_tid));
	}

	out_qs[dest_tid]->try_send(c);
}

State *PRAStar::PRAStarThread::take(void)
{
	Timer t;
	bool entered_loop = false;

	t.start();
	while (open.empty() || !q_empty) {
		entered_loop = true;

		bool has_sends = flush_sends();
		flush_receives(has_sends);

		if (cc->is_complete()){
			p->set_done();
			return NULL;
		}
        }
	t.stop();
	if (entered_loop)
		time_spinning += t.get_wall_time();

	State *ret = NULL;
	if (!p->is_done())
		ret = open.take();

        return ret;
}

/**
 * Run the search thread.
 */
void PRAStar::PRAStarThread::run(void){
        vector<State *> *children = NULL;

        while(!p->is_done()){

		State *s = take();
		if (s == NULL)
			continue;

		if (s->get_f() >= p->bound.read()) {
			open.prune();
			continue;
		}
		if (s->is_goal()) {
			p->set_path(s->get_path());
		}

		children = p->expand(s);
		for (unsigned int i = 0; i < children->size(); i += 1) {
			State *c = children->at(i);
			if (c->get_f() < p->bound.read())
				send_state(c);
			else
				delete c;
		}
		delete children;
        }
}

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

PRAStar::PRAStar(unsigned int n_threads, bool use_abst)
	: n_threads(n_threads),
	  bound(fp_infinity),
	  project(NULL),
	  path(NULL),
	  use_abstraction(use_abst){
        done = false;
}

PRAStar::~PRAStar(void)
{
        for (iter = threads.begin(); iter != threads.end(); iter++)
		delete (*iter);

}

void PRAStar::set_done()
{
	mutex.lock();
        done = true;
	mutex.unlock();
}

bool PRAStar::is_done()
{
        return done;
}

void PRAStar::set_path(vector<State *> *p)
{
	mutex.lock();
        if (this->path == NULL ||
	    this->path->at(0)->get_g() > p->at(0)->get_g()){
		this->path = p;
		bound.set(p->at(0)->get_g());
        }
	mutex.unlock();
}

bool PRAStar::has_path()
{
        bool ret;
        ret = (path != NULL);
        return ret;
}

vector<State *> *PRAStar::search(Timer *timer, State *init)
{
	project = init->get_domain()->get_projection();

        CompletionCounter cc = CompletionCounter(n_threads);

	threads.resize(n_threads, NULL);
        for (unsigned int i = 0; i < n_threads; i += 1)
                threads.at(i) = new PRAStarThread(this, &threads, &cc);

	if (use_abstraction)
		threads.at(project->project(init)%n_threads)->open.add(init);
	else
		threads.at(init->hash() % n_threads)->open.add(init);

        for (iter = threads.begin(); iter != threads.end(); iter++) {
		(*iter)->start();
        }

        for (iter = threads.begin(); iter != threads.end(); iter++)
		(*iter)->join();

        return path;
}

void PRAStar::output_stats(void)
{
	time_spinning = 0.0;
	max_open_size = 0;
	avg_open_size = 0;
        for (iter = threads.begin(); iter != threads.end(); iter++) {
		time_spinning += (*iter)->time_spinning;
		avg_open_size += (*iter)->open.get_avg_size();
		if ((*iter)->open.get_max_size() > max_open_size)
			max_open_size = (*iter)->open.get_max_size();
        }
	avg_open_size /= n_threads;

	cout << "total-time-acquiring-locks: "
	     << Mutex::get_total_lock_acquisition_time() << endl;
	cout << "average-time-acquiring-locks: "
	     << Mutex::get_avg_lock_acquisition_time() << endl;
	cout << "total-time-waiting: " << time_spinning << endl;
	cout << "avgerage-time-waiting: "
	     << time_spinning / n_threads << endl;
	cout << "average-open-size: " << avg_open_size << endl;
	cout << "max-open-size: " << max_open_size << endl;
}
<commit_msg>Fix a typeo in PRA* output.<commit_after>/**
 * \file prastar.cc
 *
 *
 *
 * \author Seth Lemons
 * \date 2008-11-19
 */

#include <assert.h>
#include <math.h>
#include <errno.h>

#include <vector>
#include <limits>

extern "C" {
#include "lockfree/include/atomic.h"
}

#include "util/timer.h"
#include "util/mutex.h"
#include "util/msg_buffer.h"
#include "prastar.h"
#include "projection.h"
#include "search.h"
#include "state.h"

using namespace std;

PRAStar::PRAStarThread::PRAStarThread(PRAStar *p, vector<PRAStarThread *> *threads, CompletionCounter* cc)
	: p(p),
	  threads(threads),
	  cc(cc),
	  q_empty(true)
{
	time_spinning = 0;
	out_qs.resize(threads->size(), NULL);
        completed = false;
}

PRAStar::PRAStarThread::~PRAStarThread(void)
{
	 vector<MsgBuffer<State*> *>::iterator i;
	for (i = out_qs.begin(); i != out_qs.end(); i++)
		if (*i)
			delete *i;
}

vector<State*> *PRAStar::PRAStarThread::get_queue(void)
{
	return &q;
}

Mutex *PRAStar::PRAStarThread::get_mutex(void)
{
	return &mutex;
}

void PRAStar::PRAStarThread::post_send(void *t)
{

	PRAStarThread *thr = (PRAStarThread*) t;
	if (thr->completed) {
		thr->cc->uncomplete();
		thr->completed = false;
	}
	thr->q_empty = false;
}

bool PRAStar::PRAStarThread::flush_sends(void)
{
	unsigned int i;
	bool has_sends = false;
	for (i = 0; i < threads->size(); i += 1) {
		if (!out_qs[i])
			continue;
		if (out_qs[i]) {
			out_qs[i]->try_flush();
			if (!out_qs[i]->is_empty())
				has_sends = true;
		}
	}

	return has_sends;
}

/**
 * Flush the queue
 */
void PRAStar::PRAStarThread::flush_receives(bool has_sends)
{
	// wait for either completion or more nodes to expand
	if (open.empty())
		mutex.lock();
	else if (!mutex.try_lock())
		return;

	if (q_empty && !has_sends) {
		if (!open.empty()) {
			mutex.unlock();
			return;
		}
		completed = true;
		cc->complete();

		// busy wait
		mutex.unlock();
		while (q_empty && !cc->is_complete() && !p->is_done())
			;
		mutex.lock();

		// we are done, just return
		if (cc->is_complete()) {
			assert(q_empty);
			mutex.unlock();
			return;
		}
	}

	// got some stuff on the queue, lets do duplicate detection
	// and add stuff to the open list
	for (unsigned int i = 0; i < q.size(); i += 1) {
		State *c = q[i];
		if (c->get_f() >= p->bound.read()) {
			delete c;
			continue;
		}
		State *dup = closed.lookup(c);
		if (dup){
			if (dup->get_g() > c->get_g()) {
				dup->update(c->get_parent(),
					    c->get_c(),
					    c->get_g());
				if (dup->is_open())
					open.see_update(dup);
				else
					open.add(dup);
			}
			delete c;
		}
		else{
			open.add(c);
			closed.add(c);
		}
	}
	q.clear();
	q_empty = true;
	mutex.unlock();
}

void PRAStar::PRAStarThread::send_state(State *c)
{
/*
	unsigned long hash = p->project->project(c);
*/
	unsigned long hash =
		p->use_abstraction
		? p->project->project(c)
		: c->hash();
	unsigned int dest_tid = threads->at(hash % p->n_threads)->get_id();
	bool self_add = dest_tid == this->get_id();

	assert (p->n_threads != 1 || self_add);

	if (self_add) {
		State *dup = closed.lookup(c);
		if (dup){
			if (dup->get_g() > c->get_g()) {
				dup->update(c->get_parent(),
					    c->get_c(),
					    c->get_g());
				if (dup->is_open())
					open.see_update(dup);
				else
					open.add(dup);
			}
			delete c;
		}
		else{
			open.add(c);
			closed.add(c);
		}
		return;
	}

	// not a self add
	//
	// Do a buffered send, in which case we can just sit on a lock
	// because there is no work to do anyway.
	if (!out_qs[dest_tid]) {
		Mutex *lk = threads->at(dest_tid)->get_mutex();
		vector<State*> *qu = threads->at(dest_tid)->get_queue();
		out_qs[dest_tid] =
			new MsgBuffer<State*>(lk, qu,
					      post_send,
					      threads->at(dest_tid));
	}

	out_qs[dest_tid]->try_send(c);
}

State *PRAStar::PRAStarThread::take(void)
{
	Timer t;
	bool entered_loop = false;

	t.start();
	while (open.empty() || !q_empty) {
		entered_loop = true;

		bool has_sends = flush_sends();
		flush_receives(has_sends);

		if (cc->is_complete()){
			p->set_done();
			return NULL;
		}
        }
	t.stop();
	if (entered_loop)
		time_spinning += t.get_wall_time();

	State *ret = NULL;
	if (!p->is_done())
		ret = open.take();

        return ret;
}

/**
 * Run the search thread.
 */
void PRAStar::PRAStarThread::run(void){
        vector<State *> *children = NULL;

        while(!p->is_done()){

		State *s = take();
		if (s == NULL)
			continue;

		if (s->get_f() >= p->bound.read()) {
			open.prune();
			continue;
		}
		if (s->is_goal()) {
			p->set_path(s->get_path());
		}

		children = p->expand(s);
		for (unsigned int i = 0; i < children->size(); i += 1) {
			State *c = children->at(i);
			if (c->get_f() < p->bound.read())
				send_state(c);
			else
				delete c;
		}
		delete children;
        }
}

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

PRAStar::PRAStar(unsigned int n_threads, bool use_abst)
	: n_threads(n_threads),
	  bound(fp_infinity),
	  project(NULL),
	  path(NULL),
	  use_abstraction(use_abst){
        done = false;
}

PRAStar::~PRAStar(void)
{
        for (iter = threads.begin(); iter != threads.end(); iter++)
		delete (*iter);

}

void PRAStar::set_done()
{
	mutex.lock();
        done = true;
	mutex.unlock();
}

bool PRAStar::is_done()
{
        return done;
}

void PRAStar::set_path(vector<State *> *p)
{
	mutex.lock();
        if (this->path == NULL ||
	    this->path->at(0)->get_g() > p->at(0)->get_g()){
		this->path = p;
		bound.set(p->at(0)->get_g());
        }
	mutex.unlock();
}

bool PRAStar::has_path()
{
        bool ret;
        ret = (path != NULL);
        return ret;
}

vector<State *> *PRAStar::search(Timer *timer, State *init)
{
	project = init->get_domain()->get_projection();

        CompletionCounter cc = CompletionCounter(n_threads);

	threads.resize(n_threads, NULL);
        for (unsigned int i = 0; i < n_threads; i += 1)
                threads.at(i) = new PRAStarThread(this, &threads, &cc);

	if (use_abstraction)
		threads.at(project->project(init)%n_threads)->open.add(init);
	else
		threads.at(init->hash() % n_threads)->open.add(init);

        for (iter = threads.begin(); iter != threads.end(); iter++) {
		(*iter)->start();
        }

        for (iter = threads.begin(); iter != threads.end(); iter++)
		(*iter)->join();

        return path;
}

void PRAStar::output_stats(void)
{
	time_spinning = 0.0;
	max_open_size = 0;
	avg_open_size = 0;
        for (iter = threads.begin(); iter != threads.end(); iter++) {
		time_spinning += (*iter)->time_spinning;
		avg_open_size += (*iter)->open.get_avg_size();
		if ((*iter)->open.get_max_size() > max_open_size)
			max_open_size = (*iter)->open.get_max_size();
        }
	avg_open_size /= n_threads;

	cout << "total-time-acquiring-locks: "
	     << Mutex::get_total_lock_acquisition_time() << endl;
	cout << "average-time-acquiring-locks: "
	     << Mutex::get_avg_lock_acquisition_time() << endl;
	cout << "total-time-waiting: " << time_spinning << endl;
	cout << "average-time-waiting: "
	     << time_spinning / n_threads << endl;
	cout << "average-open-size: " << avg_open_size << endl;
	cout << "max-open-size: " << max_open_size << endl;
}
<|endoftext|>


<commit_before>#include <Python.h>
#include "structmember.h"

#include <cassert>
#include <string>
#include <iostream>

#include <indri/CompressedCollection.hpp>
#include <indri/DiskIndex.hpp>
#include <indri/QueryEnvironment.hpp>
#include <indri/Path.hpp>

using std::string;

#define CHECK_EQ(first, second) assert(first == second)

// Index

typedef struct {
    PyObject_HEAD

    indri::api::Parameters* parameters_;

    indri::collection::CompressedCollection* collection_;
    indri::index::DiskIndex* index_;

    indri::api::QueryEnvironment* query_env_;
} Index;

static void Index_dealloc(Index* self) {
    self->ob_type->tp_free((PyObject*) self);

    self->collection_->close();
    self->index_->close();
    self->query_env_->close();

    delete self->parameters_;

    delete self->collection_;
    delete self->index_;
    delete self->query_env_;
}

static PyObject* Index_new(PyTypeObject* type, PyObject* args, PyObject* kwds) {
    Index* self;

    self = (Index*) type->tp_alloc(type, 0);
    if (self != NULL) {
        self->parameters_ = new indri::api::Parameters();

        self->collection_ = new indri::collection::CompressedCollection();
        self->index_ = new indri::index::DiskIndex();

        self->query_env_ = new indri::api::QueryEnvironment();
    }

    return (PyObject*) self;
}

static int Index_init(Index* self, PyObject* args, PyObject* kwds) {
    PyObject* repository_path_object = NULL;

    static char* kwlist[] = {"repository_path", NULL};

    if (!PyArg_ParseTupleAndKeywords(args, kwds, "S", kwlist,
                                     &repository_path_object)) {
        return -1;
    }

    const char* repository_path = PyString_AsString(repository_path_object);

    // Load parameters.
    self->parameters_->loadFile(indri::file::Path::combine(repository_path, "manifest"));

    const std::string collection_path =
        indri::file::Path::combine(repository_path, "collection");

    try {
        self->collection_->open(collection_path);
    } catch (const lemur::api::Exception& e) {
        PyErr_SetString(PyExc_IOError, e.what().c_str());

        return -1;
    }

    // Load index.
    std::string index_path = "index";

    indri::api::Parameters container = (*self->parameters_)["indexes"];

    if(container.exists("index")) {
        indri::api::Parameters indexes = container["index"];

        if (indexes.size() != 1) {
            PyErr_SetString(PyExc_IOError, "Indri repository contain more than one index.");

            return -1;
        }

        index_path = indri::file::Path::combine(
            index_path, (std::string) indexes[static_cast<size_t>(0)]);
    } else {
        PyErr_SetString(PyExc_IOError, "Indri repository does not contain an index.");

        return -1;
    }

    try {
        self->index_->open(repository_path, index_path);
    } catch (const lemur::api::Exception& e) {
        PyErr_SetString(PyExc_IOError, e.what().c_str());

        return -1;
    }

    try {
        self->query_env_->addIndex(repository_path);
    } catch (const lemur::api::Exception& e) {
        PyErr_SetString(PyExc_IOError, e.what().c_str());

        return -1;
    }

    return 0;
}

static PyMemberDef Index_members[] = {
    {NULL}  /* Sentinel */
};

static PyObject* Index_document(Index* self, PyObject* args) {
    int int_document_id;

    if (!PyArg_ParseTuple(args, "i", &int_document_id)) {
        return NULL;
    }

    if (int_document_id < self->index_->documentBase() ||
        int_document_id >= self->index_->documentMaximum()) {
        PyErr_SetString(
            PyExc_IndexError,
            "Specified internal document identifier is out of bounds.");

        return NULL;
    }

    const string ext_document_id = self->collection_->retrieveMetadatum(
        int_document_id, "docno");
    const indri::index::TermList* const term_list = self->index_->termList(
        int_document_id);

    PyObject* terms = PyTuple_New(term_list->terms().size());

    indri::utility::greedy_vector<lemur::api::TERMID_T>::const_iterator term_it =
      term_list->terms().begin();

    Py_ssize_t pos = 0;
    for (; term_it != term_list->terms().end(); ++term_it, ++pos) {
      PyTuple_SetItem(terms, pos, PyInt_FromLong(*term_it));
    }

    delete term_list;

    return PyTuple_Pack(2, PyString_FromString(ext_document_id.c_str()), terms);
}

static PyObject* Index_document_base(Index* self) {
    return PyInt_FromLong(self->index_->documentBase());
}

static PyObject* Index_maximum_document(Index* self) {
    return PyInt_FromLong(self->index_->documentMaximum());
}

static PyObject* Index_run_query(Index* self, PyObject* args) {
    char* query_str;
    long results_requested = 100;

    if (!PyArg_ParseTuple(args, "s|i", &query_str, &results_requested)) {
        return NULL;
    }

    const std::vector<indri::api::ScoredExtentResult> query_results =
        self->query_env_->runQuery(query_str, results_requested);

    PyObject* results = PyTuple_New(query_results.size());

    std::vector<indri::api::ScoredExtentResult>::const_iterator it = query_results.begin();

    Py_ssize_t pos = 0;
    for (; it != query_results.end(); ++it, ++pos) {
      PyTuple_SetItem(
          results,
          pos,
          PyTuple_Pack(2, PyInt_FromLong(it->document), PyFloat_FromDouble(it->score)));
    }

    return results;
}

static PyObject* Index_get_dictionary(Index* self, PyObject* args) {
    indri::index::VocabularyIterator* const vocabulary_it = self->index_->vocabularyIterator();

    PyObject* const token2id = PyDict_New();
    PyObject* const id2token = PyDict_New();
    PyObject* const id2df = PyDict_New();

    vocabulary_it->startIteration();

    while (!vocabulary_it->finished()) {
        indri::index::DiskTermData* const term_data = vocabulary_it->currentEntry();

        const lemur::api::TERMID_T term_id = term_data->termID;
        const string term = term_data->termData->term;

        const unsigned int document_frequency = term_data->termData->corpus.documentCount;

        PyDict_SetItemString(token2id,
                             term.c_str(),
                             PyInt_FromLong(term_id));

        PyDict_SetItem(id2token,
                       PyInt_FromLong(term_id),
                       PyString_FromString(term.c_str()));

        PyDict_SetItem(id2df,
                       PyInt_FromLong(term_id),
                       PyInt_FromLong(document_frequency));

        vocabulary_it->nextEntry();
    }

    delete vocabulary_it;

    CHECK_EQ(PyDict_Size(token2id), self->index_->uniqueTermCount());
    CHECK_EQ(PyDict_Size(id2token), self->index_->uniqueTermCount());
    CHECK_EQ(PyDict_Size(id2df), self->index_->uniqueTermCount());

    return PyTuple_Pack(3, token2id, id2token, id2df);
}

static PyMethodDef Index_methods[] = {
    {"document", (PyCFunction) Index_document, METH_VARARGS,
     "Return a document (ext_document_id, terms) pair."},
    {"document_base", (PyCFunction) Index_document_base, METH_NOARGS,
     "Returns the lower bound document identifier (inclusive)."},
    {"maximum_document", (PyCFunction) Index_maximum_document, METH_NOARGS,
     "Returns the upper bound document identifier (exclusive)."},
    {"query", (PyCFunction) Index_run_query, METH_VARARGS,
     "Queries an Indri index."},
    {"get_dictionary", (PyCFunction) Index_get_dictionary, METH_NOARGS,
     "Extracts the dictionary from the Index."},
    {NULL}  /* Sentinel */
};

static PyTypeObject IndexType = {
    PyObject_HEAD_INIT(NULL)
    0,                         /*ob_size*/
    "pyndri.Index",             /*tp_name*/
    sizeof(Index),             /*tp_basicsize*/
    0,                         /*tp_itemsize*/
    (destructor) Index_dealloc, /*tp_dealloc*/
    0,                         /*tp_print*/
    0,                         /*tp_getattr*/
    0,                         /*tp_setattr*/
    0,                         /*tp_compare*/
    0,                         /*tp_repr*/
    0,                         /*tp_as_number*/
    0,                         /*tp_as_sequence*/
    0,                         /*tp_as_mapping*/
    0,                         /*tp_hash */
    0,                         /*tp_call*/
    0,                         /*tp_str*/
    0,                         /*tp_getattro*/
    0,                         /*tp_setattro*/
    0,                         /*tp_as_buffer*/
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
    "Index objects",           /* tp_doc */
    0,                   /* tp_traverse */
    0,                   /* tp_clear */
    0,                   /* tp_richcompare */
    0,                   /* tp_weaklistoffset */
    0,                   /* tp_iter */
    0,                   /* tp_iternext */
    Index_methods,             /* tp_methods */
    Index_members,             /* tp_members */
    0,                         /* tp_getset */
    0,                         /* tp_base */
    0,                         /* tp_dict */
    0,                         /* tp_descr_get */
    0,                         /* tp_descr_set */
    0,                         /* tp_dictoffset */
    (initproc) Index_init,      /* tp_init */
    0,                         /* tp_alloc */
    Index_new,                 /* tp_new */
};

// Module methods.

static PyMethodDef PyndriMethods[] = {
    {NULL, NULL, 0, NULL}
};

PyMODINIT_FUNC initpyndri(void) {
    if (PyType_Ready(&IndexType) < 0) {
        return;
    }

    PyObject* const module = Py_InitModule("pyndri", PyndriMethods);

    Py_INCREF(&IndexType);
    PyModule_AddObject(module, "Index", (PyObject*) &IndexType);
}
<commit_msg>Adds additional check for document frequencies.<commit_after>#include <Python.h>
#include "structmember.h"

#include <cassert>
#include <string>
#include <iostream>

#include <indri/CompressedCollection.hpp>
#include <indri/DiskIndex.hpp>
#include <indri/QueryEnvironment.hpp>
#include <indri/Path.hpp>

using std::string;

#define CHECK_EQ(first, second) assert(first == second)
#define CHECK_GT(first, second) assert(first > second)

// Index

typedef struct {
    PyObject_HEAD

    indri::api::Parameters* parameters_;

    indri::collection::CompressedCollection* collection_;
    indri::index::DiskIndex* index_;

    indri::api::QueryEnvironment* query_env_;
} Index;

static void Index_dealloc(Index* self) {
    self->ob_type->tp_free((PyObject*) self);

    self->collection_->close();
    self->index_->close();
    self->query_env_->close();

    delete self->parameters_;

    delete self->collection_;
    delete self->index_;
    delete self->query_env_;
}

static PyObject* Index_new(PyTypeObject* type, PyObject* args, PyObject* kwds) {
    Index* self;

    self = (Index*) type->tp_alloc(type, 0);
    if (self != NULL) {
        self->parameters_ = new indri::api::Parameters();

        self->collection_ = new indri::collection::CompressedCollection();
        self->index_ = new indri::index::DiskIndex();

        self->query_env_ = new indri::api::QueryEnvironment();
    }

    return (PyObject*) self;
}

static int Index_init(Index* self, PyObject* args, PyObject* kwds) {
    PyObject* repository_path_object = NULL;

    static char* kwlist[] = {"repository_path", NULL};

    if (!PyArg_ParseTupleAndKeywords(args, kwds, "S", kwlist,
                                     &repository_path_object)) {
        return -1;
    }

    const char* repository_path = PyString_AsString(repository_path_object);

    // Load parameters.
    self->parameters_->loadFile(indri::file::Path::combine(repository_path, "manifest"));

    const std::string collection_path =
        indri::file::Path::combine(repository_path, "collection");

    try {
        self->collection_->open(collection_path);
    } catch (const lemur::api::Exception& e) {
        PyErr_SetString(PyExc_IOError, e.what().c_str());

        return -1;
    }

    // Load index.
    std::string index_path = "index";

    indri::api::Parameters container = (*self->parameters_)["indexes"];

    if(container.exists("index")) {
        indri::api::Parameters indexes = container["index"];

        if (indexes.size() != 1) {
            PyErr_SetString(PyExc_IOError, "Indri repository contain more than one index.");

            return -1;
        }

        index_path = indri::file::Path::combine(
            index_path, (std::string) indexes[static_cast<size_t>(0)]);
    } else {
        PyErr_SetString(PyExc_IOError, "Indri repository does not contain an index.");

        return -1;
    }

    try {
        self->index_->open(repository_path, index_path);
    } catch (const lemur::api::Exception& e) {
        PyErr_SetString(PyExc_IOError, e.what().c_str());

        return -1;
    }

    try {
        self->query_env_->addIndex(repository_path);
    } catch (const lemur::api::Exception& e) {
        PyErr_SetString(PyExc_IOError, e.what().c_str());

        return -1;
    }

    return 0;
}

static PyMemberDef Index_members[] = {
    {NULL}  /* Sentinel */
};

static PyObject* Index_document(Index* self, PyObject* args) {
    int int_document_id;

    if (!PyArg_ParseTuple(args, "i", &int_document_id)) {
        return NULL;
    }

    if (int_document_id < self->index_->documentBase() ||
        int_document_id >= self->index_->documentMaximum()) {
        PyErr_SetString(
            PyExc_IndexError,
            "Specified internal document identifier is out of bounds.");

        return NULL;
    }

    const string ext_document_id = self->collection_->retrieveMetadatum(
        int_document_id, "docno");
    const indri::index::TermList* const term_list = self->index_->termList(
        int_document_id);

    PyObject* terms = PyTuple_New(term_list->terms().size());

    indri::utility::greedy_vector<lemur::api::TERMID_T>::const_iterator term_it =
      term_list->terms().begin();

    Py_ssize_t pos = 0;
    for (; term_it != term_list->terms().end(); ++term_it, ++pos) {
      PyTuple_SetItem(terms, pos, PyInt_FromLong(*term_it));
    }

    delete term_list;

    return PyTuple_Pack(2, PyString_FromString(ext_document_id.c_str()), terms);
}

static PyObject* Index_document_base(Index* self) {
    return PyInt_FromLong(self->index_->documentBase());
}

static PyObject* Index_maximum_document(Index* self) {
    return PyInt_FromLong(self->index_->documentMaximum());
}

static PyObject* Index_run_query(Index* self, PyObject* args) {
    char* query_str;
    long results_requested = 100;

    if (!PyArg_ParseTuple(args, "s|i", &query_str, &results_requested)) {
        return NULL;
    }

    const std::vector<indri::api::ScoredExtentResult> query_results =
        self->query_env_->runQuery(query_str, results_requested);

    PyObject* results = PyTuple_New(query_results.size());

    std::vector<indri::api::ScoredExtentResult>::const_iterator it = query_results.begin();

    Py_ssize_t pos = 0;
    for (; it != query_results.end(); ++it, ++pos) {
      PyTuple_SetItem(
          results,
          pos,
          PyTuple_Pack(2, PyInt_FromLong(it->document), PyFloat_FromDouble(it->score)));
    }

    return results;
}

static PyObject* Index_get_dictionary(Index* self, PyObject* args) {
    indri::index::VocabularyIterator* const vocabulary_it = self->index_->vocabularyIterator();

    PyObject* const token2id = PyDict_New();
    PyObject* const id2token = PyDict_New();
    PyObject* const id2df = PyDict_New();

    vocabulary_it->startIteration();

    while (!vocabulary_it->finished()) {
        indri::index::DiskTermData* const term_data = vocabulary_it->currentEntry();

        const lemur::api::TERMID_T term_id = term_data->termID;
        const string term = term_data->termData->term;

        const unsigned int document_frequency = term_data->termData->corpus.documentCount;

        CHECK_GT(document_frequency, 0);

        PyDict_SetItemString(token2id,
                             term.c_str(),
                             PyInt_FromLong(term_id));

        PyDict_SetItem(id2token,
                       PyInt_FromLong(term_id),
                       PyString_FromString(term.c_str()));

        PyDict_SetItem(id2df,
                       PyInt_FromLong(term_id),
                       PyInt_FromLong(document_frequency));

        vocabulary_it->nextEntry();
    }

    delete vocabulary_it;

    CHECK_EQ(PyDict_Size(token2id), self->index_->uniqueTermCount());
    CHECK_EQ(PyDict_Size(id2token), self->index_->uniqueTermCount());
    CHECK_EQ(PyDict_Size(id2df), self->index_->uniqueTermCount());

    return PyTuple_Pack(3, token2id, id2token, id2df);
}

static PyMethodDef Index_methods[] = {
    {"document", (PyCFunction) Index_document, METH_VARARGS,
     "Return a document (ext_document_id, terms) pair."},
    {"document_base", (PyCFunction) Index_document_base, METH_NOARGS,
     "Returns the lower bound document identifier (inclusive)."},
    {"maximum_document", (PyCFunction) Index_maximum_document, METH_NOARGS,
     "Returns the upper bound document identifier (exclusive)."},
    {"query", (PyCFunction) Index_run_query, METH_VARARGS,
     "Queries an Indri index."},
    {"get_dictionary", (PyCFunction) Index_get_dictionary, METH_NOARGS,
     "Extracts the dictionary from the Index."},
    {NULL}  /* Sentinel */
};

static PyTypeObject IndexType = {
    PyObject_HEAD_INIT(NULL)
    0,                         /*ob_size*/
    "pyndri.Index",             /*tp_name*/
    sizeof(Index),             /*tp_basicsize*/
    0,                         /*tp_itemsize*/
    (destructor) Index_dealloc, /*tp_dealloc*/
    0,                         /*tp_print*/
    0,                         /*tp_getattr*/
    0,                         /*tp_setattr*/
    0,                         /*tp_compare*/
    0,                         /*tp_repr*/
    0,                         /*tp_as_number*/
    0,                         /*tp_as_sequence*/
    0,                         /*tp_as_mapping*/
    0,                         /*tp_hash */
    0,                         /*tp_call*/
    0,                         /*tp_str*/
    0,                         /*tp_getattro*/
    0,                         /*tp_setattro*/
    0,                         /*tp_as_buffer*/
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
    "Index objects",           /* tp_doc */
    0,                   /* tp_traverse */
    0,                   /* tp_clear */
    0,                   /* tp_richcompare */
    0,                   /* tp_weaklistoffset */
    0,                   /* tp_iter */
    0,                   /* tp_iternext */
    Index_methods,             /* tp_methods */
    Index_members,             /* tp_members */
    0,                         /* tp_getset */
    0,                         /* tp_base */
    0,                         /* tp_dict */
    0,                         /* tp_descr_get */
    0,                         /* tp_descr_set */
    0,                         /* tp_dictoffset */
    (initproc) Index_init,      /* tp_init */
    0,                         /* tp_alloc */
    Index_new,                 /* tp_new */
};

// Module methods.

static PyMethodDef PyndriMethods[] = {
    {NULL, NULL, 0, NULL}
};

PyMODINIT_FUNC initpyndri(void) {
    if (PyType_Ready(&IndexType) < 0) {
        return;
    }

    PyObject* const module = Py_InitModule("pyndri", PyndriMethods);

    Py_INCREF(&IndexType);
    PyModule_AddObject(module, "Index", (PyObject*) &IndexType);
}
<|endoftext|>