<commit_before>/* Copyright 2020 Google LLC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include "ml_metadata/tools/mlmd_bench/fill_types_workload.h"

#include <vector>

#include <gtest/gtest.h>
#include "absl/memory/memory.h"
#include "ml_metadata/metadata_store/metadata_store.h"
#include "ml_metadata/metadata_store/metadata_store_factory.h"
#include "ml_metadata/metadata_store/test_util.h"
#include "ml_metadata/metadata_store/types.h"
#include "ml_metadata/proto/metadata_store.pb.h"
#include "ml_metadata/proto/metadata_store_service.pb.h"
#include "ml_metadata/tools/mlmd_bench/proto/mlmd_bench.pb.h"
#include "tensorflow/core/lib/core/status_test_util.h"

namespace ml_metadata {
namespace {

constexpr int64 kNumOperations = 100;

// Generates FillTypes Workload where `update` indicates inserting or updating.
void GenerateFillTypesWorkload(
    const bool update, const int64 num_operations,
    std::vector<std::unique_ptr<FillTypes>>& fill_types) {
  FillTypesConfig fill_artifact_types_config =
      testing::ParseTextProtoOrDie<FillTypesConfig>(
          R"(
            specification: ARTIFACT_TYPE
            num_properties { minimum: 1 maximum: 10 }
          )");
  fill_artifact_types_config.set_update(update);
  std::unique_ptr<FillTypes> fill_artifact_types = absl::make_unique<FillTypes>(
      FillTypes(fill_artifact_types_config, num_operations));

  FillTypesConfig fill_execution_types_config =
      testing::ParseTextProtoOrDie<FillTypesConfig>(
          R"(
            specification: EXECUTION_TYPE
            num_properties { minimum: 1 maximum: 10 }
          )");
  fill_execution_types_config.set_update(update);
  std::unique_ptr<FillTypes> fill_execution_types =
      absl::make_unique<FillTypes>(
          FillTypes(fill_execution_types_config, num_operations));

  FillTypesConfig fill_context_types_config =
      testing::ParseTextProtoOrDie<FillTypesConfig>(
          R"(
            specification: CONTEXT_TYPE
            num_properties { minimum: 1 maximum: 10 }
          )");
  fill_context_types_config.set_update(update);
  std::unique_ptr<FillTypes> fill_context_types = absl::make_unique<FillTypes>(
      FillTypes(fill_context_types_config, num_operations));

  fill_types.push_back(std::move(fill_artifact_types));
  fill_types.push_back(std::move(fill_execution_types));
  fill_types.push_back(std::move(fill_context_types));
}

// Checks types update status. If the updates are working properly, the type id
// should remain the same even after the updates. On the other hand, the
// properties size for each type should be greater than before since some new
// fields have been added in the update process.
bool CheckArtifactTypesUpdateStatus(
    const GetArtifactTypesResponse& response_before,
    const GetArtifactTypesResponse& response_after,
    const int64 num_operations) {
  for (int64 i = 0; i < num_operations; ++i) {
    if (response_before.artifact_types()[i].id() !=
        response_after.artifact_types()[i].id()) {
      return false;
    }
    if (response_before.artifact_types()[i].properties().size() >=
        response_after.artifact_types()[i].properties().size()) {
      return false;
    }
  }
  return true;
}

bool CheckExecutionTypesUpdateStatus(
    const GetExecutionTypesResponse& response_before,
    const GetExecutionTypesResponse& response_after,
    const int64 num_operations) {
  for (int64 i = 0; i < num_operations; ++i) {
    if (response_before.execution_types()[i].id() !=
        response_after.execution_types()[i].id()) {
      return false;
    }
    if (response_before.execution_types()[i].properties().size() >=
        response_after.execution_types()[i].properties().size()) {
      return false;
    }
  }
  return true;
}

bool CheckContextTypesUpdateStatus(
    const GetContextTypesResponse& response_before,
    const GetContextTypesResponse& response_after, const int64 num_operations) {
  for (int64 i = 0; i < num_operations; ++i) {
    if (response_before.context_types()[i].id() !=
        response_after.context_types()[i].id()) {
      return false;
    }
    if (response_before.context_types()[i].properties().size() >=
        response_after.context_types()[i].properties().size()) {
      return false;
    }
  }
  return true;
}

// Updates the get types response of the current store instance.
tensorflow::Status UpdateGetResponse(
    MetadataStore* store, GetArtifactTypesResponse& get_artifact_types_response,
    GetExecutionTypesResponse& get_execution_types_response,
    GetContextTypesResponse& get_context_types_response) {
  TF_RETURN_IF_ERROR(
      store->GetArtifactTypes(/*request=*/{}, &get_artifact_types_response));
  TF_RETURN_IF_ERROR(
      store->GetExecutionTypes(/*request=*/{}, &get_execution_types_response));
  TF_RETURN_IF_ERROR(
      store->GetContextTypes(/*request=*/{}, &get_context_types_response));
  return tensorflow::Status::OK();
}

// Executes the given FillTypes workloads.
tensorflow::Status SetUpAndExecuteWorkload(
    MetadataStore* store,
    std::vector<std::unique_ptr<FillTypes>>& fill_types_workloads) {
  for (auto& workload : fill_types_workloads) {
    TF_RETURN_IF_ERROR(workload->SetUp(store));
    for (int64 i = 0; i < workload->num_operations(); ++i) {
      OpStats op_stats;
      TF_RETURN_IF_ERROR(workload->RunOp(i, store, op_stats));
    }
  }
  return tensorflow::Status::OK();
}

// Test fixture that uses the same data configuration for multiple following
// FillTypes insert tests.
class FillTypesInsertTest : public ::testing::Test {
 protected:
  void SetUp() override {
    ConnectionConfig mlmd_config;
    // Uses a fake in-memory SQLite database for testing.
    mlmd_config.mutable_fake_database();
    TF_ASSERT_OK(CreateMetadataStore(mlmd_config, &store_));
    // `false` indicates the insert cases for FillTypes.
    GenerateFillTypesWorkload(false, kNumOperations, fill_types_insert_);
  }

  std::vector<std::unique_ptr<FillTypes>> fill_types_insert_;
  std::unique_ptr<MetadataStore> store_;
};

// Tests the SetUpImpl() for FillTypes insert cases.
// Checks the SetUpImpl() indeed prepares a list of work items whose length is
// the same as the specified number of operations.
TEST_F(FillTypesInsertTest, SetUpImplTest) {
  for (auto& workload : fill_types_insert_) {
    TF_ASSERT_OK(workload->SetUp(store_.get()));
    EXPECT_EQ(kNumOperations, workload->num_operations());
  }
}

// Tests the RunOpImpl() for insert types.
// Checks indeed all the work items have been executed and all the types have
// been inserted into the db.
TEST_F(FillTypesInsertTest, InsertTest) {
  TF_ASSERT_OK(SetUpAndExecuteWorkload(store_.get(), fill_types_insert_));

  GetArtifactTypesResponse get_artifact_types_response;
  GetExecutionTypesResponse get_execution_types_response;
  GetContextTypesResponse get_context_types_response;
  TF_ASSERT_OK(UpdateGetResponse(store_.get(), get_artifact_types_response,
                                 get_execution_types_response,
                                 get_context_types_response));

  EXPECT_EQ(get_artifact_types_response.artifact_types_size(),
            fill_types_insert_[0]->num_operations());
  EXPECT_EQ(get_execution_types_response.execution_types_size(),
            fill_types_insert_[1]->num_operations());
  EXPECT_EQ(get_context_types_response.context_types_size(),
            fill_types_insert_[2]->num_operations());
}

// Test fixture that uses the same data configuration for multiple following
// FillTypes update tests.
class FillTypesUpdateTest : public ::testing::Test {
 protected:
  void SetUp() override {
    ConnectionConfig mlmd_config;
    // Uses a fake in-memory SQLite database for testing.
    mlmd_config.mutable_fake_database();
    TF_ASSERT_OK(CreateMetadataStore(mlmd_config, &store_));
    // Inserts some types into the db for later update.
    GenerateFillTypesWorkload(false, kNumOperations, fill_types_insert_);
    TF_ASSERT_OK(SetUpAndExecuteWorkload(store_.get(), fill_types_insert_));
  }

  std::vector<std::unique_ptr<FillTypes>> fill_types_insert_;
  std::vector<std::unique_ptr<FillTypes>> fill_types_update_;
  std::unique_ptr<MetadataStore> store_;
};

// Tests the SetUpImpl() for FillTypes update cases.
// Checks the SetUpImpl() indeed prepares a list of work items whose length is
// the same as the specified number of operations.
TEST_F(FillTypesUpdateTest, SetUpImplTest) {
  // `true` indicates the update cases for FillTypes.
  GenerateFillTypesWorkload(true, kNumOperations, fill_types_update_);
  for (auto& workload : fill_types_update_) {
    TF_ASSERT_OK(workload->SetUp(store_.get()));
    EXPECT_EQ(kNumOperations, workload->num_operations());
  }
}

// Tests the normal update cases(the number of types inside db are enough for
// updating).
TEST_F(FillTypesUpdateTest, NormalUpdateTest) {
  // Gets the get_response_before_update for later comparison.
  GetArtifactTypesResponse get_artifact_types_response_before_update;
  GetExecutionTypesResponse get_execution_types_response_before_update;
  GetContextTypesResponse get_context_types_response_before_update;
  TF_ASSERT_OK(UpdateGetResponse(store_.get(),
                                 get_artifact_types_response_before_update,
                                 get_execution_types_response_before_update,
                                 get_context_types_response_before_update));

  // The update number_opeartions is the same the insert which means the number
  // of types inside db are enough for update.
  GenerateFillTypesWorkload(true, kNumOperations, fill_types_update_);

  // Prepares and executes the update workloads.
  TF_ASSERT_OK(SetUpAndExecuteWorkload(store_.get(), fill_types_update_));

  // Gets the get_response_after_update for later comparison.
  GetArtifactTypesResponse get_artifact_types_response_after_update;
  GetExecutionTypesResponse get_execution_types_response_after_update;
  GetContextTypesResponse get_context_types_response_after_update;
  TF_ASSERT_OK(UpdateGetResponse(store_.get(),
                                 get_artifact_types_response_after_update,
                                 get_execution_types_response_after_update,
                                 get_context_types_response_after_update));

  // If the updates are working properly, the type id should remain the
  // same even after the updates. On the other hand, the properties size for
  // each type should be greater than before since some new fields have been
  // added in the update process.
  EXPECT_TRUE(
      CheckArtifactTypesUpdateStatus(get_artifact_types_response_before_update,
                                     get_artifact_types_response_after_update,
                                     fill_types_update_[0]->num_operations()));
  EXPECT_TRUE(CheckExecutionTypesUpdateStatus(
      get_execution_types_response_before_update,
      get_execution_types_response_after_update,
      fill_types_update_[1]->num_operations()));
  EXPECT_TRUE(
      CheckContextTypesUpdateStatus(get_context_types_response_before_update,
                                    get_context_types_response_after_update,
                                    fill_types_update_[2]->num_operations()));
  // There should no more types inside db after update since the updates only
  // update the existed types inserted before by fill_types_insert_.
  EXPECT_EQ(get_artifact_types_response_after_update.artifact_types_size(),
            fill_types_insert_[0]->num_operations());
  EXPECT_EQ(get_execution_types_response_after_update.execution_types_size(),
            fill_types_insert_[1]->num_operations());
  EXPECT_EQ(get_context_types_response_after_update.context_types_size(),
            fill_types_insert_[2]->num_operations());
}

