<commit_before>// Copyright 2016 DIANA-HEP
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     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 <iostream>
#include <vector>
#include <string>

#include <Python.h>
#include <numpy/arrayobject.h>

#include <TFile.h>
#include <TError.h>
#include <TTree.h>
#include <TLeaf.h>
#include <TBranch.h>
#include <TBranchElement.h>

static char module_docstring[] = "Simple, streamlined Numpy array-filling from ROOT.";
static char fillarrays_docstring[] = "Fills N arrays at once from a ROOT file's TTree.\n\nparams:\n    fileName: string, can include root:// protocol\n    ttreeName: string, can include directory slashes\n    arrays: list of (string, array) or (string, string, array, array) tuples: (data name, data array) or (data name, size name, data array, size array). Arrays must be preallocated.\n\nRaises IndexError if more values are found in the ROOT file than are allocated in the array.";

static PyObject* fillarrays(PyObject* self, PyObject* args);

static PyMethodDef module_methods[] = {
  {"fillarrays", (PyCFunction)fillarrays, METH_VARARGS, fillarrays_docstring},
  {NULL, NULL, 0, NULL}
};

#if PY_MAJOR_VERSION >= 3
static struct PyModuleDef moduledef = {
  PyModuleDef_HEAD_INIT,
  "_fastreader",
  NULL,
  0,
  module_methods,
  NULL,
  NULL,
  NULL,
  NULL
};

PyMODINIT_FUNC PyInit__fastreader(void) {
  PyObject* module = PyModule_Create(&moduledef);
  if (module != NULL)
    import_array();
  return module;
}
#else
PyMODINIT_FUNC init_fastreader(void) {
  PyObject* module = Py_InitModule3("_fastreader", module_methods, module_docstring);
  if (module != NULL)
    import_array();
}
#endif

class BranchArrayInfo {
public:
  int64_t dataIndex;
  int64_t sizeIndex;

  TBranch* dataBranch;
  char* bufferForEntry;
  Int_t sizeForEntry;

  int dataItemBytes;
  void* dataPointer;
  void* sizePointer;
  char* dataName;
  char* sizeName;
  int64_t dataLength;
  int64_t sizeLength;
  char dataType;
  bool flat;

  BranchArrayInfo(void* dataPointer, void* sizePointer, char* dataName, char* sizeName, int64_t dataLength, int64_t sizeLength, char dataType, int dataItemBytes, bool flat):
    dataIndex(0), sizeIndex(0), dataBranch(NULL), bufferForEntry(NULL), sizeForEntry(0),
    dataPointer(dataPointer), sizePointer(sizePointer), dataName(dataName), sizeName(sizeName), dataLength(dataLength), sizeLength(sizeLength), dataType(dataType), dataItemBytes(dataItemBytes), flat(flat) { }

  ~BranchArrayInfo() {
    if (bufferForEntry != NULL) delete bufferForEntry;
  }
};

static PyObject* fillarrays(PyObject* self, PyObject* args) {
  char* fileName;
  char* treeName;
  PyObject* branches_arrays;

  if (!PyArg_ParseTuple(args, "ssO", &fileName, &treeName, &branches_arrays))
    return NULL;

  if (!PySequence_Check(branches_arrays)) {
    PyErr_SetString(PyExc_TypeError, "third argument must be a sequence of (string, array) pairs");
    return NULL;
  }

  int numArrays = PySequence_Length(branches_arrays);
  std::vector<BranchArrayInfo> branchArrayInfos;

  for (Py_ssize_t i = 0;  i < numArrays;  i++) {
    // Doesn't need to be fast, but PySequence_Fast_GET_ITEM returns a borrowed reference, which is nice because I don't have to handle references.
    PyObject* branch_array = PySequence_Fast_GET_ITEM(branches_arrays, i);
    if (!PyTuple_Check(branch_array)) {
      PyErr_SetString(PyExc_TypeError, "third argument must be a sequence of (string, array) or (string, string, array, array) tuples");
      return NULL;
    }

    PyObject* dataArray;
    PyObject* sizeArray;

    void* dataPointer;
    void* sizePointer;
    char* dataName;
    char* sizeName;
    int64_t dataLength;
    int64_t sizeLength;
    char dataType;
    int dataItemBytes;
    bool flat;

    if (PySequence_Size(branch_array) == 2)
      flat = true;
    else if (PySequence_Size(branch_array) == 4)
      flat = false;
    else {
      PyErr_SetString(PyExc_TypeError, "third argument must be a sequence of (string, array) or (string, string, array, array) tuples");
      return NULL;
    }

    if (flat) {
      if (!PyArg_ParseTuple(branch_array, "sO", &dataName, &dataArray)) {
        PyErr_SetString(PyExc_TypeError, "third argument must be a sequence of (string, array) or (string, string, array, array) tuples");
        return NULL;
      }
      sizeName = NULL;
      sizeArray = NULL;
      sizeLength = 0;
    }
    else {
      if (!PyArg_ParseTuple(branch_array, "ssOO", &dataName, &sizeName, &dataArray, &sizeArray)) {
        PyErr_SetString(PyExc_TypeError, "third argument must be a sequence of (string, array) or (string, string, array, array) tuples");
        return NULL;
      }

      sizePointer = PyArray_DATA(sizeArray);

      int ndim = PyArray_NDIM(sizeArray);
      if (ndim != 1) {
        PyErr_SetString(PyExc_TypeError, "size arrays must be one-dimensional");
        return NULL;
      }
      sizeLength = PyArray_DIM(sizeArray, 0);

      if (PyArray_DESCR(sizeArray)->type != 'L') {
        PyErr_SetString(PyExc_TypeError, "size arrays must be uint64");
        return NULL;
      }
    }

    dataPointer = PyArray_DATA(dataArray);

    int ndim = PyArray_NDIM(dataArray);
    if (ndim != 1) {
      PyErr_SetString(PyExc_TypeError, "data arrays must be one-dimensional");
      return NULL;
    }
    dataLength = PyArray_DIM(dataArray, 0);

    dataType = PyArray_DESCR(dataArray)->type;
    switch (dataType) {
    case 'B':  // numpy.uint8
    case 'd':  // numpy.float64
    case 'l':  // numpy.int64
    case 'L':  // numpy.uint64
    case 'f':  // numpy.float32
    case 'i':  // numpy.int32
    case 'I':  // numpy.uint32
      break;
    default:
      PyErr_SetString(PyExc_TypeError, "data dtype not supported (only uint8, float64, int64, uint64, float32, int32, uint32)");
      return NULL;
    }

    dataItemBytes = PyArray_DESCR(dataArray)->elsize;

    branchArrayInfos.push_back(BranchArrayInfo(dataPointer, sizePointer, dataName, sizeName, dataLength, sizeLength, dataType, dataItemBytes, flat));
  }

  // FIXME: Are the ROOT references new? borrowed? stolen?

  Int_t oldLevel = gErrorIgnoreLevel;   // error message suppression is not thread safe
  gErrorIgnoreLevel = kError;           // but oh well...
  TFile* tfile = TFile::Open(fileName);
  gErrorIgnoreLevel = oldLevel;         // FIXME: turn off more selectively?

  TTree* ttree;
  tfile->GetObject(treeName, ttree);
  if (ttree == NULL) {
    PyErr_SetString(PyExc_IOError, "bad or missing TTree");
    return NULL;
  }

  for (int i = 0;  i < numArrays;  i++) {
    TBranch* dataBranch = ttree->GetBranch(branchArrayInfos[i].dataName);
    if (dataBranch == NULL) {
      PyErr_SetString(PyExc_IOError, "bad or missing TBranch");
      return NULL;
    }
    branchArrayInfos[i].dataBranch = dataBranch;
  }

  // fragile: the placement of this function call matters
  ttree->SetMakeClass(1);

  for (int i = 0;  i < numArrays;  i++) {
    if (branchArrayInfos[i].flat) {
      // FIXME: implement flat
      PyErr_SetString(PyExc_NotImplementedError, "flat column");
      return NULL;
    }
    else {
      if (!branchArrayInfos[i].dataBranch->IsA()->InheritsFrom("TBranchElement")) {
        PyErr_SetString(PyExc_IOError, "non-flat data should be a TBranchElement");
        return NULL;
      }

      TBranchElement *branchElement = (TBranchElement*)branchArrayInfos[i].dataBranch;

      int bufferSize = ((TLeaf*)(branchElement->GetListOfLeaves()->First()))->GetLeafCount()->GetMaximum();

      branchArrayInfos[i].bufferForEntry = new char[bufferSize * branchArrayInfos[i].dataItemBytes];

      ttree->SetBranchAddress(branchArrayInfos[i].dataName, branchArrayInfos[i].bufferForEntry);
      ttree->SetBranchAddress(branchArrayInfos[i].sizeName, &(branchArrayInfos[i].sizeForEntry));
    }
  }

  Long64_t numEntries = ttree->GetEntries();
  Long64_t entry;
  int i;
  uint64_t item;
  uint64_t sizeForEntry;

  // for (entry = 0;  entry < numEntries;  entry++) {
  //   for (i = 0;  i < numArrays;  i++) {
  //     branchArrayInfos[i].dataBranch->GetEntry(entry);
  //     sizeForEntry = branchArrayInfos[i].sizeForEntry;

  //     if (branchArrayInfos[i].flat) {
  //       // FIXME: implement
  //     }
  //     else {
  //       branchArrayInfos[i]

  //       // if (PyArray_SETITEM(branchArrayInfos[i].sizeArray, &sizeForEntry, NULL) != 0) {
  //       //   PyErr_SetString(PyExc_IOError, "failed to fill size array");
  //       //   return NULL;
  //       // }

  //       for (item = 0;  item < sizeForEntry;  item++) {

  //         ((Double_t*)branchArrayInfos[i].bufferForEntry)[item];

  //       }
  //     }

  //   }
  // }

  return Py_BuildValue("O", Py_None);
}
<commit_msg>like this<commit_after>// Copyright 2016 DIANA-HEP
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     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 <iostream>
#include <vector>
#include <string>

#include <Python.h>
#include <numpy/arrayobject.h>

#include <TFile.h>
#include <TError.h>
#include <TTree.h>
#include <TLeaf.h>
#include <TBranch.h>
#include <TBranchElement.h>

