<commit_before>// For conditions of distribution and use, see copyright notice in LICENSE

#include "StableHeaders.h"
#include "Mesh.h"
#include "Framework.h"
#include "GraphicsWorld.h"
#include "Placeable.h"
#include "Scene/Scene.h"
#include "AttributeMetadata.h"
#include "LoggingFunctions.h"
#include "AssetRefListener.h"
#include "IMeshAsset.h"
#include "IMaterialAsset.h"

#include <Engine/Scene/Scene.h>
#include <Engine/Scene/Node.h>
#include <Engine/Graphics/AnimatedModel.h>
#include <Engine/Resource/ResourceCache.h>

namespace Tundra
{

Mesh::Mesh(Urho3D::Context* context, Scene* scene) :
    IComponent(context, scene),
    INIT_ATTRIBUTE_VALUE(nodeTransformation, "Transform", Transform(float3(0,0,0),float3(0,0,0),float3(1,1,1))),
    INIT_ATTRIBUTE_VALUE(meshRef, "Mesh ref", AssetReference("", "OgreMesh")),
    INIT_ATTRIBUTE_VALUE(skeletonRef, "Skeleton ref", AssetReference("", "OgreSkeleton")),
    INIT_ATTRIBUTE_VALUE(materialRefs, "Material refs", AssetReferenceList("OgreMaterial")),
    INIT_ATTRIBUTE_VALUE(drawDistance, "Draw distance", 0.0f),
    INIT_ATTRIBUTE_VALUE(castShadows, "Cast shadows", false),
    INIT_ATTRIBUTE_VALUE(useInstancing, "Use instancing", false)
{
    if (scene)
        world_ = scene->Subsystem<GraphicsWorld>();

    static AttributeMetadata drawDistanceData("", "0", "10000");
    drawDistance.SetMetadata(&drawDistanceData);

    static AttributeMetadata materialMetadata;
    materialMetadata.elementType = "AssetReference";
    materialRefs.SetMetadata(&materialMetadata);

    ParentEntitySet.Connect(this, &Mesh::UpdateSignals);
}

Mesh::~Mesh()
{
    if (world_.Expired())
    {
        if (mesh_)
            LogError("Mesh: World has expired, skipping uninitialization!");
        return;
    }

    if (mesh_)
    {
        MeshAboutToBeDestroyed.Emit();

        mesh_.Reset();
        // The mesh component will be destroyed along with the adjustment node
        adjustmentNode_->Remove();
        adjustmentNode_.Reset();
    }
}

Urho3D::Node* Mesh::BoneNode(const String& name) const
{
    // When a skeletal mesh is created, the bone hierarchy will be under the adjustment node
    return adjustmentNode_ ? adjustmentNode_->GetChild(name, true) : nullptr;
}

void Mesh::DeserializeFrom(Urho3D::XMLElement& element, AttributeChange::Type change)
{
    if (!BeginDeserialization(element))
        return;

    if (change == AttributeChange::Default)
        change = updateMode;
    assert(change != AttributeChange::Default);

    Urho3D::XMLElement attributeElement = element.GetChild("attribute");
    while(attributeElement)
    {
        if (attributeElement.GetAttribute("id").Empty() && attributeElement.GetAttribute("name").Compare("Mesh materials", false) == 0)
            attributeElement.SetAttribute("name", "Material refs");
        DeserializeAttributeFrom(attributeElement, change);
        attributeElement = attributeElement.GetNext("attribute");
    }
}

Urho3D::AnimatedModel* Mesh::UrhoMesh() const
{
    return mesh_;
}

void Mesh::UpdateSignals()
{
    Entity* parent = ParentEntity();
    if (!parent)
        return;

    // If scene is not view-enabled, no further action
    if (!ViewEnabled())
        return;

    meshRefListener_ = new AssetRefListener();
    materialRefListListener_ = new AssetRefListListener(framework->Asset());

    parent->ComponentAdded.Connect(this, &Mesh::OnComponentStructureChanged);
    parent->ComponentRemoved.Connect(this, &Mesh::OnComponentStructureChanged);

    if (parent->ParentScene())
        world_ = parent->ParentScene()->Subsystem<GraphicsWorld>();

    if (world_ && !mesh_)
    {
        Urho3D::Scene* urhoScene = world_->UrhoScene();
        adjustmentNode_ = urhoScene->CreateChild("AdjustmentNode");

        // Make the entity & component links for identifying raycasts
        adjustmentNode_->SetVar(GraphicsWorld::entityLink, Variant(WeakPtr<RefCounted>(parent)));
        adjustmentNode_->SetVar(GraphicsWorld::componentLink, Variant(WeakPtr<RefCounted>(this)));

        mesh_ = adjustmentNode_->CreateComponent<Urho3D::AnimatedModel>();

        // Connect ref listeners
        meshRefListener_->Loaded.Connect(this, &Mesh::OnMeshAssetLoaded);
        materialRefListListener_->Changed.Connect(this, &Mesh::OnMaterialAssetRefsChanged);
        materialRefListListener_->Failed.Connect(this, &Mesh::OnMaterialAssetFailed);
        materialRefListListener_->Loaded.Connect(this, &Mesh::OnMaterialAssetLoaded);
    }

    // Make sure we attach to the Placeable if exists.
    AttachMesh();
}

void Mesh::DetachMesh()
{
    if (!mesh_ || world_.Expired())
        return;

    if (placeable_)
    {
        Urho3D::Scene* urhoScene = world_->UrhoScene();
        // When removed from the placeable, attach to scene root to avoid being removed from scene
        adjustmentNode_->SetParent(urhoScene);
        placeable_.Reset();
        mesh_->SetEnabled(false); // We should not render while detached
    }
}

void Mesh::AttachMesh()
{
    if (!mesh_ || world_.Expired())
        return;

    // Detach first, in case the original placeable no longer exists
    DetachMesh();

    Entity *entity = ParentEntity();
    if (!entity)
        return;
    placeable_ = entity->Component<Placeable>();
    if (!placeable_)
        return;

    Urho3D::Node* placeableNode = placeable_->UrhoSceneNode();
    if (!placeableNode)
    {
        LogError("Can not attach mesh: placeable does not have an Urho3D scene node");
        return;
    }
    adjustmentNode_->SetParent(placeableNode);
    mesh_->SetEnabled(true);
}

void Mesh::OnComponentStructureChanged(IComponent*, AttributeChange::Type)
{
    // No-op if attached to the same placeable already
    if (placeable_ == parentEntity->Component<Placeable>())
        return;

    AttachMesh();
}

void Mesh::AttributesChanged()
{
    // None of the attributes have an effect when the scene is not viewenabled and there is no actual mesh
    if (!mesh_)
        return;

    if (drawDistance.ValueChanged())
        mesh_->SetDrawDistance(drawDistance.Get());
    if (castShadows.ValueChanged())
        mesh_->SetCastShadows(castShadows.Get());
    if (nodeTransformation.ValueChanged())
    {
        const Transform &newTransform = nodeTransformation.Get();
        adjustmentNode_->SetPosition(newTransform.pos);
        adjustmentNode_->SetRotation(newTransform.Orientation());
        adjustmentNode_->SetScale(newTransform.scale);
    }
    if (meshRef.ValueChanged() && meshRefListener_)
    {
        /// @todo Why is this warning here? Pretty normal to clear mesh ref...
        if (meshRef.Get().ref.Trimmed().Empty())
            LogDebug("Warning: Mesh \"" + this->parentEntity->Name() + "\" mesh ref was set to an empty reference!");
        meshRefListener_->HandleAssetRefChange(&meshRef);
    }
    if (materialRefs.ValueChanged() && materialRefListListener_)
    {
        /* Let the listener resolve and cleanup the refs, while us keeping the originals intact.
           Changes are handled in OnMaterialAssetRefsChanged/Failed/Loaded. */
        materialRefListListener_->HandleChange(materialRefs.Get());
    }
    if (skeletonRef.ValueChanged())
    {
        /// \todo Implement
    }
}

void Mesh::OnMeshAssetLoaded(AssetPtr asset)
{
    IMeshAsset* mAsset = dynamic_cast<IMeshAsset*>(asset.Get());
    if (!mAsset)
    {
        LogErrorF("Mesh: Mesh asset load finished for '%s', but downloaded asset was not of type IMeshAsset!", asset->Name().CString());
        return;
    }

    if (mesh_)
    {
        mesh_->SetModel(mAsset->UrhoModel());

        // Apply all materials that have been loaded so far.
        // OnMaterialAssetLoaded will do the right thing once model has been set.
        Vector<AssetPtr> materialAssets = materialRefListListener_->Assets();
        for(uint mi=0; mi<materialAssets.Size(); ++mi)
        {
            AssetPtr &materialAssetPtr = materialAssets[mi];
            IMaterialAsset *materialAsset = dynamic_cast<IMaterialAsset*>(materialAssetPtr.Get());
            if (materialAsset && materialAsset->IsLoaded())
            {
                if (mi < mesh_->GetNumGeometries())
                    mesh_->SetMaterial(mi, materialAsset->UrhoMaterial());
                else
                    LogWarningF("Mesh: Illegal submesh index %d for material %s. Target mesh %s has %d submeshes.", mi, materialAsset->Name().CString(), meshRef.Get().ref.CString(), mesh_->GetNumGeometries());
            }
        }
    }
    else
        LogWarningF("Mesh: Model asset loaded but target mesh has not been created yet in %s", ParentEntity()->ToString().CString());
}

void Mesh::OnMaterialAssetRefsChanged(const AssetReferenceList &mRefs)
{
    if (!mesh_ || !mesh_->GetModel())
        return;

    for (uint gi=0; gi<mesh_->GetNumGeometries(); ++gi)
    {
        /** @todo Set empty material to these indexes!
            For Lasse to fill.

        if (gi >= mRefs.Size() || mRefs.refs[gi].ref.Empty())
            mesh_->SetMaterial(index, <default_empty_material> or nullptr?);
        */
    }
}

void Mesh::OnMaterialAssetFailed(uint index, IAssetTransfer* /*transfer*/, String /*error*/)
{
    /** @todo Set error material to these indexes!
        For Lasse to fill.

    if (mesh_ && mesh_->GetModel())
        mesh_->SetMaterial(index, <default_error_material>);
    */
}

void Mesh::OnMaterialAssetLoaded(uint index, AssetPtr asset)
{
    IMaterialAsset* mAsset = dynamic_cast<IMaterialAsset*>(asset.Get());
    if (!mAsset)
    {
        LogErrorF("Mesh: Material asset load finished for '%s', but downloaded asset was not of type IMaterialAsset!", asset->Name().CString());
        return;
    }

    if (mesh_ && mesh_->GetModel())
    {
        if (index < mesh_->GetNumGeometries())
            mesh_->SetMaterial(index, mAsset->UrhoMaterial());
        else
            LogWarningF("Mesh: Illegal submesh index %d for material %s. Target mesh %s has %d submeshes.", index, mAsset->Name().CString(), meshRef.Get().ref.CString(), mesh_->GetNumGeometries());
    }
}

}
<commit_msg>Mesh: Fix atm commented code.<commit_after>// For conditions of distribution and use, see copyright notice in LICENSE

#include "StableHeaders.h"
#include "Mesh.h"
#include "Framework.h"
#include "GraphicsWorld.h"
#include "Placeable.h"
#include "Scene/Scene.h"
#include "AttributeMetadata.h"
#include "LoggingFunctions.h"
#include "AssetRefListener.h"
#include "IMeshAsset.h"
#include "IMaterialAsset.h"

#include <Engine/Scene/Scene.h>
#include <Engine/Scene/Node.h>
#include <Engine/Graphics/AnimatedModel.h>
#include <Engine/Resource/ResourceCache.h>