// Tests the update cases(the number of types inside db are not enough for
// updating) that needs making up and inserting new types in db for update.
TEST_F(FillTypesUpdateTest, MakeUpUpdateTest) {
  // Gets the get_response_before_update for later comparison.
  GetArtifactTypesResponse get_artifact_types_response_before_update;
  GetExecutionTypesResponse get_execution_types_response_before_update;
  GetContextTypesResponse get_context_types_response_before_update;
  TF_ASSERT_OK(UpdateGetResponse(store_.get(),
                                 get_artifact_types_response_before_update,
                                 get_execution_types_response_before_update,
                                 get_context_types_response_before_update));

  // The num_operations passed into the fill_types_update is bigger than the
  // existed number of types inside db(the inserted types by fill_types). So,
  // the fill_types_update will make up the shortage types and update them
  // together.
  GenerateFillTypesWorkload(true, kNumOperations + 100, fill_types_update_);

  TF_ASSERT_OK(SetUpAndExecuteWorkload(store_.get(), fill_types_update_));

  // Gets the get_response_after_update for later comparison.
  GetArtifactTypesResponse get_artifact_types_response_after_update;
  GetExecutionTypesResponse get_execution_types_response_after_update;
  GetContextTypesResponse get_context_types_response_after_update;
  TF_ASSERT_OK(UpdateGetResponse(store_.get(),
                                 get_artifact_types_response_after_update,
                                 get_execution_types_response_after_update,
                                 get_context_types_response_after_update));

  // If the updates are working properly, for the types inserted before in
  // fill_types workload, the type id should remain the same even after the
  // updates. On the other hand, the properties size for these types should
  // be greater than before since some new fields have been added
  // in the update process.
  EXPECT_TRUE(
      CheckArtifactTypesUpdateStatus(get_artifact_types_response_before_update,
                                     get_artifact_types_response_after_update,
                                     fill_types_insert_[0]->num_operations()));
  EXPECT_TRUE(CheckExecutionTypesUpdateStatus(
      get_execution_types_response_before_update,
      get_execution_types_response_after_update,
      fill_types_insert_[1]->num_operations()));
  EXPECT_TRUE(
      CheckContextTypesUpdateStatus(get_context_types_response_before_update,
                                    get_context_types_response_after_update,
                                    fill_types_insert_[2]->num_operations()));
  // Since the update process makes up new types in db, the current number
  // of types in db should be the same as the num_operations of the
  // fill_type_update.
  EXPECT_EQ(get_artifact_types_response_after_update.artifact_types_size(),
            fill_types_update_[0]->num_operations());
  EXPECT_EQ(get_execution_types_response_after_update.execution_types_size(),
            fill_types_update_[1]->num_operations());
  EXPECT_EQ(get_context_types_response_after_update.context_types_size(),
            fill_types_update_[2]->num_operations());
}

}  // namespace
}  // namespace ml_metadata
<commit_msg>Refactors fill_types_workload_test.cc on code structure.<commit_after>/* Copyright 2020 Google LLC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include "ml_metadata/tools/mlmd_bench/fill_types_workload.h"

#include <vector>

#include <gtest/gtest.h>
#include "absl/memory/memory.h"
#include "ml_metadata/metadata_store/metadata_store.h"
#include "ml_metadata/metadata_store/metadata_store_factory.h"
#include "ml_metadata/metadata_store/test_util.h"
#include "ml_metadata/metadata_store/types.h"
#include "ml_metadata/proto/metadata_store.pb.h"
#include "ml_metadata/proto/metadata_store_service.pb.h"
#include "ml_metadata/tools/mlmd_bench/proto/mlmd_bench.pb.h"
#include "tensorflow/core/lib/core/status_test_util.h"

namespace ml_metadata {
namespace {

constexpr int64 kNumOperations = 100;
constexpr int64 kNumOperationsForMakeUpCases = 200;

// Generates FillTypes Workload where `update` indicates inserting or updating.
void GenerateFillTypesWorkload(
    const bool update, const int64 num_operations,
    std::vector<std::unique_ptr<FillTypes>>& fill_types) {
  FillTypesConfig fill_artifact_types_config =
      testing::ParseTextProtoOrDie<FillTypesConfig>(
          R"(
            specification: ARTIFACT_TYPE
            num_properties { minimum: 1 maximum: 10 }
          )");
  fill_artifact_types_config.set_update(update);
  std::unique_ptr<FillTypes> fill_artifact_types = absl::make_unique<FillTypes>(
      FillTypes(fill_artifact_types_config, num_operations));

  FillTypesConfig fill_execution_types_config =
      testing::ParseTextProtoOrDie<FillTypesConfig>(
          R"(
            specification: EXECUTION_TYPE
            num_properties { minimum: 1 maximum: 10 }
          )");
  fill_execution_types_config.set_update(update);
  std::unique_ptr<FillTypes> fill_execution_types =
      absl::make_unique<FillTypes>(
          FillTypes(fill_execution_types_config, num_operations));

  FillTypesConfig fill_context_types_config =
      testing::ParseTextProtoOrDie<FillTypesConfig>(
          R"(
            specification: CONTEXT_TYPE
            num_properties { minimum: 1 maximum: 10 }
          )");
  fill_context_types_config.set_update(update);
  std::unique_ptr<FillTypes> fill_context_types = absl::make_unique<FillTypes>(
      FillTypes(fill_context_types_config, num_operations));

  fill_types.push_back(std::move(fill_artifact_types));
  fill_types.push_back(std::move(fill_execution_types));
  fill_types.push_back(std::move(fill_context_types));
}

// Checks types update status. If the updates are working properly, the type id
// should remain the same even after the updates. On the other hand, the
// properties size for each type should be greater than before since some new
// fields have been added in the update process.
template <typename Type>
bool CheckTypesUpdateStatus(const Type& type_before, const Type& type_after) {
  if (type_before.id() != type_after.id()) {
    return false;
  }
  if (type_before.properties().size() >= type_after.properties().size()) {
    return false;
  }
  return true;
}

// Updates the get types response of the current store instance.
tensorflow::Status UpdateGetResponse(
    MetadataStore* store, GetArtifactTypesResponse& get_artifact_types_response,
    GetExecutionTypesResponse& get_execution_types_response,
    GetContextTypesResponse& get_context_types_response) {
  TF_RETURN_IF_ERROR(
      store->GetArtifactTypes(/*request=*/{}, &get_artifact_types_response));
  TF_RETURN_IF_ERROR(
      store->GetExecutionTypes(/*request=*/{}, &get_execution_types_response));
  TF_RETURN_IF_ERROR(
      store->GetContextTypes(/*request=*/{}, &get_context_types_response));
  return tensorflow::Status::OK();
}

// Executes the given FillTypes workloads.
tensorflow::Status SetUpAndExecuteWorkload(
    MetadataStore* store,
    std::vector<std::unique_ptr<FillTypes>>& fill_types_workloads) {
  for (auto& workload : fill_types_workloads) {
    TF_RETURN_IF_ERROR(workload->SetUp(store));
    for (int64 i = 0; i < workload->num_operations(); ++i) {
      OpStats op_stats;
      TF_RETURN_IF_ERROR(workload->RunOp(i, store, op_stats));
    }
  }
  return tensorflow::Status::OK();
}

// Test fixture that uses the same data configuration for multiple following
// FillTypes insert tests.
class FillTypesInsertTest : public ::testing::Test {
 protected:
  void SetUp() override {
    ConnectionConfig mlmd_config;
    // Uses a fake in-memory SQLite database for testing.
    mlmd_config.mutable_fake_database();
    TF_ASSERT_OK(CreateMetadataStore(mlmd_config, &store_));
    // `false` indicates the insert cases for FillTypes.
    GenerateFillTypesWorkload(false, kNumOperations, fill_types_insert_);
  }

  std::vector<std::unique_ptr<FillTypes>> fill_types_insert_;
  std::unique_ptr<MetadataStore> store_;
};

// Tests the SetUpImpl() for FillTypes insert cases.
// Checks the SetUpImpl() indeed prepares a list of work items whose length is
// the same as the specified number of operations.
TEST_F(FillTypesInsertTest, SetUpImplTest) {
  for (auto& workload : fill_types_insert_) {
    TF_ASSERT_OK(workload->SetUp(store_.get()));
    ASSERT_EQ(kNumOperations, workload->num_operations());
  }
}

// Tests the RunOpImpl() for insert types.
// Checks indeed all the work items have been executed and all the types have
// been inserted into the db.
TEST_F(FillTypesInsertTest, InsertTest) {
  TF_ASSERT_OK(SetUpAndExecuteWorkload(store_.get(), fill_types_insert_));

  GetArtifactTypesResponse get_artifact_types_response;
  GetExecutionTypesResponse get_execution_types_response;
  GetContextTypesResponse get_context_types_response;
  TF_ASSERT_OK(UpdateGetResponse(store_.get(), get_artifact_types_response,
                                 get_execution_types_response,
                                 get_context_types_response));

  EXPECT_EQ(get_artifact_types_response.artifact_types_size(), kNumOperations);
  EXPECT_EQ(get_execution_types_response.execution_types_size(),
            kNumOperations);
  EXPECT_EQ(get_context_types_response.context_types_size(), kNumOperations);
}

// Test fixture that uses the same data configuration for multiple following
// FillTypes update tests.
class FillTypesUpdateTest : public ::testing::Test {
 protected:
  void SetUp() override {
    ConnectionConfig mlmd_config;
    // Uses a fake in-memory SQLite database for testing.
    mlmd_config.mutable_fake_database();
    TF_ASSERT_OK(CreateMetadataStore(mlmd_config, &store_));
    // Inserts some types into the db for later update.
    GenerateFillTypesWorkload(false, kNumOperations, fill_types_insert_);
    TF_ASSERT_OK(SetUpAndExecuteWorkload(store_.get(), fill_types_insert_));
  }

  std::vector<std::unique_ptr<FillTypes>> fill_types_insert_;
  std::vector<std::unique_ptr<FillTypes>> fill_types_update_;
  std::unique_ptr<MetadataStore> store_;
};

// Tests the SetUpImpl() for FillTypes update cases.
// Checks the SetUpImpl() indeed prepares a list of work items whose length is
// the same as the specified number of operations.
TEST_F(FillTypesUpdateTest, SetUpImplTest) {
  // `true` indicates the update cases for FillTypes.
  GenerateFillTypesWorkload(true, kNumOperations, fill_types_update_);
  for (auto& workload : fill_types_update_) {
    TF_ASSERT_OK(workload->SetUp(store_.get()));
    ASSERT_EQ(kNumOperations, workload->num_operations());
  }
}