static char module_docstring[] = "Simple, streamlined Numpy array-filling from ROOT.";
static char fillarrays_docstring[] = "Fills N arrays at once from a ROOT file's TTree.\n\nparams:\n    fileName: string, can include root:// protocol\n    ttreeName: string, can include directory slashes\n    arrays: list of (string, array) or (string, string, array, array) tuples: (data name, data array) or (data name, size name, data array, size array). Arrays must be preallocated.\n\nRaises IndexError if more values are found in the ROOT file than are allocated in the array.";

static PyObject* fillarrays(PyObject* self, PyObject* args);

static PyMethodDef module_methods[] = {
  {"fillarrays", (PyCFunction)fillarrays, METH_VARARGS, fillarrays_docstring},
  {NULL, NULL, 0, NULL}
};

#if PY_MAJOR_VERSION >= 3
static struct PyModuleDef moduledef = {
  PyModuleDef_HEAD_INIT,
  "_fastreader",
  NULL,
  0,
  module_methods,
  NULL,
  NULL,
  NULL,
  NULL
};

PyMODINIT_FUNC PyInit__fastreader(void) {
  PyObject* module = PyModule_Create(&moduledef);
  if (module != NULL)
    import_array();
  return module;
}
#else
PyMODINIT_FUNC init_fastreader(void) {
  PyObject* module = Py_InitModule3("_fastreader", module_methods, module_docstring);
  if (module != NULL)
    import_array();
}
#endif

class BranchArrayInfo {
public:
  int64_t dataIndex;
  int64_t sizeIndex;

  TBranch* dataBranch;
  char* bufferForEntry;
  Int_t sizeForEntry;

  int dataItemBytes;
  void* dataPointer;
  void* sizePointer;
  char* dataName;
  char* sizeName;
  int64_t dataLength;
  int64_t sizeLength;
  char dataType;
  bool flat;

  BranchArrayInfo(void* dataPointer, void* sizePointer, char* dataName, char* sizeName, int64_t dataLength, int64_t sizeLength, char dataType, int dataItemBytes, bool flat):
    dataIndex(0), sizeIndex(0), dataBranch(NULL), bufferForEntry(NULL), sizeForEntry(0),
    dataPointer(dataPointer), sizePointer(sizePointer), dataName(dataName), sizeName(sizeName), dataLength(dataLength), sizeLength(sizeLength), dataType(dataType), dataItemBytes(dataItemBytes), flat(flat) { }

  ~BranchArrayInfo() {
    if (bufferForEntry != NULL) delete bufferForEntry;
  }
};

static PyObject* fillarrays(PyObject* self, PyObject* args) {
  char* fileName;
  char* treeName;
  PyObject* branches_arrays;

  if (!PyArg_ParseTuple(args, "ssO", &fileName, &treeName, &branches_arrays))
    return NULL;

  if (!PySequence_Check(branches_arrays)) {
    PyErr_SetString(PyExc_TypeError, "third argument must be a sequence of (string, array) pairs");
    return NULL;
  }

  int numArrays = PySequence_Length(branches_arrays);
  std::vector<BranchArrayInfo> branchArrayInfos;

  for (Py_ssize_t i = 0;  i < numArrays;  i++) {
    // Doesn't need to be fast, but PySequence_Fast_GET_ITEM returns a borrowed reference, which is nice because I don't have to handle references.
    PyObject* branch_array = PySequence_Fast_GET_ITEM(branches_arrays, i);
    if (!PyTuple_Check(branch_array)) {
      PyErr_SetString(PyExc_TypeError, "third argument must be a sequence of (string, array) or (string, string, array, array) tuples");
      return NULL;
    }

    PyObject* dataArray;
    PyObject* sizeArray;

    void* dataPointer;
    void* sizePointer;
    char* dataName;
    char* sizeName;
    int64_t dataLength;
    int64_t sizeLength;
    char dataType;
    int dataItemBytes;
    bool flat;

    if (PySequence_Size(branch_array) == 2)
      flat = true;
    else if (PySequence_Size(branch_array) == 4)
      flat = false;
    else {
      PyErr_SetString(PyExc_TypeError, "third argument must be a sequence of (string, array) or (string, string, array, array) tuples");
      return NULL;
    }

    if (flat) {
      if (!PyArg_ParseTuple(branch_array, "sO", &dataName, &dataArray)) {
        PyErr_SetString(PyExc_TypeError, "third argument must be a sequence of (string, array) or (string, string, array, array) tuples");
        return NULL;
      }
      sizeName = NULL;
      sizeArray = NULL;
      sizeLength = 0;
    }
    else {
      if (!PyArg_ParseTuple(branch_array, "ssOO", &dataName, &sizeName, &dataArray, &sizeArray)) {
        PyErr_SetString(PyExc_TypeError, "third argument must be a sequence of (string, array) or (string, string, array, array) tuples");
        return NULL;
      }

      sizePointer = PyArray_DATA(sizeArray);

      int ndim = PyArray_NDIM(sizeArray);
      if (ndim != 1) {
        PyErr_SetString(PyExc_TypeError, "size arrays must be one-dimensional");
        return NULL;
      }
      sizeLength = PyArray_DIM(sizeArray, 0);

      if (PyArray_DESCR(sizeArray)->type != 'L') {
        PyErr_SetString(PyExc_TypeError, "size arrays must be uint64");
        return NULL;
      }
    }

    dataPointer = PyArray_DATA(dataArray);

    int ndim = PyArray_NDIM(dataArray);
    if (ndim != 1) {
      PyErr_SetString(PyExc_TypeError, "data arrays must be one-dimensional");
      return NULL;
    }
    dataLength = PyArray_DIM(dataArray, 0);

    dataType = PyArray_DESCR(dataArray)->type;
    switch (dataType) {
    case 'B':  // numpy.uint8
    case 'd':  // numpy.float64
    case 'l':  // numpy.int64
    case 'L':  // numpy.uint64
    case 'f':  // numpy.float32
    case 'i':  // numpy.int32
    case 'I':  // numpy.uint32
      break;
    default:
      PyErr_SetString(PyExc_TypeError, "data dtype not supported (only uint8, float64, int64, uint64, float32, int32, uint32)");
      return NULL;
    }

    dataItemBytes = PyArray_DESCR(dataArray)->elsize;

    branchArrayInfos.push_back(BranchArrayInfo(dataPointer, sizePointer, dataName, sizeName, dataLength, sizeLength, dataType, dataItemBytes, flat));
  }

  // FIXME: Are the ROOT references new? borrowed? stolen?

  Int_t oldLevel = gErrorIgnoreLevel;   // error message suppression is not thread safe
  gErrorIgnoreLevel = kError;           // but oh well...
  TFile* tfile = TFile::Open(fileName);
  gErrorIgnoreLevel = oldLevel;         // FIXME: turn off more selectively?

  TTree* ttree;
  tfile->GetObject(treeName, ttree);
  if (ttree == NULL) {
    PyErr_SetString(PyExc_IOError, "bad or missing TTree");
    return NULL;
  }

  for (int i = 0;  i < numArrays;  i++) {
    TBranch* dataBranch = ttree->GetBranch(branchArrayInfos[i].dataName);
    if (dataBranch == NULL) {
      PyErr_SetString(PyExc_IOError, "bad or missing TBranch");
      return NULL;
    }
    branchArrayInfos[i].dataBranch = dataBranch;
  }

  // fragile: the placement of this function call matters
  ttree->SetMakeClass(1);

  for (int i = 0;  i < numArrays;  i++) {
    if (branchArrayInfos[i].flat) {
      // FIXME: implement flat
      PyErr_SetString(PyExc_NotImplementedError, "flat column");
      return NULL;
    }
    else {
      if (!branchArrayInfos[i].dataBranch->IsA()->InheritsFrom("TBranchElement")) {
        PyErr_SetString(PyExc_IOError, "non-flat data should be a TBranchElement");
        return NULL;
      }

      TBranchElement *branchElement = (TBranchElement*)branchArrayInfos[i].dataBranch;

      int bufferSize = ((TLeaf*)(branchElement->GetListOfLeaves()->First()))->GetLeafCount()->GetMaximum();

      branchArrayInfos[i].bufferForEntry = new char[bufferSize * branchArrayInfos[i].dataItemBytes];

      ttree->SetBranchAddress(branchArrayInfos[i].dataName, branchArrayInfos[i].bufferForEntry);
      ttree->SetBranchAddress(branchArrayInfos[i].sizeName, &(branchArrayInfos[i].sizeForEntry));
    }
  }

  Long64_t numEntries = ttree->GetEntries();
  Long64_t entry;
  int i;
  uint64_t item;
  uint64_t sizeForEntry;

  for (entry = 0;  entry < numEntries;  entry++) {
    for (i = 0;  i < numArrays;  i++) {
      branchArrayInfos[i].dataBranch->GetEntry(entry);
      sizeForEntry = branchArrayInfos[i].sizeForEntry;

      if (branchArrayInfos[i].flat) {
        // FIXME: implement
      }
      else {
        ((uint64_t*)branchArrayInfos[i].sizePointer)[branchArrayInfos[i].sizeIndex] = sizeForEntry;
        branchArrayInfos[i].sizeIndex++;

        // if (PyArray_SETITEM(branchArrayInfos[i].sizeArray, &sizeForEntry, NULL) != 0) {
        //   PyErr_SetString(PyExc_IOError, "failed to fill size array");
        //   return NULL;
        // }

        for (item = 0;  item < sizeForEntry;  item++) {

          ((Double_t*)branchArrayInfos[i].bufferForEntry)[item];

        }
      }

    }
  }

  return Py_BuildValue("O", Py_None);
}
<|endoftext|>


<commit_before>/* Copyright (C) 2002-2003 MySQL AB

   This program 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 2 of the License, or
   (at your option) any later version.

   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 General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

/*
  Derived tables
  These were introduced by Sinisa <sinisa@mysql.com>
*/

#include "mysql_priv.h"
#include "sql_select.h"
#include "sql_acl.h"

/*
  call given derived table processor (preparing or filling tables)

  SYNOPSIS
    mysql_handle_derived()
    lex                 LEX for this thread
    processor           procedure of derived table processing

  RETURN
    0	ok
    -1	Error
    1	Error and error message given
*/