namespace Tundra
{

Mesh::Mesh(Urho3D::Context* context, Scene* scene) :
    IComponent(context, scene),
    INIT_ATTRIBUTE_VALUE(nodeTransformation, "Transform", Transform(float3(0,0,0),float3(0,0,0),float3(1,1,1))),
    INIT_ATTRIBUTE_VALUE(meshRef, "Mesh ref", AssetReference("", "OgreMesh")),
    INIT_ATTRIBUTE_VALUE(skeletonRef, "Skeleton ref", AssetReference("", "OgreSkeleton")),
    INIT_ATTRIBUTE_VALUE(materialRefs, "Material refs", AssetReferenceList("OgreMaterial")),
    INIT_ATTRIBUTE_VALUE(drawDistance, "Draw distance", 0.0f),
    INIT_ATTRIBUTE_VALUE(castShadows, "Cast shadows", false),
    INIT_ATTRIBUTE_VALUE(useInstancing, "Use instancing", false)
{
    if (scene)
        world_ = scene->Subsystem<GraphicsWorld>();

    static AttributeMetadata drawDistanceData("", "0", "10000");
    drawDistance.SetMetadata(&drawDistanceData);

    static AttributeMetadata materialMetadata;
    materialMetadata.elementType = "AssetReference";
    materialRefs.SetMetadata(&materialMetadata);

    ParentEntitySet.Connect(this, &Mesh::UpdateSignals);
}

Mesh::~Mesh()
{
    if (world_.Expired())
    {
        if (mesh_)
            LogError("Mesh: World has expired, skipping uninitialization!");
        return;
    }

    if (mesh_)
    {
        MeshAboutToBeDestroyed.Emit();

        mesh_.Reset();
        // The mesh component will be destroyed along with the adjustment node
        adjustmentNode_->Remove();
        adjustmentNode_.Reset();
    }
}

Urho3D::Node* Mesh::BoneNode(const String& name) const
{
    // When a skeletal mesh is created, the bone hierarchy will be under the adjustment node
    return adjustmentNode_ ? adjustmentNode_->GetChild(name, true) : nullptr;
}

void Mesh::DeserializeFrom(Urho3D::XMLElement& element, AttributeChange::Type change)
{
    if (!BeginDeserialization(element))
        return;

    if (change == AttributeChange::Default)
        change = updateMode;
    assert(change != AttributeChange::Default);

    Urho3D::XMLElement attributeElement = element.GetChild("attribute");
    while(attributeElement)
    {
        if (attributeElement.GetAttribute("id").Empty() && attributeElement.GetAttribute("name").Compare("Mesh materials", false) == 0)
            attributeElement.SetAttribute("name", "Material refs");
        DeserializeAttributeFrom(attributeElement, change);
        attributeElement = attributeElement.GetNext("attribute");
    }
}

Urho3D::AnimatedModel* Mesh::UrhoMesh() const
{
    return mesh_;
}

void Mesh::UpdateSignals()
{
    Entity* parent = ParentEntity();
    if (!parent)
        return;

    // If scene is not view-enabled, no further action
    if (!ViewEnabled())
        return;

    meshRefListener_ = new AssetRefListener();
    materialRefListListener_ = new AssetRefListListener(framework->Asset());

    parent->ComponentAdded.Connect(this, &Mesh::OnComponentStructureChanged);
    parent->ComponentRemoved.Connect(this, &Mesh::OnComponentStructureChanged);

    if (parent->ParentScene())
        world_ = parent->ParentScene()->Subsystem<GraphicsWorld>();

    if (world_ && !mesh_)
    {
        Urho3D::Scene* urhoScene = world_->UrhoScene();
        adjustmentNode_ = urhoScene->CreateChild("AdjustmentNode");

        // Make the entity & component links for identifying raycasts
        adjustmentNode_->SetVar(GraphicsWorld::entityLink, Variant(WeakPtr<RefCounted>(parent)));
        adjustmentNode_->SetVar(GraphicsWorld::componentLink, Variant(WeakPtr<RefCounted>(this)));

        mesh_ = adjustmentNode_->CreateComponent<Urho3D::AnimatedModel>();

        // Connect ref listeners
        meshRefListener_->Loaded.Connect(this, &Mesh::OnMeshAssetLoaded);
        materialRefListListener_->Changed.Connect(this, &Mesh::OnMaterialAssetRefsChanged);
        materialRefListListener_->Failed.Connect(this, &Mesh::OnMaterialAssetFailed);
        materialRefListListener_->Loaded.Connect(this, &Mesh::OnMaterialAssetLoaded);
    }

    // Make sure we attach to the Placeable if exists.
    AttachMesh();
}

void Mesh::DetachMesh()
{
    if (!mesh_ || world_.Expired())
        return;

    if (placeable_)
    {
        Urho3D::Scene* urhoScene = world_->UrhoScene();
        // When removed from the placeable, attach to scene root to avoid being removed from scene
        adjustmentNode_->SetParent(urhoScene);
        placeable_.Reset();
        mesh_->SetEnabled(false); // We should not render while detached
    }
}

void Mesh::AttachMesh()
{
    if (!mesh_ || world_.Expired())
        return;

    // Detach first, in case the original placeable no longer exists
    DetachMesh();

    Entity *entity = ParentEntity();
    if (!entity)
        return;
    placeable_ = entity->Component<Placeable>();
    if (!placeable_)
        return;

    Urho3D::Node* placeableNode = placeable_->UrhoSceneNode();
    if (!placeableNode)
    {
        LogError("Can not attach mesh: placeable does not have an Urho3D scene node");
        return;
    }
    adjustmentNode_->SetParent(placeableNode);
    mesh_->SetEnabled(true);
}

void Mesh::OnComponentStructureChanged(IComponent*, AttributeChange::Type)
{
    // No-op if attached to the same placeable already
    if (placeable_ == parentEntity->Component<Placeable>())
        return;

    AttachMesh();
}

void Mesh::AttributesChanged()
{
    // None of the attributes have an effect when the scene is not viewenabled and there is no actual mesh
    if (!mesh_)
        return;

    if (drawDistance.ValueChanged())
        mesh_->SetDrawDistance(drawDistance.Get());
    if (castShadows.ValueChanged())
        mesh_->SetCastShadows(castShadows.Get());
    if (nodeTransformation.ValueChanged())
    {
        const Transform &newTransform = nodeTransformation.Get();
        adjustmentNode_->SetPosition(newTransform.pos);
        adjustmentNode_->SetRotation(newTransform.Orientation());
        adjustmentNode_->SetScale(newTransform.scale);
    }
    if (meshRef.ValueChanged() && meshRefListener_)
    {
        /// @todo Why is this warning here? Pretty normal to clear mesh ref...
        if (meshRef.Get().ref.Trimmed().Empty())
            LogDebug("Warning: Mesh \"" + this->parentEntity->Name() + "\" mesh ref was set to an empty reference!");
        meshRefListener_->HandleAssetRefChange(&meshRef);
    }
    if (materialRefs.ValueChanged() && materialRefListListener_)
    {
        /* Let the listener resolve and cleanup the refs, while us keeping the originals intact.
           Changes are handled in OnMaterialAssetRefsChanged/Failed/Loaded. */
        materialRefListListener_->HandleChange(materialRefs.Get());
    }
    if (skeletonRef.ValueChanged())
    {
        /// \todo Implement
    }
}

void Mesh::OnMeshAssetLoaded(AssetPtr asset)
{
    IMeshAsset* mAsset = dynamic_cast<IMeshAsset*>(asset.Get());
    if (!mAsset)
    {
        LogErrorF("Mesh: Mesh asset load finished for '%s', but downloaded asset was not of type IMeshAsset!", asset->Name().CString());
        return;
    }

    if (mesh_)
    {
        mesh_->SetModel(mAsset->UrhoModel());

        // Apply all materials that have been loaded so far.
        // OnMaterialAssetLoaded will do the right thing once model has been set.
        Vector<AssetPtr> materialAssets = materialRefListListener_->Assets();
        for(uint mi=0; mi<materialAssets.Size(); ++mi)
        {
            AssetPtr &materialAssetPtr = materialAssets[mi];
            IMaterialAsset *materialAsset = dynamic_cast<IMaterialAsset*>(materialAssetPtr.Get());
            if (materialAsset && materialAsset->IsLoaded())
            {
                if (mi < mesh_->GetNumGeometries())
                    mesh_->SetMaterial(mi, materialAsset->UrhoMaterial());
                else
                    LogWarningF("Mesh: Illegal submesh index %d for material %s. Target mesh %s has %d submeshes.", mi, materialAsset->Name().CString(), meshRef.Get().ref.CString(), mesh_->GetNumGeometries());
            }
        }
    }
    else
        LogWarningF("Mesh: Model asset loaded but target mesh has not been created yet in %s", ParentEntity()->ToString().CString());
}

void Mesh::OnMaterialAssetRefsChanged(const AssetReferenceList &mRefs)
{
    if (!mesh_ || !mesh_->GetModel())
        return;

    for (uint gi=0; gi<mesh_->GetNumGeometries(); ++gi)
    {
        /** @todo Set empty material to these indexes!
            For Lasse to fill.

        if (gi >= mRefs.Size() || mRefs.refs[gi].ref.Empty())
            mesh_->SetMaterial(gi, <default_empty_material> or nullptr?);
        */
    }
}

void Mesh::OnMaterialAssetFailed(uint index, IAssetTransfer* /*transfer*/, String /*error*/)
{
    /** @todo Set error material to these indexes!
        For Lasse to fill.

    // Don't log an warning on load failure if index is out of submesh range.
    if (mesh_ && mesh_->GetModel() && index < mesh_->GetNumGeometries())
        mesh_->SetMaterial(index, <default_error_material>);
    */
}

void Mesh::OnMaterialAssetLoaded(uint index, AssetPtr asset)
{
    IMaterialAsset* mAsset = dynamic_cast<IMaterialAsset*>(asset.Get());
    if (!mAsset)
    {
        LogErrorF("Mesh: Material asset load finished for '%s', but downloaded asset was not of type IMaterialAsset!", asset->Name().CString());
        return;
    }

    if (mesh_ && mesh_->GetModel())
    {
        if (index < mesh_->GetNumGeometries())
            mesh_->SetMaterial(index, mAsset->UrhoMaterial());
        else
            LogWarningF("Mesh: Illegal submesh index %d for material %s. Target mesh %s has %d submeshes.", index, mAsset->Name().CString(), meshRef.Get().ref.CString(), mesh_->GetNumGeometries());
    }
}

}
<|endoftext|>