// Tests the normal update cases(the number of types inside db are enough for
// updating).
TEST_F(FillTypesUpdateTest, NormalUpdateTest) {
  // Gets the get_response_before_update for later comparison.
  GetArtifactTypesResponse get_artifact_types_response_before_update;
  GetExecutionTypesResponse get_execution_types_response_before_update;
  GetContextTypesResponse get_context_types_response_before_update;
  TF_ASSERT_OK(UpdateGetResponse(store_.get(),
                                 get_artifact_types_response_before_update,
                                 get_execution_types_response_before_update,
                                 get_context_types_response_before_update));

  // The update number_opeartions is the same the insert which means the number
  // of types inside db are enough for update.
  GenerateFillTypesWorkload(true, kNumOperations, fill_types_update_);

  // Prepares and executes the update workloads.
  TF_ASSERT_OK(SetUpAndExecuteWorkload(store_.get(), fill_types_update_));

  // Gets the get_response_after_update for later comparison.
  GetArtifactTypesResponse get_artifact_types_response_after_update;
  GetExecutionTypesResponse get_execution_types_response_after_update;
  GetContextTypesResponse get_context_types_response_after_update;
  TF_ASSERT_OK(UpdateGetResponse(store_.get(),
                                 get_artifact_types_response_after_update,
                                 get_execution_types_response_after_update,
                                 get_context_types_response_after_update));

  // If the updates are working properly, the type id should remain the
  // same even after the updates. On the other hand, the properties size for
  // each type should be greater than before since some new fields have been
  // added in the update process.
  for (int64 i = 0; i < kNumOperations; ++i) {
    EXPECT_TRUE(CheckTypesUpdateStatus<ArtifactType>(
        get_artifact_types_response_before_update.artifact_types()[i],
        get_artifact_types_response_after_update.artifact_types()[i]));
    EXPECT_TRUE(CheckTypesUpdateStatus<ExecutionType>(
        get_execution_types_response_before_update.execution_types()[i],
        get_execution_types_response_after_update.execution_types()[i]));
    EXPECT_TRUE(CheckTypesUpdateStatus<ContextType>(
        get_context_types_response_before_update.context_types()[i],
        get_context_types_response_after_update.context_types()[i]));
  }

  // There should no more types inside db after update since the updates only
  // update the existed types inserted before by fill_types_insert_.
  EXPECT_EQ(get_artifact_types_response_after_update.artifact_types_size(),
            kNumOperations);
  EXPECT_EQ(get_execution_types_response_after_update.execution_types_size(),
            kNumOperations);
  EXPECT_EQ(get_context_types_response_after_update.context_types_size(),
            kNumOperations);
}

// Tests the update cases(the number of types inside db are not enough for
// updating) that needs making up and inserting new types in db for update.
TEST_F(FillTypesUpdateTest, MakeUpUpdateTest) {
  // Gets the get_response_before_update for later comparison.
  GetArtifactTypesResponse get_artifact_types_response_before_update;
  GetExecutionTypesResponse get_execution_types_response_before_update;
  GetContextTypesResponse get_context_types_response_before_update;
  TF_ASSERT_OK(UpdateGetResponse(store_.get(),
                                 get_artifact_types_response_before_update,
                                 get_execution_types_response_before_update,
                                 get_context_types_response_before_update));

  // The num_operations passed into the fill_types_update is bigger than the
  // existed number of types inside db(the inserted types by fill_types). So,
  // the fill_types_update will make up the shortage types and update them
  // together.
  GenerateFillTypesWorkload(true, kNumOperationsForMakeUpCases,
                            fill_types_update_);

  TF_ASSERT_OK(SetUpAndExecuteWorkload(store_.get(), fill_types_update_));

  // Gets the get_response_after_update for later comparison.
  GetArtifactTypesResponse get_artifact_types_response_after_update;
  GetExecutionTypesResponse get_execution_types_response_after_update;
  GetContextTypesResponse get_context_types_response_after_update;
  TF_ASSERT_OK(UpdateGetResponse(store_.get(),
                                 get_artifact_types_response_after_update,
                                 get_execution_types_response_after_update,
                                 get_context_types_response_after_update));

  // If the updates are working properly, for the types inserted before in
  // fill_types workload, the type id should remain the same even after the
  // updates. On the other hand, the properties size for these types should
  // be greater than before since some new fields have been added
  // in the update process.
  for (int64 i = 0; i < kNumOperations; ++i) {
    EXPECT_TRUE(CheckTypesUpdateStatus<ArtifactType>(
        get_artifact_types_response_before_update.artifact_types()[i],
        get_artifact_types_response_after_update.artifact_types()[i]));
    EXPECT_TRUE(CheckTypesUpdateStatus<ExecutionType>(
        get_execution_types_response_before_update.execution_types()[i],
        get_execution_types_response_after_update.execution_types()[i]));
    EXPECT_TRUE(CheckTypesUpdateStatus<ContextType>(
        get_context_types_response_before_update.context_types()[i],
        get_context_types_response_after_update.context_types()[i]));
  }

  // Since the update process makes up new types in db, the current number
  // of types in db should be the same as the num_operations of the
  // fill_type_update.
  EXPECT_EQ(get_artifact_types_response_after_update.artifact_types_size(),
            kNumOperationsForMakeUpCases);
  EXPECT_EQ(get_execution_types_response_after_update.execution_types_size(),
            kNumOperationsForMakeUpCases);
  EXPECT_EQ(get_context_types_response_after_update.context_types_size(),
            kNumOperationsForMakeUpCases);
}

}  // namespace
}  // namespace ml_metadata
<|endoftext|>


<commit_before>#include "application.hpp"
#include <stdexcept>
#include "sprite.hpp"
#include "horizontal_packing.hpp"
#include "image_widget.hpp"
#include "label.hpp"

using namespace std;
using namespace Vulkan;

namespace Granite
{
Application::Application(unsigned width, unsigned height)
{
	EventManager::get_global();
	Filesystem::get();

	platform = create_default_application_platform(width, height);

	if (!wsi.init(platform.get(), width, height))
		throw runtime_error("Failed to initialize WSI.");
}

SceneViewerApplication::SceneViewerApplication(const std::string &path, unsigned width, unsigned height)
	: Application(width, height),
      horiz("assets://shaders/quad.vert", "assets://shaders/blur.frag"),
      vert("assets://shaders/quad.vert", "assets://shaders/blur.frag")
{
	horiz.set_defines({{ "METHOD", 2 }});
	vert.set_defines({{ "METHOD", 5 }});

	scene_loader.load_scene(path);
	animation_system = scene_loader.consume_animation_system();

	auto *environment = scene_loader.get_scene().get_environment();
	if (environment)
		context.set_fog_parameters(environment->fog);

	cam.look_at(vec3(0.0f, 0.0f, 8.0f), vec3(0.0f));
	context.set_camera(cam);

	auto &ui = UI::UIManager::get();
	window = ui.add_child<UI::Window>();
	auto *w0 = window->add_child<UI::Widget>();
	auto *w1 = window->add_child<UI::Widget>();
	auto *w2 = window->add_child<UI::Widget>();
	auto *image = window->add_child<UI::Image>("assets://gltf-sandbox/textures/maister.png");
	image->set_minimum_geometry(image->get_target_geometry() * vec2(1.0f / 16.0f));
	image->set_keep_aspect_ratio(true);
	auto *w3 = window->add_child<UI::Widget>();
	w0->set_background_color(vec4(1.0f, 0.0f, 0.0f, 1.0f));
	w1->set_background_color(vec4(0.0f, 1.0f, 0.0f, 1.0f));
	w2->set_background_color(vec4(1.0f, 1.0f, 0.0f, 1.0f));
	w3->set_background_color(vec4(0.0f, 1.0f, 1.0f, 1.0f));
	w0->set_target_geometry(vec2(400.0f, 60.0f));
	w1->set_target_geometry(vec2(400.0f, 60.0f));
	w2->set_target_geometry(vec2(400.0f, 60.0f));
	w3->set_target_geometry(vec2(40.0f, 60.0f));
	w0->set_minimum_geometry(vec2(40.0f, 10.0f));
	w1->set_minimum_geometry(vec2(40.0f, 10.0f));
	w2->set_minimum_geometry(vec2(40.0f, 10.0f));
	w3->set_minimum_geometry(vec2(40.0f, 10.0f));
	window->set_target_geometry(ivec2(10));

	auto *label = window->add_child<UI::Label>("Hai :D");
	label->set_margin(20.0f);
	label->set_color(vec4(0.0f, 0.0f, 0.0f, 1.0f));
	label->set_font_alignment(Font::Alignment::Center);

	auto *w4 = window->add_child<UI::HorizontalPacking>();
	w4->set_margin(10.0f);
	auto *tmp = w4->add_child<UI::Widget>();
	tmp->set_background_color(vec4(0.0f, 0.0f, 0.0f, 1.0f));
	tmp->set_minimum_geometry(vec2(50.0f));
	//tmp->set_target_geometry(vec2(50.0f));
	tmp = w4->add_child<UI::Widget>();
	tmp->set_background_color(vec4(0.0f, 0.0f, 0.0f, 1.0f));
	tmp->set_minimum_geometry(vec2(50.0f));
	//tmp->set_target_geometry(vec2(50.0f));

	w2->set_size_is_flexible(true);

	EventManager::get_global().register_latch_handler(SwapchainParameterEvent::type_id,
	                                                  &SceneViewerApplication::on_swapchain_changed,
	                                                  &SceneViewerApplication::on_swapchain_destroyed,
	                                                  this);
}

bool SceneViewerApplication::get_clear_depth_stencil(VkClearDepthStencilValue *value)
{
	if (value)
	{
		value->depth = 1.0f;
		value->stencil = 0;
	}
	return true;
}

bool SceneViewerApplication::get_clear_color(unsigned index, VkClearColorValue *value)
{
	if (value)
	{
		value->float32[0] = context.get_fog_parameters().color.r;
		value->float32[1] = context.get_fog_parameters().color.g;
		value->float32[2] = context.get_fog_parameters().color.b;
		value->float32[3] = 0.0f;
	}
	return true;
}

void SceneViewerApplication::on_swapchain_changed(const Event &e)
{
	auto &swap = e.as<SwapchainParameterEvent>();
	graph.reset();

	ResourceDimensions dim;
	dim.width = swap.get_width();
	dim.height = swap.get_height();
	dim.format = swap.get_format();
	graph.set_backbuffer_dimensions(dim);
	graph.set_backbuffer_source("backbuffer");

	AttachmentInfo backbuffer;
	AttachmentInfo backbuffer_depth = backbuffer;
	backbuffer_depth.format = swap.get_device().get_default_depth_stencil_format();

	AttachmentInfo info;
	info.format = VK_FORMAT_R16G16B16A16_SFLOAT;
	info.size_x = 1.0f;
	info.size_y = 1.0f;
	info.size_class = SizeClass::SwapchainRelative;

	auto &pass = graph.add_pass("main");
	pass.add_color_output("main", info);
	pass.set_depth_stencil_output("backbuffer_depth", backbuffer_depth);
	pass.set_implementation(this);

	auto &horiz = graph.add_pass("blur_horiz");
	horiz.add_color_output("horiz", info);
	horiz.add_texture_input("main");
	horiz.set_implementation(&this->horiz);

	auto &vert = graph.add_pass("blur_vert");
	vert.add_color_output("backbuffer", backbuffer);
	vert.add_texture_input("horiz");
	vert.set_implementation(&this->vert);

	graph.bake();
	graph.log();
}

void SceneViewerApplication::build_render_pass(RenderPass &, Vulkan::CommandBuffer &cmd)
{
	renderer.flush(cmd, context);
	UI::UIManager::get().render(cmd);
}

void SceneViewerApplication::on_swapchain_destroyed(const Event &)
{
}

void SceneViewerApplication::render_frame(double, double elapsed_time)
{
	auto &wsi = get_wsi();
	auto &scene = scene_loader.get_scene();
	auto &device = wsi.get_device();

	animation_system->animate(elapsed_time);
	context.set_camera(cam);
	visible.clear();

	window->set_background_color(vec4(1.0f));
	window->set_margin(5);
	window->set_floating_position(ivec2(40));
	window->set_title("My Window");
	//window->set_target_geometry(window->get_target_geometry() + vec2(1.0f));

	scene.update_cached_transforms();
	scene.refresh_per_frame(context);
	scene.gather_visible_opaque_renderables(context.get_visibility_frustum(), visible);
	scene.gather_background_renderables(visible);

	auto cmd = device.request_command_buffer();
	renderer.begin();
	renderer.push_renderables(context, visible);
	graph.setup_attachments(device, &device.get_swapchain_view());
	graph.enqueue_initial_barriers(*cmd);
	graph.enqueue_render_passes(*cmd);
	device.submit(cmd);
}

int Application::run()
{
	auto &wsi = get_wsi();
	while (get_platform().alive(wsi))
	{
		Filesystem::get().poll_notifications();
		wsi.begin_frame();
		render_frame(wsi.get_platform().get_frame_timer().get_frame_time(),
					 wsi.get_platform().get_frame_timer().get_elapsed());
		wsi.end_frame();
	}
	return 0;
}

}
<commit_msg>Test 3x3 gauss.<commit_after>#include "application.hpp"
#include <stdexcept>
#include "sprite.hpp"
#include "horizontal_packing.hpp"
#include "image_widget.hpp"
#include "label.hpp"