int
mysql_handle_derived(LEX *lex, int (*processor)(THD*, LEX*, TABLE_LIST*))
{
  if (lex->derived_tables)
  {
    for (SELECT_LEX *sl= lex->all_selects_list;
	 sl;
	 sl= sl->next_select_in_list())
    {
      for (TABLE_LIST *cursor= sl->get_table_list();
	   cursor;
	   cursor= cursor->next_local)
      {
	int res;
	if ((res= (*processor)(lex->thd, lex, cursor)))
	  return res;
      }
      if (lex->describe)
      {
	/*
	  Force join->join_tmp creation, because we will use this JOIN
	  twice for EXPLAIN and we have to have unchanged join for EXPLAINing
	*/
	sl->uncacheable|= UNCACHEABLE_EXPLAIN;
	sl->master_unit()->uncacheable|= UNCACHEABLE_EXPLAIN;
      }
    }
  }
  return 0;
}

/*
  Create temporary table structure (but do not fill it)

  SYNOPSIS
    mysql_derived_prepare()
    thd			Thread handle
    lex                 LEX for this thread
    orig_table_list     TABLE_LIST for the upper SELECT

  IMPLEMENTATION
    Derived table is resolved with temporary table.

    After table creation, the above TABLE_LIST is updated with a new table.

    This function is called before any command containing derived table
    is executed.

    Derived tables is stored in thd->derived_tables and freed in
    close_thread_tables()

  RETURN
    0	ok
    1	Error
    -1	Error and error message given
  */

int mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *orig_table_list)
{
  SELECT_LEX_UNIT *unit= orig_table_list->derived;
  int res= 0;
  DBUG_ENTER("mysql_derived_prepare");
  if (unit)
  {
    SELECT_LEX *first_select= unit->first_select();
    TABLE *table= 0;
    select_union *derived_result;
    bool is_union= first_select->next_select() &&
      first_select->next_select()->linkage == UNION_TYPE;

    if (!(derived_result= new select_union(0)))
      DBUG_RETURN(1); // out of memory

    // st_select_lex_unit::prepare correctly work for single select
    if ((res= unit->prepare(thd, derived_result, 0)))
      goto exit;

    derived_result->tmp_table_param.init();
    derived_result->tmp_table_param.field_count= unit->types.elements;
    /*
      Temp table is created so that it hounours if UNION without ALL is to be
      processed
    */
    if (!(table= create_tmp_table(thd, &derived_result->tmp_table_param,
				  unit->types, (ORDER*) 0,
				  is_union && unit->union_distinct, 1,
				  (first_select->options | thd->options |
				   TMP_TABLE_ALL_COLUMNS),
				  HA_POS_ERROR,
				  orig_table_list->alias)))
    {
      res= -1;
      goto exit;
    }
    derived_result->set_table(table);

exit:
    /* Hide "Unknown column" or "Unknown function" error */
    if (orig_table_list->view)
    {
      if (thd->net.last_errno == ER_BAD_FIELD_ERROR ||
          thd->net.last_errno == ER_SP_DOES_NOT_EXIST)
      {
        thd->clear_error();
        my_error(ER_VIEW_INVALID, MYF(0), orig_table_list->db,
                 orig_table_list->real_name);
      }
    }

    /*
      if it is preparation PS only or commands that need only VIEW structure
      then we do not need real data and we can skip execution (and parameters
      is not defined, too)
    */
    if (res)
    {
      if (table)
	free_tmp_table(thd, table);
      delete derived_result;
    }
    else
    {
      if (!thd->fill_derived_tables())
	delete derived_result;
      orig_table_list->derived_result= derived_result;
      orig_table_list->table= table;
      orig_table_list->real_name= table->real_name;
      table->derived_select_number= first_select->select_number;
      table->tmp_table= TMP_TABLE;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
      table->grant.privilege= SELECT_ACL;
#endif
      orig_table_list->db= (char *)"";
      // Force read of table stats in the optimizer
      table->file->info(HA_STATUS_VARIABLE);
      /* Add new temporary table to list of open derived tables */
      table->next= thd->derived_tables;
      thd->derived_tables= table;
    }
  }
  else if (orig_table_list->ancestor)
    orig_table_list->set_ancestor();
  DBUG_RETURN(res);
}

/*
  fill derived table

  SYNOPSIS
    mysql_derived_filling()
    thd			Thread handle
    lex                 LEX for this thread
    unit                node that contains all SELECT's for derived tables
    orig_table_list     TABLE_LIST for the upper SELECT

  IMPLEMENTATION
    Derived table is resolved with temporary table. It is created based on the
    queries defined. After temporary table is filled, if this is not EXPLAIN,
    then the entire unit / node is deleted. unit is deleted if UNION is used
    for derived table and node is deleted is it is a  simple SELECT.

  RETURN
    0	ok
    1	Error
    -1	Error and error message given
  */

int mysql_derived_filling(THD *thd, LEX *lex, TABLE_LIST *orig_table_list)
{
  TABLE *table= orig_table_list->table;
  SELECT_LEX_UNIT *unit= orig_table_list->derived;
  int res= 0;

  /*check that table creation pass without problem and it is derived table */
  if (table && unit)
  {
    SELECT_LEX *first_select= unit->first_select();
    select_union *derived_result= orig_table_list->derived_result;
    SELECT_LEX *save_current_select= lex->current_select;
    bool is_union= first_select->next_select() &&
      first_select->next_select()->linkage == UNION_TYPE;
    if (is_union)
    {
      // execute union without clean up
      res= unit->exec();
    }
    else
    {
      unit->offset_limit_cnt= first_select->offset_limit;
      unit->select_limit_cnt= first_select->select_limit+
	first_select->offset_limit;
      if (unit->select_limit_cnt < first_select->select_limit)
	unit->select_limit_cnt= HA_POS_ERROR;
      if (unit->select_limit_cnt == HA_POS_ERROR)
	first_select->options&= ~OPTION_FOUND_ROWS;

      lex->current_select= first_select;
      res= mysql_select(thd, &first_select->ref_pointer_array,
			(TABLE_LIST*) first_select->table_list.first,
			first_select->with_wild,
			first_select->item_list, first_select->where,
			(first_select->order_list.elements+
			 first_select->group_list.elements),
			(ORDER *) first_select->order_list.first,
			(ORDER *) first_select->group_list.first,
			first_select->having, (ORDER*) NULL,
			(first_select->options | thd->options |
			 SELECT_NO_UNLOCK),
			derived_result, unit, first_select);
    }

    if (!res)
    {
      /*
        Here we entirely fix both TABLE_LIST and list of SELECT's as
        there were no derived tables
      */
      if (derived_result->flush())
        res= 1;

      if (!lex->describe)
        unit->cleanup();
    }
    else
    {
      free_tmp_table(thd, table);
      unit->cleanup();
    }
    lex->current_select= save_current_select;
    if (res)
      free_tmp_table(thd, table);
  }
  return res;
}
<commit_msg>Small cleanup of derived tables handling.<commit_after>/* Copyright (C) 2002-2003 MySQL AB

   This program 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 2 of the License, or
   (at your option) any later version.

   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 General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

/*
  Derived tables
  These were introduced by Sinisa <sinisa@mysql.com>
*/

#include "mysql_priv.h"
#include "sql_select.h"
#include "sql_acl.h"

/*
  call given derived table processor (preparing or filling tables)

  SYNOPSIS
    mysql_handle_derived()
    lex                 LEX for this thread
    processor           procedure of derived table processing

  RETURN
    0	ok
    -1	Error
    1	Error and error message given
*/

int
mysql_handle_derived(LEX *lex, int (*processor)(THD*, LEX*, TABLE_LIST*))
{
  if (lex->derived_tables)
  {
    for (SELECT_LEX *sl= lex->all_selects_list;
	 sl;
	 sl= sl->next_select_in_list())
    {
      for (TABLE_LIST *cursor= sl->get_table_list();
	   cursor;
	   cursor= cursor->next_local)
      {
	int res;
	if ((res= (*processor)(lex->thd, lex, cursor)))
	  return res;
      }
      if (lex->describe)
      {
	/*
	  Force join->join_tmp creation, because we will use this JOIN
	  twice for EXPLAIN and we have to have unchanged join for EXPLAINing
	*/
	sl->uncacheable|= UNCACHEABLE_EXPLAIN;
	sl->master_unit()->uncacheable|= UNCACHEABLE_EXPLAIN;
      }
    }
  }
  return 0;
}

/*
  Create temporary table structure (but do not fill it)

  SYNOPSIS
    mysql_derived_prepare()
    thd			Thread handle
    lex                 LEX for this thread
    orig_table_list     TABLE_LIST for the upper SELECT

  IMPLEMENTATION
    Derived table is resolved with temporary table.

    After table creation, the above TABLE_LIST is updated with a new table.

    This function is called before any command containing derived table
    is executed.

    Derived tables is stored in thd->derived_tables and freed in
    close_thread_tables()

  RETURN
    0	ok
    1	Error
    -1	Error and error message given
  */

int mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *orig_table_list)
{
  SELECT_LEX_UNIT *unit= orig_table_list->derived;
  int res= 0;
  DBUG_ENTER("mysql_derived_prepare");
  if (unit)
  {
    SELECT_LEX *first_select= unit->first_select();
    TABLE *table= 0;
    select_union *derived_result;
    bool is_union= first_select->next_select() &&
      first_select->next_select()->linkage == UNION_TYPE;

    if (!(derived_result= new select_union(0)))
      DBUG_RETURN(1); // out of memory

    // st_select_lex_unit::prepare correctly work for single select
    if ((res= unit->prepare(thd, derived_result, 0)))
      goto exit;

    derived_result->tmp_table_param.init();
    derived_result->tmp_table_param.field_count= unit->types.elements;
    /*
      Temp table is created so that it hounours if UNION without ALL is to be
      processed
    */
    if (!(table= create_tmp_table(thd, &derived_result->tmp_table_param,
				  unit->types, (ORDER*) 0,
				  is_union && unit->union_distinct, 1,
				  (first_select->options | thd->options |
				   TMP_TABLE_ALL_COLUMNS),
				  HA_POS_ERROR,
				  orig_table_list->alias)))
    {
      res= -1;
      goto exit;
    }
    derived_result->set_table(table);

exit:
    /* Hide "Unknown column" or "Unknown function" error */
    if (orig_table_list->view)
    {
      if (thd->net.last_errno == ER_BAD_FIELD_ERROR ||
          thd->net.last_errno == ER_SP_DOES_NOT_EXIST)
      {
        thd->clear_error();
        my_error(ER_VIEW_INVALID, MYF(0), orig_table_list->db,
                 orig_table_list->real_name);
      }
    }

    /*
      if it is preparation PS only or commands that need only VIEW structure
      then we do not need real data and we can skip execution (and parameters
      is not defined, too)
    */
    if (res)
    {
      if (table)
	free_tmp_table(thd, table);
      delete derived_result;
    }
    else
    {
      if (!thd->fill_derived_tables())
	delete derived_result;
      orig_table_list->derived_result= derived_result;
      orig_table_list->table= table;
      orig_table_list->real_name= table->real_name;
      table->derived_select_number= first_select->select_number;
      table->tmp_table= TMP_TABLE;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
      table->grant.privilege= SELECT_ACL;
#endif
      orig_table_list->db= (char *)"";
      // Force read of table stats in the optimizer
      table->file->info(HA_STATUS_VARIABLE);
      /* Add new temporary table to list of open derived tables */
      table->next= thd->derived_tables;
      thd->derived_tables= table;
    }
  }
  else if (orig_table_list->ancestor)
    orig_table_list->set_ancestor();
  DBUG_RETURN(res);
}

/*
  fill derived table

  SYNOPSIS
    mysql_derived_filling()
    thd			Thread handle
    lex                 LEX for this thread
    unit                node that contains all SELECT's for derived tables
    orig_table_list     TABLE_LIST for the upper SELECT

  IMPLEMENTATION
    Derived table is resolved with temporary table. It is created based on the
    queries defined. After temporary table is filled, if this is not EXPLAIN,
    then the entire unit / node is deleted. unit is deleted if UNION is used
    for derived table and node is deleted is it is a  simple SELECT.

  RETURN
    0	ok
    1	Error
    -1	Error and error message given
  */

int mysql_derived_filling(THD *thd, LEX *lex, TABLE_LIST *orig_table_list)
{
  TABLE *table= orig_table_list->table;
  SELECT_LEX_UNIT *unit= orig_table_list->derived;
  int res= 0;

  /*check that table creation pass without problem and it is derived table */
  if (table && unit)
  {
    SELECT_LEX *first_select= unit->first_select();
    select_union *derived_result= orig_table_list->derived_result;
    SELECT_LEX *save_current_select= lex->current_select;
    bool is_union= first_select->next_select() &&
      first_select->next_select()->linkage == UNION_TYPE;
    if (is_union)
    {
      // execute union without clean up
      res= unit->exec();
    }
    else
    {
      unit->offset_limit_cnt= first_select->offset_limit;
      unit->select_limit_cnt= first_select->select_limit+
	first_select->offset_limit;
      if (unit->select_limit_cnt < first_select->select_limit)
	unit->select_limit_cnt= HA_POS_ERROR;
      if (unit->select_limit_cnt == HA_POS_ERROR)
	first_select->options&= ~OPTION_FOUND_ROWS;

      lex->current_select= first_select;
      res= mysql_select(thd, &first_select->ref_pointer_array,
			(TABLE_LIST*) first_select->table_list.first,
			first_select->with_wild,
			first_select->item_list, first_select->where,
			(first_select->order_list.elements+
			 first_select->group_list.elements),
			(ORDER *) first_select->order_list.first,
			(ORDER *) first_select->group_list.first,
			first_select->having, (ORDER*) NULL,
			(first_select->options | thd->options |
			 SELECT_NO_UNLOCK),
			derived_result, unit, first_select);
    }

    if (!res)
    {
      /*
        Here we entirely fix both TABLE_LIST and list of SELECT's as
        there were no derived tables
      */
      if (derived_result->flush())
        res= 1;

      if (!lex->describe)
        unit->cleanup();
    }
    else
      unit->cleanup();
    lex->current_select= save_current_select;
  }
  return res;
}
<|endoftext|>


<commit_before>/*
    Authors: Darya Filippova, Geet Duggal, Rob Patro
    dfilippo | geet | robp @cs.cmu.edu
*/

#include <limits>
#include <boost/heap/binomial_heap.hpp>
#include <boost/range/irange.hpp>
#include <algorithm>
#include "ArmatusDAG.hpp"

ArmatusDAG::ArmatusDAG(ArmatusParams& p) :
	subProbs(SubProbsVec(p.n)),
    edgeWeights(EdgeWeightsVec(p.n, vector<double>())) {
	params = &p;
}

void ArmatusDAG::build() {
	// Since building the DAG visits everything in the same order as computing
	// a single optimial solution, I've had this function also fill out the
	// necessary information to obtain a backtrace of the opt. sln.
	subProbs[0].topK.push_back({std::numeric_limits<size_t>::max(), 0, 0.0});
	// What's the right condition here?
	subProbs[1].topK.push_back({std::numeric_limits<size_t>::max(), 0, q(0,1)});

    for (size_t l : boost::irange(size_t{2}, params->n)) {
        // was this
  	    // edgeWeights[l].resize(l-1);
        // now this
  	    edgeWeights[l].resize(l);
  	    size_t chosenEdge = 0;
  	    double bestScore = 0.0;
  	    for (size_t k : boost::irange(size_t{0}, l)) {
  	    	// The last domain is [k,l]
  	    	double edgeWeight = std::max(q(k, l), 0.0);
            edgeWeights[l][k] = edgeWeight;

            if (k > 0) {
                // The score of this solution is the benefit we get from traversing
                // the edge + the best score available at the child
                double thisScore = edgeWeight + subProbs[k-1].topK[0].score;

                // If this is the best derivation of this subproblem so far
                // then record it.
                if (thisScore > bestScore) {
                    bestScore = thisScore;
                    chosenEdge = k;
                }
            } else {
                double thisScore = edgeWeight;
                if (thisScore > bestScore) {
                    bestScore = thisScore;
                    chosenEdge = 0;
                }
            }
  	    }

  	    // Put our best solution on the stack.
  	    subProbs[l].topK.push_back({chosenEdge, 0, bestScore});
    }
}

double ArmatusDAG::s(size_t k, size_t l) {
	size_t d = l-k+1;
    return params->sums(k, l)/ std::pow(static_cast<double>(d),params->gamma);
}

double ArmatusDAG::q(size_t k, size_t l) {
	size_t d = l-k+1;
	return (s(k, l) - params->mu[d]);
}

/**
*  This interface is *wrong* but I wanted to sketch the
*  basic algo.
**/

/*
class BackPointerComparator {
    bool operator()(const BackPointer& x, const BackPointer& y) {
	    return (this->edgeWeights[l][x.edge] + this->subProbs[x.edge].topK[x.childSolution].score) <
    	       (this->edgeWeights[l][y.edge] + this->subProbs[y.edge].topK[y.childSolution].score);
    }
};
*/

void ArmatusDAG::computeTopK(uint32_t k) {
	for (size_t l : boost::irange(size_t{2}, params->n)) {
		std::function<bool (const BackPointer&, const BackPointer&)> BackPointerComparator = [l, this] (const BackPointer& x, const BackPointer& y) -> bool {
            auto scoreX = this->edgeWeights[l][x.edge];
            if (x.edge > 0) { scoreX += this->subProbs[x.edge-1].topK[x.childSolution].score; }
            auto scoreY = this->edgeWeights[l][y.edge];
            if (y.edge > 0) { scoreY += this->subProbs[y.edge-1].topK[y.childSolution].score; }

			return scoreX < scoreY;
		};

		boost::heap::binomial_heap<BackPointer,
		                           boost::heap::compare<decltype(BackPointerComparator)>> pq(BackPointerComparator);
		for (size_t j : boost::irange(size_t{0}, l)) {
            if (j > 0) {
                size_t i = j - 1;
                bool isDomain = edgeWeights[l][j] > 0;
                bool prevIsDomain = (subProbs[i].topK.size() > 0 and edgeWeights[i].size() > 0) ? edgeWeights[i][subProbs[i].topK[0].edge] > 0 : true;

                if (isDomain or prevIsDomain) {
                    double score = edgeWeights[l][j] + subProbs[i].topK[0].score;
                    pq.push({j, 0, score});
                }
            } else {
                double score = edgeWeights[l][0];
                pq.push({0, 0, score});
            }
		}

		while (subProbs[l].topK.size() < k and !pq.empty()) {
			auto bp = pq.top();
			pq.pop();
            size_t j = bp.edge;
            if (j > 0) {
                if (bp != subProbs[l].topK.back()) { subProbs[l].topK.push_back(bp); }
                size_t nextSlnIdx = bp.childSolution + 1;
                bool isDomain = edgeWeights[l][j] > 0;

                size_t i = j - 1;
                //std::cout << bp.edge << "\t" << subProbs.size() << endl;
                if (nextSlnIdx < subProbs[i].topK.size()) {
                    bool isPrevDomain = edgeWeights[i][subProbs[i].topK[nextSlnIdx].edge] > 0;
                    if (isDomain or isPrevDomain) {
                        double score = edgeWeights[l][j] + subProbs[i].topK[nextSlnIdx].score;
                        pq.push({j, nextSlnIdx, score});
                    }
                }
            } else {
                if (bp != subProbs[l].topK.back()) { subProbs[l].topK.push_back(bp); }
            }
		}
	}

	// size_t sln = 0;
	// while (sln < k and sln < subProbs[params->n-1].topK.size()) {
	// 	std::cerr << "solution " << sln << " has score " << subProbs[params->n-1].topK[sln].score << "\n";
	// 	++sln;
	// }
}

