<commit_before>// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/sync/glue/preference_change_processor.h"

#include <set>
#include <string>

#include "base/json/json_reader.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/chrome_thread.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/sync/glue/preference_model_associator.h"
#include "chrome/browser/sync/profile_sync_service.h"
#include "chrome/browser/sync/protocol/preference_specifics.pb.h"
#include "chrome/common/json_value_serializer.h"
#include "chrome/common/notification_service.h"
#include "chrome/common/pref_names.h"

namespace browser_sync {

PreferenceChangeProcessor::PreferenceChangeProcessor(
    PreferenceModelAssociator* model_associator,
    UnrecoverableErrorHandler* error_handler)
    : ChangeProcessor(error_handler),
      pref_service_(NULL),
      model_associator_(model_associator) {
  DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
  DCHECK(model_associator);
  DCHECK(error_handler);
}

PreferenceChangeProcessor::~PreferenceChangeProcessor(){
  DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
}

void PreferenceChangeProcessor::Observe(NotificationType type,
                                        const NotificationSource& source,
                                        const NotificationDetails& details) {
  DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
  DCHECK(running());
  DCHECK(NotificationType::PREF_CHANGED == type);
  DCHECK_EQ(pref_service_, Source<PrefService>(source).ptr());

  std::wstring* name = Details<std::wstring>(details).ptr();
  const PrefService::Preference* preference =
      pref_service_->FindPreference((*name).c_str());
  DCHECK(preference);

  sync_api::WriteTransaction trans(share_handle());
  sync_api::WriteNode node(&trans);

  int64 sync_id = model_associator_->GetSyncIdFromChromeId(*name);
  if (sync_api::kInvalidId == sync_id) {
    LOG(ERROR) << "Unexpected notification for: " << *name;
    error_handler()->OnUnrecoverableError();
    return;
  } else {
    if (!node.InitByIdLookup(sync_id)) {
      LOG(ERROR) << "Preference node lookup failed.";
      error_handler()->OnUnrecoverableError();
      return;
    }
  }

  if (!WritePreference(&node, *name, preference->GetValue())) {
    LOG(ERROR) << "Failed to update preference node.";
    error_handler()->OnUnrecoverableError();
    return;
  }
}

void PreferenceChangeProcessor::ApplyChangesFromSyncModel(
    const sync_api::BaseTransaction* trans,
    const sync_api::SyncManager::ChangeRecord* changes,
    int change_count) {
  DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
  if (!running())
    return;
  StopObserving();

  for (int i = 0; i < change_count; ++i) {
    sync_api::ReadNode node(trans);
    // TODO(ncarter): Can't look up the name for deletions: lookup of
    // deleted items fails at the syncapi layer.  However, the node should
    // generally still exist in the syncable database; we just need to
    // plumb the syncapi so that it succeeds.
    if (sync_api::SyncManager::ChangeRecord::ACTION_DELETE ==
        changes[i].action) {
      // Until the above is fixed, we have no choice but to ignore deletions.
      LOG(ERROR) << "No way to handle pref deletion";
      continue;
    }

    if (!node.InitByIdLookup(changes[i].id)) {
      LOG(ERROR) << "Preference node lookup failed.";
      error_handler()->OnUnrecoverableError();
      return;
    }
    DCHECK(syncable::PREFERENCES == node.GetModelType());

    std::string name;
    scoped_ptr<Value> value(ReadPreference(&node, &name));
    if (sync_api::SyncManager::ChangeRecord::ACTION_DELETE ==
        changes[i].action) {
      pref_service_->ClearPref(UTF8ToWide(name).c_str());
    } else {
      const PrefService::Preference* preference =
          pref_service_->FindPreference(UTF8ToWide(name).c_str());
      if (value.get() && preference) {
        pref_service_->Set(UTF8ToWide(name).c_str(), *value);
      }
    }
  }
  StartObserving();
}

bool PreferenceChangeProcessor::WritePreference(
    sync_api::WriteNode* node,
    const std::wstring& name,
    const Value* value) {
  std::string serialized;
  JSONStringValueSerializer json(&serialized);
  if (!json.Serialize(*value)) {
    LOG(ERROR) << "Failed to serialize preference value.";
    error_handler()->OnUnrecoverableError();
    return false;
  }

  sync_pb::PreferenceSpecifics preference;
  preference.set_name(WideToUTF8(name));
  preference.set_value(serialized);
  node->SetPreferenceSpecifics(preference);
  node->SetTitle(name);
  return true;
}

Value* PreferenceChangeProcessor::ReadPreference(
    sync_api::ReadNode* node,
    std::string* name) {
  const sync_pb::PreferenceSpecifics& preference(
      node->GetPreferenceSpecifics());
  base::JSONReader reader;
  scoped_ptr<Value> value(reader.JsonToValue(preference.value(), false, false));
  if (!value.get()) {
    LOG(ERROR) << "Failed to deserialize preference value: "
               << reader.error_message();
    error_handler()->OnUnrecoverableError();
    return NULL;
  }
  *name = preference.name();
  return value.release();
}

void PreferenceChangeProcessor::StartImpl(Profile* profile) {
  DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
  pref_service_ = profile->GetPrefs();
  StartObserving();
}

void PreferenceChangeProcessor::StopImpl() {
  DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
  StopObserving();
  pref_service_ = NULL;
}

void PreferenceChangeProcessor::StartObserving() {
  DCHECK(pref_service_);
  for (std::set<std::wstring>::const_iterator it =
      model_associator_->synced_preferences().begin();
      it != model_associator_->synced_preferences().end(); ++it) {
    pref_service_->AddPrefObserver((*it).c_str(), this);
  }
}

void PreferenceChangeProcessor::StopObserving() {
  DCHECK(pref_service_);
  for (std::set<std::wstring>::const_iterator it =
      model_associator_->synced_preferences().begin();
      it != model_associator_->synced_preferences().end(); ++it) {
    pref_service_->RemovePrefObserver((*it).c_str(), this);
  }
}

}  // namespace browser_sync
<commit_msg>Send out a notification when the bookmark bar changes.<commit_after>// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/sync/glue/preference_change_processor.h"

#include <set>
#include <string>

#include "base/json/json_reader.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/chrome_thread.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/sync/glue/preference_model_associator.h"
#include "chrome/browser/sync/profile_sync_service.h"
#include "chrome/browser/sync/protocol/preference_specifics.pb.h"
#include "chrome/common/json_value_serializer.h"
#include "chrome/common/notification_service.h"
#include "chrome/common/pref_names.h"

namespace browser_sync {

PreferenceChangeProcessor::PreferenceChangeProcessor(
    PreferenceModelAssociator* model_associator,
    UnrecoverableErrorHandler* error_handler)
    : ChangeProcessor(error_handler),
      pref_service_(NULL),
      model_associator_(model_associator) {
  DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
  DCHECK(model_associator);
  DCHECK(error_handler);
}

PreferenceChangeProcessor::~PreferenceChangeProcessor(){
  DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
}

void PreferenceChangeProcessor::Observe(NotificationType type,
                                        const NotificationSource& source,
                                        const NotificationDetails& details) {
  DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
  DCHECK(running());
  DCHECK(NotificationType::PREF_CHANGED == type);
  DCHECK_EQ(pref_service_, Source<PrefService>(source).ptr());

  std::wstring* name = Details<std::wstring>(details).ptr();
  const PrefService::Preference* preference =
      pref_service_->FindPreference((*name).c_str());
  DCHECK(preference);

  sync_api::WriteTransaction trans(share_handle());
  sync_api::WriteNode node(&trans);

  int64 sync_id = model_associator_->GetSyncIdFromChromeId(*name);
  if (sync_api::kInvalidId == sync_id) {
    LOG(ERROR) << "Unexpected notification for: " << *name;
    error_handler()->OnUnrecoverableError();
    return;
  } else {
    if (!node.InitByIdLookup(sync_id)) {
      LOG(ERROR) << "Preference node lookup failed.";
      error_handler()->OnUnrecoverableError();
      return;
    }
  }

  if (!WritePreference(&node, *name, preference->GetValue())) {
    LOG(ERROR) << "Failed to update preference node.";
    error_handler()->OnUnrecoverableError();
    return;
  }
}

void PreferenceChangeProcessor::ApplyChangesFromSyncModel(
    const sync_api::BaseTransaction* trans,
    const sync_api::SyncManager::ChangeRecord* changes,
    int change_count) {
  DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
  if (!running())
    return;
  StopObserving();

  for (int i = 0; i < change_count; ++i) {
    sync_api::ReadNode node(trans);
    // TODO(ncarter): Can't look up the name for deletions: lookup of
    // deleted items fails at the syncapi layer.  However, the node should
    // generally still exist in the syncable database; we just need to
    // plumb the syncapi so that it succeeds.
    if (sync_api::SyncManager::ChangeRecord::ACTION_DELETE ==
        changes[i].action) {
      // Until the above is fixed, we have no choice but to ignore deletions.
      LOG(ERROR) << "No way to handle pref deletion";
      continue;
    }

    if (!node.InitByIdLookup(changes[i].id)) {
      LOG(ERROR) << "Preference node lookup failed.";
      error_handler()->OnUnrecoverableError();
      return;
    }
    DCHECK(syncable::PREFERENCES == node.GetModelType());

    std::string name;
    scoped_ptr<Value> value(ReadPreference(&node, &name));
    if (sync_api::SyncManager::ChangeRecord::ACTION_DELETE ==
        changes[i].action) {
      pref_service_->ClearPref(UTF8ToWide(name).c_str());
    } else {
      std::wstring pref_name(UTF8ToWide(name));
      const PrefService::Preference* preference =
          pref_service_->FindPreference(pref_name.c_str());
      if (value.get() && preference) {
        pref_service_->Set(pref_name.c_str(), *value);
        if (pref_name == prefs::kShowBookmarkBar) {
          // If it was the bookmark bar, send an additional notification.
          NotificationService::current()->Notify(
              NotificationType::BOOKMARK_BAR_VISIBILITY_PREF_CHANGED,
              Source<PreferenceChangeProcessor>(this),
              NotificationService::NoDetails());
        }
      }
    }
  }
  StartObserving();
}

bool PreferenceChangeProcessor::WritePreference(
    sync_api::WriteNode* node,
    const std::wstring& name,
    const Value* value) {
  std::string serialized;
  JSONStringValueSerializer json(&serialized);
  if (!json.Serialize(*value)) {
    LOG(ERROR) << "Failed to serialize preference value.";
    error_handler()->OnUnrecoverableError();
    return false;
  }

  sync_pb::PreferenceSpecifics preference;
  preference.set_name(WideToUTF8(name));
  preference.set_value(serialized);
  node->SetPreferenceSpecifics(preference);
  node->SetTitle(name);
  return true;
}

Value* PreferenceChangeProcessor::ReadPreference(
    sync_api::ReadNode* node,
    std::string* name) {
  const sync_pb::PreferenceSpecifics& preference(
      node->GetPreferenceSpecifics());
  base::JSONReader reader;
  scoped_ptr<Value> value(reader.JsonToValue(preference.value(), false, false));
  if (!value.get()) {
    LOG(ERROR) << "Failed to deserialize preference value: "
               << reader.error_message();
    error_handler()->OnUnrecoverableError();
    return NULL;
  }
  *name = preference.name();
  return value.release();
}

void PreferenceChangeProcessor::StartImpl(Profile* profile) {
  DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
  pref_service_ = profile->GetPrefs();
  StartObserving();
}

void PreferenceChangeProcessor::StopImpl() {
  DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
  StopObserving();
  pref_service_ = NULL;
}

void PreferenceChangeProcessor::StartObserving() {
  DCHECK(pref_service_);
  for (std::set<std::wstring>::const_iterator it =
      model_associator_->synced_preferences().begin();
      it != model_associator_->synced_preferences().end(); ++it) {
    pref_service_->AddPrefObserver((*it).c_str(), this);
  }
}

void PreferenceChangeProcessor::StopObserving() {
  DCHECK(pref_service_);
  for (std::set<std::wstring>::const_iterator it =
      model_associator_->synced_preferences().begin();
      it != model_associator_->synced_preferences().end(); ++it) {
    pref_service_->RemovePrefObserver((*it).c_str(), this);
  }
}

}  // namespace browser_sync
<|endoftext|>