using namespace std;
using namespace Vulkan;

namespace Granite
{
Application::Application(unsigned width, unsigned height)
{
	EventManager::get_global();
	Filesystem::get();

	platform = create_default_application_platform(width, height);

	if (!wsi.init(platform.get(), width, height))
		throw runtime_error("Failed to initialize WSI.");
}

SceneViewerApplication::SceneViewerApplication(const std::string &path, unsigned width, unsigned height)
	: Application(width, height),
      horiz("assets://shaders/quad.vert", "assets://shaders/blur.frag"),
      vert("assets://shaders/quad.vert", "assets://shaders/blur.frag")
{
	horiz.set_defines({{ "METHOD", 0 }});
	vert.set_defines({{ "METHOD", 3 }});

	scene_loader.load_scene(path);
	animation_system = scene_loader.consume_animation_system();

	auto *environment = scene_loader.get_scene().get_environment();
	if (environment)
		context.set_fog_parameters(environment->fog);

	cam.look_at(vec3(0.0f, 0.0f, 8.0f), vec3(0.0f));
	context.set_camera(cam);

	auto &ui = UI::UIManager::get();
	window = ui.add_child<UI::Window>();
	auto *w0 = window->add_child<UI::Widget>();
	auto *w1 = window->add_child<UI::Widget>();
	auto *w2 = window->add_child<UI::Widget>();
	auto *image = window->add_child<UI::Image>("assets://gltf-sandbox/textures/maister.png");
	image->set_minimum_geometry(image->get_target_geometry() * vec2(1.0f / 16.0f));
	image->set_keep_aspect_ratio(true);
	auto *w3 = window->add_child<UI::Widget>();
	w0->set_background_color(vec4(1.0f, 0.0f, 0.0f, 1.0f));
	w1->set_background_color(vec4(0.0f, 1.0f, 0.0f, 1.0f));
	w2->set_background_color(vec4(1.0f, 1.0f, 0.0f, 1.0f));
	w3->set_background_color(vec4(0.0f, 1.0f, 1.0f, 1.0f));
	w0->set_target_geometry(vec2(400.0f, 60.0f));
	w1->set_target_geometry(vec2(400.0f, 60.0f));
	w2->set_target_geometry(vec2(400.0f, 60.0f));
	w3->set_target_geometry(vec2(40.0f, 60.0f));
	w0->set_minimum_geometry(vec2(40.0f, 10.0f));
	w1->set_minimum_geometry(vec2(40.0f, 10.0f));
	w2->set_minimum_geometry(vec2(40.0f, 10.0f));
	w3->set_minimum_geometry(vec2(40.0f, 10.0f));
	window->set_target_geometry(ivec2(10));

	auto *label = window->add_child<UI::Label>("Hai :D");
	label->set_margin(20.0f);
	label->set_color(vec4(0.0f, 0.0f, 0.0f, 1.0f));
	label->set_font_alignment(Font::Alignment::Center);

	auto *w4 = window->add_child<UI::HorizontalPacking>();
	w4->set_margin(10.0f);
	auto *tmp = w4->add_child<UI::Widget>();
	tmp->set_background_color(vec4(0.0f, 0.0f, 0.0f, 1.0f));
	tmp->set_minimum_geometry(vec2(50.0f));
	//tmp->set_target_geometry(vec2(50.0f));
	tmp = w4->add_child<UI::Widget>();
	tmp->set_background_color(vec4(0.0f, 0.0f, 0.0f, 1.0f));
	tmp->set_minimum_geometry(vec2(50.0f));
	//tmp->set_target_geometry(vec2(50.0f));

	w2->set_size_is_flexible(true);

	EventManager::get_global().register_latch_handler(SwapchainParameterEvent::type_id,
	                                                  &SceneViewerApplication::on_swapchain_changed,
	                                                  &SceneViewerApplication::on_swapchain_destroyed,
	                                                  this);
}

bool SceneViewerApplication::get_clear_depth_stencil(VkClearDepthStencilValue *value)
{
	if (value)
	{
		value->depth = 1.0f;
		value->stencil = 0;
	}
	return true;
}

bool SceneViewerApplication::get_clear_color(unsigned index, VkClearColorValue *value)
{
	if (value)
	{
		value->float32[0] = context.get_fog_parameters().color.r;
		value->float32[1] = context.get_fog_parameters().color.g;
		value->float32[2] = context.get_fog_parameters().color.b;
		value->float32[3] = 0.0f;
	}
	return true;
}

void SceneViewerApplication::on_swapchain_changed(const Event &e)
{
	auto &swap = e.as<SwapchainParameterEvent>();
	graph.reset();

	ResourceDimensions dim;
	dim.width = swap.get_width();
	dim.height = swap.get_height();
	dim.format = swap.get_format();
	graph.set_backbuffer_dimensions(dim);
	graph.set_backbuffer_source("backbuffer");

	AttachmentInfo backbuffer;
	AttachmentInfo backbuffer_depth = backbuffer;
	backbuffer_depth.format = swap.get_device().get_default_depth_stencil_format();

	AttachmentInfo info;
	info.format = VK_FORMAT_R16G16B16A16_SFLOAT;
	info.size_x = 1.0f;
	info.size_y = 1.0f;
	info.size_class = SizeClass::SwapchainRelative;

	auto &pass = graph.add_pass("main");
	pass.add_color_output("main", info);
	pass.set_depth_stencil_output("backbuffer_depth", backbuffer_depth);
	pass.set_implementation(this);

	auto &horiz = graph.add_pass("blur_horiz");
	horiz.add_color_output("horiz", info);
	horiz.add_texture_input("main");
	horiz.set_implementation(&this->horiz);

	auto &vert = graph.add_pass("blur_vert");
	vert.add_color_output("backbuffer", backbuffer);
	vert.add_texture_input("horiz");
	vert.set_implementation(&this->vert);

	graph.bake();
	graph.log();
}

void SceneViewerApplication::build_render_pass(RenderPass &, Vulkan::CommandBuffer &cmd)
{
	renderer.flush(cmd, context);
	UI::UIManager::get().render(cmd);
}

void SceneViewerApplication::on_swapchain_destroyed(const Event &)
{
}

void SceneViewerApplication::render_frame(double, double elapsed_time)
{
	auto &wsi = get_wsi();
	auto &scene = scene_loader.get_scene();
	auto &device = wsi.get_device();

	animation_system->animate(elapsed_time);
	context.set_camera(cam);
	visible.clear();

	window->set_background_color(vec4(1.0f));
	window->set_margin(5);
	window->set_floating_position(ivec2(40));
	window->set_title("My Window");
	//window->set_target_geometry(window->get_target_geometry() + vec2(1.0f));

	scene.update_cached_transforms();
	scene.refresh_per_frame(context);
	scene.gather_visible_opaque_renderables(context.get_visibility_frustum(), visible);
	scene.gather_background_renderables(visible);

	auto cmd = device.request_command_buffer();
	renderer.begin();
	renderer.push_renderables(context, visible);
	graph.setup_attachments(device, &device.get_swapchain_view());
	graph.enqueue_initial_barriers(*cmd);
	graph.enqueue_render_passes(*cmd);
	device.submit(cmd);
}

int Application::run()
{
	auto &wsi = get_wsi();
	while (get_platform().alive(wsi))
	{
		Filesystem::get().poll_notifications();
		wsi.begin_frame();
		render_frame(wsi.get_platform().get_frame_timer().get_frame_time(),
					 wsi.get_platform().get_frame_timer().get_elapsed());
		wsi.end_frame();
	}
	return 0;
}

}
<|endoftext|>


<commit_before>/*
 *  despot1_main.cpp
 *
 *  Created by Tobias Wood on 23/01/2012.
 *  Copyright 2012 Tobias Wood. All rights reserved.
 *
 */

#include <time.h>
#include <getopt.h>
#include <iostream>
#include <atomic>
#include <Eigen/Dense>
#include <unsupported/Eigen/NonLinearOptimization>

#include "NiftiImage.h"
#include "DESPOT.h"
#include "DESPOT_Functors.h"
#include "RegionContraction.h"

#define USE_PROCPAR
#ifdef USE_PROCPAR
	#include "procpar.h"
	using namespace Recon;
#endif

using namespace std;
using namespace Eigen;

//******************************************************************************
// Arguments / Usage
//******************************************************************************
const string credit {
"despot2 - Written by tobias.wood@kcl.ac.uk, based on work by Sean Deoni. \n\
Acknowledgements greatfully received, grant discussions welcome."
};

const string usage {
"Usage is: despot2 [options] output_prefix T1_map ssfp_files\n\
\
Options:\n\
	--help, -h        : Print this message.\n\
	--mask, -m file   : Mask input with specified file.\n\
	--B0 file         : B0 Map file.\n\
	--B1 file         : B1 Map file.\n\
	--M0 file         : Proton density file.\n\
	--verbose, -v     : Print slice processing times.\n\
	--start_slice N   : Start processing from slice N.\n\
	--end_slice   N   : Finish processing at slice N.\n\
	--tesla, -t 3     : Enables DESPOT-FM with boundaries suitable for 3T\n\
	            7     : Boundaries suitable for 7T (default)\n\
	            u     : User specified boundaries from stdin.\n"

};

// tesla == 0 means NO DESPOT-FM
static int tesla = 0, fitB0 = false, verbose = false, start_slice = -1, end_slice = -1;
static string outPrefix;
static struct option long_options[] =
{
	{"B0", required_argument, 0, '0'},
	{"B1", required_argument, 0, '1'},
	{"M0", required_argument, 0, 'M'},
	{"help", no_argument, 0, 'h'},
	{"mask", required_argument, 0, 'm'},
	{"tesla", optional_argument, 0, 'f'},
	{"verbose", no_argument, 0, 'v'},
	{"start_slice", required_argument, 0, 'S'},
	{"end_slice", required_argument, 0, 'E'},
	{0, 0, 0, 0}
};