WeightedDomainEnsemble ArmatusDAG::extractTopK(uint32_t k) {

  const size_t INVALID = std::numeric_limits<size_t>::max();
  // We skip 0 since that was already extracted by viterbiPath
  uint32_t currSln{0};

  WeightedDomainEnsemble solutions{ DomainEnsemble(k, DomainSet()), Weights(k, 0.0) };
  auto root = params->n-1;
  auto topScore = subProbs[root].topK[0].score;
  auto scores = Weights(k, 0.0);

  while (currSln < k and currSln < subProbs[root].topK.size()) {
    auto currentScore = subProbs[root].topK[currSln].score;
    scores[currSln] = currentScore;
    auto& currentDomainSet = solutions.domainSets[currSln];
    solutions.weights[currSln] = currentScore / topScore;

    // Which solution to use at the child
    auto bp = currSln;
    int64_t end = params->n-1;

    while (end >= 0 and subProbs[end].topK[bp].edge != INVALID) {
      size_t begin = subProbs[end].topK[bp].edge;
      size_t start = begin;
//      if (subProbs[begin].topK[subProbs[end].topK[bp].childSolution].edge != INVALID)
//         start++;
      Domain d(start, end);
      if (d.score(*params) > 0.0){
         currentDomainSet.push_back(d);
      }
      bp = subProbs[end].topK[bp].childSolution;
      end = begin-1;
    }

    ++currSln;
  }

  /**
   * consistency check all the extracted domains (for debug purposes only,
   * take this out for release / speed).
   */
  size_t c = 0;
  double eps = 1e-3;
  for (auto& ds : solutions.domainSets) {
    double score = 0.0;
    for (auto& d : ds) {
      score += d.score(*params);
    }
    if (std::abs(score - scores[c]) > eps) {
      std::cerr << "For solution [" << c << "], the recorded score was " << scores[c] << " but extracted domains sum to " << score << "\n";
    }
    ++c;
  }

  return solutions;
}

vector<Domain> ArmatusDAG::viterbiPath() {
	vector<Domain> domains;
  const size_t INVALID = std::numeric_limits<size_t>::max();
	size_t end = params->n-1;
	//std::cerr << "Best score is " << subProbs[end].topK[0].score << "\n";

	while (subProbs[end].topK[0].edge != INVALID) {
		size_t begin = subProbs[end].topK[0].edge;
		domains.push_back({begin+1, end});
		end = begin;
	}

    sort(domains.begin(), domains.end());

	return domains;
}
<commit_msg>Resolved boundary case issue<commit_after>/*
    Authors: Darya Filippova, Geet Duggal, Rob Patro
    dfilippo | geet | robp @cs.cmu.edu
*/

#include <limits>
#include <boost/heap/binomial_heap.hpp>
#include <boost/range/irange.hpp>
#include <algorithm>
#include "ArmatusDAG.hpp"

ArmatusDAG::ArmatusDAG(ArmatusParams& p) :
	subProbs(SubProbsVec(p.n)),
    edgeWeights(EdgeWeightsVec(p.n, vector<double>())) {
	params = &p;
}

void ArmatusDAG::build() {
	// Since building the DAG visits everything in the same order as computing
	// a single optimial solution, I've had this function also fill out the
	// necessary information to obtain a backtrace of the opt. sln.
	subProbs[0].topK.push_back({std::numeric_limits<size_t>::max(), 0, 0.0});
	// What's the right condition here?
	// subProbs[1].topK.push_back({0, 0, std::max(q(0,1), 0.0)});

    for (size_t l : boost::irange(size_t{1}, params->n)) {
        // was this
  	    // edgeWeights[l].resize(l-1);
        // now this
  	    edgeWeights[l].resize(l);
  	    size_t chosenEdge = 0;
  	    double bestScore = 0.0;
  	    for (size_t k : boost::irange(size_t{0}, l)) {
  	    	// The last domain is [k,l]
  	    	double edgeWeight = std::max(q(k, l), 0.0);
            edgeWeights[l][k] = edgeWeight;
            double totalScore = edgeWeight;
            bool isDomain = edgeWeight > 0.0;
            bool isPrevDomain = (k == 0) ? true : false;
            if (k > 0) {
                double prevDomainScore = subProbs[k-1].topK[0].score;
                isPrevDomain = prevDomainScore > 0.0;
                // The score of this solution is the benefit we get from traversing
                // the edge + the best score available at the child
                totalScore += prevDomainScore;
             }
             if (isDomain or isPrevDomain) {
                 if (totalScore > bestScore) {
                     bestScore = totalScore;
                     chosenEdge = k;
                 }
            }
  	    }

  	    // Put our best solution on the stack.
  	    subProbs[l].topK.push_back({chosenEdge, 0, bestScore});
    }
}

double ArmatusDAG::s(size_t k, size_t l) {
	size_t d = l-k+1;
    return params->sums(k, l)/ std::pow(static_cast<double>(d),params->gamma);
}

double ArmatusDAG::q(size_t k, size_t l) {
	size_t d = l-k+1;
	return (s(k, l) - params->mu[d]);
}

/**
*  This interface is *wrong* but I wanted to sketch the
*  basic algo.
**/
void ArmatusDAG::computeTopK(uint32_t k) {
	for (size_t l : boost::irange(size_t{1}, params->n)) {
		std::function<bool (const BackPointer&, const BackPointer&)> BackPointerComparator = [l, this] (const BackPointer& x, const BackPointer& y) -> bool {
            auto scoreX = this->edgeWeights[l][x.edge];
            if (x.edge > 0) { scoreX += this->subProbs[x.edge-1].topK[x.childSolution].score; }
            auto scoreY = this->edgeWeights[l][y.edge];
            if (y.edge > 0) { scoreY += this->subProbs[y.edge-1].topK[y.childSolution].score; }

			return scoreX < scoreY;
		};

		boost::heap::binomial_heap<BackPointer,
		                           boost::heap::compare<decltype(BackPointerComparator)>> pq(BackPointerComparator);
		for (size_t j : boost::irange(size_t{0}, l)) {
            if (j > 0) {
                size_t i = j - 1;
                bool isDomain = edgeWeights[l][j] > 0.0;
                bool prevIsDomain = (subProbs[i].topK.size() > 0 and edgeWeights[i].size() > 0) ? edgeWeights[i][subProbs[i].topK[0].edge] > 0 : true;
                if (j == 1) { prevIsDomain = false; }

                if (isDomain or prevIsDomain) {
                    double score = edgeWeights[l][j] + subProbs[i].topK[0].score;
                    pq.push({j, 0, score});
                }
            } else {
                double score = edgeWeights[l][0];
                pq.push({0, 0, score});
            }
		}

		while (subProbs[l].topK.size() < k and !pq.empty()) {
			auto bp = pq.top();
			pq.pop();
            size_t j = bp.edge;
            if (j > 0) {
                bool alreadyFound{false};
                if (bp != subProbs[l].topK[0] ) { subProbs[l].topK.push_back(bp); }
                size_t nextSlnIdx = bp.childSolution + 1;
                bool isDomain = edgeWeights[l][j] > 0.0;

                size_t i = j - 1;
                //std::cout << bp.edge << "\t" << subProbs.size() << endl;
                if (nextSlnIdx < subProbs[i].topK.size()) {
                    bool prevIsDomain = edgeWeights[i][subProbs[i].topK[nextSlnIdx].edge] > 0.0;
                    if (isDomain or prevIsDomain ) {
                        double score = edgeWeights[l][j] + subProbs[i].topK[nextSlnIdx].score;
                        pq.push({j, nextSlnIdx, score});
                    }
                }
            } else {
                if (bp != subProbs[l].topK[0] ) { subProbs[l].topK.push_back(bp); }
            }
		}
	}

	// size_t sln = 0;
	// while (sln < k and sln < subProbs[params->n-1].topK.size()) {
	// 	std::cerr << "solution " << sln << " has score " << subProbs[params->n-1].topK[sln].score << "\n";
	// 	++sln;
	// }
}

WeightedDomainEnsemble ArmatusDAG::extractTopK(uint32_t k) {

  const size_t INVALID = std::numeric_limits<size_t>::max();
  // We skip 0 since that was already extracted by viterbiPath
  uint32_t currSln{0};

  WeightedDomainEnsemble solutions{ DomainEnsemble(k, DomainSet()), Weights(k, 0.0) };
  auto root = params->n-1;
  auto topScore = subProbs[root].topK[0].score;
  auto scores = Weights(k, 0.0);

  while (currSln < k and currSln < subProbs[root].topK.size()) {
    auto currentScore = subProbs[root].topK[currSln].score;
    scores[currSln] = currentScore;
    auto& currentDomainSet = solutions.domainSets[currSln];
    solutions.weights[currSln] = currentScore / topScore;

    // Which solution to use at the child
    auto bp = currSln;
    int64_t end = params->n-1;
    bool prevWasDomain = true;
    bool done{false};
    Domain prevDomain(root, root);

    while (end > 0) { // and subProbs[end].topK[bp].edge != INVALID) {
      //std::cerr << "end: " << end << "\n";
      size_t begin = subProbs[end].topK[bp].edge;
      size_t start = begin;
//      if (subProbs[begin].topK[subProbs[end].topK[bp].childSolution].edge != INVALID)
//         start++;
      Domain d(start, end);
      if (d.score(*params) > 0.0){
         currentDomainSet.push_back(d);
         prevWasDomain = true;
      } else {
         //currentDomainSet.push_back(d);
        if (!prevWasDomain) {
                std::cerr << "WHAT: solution contained two adjacent non-domains; current domain is (" << start << ", " << end << ") prev domain was (" << prevDomain.start << ", " << prevDomain.end << ")\n";
        }
        prevWasDomain = false;
      }
      prevDomain = d;
      bp = subProbs[end].topK[bp].childSolution;
      end = begin-1;
   }

    ++currSln;
  }

  /**
   * consistency check all the extracted domains (for debug purposes only,
   * take this out for release / speed).
   */
  size_t c = 0;
  double eps = 1e-3;
  for (auto& ds : solutions.domainSets) {
    double score = 0.0;
    for (auto& d : ds) {
      score += d.score(*params);
    }
    if (std::abs(score - scores[c]) > eps) {
      std::cerr << "For solution [" << c << "], the recorded score was " << scores[c] << " but extracted domains sum to " << score << "\n";
    }
    ++c;
  }

  return solutions;
}

vector<Domain> ArmatusDAG::viterbiPath() {
	vector<Domain> domains;
  const size_t INVALID = std::numeric_limits<size_t>::max();
	size_t end = params->n-1;
	//std::cerr << "Best score is " << subProbs[end].topK[0].score << "\n";

	while (subProbs[end].topK[0].edge != INVALID) {
		size_t begin = subProbs[end].topK[0].edge;
		domains.push_back({begin+1, end});
		end = begin;
	}

    sort(domains.begin(), domains.end());

	return domains;
}
<|endoftext|>