<commit_before>/*
 * Copyright 2014-present Facebook, Inc.
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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
 *
 *   http://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 <thrift/lib/cpp/transport/THeader.h>
#include <thrift/lib/cpp/util/THttpParser.h>

#include <memory>
#include <folly/io/IOBuf.h>
#include <folly/io/IOBufQueue.h>
#include <folly/Random.h>

#include <folly/portability/GTest.h>

using namespace apache::thrift;
using namespace folly;
using namespace apache::thrift::transport;

namespace apache { namespace thrift { namespace transport {

TEST(THeaderTest, largetransform) {
  THeader header;
  header.setTransform(THeader::ZLIB_TRANSFORM); // ZLib flag

  size_t buf_size = 10000000;
  std::unique_ptr<IOBuf> buf(IOBuf::create(buf_size));
  // Make the data compressible, but not totally random
  for (size_t i = 0; i < buf_size / 4; i++) {
    buf->writableData()[i] = (int8_t)folly::Random::rand32(256);
    buf->writableData()[i+1] = buf->writableData()[i];
    buf->writableData()[i+2] = buf->writableData()[i];
    buf->writableData()[i+3] = (int8_t)folly::Random::rand32(256);
  }
  buf->append(buf_size);

  std::map<std::string, std::string> persistentHeaders;
  buf = header.addHeader(std::move(buf), persistentHeaders);
  buf_size = buf->computeChainDataLength();
  buf->gather(buf_size);
  std::unique_ptr<IOBufQueue> queue(new IOBufQueue);
  std::unique_ptr<IOBufQueue> queue2(new IOBufQueue);
  queue->append(std::move(buf));
  queue2->append(queue->split(buf_size/4));
  queue2->append(queue->split(buf_size/4));
  queue2->append(IOBuf::create(0)); // Empty buffer should work
  queue2->append(queue->split(buf_size/4));
  queue2->append(queue->move());

  size_t needed;

  buf = header.removeHeader(queue2.get(), needed, persistentHeaders);
  EXPECT_EQ(buf->computeChainDataLength(), 10000000);
}

TEST(THeaderTest, http_clear_header) {
  THeader header;
  header.setClientType(THRIFT_HTTP_CLIENT_TYPE);
  auto parser = std::make_shared<apache::thrift::util::THttpClientParser>(
      "testhost", "testuri");
  header.setHttpClientParser(parser);
  header.setHeader("WriteHeader", "foo");

  size_t buf_size = 1000000;
  std::unique_ptr<IOBuf> buf(IOBuf::create(buf_size));
  std::map<std::string, std::string> persistentHeaders;
  buf = header.addHeader(std::move(buf), persistentHeaders);

  EXPECT_TRUE(header.isWriteHeadersEmpty());
}

TEST(THeaderTest, transform) {
  // Simple test for TRANSFORMS enum to string conversion
  EXPECT_EQ(
    THeader::getStringTransform(THeader::TRANSFORMS::ZLIB_TRANSFORM), "zlib");
}

TEST(THeaderTest, eraseReadHeader) {
  THeader header;
  header.setReadHeaders({{"foo", "v"}, {"bar", "v"}, {"moo", "v"}});
  EXPECT_EQ(3, header.getHeaders().size());
  header.eraseReadHeader("bar");
  EXPECT_EQ(2, header.getHeaders().size());
}

TEST(THeaderTest, removeHeaderNullptrQueue) {
  THeader header;
  size_t needed;
  THeader::StringToStringMap persistentHeaders;
  EXPECT_EQ(nullptr, header.removeHeader(nullptr, needed, persistentHeaders));
  EXPECT_EQ(4, needed);
}

TEST(THeaderTest, removeHeaderEmptyQueue) {
  THeader header;
  size_t needed;
  THeader::StringToStringMap persistentHeaders;
  IOBufQueue queue(IOBufQueue::cacheChainLength());
  EXPECT_EQ(nullptr, header.removeHeader(&queue, needed, persistentHeaders));
  EXPECT_EQ(4, needed);
}

TEST(THeaderTest, explicitTimeoutAndPriority) {
  THeader header;
  header.setClientTimeout(std::chrono::milliseconds(100));
  header.setClientQueueTimeout(std::chrono::milliseconds(200));
  header.setCallPriority(concurrency::PRIORITY::IMPORTANT);
  std::map<std::string, std::string> headerMap;
  headerMap[THeader::CLIENT_TIMEOUT_HEADER] = "10";
  headerMap[THeader::QUEUE_TIMEOUT_HEADER] = "20";
  headerMap[THeader::PRIORITY_HEADER] = "3";
  header.setReadHeaders(std::move(headerMap));
  EXPECT_EQ(std::chrono::milliseconds(100), header.getClientTimeout());
  EXPECT_EQ(std::chrono::milliseconds(200), header.getClientQueueTimeout());
  EXPECT_EQ(concurrency::PRIORITY::IMPORTANT, header.getCallPriority());
}

TEST(THeaderTest, headerTimeoutAndPriority) {
  THeader header;
  std::map<std::string, std::string> headerMap;
  headerMap[THeader::CLIENT_TIMEOUT_HEADER] = "10";
  headerMap[THeader::QUEUE_TIMEOUT_HEADER] = "20";
  headerMap[THeader::PRIORITY_HEADER] = "3";
  header.setReadHeaders(std::move(headerMap));
  EXPECT_EQ(std::chrono::milliseconds(10), header.getClientTimeout());
  EXPECT_EQ(std::chrono::milliseconds(20), header.getClientQueueTimeout());
  EXPECT_EQ(concurrency::PRIORITY::NORMAL, header.getCallPriority());
}

TEST(THeaderTest, defaultTimeoutAndPriority) {
  THeader header;
  EXPECT_EQ(std::chrono::milliseconds(0), header.getClientTimeout());
  EXPECT_EQ(std::chrono::milliseconds(0), header.getClientQueueTimeout());
  EXPECT_EQ(concurrency::PRIORITY::N_PRIORITIES, header.getCallPriority());
}
}}}
<commit_msg>Reformat THeadertest.cpp<commit_after>/*
 * Copyright 2014-present Facebook, Inc.
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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
 *
 *   http://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 <thrift/lib/cpp/transport/THeader.h>
#include <thrift/lib/cpp/util/THttpParser.h>

#include <folly/Random.h>
#include <folly/io/IOBuf.h>
#include <folly/io/IOBufQueue.h>
#include <memory>

#include <folly/portability/GTest.h>

using namespace apache::thrift;
using namespace folly;
using namespace apache::thrift::transport;

namespace apache {
namespace thrift {
namespace transport {

TEST(THeaderTest, largetransform) {
  THeader header;
  header.setTransform(THeader::ZLIB_TRANSFORM); // ZLib flag

  size_t buf_size = 10000000;
  std::unique_ptr<IOBuf> buf(IOBuf::create(buf_size));
  // Make the data compressible, but not totally random
  for (size_t i = 0; i < buf_size / 4; i++) {
    buf->writableData()[i] = (int8_t)folly::Random::rand32(256);
    buf->writableData()[i + 1] = buf->writableData()[i];
    buf->writableData()[i + 2] = buf->writableData()[i];
    buf->writableData()[i + 3] = (int8_t)folly::Random::rand32(256);
  }
  buf->append(buf_size);

  std::map<std::string, std::string> persistentHeaders;
  buf = header.addHeader(std::move(buf), persistentHeaders);
  buf_size = buf->computeChainDataLength();
  buf->gather(buf_size);
  std::unique_ptr<IOBufQueue> queue(new IOBufQueue);
  std::unique_ptr<IOBufQueue> queue2(new IOBufQueue);
  queue->append(std::move(buf));
  queue2->append(queue->split(buf_size / 4));
  queue2->append(queue->split(buf_size / 4));
  queue2->append(IOBuf::create(0)); // Empty buffer should work
  queue2->append(queue->split(buf_size / 4));
  queue2->append(queue->move());

  size_t needed;

  buf = header.removeHeader(queue2.get(), needed, persistentHeaders);
  EXPECT_EQ(buf->computeChainDataLength(), 10000000);
}

TEST(THeaderTest, http_clear_header) {
  THeader header;
  header.setClientType(THRIFT_HTTP_CLIENT_TYPE);
  auto parser = std::make_shared<apache::thrift::util::THttpClientParser>(
      "testhost", "testuri");
  header.setHttpClientParser(parser);
  header.setHeader("WriteHeader", "foo");

  size_t buf_size = 1000000;
  std::unique_ptr<IOBuf> buf(IOBuf::create(buf_size));
  std::map<std::string, std::string> persistentHeaders;
  buf = header.addHeader(std::move(buf), persistentHeaders);

  EXPECT_TRUE(header.isWriteHeadersEmpty());
}

TEST(THeaderTest, transform) {
  // Simple test for TRANSFORMS enum to string conversion
  EXPECT_EQ(
      THeader::getStringTransform(THeader::TRANSFORMS::ZLIB_TRANSFORM), "zlib");
}

TEST(THeaderTest, eraseReadHeader) {
  THeader header;
  header.setReadHeaders({{"foo", "v"}, {"bar", "v"}, {"moo", "v"}});
  EXPECT_EQ(3, header.getHeaders().size());
  header.eraseReadHeader("bar");
  EXPECT_EQ(2, header.getHeaders().size());
}

TEST(THeaderTest, removeHeaderNullptrQueue) {
  THeader header;
  size_t needed;
  THeader::StringToStringMap persistentHeaders;
  EXPECT_EQ(nullptr, header.removeHeader(nullptr, needed, persistentHeaders));
  EXPECT_EQ(4, needed);
}

TEST(THeaderTest, removeHeaderEmptyQueue) {
  THeader header;
  size_t needed;
  THeader::StringToStringMap persistentHeaders;
  IOBufQueue queue(IOBufQueue::cacheChainLength());
  EXPECT_EQ(nullptr, header.removeHeader(&queue, needed, persistentHeaders));
  EXPECT_EQ(4, needed);
}

TEST(THeaderTest, explicitTimeoutAndPriority) {
  THeader header;
  header.setClientTimeout(std::chrono::milliseconds(100));
  header.setClientQueueTimeout(std::chrono::milliseconds(200));
  header.setCallPriority(concurrency::PRIORITY::IMPORTANT);
  std::map<std::string, std::string> headerMap;
  headerMap[THeader::CLIENT_TIMEOUT_HEADER] = "10";
  headerMap[THeader::QUEUE_TIMEOUT_HEADER] = "20";
  headerMap[THeader::PRIORITY_HEADER] = "3";
  header.setReadHeaders(std::move(headerMap));
  EXPECT_EQ(std::chrono::milliseconds(100), header.getClientTimeout());
  EXPECT_EQ(std::chrono::milliseconds(200), header.getClientQueueTimeout());
  EXPECT_EQ(concurrency::PRIORITY::IMPORTANT, header.getCallPriority());
}

TEST(THeaderTest, headerTimeoutAndPriority) {
  THeader header;
  std::map<std::string, std::string> headerMap;
  headerMap[THeader::CLIENT_TIMEOUT_HEADER] = "10";
  headerMap[THeader::QUEUE_TIMEOUT_HEADER] = "20";
  headerMap[THeader::PRIORITY_HEADER] = "3";
  header.setReadHeaders(std::move(headerMap));
  EXPECT_EQ(std::chrono::milliseconds(10), header.getClientTimeout());
  EXPECT_EQ(std::chrono::milliseconds(20), header.getClientQueueTimeout());
  EXPECT_EQ(concurrency::PRIORITY::NORMAL, header.getCallPriority());
}

TEST(THeaderTest, defaultTimeoutAndPriority) {
  THeader header;
  EXPECT_EQ(std::chrono::milliseconds(0), header.getClientTimeout());
  EXPECT_EQ(std::chrono::milliseconds(0), header.getClientQueueTimeout());
  EXPECT_EQ(concurrency::PRIORITY::N_PRIORITIES, header.getCallPriority());
}
} // namespace transport
} // namespace thrift
} // namespace apache
<|endoftext|>


<commit_before>#include "shaderlab/ShaderAdapter.h"
#include "shaderlab/PinType.h"
#include "shaderlab/node/CustomBlock.h"
#include "shaderlab/node/Texture2DAsset.h"
#include "shaderlab/node/SubGraph.h"

#include <blueprint/Pin.h>
#include <blueprint/Node.h>

#include <dag/Graph.h>
#include <shadergraph/Block.h>
#include <shadergraph/ValueImpl.h>
#include <shadergraph/block/CustomBlock.h>
#include <shadergraph/block/Texture2DAsset.h>
#include <shadergraph/block/SubGraph.h>

#include <assert.h>

namespace shaderlab
{

int ShaderAdapter::TypeBackToFront(shadergraph::VarType type, int count)
{
    int ret = -1;

    switch (type)
    {
    case shadergraph::VarType::Invalid:
        ret = PIN_INVALID;
        break;
    case shadergraph::VarType::Dynamic:
        ret = PIN_DYNAMIC;
        break;
    case shadergraph::VarType::Bool:
        ret = PIN_BOOL;
        break;
    case shadergraph::VarType::Bool2:
        ret = PIN_BOOL2;
        break;
    case shadergraph::VarType::Bool3:
        ret = PIN_BOOL3;
        break;
    case shadergraph::VarType::Bool4:
        ret = PIN_BOOL4;
        break;
    case shadergraph::VarType::UInt:
        ret = PIN_UINT;
        break;
    case shadergraph::VarType::Int:
        ret = PIN_INT;
        break;
    case shadergraph::VarType::Int2:
        ret = PIN_INT2;
        break;
    case shadergraph::VarType::Int3:
        ret = PIN_INT3;
        break;
    case shadergraph::VarType::Int4:
        ret = PIN_INT4;
        break;
    case shadergraph::VarType::Float:
        ret = PIN_FLOAT;
        break;
    case shadergraph::VarType::Float2:
        ret = PIN_FLOAT2;
        break;
    case shadergraph::VarType::Float3:
        ret = PIN_FLOAT3;
        break;
    case shadergraph::VarType::Float4:
        ret = PIN_FLOAT4;
        break;
    case shadergraph::VarType::Matrix2:
        ret = PIN_MAT2;
        break;
    case shadergraph::VarType::Matrix3:
        ret = PIN_MAT3;
        break;
    case shadergraph::VarType::Matrix4:
        ret = PIN_MAT4;
        break;
    case shadergraph::VarType::Sampler2D:
        ret = PIN_SAMPLER_2D;
        break;
    case shadergraph::VarType::SamplerCube:
        ret = PIN_SAMPLER_CUBE;
        break;
    case shadergraph::VarType::Array:
        ret = PIN_ARRAY;
        break;
    case shadergraph::VarType::Struct:
        ret = PIN_STRUCT;
        break;
    case shadergraph::VarType::Function:
        ret = PIN_FUNCTION;
        break;
    default:
        assert(0);
    }

    return ret;
}

void ShaderAdapter::Front2Back(const bp::Node& front, dag::Node<shadergraph::Variant>& back,
                               const std::string& dir, const ur::Device& dev)
{
    // update uniforms
    auto& src = front.GetProps();
    auto& dst = static_cast<shadergraph::Block&>(back).GetVariants();
    for (auto& s_p : src)
    {
        auto& s = s_p.var;
        for (auto& d : dst)
        {
            if (s.name != d.name) {
                continue;
            }

            assert(d.type == shadergraph::VarType::Uniform);
            auto u_var = std::static_pointer_cast<shadergraph::UniformVal>(d.val)->var;
            if (!u_var.val) {
                continue;
            }

            switch (u_var.type)
            {
            case shadergraph::VarType::Bool:
                std::static_pointer_cast<shadergraph::BoolVal>(u_var.val)->x = s.b;
                break;
            case shadergraph::VarType::Int:
                std::static_pointer_cast<shadergraph::IntVal>(u_var.val)->x = s.i;
                break;
            case shadergraph::VarType::Float:
                std::static_pointer_cast<shadergraph::FloatVal>(u_var.val)->x = s.f;
                break;
            case shadergraph::VarType::Float2:
                memcpy(std::static_pointer_cast<shadergraph::Float2Val>(u_var.val)->xy, s.f2, sizeof(float) * 2);
                break;
            case shadergraph::VarType::Float3:
                memcpy(std::static_pointer_cast<shadergraph::Float3Val>(u_var.val)->xyz, s.f3, sizeof(float) * 3);
                break;
            case shadergraph::VarType::Float4:
                memcpy(std::static_pointer_cast<shadergraph::Float4Val>(u_var.val)->xyzw, s.f4, sizeof(float) * 4);
                break;
            case shadergraph::VarType::Array:
                // todo
                break;
            default:
                assert(0);
            }

            break;
        }
    }

    auto type = front.get_type();
    if (type == rttr::type::get<node::CustomBlock>())
    {
        auto& src = static_cast<const node::CustomBlock&>(front);
        auto& dst = static_cast<shadergraph::block::CustomBlock&>(back);
        dst.SetCode(src.GetCode());
    }
    else if (type == rttr::type::get<node::Texture2DAsset>())
    {
        auto& src = static_cast<const node::Texture2DAsset&>(front);
        auto& dst = static_cast<shadergraph::block::Texture2DAsset&>(back);
        const_cast<node::Texture2DAsset&>(src).UpdateTexture(dev);
    }
    else if (type == rttr::type::get<node::SubGraph>())
    {
        auto& src = static_cast<const node::SubGraph&>(front);
        auto& dst = static_cast<shadergraph::block::SubGraph&>(back);
        dst.Setup(src.GetBackGraph(), src.GetInputVars(), src.GetOutputVars());
    }
}

}<commit_msg>[FIXED] init block's var value<commit_after>#include "shaderlab/ShaderAdapter.h"
#include "shaderlab/PinType.h"
#include "shaderlab/node/CustomBlock.h"
#include "shaderlab/node/Texture2DAsset.h"
#include "shaderlab/node/SubGraph.h"

#include <blueprint/Pin.h>
#include <blueprint/Node.h>

#include <dag/Graph.h>
#include <shadergraph/Block.h>
#include <shadergraph/ValueImpl.h>
#include <shadergraph/block/CustomBlock.h>
#include <shadergraph/block/Texture2DAsset.h>
#include <shadergraph/block/SubGraph.h>

#include <assert.h>

namespace shaderlab
{

int ShaderAdapter::TypeBackToFront(shadergraph::VarType type, int count)
{
    int ret = -1;

    switch (type)
    {
    case shadergraph::VarType::Invalid:
        ret = PIN_INVALID;
        break;
    case shadergraph::VarType::Dynamic:
        ret = PIN_DYNAMIC;
        break;
    case shadergraph::VarType::Bool:
        ret = PIN_BOOL;
        break;
    case shadergraph::VarType::Bool2:
        ret = PIN_BOOL2;
        break;
    case shadergraph::VarType::Bool3:
        ret = PIN_BOOL3;
        break;
    case shadergraph::VarType::Bool4:
        ret = PIN_BOOL4;
        break;
    case shadergraph::VarType::UInt:
        ret = PIN_UINT;
        break;
    case shadergraph::VarType::Int:
        ret = PIN_INT;
        break;
    case shadergraph::VarType::Int2:
        ret = PIN_INT2;
        break;
    case shadergraph::VarType::Int3:
        ret = PIN_INT3;
        break;
    case shadergraph::VarType::Int4:
        ret = PIN_INT4;
        break;
    case shadergraph::VarType::Float:
        ret = PIN_FLOAT;
        break;
    case shadergraph::VarType::Float2:
        ret = PIN_FLOAT2;
        break;
    case shadergraph::VarType::Float3:
        ret = PIN_FLOAT3;
        break;
    case shadergraph::VarType::Float4:
        ret = PIN_FLOAT4;
        break;
    case shadergraph::VarType::Matrix2:
        ret = PIN_MAT2;
        break;
    case shadergraph::VarType::Matrix3:
        ret = PIN_MAT3;
        break;
    case shadergraph::VarType::Matrix4:
        ret = PIN_MAT4;
        break;
    case shadergraph::VarType::Sampler2D:
        ret = PIN_SAMPLER_2D;
        break;
    case shadergraph::VarType::SamplerCube:
        ret = PIN_SAMPLER_CUBE;
        break;
    case shadergraph::VarType::Array:
        ret = PIN_ARRAY;
        break;
    case shadergraph::VarType::Struct:
        ret = PIN_STRUCT;
        break;
    case shadergraph::VarType::Function:
        ret = PIN_FUNCTION;
        break;
    default:
        assert(0);
    }

    return ret;
}

void ShaderAdapter::Front2Back(const bp::Node& front, dag::Node<shadergraph::Variant>& back,
                               const std::string& dir, const ur::Device& dev)
{
    // update uniforms
    auto& src = front.GetProps();
    auto& dst = static_cast<shadergraph::Block&>(back).GetVariants();
    for (auto& s_p : src)
    {
        auto& s = s_p.var;
        for (auto& d : dst)
        {
            if (s.name != d.name) {
                continue;
            }

            assert(d.type == shadergraph::VarType::Uniform);
            auto& u_var = std::static_pointer_cast<shadergraph::UniformVal>(d.val)->var;
            if (!u_var.val)
            {
            switch (u_var.type)
            {
            case shadergraph::VarType::Bool:
                u_var.val = std::make_shared<shadergraph::BoolVal>();
                break;
            case shadergraph::VarType::Int:
                u_var.val = std::make_shared<shadergraph::IntVal>();
                break;
            case shadergraph::VarType::Float:
                u_var.val = std::make_shared<shadergraph::FloatVal>();
                break;
            case shadergraph::VarType::Float2:
                u_var.val = std::make_shared<shadergraph::Float2Val>();
                break;
            case shadergraph::VarType::Float3:
                u_var.val = std::make_shared<shadergraph::Float3Val>();
                break;
            case shadergraph::VarType::Float4:
                u_var.val = std::make_shared<shadergraph::Float4Val>();
                break;
            case shadergraph::VarType::Array:
                // todo
                break;
            default:
                assert(0);
            }
            }

            switch (u_var.type)
            {
            case shadergraph::VarType::Bool:
                std::static_pointer_cast<shadergraph::BoolVal>(u_var.val)->x = s.b;
                break;
            case shadergraph::VarType::Int:
                std::static_pointer_cast<shadergraph::IntVal>(u_var.val)->x = s.i;
                break;
            case shadergraph::VarType::Float:
                std::static_pointer_cast<shadergraph::FloatVal>(u_var.val)->x = s.f;
                break;
            case shadergraph::VarType::Float2:
                memcpy(std::static_pointer_cast<shadergraph::Float2Val>(u_var.val)->xy, s.f2, sizeof(float) * 2);
                break;
            case shadergraph::VarType::Float3:
                memcpy(std::static_pointer_cast<shadergraph::Float3Val>(u_var.val)->xyz, s.f3, sizeof(float) * 3);
                break;
            case shadergraph::VarType::Float4:
                memcpy(std::static_pointer_cast<shadergraph::Float4Val>(u_var.val)->xyzw, s.f4, sizeof(float) * 4);
                break;
            case shadergraph::VarType::Array:
                // todo
                break;
            default:
                assert(0);
            }

            break;
        }
    }

    auto type = front.get_type();
    if (type == rttr::type::get<node::CustomBlock>())
    {
        auto& src = static_cast<const node::CustomBlock&>(front);
        auto& dst = static_cast<shadergraph::block::CustomBlock&>(back);
        dst.SetCode(src.GetCode());
    }
    else if (type == rttr::type::get<node::Texture2DAsset>())
    {
        auto& src = static_cast<const node::Texture2DAsset&>(front);
        auto& dst = static_cast<shadergraph::block::Texture2DAsset&>(back);
        const_cast<node::Texture2DAsset&>(src).UpdateTexture(dev);
    }
    else if (type == rttr::type::get<node::SubGraph>())
    {
        auto& src = static_cast<const node::SubGraph&>(front);
        auto& dst = static_cast<shadergraph::block::SubGraph&>(back);
        dst.Setup(src.GetBackGraph(), src.GetInputVars(), src.GetOutputVars());
    }
}

}<|endoftext|>


<commit_before>/**
 * WordRelQuery.cc
 *
 * Implement pattern matching for RelEx queries.
 * A "RelEx query" is a sentence such as "What did Bob eat?"
 * RelEx generates a dependency graph for tthis sentence,  replacing
 * "What" by "$qVar". Pattern matching is used to find an identical
 * dependency graph, for which $qVar would have a grounding; e.g.
 * "Bob ate cake", so that $qVar is grounded as "cake", thus "solving"
 * the query.
 *
 * The result of matching dependency graphs means that queries will be
 * interpreted very literally; the structure of a query sentence must
 * closely resemble the structure of a sentence in the corpus; otherwise,
 * no matching response will be found.  Some generality can be obtained
 * by converting dependency graphs into semantic triples; the code below
 * show work for that case as well.
 *
 * Copyright (c) 2008 Linas Vepstas <linas@linas.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License v3 as
 * published by the Free Software Foundation and including the exceptions
 * at http://opencog.org/wiki/Licenses
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, write to:
 * Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "WordRelQuery.h"

#include <stdio.h>

#include <opencog/atomspace/ForeachChaseLink.h>
#include <opencog/atomspace/Node.h>

using namespace opencog;

WordRelQuery::WordRelQuery(void)
{
	atom_space = NULL;
	pme = NULL;
}

WordRelQuery::~WordRelQuery()
{
	if (pme) delete pme;
	pme = NULL;
}

// #define DEBUG
#ifdef DEBUG
static void prt(Atom *atom)
{
   std::string str = atom->toString();
   printf ("%s\n", str.c_str());
}
#endif

/* ======================================================== */
/* Routines used to determine if an assertion is a query.
 * XXX This algo is flawed, fragile, but simple.
 * XXX It almost surely would be much better to implement this in
 * scheme instead of C++.
 */

