<commit_before>#include <RGBLed.h>
#include <Backlight.h>
#include <AdapterBoard.h>
#include <Arduino.h>
#include "usb_commands.h"

bool isUsb = false;
unsigned long lastPowerChange = 0;
unsigned long lastBacklightChange = 0;

AdapterBoard::AdapterBoard()
{
}

void AdapterBoard::init()
{
  //Initialize the led, set to 'standby'
  led.init(LED_R, LED_G, LED_B);
  led.set(STANDBY_COLOUR);

  //Setup backlight, restore previous brightness, but don't enable
  backlight.init(BACKLIGHT_PIN, SUPPLY_EN);
  backlight.off();

  //Initialize the switches on board
  initSwitches();
}

void AdapterBoard::poll()
{
  //Check the switches, execute action if necessary
  pollSwitches();

  //Handle any USB commands waiting
  handleUSB();
}

void AdapterBoard::initSwitches()
{
  pinMode(SW_ON, INPUT);
  pinMode(SW_UP, INPUT);
  pinMode(SW_DOWN, INPUT);

  prev_swOn1 = HIGH;
  prev_swUp1 = HIGH;
  prev_swDown1 = HIGH;
  prev_swOn2 = HIGH;
  prev_swUp2 = HIGH;
  prev_swDown2 = HIGH;
  switchDelay = 0;
}

void AdapterBoard::pollSwitches()
{
  //Ignore a few polls
  if(switchDelay++ < 40000)
    return;
  switchDelay = 0;

  int swOn = digitalRead(SW_ON);
  int swUp = digitalRead(SW_UP);
  int swDown = digitalRead(SW_DOWN);

  if(swOn == LOW && prev_swOn1 == LOW && prev_swOn2 == HIGH)
    togglePower();

  if((millis()) - lastBacklightChange > 150)
  {
    lastBacklightChange = millis();
    if(swUp == LOW && prev_swUp1 == LOW && prev_swUp2 == HIGH)
      backlight.up();
    if(swDown == LOW && prev_swDown1 == LOW && prev_swDown2 == HIGH)
      backlight.down();
  }

  prev_swOn2 = prev_swOn1;
  prev_swUp2 = prev_swUp1;
  prev_swDown2 = prev_swDown1;

  prev_swOn1 = swOn;
  prev_swUp1 = swUp;
  prev_swDown1 = swDown;
}

void AdapterBoard::togglePower()
{
  if((millis() - lastPowerChange) < 300)
    return;
  lastPowerChange = millis();

  if(backlight.isOn())
  {
    led.set(STANDBY_COLOUR);
    backlight.set(backlight.get());
    backlight.off();
  }
  else
  {
    if(isUsb)
      led.set(ONUSB_COLOUR);
    else
      led.set(ON_COLOUR);

    backlight.setLast();
    backlight.on();
  }
}

char buf[EP_LEN];

void AdapterBoard::handleUSB()
{
  if(usb.isEnumerated())
  {
    isUsb = true;
    if(backlight.isOn())
      led.set(ONUSB_COLOUR);

    if(usb.hasData())
    {
      char resp[EP_LEN];
      bool unknown = false;
      bool needsAck = true;
      usb.read(buf, EP_LEN);

      //Command type specifier stored in byte 0
      switch(buf[0])
      {
        case CMD_BL_ON:
          led.set(ONUSB_COLOUR);
          backlight.on();
          break;
        case CMD_BL_OFF:
          led.set(STANDBY_COLOUR);
          backlight.off();
          break;
        case CMD_BL_LEVEL:
          backlight.silentSet(buf[1]);
          break;
        case CMD_BL_UP:
          backlight.up();
          break;
        case CMD_BL_DOWN:
          backlight.down();
          break;
        case CMD_BL_GET_STATE:
          needsAck = false;
          //Send state response
          resp[0] = CMD_RESP;
          resp[1] = CMD_BL_GET_STATE;
          resp[2] = backlight.isOn();
          resp[3] = backlight.get();
          while(!usb.canSend());
          usb.write(resp, EP_LEN);
          break;

        case CMD_RGB_SET:
          led.set(buf[1], buf[2], buf[3]);
          break;
        case CMD_RGB_GET:
          needsAck = false;
          //Send response
          resp[0] = CMD_RESP;
          resp[1] = CMD_RGB_GET;
          resp[2] = led.r;
          resp[2] = led.g;
          resp[3] = led.b;
          while(!usb.canSend());
          usb.write(resp, EP_LEN);
          break;

        default:
          unknown = true;
          break;
      }

      if(!unknown && needsAck)
      {
        //Send ACK back to PC
        resp[0] = CMD_ACK;
        resp[1] = buf[0];
        while(!usb.canSend());
        usb.write(resp, EP_LEN);
      }
    }
  }
  else
  {
    isUsb = false;
    if(backlight.isOn())
      led.set(ON_COLOUR);
  }
}
<commit_msg>Force switch polling to be at ~10ms intervals<commit_after>#include <RGBLed.h>
#include <Backlight.h>
#include <AdapterBoard.h>
#include <Arduino.h>
#include "usb_commands.h"

bool isUsb = false;
unsigned long lastPowerChange = 0;
unsigned long lastBacklightChange = 0;

AdapterBoard::AdapterBoard()
{
}

void AdapterBoard::init()
{
  //Initialize the led, set to 'standby'
  led.init(LED_R, LED_G, LED_B);
  led.set(STANDBY_COLOUR);

  //Setup backlight, restore previous brightness, but don't enable
  backlight.init(BACKLIGHT_PIN, SUPPLY_EN);
  backlight.off();

  //Initialize the switches on board
  initSwitches();
}

void AdapterBoard::poll()
{
  //Check the switches, execute action if necessary
  pollSwitches();

  //Handle any USB commands waiting
  handleUSB();
}

void AdapterBoard::initSwitches()
{
  pinMode(SW_ON, INPUT);
  pinMode(SW_UP, INPUT);
  pinMode(SW_DOWN, INPUT);

  prev_swOn1 = HIGH;
  prev_swUp1 = HIGH;
  prev_swDown1 = HIGH;
  prev_swOn2 = HIGH;
  prev_swUp2 = HIGH;
  prev_swDown2 = HIGH;
  switchDelay = millis();
}

void AdapterBoard::pollSwitches()
{
  if((millis() - switchDelay) < 10)
    return;
  switchDelay = millis();

  int swOn = digitalRead(SW_ON);
  int swUp = digitalRead(SW_UP);
  int swDown = digitalRead(SW_DOWN);

  if(swOn == LOW && prev_swOn1 == LOW && prev_swOn2 == HIGH)
    togglePower();

  if((millis()) - lastBacklightChange > 150)
  {
    lastBacklightChange = millis();
    if(swUp == LOW && prev_swUp1 == LOW && prev_swUp2 == HIGH)
      backlight.up();
    if(swDown == LOW && prev_swDown1 == LOW && prev_swDown2 == HIGH)
      backlight.down();
  }

  prev_swOn2 = prev_swOn1;
  prev_swUp2 = prev_swUp1;
  prev_swDown2 = prev_swDown1;

  prev_swOn1 = swOn;
  prev_swUp1 = swUp;
  prev_swDown1 = swDown;
}

void AdapterBoard::togglePower()
{
  if((millis() - lastPowerChange) < 300)
    return;
  lastPowerChange = millis();

  if(backlight.isOn())
  {
    led.set(STANDBY_COLOUR);
    backlight.set(backlight.get());
    backlight.off();
  }
  else
  {
    if(isUsb)
      led.set(ONUSB_COLOUR);
    else
      led.set(ON_COLOUR);

    backlight.setLast();
    backlight.on();
  }
}

char buf[EP_LEN];

void AdapterBoard::handleUSB()
{
  if(usb.isEnumerated())
  {
    isUsb = true;
    if(backlight.isOn())
      led.set(ONUSB_COLOUR);

    if(usb.hasData())
    {
      char resp[EP_LEN];
      bool unknown = false;
      bool needsAck = true;
      usb.read(buf, EP_LEN);

      //Command type specifier stored in byte 0
      switch(buf[0])
      {
        case CMD_BL_ON:
          led.set(ONUSB_COLOUR);
          backlight.on();
          break;
        case CMD_BL_OFF:
          led.set(STANDBY_COLOUR);
          backlight.off();
          break;
        case CMD_BL_LEVEL:
          backlight.silentSet(buf[1]);
          break;
        case CMD_BL_UP:
          backlight.up();
          break;
        case CMD_BL_DOWN:
          backlight.down();
          break;
        case CMD_BL_GET_STATE:
          needsAck = false;
          //Send state response
          resp[0] = CMD_RESP;
          resp[1] = CMD_BL_GET_STATE;
          resp[2] = backlight.isOn();
          resp[3] = backlight.get();
          while(!usb.canSend());
          usb.write(resp, EP_LEN);
          break;

        case CMD_RGB_SET:
          led.set(buf[1], buf[2], buf[3]);
          break;
        case CMD_RGB_GET:
          needsAck = false;
          //Send response
          resp[0] = CMD_RESP;
          resp[1] = CMD_RGB_GET;
          resp[2] = led.r;
          resp[2] = led.g;
          resp[3] = led.b;
          while(!usb.canSend());
          usb.write(resp, EP_LEN);
          break;

        default:
          unknown = true;
          break;
      }

      if(!unknown && needsAck)
      {
        //Send ACK back to PC
        resp[0] = CMD_ACK;
        resp[1] = buf[0];
        while(!usb.canSend());
        usb.write(resp, EP_LEN);
      }
    }
  }
  else
  {
    isUsb = false;
    if(backlight.isOn())
      led.set(ON_COLOUR);
  }
}
<|endoftext|>


<commit_before><commit_msg>Prev -> Previous; Fix using the keyboard to navigate listviews.<commit_after><|endoftext|>


<commit_before>/**
 * Copyright (c) 2015 - The CM Authors <legal@clickmatcher.com>
 *   All Rights Reserved.
 *
 * This file is CONFIDENTIAL -- Distribution or duplication of this material or
 * the information contained herein is strictly forbidden unless prior written
 * permission is obtained.
 */
#include "stx/inspect.h"
#include "stx/assets.h"
#include "zbase/JavaScriptContext.h"
#include "js/Conversions.h"
#include "jsapi.h"
#include "jsstr.h"

using namespace stx;