<commit_before>/*
  This file is part of Ingen.
  Copyright 2007-2015 David Robillard <http://drobilla.net/>

  Ingen is free software: you can redistribute it and/or modify it under the
  terms of the GNU Affero General Public License as published by the Free
  Software Foundation, either version 3 of the License, or any later version.

  Ingen is distributed in the hope that it will be useful, but WITHOUT ANY
  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  A PARTICULAR PURPOSE.  See the GNU Affero General Public License for details.

  You should have received a copy of the GNU Affero General Public License
  along with Ingen.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <utility>

#include "ingen/AtomReader.hpp"
#include "ingen/Log.hpp"
#include "ingen/Node.hpp"
#include "ingen/URIMap.hpp"
#include "lv2/lv2plug.in/ns/ext/atom/util.h"
#include "raul/Path.hpp"

namespace Ingen {

AtomReader::AtomReader(URIMap&    map,
                       URIs&      uris,
                       Log&       log,
                       Forge&     forge,
                       Interface& iface)
	: _map(map)
	, _uris(uris)
	, _log(log)
	, _forge(forge)
	, _iface(iface)
{
}

void
AtomReader::get_atom(const LV2_Atom* in, Atom& out)
{
	if (in) {
		if (in->type == _uris.atom_URID) {
			const LV2_Atom_URID* urid = (const LV2_Atom_URID*)in;
			const char* uri           = _map.unmap_uri(urid->body);
			if (uri) {
				out = _forge.alloc_uri(_map.unmap_uri(urid->body));
			} else {
				_log.error(fmt("Unable to unmap URID %1%\n") % urid->body);
			}
		} else {
			out = _forge.alloc(in->size, in->type, LV2_ATOM_BODY_CONST(in));
		}
	}
}

void
AtomReader::get_props(const LV2_Atom_Object*       obj,
                      Ingen::Resource::Properties& props)
{
	if (obj->body.otype) {
		props.insert(
			std::make_pair(_uris.rdf_type,
			               _forge.alloc_uri(_map.unmap_uri(obj->body.otype))));
	}
	LV2_ATOM_OBJECT_FOREACH(obj, p) {
		Atom val;
		get_atom(&p->value, val);
		props.insert(std::make_pair(Raul::URI(_map.unmap_uri(p->key)), val));
	}
}

boost::optional<Raul::URI>
AtomReader::atom_to_uri(const LV2_Atom* atom)
{
	if (!atom) {
		return boost::optional<Raul::URI>();
	} else if (atom->type == _uris.atom_URI) {
		const char* str = (const char*)LV2_ATOM_BODY_CONST(atom);
		if (Raul::URI::is_valid(str)) {
			return Raul::URI(str);
		} else {
			_log.warn(fmt("Invalid URI <%1%>\n") % str);
		}
	} else if (atom->type == _uris.atom_Path) {
		const char* str = (const char*)LV2_ATOM_BODY_CONST(atom);
		if (!strncmp(str, "file://", 5)) {
			return Raul::URI(str);
		} else {
			return Raul::URI(std::string("file://") + str);
		}
	} else if (atom->type == _uris.atom_URID) {
		const char* str = _map.unmap_uri(((const LV2_Atom_URID*)atom)->body);
		if (str) {
			return Raul::URI(str);
		} else {
			_log.warn(fmt("Unknown URID %1%\n") % str);
		}
	}
	return boost::optional<Raul::URI>();
}

boost::optional<Raul::Path>
AtomReader::atom_to_path(const LV2_Atom* atom)
{
	boost::optional<Raul::URI> uri = atom_to_uri(atom);
	if (uri && Node::uri_is_path(*uri)) {
		return Node::uri_to_path(*uri);
	}
	return boost::optional<Raul::Path>();
}

bool
AtomReader::is_message(URIs& uris, const LV2_Atom* msg)
{
	if (msg->type != uris.atom_Object) {
		return false;
	}

	const LV2_Atom_Object* obj = (const LV2_Atom_Object*)msg;
	return (obj->body.otype == uris.patch_Get ||
	        obj->body.otype == uris.patch_Delete ||
	        obj->body.otype == uris.patch_Put ||
	        obj->body.otype == uris.patch_Set ||
	        obj->body.otype == uris.patch_Patch ||
	        obj->body.otype == uris.patch_Move ||
	        obj->body.otype == uris.patch_Response);
}

bool
AtomReader::write(const LV2_Atom* msg)
{
	if (msg->type != _uris.atom_Object) {
		_log.warn(fmt("Unknown message type <%1%>\n")
		          % _map.unmap_uri(msg->type));
		return false;
	}

	const LV2_Atom_Object* obj     = (const LV2_Atom_Object*)msg;
	const LV2_Atom*        subject = NULL;
	const LV2_Atom*        number  = NULL;

	lv2_atom_object_get(obj,
	                    (LV2_URID)_uris.patch_subject,        &subject,
	                    (LV2_URID)_uris.patch_sequenceNumber, &number,
	                    NULL);

	const boost::optional<Raul::URI> subject_uri = atom_to_uri(subject);

	const int32_t seq_id = ((number && number->type == _uris.atom_Int)
	                        ? ((const LV2_Atom_Int*)number)->body
	                        : 0);
	_iface.set_response_id(seq_id);

	if (obj->body.otype == _uris.patch_Get) {
		if (subject_uri) {
			_iface.get(*subject_uri);
		}
	} else if (obj->body.otype == _uris.patch_Delete) {
		const LV2_Atom_Object* body = NULL;
		lv2_atom_object_get(obj, (LV2_URID)_uris.patch_body, &body, 0);

		if (subject_uri && !body) {
			_iface.del(*subject_uri);
			return true;
		} else if (body && body->body.otype == _uris.ingen_Arc) {
			const LV2_Atom* tail       = NULL;
			const LV2_Atom* head       = NULL;
			const LV2_Atom* incidentTo = NULL;
			lv2_atom_object_get(body,
			                    (LV2_URID)_uris.ingen_tail, &tail,
			                    (LV2_URID)_uris.ingen_head, &head,
			                    (LV2_URID)_uris.ingen_incidentTo, &incidentTo,
			                    NULL);

			boost::optional<Raul::Path> subject_path(atom_to_path(subject));
			boost::optional<Raul::Path> tail_path(atom_to_path(tail));
			boost::optional<Raul::Path> head_path(atom_to_path(head));
			boost::optional<Raul::Path> other_path(atom_to_path(incidentTo));
			if (tail_path && head_path) {
				_iface.disconnect(*tail_path, *head_path);
			} else if (subject_path && other_path) {
				_iface.disconnect_all(*subject_path, *other_path);
			} else {
				_log.warn("Delete of unknown object\n");
				return false;
			}
		}
	} else if (obj->body.otype == _uris.patch_Put) {
		const LV2_Atom_Object* body = NULL;
		lv2_atom_object_get(obj, (LV2_URID)_uris.patch_body, &body, 0);
		if (!body) {
			_log.warn("Put message has no body\n");
			return false;
		} else if (!subject_uri) {
			_log.warn("Put message has no subject\n");
			return false;
		}

		if (body->body.otype == _uris.ingen_Arc) {
			LV2_Atom* tail = NULL;
			LV2_Atom* head = NULL;
			lv2_atom_object_get(body,
			                    (LV2_URID)_uris.ingen_tail, &tail,
			                    (LV2_URID)_uris.ingen_head, &head,
			                    NULL);
			if (!tail || !head) {
				_log.warn("Arc has no tail or head\n");
				return false;
			}

			boost::optional<Raul::Path> tail_path(atom_to_path(tail));
			boost::optional<Raul::Path> head_path(atom_to_path(head));
			if (tail_path && head_path) {
				_iface.connect(*tail_path, *head_path);
			} else {
				_log.warn(fmt("Arc %1% => %2% has non-path tail or head\n")
				          % atom_to_uri(tail) % atom_to_uri(head));
			}
		} else {
			Ingen::Resource::Properties props;
			get_props(body, props);
			_iface.put(*subject_uri, props);
		}
	} else if (obj->body.otype == _uris.patch_Set) {
		if (!subject_uri) {
			_log.warn("Set message has no subject\n");
			return false;
		}

		const LV2_Atom_URID* prop = NULL;
		lv2_atom_object_get(obj, (LV2_URID)_uris.patch_property, &prop, 0);
		if (!prop || ((const LV2_Atom*)prop)->type != _uris.atom_URID) {
			_log.warn("Set message missing property\n");
			return false;
		}

		const LV2_Atom* value = NULL;
		lv2_atom_object_get(obj, (LV2_URID)_uris.patch_value, &value, 0);
		if (!value) {
			_log.warn("Set message missing value\n");
			return false;
		}

		Atom atom;
		get_atom(value, atom);
		_iface.set_property(*subject_uri,
		                    Raul::URI(_map.unmap_uri(prop->body)),
		                    atom);
	} else if (obj->body.otype == _uris.patch_Patch) {
		if (!subject_uri) {
			_log.warn("Patch message has no subject\n");
			return false;
		}

		const LV2_Atom_Object* remove = NULL;
		lv2_atom_object_get(obj, (LV2_URID)_uris.patch_remove, &remove, 0);
		if (!remove) {
			_log.warn("Patch message has no remove\n");
			return false;
		}

		const LV2_Atom_Object* add = NULL;
		lv2_atom_object_get(obj, (LV2_URID)_uris.patch_add, &add, 0);
		if (!add) {
			_log.warn("Patch message has no add\n");
			return false;
		}

		Ingen::Resource::Properties add_props;
		get_props(add, add_props);

		Ingen::Resource::Properties remove_props;
		get_props(remove, remove_props);

		_iface.delta(*subject_uri, remove_props, add_props);
	} else if (obj->body.otype == _uris.patch_Move) {
		if (!subject) {
			_log.warn("Move message has no subject\n");
			return false;
		}

		const LV2_Atom* dest = NULL;
		lv2_atom_object_get(obj, (LV2_URID)_uris.patch_destination, &dest, 0);
		if (!dest) {
			_log.warn("Move message has no destination\n");
			return false;
		}

		boost::optional<Raul::Path> subject_path(atom_to_path(subject));
		if (!subject_path) {
			_log.warn("Move message has non-path subject\n");
			return false;
		}

		boost::optional<Raul::Path> dest_path(atom_to_path(dest));
		if (!dest_path) {
			_log.warn("Move message has non-path destination\n");
			return false;
		}

		_iface.move(*subject_path, *dest_path);
	} else if (obj->body.otype == _uris.patch_Response) {
		const LV2_Atom* request = NULL;
		const LV2_Atom* body    = NULL;
		lv2_atom_object_get(obj,
		                    (LV2_URID)_uris.patch_request, &request,
		                    (LV2_URID)_uris.patch_body, &body,
		                    0);
		if (!request || request->type != _uris.atom_Int) {
			_log.warn("Response message has no request\n");
			return false;
		} else if (!body || body->type != _uris.atom_Int) {
			_log.warn("Response message body is not integer\n");
			return false;
		}
		_iface.response(((const LV2_Atom_Int*)request)->body,
		                (Ingen::Status)((const LV2_Atom_Int*)body)->body,
		                subject_uri ? subject_uri->c_str() : "");
	} else {
		_log.warn(fmt("Unknown object type <%1%>\n")
		          % _map.unmap_uri(obj->body.otype));
	}

	return true;
}

} // namespace Ingen
<commit_msg>Fix link error, maybe.<commit_after>/*
  This file is part of Ingen.
  Copyright 2007-2015 David Robillard <http://drobilla.net/>

  Ingen is free software: you can redistribute it and/or modify it under the
  terms of the GNU Affero General Public License as published by the Free
  Software Foundation, either version 3 of the License, or any later version.

  Ingen is distributed in the hope that it will be useful, but WITHOUT ANY
  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  A PARTICULAR PURPOSE.  See the GNU Affero General Public License for details.

  You should have received a copy of the GNU Affero General Public License
  along with Ingen.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <utility>

#include "ingen/AtomReader.hpp"
#include "ingen/Log.hpp"
#include "ingen/Node.hpp"
#include "ingen/URIMap.hpp"
#include "lv2/lv2plug.in/ns/ext/atom/util.h"
#include "raul/Path.hpp"

namespace Ingen {

AtomReader::AtomReader(URIMap&    map,
                       URIs&      uris,
                       Log&       log,
                       Forge&     forge,
                       Interface& iface)
	: _map(map)
	, _uris(uris)
	, _log(log)
	, _forge(forge)
	, _iface(iface)
{
}

void
AtomReader::get_atom(const LV2_Atom* in, Atom& out)
{
	if (in) {
		if (in->type == _uris.atom_URID) {
			const LV2_Atom_URID* urid = (const LV2_Atom_URID*)in;
			const char* uri           = _map.unmap_uri(urid->body);
			if (uri) {
				out = _forge.alloc_uri(_map.unmap_uri(urid->body));
			} else {
				_log.error(fmt("Unable to unmap URID %1%\n") % urid->body);
			}
		} else {
			out = _forge.alloc(in->size, in->type, LV2_ATOM_BODY_CONST(in));
		}
	}
}

void
AtomReader::get_props(const LV2_Atom_Object*       obj,
                      Ingen::Resource::Properties& props)
{
	if (obj->body.otype) {
		props.insert(
			std::make_pair(_uris.rdf_type,
			               _forge.alloc_uri(_map.unmap_uri(obj->body.otype))));
	}
	LV2_ATOM_OBJECT_FOREACH(obj, p) {
		Atom val;
		get_atom(&p->value, val);
		props.insert(std::make_pair(Raul::URI(_map.unmap_uri(p->key)), val));
	}
}

boost::optional<Raul::URI>
AtomReader::atom_to_uri(const LV2_Atom* atom)
{
	if (!atom) {
		return boost::optional<Raul::URI>();
	} else if (atom->type == _uris.atom_URI) {
		const char* str = (const char*)LV2_ATOM_BODY_CONST(atom);
		if (Raul::URI::is_valid(str)) {
			return Raul::URI(str);
		} else {
			_log.warn(fmt("Invalid URI <%1%>\n") % str);
		}
	} else if (atom->type == _uris.atom_Path) {
		const char* str = (const char*)LV2_ATOM_BODY_CONST(atom);
		if (!strncmp(str, "file://", 5)) {
			return Raul::URI(str);
		} else {
			return Raul::URI(std::string("file://") + str);
		}
	} else if (atom->type == _uris.atom_URID) {
		const char* str = _map.unmap_uri(((const LV2_Atom_URID*)atom)->body);
		if (str) {
			return Raul::URI(str);
		} else {
			_log.warn(fmt("Unknown URID %1%\n") % str);
		}
	}
	return boost::optional<Raul::URI>();
}

boost::optional<Raul::Path>
AtomReader::atom_to_path(const LV2_Atom* atom)
{
	boost::optional<Raul::URI> uri = atom_to_uri(atom);
	if (uri && Node::uri_is_path(*uri)) {
		return Node::uri_to_path(*uri);
	}
	return boost::optional<Raul::Path>();
}

bool
AtomReader::is_message(URIs& uris, const LV2_Atom* msg)
{
	if (msg->type != uris.atom_Object) {
		return false;
	}

	const LV2_Atom_Object* obj = (const LV2_Atom_Object*)msg;
	return (obj->body.otype == uris.patch_Get ||
	        obj->body.otype == uris.patch_Delete ||
	        obj->body.otype == uris.patch_Put ||
	        obj->body.otype == uris.patch_Set ||
	        obj->body.otype == uris.patch_Patch ||
	        obj->body.otype == uris.patch_Move ||
	        obj->body.otype == uris.patch_Response);
}

bool
AtomReader::write(const LV2_Atom* msg)
{
	if (msg->type != _uris.atom_Object) {
		_log.warn(fmt("Unknown message type <%1%>\n")
		          % _map.unmap_uri(msg->type));
		return false;
	}

	const LV2_Atom_Object* obj     = (const LV2_Atom_Object*)msg;
	const LV2_Atom*        subject = NULL;
	const LV2_Atom*        number  = NULL;

	lv2_atom_object_get(obj,
	                    (LV2_URID)_uris.patch_subject,        &subject,
	                    (LV2_URID)_uris.patch_sequenceNumber, &number,
	                    NULL);

	const boost::optional<Raul::URI> subject_uri = atom_to_uri(subject);

	const int32_t seq_id = ((number && number->type == _uris.atom_Int)
	                        ? ((const LV2_Atom_Int*)number)->body
	                        : 0);
	_iface.set_response_id(seq_id);

	if (obj->body.otype == _uris.patch_Get) {
		if (subject_uri) {
			_iface.get(*subject_uri);
		}
	} else if (obj->body.otype == _uris.patch_Delete) {
		const LV2_Atom_Object* body = NULL;
		lv2_atom_object_get(obj, (LV2_URID)_uris.patch_body, &body, 0);

		if (subject_uri && !body) {
			_iface.del(*subject_uri);
			return true;
		} else if (body && body->body.otype == _uris.ingen_Arc) {
			const LV2_Atom* tail       = NULL;
			const LV2_Atom* head       = NULL;
			const LV2_Atom* incidentTo = NULL;
			lv2_atom_object_get(body,
			                    (LV2_URID)_uris.ingen_tail, &tail,
			                    (LV2_URID)_uris.ingen_head, &head,
			                    (LV2_URID)_uris.ingen_incidentTo, &incidentTo,
			                    NULL);

			boost::optional<Raul::Path> subject_path(atom_to_path(subject));
			boost::optional<Raul::Path> tail_path(atom_to_path(tail));
			boost::optional<Raul::Path> head_path(atom_to_path(head));
			boost::optional<Raul::Path> other_path(atom_to_path(incidentTo));
			if (tail_path && head_path) {
				_iface.disconnect(*tail_path, *head_path);
			} else if (subject_path && other_path) {
				_iface.disconnect_all(*subject_path, *other_path);
			} else {
				_log.warn("Delete of unknown object\n");
				return false;
			}
		}
	} else if (obj->body.otype == _uris.patch_Put) {
		const LV2_Atom_Object* body = NULL;
		lv2_atom_object_get(obj, (LV2_URID)_uris.patch_body, &body, 0);
		if (!body) {
			_log.warn("Put message has no body\n");
			return false;
		} else if (!subject_uri) {
			_log.warn("Put message has no subject\n");
			return false;
		}

		if (body->body.otype == _uris.ingen_Arc) {
			LV2_Atom* tail = NULL;
			LV2_Atom* head = NULL;
			lv2_atom_object_get(body,
			                    (LV2_URID)_uris.ingen_tail, &tail,
			                    (LV2_URID)_uris.ingen_head, &head,
			                    NULL);
			if (!tail || !head) {
				_log.warn("Arc has no tail or head\n");
				return false;
			}

			boost::optional<Raul::Path> tail_path(atom_to_path(tail));
			boost::optional<Raul::Path> head_path(atom_to_path(head));
			if (tail_path && head_path) {
				_iface.connect(*tail_path, *head_path);
			} else {
				_log.warn("Arc has non-path tail or head\n");
			}
		} else {
			Ingen::Resource::Properties props;
			get_props(body, props);
			_iface.put(*subject_uri, props);
		}
	} else if (obj->body.otype == _uris.patch_Set) {
		if (!subject_uri) {
			_log.warn("Set message has no subject\n");
			return false;
		}

		const LV2_Atom_URID* prop = NULL;
		lv2_atom_object_get(obj, (LV2_URID)_uris.patch_property, &prop, 0);
		if (!prop || ((const LV2_Atom*)prop)->type != _uris.atom_URID) {
			_log.warn("Set message missing property\n");
			return false;
		}

		const LV2_Atom* value = NULL;
		lv2_atom_object_get(obj, (LV2_URID)_uris.patch_value, &value, 0);
		if (!value) {
			_log.warn("Set message missing value\n");
			return false;
		}

		Atom atom;
		get_atom(value, atom);
		_iface.set_property(*subject_uri,
		                    Raul::URI(_map.unmap_uri(prop->body)),
		                    atom);
	} else if (obj->body.otype == _uris.patch_Patch) {
		if (!subject_uri) {
			_log.warn("Patch message has no subject\n");
			return false;
		}

		const LV2_Atom_Object* remove = NULL;
		lv2_atom_object_get(obj, (LV2_URID)_uris.patch_remove, &remove, 0);
		if (!remove) {
			_log.warn("Patch message has no remove\n");
			return false;
		}

		const LV2_Atom_Object* add = NULL;
		lv2_atom_object_get(obj, (LV2_URID)_uris.patch_add, &add, 0);
		if (!add) {
			_log.warn("Patch message has no add\n");
			return false;
		}

		Ingen::Resource::Properties add_props;
		get_props(add, add_props);

		Ingen::Resource::Properties remove_props;
		get_props(remove, remove_props);

		_iface.delta(*subject_uri, remove_props, add_props);
	} else if (obj->body.otype == _uris.patch_Move) {
		if (!subject) {
			_log.warn("Move message has no subject\n");
			return false;
		}

		const LV2_Atom* dest = NULL;
		lv2_atom_object_get(obj, (LV2_URID)_uris.patch_destination, &dest, 0);
		if (!dest) {
			_log.warn("Move message has no destination\n");
			return false;
		}

		boost::optional<Raul::Path> subject_path(atom_to_path(subject));
		if (!subject_path) {
			_log.warn("Move message has non-path subject\n");
			return false;
		}

		boost::optional<Raul::Path> dest_path(atom_to_path(dest));
		if (!dest_path) {
			_log.warn("Move message has non-path destination\n");
			return false;
		}

		_iface.move(*subject_path, *dest_path);
	} else if (obj->body.otype == _uris.patch_Response) {
		const LV2_Atom* request = NULL;
		const LV2_Atom* body    = NULL;
		lv2_atom_object_get(obj,
		                    (LV2_URID)_uris.patch_request, &request,
		                    (LV2_URID)_uris.patch_body, &body,
		                    0);
		if (!request || request->type != _uris.atom_Int) {
			_log.warn("Response message has no request\n");
			return false;
		} else if (!body || body->type != _uris.atom_Int) {
			_log.warn("Response message body is not integer\n");
			return false;
		}
		_iface.response(((const LV2_Atom_Int*)request)->body,
		                (Ingen::Status)((const LV2_Atom_Int*)body)->body,
		                subject_uri ? subject_uri->c_str() : "");
	} else {
		_log.warn(fmt("Unknown object type <%1%>\n")
		          % _map.unmap_uri(obj->body.otype));
	}

	return true;
}

} // namespace Ingen
<|endoftext|>


<commit_before>#include <iostream.h>
#include <stdlib.h>
#include <stdio.h>
#include <ugens.h>
#include <mixerr.h>
#include <Instrument.h>
#include "WAVESHAPE.h"
#include <rt.h>
#include <rtdefs.h>

WAVESHAPE::WAVESHAPE() : Instrument()
{
	// future setup here?
}

int WAVESHAPE::init(float p[], short n_args)
{
// p0 = start; p1 = dur; p2 = pitch (hz or oct.pc);
// p3 = index low point; p4 = index  high point; p5 = amp
// p6 = stereo spread (0-1) <optional>
// function slot 1 is waveform to be shaped (generally sine)
//    slot 2 is amp envelope
//    slot 3 is the transfer function
//    slot 4 is the index envelope

	nsamps = rtsetoutput(p[0], p[1], this);

	waveform = floc(1);    /* function 1 is waveform */
	if (waveform == NULL)
		die("WAVESHAPE", "You need to store a waveform in function 1.");
	lenwave = fsize(1);

	if (p[2] < 15.0) p[2] = cpspch(p[2]);
	si = p[2] * (float)(lenwave/SR);

	ampenv = floc(2);
	if (ampenv) {
		int lenamp = fsize(2);
		tableset(p[1], lenamp, amptabs);
	}
	else
		advise("WAVESHAPE", "Setting phrase curve to all 1's.");

	xfer = floc(3);
	lenxfer = fsize(3);

	indenv = floc(4);  /* function 4 is index guide */
	lenind = fsize(4);
	tableset(p[1],lenind,indtabs);

	diff = p[4] - p[3];
	indbase = p[3];

	/*initialize dc blocking filter*/
	c=PI*(float)(p[2]/2./SR);  /*cutoff frequency at pitch/2 */
	a0=1./(1.+c);
	a1= -a0;
	b1=a0*(1-c);
	z1=0;

	phs = 0.0;
	amp = p[5];
	spread = p[6];

	skip = (int)(SR/(float)resetval);       // how often to update amp curve

	return(nsamps);
}