/**
 * Return true, if atom is of type Node, and if the node
 * name is "match_name" (currently hard-coded as _$qVar)
 */
bool WordRelQuery::is_qVar(Handle word_prop)
{
	Atom *atom = TLB::getAtom(word_prop);
	if (DEFINED_LINGUISTIC_CONCEPT_NODE != atom->getType()) return false;

	Node *n = static_cast<Node *>(atom);
	const std::string& name = n->getName();
	const char * str = name.c_str();
	if (0 == strcmp(str, "who"))
		return true;
	if (0 == strcmp(str, "what"))
		return true;
	if (0 == strcmp(str, "when"))
		return true;
	if (0 == strcmp(str, "where"))
		return true;
	if (0 == strcmp(str, "why"))
		return true;

	return false;
}

/**
 * Search for queries.
 * XXX This implementation is kinda-wrong, its very specific
 * to the structure of the relex-to-opencog conversion, and
 * is fragile, if that structure changes.
 */
bool WordRelQuery::is_word_a_query(Handle word_inst)
{
	return foreach_binary_link(word_inst, INHERITANCE_LINK, &WordRelQuery::is_qVar, this);
}

/* ======================================================== */
/* Routines to help put the query into normal form. */

/**
 * Return true, if the node is, for example, #singluar or #masculine.
 */
