13#include <json/value.h>
14#include <nexus/NeXusFile.hpp>
16#include <boost/regex.hpp>
20using namespace Types::Core;
24Logger
g_log(
"TimeSeriesProperty");
31template <
typename TYPE>
42template <
typename TYPE>
44 const std::vector<Types::Core::DateAndTime> ×,
45 const std::vector<TYPE> &values)
65 auto timeSeriesProperty = this->clone();
66 auto values = timeSeriesProperty->valuesAsVector();
67 auto times = timeSeriesProperty->timesAsVector();
69 for (
auto it = times.begin(); it != times.end(); ++it) {
75 timeSeriesProperty->clear();
76 timeSeriesProperty->addValues(times, values);
77 return timeSeriesProperty;
89 if (this->m_values.size() < 2) {
90 throw std::runtime_error(
"Derivative is not defined for a time-series "
91 "property with less then two values");
94 this->sortIfNecessary();
95 auto it = this->m_values.begin();
96 int64_t t0 = it->time().totalNanoseconds();
100 auto timeSeriesDeriv = std::make_unique<TimeSeriesProperty<double>>(this->name() +
"_derivative");
101 timeSeriesDeriv->reserve(this->m_values.size() - 1);
102 for (; it != m_values.end(); it++) {
103 TYPE v1 = it->value();
104 int64_t t1 = it->time().totalNanoseconds();
106 double deriv = 1.e+9 * (double(v1 - v0) / double(t1 - t0));
107 auto tm =
static_cast<int64_t
>((t1 + t0) / 2);
108 timeSeriesDeriv->addValue(Types::Core::DateAndTime(tm), deriv);
113 return timeSeriesDeriv;
117 throw std::runtime_error(
"Time series property derivative is not defined for strings");
125 return m_values.size() * (
sizeof(
TYPE) +
sizeof(DateAndTime));
146 if (this->
operator!=(*
rhs)) {
147 m_values.insert(m_values.end(),
rhs->m_values.begin(),
rhs->m_values.end());
156 m_size =
static_cast<int>(m_values.size());
160 <<
" could not be added to another property of the same "
161 "name but incompatible type.\n";
174 if (this->name() !=
right.name())
183 if (this->realSize() !=
right.realSize()) {
186 const std::vector<DateAndTime> lhsTimes = this->timesAsVector();
187 const std::vector<DateAndTime> rhsTimes =
right.timesAsVector();
188 if (!std::equal(lhsTimes.begin(), lhsTimes.end(), rhsTimes.begin())) {
192 const std::vector<TYPE> lhsValues = this->valuesAsVector();
193 const std::vector<TYPE> rhsValues =
right.valuesAsVector();
194 if (!std::equal(lhsValues.begin(), lhsValues.end(), rhsValues.begin())) {
220 return !(*
this ==
right);
259template <
typename TYPE>
261 const Types::Core::DateAndTime &stop) {
266 if (m_values.size() <= 1)
269 typename std::vector<TimeValueUnit<TYPE>>::iterator iterhead, iterend;
272 int istart = this->findIndex(start);
273 if (istart >= 0 &&
static_cast<size_t>(istart) < m_values.size()) {
275 iterhead = m_values.begin() + istart;
281 bool useprefiltertime = !(m_values[istart].time() == start);
284 m_values.erase(m_values.begin(), iterhead);
286 if (useprefiltertime) {
287 m_values[0].setTime(start);
295 int iend = this->findIndex(stop);
296 if (
static_cast<size_t>(iend) < m_values.size()) {
297 if (m_values[iend].time() == stop) {
299 iterend = m_values.begin() + iend;
302 iterend = m_values.begin() + iend + 1;
305 m_values.erase(iterend, m_values.end());
309 m_size =
static_cast<int>(m_values.size());
317template <
typename TYPE>
323 if (m_values.size() <= 1) {
328 std::vector<TimeValueUnit<TYPE>> mp_copy;
330 g_log.
debug() <<
"DB541 mp_copy Size = " << mp_copy.size() <<
" Original MP Size = " << m_values.size() <<
"\n";
333 for (
const auto &splitter : splittervec) {
334 Types::Core::DateAndTime t_start = splitter.start();
335 Types::Core::DateAndTime t_stop = splitter.stop();
337 int tstartindex = findIndex(t_start);
338 if (tstartindex < 0) {
341 }
else if (tstartindex >=
int(m_values.size())) {
343 tstartindex = int(m_values.size()) - 1;
346 int tstopindex = findIndex(t_stop);
348 if (tstopindex < 0) {
350 }
else if (tstopindex >=
int(m_values.size())) {
351 tstopindex = int(m_values.size()) - 1;
353 if (t_stop == m_values[
size_t(tstopindex)].time() &&
size_t(tstopindex) > 0) {
359 if (tstartindex < 0 || tstopindex >=
int(m_values.size())) {
363 if (tstartindex == tstopindex) {
365 mp_copy.emplace_back(temp);
367 mp_copy.emplace_back(t_start, m_values[tstartindex].
value());
368 for (
auto im =
size_t(tstartindex + 1); im <= size_t(tstopindex); ++im) {
369 mp_copy.emplace_back(m_values[im].time(), m_values[im].value());
374 g_log.
debug() <<
"DB530 Filtered Log Size = " << mp_copy.size() <<
" Original Log Size = " << m_values.size()
382 m_size =
static_cast<int>(m_values.size());
399template <
typename TYPE>
401 bool isPeriodic)
const {
408 std::vector<TimeSeriesProperty<TYPE> *> outputs_tsp;
410 size_t numOutputs = outputs.size();
412 for (
size_t i = 0; i < numOutputs; i++) {
415 outputs_tsp.emplace_back(myOutput);
416 if (this->m_values.size() == 1) {
418 myOutput->
m_values = this->m_values;
419 myOutput->m_size = 1;
421 myOutput->m_values.clear();
422 myOutput->m_size = 0;
425 outputs_tsp.emplace_back(
nullptr);
430 if (this->m_values.size() == 1)
434 size_t i_property = 0;
437 auto itspl = splitter.begin();
440 g_log.
debug() <<
"[DB] Number of time series entries = " << m_values.size()
441 <<
", Number of splitters = " << splitter.size() <<
"\n";
442 while (itspl != splitter.end() && i_property < m_values.size()) {
444 DateAndTime start = itspl->start();
445 DateAndTime stop = itspl->stop();
447 int output_index = itspl->index();
449 if (output_index < 0 || output_index >=
static_cast<int>(numOutputs))
461 while (i_property < m_values.size() && m_values[i_property].time() < start)
464 if (i_property == m_values.size()) {
466 myOutput->
addValue(m_values[i_property - 1].time(), m_values[i_property - 1].value());
471 if (m_values[i_property].time() > start && i_property > 0 && !isPeriodic) {
474 size_t i_prev = i_property - 1;
475 if (myOutput->
size() == 0 || m_values[i_prev].time() != myOutput->
lastTime())
476 myOutput->
addValue(m_values[i_prev].time(), m_values[i_prev].value());
480 while (i_property < m_values.size() && m_values[i_property].time() < stop) {
483 myOutput->
addValue(m_values[i_property].time(), m_values[i_property].value());
493 for (std::size_t i = 0; i < numOutputs; i++) {
496 myOutput->
m_size = myOutput->realSize();
505template <
typename TYPE>
507 const std::vector<int> &inputWorkspaceIndicies,
508 const std::vector<TimeSeriesProperty *> &output) {
511 if (timeToFilterTo.size() != inputWorkspaceIndicies.size() + 1) {
512 throw std::runtime_error(
"Input time vector's size does not match(one more larger than) target "
513 "workspace index vector's size inputWorkspaceIndicies.size() \n");
523 auto const currentTimes = timesAsVector();
524 auto const currentValues = valuesAsVector();
525 size_t index_splitter = 0;
529 DateAndTime firstPropTime = currentTimes[0];
530 auto firstFilterTime = std::lower_bound(timeToFilterTo.begin(), timeToFilterTo.end(), firstPropTime);
531 if (firstFilterTime == timeToFilterTo.end()) {
535 }
else if (firstFilterTime != timeToFilterTo.begin()) {
537 index_splitter = firstFilterTime - timeToFilterTo.begin() - 1;
540 DateAndTime filterStartTime = timeToFilterTo[index_splitter];
541 DateAndTime filterEndTime;
544 auto firstEntryInSplitter = std::lower_bound(currentTimes.begin(), currentTimes.end(), filterStartTime);
545 if (firstEntryInSplitter == currentTimes.end()) {
549 DateAndTime last_entry_time = this->lastTime();
550 TYPE last_entry_value = this->lastValue();
551 for (
auto &i : output) {
552 i->addValue(last_entry_time, last_entry_value);
560 size_t timeIndex = firstEntryInSplitter - currentTimes.begin();
561 firstPropTime = *firstEntryInSplitter;
563 for (; index_splitter < timeToFilterTo.size() - 1; ++index_splitter) {
564 int wsIndex = inputWorkspaceIndicies[index_splitter];
566 filterStartTime = timeToFilterTo[index_splitter];
567 filterEndTime = timeToFilterTo[index_splitter + 1];
574 const size_t numEntries = currentTimes.size();
577 if (timeIndex >= numEntries) {
580 auto currentTime = currentTimes.back();
581 if (output[wsIndex]->size() == 0 || output[wsIndex]->lastTime() != currentTime) {
582 output[wsIndex]->addValue(currentTime, currentValues.back());
587 for (; timeIndex < numEntries; ++timeIndex) {
588 auto currentTime = currentTimes[timeIndex];
589 if (output[wsIndex]->size() == 0 || output[wsIndex]->lastTime() < currentTime) {
591 output[wsIndex]->addValue(currentTime, currentValues[timeIndex]);
593 if (currentTime > filterEndTime)
600 for (
size_t i = 0; i < output.size(); ++i) {
601 if (output[i]->size() == 0) {
602 std::stringstream errss;
603 errss <<
"entry " << m_name <<
" has 0 size, whose first entry is at " << this->firstTime().toSimpleString();
617#pragma warning(disable : 4244)
618#pragma warning(disable : 4804)
621#if defined(__GNUC__) && !(defined(__INTEL_COMPILER))
622#pragma GCC diagnostic ignored "-Wconversion"
638template <
typename TYPE>
640 double TimeTolerance,
bool centre)
const {
641 const bool emptyMin = (min ==
EMPTY_DBL());
642 const bool emptyMax = (max ==
EMPTY_DBL());
644 if (!emptyMin && !emptyMax && max < min) {
645 std::stringstream ss;
646 ss <<
"TimeSeriesProperty::makeFilterByValue: 'max' argument must be "
647 "greater than 'min' "
648 <<
"(got min=" << min <<
" max=" << max <<
")";
649 throw std::invalid_argument(ss.str());
655 min =
static_cast<double>(
minValue());
657 max =
static_cast<double>(
maxValue());
663 if (m_values.empty())
670 bool lastGood(
false);
671 time_duration tol = DateAndTime::durationFromSeconds(TimeTolerance);
674 DateAndTime start, stop;
676 for (
size_t i = 0; i < m_values.size(); ++i) {
677 const DateAndTime lastTime = t;
679 t = m_values[i].time();
680 TYPE val = m_values[i].value();
683 const bool isGood = ((val >= min) && (val <= max));
687 if (isGood != lastGood) {
693 start = centre ? t - tol : t;
698 stop = centre ? lastTime + tol : t;
699 split.emplace_back(start, stop, 0);
711 split.emplace_back(start, stop, 0);
723 "is not implemented for string "
737template <
typename TYPE>
740 const bool emptyMin = (min ==
EMPTY_DBL());
741 const bool emptyMax = (max ==
EMPTY_DBL());
743 if (!emptyMin && !emptyMax && max < min) {
744 std::stringstream ss;
745 ss <<
"TimeSeriesProperty::expandFilterToRange: 'max' argument must be "
746 "greater than 'min' "
747 <<
"(got min=" << min <<
" max=" << max <<
")";
748 throw std::invalid_argument(ss.str());
754 min =
static_cast<double>(
minValue());
756 max =
static_cast<double>(
maxValue());
759 double val =
static_cast<double>(firstValue());
760 if ((val >= min) && (val <= max)) {
762 extraFilter.emplace_back(range.
begin(), firstTime(), 0);
769 val =
static_cast<double>(lastValue());
770 if ((val >= min) && (val <= max)) {
772 extraFilter.emplace_back(lastTime(), range.
end(), 0);
786 "is not implemented for string "
796 const auto &filter = getSplittingIntervals();
797 retVal = this->averageValueInFilter(filter);
798 }
catch (std::exception &) {
800 retVal = std::numeric_limits<double>::quiet_NaN();
811template <
typename TYPE>
816 if (realSize() == 0 || filter.empty()) {
817 return std::numeric_limits<double>::quiet_NaN();
821 if (realSize() == 1) {
822 return static_cast<double>(m_values.front().value());
827 double numerator(0.0), totalTime(0.0);
829 for (
const auto &time : filter) {
831 totalTime += time.duration();
835 double value =
static_cast<double>(getSingleValue(time.start(),
index));
836 DateAndTime startTime = time.start();
838 while (
index < realSize() - 1 && m_values[
index + 1].time() < time.stop()) {
840 numerator += DateAndTime::secondsFromDuration(m_values[
index].time() - startTime) *
value;
841 startTime = m_values[
index].time();
842 value =
static_cast<double>(m_values[
index].value());
846 numerator += DateAndTime::secondsFromDuration(time.stop() - startTime) *
value;
850 return numerator / totalTime;
858 "averageValueInFilter is not "
859 "implemented for string properties");
863 std::pair<double, double> retVal{0., 0.};
865 const auto &filter = getSplittingIntervals();
866 retVal = this->averageAndStdDevInFilter(filter);
867 }
catch (std::exception &) {
868 retVal.first = std::numeric_limits<double>::quiet_NaN();
869 retVal.second = std::numeric_limits<double>::quiet_NaN();
874template <
typename TYPE>
875std::pair<double, double>
879 const double mean = this->averageValueInFilter(filter);
883 if (realSize() <= 1 || filter.empty()) {
884 return std::pair<double, double>{mean, std::numeric_limits<double>::quiet_NaN()};
887 double numerator(0.0), totalTime(0.0);
889 for (
const auto &time : filter) {
891 totalTime += time.duration();
895 double value =
static_cast<double>(getSingleValue(time.start(),
index));
896 double valuestddev = (
value - mean) * (
value - mean);
897 DateAndTime startTime = time.start();
899 while (
index < realSize() - 1 && m_values[
index + 1].time() < time.stop()) {
902 numerator += DateAndTime::secondsFromDuration(m_values[
index].time() - startTime) * valuestddev;
903 startTime = m_values[
index].time();
904 value =
static_cast<double>(m_values[
index].value());
905 valuestddev = (
value - mean) * (
value - mean);
909 numerator += DateAndTime::secondsFromDuration(time.stop() - startTime) * valuestddev;
913 return std::pair<double, double>{mean, std::sqrt(numerator / totalTime)};
920std::pair<double, double>
923 "averageAndStdDevInFilter is not "
924 "implemented for string properties");
931#if defined(__GNUC__) && !(defined(__INTEL_COMPILER))
932#pragma GCC diagnostic warning "-Wconversion"
946 std::map<DateAndTime, TYPE> asMap;
948 if (!m_values.empty()) {
949 for (
size_t i = 0; i < m_values.size(); i++)
950 asMap[m_values[i].time()] = m_values[i].value();
963 std::vector<TYPE> out;
964 out.reserve(m_values.size());
966 for (
size_t i = 0; i < m_values.size(); i++)
967 out.emplace_back(m_values[i].value());
979 std::multimap<DateAndTime, TYPE> asMultiMap;
981 if (!m_values.empty()) {
982 for (
size_t i = 0; i < m_values.size(); i++)
983 asMultiMap.insert(std::make_pair(m_values[i].time(), m_values[i].value()));
996 std::vector<DateAndTime> out;
997 out.reserve(m_values.size());
999 for (
size_t i = 0; i < m_values.size(); i++) {
1000 out.emplace_back(m_values[i].time());
1011 if (m_filter.empty()) {
1012 return this->timesAsVector();
1014 if (!m_filterApplied) {
1019 std::vector<DateAndTime> out;
1021 for (
const auto &
value : m_values) {
1022 if (isTimeFiltered(
value.time())) {
1023 out.emplace_back(
value.time());
1039 std::vector<double> out;
1040 out.reserve(m_values.size());
1042 Types::Core::DateAndTime start = m_values[0].time();
1043 for (
size_t i = 0; i < m_values.size(); i++) {
1044 out.emplace_back(DateAndTime::secondsFromDuration(m_values[i].time() - start));
1055template <
typename TYPE>
1059 m_values.emplace_back(newvalue);
1077 m_filterApplied =
false;
1087 return addValue(Types::Core::DateAndTime(time),
value);
1097 Types::Core::DateAndTime dt;
1098 dt.set_from_time_t(time);
1099 return addValue(dt,
value);
1107template <
typename TYPE>
1109 const std::vector<TYPE> &values) {
1110 size_t length = std::min(times.size(), values.size());
1111 m_size +=
static_cast<int>(length);
1112 for (
size_t i = 0; i < length; ++i) {
1113 m_values.emplace_back(times[i], values[i]);
1116 if (!values.empty())
1125template <
typename TYPE>
1127 const std::vector<TYPE> &values) {
1129 addValues(times, values);
1137 if (m_values.empty()) {
1138 const std::string
error(
"lastTime(): TimeSeriesProperty '" + name() +
"' is empty");
1140 throw std::runtime_error(
error);
1145 return m_values.rbegin()->time();
1152 if (m_values.empty()) {
1153 const std::string
error(
"firstValue(): TimeSeriesProperty '" + name() +
"' is empty");
1155 throw std::runtime_error(
error);
1160 return m_values[0].value();
1167 if (m_values.empty()) {
1168 const std::string
error(
"firstTime(): TimeSeriesProperty '" + name() +
"' is empty");
1170 throw std::runtime_error(
error);
1175 return m_values[0].time();
1183 if (m_values.empty()) {
1184 const std::string
error(
"lastValue(): TimeSeriesProperty '" + name() +
"' is empty");
1186 throw std::runtime_error(
error);
1191 return m_values.rbegin()->value();
1205 return raw_stats.
mean;
1225 std::stringstream ins;
1226 for (
size_t i = 0; i < m_values.size(); i++) {
1228 ins << m_values[i].time().toSimpleString();
1229 ins <<
" " << m_values[i].value() <<
"\n";
1233 ins <<
"Error Error"
1248 std::vector<std::string> values;
1249 values.reserve(m_values.size());
1251 for (
size_t i = 0; i < m_values.size(); i++) {
1252 std::stringstream line;
1253 line << m_values[i].time().toSimpleString() <<
" " << m_values[i].value();
1254 values.emplace_back(line.str());
1273 std::map<DateAndTime, TYPE> asMap;
1274 if (m_values.empty())
1277 TYPE d = m_values[0].value();
1278 asMap[m_values[0].time()] =
d;
1280 for (
size_t i = 1; i < m_values.size(); i++) {
1281 if (m_values[i].
value() !=
d) {
1283 asMap[m_values[i].time()] = m_values[i].value();
1284 d = m_values[i].value();
1297 "Cannot extract TimeSeries from a "
1308 "Cannot extract TimeSeries from a "
1316template <
typename TYPE>
1319 "Cannot extract TimeSeries from "
1330 m_filterApplied =
false;
1341 if (realSize() > 1) {
1342 auto lastValue = m_values.back();
1344 m_values.emplace_back(lastValue);
1358template <
typename TYPE>
1360 const std::vector<TYPE> &new_values) {
1361 if (time_sec.size() != new_values.size()) {
1362 std::stringstream msg;
1363 msg <<
"TimeSeriesProperty \"" << name() <<
"\" create: mismatched size "
1364 <<
"for the time and values vectors.";
1365 throw std::invalid_argument(msg.str());
1369 std::vector<DateAndTime> times;
1370 DateAndTime::createVector(start_time, time_sec, times);
1372 this->
create(times, new_values);
1383template <
typename TYPE>
1385 const std::vector<TYPE> &new_values) {
1386 if (new_times.size() != new_values.size())
1387 throw std::invalid_argument(
"TimeSeriesProperty::create: mismatched size "
1388 "for the time and values vectors.");
1391 m_values.reserve(new_times.size());
1393 std::size_t num = new_values.size();
1396 for (std::size_t i = 0; i < num; i++) {
1398 m_values.emplace_back(newentry);
1406 m_size =
static_cast<int>(m_values.size());
1414 if (m_values.empty()) {
1415 const std::string
error(
"getSingleValue(): TimeSeriesProperty '" + name() +
"' is empty");
1417 throw std::runtime_error(
error);
1425 if (t < m_values[0].time()) {
1427 value = m_values[0].value();
1428 }
else if (t >= m_values.back().time()) {
1430 value = m_values.back().value();
1433 int index = this->findIndex(t);
1438 }
else if (
index ==
int(m_values.size())) {
1440 index =
static_cast<int>(m_values.size()) - 1;
1441 }
else if (
index >
int(m_values.size())) {
1442 std::stringstream errss;
1443 errss <<
"TimeSeriesProperty.findIndex() returns index (" <<
index <<
" ) > maximum defined value "
1445 throw std::logic_error(errss.str());
1459template <
typename TYPE>
1461 if (m_values.empty()) {
1462 const std::string
error(
"getSingleValue(): TimeSeriesProperty '" + name() +
"' is empty");
1464 throw std::runtime_error(
error);
1472 if (t < m_values[0].time()) {
1474 value = m_values[0].value();
1476 }
else if (t >= m_values.back().time()) {
1478 value = m_values.back().value();
1479 index = int(m_values.size()) - 1;
1482 index = this->findIndex(t);
1487 }
else if (
index ==
int(m_values.size())) {
1489 index =
static_cast<int>(m_values.size()) - 1;
1490 }
else if (
index >
int(m_values.size())) {
1491 std::stringstream errss;
1492 errss <<
"TimeSeriesProperty.findIndex() returns index (" <<
index <<
" ) > maximum defined value "
1494 throw std::logic_error(errss.str());
1515 if (m_values.empty()) {
1516 const std::string
error(
"nthInterval(): TimeSeriesProperty '" + name() +
"' is empty");
1518 throw std::runtime_error(
error);
1528 if (m_filter.empty()) {
1530 if (
n >=
static_cast<int>(m_values.size()) ||
1531 (
n ==
static_cast<int>(m_values.size()) - 1 && m_values.size() == 1)) {
1534 }
else if (
n ==
static_cast<int>(m_values.size()) - 1) {
1536 time_duration
d = m_values.rbegin()->time() - (m_values.rbegin() + 1)->time();
1537 DateAndTime endTime = m_values.rbegin()->time() +
d;
1542 DateAndTime startT = m_values[
static_cast<std::size_t
>(
n)].time();
1543 DateAndTime endT = m_values[
static_cast<std::size_t
>(
n) + 1].time();
1550 this->applyFilter();
1552 if (
static_cast<size_t>(
n) > m_filterQuickRef.back().second + 1) {
1555 }
else if (
static_cast<size_t>(
n) == m_filterQuickRef.back().second + 1) {
1557 auto ind_t1 =
static_cast<long>(m_filterQuickRef.back().first);
1558 long ind_t2 = ind_t1 - 1;
1559 Types::Core::DateAndTime t1 = (m_values.begin() + ind_t1)->time();
1560 Types::Core::DateAndTime t2 = (m_values.begin() + ind_t2)->time();
1561 time_duration
d = t1 - t2;
1562 Types::Core::DateAndTime t3 = t1 +
d;
1567 Types::Core::DateAndTime t0;
1568 Types::Core::DateAndTime tf;
1570 size_t refindex = this->findNthIndexFromQuickRef(
n);
1571 if (refindex + 3 >= m_filterQuickRef.size())
1572 throw std::logic_error(
"nthInterval: Haven't considered this case.");
1574 int diff =
n -
static_cast<int>(m_filterQuickRef[refindex].second);
1576 throw std::logic_error(
"nthInterval: diff cannot be less than 0.");
1579 Types::Core::DateAndTime ftime0 = m_filter[m_filterQuickRef[refindex].first].first;
1580 size_t iStartIndex = m_filterQuickRef[refindex + 1].first +
static_cast<size_t>(diff);
1581 Types::Core::DateAndTime ltime0 = m_values[iStartIndex].time();
1582 if (iStartIndex == 0 && ftime0 < ltime0) {
1585 }
else if (diff == 0) {
1595 size_t iStopIndex = iStartIndex + 1;
1596 if (iStopIndex >= m_values.size()) {
1598 Types::Core::DateAndTime ftimef = m_filter[m_filterQuickRef[refindex + 3].first].first;
1602 Types::Core::DateAndTime ltimef = m_values[iStopIndex].time();
1603 Types::Core::DateAndTime ftimef = m_filter[m_filterQuickRef[refindex + 3].first].first;
1604 if (ltimef < ftimef)
1628 if (m_values.empty()) {
1629 const std::string
error(
"nthValue(): TimeSeriesProperty '" + name() +
"' is empty");
1631 throw std::runtime_error(
error);
1637 if (m_filter.empty()) {
1639 if (
static_cast<size_t>(
n) < m_values.size()) {
1648 this->applyFilter();
1650 if (
static_cast<size_t>(
n) > m_filterQuickRef.back().second + 1) {
1652 size_t ilog = (m_filterQuickRef.rbegin() + 1)->first;
1653 value = m_values[ilog].value();
1656 Types::Core::DateAndTime t0;
1657 Types::Core::DateAndTime tf;
1659 size_t refindex = findNthIndexFromQuickRef(
n);
1660 if (refindex + 3 >= m_filterQuickRef.size()) {
1661 throw std::logic_error(
"Not consider out of boundary case here. ");
1664 m_filterQuickRef[refindex + 1].first + (
static_cast<std::size_t
>(
n) - m_filterQuickRef[refindex].second);
1665 value = m_values[ilog].value();
1680 if (m_values.empty()) {
1681 const std::string
error(
"nthTime(): TimeSeriesProperty '" + name() +
"' is empty");
1683 throw std::runtime_error(
error);
1686 if (n < 0 || n >=
static_cast<int>(m_values.size()))
1687 n =
static_cast<int>(m_values.size()) - 1;
1689 return m_values[
static_cast<size_t>(
n)].time();
1712 m_filterQuickRef.clear();
1714 if (filter->
size() == 0) {
1720 std::vector<Types::Core::DateAndTime> filtertimes = filter->
timesAsVector();
1722 assert(filtertimes.size() == filtervalues.size());
1723 const size_t nFilterTimes(filtertimes.size());
1724 m_filter.reserve(nFilterTimes + 1);
1726 bool lastIsTrue =
false;
1727 auto fend = filtertimes.end();
1728 auto vit = filtervalues.begin();
1729 for (
auto fit = filtertimes.begin(); fit != fend; ++fit) {
1730 if (*vit && !lastIsTrue) {
1732 m_filter.emplace_back(*fit,
true);
1734 }
else if (!(*vit) && lastIsTrue) {
1736 m_filter.emplace_back(*fit,
false);
1743 if (filtervalues.back()) {
1744 DateAndTime lastTime, nextLastT;
1745 if (m_values.back().time() > filtertimes.back()) {
1746 const size_t nvalues(m_values.size());
1748 lastTime = m_values.back().time();
1749 if (nvalues > 1 && m_values[nvalues - 2].time() > filtertimes.back())
1750 nextLastT = m_values[nvalues - 2].time();
1752 nextLastT = filtertimes.back();
1755 lastTime = filtertimes.back();
1756 const size_t nfilterValues(filtervalues.size());
1760 if (nfilterValues > 1 && m_values.back().time() > filtertimes[nfilterValues - 2])
1761 nextLastT = filtertimes[nfilterValues - 2];
1763 nextLastT = m_values.back().time();
1766 time_duration dtime = lastTime - nextLastT;
1767 m_filter.emplace_back(lastTime + dtime,
false);
1771 m_filterApplied =
false;
1780 m_filterQuickRef.clear();
1787 if (m_filter.empty()) {
1789 m_size = int(m_values.size());
1792 if (!m_filterApplied) {
1793 this->applyFilter();
1795 size_t nvalues = m_filterQuickRef.empty() ? m_values.size() : m_filterQuickRef.back().second;
1801 if (nvalues == m_values.size() + 1) {
1804 m_size =
static_cast<int>(nvalues);
1813 static const boost::regex re(
"^[0-9]{4}.[0-9]{2}.[0-9]{2}.[0-9]{2}.[0-9]{2}.[0-9]{2}");
1814 return boost::regex_search(str.begin(), str.end(), re);
1850 if (this->size() > 0) {
1851 const auto &intervals = this->getSplittingIntervals();
1852 const double duration_sec =
1853 std::accumulate(intervals.cbegin(), intervals.cend(), 0.,
1854 [](
double sum,
const auto &interval) { return sum + interval.duration(); });
1856 const auto time_weighted = this->timeAverageValueAndStdDev();
1860 out.
time_mean = std::numeric_limits<double>::quiet_NaN();
1862 out.
duration = std::numeric_limits<double>::quiet_NaN();
1877 size_t numremoved = 0;
1879 typename std::vector<TimeValueUnit<TYPE>>::iterator vit;
1880 vit = m_values.begin() + 1;
1881 Types::Core::DateAndTime prevtime = m_values.begin()->time();
1882 while (vit != m_values.end()) {
1883 Types::Core::DateAndTime currtime = vit->time();
1884 if (prevtime == currtime) {
1886 g_log.
debug() <<
"Entry @ Time = " << prevtime
1887 <<
"has duplicate time stamp. Remove entry with Value = " << (vit - 1)->
value() <<
"\n";
1890 vit = m_values.erase(vit - 1);
1896 prevtime = currtime;
1904 g_log.
warning() <<
"Log " << this->name() <<
" has " << numremoved <<
" entries removed due to duplicated time. "
1912 std::stringstream ss;
1913 for (
size_t i = 0; i < m_values.size(); ++i)
1914 ss << m_values[i].time() <<
"\t\t" << m_values[i].value() <<
"\n";
1930 bool sorted = is_sorted(m_values.begin(), m_values.end());
1938 g_log.
information(
"TimeSeriesProperty is not sorted. Sorting is operated on it. ");
1939 std::stable_sort(m_values.begin(), m_values.end());
1952 if (m_values.empty())
1959 if (t <= m_values[0].time()) {
1961 }
else if (t >= m_values.back().time()) {
1962 return (
int(m_values.size()));
1966 typename std::vector<TimeValueUnit<TYPE>>::const_iterator fid;
1968 fid = std::lower_bound(m_values.begin(), m_values.end(), temp);
1970 int newindex = int(fid - m_values.begin());
1971 if (fid->time() > t)
1983template <
typename TYPE>
1987 throw std::invalid_argument(
"Start Index cannot be less than 0");
1989 if (iend >=
static_cast<int>(m_values.size())) {
1990 throw std::invalid_argument(
"End Index cannot exceed the boundary");
1992 if (istart > iend) {
1993 throw std::invalid_argument(
"Start index cannot be greater than end index");
1997 if (t < (m_values.begin() + istart)->time()) {
2000 if (t > (m_values.begin() + iend)->time()) {
2001 return static_cast<int>(m_values.size());
2009 typename std::vector<TimeValueUnit<TYPE>>::iterator fid;
2010 fid = std::lower_bound((m_values.begin() + istart), (m_values.begin() + iend + 1), temppair);
2011 if (fid == m_values.end())
2012 throw std::runtime_error(
"Cannot find data");
2015 size_t index = size_t(fid - m_values.begin());
2030 if (m_filterApplied)
2032 if (m_filter.empty())
2035 m_filterQuickRef.clear();
2039 for (
size_t ift = 0; ift < m_filter.size(); ift++) {
2040 if (m_filter[ift].second) {
2044 istart = icurlog - 1;
2046 if (icurlog <
static_cast<int>(m_values.size()))
2047 icurlog = this->upperBound(m_filter[ift].first, istart,
static_cast<int>(m_values.size()) - 1);
2051 if (!m_filterQuickRef.empty())
2052 throw std::logic_error(
"return log index < 0 only occurs with the first log entry");
2054 m_filterQuickRef.emplace_back(ift, 0);
2055 m_filterQuickRef.emplace_back(0, 0);
2058 }
else if (icurlog >=
static_cast<int>(m_values.size())) {
2062 if (m_filterQuickRef.size() >= 4)
2063 ip = m_filterQuickRef.back().second;
2064 m_filterQuickRef.emplace_back(ift, ip);
2065 m_filterQuickRef.emplace_back(m_values.size() + 1, ip);
2068 size_t numintervals = 0;
2069 if (!m_filterQuickRef.empty()) {
2070 numintervals = m_filterQuickRef.back().second;
2072 if (m_filter[ift].first < m_values[
static_cast<std::size_t
>(icurlog)].time()) {
2074 throw std::logic_error(
"In this case, icurlog won't be zero! ");
2078 m_filterQuickRef.emplace_back(ift, numintervals);
2080 m_filterQuickRef.emplace_back(icurlog, numintervals);
2083 else if (m_filterQuickRef.size() % 4 == 2) {
2085 int ilastlog = icurlog;
2087 if (ilastlog <
static_cast<int>(m_values.size())) {
2089 icurlog = this->upperBound(m_filter[ift].first, icurlog,
static_cast<int>(m_values.size()) - 1);
2094 if (m_filterQuickRef.size() != 2)
2095 throw std::logic_error(
"False filter is before first log entry. "
2096 "QuickRef size must be 2.");
2097 m_filterQuickRef.pop_back();
2098 m_filterQuickRef.clear();
2102 throw std::logic_error(
"LastLog is not expected to be less than 0");
2104 int delta_numintervals = icurlog - ilastlog;
2105 if (delta_numintervals < 0)
2106 throw std::logic_error(
"Havn't considered delta numinterval can be less than 0.");
2108 size_t new_numintervals = m_filterQuickRef.back().second +
static_cast<size_t>(delta_numintervals);
2110 m_filterQuickRef.emplace_back(icurlog, new_numintervals);
2111 m_filterQuickRef.emplace_back(ift, new_numintervals);
2115 size_t new_numintervals = m_filterQuickRef.back().second + 1;
2116 m_filterQuickRef.emplace_back(icurlog - 1, new_numintervals);
2117 m_filterQuickRef.emplace_back(ift, new_numintervals);
2124 m_filterApplied =
true;
2141 throw std::invalid_argument(
"Unable to take into account negative index. ");
2142 else if (m_filterQuickRef.empty())
2143 throw std::runtime_error(
"Quick reference is not established. ");
2146 if (
static_cast<size_t>(
n) >= m_filterQuickRef.back().second) {
2148 index = m_filterQuickRef.size();
2151 for (
size_t i = 0; i < m_filterQuickRef.size(); i += 4) {
2152 if (
static_cast<size_t>(
n) >= m_filterQuickRef[i].second &&
2153 static_cast<size_t>(
n) < m_filterQuickRef[i + 3].second) {
2173 return "Could not set value: properties have different type.";
2177 m_propSortedFlag = prop->m_propSortedFlag;
2178 m_filter = prop->m_filter;
2179 m_filterQuickRef = prop->m_filterQuickRef;
2180 m_filterApplied = prop->m_filterApplied;
2187 std::vector<DateAndTime> times = this->timesAsVector();
2188 const DateAndTime &start = times.front();
2189 std::vector<double> timeSec(times.size());
2190 for (
size_t i = 0; i < times.size(); i++)
2191 timeSec[i] =
static_cast<double>(times[i].totalNanoseconds() - start.totalNanoseconds()) * 1e-9;
2192 file->writeData(
"time", timeSec);
2193 file->openData(
"time");
2194 file->putAttr(
"start", start.toISO8601String());
2201 std::vector<std::string> values = this->valuesAsVector();
2204 file->makeGroup(this->name(),
"NXlog",
true);
2207 auto max_it = std::max_element(values.begin(), values.end(),
2208 [](
const std::string &a,
const std::string &b) { return a.size() < b.size(); });
2210 size_t maxlen = max_it->size() + 1;
2212 std::vector<char> strs(values.size() * maxlen);
2214 for (
const auto &prop : values) {
2215 std::copy(prop.begin(), prop.end(), &strs[
index]);
2219 std::vector<int> dims{
static_cast<int>(values.size()),
static_cast<int>(maxlen)};
2220 file->makeData(
"value", ::NeXus::CHAR, dims,
true);
2221 file->putData(strs.data());
2223 saveTimeVector(file);
2234 std::vector<bool>
value = this->valuesAsVector();
2237 std::vector<uint8_t> asUint(
value.begin(),
value.end());
2238 file->makeGroup(this->name(),
"NXlog",
true);
2239 file->writeData(
"value", asUint);
2240 file->putAttr(
"boolean",
"1");
2241 saveTimeVector(file);
2246 auto value = this->valuesAsVector();
2249 file->makeGroup(this->name(),
"NXlog", 1);
2250 file->writeData(
"value",
value);
2251 file->openData(
"value");
2252 file->putAttr(
"units", this->units());
2254 saveTimeVector(file);
2274template <
typename TYPE>
2276 std::vector<double> &counts)
const {
2278 size_t nPoints = counts.size();
2282 auto t0 =
static_cast<double>(tMin.totalNanoseconds());
2283 auto t1 =
static_cast<double>(tMax.totalNanoseconds());
2285 throw std::invalid_argument(
"invalid arguments for histogramData; tMax<tMin");
2287 double dt = (t1 - t0) /
static_cast<double>(nPoints);
2289 for (
auto &ev : m_values) {
2290 auto time =
static_cast<double>(ev.time().totalNanoseconds());
2291 if (time < t0 || time >= t1)
2293 auto ind =
static_cast<size_t>((time - t0) / dt);
2294 counts[ind] +=
static_cast<double>(ev.value());
2300 const Types::Core::DateAndTime &tMax,
2301 std::vector<double> &counts)
const {
2305 throw std::runtime_error(
"histogramData is not implememnted for time series "
2306 "properties containing strings");
2316 if (m_filter.empty()) {
2317 return this->valuesAsVector();
2319 if (!m_filterApplied) {
2324 std::vector<TYPE> filteredValues;
2325 for (
const auto &
value : m_values) {
2326 if (isTimeFiltered(
value.time())) {
2327 filteredValues.emplace_back(
value.value());
2331 return filteredValues;
2354 auto filterEntry = std::lower_bound(m_filter.begin(), m_filter.end(), time,
2355 [](
const std::pair<Types::Core::DateAndTime, bool> &filterEntry,
2356 const Types::Core::DateAndTime &t) { return filterEntry.first <= t; });
2358 if (filterEntry == m_filter.begin()) {
2359 return !filterEntry->second;
2364 return filterEntry->second;
2374 std::vector<SplittingInterval> intervals;
2376 if (m_filter.empty()) {
2377 intervals.emplace_back(firstTime(), lastTime());
2381 if (!m_filterApplied) {
2386 const auto &localFilter = m_filter;
2388 const auto findNext = [&localFilter](
size_t &
index,
const bool val) {
2389 for (;
index < localFilter.size(); ++
index) {
2390 const auto &entry = localFilter[
index];
2391 if (entry.second == val) {
2395 return localFilter.back().first;
2400 while (
index < m_filter.size()) {
2401 DateAndTime start, stop;
2403 if (m_filter[0].second) {
2404 start = m_filter[0].first;
2406 start = firstTime();
2409 start = findNext(
index,
true);
2411 stop = findNext(
index,
false);
2412 if (stop != start) {
2413 intervals.emplace_back(start, stop);
2423#define INSTANTIATE(TYPE) template class TimeSeriesProperty<TYPE>;
2451 using namespace Kernel::Math;
2452 double singleValue = 0;
2453 switch (statisticType) {
2455 singleValue = propertyToFilter->
nthValue(0);
2458 singleValue = propertyToFilter->
nthValue(propertyToFilter->
size() - 1);
2473 throw std::invalid_argument(
"filterByStatistic - Unknown statistic type: " +
2474 boost::lexical_cast<std::string>(propertyToFilter));
const std::vector< double > & rhs
size_t m_size
Maximum size of the store.
double value
The value of the point.
std::map< DeltaEMode::Type, std::string > index
#define INSTANTIATE(TYPE)
#define UNUSED_ARG(x)
Function arguments are sometimes unused in certain implmentations but are required for documentation ...
Marks code as not implemented yet.
void debug(const std::string &msg)
Logs at debug level.
void warning(const std::string &msg)
Logs at warning level.
void information(const std::string &msg)
Logs at information level.
Base class for properties.
Represents a time interval.
Types::Core::DateAndTime begin() const
Beginning of the interval.
Types::Core::DateAndTime end() const
End of the interval.
A specialised Property class for holding a series of time-value pairs.
void splitByTime(std::vector< SplittingInterval > &splitter, std::vector< Property * > outputs, bool isPeriodic) const override
Split out a time series property by time intervals.
void replaceValues(const std::vector< Types::Core::DateAndTime > ×, const std::vector< TYPE > &values)
Replaces the time series with new values time series values.
TimeSeriesProperty< TYPE > * clone() const override
"Virtual" copy constructor
std::string getDefault() const override
Returns the default value.
std::map< Types::Core::DateAndTime, TYPE > valueAsMap() const
Return the time series as a C++ map<DateAndTime, TYPE>
size_t getMemorySize() const override
Return the memory used by the property, in bytes.
Json::Value valueAsJson() const override
std::string toString() const
Stringize the property.
int size() const override
Returns the number of values at UNIQUE time intervals in the time series.
TYPE minValue() const
Returns the minimum value found in the series.
TYPE maxValue() const
Returns the maximum value found in the series.
bool isTimeFiltered(const Types::Core::DateAndTime &time) const
Find if time lies in a filtered region.
std::vector< Mantid::Kernel::SplittingInterval > getSplittingIntervals() const
If filtering by log, get the time intervals for splitting.
std::vector< TYPE > valuesAsVector() const
Return the time series's values (unfiltered) as a vector<TYPE>
Types::Core::DateAndTime firstTime() const
Returns the first time regardless of filter.
void eliminateDuplicates()
Detects whether there are duplicated entries (of time) in property & eliminates them.
std::vector< double > timesAsVectorSeconds() const
Return the series as list of times, where the time is the number of seconds since the start.
std::multimap< Types::Core::DateAndTime, TYPE > valueAsMultiMap() const
Return the time series as a correct C++ multimap<DateAndTime, TYPE>.
TimeSeriesProperty & operator+=(Property const *right) override
Add the value of another property.
TimeSeriesProperty< TYPE > & merge(Property *rhs) override
Merge the given property with this one.
std::string value() const override
Get the time series property as a string of 'time value'.
void makeFilterByValue(std::vector< SplittingInterval > &split, double min, double max, double TimeTolerance=0.0, bool centre=false) const override
Fill a TimeSplitterType that will filter the events by matching.
bool isDefault() const override
Returns if the value is at the default.
void sortIfNecessary() const
Sort the property into increasing times, if not already sorted.
size_t findNthIndexFromQuickRef(int n) const
A new algorithm to find Nth index.
double mean() const
Returns the mean value found in the series.
int findIndex(Types::Core::DateAndTime t) const
Find the index of the entry of time t in the mP vector (sorted)
TYPE firstValue() const
Returns the first value regardless of filter.
virtual bool operator!=(const TimeSeriesProperty< TYPE > &right) const
Deep comparison (not equal).
void histogramData(const Types::Core::DateAndTime &tMin, const Types::Core::DateAndTime &tMax, std::vector< double > &counts) const
generate constant time-step histogram from the property values
void expandFilterToRange(std::vector< SplittingInterval > &split, double min, double max, const TimeInterval &range) const override
Make sure an existing filter covers the full time range given.
~TimeSeriesProperty() override
Virtual destructor.
std::string setValue(const std::string &) override
Set a property from a string.
std::vector< Types::Core::DateAndTime > filteredTimesAsVector() const
Get filtered times as a vector.
void addValue(const Types::Core::DateAndTime &time, const TYPE &value)
Add a value to the map using a DateAndTime object.
void applyFilter() const
Apply a filter.
std::pair< double, double > averageAndStdDevInFilter(const std::vector< SplittingInterval > &filter) const override
Calculate the time-weighted average and standard deviation of a property in a filtered range.
int m_size
The number of values (or time intervals) in the time series.
TimeSeriesProperty(const std::string &name)
Constructor.
void countSize() const
Updates size()
void clearFilter()
Restores the property to the unsorted state.
void filterByTimes(const std::vector< SplittingInterval > &splittervec)
Filter by a range of times.
void clear() override
Deletes the series of values in the property.
Types::Core::DateAndTime lastTime() const
Returns the last time.
void setName(const std::string &name)
Set name of property.
void saveProperty(::NeXus::File *file) override
TYPE lastValue() const
Returns the last value.
void filterByTime(const Types::Core::DateAndTime &start, const Types::Core::DateAndTime &stop) override
Filter out a run by time.
std::map< Types::Core::DateAndTime, TYPE > valueAsCorrectMap() const
Return the time series as a correct C++ map<DateAndTime, TYPE>.
std::string setValueFromJson(const Json::Value &) override
Set a property from a string.
std::unique_ptr< TimeSeriesProperty< double > > getDerivative() const
Return time series property, containing time derivative of current property.
std::vector< TimeValueUnit< TYPE > > m_values
Holds the time series data.
double timeAverageValue() const override
Returns the calculated time weighted average value.
void create(const Types::Core::DateAndTime &start_time, const std::vector< double > &time_sec, const std::vector< TYPE > &new_values)
Clears and creates a TimeSeriesProperty from these parameters.
virtual bool operator==(const TimeSeriesProperty< TYPE > &right) const
Deep comparison.
void clearOutdated() override
Deletes all but the 'last entry' in the property.
TYPE nthValue(int n) const
Returns n-th value of n-th interval in an incredibly inefficient way.
std::string setDataItem(const std::shared_ptr< DataItem > &) override
Set a property from a DataItem.
std::string isValid() const override
This doesn't check anything -we assume these are always valid.
std::vector< std::string > time_tValue() const
New method to return time series value pairs as std::vector<std::string>
void splitByTimeVector(const std::vector< Types::Core::DateAndTime > &splitter_time_vec, const std::vector< int > &target_vec, const std::vector< TimeSeriesProperty * > &outputs)
New split method.
std::vector< Types::Core::DateAndTime > timesAsVector() const override
Return the time series's times as a vector<DateAndTime>
TimeSeriesPropertyStatistics getStatistics() const
Return a TimeSeriesPropertyStatistics object.
void filterWith(const TimeSeriesProperty< bool > *filter)
Divide the property into allowed and disallowed time intervals according to filter.
Types::Core::DateAndTime nthTime(int n) const
Returns n-th time. NOTE: Complexity is order(n)! regardless of filter.
TYPE getSingleValue(const Types::Core::DateAndTime &t) const
Returns the value at a particular time.
void saveTimeVector(::NeXus::File *file)
Saves the time vector has time + start attribute.
int upperBound(Types::Core::DateAndTime t, int istart, int iend) const
Find the upper_bound of time t in container.
TimeInterval nthInterval(int n) const
Returns n-th valid time interval, in a very inefficient way.
double averageValueInFilter(const std::vector< SplittingInterval > &filter) const override
Calculate the time-weighted average of a property in a filtered range.
void addValues(const std::vector< Types::Core::DateAndTime > ×, const std::vector< TYPE > &values)
Adds vectors of values to the map.
int realSize() const override
Returns the real size of the time series property map:
static bool isTimeString(const std::string &str)
Check if str has the right time format.
Property * cloneWithTimeShift(const double timeShift) const override
"Virtual" copy constructor with a time shift in seconds
std::string setValueFromProperty(const Property &right) override
Set a value from another property.
std::vector< TYPE > filteredValuesAsVector() const
Get filtered values as a vector.
std::pair< double, double > timeAverageValueAndStdDev() const
Time weighted mean and standard deviation.
Class to hold unit value (DateAndTime, T)
MatrixWorkspace_sptr MANTID_API_DLL operator+=(const MatrixWorkspace_sptr &lhs, const MatrixWorkspace_sptr &rhs)
Adds two workspaces.
std::unique_ptr< T > create(const P &parent, const IndexArg &indexArg, const HistArg &histArg)
This is the create() method that all the other create() methods call.
void split(const int A, int &S, int &V)
Split a number into the sign and positive value.
Logger g_log("DateAndTime")
StatisticType
Maps a "statistic" to a number.
Statistics getStatistics(const std::vector< TYPE > &data, const unsigned int flags=StatOptions::AllStats)
Return a statistics object for the given data set.
std::vector< SplittingInterval > TimeSplitterType
A typedef for splitting events according their pulse time.
MANTID_KERNEL_DLL bool operator==(const Mantid::Kernel::Property &lhs, const Mantid::Kernel::Property &rhs)
Compares this to another property for equality.
double DLLExport filterByStatistic(TimeSeriesProperty< double > const *const propertyToFilter, Kernel::Math::StatisticType statisticType)
Function filtering double TimeSeriesProperties according to the requested statistics.
Helper class which provides the Collimation Length for SANS instruments.
constexpr double EMPTY_DBL() noexcept
Returns what we consider an "empty" double within a property.
Simple struct to store statistics.
double median
Median value.
double minimum
Minimum value.
double maximum
Maximum value.
double standard_deviation
standard_deviation of the values
Struct holding some useful statistics for a TimeSeriesProperty.
double time_standard_deviation
time weighted standard deviation
double standard_deviation
standard_deviation of the values
double minimum
Minimum value.
double median
Median value.
double time_mean
time weighted average
double duration
Duration in seconds.
double maximum
Maximum value.