<commit_before>/*
 *  advanced_dialog.cpp
 *  PHD Guiding
 *
 *  Created by Craig Stark.
 *  Copyright (c) 2006-2010 Craig Stark.
 *  All rights reserved.
 *
 *  This source code is distributed under the following "BSD" license
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *    Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *    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.
 *    Neither the name of Craig Stark, Stark Labs nor the names of its
 *     contributors may be used to endorse or promote products derived from
 *     this software without specific prior written permission.
 *
 *  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 HOLDER 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 "phd.h"
AdvancedDialog::AdvancedDialog():
//#if defined (__WINDOWS__)
//wxDialog(pFrame, wxID_ANY, _("Advanced setup"), wxPoint(-1,-1), wxSize(210,350), wxCAPTION | wxCLOSE_BOX)
//#else
//wxDialog(pFrame, wxID_ANY, _("Advanced setup"), wxPoint(-1,-1), wxSize(250,350), wxCAPTION | wxCLOSE_BOX)
//#endif
wxDialog(pFrame, wxID_ANY, _("Advanced setup"), wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX)
{
    /*
     * the advanced dialog is made up of a number of "on the fly" generated slices that configure different things.
     *
     * pTopLevelSizer is a top level Box Sizer in wxVERTICAL mode that contains a pair of sizers,
     * pConfigSizer to hold all the configuration panes and an unamed Button sizer and the OK and CANCEL buttons.
     *
     * pConfigSizer is a Horizontal Box Sizer which contains two Vertical Box sizers, one
     * for each column of panes
     *
     * +------------------------------------+------------------------------------+
     * |    General (Frame) Settings        |   Guider Base Class Settings       |
     * +------------------------------------|                                    |
     * |    Mount  Base Class Settings      |   Ra Guide Algorithm Settings      |
     * |                                    |                                    |
     * |    Mount  Sub Class Settings       |   Dec Guide Alogrithm Settings     |
     * +------------------------------------|                                    |
     * |    Camera Base Class Settings      |   Guider Sub Class Settings        |
     * |                                    |------------------------------------+
     * |    Camera Sub  Calss Settings      |                                    |
     * +------------------------------------|                                    |
     * |    Camera Base Class Settings      |                                    |
     * +-------------------------------------------------------------------------|
     * |                              OK and Cancel Buttons                      |
     * +-------------------------------------------------------------------------+
     *
     */

    wxNotebook *pNotebook = new wxNotebook(this, wxID_ANY);

    wxSizerFlags sizer_flags = wxSizerFlags(0).Center().Border(wxALL,2).Expand();

    // build all the empty sizer
    wxBoxSizer *pGlobalTabSizer = new wxBoxSizer(wxVERTICAL);
    wxBoxSizer *pGuidingTabSizer = new wxBoxSizer(wxVERTICAL);

    // build tabs
    wxPanel *globalSettingsPanel = new wxPanel(pNotebook);
    wxPanel *guiderSettingsPanel = new wxPanel(pNotebook);
    globalSettingsPanel->SetSizer(pGlobalTabSizer);
    guiderSettingsPanel->SetSizer(pGuidingTabSizer);
    pNotebook->AddPage(globalSettingsPanel, _("Global"), true);
    pNotebook->AddPage(guiderSettingsPanel, _("Guiding"));

    // Build the global tab pane
    m_pFramePane = pFrame->GetConfigDialogPane(globalSettingsPanel);
    pGlobalTabSizer->Add(m_pFramePane, sizer_flags);

    m_pGuiderPane = pFrame->pGuider->GetConfigDialogPane(globalSettingsPanel);
    pGlobalTabSizer->Add(m_pGuiderPane, sizer_flags);

    if (pCamera)
    {
        m_pCameraPane = pCamera->GetConfigDialogPane(globalSettingsPanel);
        if (m_pCameraPane != NULL)
            pGlobalTabSizer->Add(m_pCameraPane, sizer_flags);
    }
    else
    {
        m_pCameraPane=NULL;
        wxStaticBoxSizer *pBox = new wxStaticBoxSizer(new wxStaticBox(globalSettingsPanel, wxID_ANY, _("Camera Settings")), wxVERTICAL);
        wxStaticText *pText = new wxStaticText(globalSettingsPanel, wxID_ANY, _("No Camera Connected"),wxPoint(-1,-1),wxSize(-1,-1));
        pBox->Add(pText);
        pGlobalTabSizer->Add(pBox, sizer_flags);
    }

    // Build guiding tab

    if (pSecondaryMount)
    {
        // if there are two mounts, the mount config goes to the Adaptive Optics tab
        wxPanel *aoSettingsPanel = new wxPanel(pNotebook);
        wxBoxSizer *pAoSizer = new wxBoxSizer(wxVERTICAL);
        m_pMountPane = pMount->GetConfigDialogPane(aoSettingsPanel);
        pAoSizer->Add(m_pMountPane, sizer_flags);
        aoSettingsPanel->SetSizer(pAoSizer);
        pNotebook->AddPage(aoSettingsPanel, _("Adaptive Optics"));

        // the secondary goes to the global tab pane
        m_pSecondaryMountPane = pSecondaryMount->GetConfigDialogPane(guiderSettingsPanel);
        pGuidingTabSizer->Add(m_pSecondaryMountPane, sizer_flags);
    }
    else
    {
        // otherwise there is no secondary mount, so we put up an empty AO box
        m_pSecondaryMountPane = NULL;
        //wxStaticBoxSizer *pBox = new wxStaticBoxSizer(new wxStaticBox(globalSettingsPanel, wxID_ANY, _("AO Settings")), wxVERTICAL);
        //wxStaticText *pText = new wxStaticText(globalSettingsPanel, wxID_ANY, _("No AO Connected"),wxPoint(-1,-1),wxSize(-1,-1));
        //pBox->Add(pText);
        //pGlobalTabSizer->Add(pBox, sizer_flags);

        // and the mount goes on the right
        m_pMountPane = pMount->GetConfigDialogPane(guiderSettingsPanel);
        pGuidingTabSizer->Add(m_pMountPane, sizer_flags);

    }

    wxBoxSizer *pTopLevelSizer = new wxBoxSizer(wxVERTICAL);
    pTopLevelSizer->Add(pNotebook, wxSizerFlags(0).Expand().Border(wxALL, 5));
    pTopLevelSizer->Add(CreateButtonSizer(wxOK | wxCANCEL), wxSizerFlags(0).Expand().Border(wxALL, 5));
    SetSizerAndFit(pTopLevelSizer);
}

void AdvancedDialog::LoadValues(void)
{
    m_pFramePane->LoadValues();
    m_pMountPane->LoadValues();
    if (m_pSecondaryMountPane)
    {
        m_pSecondaryMountPane->LoadValues();
    }
    m_pGuiderPane->LoadValues();

    if (m_pCameraPane != NULL)
    {
        m_pCameraPane->LoadValues();
    }
}

void AdvancedDialog::UnloadValues(void)
{
    m_pFramePane->UnloadValues();
    m_pMountPane->UnloadValues();
    if (m_pSecondaryMountPane)
    {
        m_pSecondaryMountPane->UnloadValues();
    }
    m_pGuiderPane->UnloadValues();

    if (m_pCameraPane != NULL)
    {
        m_pCameraPane->UnloadValues();
    }
}

BEGIN_EVENT_TABLE(AdvancedDialog, wxDialog)
    EVT_BUTTON(wxID_PROPERTIES,AdvancedDialog::OnSetupCamera)
END_EVENT_TABLE()

void AdvancedDialog::OnSetupCamera(wxCommandEvent& WXUNUSED(event)) {
    // Prior to this we check to make sure the current camera is a WDM camera (main dialog) but...

    if (pFrame->CaptureActive || !pCamera || !pCamera->Connected || !pCamera->HasPropertyDialog) return;  // One more safety check
    /*if (pCamera == &Camera_WDM)
        Camera_WDM.ShowPropertyDialog();
    else if (pCamera == &Camera_VFW)
        Camera_VFW.ShowPropertyDialog();*/
    pCamera->ShowPropertyDialog();
}

<commit_msg>Adding additional tabs to the advanced config box<commit_after>/*
 *  advanced_dialog.cpp
 *  PHD Guiding
 *
 *  Created by Craig Stark.
 *  Copyright (c) 2006-2010 Craig Stark.
 *  All rights reserved.
 *
 *  This source code is distributed under the following "BSD" license
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *    Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *    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.
 *    Neither the name of Craig Stark, Stark Labs nor the names of its
 *     contributors may be used to endorse or promote products derived from
 *     this software without specific prior written permission.
 *
 *  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 HOLDER 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 "phd.h"
AdvancedDialog::AdvancedDialog():
//#if defined (__WINDOWS__)
//wxDialog(pFrame, wxID_ANY, _("Advanced setup"), wxPoint(-1,-1), wxSize(210,350), wxCAPTION | wxCLOSE_BOX)
//#else
//wxDialog(pFrame, wxID_ANY, _("Advanced setup"), wxPoint(-1,-1), wxSize(250,350), wxCAPTION | wxCLOSE_BOX)
//#endif
wxDialog(pFrame, wxID_ANY, _("Advanced setup"), wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX)
{
    /*
     * the advanced dialog is made up of a number of "on the fly" generated slices that configure different things.
     *
     * pTopLevelSizer is a top level Box Sizer in wxVERTICAL mode that contains a pair of sizers,
     * pConfigSizer to hold all the configuration panes and an unamed Button sizer and the OK and CANCEL buttons.
     *
     * pConfigSizer is a Horizontal Box Sizer which contains two Vertical Box sizers, one
     * for each column of panes
     *
     * +------------------------------------+------------------------------------+
     * |    General (Frame) Settings        |   Guider Base Class Settings       |
     * +------------------------------------|                                    |
     * |    Mount  Base Class Settings      |   Ra Guide Algorithm Settings      |
     * |                                    |                                    |
     * |    Mount  Sub Class Settings       |   Dec Guide Alogrithm Settings     |
     * +------------------------------------|                                    |
     * |    Camera Base Class Settings      |   Guider Sub Class Settings        |
     * |                                    |------------------------------------+
     * |    Camera Sub  Calss Settings      |                                    |
     * +------------------------------------|                                    |
     * |    Camera Base Class Settings      |                                    |
     * +-------------------------------------------------------------------------|
     * |                              OK and Cancel Buttons                      |
     * +-------------------------------------------------------------------------+
     *
     */

    wxNotebook *pNotebook = new wxNotebook(this, wxID_ANY);

    wxSizerFlags sizer_flags = wxSizerFlags(0).Align(wxALIGN_TOP|wxALIGN_CENTER_HORIZONTAL).Border(wxALL,2).Expand();

    // build tabs -- each needs the tab, and a sizer.  Once built
    // it needs to be populated

    // Build the global tab pane
    wxPanel *pGlobalSettingsPanel = new wxPanel(pNotebook);
    wxBoxSizer *pGlobalTabSizer = new wxBoxSizer(wxVERTICAL);
    pGlobalSettingsPanel->SetSizer(pGlobalTabSizer);
    pNotebook->AddPage(pGlobalSettingsPanel, _("Global"), true);

    // and populate it
    m_pFramePane = pFrame->GetConfigDialogPane(pGlobalSettingsPanel);
    pGlobalTabSizer->Add(m_pFramePane, sizer_flags);

    // Build the guider tab
    wxPanel *pGuiderSettingsPanel = new wxPanel(pNotebook);
    wxBoxSizer *pGuidingTabSizer = new wxBoxSizer(wxVERTICAL);
    pGuiderSettingsPanel->SetSizer(pGuidingTabSizer);
    pNotebook->AddPage(pGuiderSettingsPanel, _("Guiding"));

    // and populate it
    m_pGuiderPane = pFrame->pGuider->GetConfigDialogPane(pGuiderSettingsPanel);
    pGuidingTabSizer->Add(m_pGuiderPane, sizer_flags);

    // Build the camera tab
    wxPanel *pCameraSettingsPanel = new wxPanel(pNotebook);
    wxBoxSizer *pCameraTabSizer = new wxBoxSizer(wxVERTICAL);
    pCameraSettingsPanel->SetSizer(pCameraTabSizer);
    pNotebook->AddPage(pCameraSettingsPanel, _("Camera"));

    // and populate it
    if (pCamera)
    {
        m_pCameraPane = pCamera->GetConfigDialogPane(pCameraSettingsPanel);
        if (m_pCameraPane)
        {
            pCameraTabSizer->Add(m_pCameraPane, sizer_flags);
        }
    }
    else
    {
        m_pCameraPane=NULL;
        wxStaticBoxSizer *pBox = new wxStaticBoxSizer(new wxStaticBox(pCameraSettingsPanel, wxID_ANY, _("Camera Settings")), wxVERTICAL);
        wxStaticText *pText = new wxStaticText(pCameraSettingsPanel, wxID_ANY, _("No Camera Connected"),wxPoint(-1,-1),wxSize(-1,-1));
        pBox->Add(pText);
        pCameraTabSizer->Add(pBox, sizer_flags);
    }

    // Build scope tab
    wxPanel *pScopeSettingsPanel = new wxPanel(pNotebook);
    wxBoxSizer *pScopeTabSizer = new wxBoxSizer(wxVERTICAL);
    pScopeSettingsPanel->SetSizer(pScopeTabSizer);
    pNotebook->AddPage(pScopeSettingsPanel, _("Scope"));

    // and populate it
    if (pSecondaryMount)
    {
        // if there are two mounts, the secondary mount config goes to the
        // scope tab
        m_pMountPane = pMount->GetConfigDialogPane(pScopeSettingsPanel);
        pScopeTabSizer->Add(m_pMountPane, sizer_flags);
    }
    else
    {
        // the primary mount goes to the scope tab
        m_pMountPane = pMount->GetConfigDialogPane(pScopeSettingsPanel);
        pScopeTabSizer->Add(m_pMountPane, sizer_flags);
    }

    // Build AO tab
    wxPanel *pAoSettingsPanel = new wxPanel(pNotebook);
    wxBoxSizer *pAoTabSizer = new wxBoxSizer(wxVERTICAL);
    pAoSettingsPanel->SetSizer(pAoTabSizer);
    pNotebook->AddPage(pAoSettingsPanel, _("AO"));

    // and populate it
    if (pSecondaryMount)
    {
        // if there are two mounts, the primary mount config goes to the Adaptive Optics tab
        m_pMountPane = pMount->GetConfigDialogPane(pAoSettingsPanel);
        pAoTabSizer->Add(m_pMountPane, sizer_flags);
    }
    else
    {
        // otherwise there is no secondary mount, so there is no AO.
        // construct a text box informing the user of that fact

        m_pSecondaryMountPane = NULL;

        wxStaticBoxSizer *pBox = new wxStaticBoxSizer(new wxStaticBox(pAoSettingsPanel, wxID_ANY, _("AO Settings")), wxVERTICAL);
        wxStaticText *pText = new wxStaticText(pAoSettingsPanel, wxID_ANY, _("No AO Connected"),wxPoint(-1,-1),wxSize(-1,-1));
        pBox->Add(pText);
        pAoTabSizer->Add(pBox, sizer_flags);
    }

    wxBoxSizer *pTopLevelSizer = new wxBoxSizer(wxVERTICAL);
    pTopLevelSizer->Add(pNotebook, wxSizerFlags(0).Expand().Border(wxALL, 5));
    pTopLevelSizer->Add(CreateButtonSizer(wxOK | wxCANCEL), wxSizerFlags(0).Expand().Border(wxALL, 5));
    SetSizerAndFit(pTopLevelSizer);
}