bool WordRelQuery::is_ling_cncpt(Atom *atom)
{
	if (DEFINED_LINGUISTIC_CONCEPT_NODE == atom->getType()) return true;
	return false;
}

bool WordRelQuery::is_cncpt(Atom *atom)
{
	if (CONCEPT_NODE == atom->getType()) return true;
	return false;
}

void WordRelQuery::add_to_predicate(Handle ah)
{
	/* scan for duplicates, and don't add them */
	std::vector<Handle>::const_iterator i;
	for (i = normed_predicate.begin();
	     i != normed_predicate.end(); i++)
	{
		Handle h = *i;
		if (h == ah) return;
	}
	normed_predicate.push_back(ah);
}

void WordRelQuery::add_to_vars(Handle ah)
{
	/* scan for duplicates, and don't add them */
	std::vector<Handle>::const_iterator i;
	for (i = bound_vars.begin();
	     i != bound_vars.end(); i++)
	{
		Handle h = *i;
		if (h == ah) return;
	}
	bound_vars.push_back(ah);
}

/**
 * Look to see if word instance is a bound variable,
 * if it is, then add it to the variables list.
 */
bool WordRelQuery::find_vars(Handle word_instance)
{
	foreach_outgoing_handle(word_instance, &WordRelQuery::find_vars, this);

	bool qvar = is_word_a_query(word_instance);
	if (!qvar) return false;

	add_to_vars(word_instance);
	return false;
}

/* ======================================================== */
/* runtime matching routines */

/**
 * Do two word instances have the same word lemma (word root form)?
 * Return true if they are are NOT (that is, if they
 * are mismatched).
 *
 * Current NLP structure relating word-instances to lemmas is:
 * (LemmaLink (stv 1.0 1.0)
 *    (WordInstanceNode "threw@e5649eb8-eac5-48ae-adab-41e351e29e4e")
 *    (WordNode "throw")
 * )
 * (ReferenceLink (stv 1.0 1.0)
 *    (WordInstanceNode "threw@e5649eb8-eac5-48ae-adab-41e351e29e4e")
 *    (WordNode "threw")
 * )
 */
bool WordRelQuery::word_instance_match(Atom *aa, Atom *ab)
{
	// printf ("concept comp "); prt(aa);
	// printf ("          to "); prt(ab);

	// If they're the same atom, then clearly they match.
	if (aa == ab) return false;

	// Look for incoming links that are LemmaLinks.
	// The word lemma should be at the far end.
	Atom *ca = fl.follow_binary_link(aa, LEMMA_LINK);
	Atom *cb = fl.follow_binary_link(ab, LEMMA_LINK);

	// printf ("gen comp %d ", ca==cb); prt(ca);
	// printf ("        to "); prt(cb);

	if (ca == cb) return false;
	return true;
}

/**
 * Does word instance aa have the word lemma (word root form) ab?
 * Return true if it does NOT (that is, if its mis-matched).
 * This routine is used to compare word instances of recently-read
 * sentences to those that occur in a repository of 'facts'.
 */
bool WordRelQuery::word_inst_to_word_match(Atom *aa, Atom *ab)
{
	// printf ("concept comp "); prt(aa);
	// printf ("          to "); prt(ab);

	// If they're the same atom, then clearly they match.
	if (aa == ab) return false;

	// Look for incoming links that are LemmaLinks.
	// The word lemma should be at the far end.
	Atom *ca = fl.follow_binary_link(aa, LEMMA_LINK);

	// printf ("gen comp %d ", ca==ab); prt(ca);
	// printf ("        to "); prt(ab);

	if (ca == ab) return false;
	return true;
}

#ifdef DEAD_CODE_BUT_DONT_DELETE_JUST_YET
/**
 * Return the word string associated with a word instance.
 * xxxxxxxxx this routine is never called!
 *
 * XXX
 * The actual determination of whether some concept is
 * represented by some word is fragily dependent on the
 * actual nature of concept representation in the
 * relex-to-opencog mapping. Until this is placed into
 * concrete, its inherently fragile.  This is subject
 * to change, if the relex-to-opencog mapping changes.
 *
 * Current mapping is:
 *   ReferenceLink
 *      WordInstanceNode "bark@e798a7dc"
 *      WordNode "bark"
 *
 */
const char * WordRelQuery::get_word_instance(Atom *atom)
{
	// We want the word-node associated with this word instance.
	Atom *wrd = fl.follow_binary_link(atom, REFERENCE_LINK);
	if (!wrd) return NULL;

	if (WORD_NODE != wrd->getType()) return NULL;

	Node *n = static_cast<Node *>(wrd);
	const std::string& name = n->getName();

	return name.c_str();
}
#endif /* DEAD_CODE_BUT_DONT_DELETE_JUST_YET */

/**
 * Are two nodes "equivalent", as far as the opencog representation
 * of RelEx expressions are concerned?
 *
 * Return true to signify a mismatch,
 * Return false to signify equivalence.
 */