namespace zbase {

JSClass JavaScriptContext::kGlobalJSClass = { "global", JSCLASS_GLOBAL_FLAGS };

static bool write_json_to_buf(const char16_t* str, uint32_t strlen, void* out) {
  *static_cast<String*>(out) += StringUtil::convertUTF16To8(
      std::basic_string<char16_t>(str, strlen));

  return true;
}

JavaScriptContext::JavaScriptContext(
    size_t memlimit /* = kDefaultMemLimit */) {
  {
    runtime_ = JS_NewRuntime(memlimit);
    if (!runtime_) {
      RAISE(kRuntimeError, "error while initializing JavaScript runtime");
    }

    JS::RuntimeOptionsRef(runtime_)
        .setBaseline(true)
        .setIon(true)
        .setAsmJS(true);

    JS_SetRuntimePrivate(runtime_, this);
    JS_SetErrorReporter(runtime_, &JavaScriptContext::dispatchError);

    ctx_ = JS_NewContext(runtime_, 8192);
    if (!ctx_) {
      RAISE(kRuntimeError, "error while initializing JavaScript context");
    }

    JSAutoRequest js_req(ctx_);

    global_ = JS_NewGlobalObject(
        ctx_,
        &kGlobalJSClass,
        nullptr,
        JS::FireOnNewGlobalHook);

    if (!global_) {
      RAISE(kRuntimeError, "error while initializing JavaScript context");
    }

    {
      JSAutoCompartment ac(ctx_, global_);
      JS_InitStandardClasses(ctx_, global_);

      JS_DefineFunction(
          ctx_,
          global_,
          "z1_log",
          &JavaScriptContext::dispatchLog,
          0,
          0);

      JS_DefineFunction(
          ctx_,
          global_,
          "z1_listpartitions",
          &JavaScriptContext::listPartitions,
          0,
          0);
    }
  }

  loadProgram(Assets::getAsset("zbase/mapreduce/prelude.js"));
}

JavaScriptContext::~JavaScriptContext() {
  JS_DestroyContext(ctx_);
  JS_DestroyRuntime(runtime_);
}

void JavaScriptContext::storeError(const String& error) {
  current_error_ = error;
}

void JavaScriptContext::raiseError(const String& input) {
  RAISEF(
      "JavaScriptError", "$0 for input >$1< ($2)",
      current_error_,
      input,
      input.size());
}

void JavaScriptContext::dispatchError(
    JSContext* ctx,
    const char* message,
    JSErrorReport* report) {
  auto rt = JS_GetRuntime(ctx);
  auto rt_userdata = JS_GetRuntimePrivate(rt);
  if (rt_userdata) {
    auto req = static_cast<JavaScriptContext*>(rt_userdata);
    req->storeError(StringUtil::format(
        "<script:$0:$1> $2",
        report->lineno,
        report->column,
        String(message)));
  }
}

void JavaScriptContext::storeLogline(const String& logline) {
  iputs("jslog: $0", logline);
}

bool JavaScriptContext::dispatchLog(
    JSContext* ctx,
    unsigned argc,
    JS::Value* vp) {
  auto args = JS::CallArgsFromVp(argc, vp);
  if (args.length() < 1 || !args[0].isString()) {
    return false;
  }

  auto rt = JS_GetRuntime(ctx);
  auto rt_userdata = JS_GetRuntimePrivate(rt);
  if (rt_userdata) {
    auto log_cstr = JS_EncodeString(ctx, args[0].toString());
    String log_str(log_cstr);
    JS_free(ctx, log_cstr);

    auto req = static_cast<JavaScriptContext*>(rt_userdata);
    req->storeLogline(log_str);
  }

  args.rval().set(JSVAL_TRUE);
  return true;
}

bool JavaScriptContext::listPartitions(
    JSContext* ctx,
    unsigned argc,
    JS::Value* vp) {
  auto args = JS::CallArgsFromVp(argc, vp);
  if (args.length() != 3 ||
      !args[0].isString() ||
      !args[1].isString() ||
      !args[2].isString()) {
    return false;
  }

  args.rval().set(JSVAL_TRUE);
  return true;
}

void JavaScriptContext::loadProgram(const String& program) {
  JSAutoRequest js_req(ctx_);
  JSAutoCompartment ac(ctx_, global_);

  JS::RootedValue rval(ctx_);

  JS::CompileOptions opts(ctx_);
  opts.setFileAndLine("<mapreduce>", 1);

  if (!JS::Evaluate(
        ctx_,
        global_,
        opts,
        program.c_str(),
        program.size(),
        &rval)) {
    raiseError("<script>");
  }
}

void JavaScriptContext::callMapFunction(
    const String& method_name,
    const String& json_string,
    Vector<Pair<String, String>>* tuples) {
  JSAutoRequest js_req(ctx_);
  JSAutoCompartment js_comp(ctx_, global_);

  size_t json_wstring_len = json_string.size();
  auto json_wstring = js::InflateString(
      (js::ExclusiveContext*) ctx_,
      json_string.data(),
      &json_wstring_len);
  if (!json_wstring) {
    RAISE(kRuntimeError, "maap function execution error: out of memory");
  }

  JS::RootedValue json(ctx_);
  if (JS_ParseJSON(ctx_, json_wstring, json_wstring_len, &json)) {
    JS_free(ctx_, json_wstring);
  } else {
    JS_free(ctx_, json_wstring);
    raiseError(json_string);
  }

  JS::AutoValueArray<1> argv(ctx_);
  argv[0].set(json);

  JS::RootedValue rval(ctx_);
  if (!JS_CallFunctionName(ctx_, global_, method_name.c_str(), argv, &rval)) {
    raiseError(json_string);
  }

  enumerateTuples(&rval, tuples);
}

void JavaScriptContext::callReduceFunction(
    const String& method_name,
    const String& key,
    const Vector<String>& values,
    Vector<Pair<String, String>>* tuples) {
  JSAutoRequest js_req(ctx_);
  JSAutoCompartment js_comp(ctx_, global_);

  JS::AutoValueArray<3> argv(ctx_);
  auto method_str_ptr = JS_NewStringCopyN(
      ctx_,
      method_name.data(),
      method_name.size());
  if (!method_str_ptr) {
    RAISE(kRuntimeError, "reduce function execution error: out of memory");
  } else {
    argv[0].setString(method_str_ptr);
  }

  auto key_str_ptr = JS_NewStringCopyN(ctx_, key.data(), key.size());
  if (!key_str_ptr) {
    RAISE(kRuntimeError, "reduce function execution error: out of memory");
  } else {
    argv[1].setString(key_str_ptr);
  }

  ReduceCollectionIter val_iter;
  val_iter.data = &values;
  val_iter.cur = 0;

  auto val_iter_obj_ptr = JS_NewObject(ctx_, &ReduceCollectionIter::kJSClass);
  if (!val_iter_obj_ptr) {
    RAISE(kRuntimeError, "reduce function execution error: out of memory");
  }

  JS::RootedObject val_iter_obj(ctx_, val_iter_obj_ptr);
  JS_SetPrivate(val_iter_obj, &val_iter);
  argv[2].setObject(*val_iter_obj);

  JS_DefineFunction(
      ctx_,
      val_iter_obj,
      "hasNext",
      &ReduceCollectionIter::hasNext,
      0,
      0);

  JS_DefineFunction(
      ctx_,
      val_iter_obj,
      "next",
      &ReduceCollectionIter::getNext,
      0,
      0);

  JS::RootedValue rval(ctx_);
  if (!JS_CallFunctionName(ctx_, global_, "__call_with_iter", argv, &rval)) {
    raiseError(key);
  }

  enumerateTuples(&rval, tuples);
}

void JavaScriptContext::enumerateTuples(
    JS::RootedValue* src,
    Vector<Pair<String, String>>* dst) const {
  if (!src->isObject()) {
    RAISE(kRuntimeError, "reduce function must return a list/array of tuples");
  }

  JS::RootedObject list(ctx_, &src->toObject());
  JS::AutoIdArray list_enum(ctx_, JS_Enumerate(ctx_, list));
  for (size_t i = 0; i < list_enum.length(); ++i) {
    JS::RootedValue elem(ctx_);
    JS::RootedValue elem_key(ctx_);
    JS::RootedValue elem_value(ctx_);
    JS::Rooted<jsid> elem_id(ctx_, list_enum[i]);
    if (!JS_GetPropertyById(ctx_, list, elem_id, &elem)) {
      RAISE(kIllegalStateError);
    }

    if (!elem.isObject()) {
      RAISE(kRuntimeError, "reduce function must return a list/array of tuples");
    }

    JS::RootedObject elem_obj(ctx_, &elem.toObject());

    if (!JS_GetProperty(ctx_, elem_obj, "0", &elem_key)) {
      RAISE(kRuntimeError, "reduce function must return a list/array of tuples");
    }

    if (!JS_GetProperty(ctx_, elem_obj, "1", &elem_value)) {
      RAISE(kRuntimeError, "reduce function must return a list/array of tuples");
    }

    auto tkey_jstr = JS::ToString(ctx_, elem_key);
    if (!tkey_jstr) {
      RAISE(kRuntimeError, "first tuple element must be a string");
    }

    auto tkey_cstr = JS_EncodeString(ctx_, tkey_jstr);
    String tkey(tkey_cstr);
    JS_free(ctx_, tkey_cstr);

    String tval;
    JS::RootedObject replacer(ctx_);
    JS::RootedValue space(ctx_);
    if (!JS_Stringify(
            ctx_,
            &elem_value,
            replacer,
            space,
            &write_json_to_buf,
            &tval)) {
      RAISE(kRuntimeError, "second tuple element must be convertible to JSON");
    }

    dst->emplace_back(tkey, tval);
  }
}

Option<String> JavaScriptContext::getMapReduceJobJSON() {
  JSAutoRequest js_req(ctx_);
  JSAutoCompartment js_comp(ctx_, global_);

  JS::RootedValue job_def(ctx_);
  if (!JS_GetProperty(ctx_, global_, "__z1_mr_jobs", &job_def)) {
    return None<String>();
  }

  String json_str;
  JS::RootedObject replacer(ctx_);
  JS::RootedValue space(ctx_);
  if (!JS_Stringify(
          ctx_,
          &job_def,
          replacer,
          space,
          &write_json_to_buf,
          &json_str)) {
    RAISE(kRuntimeError, "illegal job definition");
  }

  return Some(json_str);
}

JSClass JavaScriptContext::ReduceCollectionIter::kJSClass = {
  "global",
  JSCLASS_HAS_PRIVATE
};

bool JavaScriptContext::ReduceCollectionIter::hasNext(
    JSContext* ctx,
    unsigned argc,
    JS::Value* vp) {
  auto args = JS::CallArgsFromVp(argc, vp);
  if (!args.thisv().isObject()) {
     return false;
  }

  auto thisv = args.thisv();
  if (JS_GetClass(&thisv.toObject()) != &kJSClass) {
    return false;
  }

  auto iter = static_cast<ReduceCollectionIter*>(
      JS_GetPrivate(&thisv.toObject()));
  if (!iter) {
    return false;
  }

  if (iter->cur < iter->data->size()) {
    args.rval().set(JSVAL_TRUE);
  } else {
    args.rval().set(JSVAL_FALSE);
  }

  return true;
}

bool JavaScriptContext::ReduceCollectionIter::getNext(
    JSContext* ctx,
    unsigned argc,
    JS::Value* vp) {
  auto args = JS::CallArgsFromVp(argc, vp);
  if (!args.thisv().isObject()) {
     return false;
  }

  auto thisv = args.thisv();
  if (JS_GetClass(&thisv.toObject()) != &kJSClass) {
    return false;
  }

  auto iter = static_cast<ReduceCollectionIter*>(
      JS_GetPrivate(&thisv.toObject()));
  if (!iter) {
    return false;
  }

  if (iter->cur >= iter->data->size()) {
    return false;
  }

  const auto& value = (*iter->data)[iter->cur];
  ++iter->cur;

  auto val_str_ptr = JS_NewStringCopyN(ctx, value.data(), value.size());
  if (val_str_ptr) {
    args.rval().setString(val_str_ptr);
    return true;
  } else {
    return false;
  }
}

} // namespace zbase

<commit_msg>JavaScriptContext::listPartitions glue code<commit_after>/**
 * Copyright (c) 2015 - The CM Authors <legal@clickmatcher.com>
 *   All Rights Reserved.
 *
 * This file is CONFIDENTIAL -- Distribution or duplication of this material or
 * the information contained herein is strictly forbidden unless prior written
 * permission is obtained.
 */
#include "stx/inspect.h"
#include "stx/assets.h"
#include "zbase/JavaScriptContext.h"
#include "js/Conversions.h"
#include "jsapi.h"
#include "jsstr.h"

using namespace stx;