void AdvancedDialog::LoadValues(void)
{
    m_pFramePane->LoadValues();
    m_pMountPane->LoadValues();

    if (m_pSecondaryMountPane)
    {
        m_pSecondaryMountPane->LoadValues();
    }

    m_pGuiderPane->LoadValues();

    if (m_pCameraPane != NULL)
    {
        m_pCameraPane->LoadValues();
    }
}

void AdvancedDialog::UnloadValues(void)
{
    m_pFramePane->UnloadValues();
    m_pMountPane->UnloadValues();
    if (m_pSecondaryMountPane)
    {
        m_pSecondaryMountPane->UnloadValues();
    }
    m_pGuiderPane->UnloadValues();

    if (m_pCameraPane != NULL)
    {
        m_pCameraPane->UnloadValues();
    }
}

BEGIN_EVENT_TABLE(AdvancedDialog, wxDialog)
    EVT_BUTTON(wxID_PROPERTIES,AdvancedDialog::OnSetupCamera)
END_EVENT_TABLE()

void AdvancedDialog::OnSetupCamera(wxCommandEvent& WXUNUSED(event)) {
    // Prior to this we check to make sure the current camera is a WDM camera (main dialog) but...

    if (pFrame->CaptureActive || !pCamera || !pCamera->Connected || !pCamera->HasPropertyDialog) return;  // One more safety check
    /*if (pCamera == &Camera_WDM)
        Camera_WDM.ShowPropertyDialog();
    else if (pCamera == &Camera_VFW)
        Camera_VFW.ShowPropertyDialog();*/
    pCamera->ShowPropertyDialog();
}

<|endoftext|>


<commit_before>// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc.  All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * 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.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// 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.

// Author: kenton@google.com (Kenton Varda)
//  Based on original Protocol Buffers design by
//  Sanjay Ghemawat, Jeff Dean, and others.

// Copyright (c) 2008-2013, Dave Benson.  All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
//     * 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.

// Modified to implement C code by Dave Benson.

#include <set>
#include <map>

#include <protoc-c/c_enum.h>
#include <protoc-c/c_helpers.h>
#include <google/protobuf/io/printer.h>

namespace google {
namespace protobuf {
namespace compiler {
namespace c {

EnumGenerator::EnumGenerator(const EnumDescriptor* descriptor,
                             const string& dllexport_decl)
  : descriptor_(descriptor),
    dllexport_decl_(dllexport_decl) {
}

EnumGenerator::~EnumGenerator() {}

void EnumGenerator::GenerateDefinition(io::Printer* printer) {
  map<string, string> vars;
  vars["classname"] = FullNameToC(descriptor_->full_name());
  vars["shortname"] = descriptor_->name();
  vars["uc_name"] = FullNameToUpper(descriptor_->full_name());

  SourceLocation sourceLoc;
  descriptor_->GetSourceLocation(&sourceLoc);
  PrintComment (printer, sourceLoc.leading_comments);

  printer->Print(vars, "typedef enum _$classname$ {\n");
  printer->Indent();

  const EnumValueDescriptor* min_value = descriptor_->value(0);
  const EnumValueDescriptor* max_value = descriptor_->value(0);

  vars["opt_comma"] = ",";
  vars["prefix"] = FullNameToUpper(descriptor_->full_name()) + "__";
  for (int i = 0; i < descriptor_->value_count(); i++) {
    vars["name"] = descriptor_->value(i)->name();
    vars["number"] = SimpleItoa(descriptor_->value(i)->number());
    if (i + 1 == descriptor_->value_count())
      vars["opt_comma"] = "";

    SourceLocation valSourceLoc;
    descriptor_->value(i)->GetSourceLocation(&valSourceLoc);

    PrintComment (printer, valSourceLoc.leading_comments);
    PrintComment (printer, valSourceLoc.trailing_comments);
    printer->Print(vars, "$prefix$$name$ = $number$$opt_comma$\n");

    if (descriptor_->value(i)->number() < min_value->number()) {
      min_value = descriptor_->value(i);
    }
    if (descriptor_->value(i)->number() > max_value->number()) {
      max_value = descriptor_->value(i);
    }
  }

  printer->Print(vars, "  PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE($uc_name$)\n");
  printer->Outdent();
  printer->Print(vars, "} $classname$;\n");
}

void EnumGenerator::GenerateDescriptorDeclarations(io::Printer* printer) {
  map<string, string> vars;
  if (dllexport_decl_.empty()) {
    vars["dllexport"] = "";
  } else {
    vars["dllexport"] = dllexport_decl_ + " ";
  }
  vars["classname"] = FullNameToC(descriptor_->full_name());
  vars["lcclassname"] = FullNameToLower(descriptor_->full_name());

  printer->Print(vars,
    "extern $dllexport$const ProtobufCEnumDescriptor    $lcclassname$__descriptor;\n");
}

struct ValueIndex
{
  int value;
  unsigned index;
  unsigned final_index;		/* index in uniqified array of values */
  const char *name;
};
void EnumGenerator::GenerateValueInitializer(io::Printer *printer, int index)
{
  const EnumValueDescriptor *vd = descriptor_->value(index);
  map<string, string> vars;
  vars["enum_value_name"] = vd->name();
  vars["c_enum_value_name"] = FullNameToUpper(descriptor_->full_name()) + "__" + vd->name();
  vars["value"] = SimpleItoa(vd->number());
  printer->Print(vars,
   "  { \"$enum_value_name$\", \"$c_enum_value_name$\", $value$ },\n");
}

static int compare_value_indices_by_value_then_index(const void *a, const void *b)
{
  const ValueIndex *vi_a = (const ValueIndex *) a;
  const ValueIndex *vi_b = (const ValueIndex *) b;
  if (vi_a->value < vi_b->value) return -1;
  if (vi_a->value > vi_b->value) return +1;
  if (vi_a->index < vi_b->index) return -1;
  if (vi_a->index > vi_b->index) return +1;
  return 0;
}

static int compare_value_indices_by_name(const void *a, const void *b)
{
  const ValueIndex *vi_a = (const ValueIndex *) a;
  const ValueIndex *vi_b = (const ValueIndex *) b;
  return strcmp (vi_a->name, vi_b->name);
}

void EnumGenerator::GenerateEnumDescriptor(io::Printer* printer) {
  map<string, string> vars;
  vars["fullname"] = descriptor_->full_name();
  vars["lcclassname"] = FullNameToLower(descriptor_->full_name());
  vars["cname"] = FullNameToC(descriptor_->full_name());
  vars["shortname"] = descriptor_->name();
  vars["packagename"] = descriptor_->file()->package();
  vars["value_count"] = SimpleItoa(descriptor_->value_count());

  // Sort by name and value, dropping duplicate values if they appear later.
  // TODO: use a c++ paradigm for this!
  NameIndex *name_index = new NameIndex[descriptor_->value_count()];
  ValueIndex *value_index = new ValueIndex[descriptor_->value_count()];
  for (int j = 0; j < descriptor_->value_count(); j++) {
    const EnumValueDescriptor *vd = descriptor_->value(j);
    name_index[j].index = j;
    name_index[j].name = vd->name().c_str();
    value_index[j].index = j;
    value_index[j].value = vd->number();
    value_index[j].name = vd->name().c_str();
  }
  qsort(value_index, descriptor_->value_count(),
	sizeof(ValueIndex), compare_value_indices_by_value_then_index);

  // only record unique values
  int n_unique_values;
  if (descriptor_->value_count() == 0) {
    n_unique_values = 0; // should never happen
  } else {
    n_unique_values = 1;
    value_index[0].final_index = 0;
    for (int j = 1; j < descriptor_->value_count(); j++) {
      if (value_index[j-1].value != value_index[j].value)
	value_index[j].final_index = n_unique_values++;
      else
	value_index[j].final_index = n_unique_values - 1;
    }
  }

  vars["unique_value_count"] = SimpleItoa(n_unique_values);
  printer->Print(vars,
    "const ProtobufCEnumValue $lcclassname$__enum_values_by_number[$unique_value_count$] =\n"
    "{\n");
  if (descriptor_->value_count() > 0) {
    GenerateValueInitializer(printer, value_index[0].index);
    for (int j = 1; j < descriptor_->value_count(); j++) {
      if (value_index[j-1].value != value_index[j].value) {
	GenerateValueInitializer(printer, value_index[j].index);
      }
    }
  }
  printer->Print(vars, "};\n");
  printer->Print(vars, "static const ProtobufCIntRange $lcclassname$__value_ranges[] = {\n");
  unsigned n_ranges = 0;
  if (descriptor_->value_count() > 0) {
    unsigned range_start = 0;
    unsigned range_len = 1;
    int range_start_value = value_index[0].value;
    int last_value = range_start_value;
    for (int j = 1; j < descriptor_->value_count(); j++) {
      if (value_index[j-1].value != value_index[j].value) {
	if (last_value + 1 == value_index[j].value) {
	  range_len++;
	} else {
	  // output range
	  vars["range_start_value"] = SimpleItoa(range_start_value);
	  vars["orig_index"] = SimpleItoa(range_start);
	  printer->Print (vars, "{$range_start_value$, $orig_index$},");
	  range_start_value = value_index[j].value;
	  range_start += range_len;
	  range_len = 1;
	  n_ranges++;
	}
	last_value = value_index[j].value;
      }
    }
    {
    vars["range_start_value"] = SimpleItoa(range_start_value);
    vars["orig_index"] = SimpleItoa(range_start);
    printer->Print (vars, "{$range_start_value$, $orig_index$},");
    range_start += range_len;
    n_ranges++;
    }
    {
    vars["range_start_value"] = SimpleItoa(0);
    vars["orig_index"] = SimpleItoa(range_start);
    printer->Print (vars, "{$range_start_value$, $orig_index$}\n};\n");
    }
  }
  vars["n_ranges"] = SimpleItoa(n_ranges);

  qsort(value_index, descriptor_->value_count(),
        sizeof(ValueIndex), compare_value_indices_by_name);
  printer->Print(vars,
    "const ProtobufCEnumValueIndex $lcclassname$__enum_values_by_name[$value_count$] =\n"
    "{\n");
  for (int j = 0; j < descriptor_->value_count(); j++) {
    vars["index"] = SimpleItoa(value_index[j].final_index);
    vars["name"] = value_index[j].name;
    printer->Print (vars, "  { \"$name$\", $index$ },\n");
  }
  printer->Print(vars, "};\n");

  printer->Print(vars,
    "const ProtobufCEnumDescriptor $lcclassname$__descriptor =\n"
    "{\n"
    "  PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC,\n"
    "  \"$fullname$\",\n"
    "  \"$shortname$\",\n"
    "  \"$cname$\",\n"
    "  \"$packagename$\",\n"
    "  $unique_value_count$,\n"
    "  $lcclassname$__enum_values_by_number,\n"
    "  $value_count$,\n"
    "  $lcclassname$__enum_values_by_name,\n"
    "  $n_ranges$,\n"
    "  $lcclassname$__value_ranges,\n"
    "  NULL,NULL,NULL,NULL   /* reserved[1234] */\n"
    "};\n");

  delete[] value_index;
  delete[] name_index;
}

}  // namespace c
}  // namespace compiler
}  // namespace protobuf
}  // namespace google
<commit_msg>protoc-c/c_enum.cc: Make enum_values_{by_name,by_number} structs static<commit_after>// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc.  All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * 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.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// 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.

// Author: kenton@google.com (Kenton Varda)
//  Based on original Protocol Buffers design by
//  Sanjay Ghemawat, Jeff Dean, and others.