bool WordRelQuery::node_match(Node *npat, Node *nsoln)
{
	// If we are here, then we are comparing nodes.
	// The result of comparing nodes depends on the
	// node types.
	Type pattype = npat->getType();
	Type soltype = nsoln->getType();
	if ((pattype != soltype) &&
	    (WORD_NODE != soltype) &&
	    (WORD_INSTANCE_NODE != soltype)) return true;

	// DefinedLinguisticRelation nodes must match exactly;
	// so if we are here, there's already a mismatch.
	if (DEFINED_LINGUISTIC_RELATIONSHIP_NODE == soltype) return true;

	// Word instances match only if they have the same word lemma.
	if (WORD_INSTANCE_NODE == soltype)
	{
		bool mismatch = word_instance_match(npat, nsoln);
		// printf("tree_comp word instance mismatch=%d\n", mismatch);
		return mismatch;
	}

	// A word-instance in the pattern (question) might match a word-node
	// (concept) in a general-knowledge DB.
	if ((WORD_INSTANCE_NODE == pattype) &&
	    (WORD_NODE == soltype))
	{
		bool mismatch = word_inst_to_word_match(npat, nsoln);
		// printf("tree_comp word instance mismatch=%d\n", mismatch);
		return mismatch;
	}

	// XXX This code is never reached, due to above if statment.
	// This is a bit of dead code, which may need to be revived for more
	// proper relex matching ... or maybe not ...
	if (DEFINED_LINGUISTIC_CONCEPT_NODE == soltype)
	{
		/* We force agreement for gender, etc.
		 * be have more relaxed agreement for tense...
		 * i.e. match #past to #past_infinitive, etc.
		 */
		const char * sa = npat->getName().c_str();
		const char * sb = nsoln->getName().c_str();
printf("duude compare %s to %s\n", sa, sb);
		const char * ua = strchr(sa, '_');
		if (ua)
		{
			size_t len = ua-sa;
			char * s = (char *) alloca(len+1);
			strncpy(s,sa,len);
			s[len] = 0x0;
			sa = s;
		}
		const char * ub = strchr(sb, '_');
		if (ub)
		{
			size_t len = ub-sb;
			char * s = (char *) alloca(len+1);
			strncpy(s,sb,len);
			s[len] = 0x0;
			sb = s;
		}
		if (!strcmp(sa, sb)) return false;
		return true;
	}

	fprintf(stderr, "Error: unexpected node type %d %s\n", soltype,
	        classserver().getTypeName(soltype).c_str());

	std::string sa = npat->toString();
	std::string sb = nsoln->toString();
	fprintf (stderr, "unexpected comp %s\n"
	                 "             to %s\n", sa.c_str(), sb.c_str());

	return true;
}

/* ======================================================== */

bool WordRelQuery::solution(std::map<Handle, Handle> &pred_grounding,
                          std::map<Handle, Handle> &var_grounding)
{
	// Reject any solution where a variable is solved
	// by another variable (e.g. if there are multiple
	// questions in the corpus, and we just happened to
	// find one of them.)
	std::map<Handle, Handle>::const_iterator j;
	for (j=var_grounding.begin(); j != var_grounding.end(); j++)
	{
		std::pair<Handle, Handle> pv = *j;
		Handle soln = pv.second;

		// Solution is a word instance; is it also a query variable?
		bool qvar = is_word_a_query(soln);
		if (qvar) return false;
	}

	printf ("duude Found solution:\n");
	PatternMatchEngine::print_solution(pred_grounding, var_grounding);

	// And now for a cheesy hack to report the solution
	// XXX this needs to be replaced in the end, for now its just a cheesy
	// hack to pass data back to scheme.
	Handle hq = atom_space->addNode(ANCHOR_NODE, "# QUERY SOLUTION");
	Handle hv = bound_vars[0];
	Handle ha = var_grounding[hv];

	Atom *a = TLB::getAtom(ha);
	Atom *wrd = fl.follow_binary_link(a, REFERENCE_LINK);
	Node *n = dynamic_cast<Node *>(wrd);
	if (!n) return false;
	printf("duude answer=%s\n", n->getName().c_str());

	Handle hw = TLB::getHandle(wrd);
	atom_space->addLink(LIST_LINK, hq, hw);

	return false;
}

/* ===================== END OF FILE ===================== */
<commit_msg>look up word instances as appropriate<commit_after>/**
 * WordRelQuery.cc
 *
 * Implement pattern matching for RelEx queries.
 * A "RelEx query" is a sentence such as "What did Bob eat?"
 * RelEx generates a dependency graph for tthis sentence,  replacing
 * "What" by "$qVar". Pattern matching is used to find an identical
 * dependency graph, for which $qVar would have a grounding; e.g.
 * "Bob ate cake", so that $qVar is grounded as "cake", thus "solving"
 * the query.
 *
 * The result of matching dependency graphs means that queries will be
 * interpreted very literally; the structure of a query sentence must
 * closely resemble the structure of a sentence in the corpus; otherwise,
 * no matching response will be found.  Some generality can be obtained
 * by converting dependency graphs into semantic triples; the code below
 * show work for that case as well.
 *
 * Copyright (c) 2008 Linas Vepstas <linas@linas.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License v3 as
 * published by the Free Software Foundation and including the exceptions
 * at http://opencog.org/wiki/Licenses
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, write to:
 * Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "WordRelQuery.h"

#include <stdio.h>

#include <opencog/atomspace/ForeachChaseLink.h>
#include <opencog/atomspace/Node.h>

using namespace opencog;

WordRelQuery::WordRelQuery(void)
{
	atom_space = NULL;
	pme = NULL;
}

WordRelQuery::~WordRelQuery()
{
	if (pme) delete pme;
	pme = NULL;
}

// #define DEBUG
#ifdef DEBUG
static void prt(Atom *atom)
{
   std::string str = atom->toString();
   printf ("%s\n", str.c_str());
}
#endif

/* ======================================================== */
/* Routines used to determine if an assertion is a query.
 * XXX This algo is flawed, fragile, but simple.
 * XXX It almost surely would be much better to implement this in
 * scheme instead of C++.
 */

/**
 * Return true, if atom is of type Node, and if the node
 * name is "match_name" (currently hard-coded as _$qVar)
 */
bool WordRelQuery::is_qVar(Handle word_prop)
{
	Atom *atom = TLB::getAtom(word_prop);
	if (DEFINED_LINGUISTIC_CONCEPT_NODE != atom->getType()) return false;

	Node *n = static_cast<Node *>(atom);
	const std::string& name = n->getName();
	const char * str = name.c_str();
	if (0 == strcmp(str, "who"))
		return true;
	if (0 == strcmp(str, "what"))
		return true;
	if (0 == strcmp(str, "when"))
		return true;
	if (0 == strcmp(str, "where"))
		return true;
	if (0 == strcmp(str, "why"))
		return true;

	return false;
}

/**
 * Search for queries.
 * XXX This implementation is kinda-wrong, its very specific
 * to the structure of the relex-to-opencog conversion, and
 * is fragile, if that structure changes.
 */
bool WordRelQuery::is_word_a_query(Handle word_inst)
{
	return foreach_binary_link(word_inst, INHERITANCE_LINK, &WordRelQuery::is_qVar, this);
}

/* ======================================================== */
/* Routines to help put the query into normal form. */

/**
 * Return true, if the node is, for example, #singluar or #masculine.
 */
bool WordRelQuery::is_ling_cncpt(Atom *atom)
{
	if (DEFINED_LINGUISTIC_CONCEPT_NODE == atom->getType()) return true;
	return false;
}

bool WordRelQuery::is_cncpt(Atom *atom)
{
	if (CONCEPT_NODE == atom->getType()) return true;
	return false;
}

void WordRelQuery::add_to_predicate(Handle ah)
{
	/* scan for duplicates, and don't add them */
	std::vector<Handle>::const_iterator i;
	for (i = normed_predicate.begin();
	     i != normed_predicate.end(); i++)
	{
		Handle h = *i;
		if (h == ah) return;
	}
	normed_predicate.push_back(ah);
}

void WordRelQuery::add_to_vars(Handle ah)
{
	/* scan for duplicates, and don't add them */
	std::vector<Handle>::const_iterator i;
	for (i = bound_vars.begin();
	     i != bound_vars.end(); i++)
	{
		Handle h = *i;
		if (h == ah) return;
	}
	bound_vars.push_back(ah);
}

/**
 * Look to see if word instance is a bound variable,
 * if it is, then add it to the variables list.
 */
bool WordRelQuery::find_vars(Handle word_instance)
{
	foreach_outgoing_handle(word_instance, &WordRelQuery::find_vars, this);

	bool qvar = is_word_a_query(word_instance);
	if (!qvar) return false;

	add_to_vars(word_instance);
	return false;
}

/* ======================================================== */
/* runtime matching routines */

/**
 * Do two word instances have the same word lemma (word root form)?
 * Return true if they are are NOT (that is, if they
 * are mismatched).
 *
 * Current NLP structure relating word-instances to lemmas is:
 * (LemmaLink (stv 1.0 1.0)
 *    (WordInstanceNode "threw@e5649eb8-eac5-48ae-adab-41e351e29e4e")
 *    (WordNode "throw")
 * )
 * (ReferenceLink (stv 1.0 1.0)
 *    (WordInstanceNode "threw@e5649eb8-eac5-48ae-adab-41e351e29e4e")
 *    (WordNode "threw")
 * )
 */
bool WordRelQuery::word_instance_match(Atom *aa, Atom *ab)
{
	// printf ("concept comp "); prt(aa);
	// printf ("          to "); prt(ab);

	// If they're the same atom, then clearly they match.
	if (aa == ab) return false;

	// Look for incoming links that are LemmaLinks.
	// The word lemma should be at the far end.
	Atom *ca = fl.follow_binary_link(aa, LEMMA_LINK);
	Atom *cb = fl.follow_binary_link(ab, LEMMA_LINK);

	// printf ("gen comp %d ", ca==cb); prt(ca);
	// printf ("        to "); prt(cb);

	if (ca == cb) return false;
	return true;
}

/**
 * Does word instance aa have the word lemma (word root form) ab?
 * Return true if it does NOT (that is, if its mis-matched).
 * This routine is used to compare word instances of recently-read
 * sentences to those that occur in a repository of 'facts'.
 */
bool WordRelQuery::word_inst_to_word_match(Atom *aa, Atom *ab)
{
	// printf ("concept comp "); prt(aa);
	// printf ("          to "); prt(ab);

	// If they're the same atom, then clearly they match.
	if (aa == ab) return false;

	// Look for incoming links that are LemmaLinks.
	// The word lemma should be at the far end.
	Atom *ca = fl.follow_binary_link(aa, LEMMA_LINK);

	// printf ("gen comp %d ", ca==ab); prt(ca);
	// printf ("        to "); prt(ab);

	if (ca == ab) return false;
	return true;
}

#ifdef DEAD_CODE_BUT_DONT_DELETE_JUST_YET
/**
 * Return the word string associated with a word instance.
 * xxxxxxxxx this routine is never called!
 *
 * XXX
 * The actual determination of whether some concept is
 * represented by some word is fragily dependent on the
 * actual nature of concept representation in the
 * relex-to-opencog mapping. Until this is placed into
 * concrete, its inherently fragile.  This is subject
 * to change, if the relex-to-opencog mapping changes.
 *
 * Current mapping is:
 *   ReferenceLink
 *      WordInstanceNode "bark@e798a7dc"
 *      WordNode "bark"
 *
 */