namespace zbase {

JSClass JavaScriptContext::kGlobalJSClass = { "global", JSCLASS_GLOBAL_FLAGS };

static bool write_json_to_buf(const char16_t* str, uint32_t strlen, void* out) {
  *static_cast<String*>(out) += StringUtil::convertUTF16To8(
      std::basic_string<char16_t>(str, strlen));

  return true;
}

JavaScriptContext::JavaScriptContext(
    size_t memlimit /* = kDefaultMemLimit */) {
  {
    runtime_ = JS_NewRuntime(memlimit);
    if (!runtime_) {
      RAISE(kRuntimeError, "error while initializing JavaScript runtime");
    }

    JS::RuntimeOptionsRef(runtime_)
        .setBaseline(true)
        .setIon(true)
        .setAsmJS(true);

    JS_SetRuntimePrivate(runtime_, this);
    JS_SetErrorReporter(runtime_, &JavaScriptContext::dispatchError);

    ctx_ = JS_NewContext(runtime_, 8192);
    if (!ctx_) {
      RAISE(kRuntimeError, "error while initializing JavaScript context");
    }

    JSAutoRequest js_req(ctx_);

    global_ = JS_NewGlobalObject(
        ctx_,
        &kGlobalJSClass,
        nullptr,
        JS::FireOnNewGlobalHook);

    if (!global_) {
      RAISE(kRuntimeError, "error while initializing JavaScript context");
    }

    {
      JSAutoCompartment ac(ctx_, global_);
      JS_InitStandardClasses(ctx_, global_);

      JS_DefineFunction(
          ctx_,
          global_,
          "z1_log",
          &JavaScriptContext::dispatchLog,
          0,
          0);

      JS_DefineFunction(
          ctx_,
          global_,
          "z1_listpartitions",
          &JavaScriptContext::listPartitions,
          0,
          0);
    }
  }

  loadProgram(Assets::getAsset("zbase/mapreduce/prelude.js"));
}

JavaScriptContext::~JavaScriptContext() {
  JS_DestroyContext(ctx_);
  JS_DestroyRuntime(runtime_);
}

void JavaScriptContext::storeError(const String& error) {
  current_error_ = error;
}

void JavaScriptContext::raiseError(const String& input) {
  RAISEF(
      "JavaScriptError", "$0 for input >$1< ($2)",
      current_error_,
      input,
      input.size());
}

void JavaScriptContext::dispatchError(
    JSContext* ctx,
    const char* message,
    JSErrorReport* report) {
  auto rt = JS_GetRuntime(ctx);
  auto rt_userdata = JS_GetRuntimePrivate(rt);
  if (rt_userdata) {
    auto req = static_cast<JavaScriptContext*>(rt_userdata);
    req->storeError(StringUtil::format(
        "<script:$0:$1> $2",
        report->lineno,
        report->column,
        String(message)));
  }
}

void JavaScriptContext::storeLogline(const String& logline) {
  iputs("jslog: $0", logline);
}

bool JavaScriptContext::dispatchLog(
    JSContext* ctx,
    unsigned argc,
    JS::Value* vp) {
  auto args = JS::CallArgsFromVp(argc, vp);
  if (args.length() < 1 || !args[0].isString()) {
    return false;
  }

  auto rt = JS_GetRuntime(ctx);
  auto rt_userdata = JS_GetRuntimePrivate(rt);
  if (rt_userdata) {
    auto log_cstr = JS_EncodeString(ctx, args[0].toString());
    String log_str(log_cstr);
    JS_free(ctx, log_cstr);

    auto req = static_cast<JavaScriptContext*>(rt_userdata);
    req->storeLogline(log_str);
  }

  args.rval().set(JSVAL_TRUE);
  return true;
}

bool JavaScriptContext::listPartitions(
    JSContext* ctx,
    unsigned argc,
    JS::Value* vp) {
  auto args = JS::CallArgsFromVp(argc, vp);
  if (args.length() != 3 ||
      !args[0].isString() ||
      !args[1].isString() ||
      !args[2].isString()) {
    return false;
  }

  auto table_name_cstr = JS_EncodeString(ctx, args[0].toString());
  String table_name(table_name_cstr);
  JS_free(ctx, table_name_cstr);

  auto from_cstr = JS_EncodeString(ctx, args[1].toString());
  String from(from_cstr);
  JS_free(ctx, from_cstr);

  auto until_cstr = JS_EncodeString(ctx, args[2].toString());
  String until(until_cstr);
  JS_free(ctx, until_cstr);

  iputs("$0 $1-$2", table_name, from, until);

  Vector<String> partitions = {"a", "b", "c"};

  auto part_array_ptr = JS_NewArrayObject(ctx, partitions.size());
  if (!part_array_ptr) {
    RAISE(kRuntimeError, "JavaScript execution error: out of memory");
  }

  JS::RootedObject part_array(ctx, part_array_ptr);
  for (size_t i = 0; i < partitions.size(); ++i) {
    auto val_str_ptr = JS_NewStringCopyN(
        ctx,
        partitions[i].data(),
        partitions[i].size());
    if (!val_str_ptr) {
      RAISE(kRuntimeError, "JavaScript execution error: out of memory");
    }

    JS::RootedString val_str(ctx, val_str_ptr);
    if (!JS_SetElement(ctx, part_array, i, val_str)) {
      RAISE(kRuntimeError, "JavaScript execution error: out of memory");
    }
  }

  args.rval().setObject(*part_array);
  return true;
}

void JavaScriptContext::loadProgram(const String& program) {
  JSAutoRequest js_req(ctx_);
  JSAutoCompartment ac(ctx_, global_);

  JS::RootedValue rval(ctx_);

  JS::CompileOptions opts(ctx_);
  opts.setFileAndLine("<mapreduce>", 1);

  if (!JS::Evaluate(
        ctx_,
        global_,
        opts,
        program.c_str(),
        program.size(),
        &rval)) {
    raiseError("<script>");
  }
}

void JavaScriptContext::callMapFunction(
    const String& method_name,
    const String& json_string,
    Vector<Pair<String, String>>* tuples) {
  JSAutoRequest js_req(ctx_);
  JSAutoCompartment js_comp(ctx_, global_);

  size_t json_wstring_len = json_string.size();
  auto json_wstring = js::InflateString(
      (js::ExclusiveContext*) ctx_,
      json_string.data(),
      &json_wstring_len);
  if (!json_wstring) {
    RAISE(kRuntimeError, "maap function execution error: out of memory");
  }

  JS::RootedValue json(ctx_);
  if (JS_ParseJSON(ctx_, json_wstring, json_wstring_len, &json)) {
    JS_free(ctx_, json_wstring);
  } else {
    JS_free(ctx_, json_wstring);
    raiseError(json_string);
  }

  JS::AutoValueArray<1> argv(ctx_);
  argv[0].set(json);

  JS::RootedValue rval(ctx_);
  if (!JS_CallFunctionName(ctx_, global_, method_name.c_str(), argv, &rval)) {
    raiseError(json_string);
  }

  enumerateTuples(&rval, tuples);
}

void JavaScriptContext::callReduceFunction(
    const String& method_name,
    const String& key,
    const Vector<String>& values,
    Vector<Pair<String, String>>* tuples) {
  JSAutoRequest js_req(ctx_);
  JSAutoCompartment js_comp(ctx_, global_);

  JS::AutoValueArray<3> argv(ctx_);
  auto method_str_ptr = JS_NewStringCopyN(
      ctx_,
      method_name.data(),
      method_name.size());
  if (!method_str_ptr) {
    RAISE(kRuntimeError, "reduce function execution error: out of memory");
  } else {
    argv[0].setString(method_str_ptr);
  }

  auto key_str_ptr = JS_NewStringCopyN(ctx_, key.data(), key.size());
  if (!key_str_ptr) {
    RAISE(kRuntimeError, "reduce function execution error: out of memory");
  } else {
    argv[1].setString(key_str_ptr);
  }

  ReduceCollectionIter val_iter;
  val_iter.data = &values;
  val_iter.cur = 0;

  auto val_iter_obj_ptr = JS_NewObject(ctx_, &ReduceCollectionIter::kJSClass);
  if (!val_iter_obj_ptr) {
    RAISE(kRuntimeError, "reduce function execution error: out of memory");
  }

  JS::RootedObject val_iter_obj(ctx_, val_iter_obj_ptr);
  JS_SetPrivate(val_iter_obj, &val_iter);
  argv[2].setObject(*val_iter_obj);

  JS_DefineFunction(
      ctx_,
      val_iter_obj,
      "hasNext",
      &ReduceCollectionIter::hasNext,
      0,
      0);

  JS_DefineFunction(
      ctx_,
      val_iter_obj,
      "next",
      &ReduceCollectionIter::getNext,
      0,
      0);

  JS::RootedValue rval(ctx_);
  if (!JS_CallFunctionName(ctx_, global_, "__call_with_iter", argv, &rval)) {
    raiseError(key);
  }

  enumerateTuples(&rval, tuples);
}

void JavaScriptContext::enumerateTuples(
    JS::RootedValue* src,
    Vector<Pair<String, String>>* dst) const {
  if (!src->isObject()) {
    RAISE(kRuntimeError, "reduce function must return a list/array of tuples");
  }

  JS::RootedObject list(ctx_, &src->toObject());
  JS::AutoIdArray list_enum(ctx_, JS_Enumerate(ctx_, list));
  for (size_t i = 0; i < list_enum.length(); ++i) {
    JS::RootedValue elem(ctx_);
    JS::RootedValue elem_key(ctx_);
    JS::RootedValue elem_value(ctx_);
    JS::Rooted<jsid> elem_id(ctx_, list_enum[i]);
    if (!JS_GetPropertyById(ctx_, list, elem_id, &elem)) {
      RAISE(kIllegalStateError);
    }

    if (!elem.isObject()) {
      RAISE(kRuntimeError, "reduce function must return a list/array of tuples");
    }

    JS::RootedObject elem_obj(ctx_, &elem.toObject());

    if (!JS_GetProperty(ctx_, elem_obj, "0", &elem_key)) {
      RAISE(kRuntimeError, "reduce function must return a list/array of tuples");
    }

    if (!JS_GetProperty(ctx_, elem_obj, "1", &elem_value)) {
      RAISE(kRuntimeError, "reduce function must return a list/array of tuples");
    }

    auto tkey_jstr = JS::ToString(ctx_, elem_key);
    if (!tkey_jstr) {
      RAISE(kRuntimeError, "first tuple element must be a string");
    }

    auto tkey_cstr = JS_EncodeString(ctx_, tkey_jstr);
    String tkey(tkey_cstr);
    JS_free(ctx_, tkey_cstr);

    String tval;
    JS::RootedObject replacer(ctx_);
    JS::RootedValue space(ctx_);
    if (!JS_Stringify(
            ctx_,
            &elem_value,
            replacer,
            space,
            &write_json_to_buf,
            &tval)) {
      RAISE(kRuntimeError, "second tuple element must be convertible to JSON");
    }

    dst->emplace_back(tkey, tval);
  }
}

Option<String> JavaScriptContext::getMapReduceJobJSON() {
  JSAutoRequest js_req(ctx_);
  JSAutoCompartment js_comp(ctx_, global_);

  JS::RootedValue job_def(ctx_);
  if (!JS_GetProperty(ctx_, global_, "__z1_mr_jobs", &job_def)) {
    return None<String>();
  }

  String json_str;
  JS::RootedObject replacer(ctx_);
  JS::RootedValue space(ctx_);
  if (!JS_Stringify(
          ctx_,
          &job_def,
          replacer,
          space,
          &write_json_to_buf,
          &json_str)) {
    RAISE(kRuntimeError, "illegal job definition");
  }

  return Some(json_str);
}

JSClass JavaScriptContext::ReduceCollectionIter::kJSClass = {
  "global",
  JSCLASS_HAS_PRIVATE
};

bool JavaScriptContext::ReduceCollectionIter::hasNext(
    JSContext* ctx,
    unsigned argc,
    JS::Value* vp) {
  auto args = JS::CallArgsFromVp(argc, vp);
  if (!args.thisv().isObject()) {
     return false;
  }

  auto thisv = args.thisv();
  if (JS_GetClass(&thisv.toObject()) != &kJSClass) {
    return false;
  }

  auto iter = static_cast<ReduceCollectionIter*>(
      JS_GetPrivate(&thisv.toObject()));
  if (!iter) {
    return false;
  }

  if (iter->cur < iter->data->size()) {
    args.rval().set(JSVAL_TRUE);
  } else {
    args.rval().set(JSVAL_FALSE);
  }

  return true;
}

bool JavaScriptContext::ReduceCollectionIter::getNext(
    JSContext* ctx,
    unsigned argc,
    JS::Value* vp) {
  auto args = JS::CallArgsFromVp(argc, vp);
  if (!args.thisv().isObject()) {
     return false;
  }

  auto thisv = args.thisv();
  if (JS_GetClass(&thisv.toObject()) != &kJSClass) {
    return false;
  }

  auto iter = static_cast<ReduceCollectionIter*>(
      JS_GetPrivate(&thisv.toObject()));
  if (!iter) {
    return false;
  }

  if (iter->cur >= iter->data->size()) {
    return false;
  }

  const auto& value = (*iter->data)[iter->cur];
  ++iter->cur;

  auto val_str_ptr = JS_NewStringCopyN(ctx, value.data(), value.size());
  if (val_str_ptr) {
    args.rval().setString(val_str_ptr);
    return true;
  } else {
    return false;
  }
}

} // namespace zbase

<|endoftext|>


<commit_before>/* Begin CVS Header
   $Source: /Volumes/Home/Users/shoops/cvs/copasi_dev/copasi/trajectory/CTrajectoryTask.cpp,v $
   $Revision: 1.51 $
   $Name:  $
   $Author: shoops $
   $Date: 2005/07/12 21:09:54 $
   End CVS Header */

/**
 * CTrajectoryTask class.
 *
 * This class implements a trajectory task which is comprised of a
 * of a problem and a method. Additionally calls to the reporting
 * methods are done when initialized.
 *
 * Created for Copasi by Stefan Hoops 2002
 */

#include <string>

#include "copasi.h"

#include "CTrajectoryTask.h"
#include "CTrajectoryProblem.h"
#include "CTrajectoryMethod.h"
#include "model/CModel.h"
#include "model/CState.h"
#include "report/CKeyFactory.h"
#include "report/CReport.h"
#include "utilities/COutputHandler.h"
#include "utilities/CProcessReport.h"

#define XXXX_Reporting

bool fle(const C_FLOAT64 & d1, const C_FLOAT64 & d2)
{return (d1 <= d2);}

bool fl(const C_FLOAT64 & d1, const C_FLOAT64 & d2)
{return (d1 < d2);}

bool ble(const C_FLOAT64 & d1, const C_FLOAT64 & d2)
{return (d1 >= d2);}

bool bl(const C_FLOAT64 & d1, const C_FLOAT64 & d2)
{return (d1 > d2);}