// Copyright (c) 2008-2013, Dave Benson.  All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
//     * 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.

// Modified to implement C code by Dave Benson.

#include <set>
#include <map>

#include <protoc-c/c_enum.h>
#include <protoc-c/c_helpers.h>
#include <google/protobuf/io/printer.h>

namespace google {
namespace protobuf {
namespace compiler {
namespace c {

EnumGenerator::EnumGenerator(const EnumDescriptor* descriptor,
                             const string& dllexport_decl)
  : descriptor_(descriptor),
    dllexport_decl_(dllexport_decl) {
}

EnumGenerator::~EnumGenerator() {}

void EnumGenerator::GenerateDefinition(io::Printer* printer) {
  map<string, string> vars;
  vars["classname"] = FullNameToC(descriptor_->full_name());
  vars["shortname"] = descriptor_->name();
  vars["uc_name"] = FullNameToUpper(descriptor_->full_name());

  SourceLocation sourceLoc;
  descriptor_->GetSourceLocation(&sourceLoc);
  PrintComment (printer, sourceLoc.leading_comments);

  printer->Print(vars, "typedef enum _$classname$ {\n");
  printer->Indent();

  const EnumValueDescriptor* min_value = descriptor_->value(0);
  const EnumValueDescriptor* max_value = descriptor_->value(0);

  vars["opt_comma"] = ",";
  vars["prefix"] = FullNameToUpper(descriptor_->full_name()) + "__";
  for (int i = 0; i < descriptor_->value_count(); i++) {
    vars["name"] = descriptor_->value(i)->name();
    vars["number"] = SimpleItoa(descriptor_->value(i)->number());
    if (i + 1 == descriptor_->value_count())
      vars["opt_comma"] = "";

    SourceLocation valSourceLoc;
    descriptor_->value(i)->GetSourceLocation(&valSourceLoc);

    PrintComment (printer, valSourceLoc.leading_comments);
    PrintComment (printer, valSourceLoc.trailing_comments);
    printer->Print(vars, "$prefix$$name$ = $number$$opt_comma$\n");

    if (descriptor_->value(i)->number() < min_value->number()) {
      min_value = descriptor_->value(i);
    }
    if (descriptor_->value(i)->number() > max_value->number()) {
      max_value = descriptor_->value(i);
    }
  }

  printer->Print(vars, "  PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE($uc_name$)\n");
  printer->Outdent();
  printer->Print(vars, "} $classname$;\n");
}

void EnumGenerator::GenerateDescriptorDeclarations(io::Printer* printer) {
  map<string, string> vars;
  if (dllexport_decl_.empty()) {
    vars["dllexport"] = "";
  } else {
    vars["dllexport"] = dllexport_decl_ + " ";
  }
  vars["classname"] = FullNameToC(descriptor_->full_name());
  vars["lcclassname"] = FullNameToLower(descriptor_->full_name());

  printer->Print(vars,
    "extern $dllexport$const ProtobufCEnumDescriptor    $lcclassname$__descriptor;\n");
}

struct ValueIndex
{
  int value;
  unsigned index;
  unsigned final_index;		/* index in uniqified array of values */
  const char *name;
};
void EnumGenerator::GenerateValueInitializer(io::Printer *printer, int index)
{
  const EnumValueDescriptor *vd = descriptor_->value(index);
  map<string, string> vars;
  vars["enum_value_name"] = vd->name();
  vars["c_enum_value_name"] = FullNameToUpper(descriptor_->full_name()) + "__" + vd->name();
  vars["value"] = SimpleItoa(vd->number());
  printer->Print(vars,
   "  { \"$enum_value_name$\", \"$c_enum_value_name$\", $value$ },\n");
}

static int compare_value_indices_by_value_then_index(const void *a, const void *b)
{
  const ValueIndex *vi_a = (const ValueIndex *) a;
  const ValueIndex *vi_b = (const ValueIndex *) b;
  if (vi_a->value < vi_b->value) return -1;
  if (vi_a->value > vi_b->value) return +1;
  if (vi_a->index < vi_b->index) return -1;
  if (vi_a->index > vi_b->index) return +1;
  return 0;
}

static int compare_value_indices_by_name(const void *a, const void *b)
{
  const ValueIndex *vi_a = (const ValueIndex *) a;
  const ValueIndex *vi_b = (const ValueIndex *) b;
  return strcmp (vi_a->name, vi_b->name);
}

void EnumGenerator::GenerateEnumDescriptor(io::Printer* printer) {
  map<string, string> vars;
  vars["fullname"] = descriptor_->full_name();
  vars["lcclassname"] = FullNameToLower(descriptor_->full_name());
  vars["cname"] = FullNameToC(descriptor_->full_name());
  vars["shortname"] = descriptor_->name();
  vars["packagename"] = descriptor_->file()->package();
  vars["value_count"] = SimpleItoa(descriptor_->value_count());

  // Sort by name and value, dropping duplicate values if they appear later.
  // TODO: use a c++ paradigm for this!
  NameIndex *name_index = new NameIndex[descriptor_->value_count()];
  ValueIndex *value_index = new ValueIndex[descriptor_->value_count()];
  for (int j = 0; j < descriptor_->value_count(); j++) {
    const EnumValueDescriptor *vd = descriptor_->value(j);
    name_index[j].index = j;
    name_index[j].name = vd->name().c_str();
    value_index[j].index = j;
    value_index[j].value = vd->number();
    value_index[j].name = vd->name().c_str();
  }
  qsort(value_index, descriptor_->value_count(),
	sizeof(ValueIndex), compare_value_indices_by_value_then_index);

  // only record unique values
  int n_unique_values;
  if (descriptor_->value_count() == 0) {
    n_unique_values = 0; // should never happen
  } else {
    n_unique_values = 1;
    value_index[0].final_index = 0;
    for (int j = 1; j < descriptor_->value_count(); j++) {
      if (value_index[j-1].value != value_index[j].value)
	value_index[j].final_index = n_unique_values++;
      else
	value_index[j].final_index = n_unique_values - 1;
    }
  }

  vars["unique_value_count"] = SimpleItoa(n_unique_values);
  printer->Print(vars,
    "static const ProtobufCEnumValue $lcclassname$__enum_values_by_number[$unique_value_count$] =\n"
    "{\n");
  if (descriptor_->value_count() > 0) {
    GenerateValueInitializer(printer, value_index[0].index);
    for (int j = 1; j < descriptor_->value_count(); j++) {
      if (value_index[j-1].value != value_index[j].value) {
	GenerateValueInitializer(printer, value_index[j].index);
      }
    }
  }
  printer->Print(vars, "};\n");
  printer->Print(vars, "static const ProtobufCIntRange $lcclassname$__value_ranges[] = {\n");
  unsigned n_ranges = 0;
  if (descriptor_->value_count() > 0) {
    unsigned range_start = 0;
    unsigned range_len = 1;
    int range_start_value = value_index[0].value;
    int last_value = range_start_value;
    for (int j = 1; j < descriptor_->value_count(); j++) {
      if (value_index[j-1].value != value_index[j].value) {
	if (last_value + 1 == value_index[j].value) {
	  range_len++;
	} else {
	  // output range
	  vars["range_start_value"] = SimpleItoa(range_start_value);
	  vars["orig_index"] = SimpleItoa(range_start);
	  printer->Print (vars, "{$range_start_value$, $orig_index$},");
	  range_start_value = value_index[j].value;
	  range_start += range_len;
	  range_len = 1;
	  n_ranges++;
	}
	last_value = value_index[j].value;
      }
    }
    {
    vars["range_start_value"] = SimpleItoa(range_start_value);
    vars["orig_index"] = SimpleItoa(range_start);
    printer->Print (vars, "{$range_start_value$, $orig_index$},");
    range_start += range_len;
    n_ranges++;
    }
    {
    vars["range_start_value"] = SimpleItoa(0);
    vars["orig_index"] = SimpleItoa(range_start);
    printer->Print (vars, "{$range_start_value$, $orig_index$}\n};\n");
    }
  }
  vars["n_ranges"] = SimpleItoa(n_ranges);

  qsort(value_index, descriptor_->value_count(),
        sizeof(ValueIndex), compare_value_indices_by_name);
  printer->Print(vars,
    "static const ProtobufCEnumValueIndex $lcclassname$__enum_values_by_name[$value_count$] =\n"
    "{\n");
  for (int j = 0; j < descriptor_->value_count(); j++) {
    vars["index"] = SimpleItoa(value_index[j].final_index);
    vars["name"] = value_index[j].name;
    printer->Print (vars, "  { \"$name$\", $index$ },\n");
  }
  printer->Print(vars, "};\n");

  printer->Print(vars,
    "const ProtobufCEnumDescriptor $lcclassname$__descriptor =\n"
    "{\n"
    "  PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC,\n"
    "  \"$fullname$\",\n"
    "  \"$shortname$\",\n"
    "  \"$cname$\",\n"
    "  \"$packagename$\",\n"
    "  $unique_value_count$,\n"
    "  $lcclassname$__enum_values_by_number,\n"
    "  $value_count$,\n"
    "  $lcclassname$__enum_values_by_name,\n"
    "  $n_ranges$,\n"
    "  $lcclassname$__value_ranges,\n"
    "  NULL,NULL,NULL,NULL   /* reserved[1234] */\n"
    "};\n");

  delete[] value_index;
  delete[] name_index;
}

}  // namespace c
}  // namespace compiler
}  // namespace protobuf
}  // namespace google
<|endoftext|>


<commit_before>#include "balance.h"
#include "util.h"

#include <deque>
#include <algorithm>

namespace ledger {

amount_t balance_t::amount(const commodity_t& commodity) const
{
  if (! commodity) {
    if (amounts.size() == 1) {
      amounts_map::const_iterator i = amounts.begin();
      return (*i).second;
    }
  }
  else if (amounts.size() > 0) {
    amounts_map::const_iterator i = amounts.find(&commodity);
    if (i != amounts.end())
      return (*i).second;
  }
  return amount_t();
}

balance_t balance_t::value(const std::time_t moment) const
{
  balance_t temp;

  for (amounts_map::const_iterator i = amounts.begin();
       i != amounts.end();
       i++)
    temp += (*i).second.value(moment);

  return temp;
}

balance_t balance_t::price() const
{
  balance_t temp;

  for (amounts_map::const_iterator i = amounts.begin();
       i != amounts.end();
       i++)
    temp += (*i).second.price();

  return temp;
}

struct compare_amount_commodities {
  bool operator()(const amount_t * left, const amount_t * right) const {
    return left->commodity().symbol < right->commodity().symbol;
  }
};

void balance_t::write(std::ostream& out,
		      const int     first_width,
		      const int     latter_width) const
{
  bool first  = true;
  int  lwidth = latter_width;

  if (lwidth == -1)
    lwidth = first_width;

  typedef std::deque<const amount_t *> amounts_deque;
  amounts_deque sorted;

  for (amounts_map::const_iterator i = amounts.begin();
       i != amounts.end();
       i++)
    if ((*i).second)
      sorted.push_back(&(*i).second);

  std::stable_sort(sorted.begin(), sorted.end(), compare_amount_commodities());

  for (amounts_deque::const_iterator i = sorted.begin();
       i != sorted.end();
       i++) {
    int width;
    if (! first) {
      out << std::endl;
      width = lwidth;
    } else {
      first = false;
      width = first_width;
    }

    out.width(width);
    out.fill(' ');
    out << std::right << **i;
  }

  if (first) {
    out.width(first_width);
    out.fill(' ');
    out << std::right << "0";
  }
}

balance_t& balance_t::operator*=(const balance_t& bal)
{
  if (! *this || ! bal)
    return (*this = 0L);
  else if (amounts.size() == 1 && bal.amounts.size() == 1)
    return *this *= (*bal.amounts.begin()).second;
  else {
    std::string msg;
    std::ostringstream errmsg(msg);
    errmsg << "It makes no sense to multiply two balances: "
	   << *this << " * " << bal;
    throw amount_error(errmsg.str());
  }
}

balance_t& balance_t::operator/=(const balance_t& bal)
{
  if (! *this) {
    return (*this = 0L);
  }
  else if (! bal) {
    std::string msg;
    std::ostringstream errmsg(msg);
    errmsg << "Attempt to divide by zero: " << *this << " / " << bal;
    throw amount_error(errmsg.str());
  }
  else if (amounts.size() == 1 && bal.amounts.size() == 1) {
    return *this /= (*bal.amounts.begin()).second;
  }
  else if (*this == bal) {
    return (*this = 1L);
  }
  else {
    std::string msg;
    std::ostringstream errmsg(msg);
    errmsg << "It makes no sense to divide two balances: "
	   << *this << " / " << bal;
    throw amount_error(errmsg.str());
  }
}

balance_t::operator amount_t() const
{
  if (amounts.size() == 1) {
    return (*amounts.begin()).second;
  }
  else if (amounts.size() == 0) {
    return amount_t();
  }
  else {
    std::string msg;
    std::ostringstream errmsg(msg);
    errmsg << "Cannot convert a balance with "
	   << "multiple commodities to an amount: " << *this;
    throw amount_error(errmsg.str());
  }
}

balance_pair_t& balance_pair_t::operator/=(const balance_pair_t& bal_pair)
{
  if (bal_pair.cost && ! cost)
    cost = new balance_t(quantity);
  quantity /= bal_pair.quantity;
  if (cost)
    *cost /= bal_pair.cost ? *bal_pair.cost : bal_pair.quantity;

  if (bal_pair.price && *bal_pair.price) {
    if (price)
      *price /= *bal_pair.price;
  }
  return *this;
}

} // namespace ledger
<commit_msg>*** empty log message ***<commit_after>#include "balance.h"
#include "util.h"