//******************************************************************************
// Main
//******************************************************************************
int main(int argc, char **argv)
{
	//**************************************************************************
	// Argument Processing
	//**************************************************************************
	cout << credit << endl;
	if (argc < 4) {
		cout << usage << endl;
		return EXIT_FAILURE;
	}
	Eigen::initParallel();
	NiftiImage inFile, savedHeader;
	double *maskData = NULL, *B0Data = NULL, *B1Data = NULL, *T1Data = NULL,
	       *M0Data = NULL;
	string procPath;

	int indexptr = 0, c;
	while ((c = getopt_long(argc, argv, "hm:vz", long_options, &indexptr)) != -1) {
		switch (c) {
			case 'm':
				cout << "Reading mask file " << optarg << endl;
				inFile.open(optarg, 'r');
				maskData = inFile.readVolume<double>(0);
				inFile.close();
				break;
			case '0':
				cout << "Reading B0 file " << optarg << endl;
				inFile.open(optarg, 'r');
				B0Data = inFile.readVolume<double>(0);
				inFile.close();
				break;
			case '1':
				cout << "Reading B1 file " << optarg;
				inFile.open(optarg, 'r');
				B1Data = inFile.readVolume<double>(0);
				inFile.close();
				break;
			case 'M':
				cout << "Reading M0 file " << optarg;
				inFile.open(optarg, 'r');
				M0Data = inFile.readVolume<double>(0);
				inFile.close();
				break;
			case 't':
				switch (*optarg) {
					case '3': tesla = 3; break;
					case '7': tesla = 7; break;
					case 'u': tesla = -1; break;
					default:
						cout << "Unknown boundaries type " << optarg << endl;
						abort();
						break;
				}
				cout << "Using " << tesla << "T boundaries." << endl;
				break;
			case 'v':
				verbose = true;
				break;
			case 'S':
				start_slice = atoi(optarg);
				break;
			case 'E':
				end_slice = atoi(optarg);
				break;
			case 0:
				// Just a flag
				break;
			case '?': // getopt will print an error message
			case 'h':
				cout << usage << endl;
				return EXIT_FAILURE;
		}
	}
	if ((tesla != 0) && !B0Data)
		fitB0 = true;
	cout << "Output prefix will be: " << argv[optind] << endl;
	outPrefix = argv[optind++];
	cout << "Reading T1 Map from: " << argv[optind] << endl;
	inFile.open(argv[optind++], 'r');
	T1Data = inFile.readVolume<double>(0);
	inFile.close();
	savedHeader = inFile;
	//**************************************************************************
	// Gather SSFP Data
	//**************************************************************************
	int nFlip, nPhases;
	double ssfpTR;
	nPhases = argc - optind;
	vector<double> ssfpPhases(nPhases);
	VectorXd ssfpAngles;
	int voxelsPerSlice, voxelsPerVolume;
	double **ssfpData = (double **)malloc(nPhases * sizeof(double *));
	for (size_t p = 0; p < nPhases; p++)
	{
		cout << "Reading SSFP header from " << argv[optind] << endl;
		inFile.open(argv[optind], 'r');
		if (p == 0)
		{	// Read nFlip, TR and flip angles from first file
			nFlip = inFile.dim(4);
			voxelsPerSlice = inFile.voxelsPerSlice();
			voxelsPerVolume = inFile.voxelsPerVolume();
			ssfpAngles.resize(nFlip, 1);

			#ifdef USE_PROCPAR
			ParameterList pars;
			if (ReadProcpar(inFile.basename() + ".procpar", pars)) {
				ssfpTR = RealValue(pars, "tr");
				for (int i = 0; i < nFlip; i++)
					ssfpAngles[i] = RealValue(pars, "flip1", i);
			} else
			#endif
			{
				cout << "Enter SSFP TR (s): " << flush;
				cin >> ssfpTR;
				cout << "Enter " << nFlip << " flip angles (degrees): " << flush;
				for (int i = 0; i < ssfpAngles.size(); i++)
					cin >> ssfpAngles[i];
			}
		}
		#ifdef USE_PROCPAR
		ParameterList pars;
		if (ReadProcpar(inFile.basename() + ".procpar", pars)) {
			ssfpPhases[p] = RealValue(pars, "rfphase") * M_PI / 180.;
		} else
		#endif
		{
			cout << "Enter phase-cycling (degrees): " << flush;
			cin >> ssfpPhases[p]; ssfpPhases[p] *= M_PI / 180.;
		}
		cout << "Reading SSFP data..." << endl;
		ssfpData[p] = inFile.readAllVolumes<double>();
		// Don't close the first header because we've saved it to write the
		// results, and FSLIO gets fussy about cloning closed headers
		inFile.close();
		optind++;
	}
	ssfpAngles *= M_PI / 180.;

	if (optind != argc) {
		cerr << "Unprocessed arguments supplied.\n" << usage;
		exit(EXIT_FAILURE);
	}

	// Set up boundaries for DESPOT-FM if needed
	DESPOT2FM::ParamType loBounds, hiBounds;
	if (tesla != 0) {
		if (tesla > 0) {
			loBounds = DESPOT2FM::loBounds(tesla);
			hiBounds = DESPOT2FM::hiBounds(tesla);
		} else if (tesla < 0) {
			cout << "Enter " << DESPOT2FM::nP << " parameter pairs (low then high): " << flush;
			for (int i = 0; i < DESPOT2FM::nP; i++) cin >> loBounds[i] >> hiBounds[i];
		}
		// If fitting, give a suitable range and allocate results memory
		if (fitB0) {
			loBounds[DESPOT2FM::nP - 1] = -0.5 / ssfpTR;
			hiBounds[DESPOT2FM::nP - 1] =  0.5 / ssfpTR;
			B0Data = new double[voxelsPerVolume];
		} else { // Otherwise fix and let functors pick up the specified value
			loBounds[DESPOT2FM::nP - 1] = 0.;
			hiBounds[DESPOT2FM::nP - 1] = 0.;
		}
	}

	if (verbose) {
		cout << "SSFP Angles (deg): " << ssfpAngles.transpose() * 180 / M_PI << endl;
		if (tesla != 0)
			cout << "Low bounds: " << loBounds.transpose() << endl
				 << "Hi bounds:  " << hiBounds.transpose() << endl;
	}
	//**************************************************************************
	// Set up results data
	//**************************************************************************
	double *residualData = new double[voxelsPerVolume];
	double *PDData = new double[voxelsPerVolume];
	double *T2Data = new double[voxelsPerVolume];
	//**************************************************************************
	// Do the fitting
	//**************************************************************************
    time_t procStart = time(NULL);
	if ((start_slice < 0) || (start_slice >= inFile.dim(3)))
		start_slice = 0;
	if ((end_slice < 0) || (end_slice > inFile.dim(3)))
		end_slice = inFile.dim(3);
	for (int slice = start_slice; slice < end_slice; slice++) {
		// Read in data
		if (verbose)
			cout << "Starting slice " << slice << "..." << flush;

		atomic<int> voxCount{0};
		const int sliceOffset = slice * voxelsPerSlice;
		clock_t loopStart = clock();
		function<void (const int&)> processVox = [&] (const int &vox) {
			// Set up parameters and constants
			double M0 = 0., T1 = 0., T2 = 0., B0 = 0, B1 = 1., residual = 0.;
			if (!maskData || ((maskData[sliceOffset + vox] > 0.) && (T1Data[sliceOffset + vox] > 0.)))
			{	// Zero T1 causes zero-pivot error.
				voxCount++;
				T1 = T1Data[sliceOffset + vox];
				if (B0Data) B0 = B0Data[sliceOffset + vox];
				if (B1Data)	B1 = B1Data[sliceOffset + vox];
				// Gather signals.
				vector<VectorXd> signals;
				for (int p = 0; p < nPhases; p++)
				{
					VectorXd temp(nFlip);
					for (int i = 0; i < nFlip; i++)
						temp(i) = ssfpData[p][i*voxelsPerVolume + sliceOffset + vox];
					signals.push_back(temp);
				}

				if (tesla == 0) {
					// Choose phase with accumulated phase closest to 180 and then classic DESPOT2
					int index = 0;
					double bestPhase = DBL_MAX;
					for (int p = 0; p < nPhases; p++) {
						double thisPhase = (B0 * ssfpTR * 2 * M_PI) + ssfpPhases[p];
						if (fabs(fmod(thisPhase - M_PI, 2 * M_PI)) < bestPhase) {
							bestPhase = fabs(fmod(thisPhase - M_PI, 2 * M_PI));
							index = p;
						}
					}
					residual = classicDESPOT2(ssfpAngles, signals[index], ssfpTR, T1, B1, M0, T2);
				} else {
					// DESPOT2-FM
					VectorXd allB0(nFlip), allB1(nFlip);
					allB0.setConstant(B0);
					allB1.setConstant(B1);
					DESPOT2FM tc(ssfpAngles, ssfpPhases, signals,
				                 allB0, allB1, ssfpTR, false, fitB0);
					DESPOT2FM::ParamType params(DESPOT2FM::nP);
					residual = regionContraction<DESPOT2FM>(params, tc, loBounds, hiBounds);
					M0 = params[0];
					T2 = params[1];
					if (fitB0)
						B0 = params[2];

				}
			}
			PDData[sliceOffset + vox] = clamp(M0, 0., 5.e3);
			T2Data[sliceOffset + vox] = clamp(T2, 0., 0.5);
			if (fitB0)
				B0Data[sliceOffset + vox] = B0;
			residualData[sliceOffset + vox] = residual;
		};
		apply_for(voxelsPerSlice, processVox);

		if (verbose) {
			clock_t loopEnd = clock();
			if (voxCount > 0)
				cout << voxCount << " unmasked voxels, CPU time per voxel was "
				          << ((loopEnd - loopStart) / ((float)voxCount * CLOCKS_PER_SEC)) << " s, ";
			cout << "finished." << endl;
		}
	}
    time_t procEnd = time(NULL);
    struct tm *localEnd = localtime(&procEnd);
	char theTime[512];
    strftime(theTime, 512, "%H:%M:%S", localEnd);
	cout << "Finished processing at " << theTime << ". Run-time was "
	          << difftime(procEnd, procStart) << " s." << endl;
	savedHeader.setDim(4, 1);
	savedHeader.setDatatype(NIFTI_TYPE_FLOAT32);
	savedHeader.open(outPrefix + "_T2.nii.gz", NiftiImage::NIFTI_WRITE);
	savedHeader.writeVolume(0, T2Data);
	savedHeader.close();
	savedHeader.open(outPrefix + "_PD.nii.gz", NiftiImage::NIFTI_WRITE);
	savedHeader.writeVolume(0, PDData);
	savedHeader.close();
	if (fitB0) {
		savedHeader.open(outPrefix + "_B0.nii.gz", NiftiImage::NIFTI_WRITE);
		savedHeader.writeVolume(0, B0Data);
		savedHeader.close();
	}
	// Clean up memory
	for (int p = 0; p < nPhases; p++)
		free(ssfpData[p]);
	if (B0Data)
		free(B0Data);
	if (B1Data)
		free(B1Data);
	if (maskData)
		free(maskData);
	return EXIT_SUCCESS;
}
<commit_msg>Fixed bug with boundaries option being incorrectly defined.<commit_after>/*
 *  despot1_main.cpp
 *
 *  Created by Tobias Wood on 23/01/2012.
 *  Copyright 2012 Tobias Wood. All rights reserved.
 *
 */