CTrajectoryTask::CTrajectoryTask(const CCopasiContainer * pParent):
    CCopasiTask(CCopasiTask::timeCourse, pParent),
    mpState(NULL),
    mTimeSeriesRequested(true),
    mDoOutput(OUTPUT_COMPLETE)
{
  mpProblem = new CTrajectoryProblem(this);
  mpMethod =
    CTrajectoryMethod::createTrajectoryMethod(CCopasiMethod::deterministic,
        (CTrajectoryProblem *) mpProblem);
  this->add(mpMethod, true);
  //mpMethod->setObjectParent(this);
}

/*CTrajectoryTask::CTrajectoryTask(const CTrajectoryTask & src,
                                 const CCopasiContainer * pParent):
    CCopasiTask(src, pParent),
    mpState(src.mpState),
    mTimeSeriesRequested(src.mTimeSeriesRequested)
{fatalError();}*/

CTrajectoryTask::~CTrajectoryTask()
{
  cleanup();
}

void CTrajectoryTask::cleanup()
{
  pdelete(mpState);
}

void CTrajectoryTask::load(CReadConfig & configBuffer)
{
  configBuffer.getVariable("Dynamics", "bool", &mScheduled,
                           CReadConfig::LOOP);

  pdelete(mpProblem);
  mpProblem = new CTrajectoryProblem(this);
  ((CTrajectoryProblem *) mpProblem)->load(configBuffer);

  pdelete(mpMethod);
  mpMethod = CTrajectoryMethod::createTrajectoryMethod();
  this->add(mpMethod, true);
  //mpMethod->setObjectParent(this);
  ((CTrajectoryMethod *)mpMethod)->setProblem((CTrajectoryProblem *) mpProblem);
}

bool CTrajectoryTask::initialize(std::ostream * pOstream)
{
  assert(mpProblem && mpMethod);

  CTrajectoryProblem* pProblem =
    dynamic_cast<CTrajectoryProblem *>(mpProblem);
  assert(pProblem);

  bool success = true;

  if (!mReport.open(pOstream)) success = false;
  if (!mReport.compile()) success = false;
  if (!pProblem->getModel()->compileIfNecessary()) success = false;
  //  if (!pProblem->
  //      setInitialState(pProblem->getModel()->getInitialState()))
  //    success = false;

  //pProblem->setInitialState(pProblem->getModel()->getInitialState());

  return success;
}

bool CTrajectoryTask::process()
{
  assert(/*mpProblem && */mpMethod);
  mpMethod->isValidProblem(mpProblem);

  CTrajectoryProblem * pProblem = (CTrajectoryProblem *) mpProblem;
  CTrajectoryMethod * pMethod = (CTrajectoryMethod *) mpMethod;

  //the following is a hack that has to disappear soon.
  pProblem->setInitialState(pProblem->getModel()->getInitialState());

  mTimeSeriesRequested = pProblem->timeSeriesRequested();

  pdelete(mpState);
  mpState = new CState(pProblem->getInitialState());

  pMethod->setCurrentState(mpState);
  pMethod->setProblem(pProblem);

  C_FLOAT64 StepSize = pProblem->getStepSize();
  C_FLOAT64 NextTimeToReport;

  bool (*LE)(const C_FLOAT64 &, const C_FLOAT64 &);
  bool (*L)(const C_FLOAT64 &, const C_FLOAT64 &);
  if (StepSize < 0.0)
    {
      LE = &ble;
      L = &bl;
    }
  else
    {
      LE = &fle;
      L = &fl;
    }

  C_FLOAT64 ActualStepSize;
  const C_FLOAT64 & Time = mpState->getTime();
  C_FLOAT64 EndTime = pProblem->getEndTime() - StepSize;
  C_FLOAT64 outputStartTime = pProblem->getOutputStartTime();

  if (StepSize == 0.0 && pProblem->getEndTime() != pProblem->getStartTime())
    {
      CCopasiMessage(CCopasiMessage::ERROR, MCTrajectoryProblem + 1, StepSize);
      return false;
    }
  bool flagProceed = true;
  C_FLOAT64 handlerFactor = 100.0 / (pProblem->getEndTime() - pProblem->getStartTime());

  /*
    if (mpProgressHandler)
      mpProgressHandler->init(1000,
                              "performing simulation...",
                              true);
  */
  C_FLOAT64 Percentage = 0;
  unsigned C_INT32 hProcess;

  if (mpProgressHandler)
    {
      mpProgressHandler->setName("performing simulation...");
      C_FLOAT64 hundred = 100;
      hProcess = mpProgressHandler->addItem("Completion",
                                            CCopasiParameter::DOUBLE,
                                            &Percentage,
                                            &hundred);
    }

  if (mDoOutput)
    {
      pProblem->getModel()->setState(mpState);
      pProblem->getModel()->updateRates();
    }

  if (mDoOutput == OUTPUT_COMPLETE)
    {
      pProblem->getModel()->setState(mpState);
      pProblem->getModel()->updateRates();
      mReport.printHeader();
      if (mpOutputHandler) mpOutputHandler->init();
      if (mTimeSeriesRequested) mTimeSeries.init(pProblem->getStepNumber(), mpState);
    }

  if ((*LE)(outputStartTime, Time) && (mDoOutput))
    {
      mReport.printBody();
      if (mpOutputHandler) mpOutputHandler->doOutput();
      if (mTimeSeriesRequested) mTimeSeries.add();
    }

  // We start the integration
  NextTimeToReport = Time + StepSize;
  ActualStepSize = pMethod->step(StepSize, mpState);

  if (mpProgressHandler)
    {
      Percentage = (Time - pProblem->getStartTime()) * handlerFactor;
      flagProceed = mpProgressHandler->progress(hProcess);
    }

  if ((mDoOutput) && (*LE)(outputStartTime, Time) && !(Time < NextTimeToReport))
    {
      pProblem->getModel()->setState(mpState);
      pProblem->getModel()->updateRates();
      mReport.printBody();
      if (mpOutputHandler) mpOutputHandler->doOutput();
      if (mTimeSeriesRequested) mTimeSeries.add();
    }

  if (ActualStepSize != StepSize)
    {
      /* Here we will do conditional event processing */

      StepSize = NextTimeToReport - Time;
    }
  else
    {
      StepSize = pProblem->getStepSize();
      NextTimeToReport = Time + StepSize;
    }

  while ((*L)(Time, EndTime) && flagProceed)
    {
      ActualStepSize = pMethod->step(StepSize);

      //std::cout << EndTime << "  " << Time << "  " << EndTime-Time << std::endl;

      if (mpProgressHandler)
        {
          Percentage = (Time - pProblem->getStartTime()) * handlerFactor;
          flagProceed = mpProgressHandler->progress(hProcess);
        }

      if ((mDoOutput) && (*LE)(outputStartTime, Time) && !(Time < NextTimeToReport))
        {
          pProblem->getModel()->setState(mpState);
          pProblem->getModel()->updateRates();
          mReport.printBody();
          if (mpOutputHandler) mpOutputHandler->doOutput();
          if (mTimeSeriesRequested) mTimeSeries.add();
        }

      if (ActualStepSize != StepSize)
        {
          /* Here we will do conditional event processing */

          StepSize = NextTimeToReport - Time;
        }
      else
        {
          StepSize = pProblem->getStepSize();
          NextTimeToReport = Time + StepSize;
        }
    }

  // This assures that the integration stops.
  C_FLOAT64 modifiedEndTime = (StepSize < 0.0) ? pProblem->getEndTime() + StepSize * 1e-5 : pProblem->getEndTime() - StepSize * 1e-5;
  NextTimeToReport = modifiedEndTime;

  while ((*L)(Time, modifiedEndTime) && flagProceed)
    {
      ActualStepSize = pMethod->step(pProblem->getEndTime() - Time);

      if (mpProgressHandler)
        {
          Percentage = (Time - pProblem->getStartTime()) * handlerFactor;
          flagProceed = mpProgressHandler->progress(hProcess);
        }

      if ((mDoOutput) && (*LE)(outputStartTime, Time) && !(Time < NextTimeToReport))
        {
          pProblem->getModel()->setState(mpState);
          pProblem->getModel()->updateRates();
          mReport.printBody();
          if (mpOutputHandler) mpOutputHandler->doOutput();
          if (mTimeSeriesRequested) mTimeSeries.add();
        }

      if (ActualStepSize != (pProblem->getEndTime() - Time))
        {
          /* Here we will do conditional event processing */
        }
    }

  //pProblem->setEndState(new CState(*mpState));

  if (mpProgressHandler) mpProgressHandler->finish(hProcess);
  pProblem->getModel()->setState(mpState);
  pProblem->getModel()->updateRates();

  if ((*LE)(outputStartTime, Time) && (mDoOutput))
    {
      mReport.printFooter();
      if (mpOutputHandler) mpOutputHandler->finish();
      if (mTimeSeriesRequested) mTimeSeries.finish();
    }

  return true;
}

//virtual
bool CTrajectoryTask::processForScan(bool useInitialConditions, bool doOutput)
{
  assert(/*mpProblem && */mpMethod);
  mpMethod->isValidProblem(mpProblem);

  CTrajectoryProblem* pProblem =
    dynamic_cast<CTrajectoryProblem *>(mpProblem);
  assert(pProblem);

  //set flag for output
  OutputFlag storeOutput = mDoOutput;
  if (doOutput)
    mDoOutput = OUTPUT;
  else
    mDoOutput = NO_OUTPUT;

  //handle initial conditions
  CState storeState;
  if (!useInitialConditions)
    {
      storeState = pProblem->getInitialState();
      pProblem->getModel()->setTime(pProblem->getInitialState().getTime());
      pProblem->setInitialState(pProblem->getModel()->getState());
      // pProblem->getInitialState().setTime(storeState.getTime());
    }

  //switch off time series storage
  bool storeTS = pProblem->timeSeriesRequested();
  pProblem->setTimeSeriesRequested(false);

  //do the calculation
  process();

  //restore ...
  if (!useInitialConditions) pProblem->setInitialState(storeState);
  mDoOutput = storeOutput;
  pProblem->setTimeSeriesRequested(storeTS);

  return true;
}

bool CTrajectoryTask::processSimple(bool singleStep) //without output
{
  assert(/*mpProblem && */mpMethod);
  mpMethod->isValidProblem(mpProblem); //TODO perhaps omit this check for performance reasons?

  CTrajectoryProblem * pProblem = (CTrajectoryProblem *) mpProblem;
  CTrajectoryMethod * pMethod = (CTrajectoryMethod *) mpMethod;

  //give the method a state to work on
  pdelete(mpState);
  mpState = new CState(pProblem->getInitialState());
  pMethod->setCurrentState(mpState);

  pMethod->setProblem(pProblem);

  C_FLOAT64 StepSize = pProblem->getEndTime() - pProblem->getInitialState().getTime();
  bool (*L)(const C_FLOAT64 &, const C_FLOAT64 &) = (StepSize < 0.0) ? &bl : &fl;

  bool flagProceed = false;
  C_FLOAT64 handlerFactor = 100.0 / (pProblem->getEndTime() - pProblem->getStartTime());

  C_FLOAT64 Percentage = 0;
  unsigned C_INT32 hProcess;
  if (mpProgressHandler)
    {
      mpProgressHandler->setName("performing simulation...");
      C_FLOAT64 hundred = 100;
      hProcess = mpProgressHandler->addItem("%",
                                            CCopasiParameter::DOUBLE,
                                            &Percentage,
                                            &hundred);
    }

  //first step
  pMethod->step(StepSize, &pProblem->getInitialState());

  if (mpProgressHandler)
    {
      Percentage = (mpState->getTime() - pProblem->getStartTime()) * handlerFactor;
      flagProceed = mpProgressHandler->progress(hProcess);
    }

  if (mpState->getTime() == pProblem->getEndTime()) return true; //end reached in one step
  if (singleStep) return false; //end not reached but only one step requested

  //more Steps if necessary
  while ((*L)(mpState->getTime(), pProblem->getEndTime()) && (!flagProceed))
    {
      StepSize = pProblem->getEndTime() - mpState->getTime();
      pMethod->step(StepSize);

      if (mpProgressHandler)
        {
          Percentage = (mpState->getTime() - pProblem->getStartTime()) * handlerFactor;
          flagProceed = mpProgressHandler->progress(hProcess);
        }
    }

  //pProblem->setEndState(new CState(*mpState));

  if (mpProgressHandler) mpProgressHandler->finish(hProcess);

  return true;
}