const char * WordRelQuery::get_word_instance(Atom *atom)
{
	// We want the word-node associated with this word instance.
	Atom *wrd = fl.follow_binary_link(atom, REFERENCE_LINK);
	if (!wrd) return NULL;

	if (WORD_NODE != wrd->getType()) return NULL;

	Node *n = static_cast<Node *>(wrd);
	const std::string& name = n->getName();

	return name.c_str();
}
#endif /* DEAD_CODE_BUT_DONT_DELETE_JUST_YET */

/**
 * Are two nodes "equivalent", as far as the opencog representation
 * of RelEx expressions are concerned?
 *
 * Return true to signify a mismatch,
 * Return false to signify equivalence.
 */
bool WordRelQuery::node_match(Node *npat, Node *nsoln)
{
	// If we are here, then we are comparing nodes.
	// The result of comparing nodes depends on the
	// node types.
	Type pattype = npat->getType();
	Type soltype = nsoln->getType();
	if ((pattype != soltype) &&
	    (WORD_NODE != soltype) &&
	    (WORD_INSTANCE_NODE != soltype)) return true;

	// DefinedLinguisticRelation nodes must match exactly;
	// so if we are here, there's already a mismatch.
	if (DEFINED_LINGUISTIC_RELATIONSHIP_NODE == soltype) return true;

	// Word instances match only if they have the same word lemma.
	if (WORD_INSTANCE_NODE == soltype)
	{
		bool mismatch = word_instance_match(npat, nsoln);
		// printf("tree_comp word instance mismatch=%d\n", mismatch);
		return mismatch;
	}

	// A word-instance in the pattern (question) might match a word-node
	// (concept) in a general-knowledge DB.
	if ((WORD_INSTANCE_NODE == pattype) &&
	    (WORD_NODE == soltype))
	{
		bool mismatch = word_inst_to_word_match(npat, nsoln);
		// printf("tree_comp word instance mismatch=%d\n", mismatch);
		return mismatch;
	}

	// XXX This code is never reached, due to above if statment.
	// This is a bit of dead code, which may need to be revived for more
	// proper relex matching ... or maybe not ...
	if (DEFINED_LINGUISTIC_CONCEPT_NODE == soltype)
	{
		/* We force agreement for gender, etc.
		 * be have more relaxed agreement for tense...
		 * i.e. match #past to #past_infinitive, etc.
		 */
		const char * sa = npat->getName().c_str();
		const char * sb = nsoln->getName().c_str();
printf("duude compare %s to %s\n", sa, sb);
		const char * ua = strchr(sa, '_');
		if (ua)
		{
			size_t len = ua-sa;
			char * s = (char *) alloca(len+1);
			strncpy(s,sa,len);
			s[len] = 0x0;
			sa = s;
		}
		const char * ub = strchr(sb, '_');
		if (ub)
		{
			size_t len = ub-sb;
			char * s = (char *) alloca(len+1);
			strncpy(s,sb,len);
			s[len] = 0x0;
			sb = s;
		}
		if (!strcmp(sa, sb)) return false;
		return true;
	}

	fprintf(stderr, "Error: unexpected node type %d %s\n", soltype,
	        classserver().getTypeName(soltype).c_str());

	std::string sa = npat->toString();
	std::string sb = nsoln->toString();
	fprintf (stderr, "unexpected comp %s\n"
	                 "             to %s\n", sa.c_str(), sb.c_str());

	return true;
}

/* ======================================================== */

bool WordRelQuery::solution(std::map<Handle, Handle> &pred_grounding,
                          std::map<Handle, Handle> &var_grounding)
{
	// Reject any solution where a variable is solved
	// by another variable (e.g. if there are multiple
	// questions in the corpus, and we just happened to
	// find one of them.)
	std::map<Handle, Handle>::const_iterator j;
	for (j=var_grounding.begin(); j != var_grounding.end(); j++)
	{
		std::pair<Handle, Handle> pv = *j;
		Handle soln = pv.second;

		// Solution is a word instance; is it also a query variable?
		bool qvar = is_word_a_query(soln);
		if (qvar) return false;
	}

	printf ("duude Found solution:\n");
	PatternMatchEngine::print_solution(var_grounding, pred_grounding);

	// And now for a cheesy hack to report the solution
	// XXX this needs to be replaced in the end, for now its just a cheesy
	// hack to pass data back to scheme.
	Handle hq = atom_space->addNode(ANCHOR_NODE, "# QUERY SOLUTION");
	Handle hv = bound_vars[0];
	Handle ha = var_grounding[hv];

	Atom *wrd = TLB::getAtom(ha);
	if (WORD_INSTANCE_NODE == wrd->getType())
	{
		wrd = fl.follow_binary_link(wrd, REFERENCE_LINK);
	}
	Node *n = dynamic_cast<Node *>(wrd);
	if (!n) return false;
	printf("duude answer=%s\n", n->getName().c_str());

	Handle hw = TLB::getHandle(wrd);
	atom_space->addLink(LIST_LINK, hq, hw);

	return false;
}

/* ===================== END OF FILE ===================== */
<|endoftext|>


<commit_before>/**
 * @file
 */
#pragma once

#include "libbirch/external.hpp"
#include "libbirch/memory.hpp"
#include "libbirch/thread.hpp"
#include "libbirch/type.hpp"
#include "libbirch/Shape.hpp"
#include "libbirch/Iterator.hpp"
#include "libbirch/Eigen.hpp"

namespace libbirch {
/**
 * Array.
 *
 * @tparam T Value type.
 * @tparam F Shape type.
 */
template<class T, class F>
class Array {
  template<class U, class G> friend class Array;
public:
  using this_type = Array<T,F>;
  using value_type = T;
  using shape_type = F;
  using eigen_type = typename eigen_type<this_type>::type;
  using eigen_stride_type = typename eigen_stride_type<this_type>::type;

  /**
   * Constructor.
   */
  Array() :
      shape(),
      buffer(nullptr),
      isView(false) {
    assert(shape.volume() == 0);
  }

  /**
   * Constructor.
   *
   * @param shape Shape.
   */
  Array(const F& shape) :
      shape(shape),
      buffer(nullptr),
      isView(false) {
    allocate();
    initialize();
  }

  /**
   * Constructor.
   *
   * @tparam ...Args Constructor parameter types.
   *
   * @param shape Shape.
   * @param args Constructor arguments.
   */
  template<class... Args, std::enable_if_t<std::is_constructible<T,Args...>::value,int> = 0>
  Array(const F& shape, Args&&... args) :
      shape(shape),
      buffer(nullptr),
      isView(false) {
    allocate();
    initialize(std::forward<Args>(args)...);
  }

  /**
   * Constructor.
   *
   * @param shape Shape.
   * @param values Values.
   */
  template<class G = F, std::enable_if_t<G::count() == 1,int> = 0>
  Array(const std::initializer_list<T>& values) :
      shape(values.size()),
      buffer(nullptr),
      isView(false) {
    allocate();
    std::uninitialized_copy(values.begin(), values.end(), begin());
  }

  /**
   * Constructor.
   *
   * @param shape Shape.
   * @param values Values.
   */
  template<class G = F, std::enable_if_t<G::count() == 2,int> = 0>
  Array(const std::initializer_list<std::initializer_list<T>>& values) :
      shape(values.size(), values.begin()->size()),
      buffer(nullptr),
      isView(false) {
    allocate();
    auto ptr = buf();
    for (auto row : values) {
      for (auto value : row) {
        new (ptr++) T(value);
      }
    }
  }

  /**
   * Constructor.
   *
   * @param l Lambda called to construct each element.
   * @param shape Shape.
   */
  template<class L>
  Array(const L& l, const F& shape) :
      shape(shape),
      buffer(nullptr),
      isView(false) {
    allocate();
    int64_t n = 0;
    for (auto iter = begin(); iter != end(); ++iter) {
      new (&*iter) T(l(n++));
    }
  }

  /**
   * Copy constructor.
   */
  Array(const Array& o) :
      shape(o.shape.compact()),
      buffer(nullptr),
      isView(false) {
    allocate();
    uninitialized_copy(o);
  }

  /**
   * Generic copy constructor.
   */
  template<class U, class G, std::enable_if_t<F::count() == G::count() &&
      std::is_convertible<U,T>::value,int> = 0>
  Array(const Array<U,G>& o) :
      shape(o.shape.compact()),
      buffer(nullptr),
      isView(false) {
    allocate();
    uninitialized_copy(o);
  }

  /**
   * Move constructor.
   */
  Array(Array&& o) : Array() {
    if (!o.isView) {
      swap(o);
    } else {
      shape = o.shape.compact();
      allocate();
      uninitialized_copy(o);
    }
  }

  /**
   * Destructor.
   */
  ~Array() {
    release();
  }

  /**
   * Copy assignment.
   */
  Array& operator=(const Array& o) {
    assign(o);
    return *this;
  }

  /**
   * Move assignment.
   */
  Array& operator=(Array&& o) {
    if (!isView && !o.isView) {
      swap(o);
    } else {
      assign(o);
    }
    return *this;
  }

  /**
   * Copy assignment. For a view the shapes of the two arrays must
   * conform, otherwise a resize is permitted.
   */
  void assign(const Array<T,F>& o) {
    if (isView) {
      assert(o.shape.conforms(shape) && "array sizes are different");
      copy(o);
    } else {
      Array<T,F> tmp(o);
      swap(tmp);
    }
  }

  /**
   * Number of elements.
   */
  auto size() const {
    return shape.size();
  }

  /**
   * Number of elements allocated.
   */
  auto volume() const {
    return shape.volume();
  }

  /**
   * @name Element access.
   */
  ///@{
  /**
   * Iterator pointing to the first element.
   *
   * Iterators are used to access the elements of an array sequentially.
   * Elements are visited in the order in which they are stored in memory;
   * the rightmost dimension is the fastest moving (for a matrix, this is
   * "row major" order).
   */
  Iterator<T,F> begin() {
    return Iterator<T,F>(buf(), shape);
  }
  Iterator<T,F> begin() const {
    return Iterator<T,F>(buf(), shape);
  }
  Iterator<T,F> end() {
    return begin() + size();
  }
  Iterator<T,F> end() const {
    return begin() + size();
  }

  /**
   * Slice.
   *
   * @tparam V Slice type.
   *
   * @param slice Slice.
   *
   * @return The resulting view or element.
   */
  template<class V, std::enable_if_t<V::rangeCount() != 0,int> = 0>
  auto slice(const V& slice) {
    return Array<T,decltype(shape(slice))>(shape(slice), buffer,
        shape.serial(slice));
  }
  template<class V, std::enable_if_t<V::rangeCount() != 0,int> = 0>
  auto slice(const V& slice) const {
    return Array<T,decltype(shape(slice))>(shape(slice), buffer,
        shape.serial(slice));
  }
  template<class V, std::enable_if_t<V::rangeCount() == 0,int> = 0>
  auto& slice(const V& slice) {
    return *(buf() + shape.serial(slice));
  }
  template<class V, std::enable_if_t<V::rangeCount() == 0,int> = 0>
  auto slice(const V& slice) const {
    return *(buf() + shape.serial(slice));
  }