#include <time.h>
#include <getopt.h>
#include <iostream>
#include <atomic>
#include <Eigen/Dense>
#include <unsupported/Eigen/NonLinearOptimization>

#include "NiftiImage.h"
#include "DESPOT.h"
#include "DESPOT_Functors.h"
#include "RegionContraction.h"

#define USE_PROCPAR
#ifdef USE_PROCPAR
	#include "procpar.h"
	using namespace Recon;
#endif

using namespace std;
using namespace Eigen;

//******************************************************************************
// Arguments / Usage
//******************************************************************************
const string credit {
"despot2 - Written by tobias.wood@kcl.ac.uk, based on work by Sean Deoni. \n\
Acknowledgements greatfully received, grant discussions welcome."
};

const string usage {
"Usage is: despot2 [options] output_prefix T1_map ssfp_files\n\
\
Options:\n\
	--help, -h        : Print this message.\n\
	--mask, -m file   : Mask input with specified file.\n\
	--B0 file         : B0 Map file.\n\
	--B1 file         : B1 Map file.\n\
	--M0 file         : Proton density file.\n\
	--verbose, -v     : Print slice processing times.\n\
	--start_slice N   : Start processing from slice N.\n\
	--end_slice   N   : Finish processing at slice N.\n\
	--tesla, -t 3     : Enables DESPOT-FM with boundaries suitable for 3T\n\
	            7     : Boundaries suitable for 7T (default)\n\
	            u     : User specified boundaries from stdin.\n"

};

// tesla == 0 means NO DESPOT-FM
static int tesla = 0, fitB0 = false, verbose = false, start_slice = -1, end_slice = -1;
static string outPrefix;
static struct option long_options[] =
{
	{"B0", required_argument, 0, '0'},
	{"B1", required_argument, 0, '1'},
	{"M0", required_argument, 0, 'M'},
	{"help", no_argument, 0, 'h'},
	{"mask", required_argument, 0, 'm'},
	{"tesla", required_argument, 0, 't'},
	{"verbose", no_argument, 0, 'v'},
	{"start_slice", required_argument, 0, 'S'},
	{"end_slice", required_argument, 0, 'E'},
	{0, 0, 0, 0}
};

//******************************************************************************
// Main
//******************************************************************************
int main(int argc, char **argv)
{
	//**************************************************************************
	// Argument Processing
	//**************************************************************************
	cout << credit << endl;
	if (argc < 4) {
		cout << usage << endl;
		return EXIT_FAILURE;
	}
	Eigen::initParallel();
	NiftiImage inFile, savedHeader;
	double *maskData = NULL, *B0Data = NULL, *B1Data = NULL, *T1Data = NULL,
	       *M0Data = NULL;
	string procPath;

	int indexptr = 0, c;
	while ((c = getopt_long(argc, argv, "hvm:t:", long_options, &indexptr)) != -1) {
		switch (c) {
			case 'm':
				cout << "Reading mask file " << optarg << endl;
				inFile.open(optarg, 'r');
				maskData = inFile.readVolume<double>(0);
				inFile.close();
				break;
			case '0':
				cout << "Reading B0 file " << optarg << endl;
				inFile.open(optarg, 'r');
				B0Data = inFile.readVolume<double>(0);
				inFile.close();
				break;
			case '1':
				cout << "Reading B1 file " << optarg;
				inFile.open(optarg, 'r');
				B1Data = inFile.readVolume<double>(0);
				inFile.close();
				break;
			case 'M':
				cout << "Reading M0 file " << optarg;
				inFile.open(optarg, 'r');
				M0Data = inFile.readVolume<double>(0);
				inFile.close();
				break;
			case 't':
				switch (*optarg) {
					case '3': tesla = 3; break;
					case '7': tesla = 7; break;
					case 'u': tesla = -1; break;
					default:
						cout << "Unknown boundaries type " << optarg << endl;
						abort();
						break;
				}
				cout << "Using " << tesla << "T boundaries." << endl;
				break;
			case 'v':
				verbose = true;
				break;
			case 'S':
				start_slice = atoi(optarg);
				break;
			case 'E':
				end_slice = atoi(optarg);
				break;
			case 0:
				// Just a flag
				break;
			case '?': // getopt will print an error message
			case 'h':
				cout << usage << endl;
				return EXIT_FAILURE;
		}
	}
	if ((tesla != 0) && !B0Data)
		fitB0 = true;
	cout << "Output prefix will be: " << argv[optind] << endl;
	outPrefix = argv[optind++];
	cout << "Reading T1 Map from: " << argv[optind] << endl;
	inFile.open(argv[optind++], 'r');
	T1Data = inFile.readVolume<double>(0);
	inFile.close();
	savedHeader = inFile;
	//**************************************************************************
	// Gather SSFP Data
	//**************************************************************************
	int nFlip, nPhases;
	double ssfpTR;
	nPhases = argc - optind;
	vector<double> ssfpPhases(nPhases);
	VectorXd ssfpAngles;
	int voxelsPerSlice, voxelsPerVolume;
	double **ssfpData = (double **)malloc(nPhases * sizeof(double *));
	for (size_t p = 0; p < nPhases; p++)
	{
		cout << "Reading SSFP header from " << argv[optind] << endl;
		inFile.open(argv[optind], 'r');
		if (p == 0)
		{	// Read nFlip, TR and flip angles from first file
			nFlip = inFile.dim(4);
			voxelsPerSlice = inFile.voxelsPerSlice();
			voxelsPerVolume = inFile.voxelsPerVolume();
			ssfpAngles.resize(nFlip, 1);

			#ifdef USE_PROCPAR
			ParameterList pars;
			if (ReadProcpar(inFile.basename() + ".procpar", pars)) {
				ssfpTR = RealValue(pars, "tr");
				for (int i = 0; i < nFlip; i++)
					ssfpAngles[i] = RealValue(pars, "flip1", i);
			} else
			#endif
			{
				cout << "Enter SSFP TR (s): " << flush;
				cin >> ssfpTR;
				cout << "Enter " << nFlip << " flip angles (degrees): " << flush;
				for (int i = 0; i < ssfpAngles.size(); i++)
					cin >> ssfpAngles[i];
			}
		}
		#ifdef USE_PROCPAR
		ParameterList pars;
		if (ReadProcpar(inFile.basename() + ".procpar", pars)) {
			ssfpPhases[p] = RealValue(pars, "rfphase") * M_PI / 180.;
		} else
		#endif
		{
			cout << "Enter phase-cycling (degrees): " << flush;
			cin >> ssfpPhases[p]; ssfpPhases[p] *= M_PI / 180.;
		}
		cout << "Reading SSFP data..." << endl;
		ssfpData[p] = inFile.readAllVolumes<double>();
		// Don't close the first header because we've saved it to write the
		// results, and FSLIO gets fussy about cloning closed headers
		inFile.close();
		optind++;
	}
	ssfpAngles *= M_PI / 180.;

	if (optind != argc) {
		cerr << "Unprocessed arguments supplied.\n" << usage;
		exit(EXIT_FAILURE);
	}

	// Set up boundaries for DESPOT-FM if needed
	DESPOT2FM::ParamType loBounds, hiBounds;
	if (tesla != 0) {
		if (tesla > 0) {
			loBounds = DESPOT2FM::loBounds(tesla);
			hiBounds = DESPOT2FM::hiBounds(tesla);
		} else if (tesla < 0) {
			cout << "Enter " << DESPOT2FM::nP << " parameter pairs (low then high): " << flush;
			for (int i = 0; i < DESPOT2FM::nP; i++) cin >> loBounds[i] >> hiBounds[i];
		}
		// If fitting, give a suitable range and allocate results memory
		if (fitB0) {
			loBounds[DESPOT2FM::nP - 1] = -0.5 / ssfpTR;
			hiBounds[DESPOT2FM::nP - 1] =  0.5 / ssfpTR;
			B0Data = new double[voxelsPerVolume];
		} else { // Otherwise fix and let functors pick up the specified value
			loBounds[DESPOT2FM::nP - 1] = 0.;
			hiBounds[DESPOT2FM::nP - 1] = 0.;
		}
	}

	if (verbose) {
		cout << "SSFP Angles (deg): " << ssfpAngles.transpose() * 180 / M_PI << endl;
		if (tesla != 0)
			cout << "Low bounds: " << loBounds.transpose() << endl
				 << "Hi bounds:  " << hiBounds.transpose() << endl;
	}
	//**************************************************************************
	// Set up results data
	//**************************************************************************
	double *residualData = new double[voxelsPerVolume];
	double *PDData = new double[voxelsPerVolume];
	double *T2Data = new double[voxelsPerVolume];
	//**************************************************************************
	// Do the fitting
	//**************************************************************************
    time_t procStart = time(NULL);
	if ((start_slice < 0) || (start_slice >= inFile.dim(3)))
		start_slice = 0;
	if ((end_slice < 0) || (end_slice > inFile.dim(3)))
		end_slice = inFile.dim(3);
	for (int slice = start_slice; slice < end_slice; slice++) {
		// Read in data
		if (verbose)
			cout << "Starting slice " << slice << "..." << flush;

		atomic<int> voxCount{0};
		const int sliceOffset = slice * voxelsPerSlice;
		clock_t loopStart = clock();
		function<void (const int&)> processVox = [&] (const int &vox) {
			// Set up parameters and constants
			double M0 = 0., T1 = 0., T2 = 0., B0 = 0, B1 = 1., residual = 0.;
			if (!maskData || ((maskData[sliceOffset + vox] > 0.) && (T1Data[sliceOffset + vox] > 0.)))
			{	// Zero T1 causes zero-pivot error.
				voxCount++;
				T1 = T1Data[sliceOffset + vox];
				if (B0Data) B0 = B0Data[sliceOffset + vox];
				if (B1Data)	B1 = B1Data[sliceOffset + vox];
				// Gather signals.
				vector<VectorXd> signals;
				for (int p = 0; p < nPhases; p++)
				{
					VectorXd temp(nFlip);
					for (int i = 0; i < nFlip; i++)
						temp(i) = ssfpData[p][i*voxelsPerVolume + sliceOffset + vox];
					signals.push_back(temp);
				}

				if (tesla == 0) {
					// Choose phase with accumulated phase closest to 180 and then classic DESPOT2
					int index = 0;
					double bestPhase = DBL_MAX;
					for (int p = 0; p < nPhases; p++) {
						double thisPhase = (B0 * ssfpTR * 2 * M_PI) + ssfpPhases[p];
						if (fabs(fmod(thisPhase - M_PI, 2 * M_PI)) < bestPhase) {
							bestPhase = fabs(fmod(thisPhase - M_PI, 2 * M_PI));
							index = p;
						}
					}
					residual = classicDESPOT2(ssfpAngles, signals[index], ssfpTR, T1, B1, M0, T2);
				} else {
					// DESPOT2-FM
					VectorXd allB0(nFlip), allB1(nFlip);
					allB0.setConstant(B0);
					allB1.setConstant(B1);
					DESPOT2FM tc(ssfpAngles, ssfpPhases, signals,
				                 allB0, allB1, ssfpTR, false, fitB0);
					DESPOT2FM::ParamType params(DESPOT2FM::nP);
					residual = regionContraction<DESPOT2FM>(params, tc, loBounds, hiBounds);
					M0 = params[0];
					T2 = params[1];
					if (fitB0)
						B0 = params[2];

				}
			}
			PDData[sliceOffset + vox] = clamp(M0, 0., 5.e3);
			T2Data[sliceOffset + vox] = clamp(T2, 0., 0.5);
			if (fitB0)
				B0Data[sliceOffset + vox] = B0;
			residualData[sliceOffset + vox] = residual;
		};
		apply_for(voxelsPerSlice, processVox);

		if (verbose) {
			clock_t loopEnd = clock();
			if (voxCount > 0)
				cout << voxCount << " unmasked voxels, CPU time per voxel was "
				          << ((loopEnd - loopStart) / ((float)voxCount * CLOCKS_PER_SEC)) << " s, ";
			cout << "finished." << endl;
		}
	}
    time_t procEnd = time(NULL);
    struct tm *localEnd = localtime(&procEnd);
	char theTime[512];
    strftime(theTime, 512, "%H:%M:%S", localEnd);
	cout << "Finished processing at " << theTime << ". Run-time was "
	          << difftime(procEnd, procStart) << " s." << endl;
	savedHeader.setDim(4, 1);
	savedHeader.setDatatype(NIFTI_TYPE_FLOAT32);
	savedHeader.open(outPrefix + "_T2.nii.gz", NiftiImage::NIFTI_WRITE);
	savedHeader.writeVolume(0, T2Data);
	savedHeader.close();
	savedHeader.open(outPrefix + "_PD.nii.gz", NiftiImage::NIFTI_WRITE);
	savedHeader.writeVolume(0, PDData);
	savedHeader.close();
	if (fitB0) {
		savedHeader.open(outPrefix + "_B0.nii.gz", NiftiImage::NIFTI_WRITE);
		savedHeader.writeVolume(0, B0Data);
		savedHeader.close();
	}
	// Clean up memory
	for (int p = 0; p < nPhases; p++)
		free(ssfpData[p]);
	if (B0Data)
		free(B0Data);
	if (B1Data)
		free(B1Data);
	if (maskData)
		free(maskData);
	return EXIT_SUCCESS;
}
<|endoftext|>