bool CTrajectoryTask::setMethodType(const int & type)
{
  CCopasiMethod::SubType Type = (CCopasiMethod::SubType) type;

  if (!CTrajectoryMethod::isValidSubType(Type)) return false;
  if (mpMethod->getSubType() == Type) return true;

  pdelete (mpMethod);
  mpMethod =
    CTrajectoryMethod::createTrajectoryMethod(Type,
        (CTrajectoryProblem *) mpProblem);
  this->add(mpMethod, true);
  //mpMethod->setObjectParent(this);

  return true;
}

CState * CTrajectoryTask::getState()
{return mpState;}

const CTimeSeries & CTrajectoryTask::getTimeSeries() const
  {return mTimeSeries;}
<commit_msg>Fixed the multiple reporting of time points described in Bug 335.<commit_after>/* Begin CVS Header
   $Source: /Volumes/Home/Users/shoops/cvs/copasi_dev/copasi/trajectory/CTrajectoryTask.cpp,v $
   $Revision: 1.52 $
   $Name:  $
   $Author: shoops $
   $Date: 2005/07/13 17:02:54 $
   End CVS Header */

/**
 * CTrajectoryTask class.
 *
 * This class implements a trajectory task which is comprised of a
 * of a problem and a method. Additionally calls to the reporting
 * methods are done when initialized.
 *
 * Created for Copasi by Stefan Hoops 2002
 */

#include <string>

#include "copasi.h"

#include "CTrajectoryTask.h"
#include "CTrajectoryProblem.h"
#include "CTrajectoryMethod.h"
#include "model/CModel.h"
#include "model/CState.h"
#include "report/CKeyFactory.h"
#include "report/CReport.h"
#include "utilities/COutputHandler.h"
#include "utilities/CProcessReport.h"

#define XXXX_Reporting

bool fle(const C_FLOAT64 & d1, const C_FLOAT64 & d2)
{return (d1 <= d2);}

bool fl(const C_FLOAT64 & d1, const C_FLOAT64 & d2)
{return (d1 < d2);}

bool ble(const C_FLOAT64 & d1, const C_FLOAT64 & d2)
{return (d1 >= d2);}

bool bl(const C_FLOAT64 & d1, const C_FLOAT64 & d2)
{return (d1 > d2);}

CTrajectoryTask::CTrajectoryTask(const CCopasiContainer * pParent):
    CCopasiTask(CCopasiTask::timeCourse, pParent),
    mpState(NULL),
    mTimeSeriesRequested(true),
    mDoOutput(OUTPUT_COMPLETE)
{
  mpProblem = new CTrajectoryProblem(this);
  mpMethod =
    CTrajectoryMethod::createTrajectoryMethod(CCopasiMethod::deterministic,
        (CTrajectoryProblem *) mpProblem);
  this->add(mpMethod, true);
  //mpMethod->setObjectParent(this);
}

/*CTrajectoryTask::CTrajectoryTask(const CTrajectoryTask & src,
                                 const CCopasiContainer * pParent):
    CCopasiTask(src, pParent),
    mpState(src.mpState),
    mTimeSeriesRequested(src.mTimeSeriesRequested)
{fatalError();}*/

CTrajectoryTask::~CTrajectoryTask()
{
  cleanup();
}

void CTrajectoryTask::cleanup()
{
  pdelete(mpState);
}

void CTrajectoryTask::load(CReadConfig & configBuffer)
{
  configBuffer.getVariable("Dynamics", "bool", &mScheduled,
                           CReadConfig::LOOP);

  pdelete(mpProblem);
  mpProblem = new CTrajectoryProblem(this);
  ((CTrajectoryProblem *) mpProblem)->load(configBuffer);

  pdelete(mpMethod);
  mpMethod = CTrajectoryMethod::createTrajectoryMethod();
  this->add(mpMethod, true);
  //mpMethod->setObjectParent(this);
  ((CTrajectoryMethod *)mpMethod)->setProblem((CTrajectoryProblem *) mpProblem);
}

bool CTrajectoryTask::initialize(std::ostream * pOstream)
{
  assert(mpProblem && mpMethod);

  CTrajectoryProblem* pProblem =
    dynamic_cast<CTrajectoryProblem *>(mpProblem);
  assert(pProblem);

  bool success = true;

  if (!mReport.open(pOstream)) success = false;
  if (!mReport.compile()) success = false;
  if (!pProblem->getModel()->compileIfNecessary()) success = false;
  //  if (!pProblem->
  //      setInitialState(pProblem->getModel()->getInitialState()))
  //    success = false;

  //pProblem->setInitialState(pProblem->getModel()->getInitialState());

  return success;
}

bool CTrajectoryTask::process()
{
  assert(/*mpProblem && */mpMethod);
  mpMethod->isValidProblem(mpProblem);

  CTrajectoryProblem * pProblem = (CTrajectoryProblem *) mpProblem;
  CTrajectoryMethod * pMethod = (CTrajectoryMethod *) mpMethod;

  //the following is a hack that has to disappear soon.
  pProblem->setInitialState(pProblem->getModel()->getInitialState());

  mTimeSeriesRequested = pProblem->timeSeriesRequested();

  pdelete(mpState);
  mpState = new CState(pProblem->getInitialState());

  pMethod->setCurrentState(mpState);
  pMethod->setProblem(pProblem);

  C_FLOAT64 StepSize = pProblem->getStepSize();
  C_FLOAT64 NextTimeToReport;

  bool (*LE)(const C_FLOAT64 &, const C_FLOAT64 &);
  bool (*L)(const C_FLOAT64 &, const C_FLOAT64 &);
  if (StepSize < 0.0)
    {
      LE = &ble;
      L = &bl;
    }
  else
    {
      LE = &fle;
      L = &fl;
    }

  C_FLOAT64 ActualStepSize;
  const C_FLOAT64 & Time = mpState->getTime();
  C_FLOAT64 EndTime = pProblem->getEndTime() - StepSize;
  C_FLOAT64 outputStartTime = pProblem->getOutputStartTime();

  if (StepSize == 0.0 && pProblem->getEndTime() != pProblem->getStartTime())
    {
      CCopasiMessage(CCopasiMessage::ERROR, MCTrajectoryProblem + 1, StepSize);
      return false;
    }
  bool flagProceed = true;
  C_FLOAT64 handlerFactor = 100.0 / (pProblem->getEndTime() - pProblem->getStartTime());

  /*
    if (mpProgressHandler)
      mpProgressHandler->init(1000,
                              "performing simulation...",
                              true);
  */
  C_FLOAT64 Percentage = 0;
  unsigned C_INT32 hProcess;

  if (mpProgressHandler)
    {
      mpProgressHandler->setName("performing simulation...");
      C_FLOAT64 hundred = 100;
      hProcess = mpProgressHandler->addItem("Completion",
                                            CCopasiParameter::DOUBLE,
                                            &Percentage,
                                            &hundred);
    }

  if (mDoOutput)
    {
      pProblem->getModel()->setState(mpState);
      pProblem->getModel()->updateRates();
    }

  if (mDoOutput == OUTPUT_COMPLETE)
    {
      pProblem->getModel()->setState(mpState);
      pProblem->getModel()->updateRates();
      mReport.printHeader();
      if (mpOutputHandler) mpOutputHandler->init();
      if (mTimeSeriesRequested) mTimeSeries.init(pProblem->getStepNumber(), mpState);
    }

  if ((*LE)(outputStartTime, Time) && (mDoOutput))
    {
      mReport.printBody();
      if (mpOutputHandler) mpOutputHandler->doOutput();
      if (mTimeSeriesRequested) mTimeSeries.add();
    }

  // We start the integration
  NextTimeToReport = Time + StepSize;
  ActualStepSize = pMethod->step(StepSize, mpState);

  if (mpProgressHandler)
    {
      Percentage = (Time - pProblem->getStartTime()) * handlerFactor;
      flagProceed = mpProgressHandler->progress(hProcess);
    }

  if ((mDoOutput) &&
      (*LE)(outputStartTime, Time) &&
      !(*L)(Time, NextTimeToReport * (1 - 100 * DBL_EPSILON)))
    {
      pProblem->getModel()->setState(mpState);
      pProblem->getModel()->updateRates();
      mReport.printBody();
      if (mpOutputHandler) mpOutputHandler->doOutput();
      if (mTimeSeriesRequested) mTimeSeries.add();
    }

  if ((*L)(Time, NextTimeToReport))
    {
      /* Here we will do conditional event processing */

      StepSize = NextTimeToReport - Time;
    }
  else
    {
      NextTimeToReport += pProblem->getStepSize();
      StepSize = NextTimeToReport - Time;
    }

  while ((*L)(Time, EndTime) && flagProceed)
    {
      ActualStepSize = pMethod->step(StepSize);

      //std::cout << EndTime << "  " << Time << "  " << EndTime-Time << std::endl;

      if (mpProgressHandler)
        {
          Percentage = (Time - pProblem->getStartTime()) * handlerFactor;
          flagProceed = mpProgressHandler->progress(hProcess);
        }

      if ((mDoOutput) &&
          (*LE)(outputStartTime, Time) &&
          !(*L)(Time, NextTimeToReport * (1 - 100 * DBL_EPSILON)))
        {
          pProblem->getModel()->setState(mpState);
          pProblem->getModel()->updateRates();
          mReport.printBody();
          if (mpOutputHandler) mpOutputHandler->doOutput();
          if (mTimeSeriesRequested) mTimeSeries.add();
        }

      if ((*L)(Time, NextTimeToReport * (1 - 100 * DBL_EPSILON)))
        {
          /* Here we will do conditional event processing */

          // Increase the step size by multplying with
          // (1 + DBL_EPSILON) to avoid infinite loops
          StepSize = NextTimeToReport - Time;
        }
      else
        {
          NextTimeToReport += pProblem->getStepSize();
          StepSize = NextTimeToReport - Time;
        }
    }

  while ((*L)(Time, NextTimeToReport * (1 - 100 * DBL_EPSILON)) && flagProceed)
    {
      ActualStepSize = pMethod->step(pProblem->getEndTime() - Time);

      if (mpProgressHandler)
        {
          Percentage = (Time - pProblem->getStartTime()) * handlerFactor;
          flagProceed = mpProgressHandler->progress(hProcess);
        }

      if ((mDoOutput) && (*LE)(outputStartTime, Time) && !(*L)(Time, NextTimeToReport * (1 - 100 * DBL_EPSILON)))
        {
          pProblem->getModel()->setState(mpState);
          pProblem->getModel()->updateRates();
          mReport.printBody();
          if (mpOutputHandler) mpOutputHandler->doOutput();
          if (mTimeSeriesRequested) mTimeSeries.add();
        }

      if ((*L)(Time, NextTimeToReport * (1 - 100 * DBL_EPSILON)))
        {
          /* Here we will do conditional event processing */

          StepSize = NextTimeToReport - Time;
        }
    }

  //pProblem->setEndState(new CState(*mpState));

  if (mpProgressHandler) mpProgressHandler->finish(hProcess);
  pProblem->getModel()->setState(mpState);
  pProblem->getModel()->updateRates();

  if ((*LE)(outputStartTime, Time) && (mDoOutput))
    {
      mReport.printFooter();
      if (mpOutputHandler) mpOutputHandler->finish();
      if (mTimeSeriesRequested) mTimeSeries.finish();
    }

  return true;
}

//virtual
bool CTrajectoryTask::processForScan(bool useInitialConditions, bool doOutput)
{
  assert(/*mpProblem && */mpMethod);
  mpMethod->isValidProblem(mpProblem);

  CTrajectoryProblem* pProblem =
    dynamic_cast<CTrajectoryProblem *>(mpProblem);
  assert(pProblem);

  //set flag for output
  OutputFlag storeOutput = mDoOutput;
  if (doOutput)
    mDoOutput = OUTPUT;
  else
    mDoOutput = NO_OUTPUT;

  //handle initial conditions
  CState storeState;
  if (!useInitialConditions)
    {
      storeState = pProblem->getInitialState();
      pProblem->getModel()->setTime(pProblem->getInitialState().getTime());
      pProblem->setInitialState(pProblem->getModel()->getState());
      // pProblem->getInitialState().setTime(storeState.getTime());
    }

  //switch off time series storage
  bool storeTS = pProblem->timeSeriesRequested();
  pProblem->setTimeSeriesRequested(false);

  //do the calculation
  process();

  //restore ...
  if (!useInitialConditions) pProblem->setInitialState(storeState);
  mDoOutput = storeOutput;
  pProblem->setTimeSeriesRequested(storeTS);

  return true;
}