  /**
   * Slice.
   *
   * @tparam ...Args Slice argument types.
   *
   * @param args... Slice arguments.
   *
   * @return The resulting view or element.
   */
  template<class... Args>
  decltype(auto) operator()(Args&&... args) {
    return slice(make_slice(std::forward<Args>(args)...));
  }
  template<class... Args>
  decltype(auto) operator()(Args&&... args) const {
    return slice(make_slice(std::forward<Args>(args)...));
  }
  ///@}

  /**
   * Compare.
   */
  template<class U, class G>
  bool operator==(const Array<U,G>& o) const {
    return std::equal(begin(), end(), o.begin());
  }
  template<class U, class G>
  bool operator!=(const Array<U,G>& o) const {
    return !(*this == o);
  }

  /**
   * @name Resize
   */
  ///@{
  /**
   * For a one-dimensional array, push an element onto the end. This increases
   * the array size by one.
   *
   * @param x Value.
   */
  void push(const T& x) {
    insert(size(), x);
  }

  /**
   * For a one-dimensional array, insert an element at a given position. This
   * increases the array size by one.
   *
   * @param i Position.
   * @param x Value.
   */
  void insert(const int64_t i, const T& x) {
    static_assert(F::count() == 1, "can only enlarge one-dimensional arrays");
    assert(!isView);

    auto n = size();
    auto s = F(n + 1);
    if (!buffer) {
      Array<T,F> tmp(s, x);
      swap(tmp);
    } else {
      buffer = (T*)std::realloc((void*)buffer, s.volume()*sizeof(T));
      std::memmove((void*)(buf() + i + 1), (void*)(buf() + i), (n - i)*sizeof(T));
      new (buf() + i) T(x);
      shape = s;
    }
  }

  /**
   * For a one-dimensional array, erase elements from a given position. This
   * decreases the array size by the number of elements.
   *
   * @param i Position.
   * @param len Number of elements to erase.
   */
  void erase(const int64_t i, const int64_t len = 1) {
    static_assert(F::count() == 1, "can only shrink one-dimensional arrays");
    assert(!isView);
    assert(len > 0);
    assert(size() >= len);

    auto n = size();
    auto s = F(n - len);
    if (s.size() == 0) {
      release();
    } else {
      for (int j = i; j < i + len; ++j) {
        buf()[j].~T();
      }
      std::memmove((void*)(buf() + i), (void*)(buf() + i + len), (n - len - i)*sizeof(T));
      buffer = (T*)std::realloc((void*)buffer, s.volume()*sizeof(T));
    }
    shape = s;
  }
  ///@}

  /**
   * @name Eigen integration
   */
  ///@{
  template<class Check = T, std::enable_if_t<std::is_arithmetic<Check>::value,int> = 0>
  operator eigen_type() const {
    return toEigen();
  }

  template<class Check = T, std::enable_if_t<std::is_arithmetic<Check>::value,int> = 0>
  auto toEigen() {
    return eigen_type(buf(), rows(), cols(), eigen_stride_type(rowStride(),
        colStride()));
  }

  template<class Check = T, std::enable_if_t<std::is_arithmetic<Check>::value,int> = 0>
  auto toEigen() const {
    return eigen_type(buf(), rows(), cols(), eigen_stride_type(rowStride(),
        colStride()));
  }

  /**
   * Construct from Eigen Matrix expression.
   */
  template<class EigenType, std::enable_if_t<is_eigen_compatible<this_type,EigenType>::value,int> = 0>
  Array(const Eigen::MatrixBase<EigenType>& o) :
      shape(o.rows(), o.cols()),
      buffer(nullptr),
      isView(false) {
    allocate();
    toEigen() = o;
  }

  /**
   * Construct from Eigen DiagonalWrapper expression.
   */
  template<class EigenType, std::enable_if_t<is_diagonal_compatible<this_type,EigenType>::value,int> = 0>
  Array(const Eigen::DiagonalWrapper<EigenType>& o) :
      shape(o.rows(), o.cols()),
      buffer(nullptr),
      isView(false) {
    allocate();
    toEigen() = o;
  }

  /**
   * Construct from Eigen TriangularView expression.
   */
  template<class EigenType, unsigned Mode, std::enable_if_t<is_triangle_compatible<this_type,EigenType>::value,int> = 0>
  Array(const Eigen::TriangularView<EigenType,Mode>& o) :
      shape(o.rows(), o.cols()),
      buffer(nullptr),
      isView(false) {
    allocate();
    toEigen() = o;
  }

  /**
   * Number of rows. For a vectoor, this is the length.
   */
  auto rows() const {
    assert(1 <= F::count() && F::count() <= 2);
    return shape.length(0);
  }

  /**
   * Number of columns. For a vector, this is 1.
   */
  auto cols() const {
    assert(1 <= F::count() && F::count() <= 2);
    return F::count() == 1 ? 1 : shape.length(1);
  }

  /**
   * Stride between rows.
   */
  auto rowStride() const {
    assert(1 <= F::count() && F::count() <= 2);
    return F::count() == 1 ? shape.volume() : shape.stride(0);
  }

  /**
   * Stride between columns.
   */
  auto colStride() const {
    assert(1 <= F::count() && F::count() <= 2);
    return F::count() == 1 ? shape.stride(0) : shape.stride(1);
  }
  ///@}

private:
  /**
   * Constructor for views.
   */
  Array(const F& shape, T* buffer, int64_t offset) :
      shape(shape),
      buffer(buffer + offset),
      isView(true) {
    //
  }

  /**
   * Raw pointer to underlying buffer.
   */
  T* buf() const {
    return buffer;
  }

  /**
   * Swap with another array.
   */
  void swap(Array<T,F>& o) {
    assert(!isView);
    assert(!o.isView);
    std::swap(shape, o.shape);
    std::swap(buffer, o.buffer);
  }

  /**
   * Allocate memory for array, leaving uninitialized.
   */
  void allocate() {
    assert(!buffer);
    size_t bytes = volume()*sizeof(T);
    if (bytes > 0) {
      buffer = (T*)std::malloc(bytes);
    }
  }

  /**
   * Deallocate memory of array.
   */
  void release() {
    if (!isView) {
      size_t bytes = volume()*sizeof(T);
      if (bytes > 0) {
        std::destroy(begin(), end());
        std::free(buffer);
        buffer = nullptr;
      }
    }
  }

  /**
   * Initialize allocated memory.
   *
   * @param args Constructor arguments.
   */
  template<class ... Args, std::enable_if_t<std::is_constructible<T,Args...>::value,int> = 0>
  void initialize(Args&&... args) {
    auto iter = begin();
    auto last = end();
    for (; iter != last; ++iter) {
      new (&*iter) T(std::forward<Args>(args)...);
    }
  }

  /**
   * Assign from another array.
   */
  template<class U>
  void copy(const U& o) {
    auto n = std::min(size(), o.size());
    auto begin1 = o.begin();
    auto end1 = begin1 + n;
    auto begin2 = begin();
    auto end2 = begin2 + n;
    if (inside(begin1, end1, begin2)) {
      std::copy_backward(begin1, end1, end2);
    } else {
      std::copy(begin1, end1, begin2);
    }
  }

  /**
   * Copy from another array.
   */
  template<class U>
  void uninitialized_copy(const U& o) {
    auto n = std::min(size(), o.size());
    auto begin1 = o.begin();
    auto end1 = begin1 + n;
    auto begin2 = begin();
    for (; begin1 != end1; ++begin1, ++begin2) {
      new (&*begin2) T(*begin1);
    }
  }

  /**
   * Shape.
   */
  F shape;

  /**
   * Buffer.
   */
  T* buffer;

  /**
   * Is this a view of another array? A view has stricter assignment
   * semantics, as it cannot be resized or moved.
   */
  bool isView;
};

/**
 * Default array for `D` dimensions.
 */
template<class T, int D>
using DefaultArray = Array<T,typename DefaultShape<D>::type>;

}
<commit_msg>Added .noalias() when initializing Array from Eigen matrix.<commit_after>/**
 * @file
 */
#pragma once

#include "libbirch/external.hpp"
#include "libbirch/memory.hpp"
#include "libbirch/thread.hpp"
#include "libbirch/type.hpp"
#include "libbirch/Shape.hpp"
#include "libbirch/Iterator.hpp"
#include "libbirch/Eigen.hpp"

namespace libbirch {
/**
 * Array.
 *
 * @tparam T Value type.
 * @tparam F Shape type.
 */
template<class T, class F>
class Array {
  template<class U, class G> friend class Array;
public:
  using this_type = Array<T,F>;
  using value_type = T;
  using shape_type = F;
  using eigen_type = typename eigen_type<this_type>::type;
  using eigen_stride_type = typename eigen_stride_type<this_type>::type;

  /**
   * Constructor.
   */
  Array() :
      shape(),
      buffer(nullptr),
      isView(false) {
    assert(shape.volume() == 0);
  }

  /**
   * Constructor.
   *
   * @param shape Shape.
   */
  Array(const F& shape) :
      shape(shape),
      buffer(nullptr),
      isView(false) {
    allocate();
    initialize();
  }

  /**
   * Constructor.
   *
   * @tparam ...Args Constructor parameter types.
   *
   * @param shape Shape.
   * @param args Constructor arguments.
   */
  template<class... Args, std::enable_if_t<std::is_constructible<T,Args...>::value,int> = 0>
  Array(const F& shape, Args&&... args) :
      shape(shape),
      buffer(nullptr),
      isView(false) {
    allocate();
    initialize(std::forward<Args>(args)...);
  }

  /**
   * Constructor.
   *
   * @param shape Shape.
   * @param values Values.
   */
  template<class G = F, std::enable_if_t<G::count() == 1,int> = 0>
  Array(const std::initializer_list<T>& values) :
      shape(values.size()),
      buffer(nullptr),
      isView(false) {
    allocate();
    std::uninitialized_copy(values.begin(), values.end(), begin());
  }

  /**
   * Constructor.
   *
   * @param shape Shape.
   * @param values Values.
   */
  template<class G = F, std::enable_if_t<G::count() == 2,int> = 0>
  Array(const std::initializer_list<std::initializer_list<T>>& values) :
      shape(values.size(), values.begin()->size()),
      buffer(nullptr),
      isView(false) {
    allocate();
    auto ptr = buf();
    for (auto row : values) {
      for (auto value : row) {
        new (ptr++) T(value);
      }
    }
  }

  /**
   * Constructor.
   *
   * @param l Lambda called to construct each element.
   * @param shape Shape.
   */
  template<class L>
  Array(const L& l, const F& shape) :
      shape(shape),
      buffer(nullptr),
      isView(false) {
    allocate();
    int64_t n = 0;
    for (auto iter = begin(); iter != end(); ++iter) {
      new (&*iter) T(l(n++));
    }
  }