<commit_before>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2015.  All rights reserved
 */
#ifndef _Stroika_Foundation_Traversal_DiscreteRange_inl_
#define _Stroika_Foundation_Traversal_DiscreteRange_inl_

#include    "../Debug/Assertions.h"
#include    "../Memory/BlockAllocated.h"

namespace   Stroika {
    namespace   Foundation {
        namespace   Traversal {

            /*
             ********************************************************************************
             ***************** DiscreteRange<T, TRAITS>::MyIteratorRep_ *********************
             ********************************************************************************
             */
            template    <typename T, typename TRAITS>
            struct   DiscreteRange<T, TRAITS>::MyIteratorRep_ : Iterator<T>::IRep  {
                DECLARE_USE_BLOCK_ALLOCATION(MyIteratorRep_);
                T fCur;
                T fEnd;
                bool fAtEnd;
                MyIteratorRep_ ()
                    : fCur (TRAITS::kLowerBound)
                    , fEnd (TRAITS::kLowerBound)
                    , fAtEnd (true)
                {
                }
                MyIteratorRep_ (T start, T end)
                    : fCur (start)
                    , fEnd (end)
                    , fAtEnd (false)
                {
                }
                virtual void    More (Memory::Optional<T>* result, bool advance) override
                {
                    RequireNotNull (result);
                    result->clear ();
                    if (advance) {
                        Require (not fAtEnd);
                        if (fCur == fEnd) {
                            fAtEnd = true;
                        }
                        else {
                            fCur = TRAITS::GetNext (fCur);
                        }
                    }
                    if (not fAtEnd) {
                        *result = fCur;
                    }
                }
                virtual IteratorOwnerID GetOwner () const override
                {
                    /*
                     *  This return value allows any two DiscreteRange iterators (of the same type) to be compared.
                     */
                    return typeid (*this).name ();
                }
                virtual bool    Equals (const typename Iterator<T>::IRep* rhs) const override
                {
                    RequireNotNull (rhs);
                    AssertNotImplemented ();
                    return false;
                }
                virtual shared_ptr<typename Iterator<T>::IRep>    Clone () const override
                {
                    shared_ptr<MyIteratorRep_>  tmp = shared_ptr<MyIteratorRep_> (new MyIteratorRep_ (fCur, fEnd));
                    tmp->fAtEnd = fAtEnd;
                    return tmp;
                }
            };

            /*
             ********************************************************************************
             ***************** DiscreteRange<T, TRAITS>::MyIterable_ ************************
             ********************************************************************************
             */
            template    <typename T, typename TRAITS>
            struct  DiscreteRange<T, TRAITS>::MyIterable_: Iterable<T> {
                struct   MyRep_ : Iterable<T>::_IRep {
                    using   inherited = typename Iterable<T>::_IRep;
                    using   _SharedPtrIRep = typename Iterable<T>::_SharedPtrIRep;
                    using   _APPLY_ARGTYPE = typename Iterable<T>::_IRep::_APPLY_ARGTYPE;
                    using   _APPLYUNTIL_ARGTYPE = typename Iterable<T>::_IRep::_APPLYUNTIL_ARGTYPE;
                    DECLARE_USE_BLOCK_ALLOCATION (MyRep_);
                    T       fStart;
                    T       fEnd;
                    bool    fForcedEnd;
                    MyRep_ ()
                        : fStart (TRAITS::kLowerBound)
                        , fEnd (TRAITS::kLowerBound)
                        , fForcedEnd (true)
                    {
                    }
                    MyRep_ (T start, T end)
                        : fStart (start)
                        , fEnd (end)
                        , fForcedEnd (false)
                    {
                    }
                    virtual _SharedPtrIRep      Clone (IteratorOwnerID forIterableEnvelope) const
                    {
                        // DiscreteRange doesnt track specific 'envelope' owner
                        return _SharedPtrIRep (new MyRep_ (*this));
                    }
                    virtual Iterator<T>         MakeIterator (IteratorOwnerID suggestedOwner) const
                    {
                        // DiscreteRange doesnt track specific 'envelope' owner
                        if (fForcedEnd) {
                            return Iterator<T> (typename Iterator<T>::SharedIRepPtr (new DiscreteRange<T, TRAITS>::MyIteratorRep_ ()));
                        }
                        else {
                            return Iterator<T> (typename Iterator<T>::SharedIRepPtr (new DiscreteRange<T, TRAITS>::MyIteratorRep_ (fStart, fEnd)));
                        }
                    }
                    virtual size_t              GetLength () const
                    {
                        using   SignedDifferenceType        =   typename TRAITS::SignedDifferenceType;
                        if (fForcedEnd) {
                            return static_cast<SignedDifferenceType> (0);
                        }
                        else {
                            return 1 + static_cast<SignedDifferenceType> (fEnd) - static_cast<SignedDifferenceType> (fStart);
                        }
                    }
                    virtual bool                IsEmpty () const
                    {
                        if (fForcedEnd) {
                            return true;
                        }
                        else {
                            return false;
                            //return fStart == fEnd;
                        }
                    }
                    virtual void                Apply (_APPLY_ARGTYPE doToElement) const
                    {
                        this->_Apply (doToElement);
                    }
                    virtual Iterator<T>         FindFirstThat (_APPLYUNTIL_ARGTYPE doToElement, IteratorOwnerID suggestedOwner) const
                    {
                        return this->_FindFirstThat (doToElement, suggestedOwner);
                    }
                };
                MyIterable_ ()
                    : Iterable<T> (typename Iterable<T>::_SharedPtrIRep (new MyRep_ ()))
                {
                }
                MyIterable_ (T start, T end)
                    : Iterable<T> (typename Iterable<T>::_SharedPtrIRep (new MyRep_ (start, end)))
                {
                }
            };

            /*
             ********************************************************************************
             ***************************** DiscreteRange<T> *********************************
             ********************************************************************************
             */
            template    <typename T, typename TRAITS>
            DiscreteRange<T, TRAITS>::DiscreteRange (T begin, T end)
                : inherited (begin, end)
            {
                Require (begin <= end);
            }
            template    <typename T, typename TRAITS>
            DiscreteRange<T, TRAITS>::DiscreteRange (const Memory::Optional<T>& begin, const Memory::Optional<T>& end)
                : inherited (begin, end)
            {
            }
            template    <typename T, typename TRAITS>
            DiscreteRange<T, TRAITS>::DiscreteRange (const Range<T, typename TRAITS::RangeTraitsType>& r)
                : inherited ()
            {
                // Could do more efficiently
                if (not r.empty ()) {
                    *this = DiscreteRange (r.GetLowerBound (), r.GetUpperBound ());
                }
            }
            template    <typename T, typename TRAITS>
            inline  DiscreteRange<T, TRAITS>    DiscreteRange<T, TRAITS>::FullRange ()
            {
                return DiscreteRange<T, TRAITS> (TRAITS::kLowerBound, TRAITS::kUpperBound);
            }
            template    <typename T, typename TRAITS>
            inline  Range<T, TRAITS>    DiscreteRange<T, TRAITS>::Intersection (const Range<T, TRAITS>& rhs) const
            {
                return inherited::Intersection (rhs);
            }
            template    <typename T, typename TRAITS>
            DiscreteRange<T, TRAITS>    DiscreteRange<T, TRAITS>::Intersection (const DiscreteRange<T, TRAITS>& rhs) const
            {
                return DiscreteRange<T, TRAITS> (inherited::Intersection (rhs));
            }
            template    <typename T, typename TRAITS>
            inline  Range<T, TRAITS>    DiscreteRange<T, TRAITS>::UnionBounds (const Range<T, TRAITS>& rhs) const
            {
                return inherited::UnionBounds (rhs);
            }
            template    <typename T, typename TRAITS>
            DiscreteRange<T, TRAITS>    DiscreteRange<T, TRAITS>::UnionBounds (const DiscreteRange<T, TRAITS>& rhs) const
            {
                auto    r   =   inherited::UnionBounds (rhs);
                return DiscreteRange<T, TRAITS> (r.GetLowerBound (), r.GetUpperBound ());
            }
            template    <typename T, typename TRAITS>
            typename DiscreteRange<T, TRAITS>::UnsignedDifferenceType DiscreteRange<T, TRAITS>::GetNumberOfContainedPoints () const
            {
                if (this->empty ()) {
                    return 0;
                }
                else {
                    return this->GetDistancespanned () + 1;
                }
            }
            template    <typename T, typename TRAITS>
            inline  auto    DiscreteRange<T, TRAITS>::Offset (SignedDifferenceType o) const -> DiscreteRange
            {
                Require (not this->empty ());
                return DiscreteRange (inherited::Offset (o));
            }
            template    <typename T, typename TRAITS>
            Iterable<T>   DiscreteRange<T, TRAITS>::Elements () const
            {
                return this->empty () ? MyIterable_ () : MyIterable_ (this->GetLowerBound (), this->GetUpperBound ());
            }
            template    <typename T, typename TRAITS>
            inline  DiscreteRange<T, TRAITS>::operator Iterable<T> () const
            {
                return Elements ();
            }
            template    <typename T, typename TRAITS>
            Iterator<T> DiscreteRange<T, TRAITS>::begin () const
            {
                return this->empty () ? Iterator<T>::GetEmptyIterator () : Iterator<T> (typename Iterator<T>::SharedIRepPtr (new MyIteratorRep_ (this->GetLowerBound (), this->GetUpperBound ())));
            }
            template    <typename T, typename TRAITS>
            inline  Iterator<T> DiscreteRange<T, TRAITS>::end () const
            {
                return Iterator<T>::GetEmptyIterator ();
            }