bool CTrajectoryTask::processSimple(bool singleStep) //without output
{
  assert(/*mpProblem && */mpMethod);
  mpMethod->isValidProblem(mpProblem); //TODO perhaps omit this check for performance reasons?

  CTrajectoryProblem * pProblem = (CTrajectoryProblem *) mpProblem;
  CTrajectoryMethod * pMethod = (CTrajectoryMethod *) mpMethod;

  //give the method a state to work on
  pdelete(mpState);
  mpState = new CState(pProblem->getInitialState());
  pMethod->setCurrentState(mpState);

  pMethod->setProblem(pProblem);

  C_FLOAT64 StepSize = pProblem->getEndTime() - pProblem->getInitialState().getTime();
  bool (*L)(const C_FLOAT64 &, const C_FLOAT64 &) = (StepSize < 0.0) ? &bl : &fl;

  bool flagProceed = false;
  C_FLOAT64 handlerFactor = 100.0 / (pProblem->getEndTime() - pProblem->getStartTime());

  C_FLOAT64 Percentage = 0;
  unsigned C_INT32 hProcess;
  if (mpProgressHandler)
    {
      mpProgressHandler->setName("performing simulation...");
      C_FLOAT64 hundred = 100;
      hProcess = mpProgressHandler->addItem("%",
                                            CCopasiParameter::DOUBLE,
                                            &Percentage,
                                            &hundred);
    }

  //first step
  pMethod->step(StepSize, &pProblem->getInitialState());

  if (mpProgressHandler)
    {
      Percentage = (mpState->getTime() - pProblem->getStartTime()) * handlerFactor;
      flagProceed = mpProgressHandler->progress(hProcess);
    }

  if (mpState->getTime() == pProblem->getEndTime()) return true; //end reached in one step
  if (singleStep) return false; //end not reached but only one step requested

  //more Steps if necessary
  while ((*L)(mpState->getTime(), pProblem->getEndTime()) && (!flagProceed))
    {
      StepSize = pProblem->getEndTime() - mpState->getTime();
      pMethod->step(StepSize);

      if (mpProgressHandler)
        {
          Percentage = (mpState->getTime() - pProblem->getStartTime()) * handlerFactor;
          flagProceed = mpProgressHandler->progress(hProcess);
        }
    }

  //pProblem->setEndState(new CState(*mpState));

  if (mpProgressHandler) mpProgressHandler->finish(hProcess);

  return true;
}

bool CTrajectoryTask::setMethodType(const int & type)
{
  CCopasiMethod::SubType Type = (CCopasiMethod::SubType) type;

  if (!CTrajectoryMethod::isValidSubType(Type)) return false;
  if (mpMethod->getSubType() == Type) return true;

  pdelete (mpMethod);
  mpMethod =
    CTrajectoryMethod::createTrajectoryMethod(Type,
        (CTrajectoryProblem *) mpProblem);
  this->add(mpMethod, true);
  //mpMethod->setObjectParent(this);

  return true;
}

CState * CTrajectoryTask::getState()
{return mpState;}

const CTimeSeries & CTrajectoryTask::getTimeSeries() const
  {return mTimeSeries;}
<|endoftext|>


<commit_before>/*
 * Copyright 2011 Simon A. F. Lund <safl@safl.dk>
 *
 * This file is part of cphVB.
 *
 * cphVB is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * cphVB 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 General Public License
 * along with cphVB. If not, see <http://www.gnu.org/licenses/>.
 */
#include <cphvb.h>
#include <cphvb_compute.h>
#ifdef _WIN32
#include <windows.h>
#include <time.h>
#else
#include <sys/time.h>
#include <unistd.h>
#endif
#include <limits.h>

// We use the same Mersenne Twister implementation as NumPy
typedef struct
{
    unsigned long key[624];
    int pos;
} rk_state;

/* Magic Mersenne Twister constants */
#define N 624
#define M 397
#define MATRIX_A 0x9908b0dfUL
#define UPPER_MASK 0x80000000UL
#define LOWER_MASK 0x7fffffffUL
#define RK_STATE_LEN 624

/* Slightly optimised reference implementation of the Mersenne Twister */
unsigned long
rk_random(rk_state *state)
{
    unsigned long y;

    if (state->pos == RK_STATE_LEN) {
        int i;

        for (i = 0; i < N - M; i++) {
            y = (state->key[i] & UPPER_MASK) | (state->key[i+1] & LOWER_MASK);
            state->key[i] = state->key[i+M] ^ (y>>1) ^ (-(y & 1) & MATRIX_A);
        }
        for (; i < N - 1; i++) {
            y = (state->key[i] & UPPER_MASK) | (state->key[i+1] & LOWER_MASK);
            state->key[i] = state->key[i+(M-N)] ^ (y>>1) ^ (-(y & 1) & MATRIX_A);
        }
        y = (state->key[N - 1] & UPPER_MASK) | (state->key[0] & LOWER_MASK);
        state->key[N - 1] = state->key[M - 1] ^ (y >> 1) ^ (-(y & 1) & MATRIX_A);

        state->pos = 0;
    }
    y = state->key[state->pos++];

    /* Tempering */
    y ^= (y >> 11);
    y ^= (y << 7) & 0x9d2c5680UL;
    y ^= (y << 15) & 0xefc60000UL;
    y ^= (y >> 18);

    return y;
}
int8_t
rk_int8(rk_state *state)
{
    return (int8_t)(rk_random(state) & 0x7f);
}

int16_t
rk_int16(rk_state *state)
{
    return (int16_t)(rk_random(state) & 0x7fff);
}

int32_t
rk_int32(rk_state *state)
{
    return rk_random(state) & 0x7fffffff;
}

int64_t
rk_int64(rk_state *state)
{
    int64_t res = rk_random(state) & 0x7fffffff;
    res = (res << 32) | rk_random(state);
    return res;
}

uint8_t
rk_uint8(rk_state *state)
{
    return (uint8_t)(rk_random(state) & 0xff);
}

uint16_t
rk_uint16(rk_state *state)
{
    return (uint16_t)(rk_random(state) & 0xffff);
}

uint32_t
rk_uint32(rk_state *state)
{
    return rk_random(state);
}

uint64_t
rk_uint64(rk_state *state)
{
    uint64_t res = rk_random(state);
    res = (res << 32) | rk_random(state);
    return res;
}

float
rk_float(rk_state *state)
{
    return rk_random(state) * 2.3283064365387e-10;
}

double
rk_double(rk_state *state)
{
    /* shifts : 67108864 = 0x4000000, 9007199254740992 = 0x20000000000000 */
    long a = rk_random(state) >> 5, b = rk_random(state) >> 6;
    return (a * 67108864.0 + b) / 9007199254740992.0;
}
void
rk_seed(unsigned long seed, rk_state *state)
{
    int pos;
    seed &= 0xffffffffUL;

    /* Knuth's PRNG as used in the Mersenne Twister reference implementation */
    for (pos = 0; pos < RK_STATE_LEN; pos++) {
        state->key[pos] = seed;
        seed = (1812433253UL * (seed ^ (seed >> 30)) + pos + 1) & 0xffffffffUL;
    }
    state->pos = RK_STATE_LEN;
}
/* Thomas Wang 32 bits integer hash function */
unsigned long
rk_hash(unsigned long key)
{
    key += ~(key << 15);
    key ^=  (key >> 10);
    key +=  (key << 3);
    key ^=  (key >> 6);
    key += ~(key << 11);
    key ^=  (key >> 16);
    return key;
}
int
rk_initseed(rk_state *state)
{
#ifdef _WIN32
	time_t ltime;
	time(&ltime);
	//Essentially time_t is a int64
	rk_seed(rk_hash(GetCurrentProcessId()) ^ rk_hash(ltime), state);
#else
    struct timeval tv;
    gettimeofday(&tv, NULL);
    rk_seed(rk_hash(getpid()) ^ rk_hash(tv.tv_sec) ^ rk_hash(tv.tv_usec), state);
#endif
    return 0;
}

//Implementation of the user-defined funtion "random". Note that we
//follows the function signature defined by cphvb_userfunc_impl.
cphvb_error cphvb_compute_random(cphvb_userfunc *arg, void* ve_arg)
{
    cphvb_random_type *a = (cphvb_random_type *) arg;
    cphvb_array *ary = a->operand[0];
    cphvb_intp size = cphvb_nelements(ary->ndim, ary->shape);

    //Make sure that the array memory is allocated.
    if(cphvb_data_malloc(ary) != CPHVB_SUCCESS)
        return CPHVB_OUT_OF_MEMORY;

    rk_state state;
    rk_initseed(&state);

    switch (ary->type)
    {
    	case CPHVB_INT8:
		{
			cphvb_int8* data = (cphvb_int8*)ary->data;
			for(cphvb_intp i=0; i<size; ++i)
				data[i] = rk_int8(&state);
		}
		break;

    	case CPHVB_INT16:
		{
			cphvb_int16* data = (cphvb_int16*)ary->data;
			for(cphvb_intp i=0; i<size; ++i)
				data[i] = rk_int16(&state);
		}
		break;

    	case CPHVB_INT32:
		{
			cphvb_int32* data = (cphvb_int32*)ary->data;
			for(cphvb_intp i=0; i<size; ++i)
				data[i] = rk_int32(&state);
		}
		break;

    	case CPHVB_INT64:
		{
			cphvb_int64* data = (cphvb_int64*)ary->data;
			for(cphvb_intp i=0; i<size; ++i)
				data[i] = rk_int64(&state);
		}
		break;

		case CPHVB_UINT8:
		{
			cphvb_uint8* data = (cphvb_uint8*)ary->data;
			for(cphvb_intp i=0; i<size; ++i)
				data[i] = rk_uint8(&state);
		}
		break;

    	case CPHVB_UINT16:
		{
			cphvb_uint16* data = (cphvb_uint16*)ary->data;
			for(cphvb_intp i=0; i<size; ++i)
				data[i] = rk_uint16(&state);
		}
		break;

    	case CPHVB_UINT32:
		{
			cphvb_uint32* data = (cphvb_uint32*)ary->data;
			for(cphvb_intp i=0; i<size; ++i)
				data[i] = rk_uint32(&state);
		}
		break;

    	case CPHVB_UINT64:
		{
			cphvb_uint64* data = (cphvb_uint64*)ary->data;
			for(cphvb_intp i=0; i<size; ++i)
				data[i] = rk_uint64(&state);
		}
		break;

    	case CPHVB_FLOAT32:
		{
			cphvb_float32* data = (cphvb_float32*)ary->data;
			for(cphvb_intp i=0; i<size; ++i)
				data[i] = rk_float(&state);
		}
		break;

    	case CPHVB_FLOAT64:
		{
			cphvb_float64* data = (cphvb_float64*)ary->data;
			for(cphvb_intp i=0; i<size; ++i)
				data[i] = rk_double(&state);
		}
		break;

    	default:
	        return CPHVB_TYPE_NOT_SUPPORTED;

	}

    return CPHVB_SUCCESS;
}
<commit_msg>Fixed a bug with the random implementation<commit_after>/*
 * Copyright 2011 Simon A. F. Lund <safl@safl.dk>
 *
 * This file is part of cphVB.
 *
 * cphVB is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * cphVB 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 General Public License
 * along with cphVB. If not, see <http://www.gnu.org/licenses/>.
 */
#include <cphvb.h>
#include <cphvb_compute.h>
#ifdef _WIN32
#include <windows.h>
#include <time.h>
#else
#include <sys/time.h>
#include <unistd.h>
#endif
#include <limits.h>

// We use the same Mersenne Twister implementation as NumPy
typedef struct
{
    unsigned long key[624];
    int pos;
} rk_state;

/* Magic Mersenne Twister constants */
#define N 624
#define M 397
#define MATRIX_A 0x9908b0dfUL
#define UPPER_MASK 0x80000000UL
#define LOWER_MASK 0x7fffffffUL
#define RK_STATE_LEN 624

/* Slightly optimised reference implementation of the Mersenne Twister */
unsigned long
rk_random(rk_state *state)
{
    unsigned long y;

    if (state->pos == RK_STATE_LEN) {
        int i;

        for (i = 0; i < N - M; i++) {
            y = (state->key[i] & UPPER_MASK) | (state->key[i+1] & LOWER_MASK);
            state->key[i] = state->key[i+M] ^ (y>>1) ^ (-(y & 1) & MATRIX_A);
        }
        for (; i < N - 1; i++) {
            y = (state->key[i] & UPPER_MASK) | (state->key[i+1] & LOWER_MASK);
            state->key[i] = state->key[i+(M-N)] ^ (y>>1) ^ (-(y & 1) & MATRIX_A);
        }
        y = (state->key[N - 1] & UPPER_MASK) | (state->key[0] & LOWER_MASK);
        state->key[N - 1] = state->key[M - 1] ^ (y >> 1) ^ (-(y & 1) & MATRIX_A);

        state->pos = 0;
    }
    y = state->key[state->pos++];

    /* Tempering */
    y ^= (y >> 11);
    y ^= (y << 7) & 0x9d2c5680UL;
    y ^= (y << 15) & 0xefc60000UL;
    y ^= (y >> 18);

    return y;
}
int8_t
rk_int8(rk_state *state)
{
    return (int8_t)(rk_random(state) & 0x7f);
}