  /**
   * Copy constructor.
   */
  Array(const Array& o) :
      shape(o.shape.compact()),
      buffer(nullptr),
      isView(false) {
    allocate();
    uninitialized_copy(o);
  }

  /**
   * Generic copy constructor.
   */
  template<class U, class G, std::enable_if_t<F::count() == G::count() &&
      std::is_convertible<U,T>::value,int> = 0>
  Array(const Array<U,G>& o) :
      shape(o.shape.compact()),
      buffer(nullptr),
      isView(false) {
    allocate();
    uninitialized_copy(o);
  }

  /**
   * Move constructor.
   */
  Array(Array&& o) : Array() {
    if (!o.isView) {
      swap(o);
    } else {
      shape = o.shape.compact();
      allocate();
      uninitialized_copy(o);
    }
  }

  /**
   * Destructor.
   */
  ~Array() {
    release();
  }

  /**
   * Copy assignment.
   */
  Array& operator=(const Array& o) {
    assign(o);
    return *this;
  }

  /**
   * Move assignment.
   */
  Array& operator=(Array&& o) {
    if (!isView && !o.isView) {
      swap(o);
    } else {
      assign(o);
    }
    return *this;
  }

  /**
   * Copy assignment. For a view the shapes of the two arrays must
   * conform, otherwise a resize is permitted.
   */
  void assign(const Array<T,F>& o) {
    if (isView) {
      assert(o.shape.conforms(shape) && "array sizes are different");
      copy(o);
    } else {
      Array<T,F> tmp(o);
      swap(tmp);
    }
  }

  /**
   * Number of elements.
   */
  auto size() const {
    return shape.size();
  }

  /**
   * Number of elements allocated.
   */
  auto volume() const {
    return shape.volume();
  }

  /**
   * @name Element access.
   */
  ///@{
  /**
   * Iterator pointing to the first element.
   *
   * Iterators are used to access the elements of an array sequentially.
   * Elements are visited in the order in which they are stored in memory;
   * the rightmost dimension is the fastest moving (for a matrix, this is
   * "row major" order).
   */
  Iterator<T,F> begin() {
    return Iterator<T,F>(buf(), shape);
  }
  Iterator<T,F> begin() const {
    return Iterator<T,F>(buf(), shape);
  }
  Iterator<T,F> end() {
    return begin() + size();
  }
  Iterator<T,F> end() const {
    return begin() + size();
  }

  /**
   * Slice.
   *
   * @tparam V Slice type.
   *
   * @param slice Slice.
   *
   * @return The resulting view or element.
   */
  template<class V, std::enable_if_t<V::rangeCount() != 0,int> = 0>
  auto slice(const V& slice) {
    return Array<T,decltype(shape(slice))>(shape(slice), buffer,
        shape.serial(slice));
  }
  template<class V, std::enable_if_t<V::rangeCount() != 0,int> = 0>
  auto slice(const V& slice) const {
    return Array<T,decltype(shape(slice))>(shape(slice), buffer,
        shape.serial(slice));
  }
  template<class V, std::enable_if_t<V::rangeCount() == 0,int> = 0>
  auto& slice(const V& slice) {
    return *(buf() + shape.serial(slice));
  }
  template<class V, std::enable_if_t<V::rangeCount() == 0,int> = 0>
  auto slice(const V& slice) const {
    return *(buf() + shape.serial(slice));
  }

  /**
   * Slice.
   *
   * @tparam ...Args Slice argument types.
   *
   * @param args... Slice arguments.
   *
   * @return The resulting view or element.
   */
  template<class... Args>
  decltype(auto) operator()(Args&&... args) {
    return slice(make_slice(std::forward<Args>(args)...));
  }
  template<class... Args>
  decltype(auto) operator()(Args&&... args) const {
    return slice(make_slice(std::forward<Args>(args)...));
  }
  ///@}

  /**
   * Compare.
   */
  template<class U, class G>
  bool operator==(const Array<U,G>& o) const {
    return std::equal(begin(), end(), o.begin());
  }
  template<class U, class G>
  bool operator!=(const Array<U,G>& o) const {
    return !(*this == o);
  }

  /**
   * @name Resize
   */
  ///@{
  /**
   * For a one-dimensional array, push an element onto the end. This increases
   * the array size by one.
   *
   * @param x Value.
   */
  void push(const T& x) {
    insert(size(), x);
  }

  /**
   * For a one-dimensional array, insert an element at a given position. This
   * increases the array size by one.
   *
   * @param i Position.
   * @param x Value.
   */
  void insert(const int64_t i, const T& x) {
    static_assert(F::count() == 1, "can only enlarge one-dimensional arrays");
    assert(!isView);

    auto n = size();
    auto s = F(n + 1);
    if (!buffer) {
      Array<T,F> tmp(s, x);
      swap(tmp);
    } else {
      buffer = (T*)std::realloc((void*)buffer, s.volume()*sizeof(T));
      std::memmove((void*)(buf() + i + 1), (void*)(buf() + i), (n - i)*sizeof(T));
      new (buf() + i) T(x);
      shape = s;
    }
  }

  /**
   * For a one-dimensional array, erase elements from a given position. This
   * decreases the array size by the number of elements.
   *
   * @param i Position.
   * @param len Number of elements to erase.
   */
  void erase(const int64_t i, const int64_t len = 1) {
    static_assert(F::count() == 1, "can only shrink one-dimensional arrays");
    assert(!isView);
    assert(len > 0);
    assert(size() >= len);

    auto n = size();
    auto s = F(n - len);
    if (s.size() == 0) {
      release();
    } else {
      for (int j = i; j < i + len; ++j) {
        buf()[j].~T();
      }
      std::memmove((void*)(buf() + i), (void*)(buf() + i + len), (n - len - i)*sizeof(T));
      buffer = (T*)std::realloc((void*)buffer, s.volume()*sizeof(T));
    }
    shape = s;
  }
  ///@}

  /**
   * @name Eigen integration
   */
  ///@{
  template<class Check = T, std::enable_if_t<std::is_arithmetic<Check>::value,int> = 0>
  operator eigen_type() const {
    return toEigen();
  }

  template<class Check = T, std::enable_if_t<std::is_arithmetic<Check>::value,int> = 0>
  auto toEigen() {
    return eigen_type(buf(), rows(), cols(), eigen_stride_type(rowStride(),
        colStride()));
  }

  template<class Check = T, std::enable_if_t<std::is_arithmetic<Check>::value,int> = 0>
  auto toEigen() const {
    return eigen_type(buf(), rows(), cols(), eigen_stride_type(rowStride(),
        colStride()));
  }

  /**
   * Construct from Eigen Matrix expression.
   */
  template<class EigenType, std::enable_if_t<is_eigen_compatible<this_type,EigenType>::value,int> = 0>
  Array(const Eigen::MatrixBase<EigenType>& o) :
      shape(o.rows(), o.cols()),
      buffer(nullptr),
      isView(false) {
    allocate();
    toEigen().noalias() = o;
  }

  /**
   * Construct from Eigen DiagonalWrapper expression.
   */
  template<class EigenType, std::enable_if_t<is_diagonal_compatible<this_type,EigenType>::value,int> = 0>
  Array(const Eigen::DiagonalWrapper<EigenType>& o) :
      shape(o.rows(), o.cols()),
      buffer(nullptr),
      isView(false) {
    allocate();
    toEigen().noalias() = o;
  }

  /**
   * Construct from Eigen TriangularView expression.
   */
  template<class EigenType, unsigned Mode, std::enable_if_t<is_triangle_compatible<this_type,EigenType>::value,int> = 0>
  Array(const Eigen::TriangularView<EigenType,Mode>& o) :
      shape(o.rows(), o.cols()),
      buffer(nullptr),
      isView(false) {
    allocate();
    toEigen() = o;
  }

  /**
   * Number of rows. For a vectoor, this is the length.
   */
  auto rows() const {
    assert(1 <= F::count() && F::count() <= 2);
    return shape.length(0);
  }

  /**
   * Number of columns. For a vector, this is 1.
   */
  auto cols() const {
    assert(1 <= F::count() && F::count() <= 2);
    return F::count() == 1 ? 1 : shape.length(1);
  }

  /**
   * Stride between rows.
   */
  auto rowStride() const {
    assert(1 <= F::count() && F::count() <= 2);
    return F::count() == 1 ? shape.volume() : shape.stride(0);
  }

  /**
   * Stride between columns.
   */
  auto colStride() const {
    assert(1 <= F::count() && F::count() <= 2);
    return F::count() == 1 ? shape.stride(0) : shape.stride(1);
  }
  ///@}

private:
  /**
   * Constructor for views.
   */
  Array(const F& shape, T* buffer, int64_t offset) :
      shape(shape),
      buffer(buffer + offset),
      isView(true) {
    //
  }

  /**
   * Raw pointer to underlying buffer.
   */
  T* buf() const {
    return buffer;
  }

  /**
   * Swap with another array.
   */
  void swap(Array<T,F>& o) {
    assert(!isView);
    assert(!o.isView);
    std::swap(shape, o.shape);
    std::swap(buffer, o.buffer);
  }

  /**
   * Allocate memory for array, leaving uninitialized.
   */
  void allocate() {
    assert(!buffer);
    size_t bytes = volume()*sizeof(T);
    if (bytes > 0) {
      buffer = (T*)std::malloc(bytes);
    }
  }

  /**
   * Deallocate memory of array.
   */
  void release() {
    if (!isView) {
      size_t bytes = volume()*sizeof(T);
      if (bytes > 0) {
        std::destroy(begin(), end());
        std::free(buffer);
        buffer = nullptr;
      }
    }
  }

  /**
   * Initialize allocated memory.
   *
   * @param args Constructor arguments.
   */
  template<class ... Args, std::enable_if_t<std::is_constructible<T,Args...>::value,int> = 0>
  void initialize(Args&&... args) {
    auto iter = begin();
    auto last = end();
    for (; iter != last; ++iter) {
      new (&*iter) T(std::forward<Args>(args)...);
    }
  }

  /**
   * Assign from another array.
   */
  template<class U>
  void copy(const U& o) {
    auto n = std::min(size(), o.size());
    auto begin1 = o.begin();
    auto end1 = begin1 + n;
    auto begin2 = begin();
    auto end2 = begin2 + n;
    if (inside(begin1, end1, begin2)) {
      std::copy_backward(begin1, end1, end2);
    } else {
      std::copy(begin1, end1, begin2);
    }
  }

  /**
   * Copy from another array.
   */
  template<class U>
  void uninitialized_copy(const U& o) {
    auto n = std::min(size(), o.size());
    auto begin1 = o.begin();
    auto end1 = begin1 + n;
    auto begin2 = begin();
    for (; begin1 != end1; ++begin1, ++begin2) {
      new (&*begin2) T(*begin1);
    }
  }

  /**
   * Shape.
   */
  F shape;

  /**
   * Buffer.
   */
  T* buffer;

  /**
   * Is this a view of another array? A view has stricter assignment
   * semantics, as it cannot be resized or moved.
   */
  bool isView;
};

/**
 * Default array for `D` dimensions.
 */
template<class T, int D>
using DefaultArray = Array<T,typename DefaultShape<D>::type>;

}
<|endoftext|>