Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F375500
History.hpp
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Size
9 KB
Referenced Files
None
Subscribers
None
History.hpp
View Options
//===-- rosa/agent/History.hpp ----------------------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/agent/History.hpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017
///
/// \brief Definition of *history* *functionality*.
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_AGENT_HISTORY_HPP
#define ROSA_AGENT_HISTORY_HPP
#include
"rosa/agent/Module.h"
#include
"rosa/config/config.h"
#include
"rosa/support/debug.hpp"
#include
"rosa/support/type_helper.hpp"
#include
<array>
namespace
rosa
{
namespace
agent
{
/// Retention policies defining what a \c rosa::agent::History instance should
/// do when the number of recorded entries reached its capacity.
enum
class
HistoryPolicy
{
SRWF
,
///< Stop Recording When Full -- no new entry is recorded when full
FIFO
///< First In First Out -- overwrite the earliest entry with a new one
};
/// Implements *history* by recording and storing values.
///
/// \note Not thread-safe implementation, which should not be a problem as any
/// instance of \c rosa::agent::Module is an internal component of a
/// \c rosa::Agent, which is the basic unit of concurrency.
///
/// \tparam T type of values to store
/// \tparam N number of values to store at most
/// \tparam P retention policy to follow when capacity is reached
///
/// \invariant The size of the underlying \c std::array is `N + 1`:\code
/// max_size() == N + 1 && N == max_size() - 1
/// \endcode
template
<
typename
T
,
size_t
N
,
HistoryPolicy
P
>
class
History
:
public
Module
,
private
std
::
array
<
T
,
N
+
1
>
{
// Bring into scope inherited functions that are used.
using
std
::
array
<
T
,
N
+
1
>::
max_size
;
using
std
::
array
<
T
,
N
+
1
>::
operator
[];
/// The index of the first data element in the circular buffer.
size_t
Data
;
/// The index of the first empty slot in the circular buffer.
size_t
Space
;
public
:
/// Creates an instances by initializing the indices for the circular buffer.
History
(
void
)
noexcept
:
Data
(
0
),
Space
(
0
)
{}
/// Destroys \p this object.
~
History
(
void
)
=
default
;
/// Tells the retention policy applied to \p this object.
///
/// \return \c rosa::agent::History::P
static
constexpr
HistoryPolicy
policyOfHistory
(
void
)
noexcept
{
return
P
;
}
/// Tells how many entries may be recorded by \c this object.
///
/// \note The number of entries that are actually recorded may be smaller.
///
/// \return \c rosa::agent::History::N
static
constexpr
size_t
lengthOfHistory
(
void
)
noexcept
{
return
N
;
}
/// Tells how many entries are currently recorded by \p this object.
///
/// \return number of entries currently recorded by \p this object.
///
/// \post The returned value cannot be larger than the capacity of \p this
/// object:\code
/// 0 <= numberOfEntries() && numberOfEntries <= lengthOfHistory()
/// \endcode
inline
size_t
numberOfEntries
(
void
)
const
noexcept
{
return
Data
<=
Space
?
Space
-
Data
:
max_size
()
-
Data
+
Space
;
}
/// Tells if \p this object has not recorded anything yet.
///
/// \return if \p this object has no entries recorded
inline
bool
empty
(
void
)
const
noexcept
{
return
numberOfEntries
()
==
0
;
}
/// Gives a constant lvalue reference to an entry stored in \p this object.
///
/// \note The recorded entries are indexed starting from the latest one.
///
/// \param I the index at which the stored entry to take from
///
/// \pre \p I is a valid index:\code
/// 0 <= I && I <= numberOfEntries()
/// \endcode
const
T
&
entry
(
const
size_t
I
=
0
)
const
noexcept
{
ASSERT
(
0
<=
I
&&
I
<
numberOfEntries
());
// Boundary check.
// Position counted back from the last recorded entry.
typename
std
::
make_signed
<
const
size_t
>::
type
Pos
=
Space
-
(
1
+
I
);
// Actual index wrapped around to the end of the buffer if negative.
return
(
*
this
)[
Pos
>=
0
?
Pos
:
max_size
()
+
Pos
];
}
private
:
/// Tells if the circular buffer is full.
///
/// \return if the circular buffer is full.
inline
bool
full
(
void
)
const
noexcept
{
return
numberOfEntries
()
==
N
;
}
/// Pushes a new entry into the circular buffer.
///
/// \note The earliest entry gets overwritten if the buffer is full.
///
/// \param V value to push into the buffer
void
pushBack
(
const
T
&
V
)
noexcept
{
// Store value to the first empty slot and step Space index.
(
*
this
)[
Space
]
=
V
;
Space
=
(
Space
+
1
)
%
max_size
();
if
(
Data
==
Space
)
{
// Buffer was full, step Data index.
Data
=
(
Data
+
1
)
%
max_size
();
}
}
public
:
/// Adds a new entry to \p this object and tells if the operation was
/// successful.
///
/// \note Success of the operation depends on the actual policy.
///
/// \param V value to store
///
/// \return if \p V was successfully stored
bool
addEntry
(
const
T
&
V
)
noexcept
{
switch
(
P
)
{
default
:
ROSA_CRITICAL
(
"unkown HistoryPolicy"
);
case
HistoryPolicy
::
SRWF
:
if
(
full
())
{
return
false
;
}
// \note Fall through to FIFO which unconditionally pushes the new entry.
case
HistoryPolicy
::
FIFO
:
// FIFO and SRWF not full.
pushBack
(
V
);
return
true
;
}
}
/// Tells the trend set by the entries recorded by \p this object.
///
/// The number of steps to go back when calculating the trend is defined as
/// argument to the function.
///
/// \note The number of steps that can be made is limited by the number of
/// entries recorded by \p this object.
///
/// \note The function is made a template only to be able to use
/// \c std::enable_if.
///
/// \tparam X always use the default!
///
/// \param D number of steps to go back in *history*
///
/// \return trend set by analyzed entries
///
/// \pre Statically, \p this object stores signed arithmetic values:\code
/// std::is_arithmetic<T>::value && std::is_signed<T>::value
/// \endcode Dynamically, \p D is a valid number of steps to take:\code
/// 0 <= D && D < N
/// \endcode
template
<
typename
X
=
T
>
typename
std
::
enable_if
<
std
::
is_arithmetic
<
X
>::
value
&&
std
::
is_signed
<
X
>::
value
,
X
>::
type
trend
(
const
size_t
D
=
N
-
1
)
const
noexcept
{
STATIC_ASSERT
((
std
::
is_same
<
X
,
T
>::
value
),
"not default template arg"
);
ASSERT
(
0
<=
D
&&
D
<
N
);
// Boundary check.
if
(
numberOfEntries
()
<
2
||
D
<
1
)
{
// No entries for computing trend.
return
{};
// Zero element of \p T
}
else
{
// Here at least two entries.
// \c S is the number of steps that can be done.
const
size_t
S
=
std
::
min
(
numberOfEntries
()
-
1
,
D
);
size_t
I
=
S
;
// Compute trend with linear regression.
size_t
SumIndices
=
0
;
T
SumEntries
=
{};
T
SumSquareEntries
=
{};
T
SumProduct
=
{};
while
(
I
>
0
)
{
// \note Indexing for the regression starts in the past.
const
size_t
Index
=
S
-
I
;
const
T
Entry
=
entry
(
--
I
);
SumIndices
+=
Index
;
SumEntries
+=
Entry
;
SumSquareEntries
+=
Entry
*
Entry
;
SumProduct
+=
Entry
*
Index
;
}
return
(
SumProduct
*
S
-
SumEntries
*
SumIndices
)
/
(
SumSquareEntries
*
S
-
SumEntries
*
SumEntries
);
}
}
/// Tells the average absolute difference between consequtive entries recorded
/// by \p this object
/// The number of steps to go back when calculating the average is defined as
/// argument to the function.
///
/// \note The number of steps that can be made is limited by the number of
/// entries recorded by \p this object.
///
/// \note The function is made a template only to be able to use
/// \c std::enable_if.
///
/// \tparam X always use the default!
///
/// \param D number of steps to go back in *history*
///
/// \pre Statically, \p this object stores arithmetic values:\code
/// std::is_arithmetic<T>::value
/// \endcode Dynamically, \p D is a valid number of steps to take:\code
/// 0 <= D && D < N
/// \endcode
template
<
typename
X
=
T
>
typename
std
::
enable_if
<
std
::
is_arithmetic
<
X
>::
value
,
unsigned_t
<
X
>>::
type
averageAbsDiff
(
const
size_t
D
=
N
-
1
)
const
noexcept
{
STATIC_ASSERT
((
std
::
is_same
<
X
,
T
>::
value
),
"not default template arg"
);
ASSERT
(
0
<=
D
&&
D
<
N
);
// Boundary check.
if
(
numberOfEntries
()
<
2
||
D
<
1
)
{
// No difference to average.
return
{};
// Zero element of \p T
}
else
{
// Here at least two entries.
// \c S is the number of steps that can be done.
const
size_t
S
=
std
::
min
(
numberOfEntries
()
-
1
,
D
);
// Sum up differences as non-negative values only, hence using an
// unsigned variable for that.
unsigned_t
<
T
>
Diffs
=
{};
// Init to zero.
// Count down entry indices and sum up all the absolute differences.
size_t
I
=
S
;
T
Last
=
entry
(
I
);
while
(
I
>
0
)
{
T
Next
=
entry
(
--
I
);
Diffs
+=
Last
<
Next
?
Next
-
Last
:
Last
-
Next
;
Last
=
Next
;
}
// Return the average of the summed differences.
return
Diffs
/
S
;
}
}
};
/// Adds a new entry to a \c rosa::agent::History instance.
///
/// \note The result of \c rosa::agent::History::addEntry is ignored.
///
/// \tparam T type of values stored in \p H
/// \tparam N number of values \p H is able to store
/// \tparam P retention policy followed by \p H when capacity is reached
///
/// \param H to add a new entry to
/// \param V value to add to \p H
///
/// \return \p H after adding \p V to it
template
<
typename
T
,
size_t
N
,
HistoryPolicy
P
>
History
<
T
,
N
,
P
>
&
operator
<<
(
History
<
T
,
N
,
P
>
&
H
,
const
T
&
V
)
noexcept
{
H
.
addEntry
(
V
);
return
H
;
}
}
// End namespace agent
}
// End namespace rosa
#endif
// ROSA_AGENT_HISTORY_HPP
File Metadata
Details
Attached
Mime Type
text/x-c++
Expires
Sun, Jun 8, 1:51 AM (1 d, 3 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
150326
Default Alt Text
History.hpp (9 KB)
Attached To
Mode
R20 SoC_Rosa_repo
Attached
Detach File
Event Timeline
Log In to Comment