int16_t
rk_int16(rk_state *state)
{
    return (int16_t)(rk_random(state) & 0x7fff);
}

int32_t
rk_int32(rk_state *state)
{
    return rk_random(state) & 0x7fffffff;
}

int64_t
rk_int64(rk_state *state)
{
    int64_t res = rk_random(state) & 0x7fffffff;
    res = (res << 32) | rk_random(state);
    return res;
}

uint8_t
rk_uint8(rk_state *state)
{
    return (uint8_t)(rk_random(state) & 0xff);
}

uint16_t
rk_uint16(rk_state *state)
{
    return (uint16_t)(rk_random(state) & 0xffff);
}

uint32_t
rk_uint32(rk_state *state)
{
    return rk_random(state);
}

uint64_t
rk_uint64(rk_state *state)
{
    uint64_t res = rk_random(state);
    res = (res << 32) | rk_random(state);
    return res;
}

float
rk_float(rk_state *state)
{
    return rk_random(state) * 2.3283064365387e-10;
}

double
rk_double(rk_state *state)
{
    /* shifts : 67108864 = 0x4000000, 9007199254740992 = 0x20000000000000 */
    long a = rk_random(state) >> 5, b = rk_random(state) >> 6;
    return (a * 67108864.0 + b) / 9007199254740992.0;
}
void
rk_seed(unsigned long seed, rk_state *state)
{
    int pos;
    seed &= 0xffffffffUL;

    /* Knuth's PRNG as used in the Mersenne Twister reference implementation */
    for (pos = 0; pos < RK_STATE_LEN; pos++) {
        state->key[pos] = seed;
        seed = (1812433253UL * (seed ^ (seed >> 30)) + pos + 1) & 0xffffffffUL;
    }
    state->pos = RK_STATE_LEN;
}
/* Thomas Wang 32 bits integer hash function */
unsigned long
rk_hash(unsigned long key)
{
    key += ~(key << 15);
    key ^=  (key >> 10);
    key +=  (key << 3);
    key ^=  (key >> 6);
    key += ~(key << 11);
    key ^=  (key >> 16);
    return key;
}
int
rk_initseed(rk_state *state)
{
#ifdef _WIN32
	time_t ltime;
	time(&ltime);
	//Essentially time_t is a int64
	rk_seed(rk_hash(GetCurrentProcessId()) ^ rk_hash(ltime), state);
#else
    struct timeval tv;
    gettimeofday(&tv, NULL);
    rk_seed(rk_hash(getpid()) ^ rk_hash(tv.tv_sec) ^ rk_hash(tv.tv_usec), state);
#endif
    return 0;
}

//Implementation of the user-defined funtion "random". Note that we
//follows the function signature defined by cphvb_userfunc_impl.
cphvb_error cphvb_compute_random(cphvb_userfunc *arg, void* ve_arg)
{
    cphvb_random_type *a = (cphvb_random_type *) arg;
    cphvb_array *ary = a->operand[0];
    cphvb_intp size = cphvb_nelements(ary->ndim, ary->shape);
    cphvb_array *base = cphvb_base_array(ary);

    //Make sure that the array memory is allocated.
    if(cphvb_data_malloc(ary) != CPHVB_SUCCESS)
        return CPHVB_OUT_OF_MEMORY;

    rk_state state;
    rk_initseed(&state);

    switch (ary->type)
    {
    	case CPHVB_INT8:
		{
			cphvb_int8* data = (cphvb_int8*)base->data;
			for(cphvb_intp i=0; i<size; ++i)
				data[i] = rk_int8(&state);
		}
		break;

    	case CPHVB_INT16:
		{
			cphvb_int16* data = (cphvb_int16*)base->data;
			for(cphvb_intp i=0; i<size; ++i)
				data[i] = rk_int16(&state);
		}
		break;

    	case CPHVB_INT32:
		{
			cphvb_int32* data = (cphvb_int32*)base->data;
			for(cphvb_intp i=0; i<size; ++i)
				data[i] = rk_int32(&state);
		}
		break;

    	case CPHVB_INT64:
		{
			cphvb_int64* data = (cphvb_int64*)base->data;
			for(cphvb_intp i=0; i<size; ++i)
				data[i] = rk_int64(&state);
		}
		break;

		case CPHVB_UINT8:
		{
			cphvb_uint8* data = (cphvb_uint8*)base->data;
			for(cphvb_intp i=0; i<size; ++i)
				data[i] = rk_uint8(&state);
		}
		break;

    	case CPHVB_UINT16:
		{
			cphvb_uint16* data = (cphvb_uint16*)base->data;
			for(cphvb_intp i=0; i<size; ++i)
				data[i] = rk_uint16(&state);
		}
		break;

    	case CPHVB_UINT32:
		{
			cphvb_uint32* data = (cphvb_uint32*)base->data;
			for(cphvb_intp i=0; i<size; ++i)
				data[i] = rk_uint32(&state);
		}
		break;

    	case CPHVB_UINT64:
		{
			cphvb_uint64* data = (cphvb_uint64*)base->data;
			for(cphvb_intp i=0; i<size; ++i)
				data[i] = rk_uint64(&state);
		}
		break;

    	case CPHVB_FLOAT32:
		{
			cphvb_float32* data = (cphvb_float32*)base->data;
			for(cphvb_intp i=0; i<size; ++i)
				data[i] = rk_float(&state);
		}
		break;

    	case CPHVB_FLOAT64:
		{
			cphvb_float64* data = (cphvb_float64*)base->data;
			for(cphvb_intp i=0; i<size; ++i)
				data[i] = rk_double(&state);
		}
		break;

    	default:
	        return CPHVB_TYPE_NOT_SUPPORTED;

	}

    return CPHVB_SUCCESS;
}
<|endoftext|>


<commit_before>/*  This file is part of the Kate project.
 *
 *  Copyright (C) 2012 Christoph Cullmann <cullmann@kde.org>
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library 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
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public License
 *  along with this library; see the file COPYING.LIB.  If not, write to
 *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 *  Boston, MA 02110-1301, USA.
 */

#include "kateprojectworker.h"
#include "kateproject.h"

#include <QDir>
#include <QDirIterator>
#include <QFile>
#include <QFileInfo>
#include <QProcess>
#include <QSet>
#include <QTime>

KateProjectWorker::KateProjectWorker (QObject *project)
  : QObject ()
  , m_project (project)
{
}

KateProjectWorker::~KateProjectWorker ()
{
}

void KateProjectWorker::loadProject (QString baseDir, QVariantMap projectMap)
{
  /**
   * setup project base directory
   * this should be FIX after initial setting
   */
  Q_ASSERT (m_baseDir.isEmpty() || (m_baseDir == baseDir));
  m_baseDir = baseDir;

  /**
   * Create dummy top level parent item and empty map inside shared pointers
   * then load the project recursively
   */
  KateProjectSharedQStandardItem topLevel (new QStandardItem ());
  KateProjectSharedQMapStringItem file2Item (new QMap<QString, QStandardItem *> ());
  loadProject (topLevel.data(), projectMap, file2Item.data());

  /**
   * create some local backup of some data we need for further processing!
   */
  QStringList files = file2Item->keys ();

  /**
   * feed back our results
   */
  QMetaObject::invokeMethod (m_project, "loadProjectDone", Qt::QueuedConnection, Q_ARG(KateProjectSharedQStandardItem, topLevel), Q_ARG(KateProjectSharedQMapStringItem, file2Item));

  /**
   * load index
   */
  loadIndex (files);
}

void KateProjectWorker::loadProject (QStandardItem *parent, const QVariantMap &project, QMap<QString, QStandardItem *> *file2Item)
{
  /**
   * recurse to sub-projects FIRST
   */
  QVariantList subGroups = project["projects"].toList ();
  foreach (const QVariant &subGroupVariant, subGroups) {
    /**
     * convert to map and get name, else skip
     */
    QVariantMap subProject = subGroupVariant.toMap ();
    if (subProject["name"].toString().isEmpty())
      continue;

    /**
     * recurse
     */
    QStandardItem *subProjectItem = new KateProjectItem (KateProjectItem::Project, subProject["name"].toString());
    loadProject (subProjectItem, subProject, file2Item);
    parent->appendRow (subProjectItem);
  }

  /**
   * load all specified files
   */
  QVariantList files = project["files"].toList ();
  foreach (const QVariant &fileVariant, files)
    loadFilesEntry (parent, fileVariant.toMap (), file2Item);
}

/**
 * small helper to construct directory parent items
 * @param dir2Item map for path => item
 * @param path current path we need item for
 * @return correct parent item for given path, will reuse existing ones
 */
static QStandardItem *directoryParent (QMap<QString, QStandardItem *> &dir2Item, QString path)
{
  /**
   * throw away simple /
   */
  if (path == "/")
    path = "";

  /**
   * quick check: dir already seen?
   */
  if (dir2Item.contains (path))
    return dir2Item[path];

  /**
   * else: construct recursively
   */
  int slashIndex = path.lastIndexOf ('/');

  /**
   * no slash?
   * simple, no recursion, append new item toplevel
   */
  if (slashIndex < 0) {
    dir2Item[path] = new KateProjectItem (KateProjectItem::Directory, path);
    dir2Item[""]->appendRow (dir2Item[path]);
    return dir2Item[path];
  }

  /**
   * else, split and recurse
   */
  QString leftPart = path.left (slashIndex);
  QString rightPart = path.right (path.size() - (slashIndex + 1));

  /**
   * special handling if / with nothing on one side are found
   */
  if (leftPart.isEmpty() || rightPart.isEmpty ())
    return directoryParent (dir2Item, leftPart.isEmpty() ? rightPart : leftPart);

  /**
   * else: recurse on left side
   */
  dir2Item[path] = new KateProjectItem (KateProjectItem::Directory, rightPart);
  directoryParent (dir2Item, leftPart)->appendRow (dir2Item[path]);
  return dir2Item[path];
}

void KateProjectWorker::loadFilesEntry (QStandardItem *parent, const QVariantMap &filesEntry, QMap<QString, QStandardItem *> *file2Item)
{
  /**
   * get directory to open or skip
   */
  QDir dir (m_baseDir);
  if (!dir.cd (filesEntry["directory"].toString()))
    return;

  /**
   * get recursive attribute, default is TRUE
   */
  const bool recursive = !filesEntry.contains ("recursive") || filesEntry["recursive"].toBool();

  /**
   * now: choose between different methodes to get files in the directory
   */
  QStringList files;

  /**
   * use GIT
   */
  if (filesEntry["git"].toBool()) {
    /**
     * try to run git with ls-files for this directory
     */
    QProcess git;
    git.setWorkingDirectory (dir.absolutePath());
    QStringList args;
    args << "ls-files" << ".";
    git.start("git", args);
    if (!git.waitForStarted() || !git.waitForFinished())
      return;

    /**
     * get output and split up into files
     */
    QStringList relFiles = QString::fromLocal8Bit (git.readAllStandardOutput ()).split (QRegExp("[\n\r]"), QString::SkipEmptyParts);

    /**
     * prepend the directory path
     */
    foreach (QString relFile, relFiles) {
      /**
       * skip non-direct files if not recursive
       */
      if (!recursive && (relFile.indexOf ("/") != -1))
        continue;

      files.append (dir.absolutePath() + "/" + relFile);
    }
  }

  /**
   * use SVN
   */
  else if (filesEntry["svn"].toBool()) {
    /**
     * try to run git with ls-files for this directory
     */
    QProcess svn;
    svn.setWorkingDirectory (dir.absolutePath());
    QStringList args;
    args << "status" << "--verbose" << ".";
    if (recursive)
      args << "--depth=infinity";
    else
      args << "--depth=files";
    svn.start("svn", args);
    if (!svn.waitForStarted() || !svn.waitForFinished())
      return;

    /**
     * get output and split up into lines
     */
    QStringList lines = QString::fromLocal8Bit (svn.readAllStandardOutput ()).split (QRegExp("[\n\r]"), QString::SkipEmptyParts);

    /**
     * remove start of line that is no filename, sort out unknown and ignore
     */
    bool first = true;
    int prefixLength = -1;
    foreach (QString line, lines) {
        /**
         * get length of stuff to cut
         */
        if (first) {
          /**
           * try to find ., else fail
           */
          prefixLength = line.lastIndexOf (".");
          if (prefixLength < 0)
                break;

          /**
           * skip first
           */
          first = false;
          continue;
        }

        /**
         * get file, if not unknown or ignored
         * prepend directory path
         */
        if ((line.size() > prefixLength) && line[0] != '?' && line[0] != 'I')
          files.append (dir.absolutePath() + "/" + line.right (line.size() - prefixLength));
    }
  }

  /**
   * fallback to use QDirIterator and search files ourself!
   */
  else {
    /**
    * default filter: only files!
    */
    dir.setFilter (QDir::Files);

    /**
    * set name filters, if any
    */
    QStringList filters = filesEntry["filters"].toStringList();
    if (!filters.isEmpty())
      dir.setNameFilters (filters);

    /**
    * construct flags for iterator
    */
    QDirIterator::IteratorFlags flags = QDirIterator::NoIteratorFlags;
    if (recursive)
      flags = flags | QDirIterator::Subdirectories;

    /**
    * create iterator and collect all files
    */
    QDirIterator dirIterator (dir, flags);
    while (dirIterator.hasNext()) {
      dirIterator.next();
      files.append (dirIterator.filePath());
    }
  }

  /**
   * sort them
   */
  files.sort ();

  /**
   * construct paths first in tree and items in a map
   */
  QMap<QString, QStandardItem *> dir2Item;
  dir2Item[""] = parent;
  QList<QPair<QStandardItem *, QStandardItem *> > item2ParentPath;
  foreach (QString filePath, files) {
    /**
     * get file info and skip NON-files
     */
    QFileInfo fileInfo (filePath);
    if (!fileInfo.isFile())
      continue;

    /**
      * skip dupes
      */
     if (file2Item->contains(filePath))
       continue;

     /**
      * construct the item with right directory prefix
      * already hang in directories in tree
      */
     QStandardItem *fileItem = new KateProjectItem (KateProjectItem::File, fileInfo.fileName());
     fileItem->setData(filePath,Qt::ToolTipRole);
     item2ParentPath.append (QPair<QStandardItem *, QStandardItem *>(fileItem, directoryParent(dir2Item, dir.relativeFilePath (fileInfo.absolutePath()))));
     fileItem->setData (filePath, Qt::UserRole);
     (*file2Item)[filePath] = fileItem;
  }

  /**
   * plug in the file items to the tree
   */
  QList<QPair<QStandardItem *, QStandardItem *> >::const_iterator i = item2ParentPath.constBegin();
  while (i != item2ParentPath.constEnd()) {
    i->second->appendRow (i->first);
    ++i;
  }
}