int WAVESHAPE::run()
{
	int i;
	float out[2];
	float aamp,ampi,val,val2,val3;
	float index;
	int branch;

	Instrument::run();

	aamp = amp;            /* in case ampenv == NULL */

	branch = 0;
	for (i = 0; i < chunksamps; i++) {
		if (--branch < 0) {
			if (ampenv)
				aamp = table(cursamp, ampenv, amptabs) * amp;
			index = diff * tablei(cursamp,indenv,indtabs)+indbase;
			ampi = index ? aamp/index : 0.0;
			branch = skip;
			}

		val = oscili(1.0,si,waveform,lenwave,&phs);  /* wave */
		val2 = wshape(val*index,xfer,lenxfer); /* waveshape */
		/*dc blocking filter*/
		val3 = a1*z1;
		z1 = b1*z1+val2;
		val3 += a0*z1;
		val3 *= ampi;
		out[0] = val3;

		if (outputchans == 2) { /* split stereo files between the channels */
			out[1] = (1.0 - spread) * out[0];
			out[0] *= spread;
			}

		rtaddout(out);
		cursamp++;
		}
	return i;
}

Instrument*
makeWAVESHAPE()
{
	WAVESHAPE *inst;

	inst = new WAVESHAPE();
	inst->set_bus_config("WAVESHAPE");

	return inst;
}