            /*
             ********************************************************************************
             *********************************** operator^ **********************************
             ********************************************************************************
             */
            template    <typename T, typename TRAITS>
            inline  DiscreteRange<T, TRAITS>   operator^ (const DiscreteRange<T, TRAITS>& lhs, const DiscreteRange<T, TRAITS>& rhs)
            {
                return lhs.Intersection (rhs);
            }

        }
    }
}
#endif /* _Stroika_Foundation_Traversal_DiscreteRange_inl_ */
<commit_msg>Fix case in Traversal/DiscreteRange when Traversal::IteratorBase::SharedPtrImplementationTemplate != shared_ptr<commit_after>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2015.  All rights reserved
 */
#ifndef _Stroika_Foundation_Traversal_DiscreteRange_inl_
#define _Stroika_Foundation_Traversal_DiscreteRange_inl_

#include    "../Debug/Assertions.h"
#include    "../Memory/BlockAllocated.h"

namespace   Stroika {
    namespace   Foundation {
        namespace   Traversal {

            /*
             ********************************************************************************
             ***************** DiscreteRange<T, TRAITS>::MyIteratorRep_ *********************
             ********************************************************************************
             */
            template    <typename T, typename TRAITS>
            struct   DiscreteRange<T, TRAITS>::MyIteratorRep_ : Iterator<T>::IRep  {
                using   inherited = typename Iterator<T>::IRep;
                DECLARE_USE_BLOCK_ALLOCATION(MyIteratorRep_);
                T fCur;
                T fEnd;
                bool fAtEnd;
                MyIteratorRep_ ()
                    : fCur (TRAITS::kLowerBound)
                    , fEnd (TRAITS::kLowerBound)
                    , fAtEnd (true)
                {
                }
                MyIteratorRep_ (T start, T end)
                    : fCur (start)
                    , fEnd (end)
                    , fAtEnd (false)
                {
                }
                virtual void    More (Memory::Optional<T>* result, bool advance) override
                {
                    RequireNotNull (result);
                    result->clear ();
                    if (advance) {
                        Require (not fAtEnd);
                        if (fCur == fEnd) {
                            fAtEnd = true;
                        }
                        else {
                            fCur = TRAITS::GetNext (fCur);
                        }
                    }
                    if (not fAtEnd) {
                        *result = fCur;
                    }
                }
                virtual IteratorOwnerID GetOwner () const override
                {
                    /*
                     *  This return value allows any two DiscreteRange iterators (of the same type) to be compared.
                     */
                    return typeid (*this).name ();
                }
                virtual bool    Equals (const typename Iterator<T>::IRep* rhs) const override
                {
                    RequireNotNull (rhs);
                    AssertNotImplemented ();
                    return false;
                }
                virtual typename inherited::SharedIRepPtr    Clone () const override
                {
                    Traversal::IteratorBase::SharedPtrImplementationTemplate<MyIteratorRep_>  tmp  { new MyIteratorRep_ (fCur, fEnd) };
                    tmp->fAtEnd = fAtEnd;
                    return tmp;
                }
            };

            /*
             ********************************************************************************
             ***************** DiscreteRange<T, TRAITS>::MyIterable_ ************************
             ********************************************************************************
             */
            template    <typename T, typename TRAITS>
            struct  DiscreteRange<T, TRAITS>::MyIterable_: Iterable<T> {
                struct   MyRep_ : Iterable<T>::_IRep {
                    using   inherited = typename Iterable<T>::_IRep;
                    using   _SharedPtrIRep = typename Iterable<T>::_SharedPtrIRep;
                    using   _APPLY_ARGTYPE = typename Iterable<T>::_IRep::_APPLY_ARGTYPE;
                    using   _APPLYUNTIL_ARGTYPE = typename Iterable<T>::_IRep::_APPLYUNTIL_ARGTYPE;
                    DECLARE_USE_BLOCK_ALLOCATION (MyRep_);
                    T       fStart;
                    T       fEnd;
                    bool    fForcedEnd;
                    MyRep_ ()
                        : fStart (TRAITS::kLowerBound)
                        , fEnd (TRAITS::kLowerBound)
                        , fForcedEnd (true)
                    {
                    }
                    MyRep_ (T start, T end)
                        : fStart (start)
                        , fEnd (end)
                        , fForcedEnd (false)
                    {
                    }
                    virtual _SharedPtrIRep      Clone (IteratorOwnerID forIterableEnvelope) const
                    {
                        // DiscreteRange doesnt track specific 'envelope' owner
                        return _SharedPtrIRep (new MyRep_ (*this));
                    }
                    virtual Iterator<T>         MakeIterator (IteratorOwnerID suggestedOwner) const
                    {
                        // DiscreteRange doesnt track specific 'envelope' owner
                        if (fForcedEnd) {
                            return Iterator<T> (typename Iterator<T>::SharedIRepPtr (new DiscreteRange<T, TRAITS>::MyIteratorRep_ ()));
                        }
                        else {
                            return Iterator<T> (typename Iterator<T>::SharedIRepPtr (new DiscreteRange<T, TRAITS>::MyIteratorRep_ (fStart, fEnd)));
                        }
                    }
                    virtual size_t              GetLength () const
                    {
                        using   SignedDifferenceType        =   typename TRAITS::SignedDifferenceType;
                        if (fForcedEnd) {
                            return static_cast<SignedDifferenceType> (0);
                        }
                        else {
                            return 1 + static_cast<SignedDifferenceType> (fEnd) - static_cast<SignedDifferenceType> (fStart);
                        }
                    }
                    virtual bool                IsEmpty () const
                    {
                        if (fForcedEnd) {
                            return true;
                        }
                        else {
                            return false;
                            //return fStart == fEnd;
                        }
                    }
                    virtual void                Apply (_APPLY_ARGTYPE doToElement) const
                    {
                        this->_Apply (doToElement);
                    }
                    virtual Iterator<T>         FindFirstThat (_APPLYUNTIL_ARGTYPE doToElement, IteratorOwnerID suggestedOwner) const
                    {
                        return this->_FindFirstThat (doToElement, suggestedOwner);
                    }
                };
                MyIterable_ ()
                    : Iterable<T> (typename Iterable<T>::_SharedPtrIRep (new MyRep_ ()))
                {
                }
                MyIterable_ (T start, T end)
                    : Iterable<T> (typename Iterable<T>::_SharedPtrIRep (new MyRep_ (start, end)))
                {
                }
            };

            /*
             ********************************************************************************
             ***************************** DiscreteRange<T> *********************************
             ********************************************************************************
             */
            template    <typename T, typename TRAITS>
            DiscreteRange<T, TRAITS>::DiscreteRange (T begin, T end)
                : inherited (begin, end)
            {
                Require (begin <= end);
            }
            template    <typename T, typename TRAITS>
            DiscreteRange<T, TRAITS>::DiscreteRange (const Memory::Optional<T>& begin, const Memory::Optional<T>& end)
                : inherited (begin, end)
            {
            }
            template    <typename T, typename TRAITS>
            DiscreteRange<T, TRAITS>::DiscreteRange (const Range<T, typename TRAITS::RangeTraitsType>& r)
                : inherited ()
            {
                // Could do more efficiently
                if (not r.empty ()) {
                    *this = DiscreteRange (r.GetLowerBound (), r.GetUpperBound ());
                }
            }
            template    <typename T, typename TRAITS>
            inline  DiscreteRange<T, TRAITS>    DiscreteRange<T, TRAITS>::FullRange ()
            {
                return DiscreteRange<T, TRAITS> (TRAITS::kLowerBound, TRAITS::kUpperBound);
            }
            template    <typename T, typename TRAITS>
            inline  Range<T, TRAITS>    DiscreteRange<T, TRAITS>::Intersection (const Range<T, TRAITS>& rhs) const
            {
                return inherited::Intersection (rhs);
            }
            template    <typename T, typename TRAITS>
            DiscreteRange<T, TRAITS>    DiscreteRange<T, TRAITS>::Intersection (const DiscreteRange<T, TRAITS>& rhs) const
            {
                return DiscreteRange<T, TRAITS> (inherited::Intersection (rhs));
            }
            template    <typename T, typename TRAITS>
            inline  Range<T, TRAITS>    DiscreteRange<T, TRAITS>::UnionBounds (const Range<T, TRAITS>& rhs) const
            {
                return inherited::UnionBounds (rhs);
            }
            template    <typename T, typename TRAITS>
            DiscreteRange<T, TRAITS>    DiscreteRange<T, TRAITS>::UnionBounds (const DiscreteRange<T, TRAITS>& rhs) const
            {
                auto    r   =   inherited::UnionBounds (rhs);
                return DiscreteRange<T, TRAITS> (r.GetLowerBound (), r.GetUpperBound ());
            }
            template    <typename T, typename TRAITS>
            typename DiscreteRange<T, TRAITS>::UnsignedDifferenceType DiscreteRange<T, TRAITS>::GetNumberOfContainedPoints () const
            {
                if (this->empty ()) {
                    return 0;
                }
                else {
                    return this->GetDistancespanned () + 1;
                }
            }
            template    <typename T, typename TRAITS>
            inline  auto    DiscreteRange<T, TRAITS>::Offset (SignedDifferenceType o) const -> DiscreteRange
            {
                Require (not this->empty ());
                return DiscreteRange (inherited::Offset (o));
            }
            template    <typename T, typename TRAITS>
            Iterable<T>   DiscreteRange<T, TRAITS>::Elements () const
            {
                return this->empty () ? MyIterable_ () : MyIterable_ (this->GetLowerBound (), this->GetUpperBound ());
            }
            template    <typename T, typename TRAITS>
            inline  DiscreteRange<T, TRAITS>::operator Iterable<T> () const
            {
                return Elements ();
            }
            template    <typename T, typename TRAITS>
            Iterator<T> DiscreteRange<T, TRAITS>::begin () const
            {
                return this->empty () ? Iterator<T>::GetEmptyIterator () : Iterator<T> (typename Iterator<T>::SharedIRepPtr (new MyIteratorRep_ (this->GetLowerBound (), this->GetUpperBound ())));
            }
            template    <typename T, typename TRAITS>
            inline  Iterator<T> DiscreteRange<T, TRAITS>::end () const
            {
                return Iterator<T>::GetEmptyIterator ();
            }

            /*
             ********************************************************************************
             *********************************** operator^ **********************************
             ********************************************************************************
             */
            template    <typename T, typename TRAITS>
            inline  DiscreteRange<T, TRAITS>   operator^ (const DiscreteRange<T, TRAITS>& lhs, const DiscreteRange<T, TRAITS>& rhs)
            {
                return lhs.Intersection (rhs);
            }

        }
    }
}
#endif /* _Stroika_Foundation_Traversal_DiscreteRange_inl_ */
<|endoftext|>