#include <deque>
#include <algorithm>

namespace ledger {

amount_t balance_t::amount(const commodity_t& commodity) const
{
  if (! commodity) {
    if (amounts.size() == 1) {
      amounts_map::const_iterator i = amounts.begin();
      return (*i).second;
    }
  }
  else if (amounts.size() > 0) {
    amounts_map::const_iterator i = amounts.find(&commodity);
    if (i != amounts.end())
      return (*i).second;
  }
  return amount_t();
}

balance_t balance_t::value(const std::time_t moment) const
{
  balance_t temp;

  for (amounts_map::const_iterator i = amounts.begin();
       i != amounts.end();
       i++)
    temp += (*i).second.value(moment);

  return temp;
}

balance_t balance_t::price() const
{
  balance_t temp;

  for (amounts_map::const_iterator i = amounts.begin();
       i != amounts.end();
       i++)
    temp += (*i).second.price();

  return temp;
}

struct compare_amount_commodities {
  bool operator()(const amount_t * left, const amount_t * right) const {
    return left->commodity().symbol < right->commodity().symbol;
  }
};

void balance_t::write(std::ostream& out,
		      const int     first_width,
		      const int     latter_width) const
{
  bool first  = true;
  int  lwidth = latter_width;

  if (lwidth == -1)
    lwidth = first_width;

  typedef std::deque<const amount_t *> amounts_deque;
  amounts_deque sorted;

  for (amounts_map::const_iterator i = amounts.begin();
       i != amounts.end();
       i++)
    if ((*i).second)
      sorted.push_back(&(*i).second);

  std::stable_sort(sorted.begin(), sorted.end(), compare_amount_commodities());

  for (amounts_deque::const_iterator i = sorted.begin();
       i != sorted.end();
       i++) {
    int width;
    if (! first) {
      out << std::endl;
      width = lwidth;
    } else {
      first = false;
      width = first_width;
    }

    out.width(width);
    out.fill(' ');
    out << std::right << **i;
  }

  if (first) {
    out.width(first_width);
    out.fill(' ');
    out << std::right << "0";
  }
}

balance_t& balance_t::operator*=(const balance_t& bal)
{
  if (! *this || ! bal) {
    return (*this = 0L);
  }
  else if (amounts.size() == 1 && bal.amounts.size() == 1) {
    return *this *= (*bal.amounts.begin()).second;
  }
  else {
    std::string msg;
    std::ostringstream errmsg(msg);
    errmsg << "It makes no sense to multiply two balances: "
	   << *this << " * " << bal;
    throw amount_error(errmsg.str());
  }
}

balance_t& balance_t::operator/=(const balance_t& bal)
{
  if (! *this) {
    return (*this = 0L);
  }
  else if (! bal) {
    std::string msg;
    std::ostringstream errmsg(msg);
    errmsg << "Attempt to divide by zero: " << *this << " / " << bal;
    throw amount_error(errmsg.str());
  }
  else if (amounts.size() == 1 && bal.amounts.size() == 1) {
    return *this /= (*bal.amounts.begin()).second;
  }
  else if (*this == bal) {
    return (*this = 1L);
  }
  else {
    std::string msg;
    std::ostringstream errmsg(msg);
    errmsg << "It makes no sense to divide two balances: "
	   << *this << " / " << bal;
    throw amount_error(errmsg.str());
  }
}

balance_t::operator amount_t() const
{
  if (amounts.size() == 1) {
    return (*amounts.begin()).second;
  }
  else if (amounts.size() == 0) {
    return amount_t();
  }
  else {
    std::string msg;
    std::ostringstream errmsg(msg);
    errmsg << "Cannot convert a balance with "
	   << "multiple commodities to an amount: " << *this;
    throw amount_error(errmsg.str());
  }
}

balance_pair_t& balance_pair_t::operator/=(const balance_pair_t& bal_pair)
{
  if (bal_pair.cost && ! cost)
    cost = new balance_t(quantity);
  quantity /= bal_pair.quantity;
  if (cost)
    *cost /= bal_pair.cost ? *bal_pair.cost : bal_pair.quantity;

  if (bal_pair.price && *bal_pair.price && price)
      *price /= *bal_pair.price;
  return *this;
}

} // namespace ledger
<|endoftext|>


<commit_before>#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <math.h>
#include <pthread.h>
#include <assert.h>

#include "buffer.h"

extern "C" {

#define PTR_OFFSET(ptr,offset)	((void*)((char*)(ptr) + (offset)))

buffer_t* __posix_force_include_buffer_t;

void *fast_malloc(size_t x) {
    void *orig = malloc(x+16);
    // Walk either 8 or 16 bytes forward
    void *ptr = (void *)((((size_t)orig + 16) >> 4) << 4);
    ((void **)ptr)[-1] = orig;
    return ptr;
}

void fast_free(void *ptr) {
    free(((void**)ptr)[-1]);
}

void *safe_malloc(size_t x) {
    void *mem;
    x = ((x + 4095)/4096) * 4096;
    posix_memalign(&mem, 4096, x + 4096 * 3);
    printf("Allocated %lu bytes at %p with an electric fence\n", x, mem);

    // write the end address to unprotect in the initial fence
    ((void **)mem)[0] = PTR_OFFSET(mem, x + 4096);

    mprotect(mem, 4096, PROT_NONE);
    mprotect(PTR_OFFSET(mem, x + 4096), 4096, PROT_NONE);

    return PTR_OFFSET(mem, 4096);
}

void safe_free(void *ptr) {
    void *start = PTR_OFFSET(ptr, -4096);
    mprotect(start, 4096, PROT_READ | PROT_WRITE | PROT_EXEC);
    void *end = ((void **)start)[0];
    mprotect(end, 4096, PROT_READ | PROT_WRITE | PROT_EXEC);
    free(start);
}

struct work {
    void (*f)(int, uint8_t *);
    int next, max;
    uint8_t *closure;
    int id;
    int active_workers;
};

#define MAX_JOBS 4096
#define THREADS 4
struct {
    work jobs[MAX_JOBS];
    int head;
    int tail;
    pthread_mutex_t mutex;
    pthread_cond_t not_empty;
    pthread_t threads[THREADS];
    int ids;
} work_queue;

struct worker_arg {
    int id;
    work *job;
};

void *worker(void *void_arg) {
    worker_arg *arg = (worker_arg *)void_arg;
    while (1) {
        //fprintf(stderr, "About to lock mutex\n");
        pthread_mutex_lock(&work_queue.mutex);
        //fprintf(stderr, "Mutex locked, checking for work\n");

        // we're master, and there's no more work
        if (arg && arg->job->id != arg->id) {
            // wait until other workers are done
            if (arg->job->active_workers) {
                pthread_mutex_unlock(&work_queue.mutex);
                while (true) {
                    //fprintf(stderr, "Master waiting for workers to finish\n");
                    pthread_mutex_lock(&work_queue.mutex);
                    if (!arg->job->active_workers)
                        break;
                    pthread_mutex_unlock(&work_queue.mutex);
                }
            }
            // job is actually done
            pthread_mutex_unlock(&work_queue.mutex);
            //fprintf(stderr, "My work here is done. I am needed ... elsewhere!\n");
            return NULL;
        }

        if (work_queue.head == work_queue.tail) {
            assert(!arg); // the master should never get here
            //fprintf(stderr, "No work left. Going to sleep.\n");

            pthread_cond_wait(&work_queue.not_empty, &work_queue.mutex);
            pthread_mutex_unlock(&work_queue.mutex);
            continue;
        }

        //fprintf(stderr, "There is work\n");
        work *job = work_queue.jobs + work_queue.head;
        if (job->next == job->max) {
            //fprintf(stderr, "Found a finished job. Removing it\n");
            work_queue.head = (work_queue.head + 1) % MAX_JOBS;
            job->id = 0; // mark the job done
            pthread_mutex_unlock(&work_queue.mutex);
        } else {
            // claim this task
            work myjob = *job;
            job->next++;
            job->active_workers++;
            pthread_mutex_unlock(&work_queue.mutex);
            //fprintf(stderr, "Doing job %d\n", myjob.next);
            myjob.f(myjob.next, myjob.closure);
            //fprintf(stderr, "Done with job %d\n", myjob.next);
            pthread_mutex_lock(&work_queue.mutex);
            job->active_workers--;
            pthread_mutex_unlock(&work_queue.mutex);
        }
    }
}

void do_par_for(void (*f)(int, uint8_t *), int min, int size, uint8_t *closure) {
    static bool thread_pool_initialized = false;
    if (!thread_pool_initialized) {
        pthread_mutex_init(&work_queue.mutex, NULL);
        pthread_cond_init(&work_queue.not_empty, NULL);
        work_queue.head = work_queue.tail = 0;
        work_queue.ids = 1;
        for (int i = 0; i < THREADS; i++) {
            pthread_create(work_queue.threads + i, NULL, worker, NULL);
        }

        thread_pool_initialized = true;
    }

    // Enqueue the job
    pthread_mutex_lock(&work_queue.mutex);
    //fprintf(stderr, "Enqueuing some work\n");
    work job = {f, min, min + size, closure, work_queue.ids++, 0};
    if (job.id == 0) job.id = work_queue.ids++; // disallow zero, as it flags a completed job
    work_queue.jobs[work_queue.tail] = job;
    work *jobPtr = work_queue.jobs + work_queue.tail;
    worker_arg arg = {job.id, jobPtr};
    int new_tail = (work_queue.tail + 1) % MAX_JOBS;
    assert(new_tail != work_queue.head);
    work_queue.tail = new_tail;

    // TODO: check to make sure the work queue doesn't overflow
    pthread_mutex_unlock(&work_queue.mutex);

    // Wake up everyone
    pthread_cond_broadcast(&work_queue.not_empty);

    // Do some work myself
    worker((void *)(&arg));
}

float sqrt_f32(float x) {
    return sqrtf(x);
}

float sin_f32(float x) {
    return sinf(x);
}

float cos_f32(float x) {
    return cosf(x);
}

float exp_f32(float x) {
    return expf(x);
}

float log_f32(float x) {
    return logf(x);
}

float pow_f32(float x, float y) {
    return powf(x, y);
}

float floor_f32(float x) {
    return floorf(x);
}

float ceil_f32(float x) {
    return ceilf(x);
}

float round_f32(float x) {
    return roundf(x);
}

#include <sys/time.h>

int currentTime() {
    static bool initialized = false;
    static timeval start;
    if (!initialized) {
        gettimeofday(&start, NULL);
        initialized = true;
        return 0;
    } else {
        timeval now;
        gettimeofday(&now, NULL);
        return
            (now.tv_sec - start.tv_sec)*1000 +
            (now.tv_usec - start.tv_usec)/1000;
    }
}

}
<commit_msg>thread count settable with environment var<commit_after>#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <math.h>
#include <pthread.h>
#include <assert.h>

#include "buffer.h"

