labplot  2.8.2
About: LabPlot is an application for plotting and analysis of 2D and 3D functions and data. It is a complete rewrite of LabPlot1 and lacks in the first release a lot of features available in the predecessor. On the other hand, the GUI and the usability is more superior.
  Fossies Dox: labplot-2.8.2.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

Column.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  File : Column.cpp
3  Project : LabPlot
4  Description : Aspect that manages a column
5  --------------------------------------------------------------------
6  Copyright : (C) 2007-2009 Tilman Benkert (thzs@gmx.net)
7  Copyright : (C) 2013-2017 Alexander Semke (alexander.semke@web.de)
8  Copyright : (C) 2017 Stefan Gerlach (stefan.gerlach@uni.kn)
9 
10  ***************************************************************************/
11 
12 /***************************************************************************
13  * *
14  * This program is free software; you can redistribute it and/or modify *
15  * it under the terms of the GNU General Public License as published by *
16  * the Free Software Foundation; either version 2 of the License, or *
17  * (at your option) any later version. *
18  * *
19  * This program is distributed in the hope that it will be useful, *
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
22  * GNU General Public License for more details. *
23  * *
24  * You should have received a copy of the GNU General Public License *
25  * along with this program; if not, write to the Free Software *
26  * Foundation, Inc., 51 Franklin Street, Fifth Floor, *
27  * Boston, MA 02110-1301 USA *
28  * *
29  ***************************************************************************/
30 
35 #include "backend/core/Project.h"
36 #include "backend/lib/trace.h"
45 
46 #include <KLocalizedString>
47 
48 #include <QClipboard>
49 #include <QFont>
50 #include <QFontMetrics>
51 #include <QIcon>
52 #include <QMenu>
53 #include <QThreadPool>
54 
55 #include <array>
56 #include <unordered_map>
57 
58 extern "C" {
59 #include <gsl/gsl_math.h>
60 #include <gsl/gsl_sort.h>
61 #include <gsl/gsl_statistics.h>
62 }
63 
64 /**
65  * \class Column
66  * \brief Aspect that manages a column
67  *
68  * This class represents a column, i.e., (mathematically) a 1D vector of
69  * values with a header. It provides a public reading and (undo aware) writing
70  * interface as defined in AbstractColumn. A column
71  * can have one of currently three data types: double, QString, or
72  * QDateTime. The string representation of the values can differ depending
73  * on the mode of the column.
74  *
75  * Column inherits from AbstractAspect and is intended to be a child
76  * of the corresponding Spreadsheet in the aspect hierarchy. Columns don't
77  * have a view as they are intended to be displayed inside a spreadsheet.
78  */
79 
80 Column::Column(const QString& name, ColumnMode mode)
81  : AbstractColumn(name, AspectType::Column), d(new ColumnPrivate(this, mode)) {
82 
83  init();
84 }
85 
86 /**
87  * \brief Common part of ctors
88  */
89 void Column::init() {
90  m_string_io = new ColumnStringIO(this);
92  d->outputFilter()->input(0, this);
93  d->inputFilter()->setHidden(true);
94  d->outputFilter()->setHidden(true);
98 
99  m_copyDataAction = new QAction(QIcon::fromTheme("edit-copy"), i18n("Copy Data"), this);
100  connect(m_copyDataAction, &QAction::triggered, this, &Column::copyData);
101 
102  m_usedInActionGroup = new QActionGroup(this);
103  connect(m_usedInActionGroup, &QActionGroup::triggered, this, &Column::navigateTo);
104  connect(this, &AbstractColumn::maskingChanged, this, [=]{d->invalidate();});
105 }
106 
108  delete m_string_io;
109  delete d;
110 }
111 
113  QMenu* menu = AbstractAspect::createContextMenu();
114  QAction* firstAction{nullptr};
115 
116  //insert after "rename" and "delete" actions, if available.
117  //MQTTTopic columns don't have these actions
118  if (menu->actions().size() > 1)
119  firstAction = menu->actions().at(1);
120 
121  //add actions available in SpreadsheetView
122  //TODO: we don't need to add anything from the view for MQTTTopic columns.
123  //at the moment it's ok to check to the null pointer for firstAction here.
124  //later, once we have some actions in the menu also for MQTT topics we'll
125  //need to explicitly to dynamic_cast for MQTTTopic
126  if (firstAction)
127  emit requestProjectContextMenu(menu);
128 
129  //"Used in" menu containing all curves where the column is used
130  QMenu* usedInMenu = new QMenu(i18n("Used in"));
131  usedInMenu->setIcon(QIcon::fromTheme("go-next-view"));
132 
133  //remove previously added actions
134  for (auto* action : m_usedInActionGroup->actions())
135  m_usedInActionGroup->removeAction(action);
136 
137  Project* project = this->project();
138 
139  //add curves where the column is currently in use
140  usedInMenu->addSection(i18n("XY-Curves"));
142  for (const auto* curve : curves) {
143  bool used = false;
144 
145  const auto* analysisCurve = dynamic_cast<const XYAnalysisCurve*>(curve);
146  if (analysisCurve) {
147  if (analysisCurve->dataSourceType() == XYAnalysisCurve::DataSourceType::Spreadsheet
148  && (analysisCurve->xDataColumn() == this || analysisCurve->yDataColumn() == this || analysisCurve->y2DataColumn() == this) )
149  used = true;
150  } else if (curve) {
151  if (curve->xColumn() == this || curve->yColumn() == this)
152  used = true;
153  }
154 
155  if (used) {
156  QAction* action = new QAction(curve->icon(), curve->name(), m_usedInActionGroup);
157  action->setData(curve->path());
158  usedInMenu->addAction(action);
159  }
160  }
161 
162  //add histograms where the column is used
163  usedInMenu->addSection(i18n("Histograms"));
165  for (const auto* hist : hists) {
166  bool used = (hist->dataColumn() == this);
167  if (used) {
168  QAction* action = new QAction(hist->icon(), hist->name(), m_usedInActionGroup);
169  action->setData(hist->path());
170  usedInMenu->addAction(action);
171  }
172  }
173 
174  //add calculated columns where the column is used in formula variables
175  usedInMenu->addSection(i18n("Calculated Columns"));
177  const QString& path = this->path();
178  for (const auto* column : columns) {
179  auto paths = column->formulaVariableColumnPaths();
180  if (paths.indexOf(path) != -1) {
181  QAction* action = new QAction(column->icon(), column->name(), m_usedInActionGroup);
182  action->setData(column->path());
183  usedInMenu->addAction(action);
184  }
185  }
186 
187 
188  if (firstAction)
189  menu->insertSeparator(firstAction);
190 
191  menu->insertMenu(firstAction, usedInMenu);
192  menu->insertSeparator(firstAction);
193 
194  menu->insertAction(firstAction, m_copyDataAction);
195  menu->insertSeparator(firstAction);
196 
197  return menu;
198 }
199 
202  d->inputFilter()->setNumberLocale(numberLocale);
203  d->outputFilter()->setNumberLocale(numberLocale);
204 }
205 
206 void Column::navigateTo(QAction* action) {
207  project()->navigateTo(action->data().toString());
208 }
209 
210 /*!
211  * copies the values of the column to the clipboard
212  */
214  QString output;
215  int rows = rowCount();
216 
217  //TODO: use locale of filter?
219  if (columnMode() == ColumnMode::Numeric) {
220  const Double2StringFilter* filter = static_cast<Double2StringFilter*>(outputFilter());
221  char format = filter->numericFormat();
222  for (int r = 0; r < rows; r++) {
223  output += numberLocale.toString(valueAt(r), format, 16); // copy with max. precision
224  if (r < rows-1)
225  output += '\n';
226  }
228  for (int r = 0; r < rowCount(); r++) {
229  output += numberLocale.toString(valueAt(r));
230  if (r < rows-1)
231  output += '\n';
232  }
233  } else {
234  for (int r = 0; r < rowCount(); r++) {
235  output += asStringColumn()->textAt(r);
236  if (r < rows-1)
237  output += '\n';
238  }
239  }
240 
241  QApplication::clipboard()->setText(output);
242 }
243 /*!
244  *
245  */
248 }
249 
251  const Project* project = this->project();
252 
253  //when executing tests we don't create any project,
254  //add a null-pointer check for tests here.
255  if (!project)
256  return;
257 
259 
260  //determine the plots where the column is consumed
261  for (const auto* curve : curves) {
262  if (curve->xColumn() == this || curve->yColumn() == this
263  || (curve->xErrorType() == XYCurve::ErrorType::Symmetric && curve->xErrorPlusColumn() == this)
264  || (curve->xErrorType() == XYCurve::ErrorType::Asymmetric && (curve->xErrorPlusColumn() == this ||curve->xErrorMinusColumn() == this))
265  || (curve->yErrorType() == XYCurve::ErrorType::Symmetric && curve->yErrorPlusColumn() == this)
266  || (curve->yErrorType() == XYCurve::ErrorType::Asymmetric && (curve->yErrorPlusColumn() == this ||curve->yErrorMinusColumn() == this)) ) {
267  auto* plot = static_cast<CartesianPlot*>(curve->parentAspect());
268  if (plots.indexOf(plot) == -1)
269  plots << plot;
270  }
271  }
272 
274  for (const auto* hist : hists) {
275  if (hist->dataColumn() == this ) {
276  auto* plot = static_cast<CartesianPlot*>(hist->parentAspect());
277  if (plots.indexOf(plot) == -1)
278  plots << plot;
279  }
280  }
281 }
282 
283 /**
284  * \brief Set the column mode
285  *
286  * This sets the column mode and, if
287  * necessary, converts it to another datatype.
288  */
290  if (mode == columnMode())
291  return;
292 
293  beginMacro(i18n("%1: change column type", name()));
294 
295  auto* old_input_filter = d->inputFilter();
296  auto* old_output_filter = d->outputFilter();
297  exec(new ColumnSetModeCmd(d, mode));
298 
299  if (d->inputFilter() != old_input_filter) {
300  removeChild(old_input_filter);
301  addChild(d->inputFilter());
302  d->inputFilter()->input(0, m_string_io);
303  }
304  if (d->outputFilter() != old_output_filter) {
305  removeChild(old_output_filter);
306  addChild(d->outputFilter());
307  d->outputFilter()->input(0, this);
308  }
309 
310  endMacro();
311 }
312 
314  if (mode == columnMode())
315  return;
316 
317  auto* old_input_filter = d->inputFilter();
318  auto* old_output_filter = d->outputFilter();
319  exec(new ColumnSetModeCmd(d, mode));
320 
321  if (d->inputFilter() != old_input_filter) {
322  removeChild(old_input_filter);
324  d->inputFilter()->input(0, m_string_io);
325  }
326  if (d->outputFilter() != old_output_filter) {
327  removeChild(old_output_filter);
329  d->outputFilter()->input(0, this);
330  }
331 }
332 
333 bool Column::isDraggable() const {
334  return true;
335 }
336 
339 }
340 
341 /**
342  * \brief Copy another column of the same type
343  *
344  * This function will return false if the data type
345  * of 'other' is not the same as the type of 'this'.
346  * Use a filter to convert a column to another type.
347  */
348 bool Column::copy(const AbstractColumn* other) {
349  Q_CHECK_PTR(other);
350  if (other->columnMode() != columnMode()) return false;
351  exec(new ColumnFullCopyCmd(d, other));
352  return true;
353 }
354 
355 /**
356  * \brief Copies a part of another column of the same type
357  *
358  * This function will return false if the data type
359  * of 'other' is not the same as the type of 'this'.
360  * \param source pointer to the column to copy
361  * \param source_start first row to copy in the column to copy
362  * \param dest_start first row to copy in
363  * \param num_rows the number of rows to copy
364  */
365 bool Column::copy(const AbstractColumn* source, int source_start, int dest_start, int num_rows) {
366  Q_CHECK_PTR(source);
367  if (source->columnMode() != columnMode()) return false;
368  exec(new ColumnPartialCopyCmd(d, source, source_start, dest_start, num_rows));
369  return true;
370 }
371 
372 /**
373  * \brief Insert some empty (or initialized with zero) rows
374  */
375 void Column::handleRowInsertion(int before, int count) {
376  AbstractColumn::handleRowInsertion(before, count);
377  exec(new ColumnInsertRowsCmd(d, before, count));
379  emit dataChanged(this);
380 
381  d->statisticsAvailable = false;
382  d->hasValuesAvailable = false;
383  d->propertiesAvailable = false;
384 }
385 
386 /**
387  * \brief Remove 'count' rows starting from row 'first'
388  */
389 void Column::handleRowRemoval(int first, int count) {
390  AbstractColumn::handleRowRemoval(first, count);
391  exec(new ColumnRemoveRowsCmd(d, first, count));
393  emit dataChanged(this);
394 
395  d->statisticsAvailable = false;
396  d->hasValuesAvailable = false;
397  d->propertiesAvailable = false;
398 }
399 
400 /**
401  * \brief Set the column plot designation
402  */
404  if (pd != plotDesignation())
406 }
407 
408 /**
409  * \brief Get width
410  */
411 int Column::width() const {
412  return d->width();
413 }
414 
415 /**
416  * \brief Set width
417  */
418 void Column::setWidth(int value) {
419  d->setWidth(value);
420 }
421 
422 /**
423  * \brief Clear the whole column
424  */
426  exec(new ColumnClearCmd(d));
427 }
428 
429 ////////////////////////////////////////////////////////////////////////////////
430 //@}
431 ////////////////////////////////////////////////////////////////////////////////
432 
433 ////////////////////////////////////////////////////////////////////////////////
434 //! \name Formula related functions
435 //@{
436 ////////////////////////////////////////////////////////////////////////////////
437 /**
438  * \brief Returns the formula used to generate column values
439  */
440 QString Column:: formula() const {
441  return d->formula();
442 }
443 
444 const QStringList& Column::formulaVariableNames() const {
445  return d->formulaVariableNames();
446 }
447 
449  return d->formulaVariableColumns();
450 }
451 
452 const QStringList& Column::formulaVariableColumnPaths() const {
453  return d->formulaVariableColumnPaths();
454 }
455 
456 void Column::setformulVariableColumnsPath(int index, const QString& path) {
458 }
459 
460 void Column::setformulVariableColumn(int index, Column* column) {
461  d->setformulVariableColumn(index, column);
462 }
463 
465  return d->formulaAutoUpdate();
466 }
467 
468 /**
469  * \brief Sets the formula used to generate column values
470  */
471 void Column::setFormula(const QString& formula, const QStringList& variableNames,
472  const QVector<Column*>& columns, bool autoUpdate) {
473  exec(new ColumnSetGlobalFormulaCmd(d, formula, variableNames, columns, autoUpdate));
474 }
475 
476 /*!
477  * in case the cell values are calculated via a global column formula,
478  * updates the values on data changes in all the dependent changes in the
479  * "variable columns".
480  */
482  d->statisticsAvailable = false;
483  d->hasValuesAvailable = false;
484  d->propertiesAvailable = false;
485  d->updateFormula();
486 }
487 
488 /**
489  * \brief Set a formula string for an interval of rows
490  */
491 void Column::setFormula(const Interval<int>& i, const QString& formula) {
492  exec(new ColumnSetFormulaCmd(d, i, formula));
493 }
494 
495 /**
496  * \brief Overloaded function for convenience
497  */
498 void Column::setFormula(int row, const QString& formula) {
499  setFormula(Interval<int>(row, row), formula);
500 }
501 
502 /**
503  * \brief Clear all formulas
504  */
507 }
508 
509 ////////////////////////////////////////////////////////////////////////////////
510 //@}
511 ////////////////////////////////////////////////////////////////////////////////
512 
513 ////////////////////////////////////////////////////////////////////////////////
514 //! \name type specific functions
515 //@{
516 ////////////////////////////////////////////////////////////////////////////////
517 
518 /**
519  * \brief Set the content of row 'row'
520  *
521  * Use this only when columnMode() is Text
522  */
523 void Column::setTextAt(int row, const QString& new_value) {
524  exec(new ColumnSetTextCmd(d, row, new_value));
525 }
526 
527 /**
528  * \brief Replace a range of values
529  *
530  * Use this only when columnMode() is Text
531  */
532 void Column::replaceTexts(int first, const QVector<QString>& new_values) {
533  if (!new_values.isEmpty()) //TODO: do we really need this check?
534  exec(new ColumnReplaceTextsCmd(d, first, new_values));
535 }
536 
537 /**
538  * \brief Set the content of row 'row'
539  *
540  * Use this only when columnMode() is DateTime, Month or Day
541  */
542 void Column::setDateAt(int row, QDate new_value) {
543  setDateTimeAt(row, QDateTime(new_value, timeAt(row)));
544 }
545 
546 /**
547  * \brief Set the content of row 'row'
548  *
549  * Use this only when columnMode() is DateTime, Month or Day
550  */
551 void Column::setTimeAt(int row, QTime new_value) {
552  setDateTimeAt(row, QDateTime(dateAt(row), new_value));
553 }
554 
555 /**
556  * \brief Set the content of row 'row'
557  *
558  * Use this only when columnMode() is DateTime, Month or Day
559  */
560 void Column::setDateTimeAt(int row, const QDateTime& new_value) {
561  exec(new ColumnSetDateTimeCmd(d, row, new_value));
562 }
563 
564 /**
565  * \brief Replace a range of values
566  *
567  * Use this only when columnMode() is DateTime, Month or Day
568  */
569 void Column::replaceDateTimes(int first, const QVector<QDateTime>& new_values) {
570  if (!new_values.isEmpty())
571  exec(new ColumnReplaceDateTimesCmd(d, first, new_values));
572 }
573 
574 /**
575  * \brief Set the content of row 'row'
576  *
577  * Use this only when columnMode() is Numeric
578  */
579 void Column::setValueAt(int row, const double new_value) {
580  exec(new ColumnSetValueCmd(d, row, new_value));
581 }
582 
583 /**
584  * \brief Replace a range of values
585  *
586  * Use this only when columnMode() is Numeric
587  */
588 void Column::replaceValues(int first, const QVector<double>& new_values) {
589  if (!new_values.isEmpty())
590  exec(new ColumnReplaceValuesCmd(d, first, new_values));
591 }
592 
593 /**
594  * \brief Set the content of row 'row'
595  *
596  * Use this only when columnMode() is Integer
597  */
598 void Column::setIntegerAt(int row, const int new_value) {
599  exec(new ColumnSetIntegerCmd(d, row, new_value));
600 }
601 
602 /**
603  * \brief Replace a range of values
604  *
605  * Use this only when columnMode() is Integer
606  */
607 void Column::replaceInteger(int first, const QVector<int>& new_values) {
608  if (!new_values.isEmpty())
609  exec(new ColumnReplaceIntegerCmd(d, first, new_values));
610 }
611 
612 /**
613  * \brief Set the content of row 'row'
614  *
615  * Use this only when columnMode() is BigInt
616  */
617 void Column::setBigIntAt(int row, const qint64 new_value) {
618  d->statisticsAvailable = false;
619  d->hasValuesAvailable = false;
620  d->propertiesAvailable = false;
621  exec(new ColumnSetBigIntCmd(d, row, new_value));
622 }
623 
624 /**
625  * \brief Replace a range of values
626  *
627  * Use this only when columnMode() is BigInt
628  */
629 void Column::replaceBigInt(int first, const QVector<qint64>& new_values) {
630  if (!new_values.isEmpty())
631  exec(new ColumnReplaceBigIntCmd(d, first, new_values));
632 }
633 
634 /*!
635  * \brief Column::properties
636  * Returns the column properties of this curve (monoton increasing, monoton decreasing, ... )
637  * \see AbstractColumn::properties
638  */
640  if (!d->propertiesAvailable)
641  d->updateProperties();
642 
643  return d->properties;
644 }
645 
647  if (!d->statisticsAvailable)
649 
650  return d->statistics;
651 }
652 
655  && (columnMode() != ColumnMode::BigInt) )
656  return;
657 
658  PERFTRACE("calculate column statistics");
659 
662 
663  int rowValuesSize = 0;
664  int notNanCount = 0;
665  double val;
666  double columnSum = 0.0;
667  double columnProduct = 1.0;
668  double columnSumNeg = 0.0;
669  double columnSumSquare = 0.0;
670  statistics.minimum = INFINITY;
671  statistics.maximum = -INFINITY;
672  std::unordered_map<double, int> frequencyOfValues;
673  QVector<double> rowData;
674  if (columnMode() == ColumnMode::Numeric) {
675  auto* rowValues = reinterpret_cast<QVector<double>*>(data());
676  rowValuesSize = rowValues->size();
677  rowData.reserve(rowValuesSize);
678 
679  for (int row = 0; row < rowValuesSize; ++row) {
680  val = rowValues->value(row);
681  if (std::isnan(val) || isMasked(row))
682  continue;
683 
684  if (val < statistics.minimum)
685  statistics.minimum = val;
686  if (val > statistics.maximum)
687  statistics.maximum = val;
688  columnSum += val;
689  columnSumNeg += (1.0 / val);
690  columnSumSquare += val*val;
691  columnProduct *= val;
692  if (frequencyOfValues.find(val) != frequencyOfValues.end())
693  frequencyOfValues.operator [](val)++;
694  else
695  frequencyOfValues.insert(std::make_pair(val, 1));
696  ++notNanCount;
697  rowData.push_back(val);
698  }
699  } else if (columnMode() == ColumnMode::Integer) {
700  //TODO: code duplication because of the reinterpret_cast...
701  auto* rowValues = reinterpret_cast<QVector<int>*>(data());
702  rowValuesSize = rowValues->size();
703  rowData.reserve(rowValuesSize);
704  for (int row = 0; row < rowValuesSize; ++row) {
705  val = rowValues->value(row);
706  if (std::isnan(val) || isMasked(row))
707  continue;
708 
709  if (val < statistics.minimum)
710  statistics.minimum = val;
711  if (val > statistics.maximum)
712  statistics.maximum = val;
713  columnSum += val;
714  columnSumNeg += (1.0 / val);
715  columnSumSquare += val*val;
716  columnProduct *= val;
717  if (frequencyOfValues.find(val) != frequencyOfValues.end())
718  frequencyOfValues.operator [](val)++;
719  else
720  frequencyOfValues.insert(std::make_pair(val, 1));
721  ++notNanCount;
722  rowData.push_back(val);
723  }
724  } else if (columnMode() == ColumnMode::BigInt) {
725  //TODO: code duplication because of the reinterpret_cast...
726  auto* rowValues = reinterpret_cast<QVector<qint64>*>(data());
727  rowValuesSize = rowValues->size();
728  rowData.reserve(rowValuesSize);
729  for (int row = 0; row < rowValuesSize; ++row) {
730  val = rowValues->value(row);
731  if (std::isnan(val) || isMasked(row))
732  continue;
733 
734  if (val < statistics.minimum)
735  statistics.minimum = val;
736  if (val > statistics.maximum)
737  statistics.maximum = val;
738  columnSum += val;
739  columnSumNeg += (1.0 / val);
740  columnSumSquare += val*val;
741  columnProduct *= val;
742  if (frequencyOfValues.find(val) != frequencyOfValues.end())
743  frequencyOfValues.operator [](val)++;
744  else
745  frequencyOfValues.insert(std::make_pair(val, 1));
746  ++notNanCount;
747  rowData.push_back(val);
748  }
749  }
750 
751  if (notNanCount == 0) {
752  d->statisticsAvailable = true;
753  return;
754  }
755 
756  if (rowData.size() < rowValuesSize)
757  rowData.squeeze();
758 
759  statistics.size = notNanCount;
760  statistics.arithmeticMean = columnSum / notNanCount;
761  statistics.geometricMean = pow(columnProduct, 1.0 / notNanCount);
762  statistics.harmonicMean = notNanCount / columnSumNeg;
763  statistics.contraharmonicMean = columnSumSquare / columnSum;
764 
765  //calculate the mode, the most frequent value in the data set
766  int maxFreq = 0;
767  double mode = NAN;
768  for (const auto& it : frequencyOfValues) {
769  if (it.second > maxFreq) {
770  maxFreq = it.second;
771  mode = it.first;
772  }
773  }
774  //check how many times the max frequency occurs in the data set.
775  //if more than once, we have a multi-modal distribution and don't show any mode
776  int maxFreqOccurance = 0;
777  for (const auto& it : frequencyOfValues) {
778  if (it.second == maxFreq)
779  ++maxFreqOccurance;
780 
781  if (maxFreqOccurance > 1) {
782  mode = NAN;
783  break;
784  }
785  }
786  statistics.mode = mode;
787 
788  double columnSumVariance = 0;
789  double columnSumMeanDeviation = 0.0;
790  double columnSumMedianDeviation = 0.0;
791  double sumForCentralMoment_r3 = 0.0;
792  double sumForCentralMoment_r4 = 0.0;
793 
794  //sort the data to calculate the percentiles
795  gsl_sort(rowData.data(), 1, notNanCount);
796 // statistics.median = (notNanCount%2) ? rowData.at((int)((notNanCount-1)/2)) :
797 // (rowData.at((int)((notNanCount-1)/2)) + rowData.at((int)(notNanCount/2)))/2.0;
798  statistics.firstQuartile = gsl_stats_quantile_from_sorted_data(rowData.data(), 1, notNanCount, 0.25);
799  statistics.median = gsl_stats_quantile_from_sorted_data(rowData.data(), 1, notNanCount, 0.50);
800  statistics.thirdQuartile = gsl_stats_quantile_from_sorted_data(rowData.data(), 1, notNanCount, 0.75);
803 
804  QVector<double> absoluteMedianList;
805  absoluteMedianList.reserve((int)notNanCount);
806  absoluteMedianList.resize((int)notNanCount);
807 
808  for (int row = 0; row < notNanCount; ++row) {
809  val = rowData.value(row);
810  columnSumVariance += gsl_pow_2(val - statistics.arithmeticMean);
811 
812  sumForCentralMoment_r3 += gsl_pow_3(val - statistics.arithmeticMean);
813  sumForCentralMoment_r4 += gsl_pow_4(val - statistics.arithmeticMean);
814  columnSumMeanDeviation += fabs(val - statistics.arithmeticMean);
815 
816  absoluteMedianList[row] = fabs(val - statistics.median);
817  columnSumMedianDeviation += absoluteMedianList[row];
818  }
819 
820  statistics.meanDeviationAroundMedian = columnSumMedianDeviation / notNanCount;
821 
822  //sort the data to calculate the median
823  gsl_sort(absoluteMedianList.data(), 1, notNanCount);
824  statistics.medianDeviation = (notNanCount%2) ? absoluteMedianList.at((int)((notNanCount-1)/2)) :
825  (absoluteMedianList.at((int)((notNanCount-1)/2)) + absoluteMedianList.at((int)(notNanCount/2)))/2.0;
826 
827  const double centralMoment_r3 = sumForCentralMoment_r3 / notNanCount;
828  const double centralMoment_r4 = sumForCentralMoment_r4 / notNanCount;
829 
830  statistics.variance = columnSumVariance / notNanCount;
831  if (notNanCount != 1)
832  statistics.standardDeviation = sqrt(statistics.variance * notNanCount / (notNanCount - 1));
833  else
835  statistics.skewness = centralMoment_r3 / gsl_pow_3(statistics.standardDeviation);
836  statistics.kurtosis = (centralMoment_r4 / gsl_pow_4(statistics.standardDeviation)) - 3.0;
837  statistics.meanDeviation = columnSumMeanDeviation / notNanCount;
838 
839  double entropy = 0.0;
840  for (const auto& v : frequencyOfValues) {
841  const double frequencyNorm = static_cast<double>(v.second) / notNanCount;
842  entropy += (frequencyNorm * log2(frequencyNorm));
843  }
844 
845  statistics.entropy = -entropy;
846 
847  d->statisticsAvailable = true;
848 }
849 
850 //////////////////////////////////////////////////////////////////////////////////////////////
851 
852 void* Column::data() const {
853  return d->data();
854 }
855 
856 /*!
857  * return \c true if the column has numeric values, \c false otherwise.
858  */
859 bool Column::hasValues() const {
860  if (d->hasValuesAvailable)
861  return d->hasValues;
862 
863  bool foundValues = false;
864  switch (columnMode()) {
865  case ColumnMode::Numeric: {
866  for (int row = 0; row < rowCount(); ++row) {
867  if (!std::isnan(valueAt(row))) {
868  foundValues = true;
869  break;
870  }
871  }
872  break;
873  }
874  case ColumnMode::Text: {
875  for (int row = 0; row < rowCount(); ++row) {
876  if (!textAt(row).isEmpty()) {
877  foundValues = true;
878  break;
879  }
880  }
881  break;
882  }
883  case ColumnMode::Integer:
884  case ColumnMode::BigInt:
885  //integer column has always valid values
886  foundValues = true;
887  break;
889  case ColumnMode::Month:
890  case ColumnMode::Day: {
891  for (int row = 0; row < rowCount(); ++row) {
892  if (dateTimeAt(row).isValid()) {
893  foundValues = true;
894  break;
895  }
896  }
897  break;
898  }
899  }
900 
901  d->hasValues = foundValues;
902  d->hasValuesAvailable = true;
903  return d->hasValues;
904 }
905 
906 /*
907  * set item at i to col[j] for same columnMode()
908  */
909 
910 void Column::setFromColumn(int i, AbstractColumn* col, int j) {
911  if (col->columnMode() != columnMode())
912  return;
913 
914  switch (columnMode()) {
915  case ColumnMode::Numeric:
916  setValueAt(i, col->valueAt(j));
917  break;
918  case ColumnMode::Integer:
919  setIntegerAt(i, col->integerAt(j));
920  break;
921  case ColumnMode::BigInt:
922  setBigIntAt(i, col->bigIntAt(j));
923  break;
924  case ColumnMode::Text:
925  setTextAt(i, col->textAt(j));
926  break;
928  case ColumnMode::Month:
929  case ColumnMode::Day:
930  setDateTimeAt(i, col->dateTimeAt(j));
931  break;
932  }
933 }
934 
935 
936 /**
937  * \brief Return the content of row 'row'.
938  *
939  * Use this only when columnMode() is Text
940  */
941 QString Column::textAt(int row) const {
942  return d->textAt(row);
943 }
944 
945 /**
946  * \brief Return the date part of row 'row'
947  *
948  * Use this only when columnMode() is DateTime, Month or Day
949  */
950 QDate Column::dateAt(int row) const {
951  return d->dateAt(row);
952 }
953 
954 /**
955  * \brief Return the time part of row 'row'
956  *
957  * Use this only when columnMode() is DateTime, Month or Day
958  */
959 QTime Column::timeAt(int row) const {
960  return d->timeAt(row);
961 }
962 
963 /**
964  * \brief Return the QDateTime in row 'row'
965  *
966  * Use this only when columnMode() is DateTime, Month or Day
967  */
968 QDateTime Column::dateTimeAt(int row) const {
969  return d->dateTimeAt(row);
970 }
971 
972 /**
973  * \brief Return the double value in row 'row'
974  */
975 double Column::valueAt(int row) const {
976  return d->valueAt(row);
977 }
978 
979 /**
980  * \brief Return the int value in row 'row'
981  */
982 int Column::integerAt(int row) const {
983  return d->integerAt(row);
984 }
985 
986 /**
987  * \brief Return the bigint value in row 'row'
988  */
989 qint64 Column::bigIntAt(int row) const {
990  return d->bigIntAt(row);
991 }
992 
993 /*
994  * call this function if the data of the column was changed directly via the data()-pointer
995  * and not via the setValueAt() in order to emit the dataChanged-signal.
996  * This is used e.g. in \c XYFitCurvePrivate::recalculate()
997  */
999  d->propertiesAvailable = false;
1000 
1002  emit dataChanged(this);
1003 
1004  d->statisticsAvailable = false;
1005  d->hasValuesAvailable = false;
1006 }
1007 
1008 ////////////////////////////////////////////////////////////////////////////////
1009 //@}
1010 ////////////////////////////////////////////////////////////////////////////////
1011 
1012 /**
1013  * \brief Return an icon to be used for decorating the views and spreadsheet column headers
1014  */
1015 QIcon Column::icon() const {
1016  return iconForMode(columnMode());
1017 }
1018 
1019 ////////////////////////////////////////////////////////////////////////////////////////////////////
1020 //! \name serialize/deserialize
1021 //@{
1022 ////////////////////////////////////////////////////////////////////////////////////////////////////
1023 
1024 /**
1025  * \brief Save the column as XML
1026  */
1027 void Column::save(QXmlStreamWriter* writer) const {
1028  writer->writeStartElement("column");
1029  writeBasicAttributes(writer);
1030 
1031  writer->writeAttribute("rows", QString::number(rowCount()));
1032  writer->writeAttribute("designation", QString::number(static_cast<int>(plotDesignation())));
1033  writer->writeAttribute("mode", QString::number(static_cast<int>(columnMode())));
1034  writer->writeAttribute("width", QString::number(width()));
1035 
1036  //save the formula used to generate column values, if available
1037  if (!formula().isEmpty() ) {
1038  writer->writeStartElement("formula");
1039  writer->writeAttribute("autoUpdate", QString::number(d->formulaAutoUpdate()));
1040  writer->writeTextElement("text", formula());
1041 
1042  writer->writeStartElement("variableNames");
1043  for (const auto& name : formulaVariableNames())
1044  writer->writeTextElement("name", name);
1045  writer->writeEndElement();
1046 
1047  writer->writeStartElement("columnPathes");
1048  for (const auto& path : formulaVariableColumnPaths())
1049  writer->writeTextElement("path", path);
1050  writer->writeEndElement();
1051 
1052  writer->writeEndElement();
1053  }
1054 
1055  writeCommentElement(writer);
1056 
1057  writer->writeStartElement("input_filter");
1058  d->inputFilter()->save(writer);
1059  writer->writeEndElement();
1060 
1061  writer->writeStartElement("output_filter");
1062  d->outputFilter()->save(writer);
1063  writer->writeEndElement();
1064 
1065  XmlWriteMask(writer);
1066 
1067  //TODO: formula in cells is not implemented yet
1068  // QVector< Interval<int> > formulas = formulaIntervals();
1069  // foreach(const Interval<int>& interval, formulas) {
1070  // writer->writeStartElement("formula");
1071  // writer->writeAttribute("start_row", QString::number(interval.start()));
1072  // writer->writeAttribute("end_row", QString::number(interval.end()));
1073  // writer->writeCharacters(formula(interval.start()));
1074  // writer->writeEndElement();
1075  // }
1076 
1077  int i;
1078  switch (columnMode()) {
1079  case ColumnMode::Numeric: {
1080  const char* data = reinterpret_cast<const char*>(static_cast< QVector<double>* >(d->data())->constData());
1081  size_t size = d->rowCount() * sizeof(double);
1082  writer->writeCharacters(QByteArray::fromRawData(data, (int)size).toBase64());
1083  break;
1084  }
1085  case ColumnMode::Integer: {
1086  const char* data = reinterpret_cast<const char*>(static_cast< QVector<int>* >(d->data())->constData());
1087  size_t size = d->rowCount() * sizeof(int);
1088  writer->writeCharacters(QByteArray::fromRawData(data, (int)size).toBase64());
1089  break;
1090  }
1091  case ColumnMode::BigInt: {
1092  const char* data = reinterpret_cast<const char*>(static_cast< QVector<qint64>* >(d->data())->constData());
1093  size_t size = d->rowCount() * sizeof(qint64);
1094  writer->writeCharacters(QByteArray::fromRawData(data, (int)size).toBase64());
1095  break;
1096  }
1097  case ColumnMode::Text:
1098  for (i = 0; i < rowCount(); ++i) {
1099  writer->writeStartElement("row");
1100  writer->writeAttribute("index", QString::number(i));
1101  writer->writeCharacters(textAt(i));
1102  writer->writeEndElement();
1103  }
1104  break;
1105  case ColumnMode::DateTime:
1106  case ColumnMode::Month:
1107  case ColumnMode::Day:
1108  for (i = 0; i < rowCount(); ++i) {
1109  writer->writeStartElement("row");
1110  writer->writeAttribute("index", QString::number(i));
1111  writer->writeCharacters(dateTimeAt(i).toString("yyyy-dd-MM hh:mm:ss:zzz"));
1112  writer->writeEndElement();
1113  }
1114  break;
1115  }
1116 
1117  writer->writeEndElement(); // "column"
1118 }
1119 
1120 //TODO: extra header
1121 class DecodeColumnTask : public QRunnable {
1122 public:
1123  DecodeColumnTask(ColumnPrivate* priv, const QString& content) {
1124  m_private = priv;
1125  m_content = content;
1126  };
1127  void run() override {
1128  QByteArray bytes = QByteArray::fromBase64(m_content.toLatin1());
1130  auto* data = new QVector<double>(bytes.size()/(int)sizeof(double));
1131  memcpy(data->data(), bytes.data(), bytes.size());
1134  auto* data = new QVector<qint64>(bytes.size()/(int)sizeof(qint64));
1135  memcpy(data->data(), bytes.data(), bytes.size());
1137  } else {
1138  auto* data = new QVector<int>(bytes.size()/(int)sizeof(int));
1139  memcpy(data->data(), bytes.data(), bytes.size());
1141  }
1142  }
1143 
1144 private:
1146  QString m_content;
1147 };
1148 
1149 /**
1150  * \brief Load the column from XML
1151  */
1152 bool Column::load(XmlStreamReader* reader, bool preview) {
1153  if (!readBasicAttributes(reader))
1154  return false;
1155 
1156  KLocalizedString attributeWarning = ki18n("Attribute '%1' missing or empty, default value is used");
1157  QXmlStreamAttributes attribs = reader->attributes();
1158 
1159  QString str = attribs.value("rows").toString();
1160  if (str.isEmpty())
1161  reader->raiseWarning(attributeWarning.subs("rows").toString());
1162  else
1163  d->resizeTo(str.toInt());
1164 
1165  str = attribs.value("designation").toString();
1166  if (str.isEmpty())
1167  reader->raiseWarning(attributeWarning.subs("designation").toString());
1168  else
1170 
1171  str = attribs.value("mode").toString();
1172  if (str.isEmpty())
1173  reader->raiseWarning(attributeWarning.subs("mode").toString());
1174  else
1176 
1177  str = attribs.value("width").toString();
1178  if (str.isEmpty())
1179  reader->raiseWarning(attributeWarning.subs("width").toString());
1180  else
1181  d->setWidth(str.toInt());
1182 
1183  // read child elements
1184  while (!reader->atEnd()) {
1185  reader->readNext();
1186 
1187  if (reader->isEndElement()) break;
1188 
1189  if (reader->isStartElement()) {
1190  bool ret_val = true;
1191  if (reader->name() == "comment")
1192  ret_val = readCommentElement(reader);
1193  else if (reader->name() == "input_filter")
1194  ret_val = XmlReadInputFilter(reader);
1195  else if (reader->name() == "output_filter")
1196  ret_val = XmlReadOutputFilter(reader);
1197  else if (reader->name() == "mask")
1198  ret_val = XmlReadMask(reader);
1199  else if (reader->name() == "formula")
1200  ret_val = XmlReadFormula(reader);
1201  else if (reader->name() == "row")
1202  ret_val = XmlReadRow(reader);
1203  else { // unknown element
1204  reader->raiseWarning(i18n("unknown element '%1'", reader->name().toString()));
1205  if (!reader->skipToEndElement()) return false;
1206  }
1207  if (!ret_val)
1208  return false;
1209  }
1210  if (!preview) {
1211  QString content = reader->text().toString().trimmed();
1212  if (!content.isEmpty() && ( columnMode() == ColumnMode::Numeric ||
1214  auto* task = new DecodeColumnTask(d, content);
1215  QThreadPool::globalInstance()->start(task);
1216  }
1217  }
1218  }
1219 
1220  return !reader->error();
1221 }
1222 
1224  d->finalizeLoad();
1225 }
1226 
1227 /**
1228  * \brief Read XML input filter element
1229  */
1231  Q_ASSERT(reader->isStartElement() == true && reader->name() == "input_filter");
1232  if (!reader->skipToNextTag()) return false;
1233  if (!d->inputFilter()->load(reader, false)) return false;
1234  if (!reader->skipToNextTag()) return false;
1235  Q_ASSERT(reader->isEndElement() == true && reader->name() == "input_filter");
1236  return true;
1237 }
1238 
1239 /**
1240  * \brief Read XML output filter element
1241  */
1243  Q_ASSERT(reader->isStartElement() == true && reader->name() == "output_filter");
1244  if (!reader->skipToNextTag()) return false;
1245  if (!d->outputFilter()->load(reader, false)) return false;
1246  if (!reader->skipToNextTag()) return false;
1247  Q_ASSERT(reader->isEndElement() == true && reader->name() == "output_filter");
1248  return true;
1249 }
1250 
1251 /**
1252  * \brief Read XML formula element
1253  */
1255  QString formula;
1256  QStringList variableNames;
1257  QStringList columnPathes;
1258 
1259  //read the autoUpdate attribute if available (older project files created with <2.8 don't have it)
1260  bool autoUpdate = false;
1261  if (reader->attributes().hasAttribute("autoUpdate"))
1262  autoUpdate = reader->attributes().value("autoUpdate").toInt();
1263 
1264  while (reader->readNext()) {
1265  if (reader->isEndElement()) break;
1266 
1267  if (reader->name() == "text")
1268  formula = reader->readElementText();
1269  else if (reader->name() == "variableNames") {
1270  while (reader->readNext()) {
1271  if (reader->name() == "variableNames" && reader->isEndElement()) break;
1272 
1273  if (reader->isStartElement())
1274  variableNames << reader->readElementText();
1275  }
1276  } else if (reader->name() == "columnPathes") {
1277  while (reader->readNext()) {
1278  if (reader->name() == "columnPathes" && reader->isEndElement()) break;
1279 
1280  if (reader->isStartElement())
1281  columnPathes << reader->readElementText();
1282  }
1283  }
1284  }
1285 
1286  d->setFormula(formula, variableNames, columnPathes, autoUpdate);
1287 
1288  return true;
1289 }
1290 
1291 //TODO: read cell formula, not implemented yet
1292 // bool Column::XmlReadFormula(XmlStreamReader* reader)
1293 // {
1294 // Q_ASSERT(reader->isStartElement() && reader->name() == "formula");
1295 //
1296 // bool ok1, ok2;
1297 // int start, end;
1298 // start = reader->readAttributeInt("start_row", &ok1);
1299 // end = reader->readAttributeInt("end_row", &ok2);
1300 // if (!ok1 || !ok2)
1301 // {
1302 // reader->raiseError(i18n("invalid or missing start or end row"));
1303 // return false;
1304 // }
1305 // setFormula(Interval<int>(start,end), reader->readElementText());
1306 //
1307 // return true;
1308 // }
1309 
1310 
1311 /**
1312  * \brief Read XML row element
1313  */
1315  Q_ASSERT(reader->isStartElement() == true && reader->name() == "row");
1316 
1317  // QXmlStreamAttributes attribs = reader->attributes();
1318 
1319  bool ok;
1320  int index = reader->readAttributeInt("index", &ok);
1321  if (!ok) {
1322  reader->raiseError(i18n("invalid or missing row index"));
1323  return false;
1324  }
1325 
1326  QString str = reader->readElementText();
1327  switch (columnMode()) {
1328  case ColumnMode::Numeric: {
1329  double value = str.toDouble(&ok);
1330  if (!ok) {
1331  reader->raiseError(i18n("invalid row value"));
1332  return false;
1333  }
1334  setValueAt(index, value);
1335  break;
1336  }
1337  case ColumnMode::Integer: {
1338  int value = str.toInt(&ok);
1339  if (!ok) {
1340  reader->raiseError(i18n("invalid row value"));
1341  return false;
1342  }
1343  setIntegerAt(index, value);
1344  break;
1345  }
1346  case ColumnMode::BigInt: {
1347  qint64 value = str.toLongLong(&ok);
1348  if (!ok) {
1349  reader->raiseError(i18n("invalid row value"));
1350  return false;
1351  }
1352  setBigIntAt(index, value);
1353  break;
1354  }
1355  case ColumnMode::Text:
1356  setTextAt(index, str);
1357  break;
1358 
1359  case ColumnMode::DateTime:
1360  case ColumnMode::Month:
1361  case ColumnMode::Day:
1362  QDateTime date_time = QDateTime::fromString(str,"yyyy-dd-MM hh:mm:ss:zzz");
1363  setDateTimeAt(index, date_time);
1364  break;
1365  }
1366 
1367  return true;
1368 }
1369 
1370 ////////////////////////////////////////////////////////////////////////////////
1371 //@}
1372 ////////////////////////////////////////////////////////////////////////////////
1373 
1374 /**
1375  * \brief Return whether the object is read-only
1376  */
1377 bool Column::isReadOnly() const {
1378  return false;
1379 }
1380 
1381 /**
1382  * \brief Return the column mode
1383  *
1384  * This function is mostly used by spreadsheets but can also be used
1385  * by plots. The column mode specifies how to interpret
1386  * the values in the column additional to the data type.
1387  */
1389  return d->columnMode();
1390 }
1391 
1392 /**
1393  * \brief Return the data vector size
1394  */
1395 int Column::rowCount() const {
1396  return d->rowCount();
1397 }
1398 
1399 /**
1400  * \brief Return the number of available data rows
1401  *
1402  * This returns the number of rows that actually contain data.
1403  * Rows beyond this can be masked etc. but should be ignored by filters,
1404  * plots etc.
1405  */
1407  return d->availableRowCount();
1408 }
1409 
1410 /**
1411  * \brief Return the column plot designation
1412  */
1414  return d->plotDesignation();
1415 }
1416 
1418  switch (plotDesignation()) {
1420  return QString("");
1421  case PlotDesignation::X:
1422  return QLatin1String("[X]");
1423  case PlotDesignation::Y:
1424  return QLatin1String("[Y]");
1425  case PlotDesignation::Z:
1426  return QLatin1String("[Z]");
1428  return QLatin1String("[") + i18n("X-error") + QLatin1Char(']');
1430  return QLatin1String("[") + i18n("X-error +") + QLatin1Char(']');
1432  return QLatin1String("[") + i18n("X-error -") + QLatin1Char(']');
1434  return QLatin1String("[") + i18n("Y-error") + QLatin1Char(']');
1436  return QLatin1String("[") + i18n("Y-error +") + QLatin1Char(']');
1438  return QLatin1String("[") + i18n("Y-error -") + QLatin1Char(']');
1439  }
1440 
1441  return QString("");
1442 }
1443 
1445  return d->outputFilter();
1446 }
1447 
1448 /**
1449  * \brief Return a wrapper column object used for String I/O.
1450  */
1452  return m_string_io;
1453 }
1454 
1455 ////////////////////////////////////////////////////////////////////////////////
1456 //! \name IntervalAttribute related functions
1457 //@{
1458 ////////////////////////////////////////////////////////////////////////////////
1459 /**
1460  * \brief Return the formula associated with row 'row'
1461  */
1462 QString Column::formula(int row) const {
1463  return d->formula(row);
1464 }
1465 
1466 /**
1467  * \brief Return the intervals that have associated formulas
1468  *
1469  * This can be used to make a list of formulas with their intervals.
1470  * Here is some example code:
1471  *
1472  * \code
1473  * QStringList list;
1474  * QVector< Interval<int> > intervals = my_column.formulaIntervals();
1475  * foreach(Interval<int> interval, intervals)
1476  * list << QString(interval.toString() + ": " + my_column.formula(interval.start()));
1477  * \endcode
1478  */
1480  return d->formulaIntervals();
1481 }
1482 
1484  DEBUG(Q_FUNC_INFO << ", mode = " << ENUM_TO_STRING(AbstractColumn, ColumnMode, columnMode()));
1485  if (columnMode() == ColumnMode::DateTime) {
1486  auto* input_filter = static_cast<String2DateTimeFilter*>(d->inputFilter());
1487  auto* output_filter = static_cast<DateTime2StringFilter*>(d->outputFilter());
1488  DEBUG(Q_FUNC_INFO << ", change format " << STDSTRING(input_filter->format()) << " to " << STDSTRING(output_filter->format()));
1489  input_filter->setFormat(output_filter->format());
1490  }
1491 
1492  emit aspectDescriptionChanged(this); // the icon for the type changed
1494  emit formatChanged(this); // all cells must be repainted
1495 
1496  d->statisticsAvailable = false;
1497  d->hasValuesAvailable = false;
1498  d->propertiesAvailable = false;
1499 }
1500 
1501 /*!
1502  * calculates the minimal value in the column.
1503  * for \c count = 0, the minimum of all elements is returned.
1504  * for \c count > 0, the minimum of the first \p count elements is returned.
1505  * for \c count < 0, the minimum of the last \p count elements is returned.
1506  */
1507 double Column::minimum(int count) const {
1508  double min = INFINITY;
1509  if (count == 0 && d->statisticsAvailable)
1510  min = const_cast<Column*>(this)->statistics().minimum;
1511  else {
1512  int start, end;
1513 
1514  if (count == 0) {
1515  start = 0;
1516  end = rowCount();
1517  } else if (count > 0) {
1518  start = 0;
1519  end = qMin(rowCount(), count);
1520  } else {
1521  start = qMax(rowCount() + count, 0);
1522  end = rowCount();
1523  }
1524  return minimum(start, end);
1525  }
1526 
1527  return min;
1528 }
1529 
1530 /*!
1531  * \brief Column::minimum
1532  * Calculates the minimum value in the column between the \p startIndex and \p endIndex, endIndex is excluded.
1533  * If startIndex is greater than endIndex the indices are swapped
1534  * \p startIndex
1535  * \p endIndex
1536  */
1537 double Column::minimum(int startIndex, int endIndex) const {
1538  double min = INFINITY;
1539 
1540  if (rowCount() == 0)
1541  return min;
1542 
1543  if (startIndex > endIndex && startIndex >= 0 && endIndex >= 0)
1544  std::swap(startIndex, endIndex);
1545 
1546  startIndex = qMax(startIndex, 0);
1547  endIndex = qMax(endIndex, 0);
1548 
1549  startIndex = qMin(startIndex, rowCount() - 1);
1550  endIndex = qMin(endIndex, rowCount() - 1);
1551 
1552  int foundIndex = 0;
1553 
1555  Properties property = properties();
1556  if (property == Properties::No) {
1557  // skipping values is only in Properties::No needed, because
1558  // when there are invalid values the property must be Properties::No
1559  switch (mode) {
1560  case ColumnMode::Numeric: {
1561  auto* vec = static_cast<QVector<double>*>(data());
1562  for (int row = startIndex; row < endIndex; ++row) {
1563  if (!isValid(row) || isMasked(row))
1564  continue;
1565 
1566  const double val = vec->at(row);
1567  if (std::isnan(val))
1568  continue;
1569 
1570  if (val < min)
1571  min = val;
1572  }
1573  break;
1574  }
1575  case ColumnMode::Integer: {
1576  auto* vec = static_cast<QVector<int>*>(data());
1577  for (int row = startIndex; row < endIndex; ++row) {
1578  if (!isValid(row) || isMasked(row))
1579  continue;
1580 
1581  const int val = vec->at(row);
1582 
1583  if (val < min)
1584  min = val;
1585  }
1586  break;
1587  }
1588  case ColumnMode::BigInt: {
1589  auto* vec = static_cast<QVector<qint64>*>(data());
1590  for (int row = startIndex; row < endIndex; ++row) {
1591  if (!isValid(row) || isMasked(row))
1592  continue;
1593 
1594  const qint64 val = vec->at(row);
1595 
1596  if (val < min)
1597  min = val;
1598  }
1599  break;
1600  }
1601  case ColumnMode::Text:
1602  break;
1603  case ColumnMode::DateTime: {
1604  auto* vec = static_cast<QVector<QDateTime>*>(data());
1605  for (int row = startIndex; row < endIndex; ++row) {
1606  if (!isValid(row) || isMasked(row))
1607  continue;
1608 
1609  const qint64 val = vec->at(row).toMSecsSinceEpoch();
1610 
1611  if (val < min)
1612  min = val;
1613  }
1614  break;
1615  }
1616  case ColumnMode::Day:
1617  case ColumnMode::Month:
1618  default:
1619  break;
1620  }
1621  return min;
1622  }
1623 
1624  // use the properties knowledge to determine maximum faster
1625  if (property == Properties::Constant || property == Properties::MonotonicIncreasing)
1626  foundIndex = startIndex;
1627  else if (property == Properties::MonotonicDecreasing)
1628  foundIndex = endIndex;
1629 
1630  switch (mode) {
1631  case ColumnMode::Numeric:
1632  case ColumnMode::Integer:
1633  case ColumnMode::BigInt:
1634  return valueAt(foundIndex);
1635  case ColumnMode::DateTime:
1636  case ColumnMode::Month:
1637  case ColumnMode::Day:
1638  return dateTimeAt(foundIndex).toMSecsSinceEpoch();
1639  case ColumnMode::Text:
1640  default:
1641  break;
1642  }
1643 
1644  return min;
1645 }
1646 
1647 /*!
1648  * calculates the maximal value in the column.
1649  * for \c count = 0, the maximum of all elements is returned.
1650  * for \c count > 0, the maximum of the first \p count elements is returned.
1651  * for \c count < 0, the maximum of the last \p count elements is returned.
1652  */
1653 double Column::maximum(int count) const {
1654  double max = -INFINITY;
1655 
1656  if (count == 0 && d->statisticsAvailable)
1657  max = const_cast<Column*>(this)->statistics().maximum;
1658  else {
1659  int start, end;
1660 
1661  if (count == 0) {
1662  start = 0;
1663  end = rowCount();
1664  } else if (count > 0) {
1665  start = 0;
1666  end = qMin(rowCount(), count);
1667  } else {
1668  start = qMax(rowCount() + count, 0);
1669  end = rowCount();
1670  }
1671  return maximum(start, end);
1672  }
1673 
1674  return max;
1675 }
1676 
1677 /*!
1678  * \brief Column::maximum
1679  * Calculates the maximum value in the column between the \p startIndex and \p endIndex.
1680  * If startIndex is greater than endIndex the indices are swapped
1681  * \p startIndex
1682  * \p endIndex
1683  */
1684 double Column::maximum(int startIndex, int endIndex) const {
1685  double max = -INFINITY;
1686  if (rowCount() == 0)
1687  return max;
1688 
1689  if (startIndex > endIndex && startIndex >= 0 && endIndex >= 0)
1690  std::swap(startIndex, endIndex);
1691 
1692  startIndex = qMax(startIndex, 0);
1693  endIndex = qMax(endIndex, 0);
1694 
1695  startIndex = qMin(startIndex, rowCount() - 1);
1696  endIndex = qMin(endIndex, rowCount() - 1);
1697  int foundIndex = 0;
1698 
1700  Properties property = properties();
1701  if (property == Properties::No) {
1702  switch (mode) {
1703  case ColumnMode::Numeric: {
1704  auto* vec = static_cast<QVector<double>*>(data());
1705  for (int row = startIndex; row < endIndex; ++row) {
1706  if (!isValid(row) || isMasked(row))
1707  continue;
1708  const double val = vec->at(row);
1709  if (std::isnan(val))
1710  continue;
1711 
1712  if (val > max)
1713  max = val;
1714  }
1715  break;
1716  }
1717  case ColumnMode::Integer: {
1718  auto* vec = static_cast<QVector<int>*>(data());
1719  for (int row = startIndex; row < endIndex; ++row) {
1720  if (!isValid(row) || isMasked(row))
1721  continue;
1722  const int val = vec->at(row);
1723 
1724  if (val > max)
1725  max = val;
1726  }
1727  break;
1728  }
1729  case ColumnMode::BigInt: {
1730  auto* vec = static_cast<QVector<qint64>*>(data());
1731  for (int row = startIndex; row < endIndex; ++row) {
1732  if (!isValid(row) || isMasked(row))
1733  continue;
1734  const qint64 val = vec->at(row);
1735 
1736  if (val > max)
1737  max = val;
1738  }
1739  break;
1740  }
1741  case ColumnMode::Text:
1742  break;
1743  case ColumnMode::DateTime: {
1744  auto* vec = static_cast<QVector<QDateTime>*>(data());
1745  for (int row = startIndex; row < endIndex; ++row) {
1746  if (!isValid(row) || isMasked(row))
1747  continue;
1748  const qint64 val = vec->at(row).toMSecsSinceEpoch();
1749 
1750  if (val > max)
1751  max = val;
1752  }
1753  break;
1754  }
1755  case ColumnMode::Day:
1756  case ColumnMode::Month:
1757  default:
1758  break;
1759  }
1760  return max;
1761  }
1762 
1763  // use the properties knowledge to determine maximum faster
1764  if (property == Properties::Constant || property == Properties::MonotonicDecreasing)
1765  foundIndex = startIndex;
1766  else if (property == Properties::MonotonicIncreasing)
1767  foundIndex = endIndex;
1768 
1769  switch (mode) {
1770  case ColumnMode::Numeric:
1771  case ColumnMode::Integer:
1772  case ColumnMode::BigInt:
1773  return valueAt(foundIndex);
1774  case ColumnMode::DateTime:
1775  case ColumnMode::Month:
1776  case ColumnMode::Day:
1777  return dateTimeAt(foundIndex).toMSecsSinceEpoch();
1778  case ColumnMode::Text:
1779  default:
1780  break;
1781  }
1782  return max;
1783 }
1784 
1785 /*!
1786  * calculates log2(x)+1 for an integer value.
1787  * Used in y(double x) to calculate the maximum steps
1788  * source: https://stackoverflow.com/questions/11376288/fast-computing-of-log2-for-64-bit-integers
1789  * source: https://graphics.stanford.edu/~seander/bithacks.html#IntegerLogLookup
1790  * @param value
1791  * @return returns calculated value
1792  */
1793 // TODO: testing if it is faster than calculating log2.
1794 // TODO: put into NSL when useful
1795 int Column::calculateMaxSteps (unsigned int value) {
1796  const std::array<signed char, 256> LogTable256 = {
1797  -1,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,
1798  4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
1799  5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
1800  5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
1801  6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
1802  6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
1803  6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
1804  6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
1805  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
1806  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
1807  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
1808  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
1809  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
1810  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
1811  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
1812  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7
1813  };
1814 
1815  unsigned int r; // r will be lg(v)
1816  unsigned int t, tt; // temporaries
1817  if ((tt = value >> 16))
1818  r = (t = tt >> 8) ? 24 + LogTable256[t] : 16 + LogTable256[tt];
1819  else
1820  r = (t = value >> 8) ? 8 + LogTable256[t] : LogTable256[value];
1821 
1822  return r+1;
1823 }
1824 
1825 /*!
1826 * Find index which corresponds to a @p x . In a vector of values
1827 * When monotonic increasing or decreasing a different algorithm will be used, which needs less steps (mean) (log_2(rowCount)) to find the value.
1828 * @param x
1829 * @return -1 if index not found, otherwise the index
1830 */
1831 int Column::indexForValue(double x, QVector<double>& column, Properties properties) {
1832  int rowCount = column.count();
1833  if (rowCount == 0)
1834  return -1;
1835 
1836  double prevValue = 0;
1837  //qint64 prevValueDateTime = 0;
1840  // bisects the index every time, so it is possible to find the value in log_2(rowCount) steps
1841  bool increase = true;
1843  increase = false;
1844 
1845  int lowerIndex = 0;
1846  int higherIndex = rowCount-1;
1847 
1848  unsigned int maxSteps = calculateMaxSteps(static_cast<unsigned int>(rowCount))+1;
1849 
1850  for (unsigned int i = 0; i < maxSteps; i++) { // so no log_2(rowCount) needed
1851  int index = lowerIndex + round(static_cast<double>(higherIndex - lowerIndex)/2);
1852  double value = column[index];
1853 
1854  if (higherIndex - lowerIndex < 2) {
1855  if (qAbs(column[lowerIndex] - x) < qAbs(column[higherIndex] - x))
1856  index = lowerIndex;
1857  else
1858  index = higherIndex;
1859 
1860  return index;
1861  }
1862 
1863  if (value > x && increase)
1864  higherIndex = index;
1865  else if (value >= x && !increase)
1866  lowerIndex = index;
1867  else if (value <= x && increase)
1868  lowerIndex = index;
1869  else if (value < x && !increase)
1870  higherIndex = index;
1871 
1872  }
1874  return 0;
1875  } else {
1876  // AbstractColumn::Properties::No
1877  // simple way
1878  int index = 0;
1879  prevValue = column[0];
1880  for (int row = 0; row < rowCount; row++) {
1881  double value = column[row];
1882  if (std::abs(value - x) <= std::abs(prevValue - x)) { // "<=" prevents also that row - 1 become < 0
1883  prevValue = value;
1884  index = row;
1885  }
1886  }
1887  return index;
1888  }
1889  return -1;
1890 }
1891 
1892 /*!
1893 * Find index which corresponds to a @p x . In a vector of values
1894 * When monotonic increasing or decreasing a different algorithm will be used, which needs less steps (mean) (log_2(rowCount)) to find the value.
1895 * @param x
1896 * @return -1 if index not found, otherwise the index
1897 */
1898 int Column::indexForValue(const double x, const QVector<QPointF>& points, Properties properties) {
1899  int rowCount = points.count();
1900 
1901  if (rowCount == 0)
1902  return -1;
1903 
1904  double prevValue = 0;
1905  //qint64 prevValueDateTime = 0;
1908  // bisects the index every time, so it is possible to find the value in log_2(rowCount) steps
1909  bool increase = true;
1911  increase = false;
1912 
1913  int lowerIndex = 0;
1914  int higherIndex = rowCount - 1;
1915 
1916  unsigned int maxSteps = calculateMaxSteps(static_cast<unsigned int>(rowCount))+1;
1917 
1918  for (unsigned int i = 0; i < maxSteps; i++) { // so no log_2(rowCount) needed
1919  int index = lowerIndex + round(static_cast<double>(higherIndex - lowerIndex)/2);
1920  double value = points[index].x();
1921 
1922  if (higherIndex - lowerIndex < 2) {
1923  if (qAbs(points[lowerIndex].x() - x) < qAbs(points[higherIndex].x() - x))
1924  index = lowerIndex;
1925  else
1926  index = higherIndex;
1927 
1928  return index;
1929  }
1930 
1931  if (value > x && increase)
1932  higherIndex = index;
1933  else if (value >= x && !increase)
1934  lowerIndex = index;
1935  else if (value <= x && increase)
1936  lowerIndex = index;
1937  else if (value < x && !increase)
1938  higherIndex = index;
1939 
1940  }
1941 
1943  return 0;
1944  } else {
1945  // AbstractColumn::Properties::No
1946  // naiv way
1947  prevValue = points[0].x();
1948  int index = 0;
1949  for (int row = 0; row < rowCount; row++) {
1950 
1951  double value = points[row].x();
1952  if (qAbs(value - x) <= qAbs(prevValue - x)) { // "<=" prevents also that row - 1 become < 0
1953  prevValue = value;
1954  index = row;
1955  }
1956  }
1957  return index;
1958  }
1959  return -1;
1960 }
1961 
1962 /*!
1963 * Find index which corresponds to a @p x . In a vector of values
1964 * When monotonic increasing or decreasing a different algorithm will be used, which needs less steps (mean) (log_2(rowCount)) to find the value.
1965 * @param x
1966 * @return -1 if index not found, otherwise the index
1967 */
1968 int Column::indexForValue(double x, QVector<QLineF>& lines, Properties properties) {
1969  int rowCount = lines.count();
1970  if (rowCount == 0)
1971  return -1;
1972  // use only p1 to find index
1973  double prevValue = 0;
1974  //qint64 prevValueDateTime = 0;
1977  // bisects the index every time, so it is possible to find the value in log_2(rowCount) steps
1978  bool increase = true;
1980  increase = false;
1981 
1982  int lowerIndex = 0;
1983  int higherIndex = rowCount-1;
1984 
1985  unsigned int maxSteps = calculateMaxSteps(static_cast<unsigned int>(rowCount))+1;
1986 
1987  for (unsigned int i = 0; i < maxSteps; i++) { // so no log_2(rowCount) needed
1988  int index = lowerIndex + round(static_cast<double>(higherIndex - lowerIndex)/2);
1989  double value = lines[index].p1().x();
1990 
1991  if (higherIndex - lowerIndex < 2) {
1992  if (qAbs(lines[lowerIndex].p1().x() - x) < qAbs(lines[higherIndex].p1().x() - x))
1993  index = lowerIndex;
1994  else
1995  index = higherIndex;
1996 
1997  return index;
1998  }
1999 
2000  if (value > x && increase)
2001  higherIndex = index;
2002  else if (value >= x && !increase)
2003  lowerIndex = index;
2004  else if (value <= x && increase)
2005  lowerIndex = index;
2006  else if (value < x && !increase)
2007  higherIndex = index;
2008 
2009  }
2010 
2012  return 0;
2013  } else {
2014  // AbstractColumn::Properties::No
2015  // naiv way
2016  int index = 0;
2017  prevValue = lines[0].p1().x();
2018  for (int row = 0; row < rowCount; row++) {
2019  double value = lines[row].p1().x();
2020  if (qAbs(value - x) <= qAbs(prevValue - x)) { // "<=" prevents also that row - 1 become < 0
2021  prevValue = value;
2022  index = row;
2023  }
2024  }
2025  return index;
2026  }
2027  return -1;
2028 }
2029 
2030 int Column::indexForValue(double x) const {
2031 
2032  double prevValue = 0;
2033  qint64 prevValueDateTime = 0;
2034  auto mode = columnMode();
2035  auto property = properties();
2036  if (property == Properties::MonotonicIncreasing ||
2037  property == Properties::MonotonicDecreasing) {
2038  // bisects the index every time, so it is possible to find the value in log_2(rowCount) steps
2039  bool increase = (property != Properties::MonotonicDecreasing);
2040 
2041  int lowerIndex = 0;
2042  int higherIndex = rowCount() - 1;
2043 
2044  unsigned int maxSteps = calculateMaxSteps(static_cast<unsigned int>(rowCount())) + 1;
2045 
2046  switch (mode) {
2047  case ColumnMode::Numeric:
2048  case ColumnMode::Integer:
2049  case ColumnMode::BigInt:
2050  for (unsigned int i = 0; i < maxSteps; i++) { // so no log_2(rowCount) needed
2051  int index = lowerIndex + round(static_cast<double>(higherIndex - lowerIndex)/2);
2052  double value = valueAt(index);
2053 
2054  if (higherIndex - lowerIndex < 2) {
2055  if (qAbs(valueAt(lowerIndex) - x) < qAbs(valueAt(higherIndex) - x))
2056  index = lowerIndex;
2057  else
2058  index = higherIndex;
2059 
2060  return index;
2061  }
2062 
2063  if (value > x && increase)
2064  higherIndex = index;
2065  else if (value >= x && !increase)
2066  lowerIndex = index;
2067  else if (value <= x && increase)
2068  lowerIndex = index;
2069  else if (value < x && !increase)
2070  higherIndex = index;
2071 
2072  }
2073  break;
2074  case ColumnMode::Text:
2075  break;
2076  case ColumnMode::DateTime:
2077  case ColumnMode::Month:
2078  case ColumnMode::Day: {
2079  qint64 xInt64 = static_cast<qint64>(x);
2080  for (unsigned int i = 0; i < maxSteps; i++) { // so no log_2(rowCount) needed
2081  int index = lowerIndex + round(static_cast<double>(higherIndex - lowerIndex)/2);
2082  qint64 value = dateTimeAt(index).toMSecsSinceEpoch();
2083 
2084  if (higherIndex - lowerIndex < 2) {
2085  if (abs(dateTimeAt(lowerIndex).toMSecsSinceEpoch() - xInt64) < abs(dateTimeAt(higherIndex).toMSecsSinceEpoch() - xInt64))
2086  index = lowerIndex;
2087  else
2088  index = higherIndex;
2089 
2090  return index;
2091  }
2092 
2093  if (value > xInt64 && increase)
2094  higherIndex = index;
2095  else if (value >= xInt64 && !increase)
2096  lowerIndex = index;
2097  else if (value <= xInt64 && increase)
2098  lowerIndex = index;
2099  else if (value < xInt64 && !increase)
2100  higherIndex = index;
2101 
2102  }
2103  }
2104  }
2105 
2106  } else if (property == Properties::Constant) {
2107  if (rowCount() > 0)
2108  return 0;
2109  else
2110  return -1;
2111  } else {
2112  // naiv way
2113  int index = 0;
2114  switch (mode) {
2115  case ColumnMode::Numeric:
2116  case ColumnMode::Integer:
2117  case ColumnMode::BigInt:
2118  for (int row = 0; row < rowCount(); row++) {
2119  if (!isValid(row) || isMasked(row))
2120  continue;
2121  if (row == 0)
2122  prevValue = valueAt(row);
2123 
2124  double value = valueAt(row);
2125  if (abs(value - x) <= abs(prevValue - x)) { // <= prevents also that row - 1 become < 0
2126  if (row < rowCount() - 1) {
2127  prevValue = value;
2128  index = row;
2129  }
2130  }
2131  }
2132  return index;
2133  case ColumnMode::Text:
2134  break;
2135  case ColumnMode::DateTime:
2136  case ColumnMode::Month:
2137  case ColumnMode::Day: {
2138  qint64 xInt64 = static_cast<qint64>(x);
2139  for (int row = 0; row < rowCount(); row++) {
2140  if (!isValid(row) || isMasked(row))
2141  continue;
2142 
2143  if (row == 0)
2144  prevValueDateTime = dateTimeAt(row).toMSecsSinceEpoch();
2145 
2146  qint64 value = dateTimeAt(row).toMSecsSinceEpoch();
2147  if (abs(value - xInt64) <= abs(prevValueDateTime - xInt64)) { // "<=" prevents also that row - 1 become < 0
2148  prevValueDateTime = value;
2149  index = row;
2150  }
2151  }
2152  return index;
2153  }
2154  }
2155  }
2156  return -1;
2157 }
2158 
2159 /*!
2160  * Finds the minimal and maximal index which are between v1 and v2
2161  * \brief Column::indicesForX
2162  * \param v1
2163  * \param v2
2164  * \param start
2165  * \param end
2166  * \return
2167  */
2168 bool Column::indicesMinMax(double v1, double v2, int& start, int& end) const {
2169 
2170  start = -1;
2171  end = -1;
2172  if (rowCount() == 0)
2173  return false;
2174 
2175  // Assumption: v1 is always the smaller value
2176  if (v1 > v2)
2177  qSwap(v1, v2);
2178 
2179  Properties property = properties();
2180  if (property == Properties::MonotonicIncreasing ||
2181  property == Properties::MonotonicDecreasing) {
2182  start = indexForValue(v1);
2183  end = indexForValue(v2);
2184 
2185  switch (columnMode()) {
2186  case ColumnMode::Integer:
2187  case ColumnMode::BigInt:
2188  case ColumnMode::Numeric: {
2189  if (start > 0 && valueAt(start - 1) <= v2 && valueAt(start - 1) >= v1)
2190  start--;
2191  if (end < rowCount() - 1 && valueAt(end + 1) <= v2 && valueAt(end + 1) >= v1)
2192  end++;
2193 
2194  break;
2195  }
2196  case ColumnMode::DateTime:
2197  case ColumnMode::Month:
2198  case ColumnMode::Day: {
2199  qint64 v1int64 = v1;
2200  qint64 v2int64 = v2;
2201  qint64 value;
2202  if (start > 0) {
2203  value = dateTimeAt(start -1).toMSecsSinceEpoch();
2204  if (value <= v2int64 && value >= v1int64)
2205  start--;
2206  }
2207 
2208  if (end > rowCount() - 1) {
2209  value = dateTimeAt(end + 1).toMSecsSinceEpoch();
2210  if (value <= v2int64 && value >= v1int64)
2211  end++;
2212  }
2213  break;
2214  }
2215  case ColumnMode::Text:
2216  return false;
2217  }
2218  return true;
2219  } else if (property == Properties::Constant) {
2220  start = 0;
2221  end = rowCount() - 1;
2222  return true;
2223  }
2224  // property == Properties::No
2225  switch (columnMode()) {
2226  case ColumnMode::Integer:
2227  case ColumnMode::BigInt:
2228  case ColumnMode::Numeric: {
2229  double value;
2230  for (int i = 0; i < rowCount(); i++) {
2231  if (!isValid(i) || isMasked(i))
2232  continue;
2233  value = valueAt(i);
2234  if (value <= v2 && value >= v1) {
2235  end = i;
2236  if (start < 0)
2237  start = i;
2238  }
2239 
2240  }
2241  break;
2242  }
2243  case ColumnMode::DateTime:
2244  case ColumnMode::Month:
2245  case ColumnMode::Day: {
2246  qint64 value;
2247  qint64 v2int64 = v2;
2248  qint64 v1int64 = v2;
2249  for (int i = 0; i < rowCount(); i++) {
2250  if (!isValid(i) || isMasked(i))
2251  continue;
2252  value = dateTimeAt(i).toMSecsSinceEpoch();
2253  if (value <= v2int64 && value >= v1int64) {
2254  end = i;
2255  if (start < 0)
2256  start = i;
2257  }
2258  }
2259  break;
2260  }
2261  case ColumnMode::Text:
2262  return false;
2263 
2264  }
2265  return true;
2266 }
AspectType
bool readCommentElement(XmlStreamReader *)
Load comment from an XML element.
@ Recursive
Recursively handle all descendents, not just immediate children.
void aspectDescriptionChanged(const AbstractAspect *)
Emitted after the name, comment or caption spec have changed.
void addChildFast(AbstractAspect *)
Add the given Aspect to my list of children without any checks and without putting this step onto the...
void addChild(AbstractAspect *)
Add the given Aspect to my list of children.
QString name() const
virtual QMenu * createContextMenu()
Return a new context menu.
void writeBasicAttributes(QXmlStreamWriter *) const
Save name and creation time to XML.
void beginMacro(const QString &text)
Begin an undo stack macro (series of commands)
virtual QString path() const
Return the path that leads from the top-most Aspect (usually a Project) to me.
void exec(QUndoCommand *)
Execute the given command, pushing it on the undoStack() if available.
QVector< AbstractAspect * > children(AspectType type, ChildIndexFlags flags={}) const
bool readBasicAttributes(XmlStreamReader *)
Load name and creation time from XML.
void writeCommentElement(QXmlStreamWriter *) const
Save the comment to XML.
void removeChild(AbstractAspect *)
Remove the given Aspect from my list of children.
virtual Project * project()
Return the Project this Aspect belongs to, or 0 if it is currently not part of one.
void endMacro()
End the current undo stack macro.
void setHidden(bool)
Set "hidden" property, i.e. whether to exclude this aspect from being shown in the explorer.
Interface definition for data with column logic.
static QIcon iconForMode(ColumnMode mode)
Convenience method for mode-dependent icon.
virtual double valueAt(int row) const
Return the double value in row 'row'.
bool isMasked(int row) const
Return whether a certain row is masked.
virtual void handleRowInsertion(int before, int count)
void maskingChanged(const AbstractColumn *source)
Rows have been masked or unmasked.
virtual QString textAt(int row) const
Return the content of row 'row'.
virtual void handleRowRemoval(int first, int count)
virtual QDateTime dateTimeAt(int row) const
Return the QDateTime in row 'row'.
void dataChanged(const AbstractColumn *source)
Data of the column has changed.
virtual qint64 bigIntAt(int row) const
Return the bigint value in row 'row'.
void formatChanged(const AbstractColumn *source)
void XmlWriteMask(QXmlStreamWriter *) const
Write XML mask element.
bool isValid(int row) const
Convenience method for mode-independent testing of validity.
virtual int integerAt(int row) const
Return the integer value in row 'row'.
virtual ColumnMode columnMode() const =0
Return the column mode.
bool XmlReadMask(XmlStreamReader *)
Read XML mask element.
bool input(int port, const AbstractColumn *source)
Connect the provided data source to the specified input port.
Simplified filter interface for filters with only one output port.
void setNumberLocale(const QLocale &locale)
void save(QXmlStreamWriter *) const override
Save to XML.
bool load(XmlStreamReader *, bool preview) override
Load from XML.
A xy-plot.
Definition: CartesianPlot.h:58
Clear the column.
Clear all associated formulas.
Copy a complete column.
Insert empty rows.
Copy parts of a column.
bool statisticsAvailable
QString formula() const
Return the formula last used to generate data for the column.
bool propertiesAvailable
const QStringList & formulaVariableNames() const
QTime timeAt(int row) const
Return the time part of row 'row'.
qint64 bigIntAt(int row) const
Return the bigint value in row 'row'.
QString textAt(int row) const
Return the content of row 'row'.
AbstractColumn::PlotDesignation plotDesignation() const
Return the column plot designation.
bool formulaAutoUpdate() const
AbstractColumn::Properties properties
void setformulVariableColumnsPath(int index, const QString &path)
int availableRowCount() const
Return the number of available rows.
int integerAt(int row) const
Return the int value in row 'row'.
bool hasValuesAvailable
AbstractSimpleFilter * inputFilter() const
Return the input filter (for string -> data type conversion)
void setPlotDesignation(AbstractColumn::PlotDesignation)
Set the column plot designation.
QDate dateAt(int row) const
Return the date part of row 'row'.
void setformulVariableColumn(int index, Column *column)
double valueAt(int row) const
Return the double value in row 'row' for columns with type Numeric, Integer or BigInt....
const QStringList & formulaVariableColumnPaths() const
AbstractColumn::ColumnMode columnMode() const
int width() const
Get width.
void setWidth(int)
Set width.
void resizeTo(int)
Resize the vector to the specified number of rows.
QVector< Interval< int > > formulaIntervals() const
Return the intervals that have associated formulas.
const QVector< Column * > & formulaVariableColumns() const
void * data() const
Return the data pointer.
void setFormula(const QString &formula, const QStringList &variableNames, const QVector< Column * > &variableColumns, bool autoUpdate)
Sets the formula used to generate column values.
AbstractColumn::ColumnStatistics statistics
void replaceData(void *)
Replace data pointer.
int rowCount() const
Return the data vector size.
QDateTime dateTimeAt(int row) const
Return the QDateTime in row 'row'.
AbstractSimpleFilter * outputFilter() const
Return the output filter (for data type -> string conversion)
Remove consecutive rows from a column.
Replace a range of integers in a int column.
Replace a range of date-times in a date-time column.
Replace a range of integers in a int column.
Replace a range of strings in a string column.
Replace a range of doubles in a double column.
Set the value for a bigint cell.
Set the value for a int cell.
Set the column mode.
Sets a column's plot designation.
Set the text for a string cell.
Set the value for a double cell.
String-IO interface of Column.
QString textAt(int) const override
Return the content of row 'row'.
Aspect that manages a column.
Definition: Column.h:42
QString formula() const
Returns the formula used to generate column values.
Definition: Column.cpp:440
void setColumnMode(AbstractColumn::ColumnMode) override
Set the column mode.
Definition: Column.cpp:289
QAction * m_copyDataAction
Definition: Column.h:161
ColumnStringIO * asStringColumn() const
Return a wrapper column object used for String I/O.
Definition: Column.cpp:1451
QVector< Interval< int > > formulaIntervals() const override
Return the intervals that have associated formulas.
Definition: Column.cpp:1479
void setValueAt(int, double) override
Set the content of row 'row'.
Definition: Column.cpp:579
const QVector< Column * > & formulaVariableColumns() const
Definition: Column.cpp:448
QTime timeAt(int) const override
Return the time part of row 'row'.
Definition: Column.cpp:959
QDateTime dateTimeAt(int) const override
Return the QDateTime in row 'row'.
Definition: Column.cpp:968
void setFromColumn(int, AbstractColumn *, int)
Definition: Column.cpp:910
void replaceInteger(int, const QVector< int > &) override
Replace a range of values.
Definition: Column.cpp:607
QIcon icon() const override
Return an icon to be used for decorating the views and spreadsheet column headers.
Definition: Column.cpp:1015
const QStringList & formulaVariableNames() const
Definition: Column.cpp:444
bool indicesMinMax(double v1, double v2, int &start, int &end) const override
Column::indicesForX.
Definition: Column.cpp:2168
void replaceBigInt(int, const QVector< qint64 > &) override
Replace a range of values.
Definition: Column.cpp:629
double minimum(int count=0) const override
Definition: Column.cpp:1507
void handleRowRemoval(int first, int count) override
Remove 'count' rows starting from row 'first'.
Definition: Column.cpp:389
~Column() override
Definition: Column.cpp:107
Column(const QString &name, AbstractColumn::ColumnMode=ColumnMode::Numeric)
Definition: Column.cpp:80
void copyData()
Definition: Column.cpp:213
void replaceValues(int, const QVector< double > &) override
Replace a range of values.
Definition: Column.cpp:588
void setIntegerAt(int, int) override
Set the content of row 'row'.
Definition: Column.cpp:598
int rowCount() const override
Return the data vector size.
Definition: Column.cpp:1395
bool copy(const AbstractColumn *) override
Copy another column of the same type.
Definition: Column.cpp:348
bool m_suppressDataChangedSignal
Definition: Column.h:160
int availableRowCount() const override
Return the number of available data rows.
Definition: Column.cpp:1406
bool isReadOnly() const override
Return whether the object is read-only.
Definition: Column.cpp:1377
void setPlotDesignation(AbstractColumn::PlotDesignation) override
Set the column plot designation.
Definition: Column.cpp:403
QMenu * createContextMenu() override
Return a new context menu.
Definition: Column.cpp:112
ColumnStringIO * m_string_io
Definition: Column.h:165
void setChanged()
Definition: Column.cpp:998
void navigateTo(QAction *)
Definition: Column.cpp:206
void setSuppressDataChangedSignal(const bool)
Definition: Column.cpp:246
bool XmlReadInputFilter(XmlStreamReader *)
Read XML input filter element.
Definition: Column.cpp:1230
QActionGroup * m_usedInActionGroup
Definition: Column.h:162
bool XmlReadOutputFilter(XmlStreamReader *)
Read XML output filter element.
Definition: Column.cpp:1242
void clear() override
Clear the whole column.
Definition: Column.cpp:425
ColumnPrivate * d
Definition: Column.h:164
void requestProjectContextMenu(QMenu *)
void replaceTexts(int, const QVector< QString > &) override
Replace a range of values.
Definition: Column.cpp:532
AbstractColumn::ColumnMode columnMode() const override
Return the column mode.
Definition: Column.cpp:1388
const AbstractColumn::ColumnStatistics & statistics() const
Definition: Column.cpp:646
AbstractSimpleFilter * outputFilter() const
Definition: Column.cpp:1444
void updateLocale()
Definition: Column.cpp:200
QString textAt(int) const override
Return the content of row 'row'.
Definition: Column.cpp:941
static int calculateMaxSteps(unsigned int value)
Definition: Column.cpp:1795
static int indexForValue(double x, QVector< double > &column, Properties properties=Properties::No)
Definition: Column.cpp:1831
void setWidth(const int)
Set width.
Definition: Column.cpp:418
void setformulVariableColumnsPath(int index, const QString &path)
Definition: Column.cpp:456
qint64 bigIntAt(int) const override
Return the bigint value in row 'row'.
Definition: Column.cpp:989
friend class ColumnStringIO
Definition: Column.h:176
void setTimeAt(int, QTime) override
Set the content of row 'row'.
Definition: Column.cpp:551
void setformulVariableColumn(int index, Column *)
Definition: Column.cpp:460
bool XmlReadRow(XmlStreamReader *)
Read XML row element.
Definition: Column.cpp:1314
void setBigIntAt(int, qint64) override
Set the content of row 'row'.
Definition: Column.cpp:617
double maximum(int count=0) const override
Definition: Column.cpp:1653
void setFormula(const QString &formula, const QStringList &variableNames, const QVector< Column * > &columns, bool autoUpdate)
Sets the formula used to generate column values.
Definition: Column.cpp:471
void setDateAt(int, QDate) override
Set the content of row 'row'.
Definition: Column.cpp:542
void setColumnModeFast(AbstractColumn::ColumnMode)
Definition: Column.cpp:313
void handleFormatChange()
Definition: Column.cpp:1483
void finalizeLoad()
Definition: Column.cpp:1223
AbstractColumn::PlotDesignation plotDesignation() const override
Return the column plot designation.
Definition: Column.cpp:1413
bool isDraggable() const override
Definition: Column.cpp:333
const QStringList & formulaVariableColumnPaths() const
Definition: Column.cpp:452
double valueAt(int) const override
Return the double value in row 'row'.
Definition: Column.cpp:975
void calculateStatistics() const
Definition: Column.cpp:653
void init()
Common part of ctors.
Definition: Column.cpp:89
void save(QXmlStreamWriter *) const override
Save the column as XML.
Definition: Column.cpp:1027
bool XmlReadFormula(XmlStreamReader *)
Read XML formula element.
Definition: Column.cpp:1254
QString plotDesignationString() const override
Definition: Column.cpp:1417
int integerAt(int) const override
Return the int value in row 'row'.
Definition: Column.cpp:982
int width() const
Get width.
Definition: Column.cpp:411
bool hasValues() const
Definition: Column.cpp:859
Properties properties() const override
Column::properties Returns the column properties of this curve (monoton increasing,...
Definition: Column.cpp:639
QVector< AspectType > dropableOn() const override
Definition: Column.cpp:337
void * data() const
Definition: Column.cpp:852
void addUsedInPlots(QVector< CartesianPlot * > &)
Definition: Column.cpp:250
void updateFormula()
Definition: Column.cpp:481
void setTextAt(int, const QString &) override
Set the content of row 'row'.
Definition: Column.cpp:523
QDate dateAt(int) const override
Return the date part of row 'row'.
Definition: Column.cpp:950
void clearFormulas() override
Clear all formulas.
Definition: Column.cpp:505
void replaceDateTimes(int, const QVector< QDateTime > &) override
Replace a range of values.
Definition: Column.cpp:569
void handleRowInsertion(int before, int count) override
Insert some empty (or initialized with zero) rows.
Definition: Column.cpp:375
void setDateTimeAt(int, const QDateTime &) override
Set the content of row 'row'.
Definition: Column.cpp:560
bool load(XmlStreamReader *, bool preview) override
Load the column from XML.
Definition: Column.cpp:1152
bool formulaAutoUpdate() const
Definition: Column.cpp:464
Conversion filter QDateTime -> QString.
ColumnPrivate * m_private
Definition: Column.cpp:1145
DecodeColumnTask(ColumnPrivate *priv, const QString &content)
Definition: Column.cpp:1123
QString m_content
Definition: Column.cpp:1146
void run() override
Definition: Column.cpp:1127
Locale-aware conversion filter double -> QString.
char numericFormat() const
Get format character as in QString::number.
A 2D-curve, provides an interface for editing many properties of the curve.
Definition: Histogram.h:42
Auxiliary class for interval based data.
Definition: Interval.h:212
Represents a project.
Definition: Project.h:42
void navigateTo(const QString &path)
Definition: Project.cpp:344
Conversion filter QString -> QDateTime.
Base class for all analysis curves.
A 2D-curve, provides an interface for editing many properties of the curve.
Definition: XYCurve.h:46
XML stream parser that supports errors as well as warnings. This class also adds line and column numb...
void raiseWarning(const QString &)
int readAttributeInt(const QString &name, bool *ok)
void raiseError(const QString &)
#define STDSTRING(qstr)
Definition: macros.h:67
#define ENUM_TO_STRING(class, enum, value)
Definition: macros.h:69
#define DEBUG(x)
Definition: macros.h:50
#define SET_NUMBER_LOCALE
Definition: macros.h:75
#define i18n(m)
Definition: nsl_common.h:38
#define PERFTRACE(msg)
Definition: trace.h:57