void KateProjectWorker::loadIndex (const QStringList &files)
{
  /**
   * create new index, this will do the loading in the constructor
   * wrap it into shared pointer for transfer to main thread
   */
  KateProjectSharedProjectIndex index (new KateProjectIndex(files));

  /**
   * send new index object back to project
   */
  QMetaObject::invokeMethod (m_project, "loadIndexDone", Qt::QueuedConnection, Q_ARG(KateProjectSharedProjectIndex, index));
}

// kate: space-indent on; indent-width 2; replace-tabs on;
<commit_msg>projects: add support for a "list" entry<commit_after>/*  This file is part of the Kate project.
 *
 *  Copyright (C) 2012 Christoph Cullmann <cullmann@kde.org>
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library 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
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public License
 *  along with this library; see the file COPYING.LIB.  If not, write to
 *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 *  Boston, MA 02110-1301, USA.
 */

#include "kateprojectworker.h"
#include "kateproject.h"

#include <QDir>
#include <QDirIterator>
#include <QFile>
#include <QFileInfo>
#include <QProcess>
#include <QSet>
#include <QTime>

KateProjectWorker::KateProjectWorker (QObject *project)
  : QObject ()
  , m_project (project)
{
}

KateProjectWorker::~KateProjectWorker ()
{
}

void KateProjectWorker::loadProject (QString baseDir, QVariantMap projectMap)
{
  /**
   * setup project base directory
   * this should be FIX after initial setting
   */
  Q_ASSERT (m_baseDir.isEmpty() || (m_baseDir == baseDir));
  m_baseDir = baseDir;

  /**
   * Create dummy top level parent item and empty map inside shared pointers
   * then load the project recursively
   */
  KateProjectSharedQStandardItem topLevel (new QStandardItem ());
  KateProjectSharedQMapStringItem file2Item (new QMap<QString, QStandardItem *> ());
  loadProject (topLevel.data(), projectMap, file2Item.data());

  /**
   * create some local backup of some data we need for further processing!
   */
  QStringList files = file2Item->keys ();

  /**
   * feed back our results
   */
  QMetaObject::invokeMethod (m_project, "loadProjectDone", Qt::QueuedConnection, Q_ARG(KateProjectSharedQStandardItem, topLevel), Q_ARG(KateProjectSharedQMapStringItem, file2Item));

  /**
   * load index
   */
  loadIndex (files);
}

void KateProjectWorker::loadProject (QStandardItem *parent, const QVariantMap &project, QMap<QString, QStandardItem *> *file2Item)
{
  /**
   * recurse to sub-projects FIRST
   */
  QVariantList subGroups = project["projects"].toList ();
  foreach (const QVariant &subGroupVariant, subGroups) {
    /**
     * convert to map and get name, else skip
     */
    QVariantMap subProject = subGroupVariant.toMap ();
    if (subProject["name"].toString().isEmpty())
      continue;

    /**
     * recurse
     */
    QStandardItem *subProjectItem = new KateProjectItem (KateProjectItem::Project, subProject["name"].toString());
    loadProject (subProjectItem, subProject, file2Item);
    parent->appendRow (subProjectItem);
  }

  /**
   * load all specified files
   */
  QVariantList files = project["files"].toList ();
  foreach (const QVariant &fileVariant, files)
    loadFilesEntry (parent, fileVariant.toMap (), file2Item);
}

/**
 * small helper to construct directory parent items
 * @param dir2Item map for path => item
 * @param path current path we need item for
 * @return correct parent item for given path, will reuse existing ones
 */
static QStandardItem *directoryParent (QMap<QString, QStandardItem *> &dir2Item, QString path)
{
  /**
   * throw away simple /
   */
  if (path == "/")
    path = "";

  /**
   * quick check: dir already seen?
   */
  if (dir2Item.contains (path))
    return dir2Item[path];

  /**
   * else: construct recursively
   */
  int slashIndex = path.lastIndexOf ('/');

  /**
   * no slash?
   * simple, no recursion, append new item toplevel
   */
  if (slashIndex < 0) {
    dir2Item[path] = new KateProjectItem (KateProjectItem::Directory, path);
    dir2Item[""]->appendRow (dir2Item[path]);
    return dir2Item[path];
  }

  /**
   * else, split and recurse
   */
  QString leftPart = path.left (slashIndex);
  QString rightPart = path.right (path.size() - (slashIndex + 1));

  /**
   * special handling if / with nothing on one side are found
   */
  if (leftPart.isEmpty() || rightPart.isEmpty ())
    return directoryParent (dir2Item, leftPart.isEmpty() ? rightPart : leftPart);

  /**
   * else: recurse on left side
   */
  dir2Item[path] = new KateProjectItem (KateProjectItem::Directory, rightPart);
  directoryParent (dir2Item, leftPart)->appendRow (dir2Item[path]);
  return dir2Item[path];
}

void KateProjectWorker::loadFilesEntry (QStandardItem *parent, const QVariantMap &filesEntry, QMap<QString, QStandardItem *> *file2Item)
{
  /**
   * get directory to open or skip
   */
  QDir dir (m_baseDir);
  if (!dir.cd (filesEntry["directory"].toString()))
    return;

  /**
   * get recursive attribute, default is TRUE
   */
  const bool recursive = !filesEntry.contains ("recursive") || filesEntry["recursive"].toBool();

  /**
   * now: choose between different methodes to get files in the directory
   */
  QStringList files;

  /**
   * use GIT
   */
  if (filesEntry["git"].toBool()) {
    /**
     * try to run git with ls-files for this directory
     */
    QProcess git;
    git.setWorkingDirectory (dir.absolutePath());
    QStringList args;
    args << "ls-files" << ".";
    git.start("git", args);
    if (!git.waitForStarted() || !git.waitForFinished())
      return;

    /**
     * get output and split up into files
     */
    QStringList relFiles = QString::fromLocal8Bit (git.readAllStandardOutput ()).split (QRegExp("[\n\r]"), QString::SkipEmptyParts);

    /**
     * prepend the directory path
     */
    foreach (QString relFile, relFiles) {
      /**
       * skip non-direct files if not recursive
       */
      if (!recursive && (relFile.indexOf ("/") != -1))
        continue;

      files.append (dir.absolutePath() + "/" + relFile);
    }
  }

  /**
   * use SVN
   */
  else if (filesEntry["svn"].toBool()) {
    /**
     * try to run git with ls-files for this directory
     */
    QProcess svn;
    svn.setWorkingDirectory (dir.absolutePath());
    QStringList args;
    args << "status" << "--verbose" << ".";
    if (recursive)
      args << "--depth=infinity";
    else
      args << "--depth=files";
    svn.start("svn", args);
    if (!svn.waitForStarted() || !svn.waitForFinished())
      return;

    /**
     * get output and split up into lines
     */
    QStringList lines = QString::fromLocal8Bit (svn.readAllStandardOutput ()).split (QRegExp("[\n\r]"), QString::SkipEmptyParts);

    /**
     * remove start of line that is no filename, sort out unknown and ignore
     */
    bool first = true;
    int prefixLength = -1;
    foreach (QString line, lines) {
        /**
         * get length of stuff to cut
         */
        if (first) {
          /**
           * try to find ., else fail
           */
          prefixLength = line.lastIndexOf (".");
          if (prefixLength < 0)
                break;

          /**
           * skip first
           */
          first = false;
          continue;
        }

        /**
         * get file, if not unknown or ignored
         * prepend directory path
         */
        if ((line.size() > prefixLength) && line[0] != '?' && line[0] != 'I')
          files.append (dir.absolutePath() + "/" + line.right (line.size() - prefixLength));
    }
  }

  else {
    files = filesEntry["list"].toStringList();

    /**
    * fallback to use QDirIterator and search files ourself!
    */
    if (files.empty()) {
      /**
      * default filter: only files!
      */
      dir.setFilter (QDir::Files);

      /**
      * set name filters, if any
      */
      QStringList filters = filesEntry["filters"].toStringList();
      if (!filters.isEmpty())
        dir.setNameFilters (filters);

      /**
      * construct flags for iterator
      */
      QDirIterator::IteratorFlags flags = QDirIterator::NoIteratorFlags;
      if (recursive)
        flags = flags | QDirIterator::Subdirectories;

      /**
      * create iterator and collect all files
      */
      QDirIterator dirIterator (dir, flags);
      while (dirIterator.hasNext()) {
        dirIterator.next();
        files.append (dirIterator.filePath());
      }
    }
  }

  /**
   * sort them
   */
  files.sort ();

  /**
   * construct paths first in tree and items in a map
   */
  QMap<QString, QStandardItem *> dir2Item;
  dir2Item[""] = parent;
  QList<QPair<QStandardItem *, QStandardItem *> > item2ParentPath;
  foreach (QString filePath, files) {
    /**
     * get file info and skip NON-files
     */
    QFileInfo fileInfo (filePath);
    if (!fileInfo.isFile())
      continue;

    /**
      * skip dupes
      */
     if (file2Item->contains(filePath))
       continue;

     /**
      * construct the item with right directory prefix
      * already hang in directories in tree
      */
     QStandardItem *fileItem = new KateProjectItem (KateProjectItem::File, fileInfo.fileName());
     fileItem->setData(filePath,Qt::ToolTipRole);
     item2ParentPath.append (QPair<QStandardItem *, QStandardItem *>(fileItem, directoryParent(dir2Item, dir.relativeFilePath (fileInfo.absolutePath()))));
     fileItem->setData (filePath, Qt::UserRole);
     (*file2Item)[filePath] = fileItem;
  }

  /**
   * plug in the file items to the tree
   */
  QList<QPair<QStandardItem *, QStandardItem *> >::const_iterator i = item2ParentPath.constBegin();
  while (i != item2ParentPath.constEnd()) {
    i->second->appendRow (i->first);
    ++i;
  }
}

void KateProjectWorker::loadIndex (const QStringList &files)
{
  /**
   * create new index, this will do the loading in the constructor
   * wrap it into shared pointer for transfer to main thread
   */
  KateProjectSharedProjectIndex index (new KateProjectIndex(files));

  /**
   * send new index object back to project
   */
  QMetaObject::invokeMethod (m_project, "loadIndexDone", Qt::QueuedConnection, Q_ARG(KateProjectSharedProjectIndex, index));
}

// kate: space-indent on; indent-width 2; replace-tabs on;
<|endoftext|>