extern "C" {

#define PTR_OFFSET(ptr,offset)	((void*)((char*)(ptr) + (offset)))

buffer_t* __posix_force_include_buffer_t;

void *fast_malloc(size_t x) {
    void *orig = malloc(x+16);
    // Walk either 8 or 16 bytes forward
    void *ptr = (void *)((((size_t)orig + 16) >> 4) << 4);
    ((void **)ptr)[-1] = orig;
    return ptr;
}

void fast_free(void *ptr) {
    free(((void**)ptr)[-1]);
}

void *safe_malloc(size_t x) {
    void *mem;
    x = ((x + 4095)/4096) * 4096;
    posix_memalign(&mem, 4096, x + 4096 * 3);
    printf("Allocated %lu bytes at %p with an electric fence\n", x, mem);

    // write the end address to unprotect in the initial fence
    ((void **)mem)[0] = PTR_OFFSET(mem, x + 4096);

    mprotect(mem, 4096, PROT_NONE);
    mprotect(PTR_OFFSET(mem, x + 4096), 4096, PROT_NONE);

    return PTR_OFFSET(mem, 4096);
}

void safe_free(void *ptr) {
    void *start = PTR_OFFSET(ptr, -4096);
    mprotect(start, 4096, PROT_READ | PROT_WRITE | PROT_EXEC);
    void *end = ((void **)start)[0];
    mprotect(end, 4096, PROT_READ | PROT_WRITE | PROT_EXEC);
    free(start);
}

struct work {
    void (*f)(int, uint8_t *);
    int next, max;
    uint8_t *closure;
    int id;
    int active_workers;
};

#define MAX_JOBS 65536
#define MAX_THREADS 64
struct {
    work jobs[MAX_JOBS];
    int head;
    int tail;
    pthread_mutex_t mutex;
    pthread_cond_t not_empty;
    pthread_t threads[MAX_THREADS];
    int ids;
} work_queue;

struct worker_arg {
    int id;
    work *job;
};

int threads;

void *worker(void *void_arg) {
    worker_arg *arg = (worker_arg *)void_arg;
    while (1) {
        //fprintf(stderr, "About to lock mutex\n");
        pthread_mutex_lock(&work_queue.mutex);
        //fprintf(stderr, "Mutex locked, checking for work\n");

        // we're master, and there's no more work
        if (arg && arg->job->id != arg->id) {
            // wait until other workers are done
            if (arg->job->active_workers) {
                pthread_mutex_unlock(&work_queue.mutex);
                while (true) {
                    //fprintf(stderr, "Master waiting for workers to finish\n");
                    pthread_mutex_lock(&work_queue.mutex);
                    if (!arg->job->active_workers)
                        break;
                    pthread_mutex_unlock(&work_queue.mutex);
                }
            }
            // job is actually done
            pthread_mutex_unlock(&work_queue.mutex);
            //fprintf(stderr, "My work here is done. I am needed ... elsewhere!\n");
            return NULL;
        }

        if (work_queue.head == work_queue.tail) {
            assert(!arg); // the master should never get here
            //fprintf(stderr, "No work left. Going to sleep.\n");

            pthread_cond_wait(&work_queue.not_empty, &work_queue.mutex);
            pthread_mutex_unlock(&work_queue.mutex);
            continue;
        }

        //fprintf(stderr, "There is work\n");
        work *job = work_queue.jobs + work_queue.head;
        if (job->next == job->max) {
            //fprintf(stderr, "Found a finished job. Removing it\n");
            work_queue.head = (work_queue.head + 1) % MAX_JOBS;
            job->id = 0; // mark the job done
            pthread_mutex_unlock(&work_queue.mutex);
        } else {
            int remaining = job->max - job->next;
            // Claim some tasks
            //int claimed = (remaining + threads - 1)/threads;
            int claimed = 1;
            //printf("Claiming %d tasks\n", claimed);
            work myjob = *job;
            job->next += claimed;
            myjob.max = job->next;
            job->active_workers++;
            pthread_mutex_unlock(&work_queue.mutex);
            //fprintf(stderr, "Doing job %d\n", myjob.next);
            for (; myjob.next < myjob.max; myjob.next++)
                myjob.f(myjob.next, myjob.closure);
            //fprintf(stderr, "Done with job %d\n", myjob.next);
            pthread_mutex_lock(&work_queue.mutex);
            job->active_workers--;
            pthread_mutex_unlock(&work_queue.mutex);
        }
    }
}

void do_par_for(void (*f)(int, uint8_t *), int min, int size, uint8_t *closure) {
    static bool thread_pool_initialized = false;
    if (!thread_pool_initialized) {
        pthread_mutex_init(&work_queue.mutex, NULL);
        pthread_cond_init(&work_queue.not_empty, NULL);
        work_queue.head = work_queue.tail = 0;
        work_queue.ids = 1;
        char *threadStr = getenv("HL_NUMTHREADS");
        threads = 8;
        if (threadStr) {
            threads = atoi(threadStr);
        } else {
            printf("HL_NUMTHREADS not defined. Defaulting to 8 threads.\n");
        }
        if (threads > MAX_THREADS) threads = MAX_THREADS;
        for (int i = 0; i < threads-1; i++) {
            pthread_create(work_queue.threads + i, NULL, worker, NULL);
        }

        thread_pool_initialized = true;
    }

    // Enqueue the job
    pthread_mutex_lock(&work_queue.mutex);
    //fprintf(stderr, "Enqueuing some work\n");
    work job = {f, min, min + size, closure, work_queue.ids++, 0};
    if (job.id == 0) job.id = work_queue.ids++; // disallow zero, as it flags a completed job
    work_queue.jobs[work_queue.tail] = job;
    work *jobPtr = work_queue.jobs + work_queue.tail;
    worker_arg arg = {job.id, jobPtr};
    int new_tail = (work_queue.tail + 1) % MAX_JOBS;
    assert(new_tail != work_queue.head);
    work_queue.tail = new_tail;

    // TODO: check to make sure the work queue doesn't overflow
    pthread_mutex_unlock(&work_queue.mutex);

    // Wake up everyone
    pthread_cond_broadcast(&work_queue.not_empty);

    // Do some work myself
    worker((void *)(&arg));
}

float sqrt_f32(float x) {
    return sqrtf(x);
}

float sin_f32(float x) {
    return sinf(x);
}

float cos_f32(float x) {
    return cosf(x);
}

float exp_f32(float x) {
    return expf(x);
}

float log_f32(float x) {
    return logf(x);
}

float pow_f32(float x, float y) {
    return powf(x, y);
}

float floor_f32(float x) {
    return floorf(x);
}

float ceil_f32(float x) {
    return ceilf(x);
}

float round_f32(float x) {
    return roundf(x);
}

#include <sys/time.h>

int currentTime() {
    static bool initialized = false;
    static timeval start;
    if (!initialized) {
        gettimeofday(&start, NULL);
        initialized = true;
        return 0;
    } else {
        timeval now;
        gettimeofday(&now, NULL);
        return
            (now.tv_sec - start.tv_sec)*1000 +
            (now.tv_usec - start.tv_usec)/1000;
    }
}

}
<|endoftext|>


<commit_before>#include <iostream>
#include <random>

#include "genetic.h"

std::mt19937 Context::rng;

namespace Config {
  const float selectBias = 3;
  const int popSize = 500;
  const int popSize2 = 500;
  const int nGen = 200;

  const float expLengthIni = 100;
  const float expLengthAdd = 5;
  const float pLength = 1/1000.0;
  const float pControl = 1/1000.0;

  const int nIn = 6;
  const int nOut = 3;
  const int nAnc = 3;
  const int nBit = nIn + nOut + nAnc;

  unsigned f(unsigned in) {
    int in1 = in & ((1 << Config::nIn/2) - 1);
    int in2 = (in >> Config::nIn/2) & ((1 << Config::nIn/2) - 1);
    return (in1 + in2) & ((1 << Config::nOut) - 1);
  }
}

class Gene {
  int tgt;
  unsigned ctrlEnc;
  unsigned ctrl;

  public:
  Gene(int _target, unsigned _control): tgt(_target), ctrlEnc(_control) {
    ctrl =
      ((ctrlEnc >> tgt) << (tgt+1)) // shift bits left of tgt to the left
        |
      (ctrlEnc & ((1 << tgt) - 1));    // keep bits right of tgt
  }

  int target() const {
    return tgt;
  }

  int control() const {
    return ctrlEnc;
  }

  unsigned apply(unsigned src) {
    return ((src & ctrl) == ctrl)
      ? (src ^ (1 << tgt))
      : src;
  }

  friend std::ostream& operator<< (std::ostream& os, const Gene& g) {
    os << g.tgt+1;
    auto c = g.ctrl;
    if(c != 0) {
      os << '[';
      for(int i = 1, m = 1; i <= Config::nBit; i++, m <<= 1)
        if(c & m)
          os << i;
      os << ']';
    }
    return os;
  }
};

class Candidate: public ICandidate<float> {
  std::vector<Gene> gt;

  public:
  Candidate() = default;

  Candidate(auto _gt): gt(_gt) { }

  static Candidate m1(const Candidate&);
  static Candidate m2(const Candidate&);
  static Candidate m3(const Candidate&);
  static Candidate m4(const Candidate&);
  static Candidate m5(const Candidate&);
  static Candidate m6(const Candidate&);
  static Candidate crossover(const Candidate&, const Candidate&);

  friend std::ostream& operator<< (std::ostream& os, const Candidate& c) {
    os << c.fitness() << ':';
    for(Gene g : c.gt)
      os << ' ' << g;
    return os;
  }

  private:
  float computeFitness() const {
    register unsigned work;
    unsigned out;
    int mism = 0;
    for(int in = 0; in < (1 << Config::nIn); in++) {
      work = in;
      for(Gene g : gt)
        work = g.apply(work);
      out = (work >> Config::nIn) & ((1 << Config::nOut) - 1);
      if(out != Config::f(in))
        mism++;
    }
    return mism + gt.size()*Config::pLength + Config::pControl*hamming();
  }

  int hamming() const {
    int v = 0;
    for(Gene g : gt) {
      unsigned ctrl = g.control();
      int h = 0;
      for(int i = 0, m = 1; i < Config::nBit - 1; i++, m <<= 1)
        if(ctrl & m)
          h++;
      v += h*h;
    }
    return v;
  }
};

/* Alter target in one gene */
Candidate Candidate::m1(const Candidate& p) {
  auto gm = p.gt;
  if(gm.size() == 0)
    return p;
  std::uniform_int_distribution<> dPos(0, gm.size() - 1);
  std::uniform_int_distribution<> dTgt(0, Config::nBit - 1);
  int pos = dPos(Context::rng);
  gm[pos] = Gene(dTgt(Context::rng), gm[pos].control());
  return Candidate(gm);
}

/* Alter control in one gene */
Candidate Candidate::m2(const Candidate& p) {
  auto gm = p.gt;
  if(gm.size() == 0)
    return p;
  std::uniform_int_distribution<> dPos(0, gm.size() - 1);
  std::uniform_int_distribution<> dCtrl(0, (1 << (Config::nBit-1)) - 1);
  int pos = dPos(Context::rng);
  gm[pos] = Gene(gm[pos].target(), dCtrl(Context::rng));
  return Candidate(gm);
}

/* Add a random operation sequence */
Candidate Candidate::m3(const Candidate& p) {
  auto gm = p.gt;
  std::uniform_int_distribution<> dPos(0, gm.size());
  std::uniform_int_distribution<> dTgt(0, Config::nBit - 1);
  std::uniform_int_distribution<> dCtrl(0, (1 << (Config::nBit-1)) - 1);
  std::uniform_real_distribution<> dProb(0, 1);
  int pos = dPos(Context::rng);
  float probTerm = 1/Config::expLengthAdd;
  do {
    gm.insert(gm.begin() + pos, Gene(dTgt(Context::rng), dCtrl(Context::rng)));
  } while(dProb(Context::rng) > probTerm);
  return Candidate(gm);
}

/* Add an operation pair */
Candidate Candidate::m4(const Candidate& p) {
  auto gm = p.gt;
  std::uniform_int_distribution<> dPos(0, gm.size());
  std::uniform_int_distribution<> dTgt(0, Config::nBit - 1);
  std::uniform_int_distribution<> dCtrl(0, (1 << (Config::nBit-1)) - 1);
  int pos1 = dPos(Context::rng),
      pos2 = dPos(Context::rng);
  if(pos2 < pos1)
    std::swap(pos1, pos2);
  unsigned tgt = dTgt(Context::rng),
           ctrl = dCtrl(Context::rng);
  gm.insert(gm.begin() + pos2, Gene(tgt, ctrl));
  gm.insert(gm.begin() + pos1, Gene(tgt, ctrl));
  return Candidate(gm);
}