void
rtprofile()
{
	RT_INTRO("WAVESHAPE",makeWAVESHAPE);
}

<commit_msg>Optionally use new func table assigns.<commit_after>/* WAVESHAPE -- a waveshaping instrument

   p0 = start time
   p1 = duration
   p2 = pitch (hz or oct.pc)
   p3 = index low point
   p4 = index high point
   p5 = amp
   p6 = stereo spread (0-1) <optional>

   function slot 1 is amp envelope
            slot 2 is waveform to be shaped (generally sine)
            slot 3 is the transfer function
            slot 4 is the index envelope
*/
#include <iostream.h>
#include <stdlib.h>
#include <stdio.h>
#include <ugens.h>
#include <mixerr.h>
#include <Instrument.h>
#include "WAVESHAPE.h"
#include <rt.h>
#include <rtdefs.h>

#ifdef COMPATIBLE_FUNC_LOCS         /* set in makefile.conf */
  #define AMP_GEN_SLOT     2
  #define WAVE_GEN_SLOT    1
  #define XFER_GEN_SLOT    3
  #define INDEX_GEN_SLOT   4
#else
  #define AMP_GEN_SLOT     1        /* so that we can use setline instead */
  #define WAVE_GEN_SLOT    2
  #define XFER_GEN_SLOT    3
  #define INDEX_GEN_SLOT   4
#endif

WAVESHAPE::WAVESHAPE() : Instrument()
{
	// future setup here?
}

int WAVESHAPE::init(float p[], short n_args)
{
	nsamps = rtsetoutput(p[0], p[1], this);

	waveform = floc(WAVE_GEN_SLOT);
	if (waveform == NULL)
		die("WAVESHAPE", "You need to store a waveform in function %d.",
								WAVE_GEN_SLOT);
	lenwave = fsize(WAVE_GEN_SLOT);

	if (p[2] < 15.0) p[2] = cpspch(p[2]);
	si = p[2] * (float)(lenwave/SR);

	ampenv = floc(AMP_GEN_SLOT);
	if (ampenv) {
		int lenamp = fsize(AMP_GEN_SLOT);
		tableset(p[1], lenamp, amptabs);
	}
	else
		advise("WAVESHAPE", "Setting phrase curve to all 1's.");

	xfer = floc(XFER_GEN_SLOT);
	lenxfer = fsize(XFER_GEN_SLOT);

	indenv = floc(INDEX_GEN_SLOT);
	lenind = fsize(INDEX_GEN_SLOT);
	tableset(p[1],lenind,indtabs);

	diff = p[4] - p[3];
	indbase = p[3];

	/*initialize dc blocking filter*/
	c=PI*(float)(p[2]/2./SR);  /*cutoff frequency at pitch/2 */
	a0=1./(1.+c);
	a1= -a0;
	b1=a0*(1-c);
	z1=0;

	phs = 0.0;
	amp = p[5];
	spread = p[6];

	skip = (int)(SR/(float)resetval);       // how often to update amp curve

	return(nsamps);
}

int WAVESHAPE::run()
{
	int i;
	float out[2];
	float aamp,ampi,val,val2,val3;
	float index;
	int branch;

	Instrument::run();

	aamp = amp;            /* in case ampenv == NULL */

	branch = 0;
	for (i = 0; i < chunksamps; i++) {
		if (--branch < 0) {
			if (ampenv)
				aamp = table(cursamp, ampenv, amptabs) * amp;
			index = diff * tablei(cursamp,indenv,indtabs)+indbase;
			ampi = index ? aamp/index : 0.0;
			branch = skip;
			}

		val = oscili(1.0,si,waveform,lenwave,&phs);  /* wave */
		val2 = wshape(val*index,xfer,lenxfer); /* waveshape */
		/*dc blocking filter*/
		val3 = a1*z1;
		z1 = b1*z1+val2;
		val3 += a0*z1;
		val3 *= ampi;
		out[0] = val3;

		if (outputchans == 2) { /* split stereo files between the channels */
			out[1] = (1.0 - spread) * out[0];
			out[0] *= spread;
			}

		rtaddout(out);
		cursamp++;
		}
	return i;
}

Instrument*
makeWAVESHAPE()
{
	WAVESHAPE *inst;

	inst = new WAVESHAPE();
	inst->set_bus_config("WAVESHAPE");

	return inst;
}

void
rtprofile()
{
	RT_INTRO("WAVESHAPE",makeWAVESHAPE);
}

<|endoftext|>