/* Delete a random slice */
Candidate Candidate::m5(const Candidate& p) {
  auto gm = p.gt;
  if(gm.size() == 0)
    return p;
  std::uniform_int_distribution<> dPos(0, gm.size());
  std::uniform_int_distribution<> dCtrl(0, (1 << (Config::nBit-1)) - 1);
  int pos1 = dPos(Context::rng),
      pos2 = dPos(Context::rng);
  if(pos2 < pos1)
    std::swap(pos1, pos2);
  gm.erase(gm.begin() + pos1, gm.begin() + pos2);
  return Candidate(gm);
}

/* Permute gates (split & swap) */
Candidate Candidate::m6(const Candidate& p) {
  if(p.gt.size() == 0)
    return p;
  std::uniform_int_distribution<> dPos(0, p.gt.size());
  int pos = dPos(Context::rng);
  std::vector<Gene> gm(p.gt.begin() + pos, p.gt.end());
  gm.insert(gm.end(), p.gt.begin(), p.gt.begin() + pos);
  return Candidate(gm);
}

/* One-point crossover */
Candidate Candidate::crossover(const Candidate& p1, const Candidate& p2) {
  auto gt1 = p1.gt,
       gt2 = p2.gt;
  std::uniform_int_distribution<> dPos1(0, gt1.size()), dPos2(0, gt2.size());
  int pos1 = dPos1(Context::rng),
      pos2 = dPos2(Context::rng);
  std::vector<Gene> gm(gt1.begin(), gt1.begin() + pos1);
  gm.insert(gm.end(), gt2.begin() + pos2, gt2.end());
  return Candidate(gm);
}

int main() {
  Context::rng = std::mt19937((std::random_device())());

  Population<Candidate> pop;
  {
    float probTerm = 1/Config::expLengthIni; /* probability of termination; expLength = expected number of genes */
    std::uniform_real_distribution<float> rDist(0, 1);
    std::uniform_int_distribution<> dTgt(0, Config::nBit - 1);
    std::uniform_int_distribution<> dCtrl(0, (1 << (Config::nBit-1)) - 1);
    for(int i = 0; i < Config::popSize; i++) {
      std::vector<Gene> gt;
      do {
        gt.push_back(Gene(dTgt(Context::rng), dCtrl(Context::rng)));
      } while(rDist(Context::rng) > probTerm);
      pop.add(Candidate(gt));
    }
  }

  for(int gen = 0; gen < Config::nGen; gen++) {
    Population<Candidate> pop2 = pop;
    //std::uniform_real_distribution<float> rDist(0, 1);
    std::uniform_int_distribution<> rDist(0, 5);
    Candidate (*func[])(const Candidate&) = {Candidate::m1, Candidate::m2, Candidate::m3, Candidate::m4, Candidate::m5, Candidate::m6};
    for(int b = 0; b < Config::popSize2; b++)
      pop2.add(func[rDist(Context::rng)](pop.rankSelect()));

    pop = pop2.trim();

    std::cout << "Gen " << gen << ": best of pop " << pop.best() << std::endl;
  }
}
<commit_msg>Candidate::getNew()<commit_after>#include <iostream>
#include <random>
#include <functional>

#include "genetic.h"

std::mt19937 Context::rng;

namespace Config {
  const float selectBias = 3;
  const int popSize = 500;
  const int popSize2 = 500;
  const int nGen = 500;

  const float expLengthIni = 100;
  const float expLengthAdd = 5;
  const float pLength = 1/1000.0;
  const float pControl = 1/1000.0;

  const int nIn = 6;
  const int nOut = 1;
  const int nAnc = 3;
  const int nBit = nIn + nOut + nAnc;

  unsigned f(unsigned in) {
    int in1 = in & ((1 << Config::nIn/2) - 1);
    int in2 = (in >> Config::nIn/2) & ((1 << Config::nIn/2) - 1);
    return (in1 + in2 >> 2) & ((1 << Config::nOut) - 1);
  }
}

class Gene {
  int tgt;
  unsigned ctrlEnc;
  unsigned ctrl;

  public:
  Gene(int _target, unsigned _control): tgt(_target), ctrlEnc(_control) {
    ctrl =
      ((ctrlEnc >> tgt) << (tgt+1)) // shift bits left of tgt to the left
        |
      (ctrlEnc & ((1 << tgt) - 1));    // keep bits right of tgt
  }

  int target() const {
    return tgt;
  }

  int control() const {
    return ctrlEnc;
  }

  unsigned apply(unsigned src) {
    return ((src & ctrl) == ctrl)
      ? (src ^ (1 << tgt))
      : src;
  }

  friend std::ostream& operator<< (std::ostream& os, const Gene& g) {
    os << g.tgt+1;
    auto c = g.ctrl;
    if(c != 0) {
      os << '[';
      for(int i = 1, m = 1; i <= Config::nBit; i++, m <<= 1)
        if(c & m)
          os << i;
      os << ']';
    }
    return os;
  }
};

class Candidate: public ICandidate<float> {
  std::vector<Gene> gt;

  public:
  Candidate() = default;

  Candidate(auto _gt): gt(_gt) { }

  static Candidate m1(const Candidate&);
  static Candidate m2(const Candidate&);
  static Candidate m3(const Candidate&);
  static Candidate m4(const Candidate&);
  static Candidate m5(const Candidate&);
  static Candidate m6(const Candidate&);
  static Candidate crossover(const Candidate&, const Candidate&);

  static Candidate getNew(std::function<Candidate()>);

  friend std::ostream& operator<< (std::ostream& os, const Candidate& c) {
    os << c.fitness() << ':';
    for(Gene g : c.gt)
      os << ' ' << g;
    return os;
  }

  private:
  float computeFitness() const {
    register unsigned work;
    unsigned out;
    int mism = 0;
    for(int in = 0; in < (1 << Config::nIn); in++) {
      work = in;
      for(Gene g : gt)
        work = g.apply(work);
      out = (work >> Config::nIn) & ((1 << Config::nOut) - 1);
      if(out != Config::f(in))
        mism++;
    }
    return mism + gt.size()*Config::pLength + Config::pControl*hamming();
  }

  int hamming() const {
    int v = 0;
    for(Gene g : gt) {
      unsigned ctrl = g.control();
      int h = 0;
      for(int i = 0, m = 1; i < Config::nBit - 1; i++, m <<= 1)
        if(ctrl & m)
          h++;
      v += h*h;
    }
    return v;
  }
};

/* Alter target in one gene */
Candidate Candidate::m1(const Candidate& p) {
  auto gm = p.gt;
  if(gm.size() == 0)
    return p;
  std::uniform_int_distribution<> dPos(0, gm.size() - 1);
  std::uniform_int_distribution<> dTgt(0, Config::nBit - 1);
  int pos = dPos(Context::rng);
  gm[pos] = Gene(dTgt(Context::rng), gm[pos].control());
  return Candidate(gm);
}

/* Alter control in one gene */
Candidate Candidate::m2(const Candidate& p) {
  auto gm = p.gt;
  if(gm.size() == 0)
    return p;
  std::uniform_int_distribution<> dPos(0, gm.size() - 1);
  std::uniform_int_distribution<> dCtrl(0, (1 << (Config::nBit-1)) - 1);
  int pos = dPos(Context::rng);
  gm[pos] = Gene(gm[pos].target(), dCtrl(Context::rng));
  return Candidate(gm);
}

/* Add a random operation sequence */
Candidate Candidate::m3(const Candidate& p) {
  auto gm = p.gt;
  std::uniform_int_distribution<> dPos(0, gm.size());
  std::uniform_int_distribution<> dTgt(0, Config::nBit - 1);
  std::uniform_int_distribution<> dCtrl(0, (1 << (Config::nBit-1)) - 1);
  std::uniform_real_distribution<> dProb(0, 1);
  int pos = dPos(Context::rng);
  float probTerm = 1/Config::expLengthAdd;
  do {
    gm.insert(gm.begin() + pos, Gene(dTgt(Context::rng), dCtrl(Context::rng)));
  } while(dProb(Context::rng) > probTerm);
  return Candidate(gm);
}

/* Add an operation pair */
Candidate Candidate::m4(const Candidate& p) {
  auto gm = p.gt;
  std::uniform_int_distribution<> dPos(0, gm.size());
  std::uniform_int_distribution<> dTgt(0, Config::nBit - 1);
  std::uniform_int_distribution<> dCtrl(0, (1 << (Config::nBit-1)) - 1);
  int pos1 = dPos(Context::rng),
      pos2 = dPos(Context::rng);
  if(pos2 < pos1)
    std::swap(pos1, pos2);
  unsigned tgt = dTgt(Context::rng),
           ctrl = dCtrl(Context::rng);
  gm.insert(gm.begin() + pos2, Gene(tgt, ctrl));
  gm.insert(gm.begin() + pos1, Gene(tgt, ctrl));
  return Candidate(gm);
}

/* Delete a random slice */
Candidate Candidate::m5(const Candidate& p) {
  auto gm = p.gt;
  if(gm.size() == 0)
    return p;
  std::uniform_int_distribution<> dPos(0, gm.size());
  std::uniform_int_distribution<> dCtrl(0, (1 << (Config::nBit-1)) - 1);
  int pos1 = dPos(Context::rng),
      pos2 = dPos(Context::rng);
  if(pos2 < pos1)
    std::swap(pos1, pos2);
  gm.erase(gm.begin() + pos1, gm.begin() + pos2);
  return Candidate(gm);
}

/* Permute gates (split & swap) */
Candidate Candidate::m6(const Candidate& p) {
  if(p.gt.size() == 0)
    return p;
  std::uniform_int_distribution<> dPos(0, p.gt.size());
  int pos = dPos(Context::rng);
  std::vector<Gene> gm(p.gt.begin() + pos, p.gt.end());
  gm.insert(gm.end(), p.gt.begin(), p.gt.begin() + pos);
  return Candidate(gm);
}

/* One-point crossover */
Candidate Candidate::crossover(const Candidate& p1, const Candidate& p2) {
  auto gt1 = p1.gt,
       gt2 = p2.gt;
  std::uniform_int_distribution<> dPos1(0, gt1.size()), dPos2(0, gt2.size());
  int pos1 = dPos1(Context::rng),
      pos2 = dPos2(Context::rng);
  std::vector<Gene> gm(gt1.begin(), gt1.begin() + pos1);
  gm.insert(gm.end(), gt2.begin() + pos2, gt2.end());
  return Candidate(gm);
}

Candidate Candidate::getNew(std::function<Candidate()> get) {
  Candidate (*mutations[])(const Candidate&) = {Candidate::m1, Candidate::m2, Candidate::m3, Candidate::m4, Candidate::m5, Candidate::m6};
  //std::uniform_real_distribution<float> rDist(0, 1);
  std::uniform_int_distribution<> rDist(0, 6);
  int which = rDist(Context::rng);
  if(which < 6)
    return mutations[which](get());
  else
    return Candidate::crossover(get(), get());
}

int main() {
  Context::rng = std::mt19937((std::random_device())());

  Population<Candidate> pop;
  {
    float probTerm = 1/Config::expLengthIni; /* probability of termination; expLength = expected number of genes */
    std::uniform_real_distribution<float> rDist(0, 1);
    std::uniform_int_distribution<> dTgt(0, Config::nBit - 1);
    std::uniform_int_distribution<> dCtrl(0, (1 << (Config::nBit-1)) - 1);
    for(int i = 0; i < Config::popSize; i++) {
      std::vector<Gene> gt;
      do {
        gt.push_back(Gene(dTgt(Context::rng), dCtrl(Context::rng)));
      } while(rDist(Context::rng) > probTerm);
      pop.add(Candidate(gt));
    }
  }

  for(int gen = 0; gen < Config::nGen; gen++) {
    Population<Candidate> pop2 = pop;
    for(int b = 0; b < Config::popSize2; b++)
      pop2.add(Candidate::getNew([&] { return pop.rankSelect(); }));

    pop = pop2.trim();

    std::cout << "Gen " << gen << ": best of pop " << pop.best() << std::endl;
  }
}
<|endoftext|>