Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F375505
History.hpp
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Size
17 KB
Referenced Files
None
Subscribers
None
History.hpp
View Options
//===-- rosa/agent/History.hpp ----------------------------------*- C++ -*-===//
//
// The RoSA Framework
//
// Distributed under the terms and conditions of the Boost Software License 1.0.
// See accompanying file LICENSE.
//
// If you did not receive a copy of the license file, see
// http://www.boost.org/LICENSE_1_0.txt.
//
//===----------------------------------------------------------------------===//
///
/// \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/Functionality.h"
#include
"rosa/config/config.h"
#include
"rosa/support/debug.hpp"
#include
"rosa/support/type_helper.hpp"
#include
<array>
#include
<vector>
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
LIFO
///< Last In First Out -- overwrite the latest entry with a new one
};
template
<
typename
T
,
HistoryPolicy
P
>
class
History
:
public
Functionality
{
public
:
History
(
void
)
noexcept
{}
/// Destroys \p this object.
virtual
~
History
(
void
)
=
default
;
/// Tells the retention policy applied to \p this object.
///
/// \return \c rosa::agent::History::P
static
constexpr
HistoryPolicy
policy
(
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 The max number of entries that may be recorded
virtual
size_t
maxLength
(
void
)
const
noexcept
=
0
;
/// 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
virtual
size_t
numberOfEntries
(
void
)
const
noexcept
=
0
;
/// Tells if \p this object has not recorded anything yet.
///
/// \return if \p this object has no entries recorded
bool
empty
(
void
)
const
noexcept
{
return
numberOfEntries
()
==
0
;
}
/// Tells if the history reached it's maximum length
///
/// \return if the history reached it's maximum length.
bool
full
(
void
)
const
noexcept
{
return
numberOfEntries
()
==
maxLength
();
}
/// 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
virtual
const
T
&
entry
(
const
size_t
I
=
0
)
const
noexcept
=
0
;
/// Removes all entries recorded in \p this object.
virtual
void
clear
()
noexcept
=
0
;
private
:
/// Pushes a new entry into the history.
///
/// \note The earliest entry gets overwritten if the history is full.
///
/// \param V value to push into the history
virtual
void
pushBack
(
const
T
&
V
)
noexcept
=
0
;
/// Replaces the most recent entry in the history.
///
/// \param V value to replace the most current value with
virtual
void
replaceFront
(
const
T
&
V
)
noexcept
=
0
;
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
::
LIFO
:
if
(
full
())
{
replaceFront
(
V
);
return
true
;
}
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 < lengthOfHistory()
/// \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
)
const
noexcept
{
STATIC_ASSERT
((
std
::
is_same
<
X
,
T
>::
value
),
"not default template arg"
);
ASSERT
(
0
<=
D
&&
D
<
maxLength
());
// 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 consecutive 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 < lengthOfHistory()
/// \endcode
template
<
typename
X
=
T
>
typename
std
::
enable_if
<
std
::
is_arithmetic
<
X
>::
value
,
size_t
>::
type
averageAbsDiff
(
const
size_t
D
)
const
noexcept
{
STATIC_ASSERT
((
std
::
is_same
<
X
,
T
>::
value
),
"not default template arg"
);
ASSERT
(
0
<=
D
&&
D
<
maxLength
());
// 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.
size_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
;
}
}
/// Tells the average of all entries recorded by \p this object
///
/// \tparam R type of the result
template
<
typename
R
>
R
average
()
const
noexcept
{
R
Average
=
0
;
for
(
size_t
I
=
0
;
I
<
numberOfEntries
();
I
++
)
{
Average
+=
entry
(
I
);
}
Average
/=
numberOfEntries
();
return
Average
;
}
};
/// Implements *history* by recording and storing values.
/// The length of the underlying std::array is static and must be set at
/// compile-time
///
/// \note Not thread-safe implementation, which should not be a problem as any
/// instance of \c rosa::agent::Functionality 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
StaticLengthHistory
:
public
History
<
T
,
P
>
,
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
:
using
History
<
T
,
P
>::
policy
;
using
History
<
T
,
P
>::
empty
;
using
History
<
T
,
P
>::
full
;
using
History
<
T
,
P
>::
addEntry
;
using
History
<
T
,
P
>::
trend
;
using
History
<
T
,
P
>::
averageAbsDiff
;
/// Creates an instances by initializing the indices for the circular buffer.
StaticLengthHistory
(
void
)
noexcept
:
Data
(
0
),
Space
(
0
)
{}
/// Destroys \p this object.
~
StaticLengthHistory
(
void
)
override
=
default
;
/// 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
size_t
maxLength
(
void
)
const
noexcept
override
{
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
size_t
numberOfEntries
(
void
)
const
noexcept
override
{
return
Data
<=
Space
?
Space
-
Data
:
max_size
()
-
Data
+
Space
;
}
/// 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
override
{
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
];
}
/// Removes all entries recorded in \p this object.
void
clear
()
noexcept
override
{
Data
=
0
;
Space
=
0
;
}
private
:
/// 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
override
{
// 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
();
}
}
/// Replaces the most recent entry in the history.
///
/// \param V value to replace the most current value with
void
replaceFront
(
const
T
&
V
)
noexcept
override
{
(
*
this
)[(
Space
-
1
)
%
max_size
()]
=
V
;
}
};
/// 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
>
StaticLengthHistory
<
T
,
N
,
P
>
&
operator
<<
(
StaticLengthHistory
<
T
,
N
,
P
>
&
H
,
const
T
&
V
)
noexcept
{
H
.
addEntry
(
V
);
return
H
;
}
/// Implements *DynamicLengthHistory* by recording and storing values.
///
/// \note Not thread-safe implementation, which should not be a problem as any
/// instance of \c rosa::agent::Functionality is an internal component of a
/// \c rosa::Agent, which is the basic unit of concurrency.
///
/// \tparam T type of values to store
/// \tparam P retention policy to follow when capacity is reached
template
<
typename
T
,
HistoryPolicy
P
>
class
DynamicLengthHistory
:
public
History
<
T
,
P
>
,
private
std
::
vector
<
T
>
{
// Bring into scope inherited functions that are used.
using
std
::
vector
<
T
>::
erase
;
using
std
::
vector
<
T
>::
begin
;
using
std
::
vector
<
T
>::
end
;
using
std
::
vector
<
T
>::
size
;
using
std
::
vector
<
T
>::
max_size
;
using
std
::
vector
<
T
>::
resize
;
using
std
::
vector
<
T
>::
push_back
;
using
std
::
vector
<
T
>::
pop_back
;
using
std
::
vector
<
T
>::
operator
[];
/// The current length of the DynamicLengthHistory.
size_t
Length
;
public
:
using
History
<
T
,
P
>::
policy
;
using
History
<
T
,
P
>::
empty
;
using
History
<
T
,
P
>::
full
;
using
History
<
T
,
P
>::
addEntry
;
using
History
<
T
,
P
>::
trend
;
using
History
<
T
,
P
>::
averageAbsDiff
;
/// Creates an instances by setting an initial length
DynamicLengthHistory
(
size_t
Length
)
noexcept
:
Length
(
Length
)
{
this
->
resize
(
Length
);
}
/// Destroys \p this object.
~
DynamicLengthHistory
(
void
)
override
=
default
;
/// 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::DynamicLengthHistory::N
size_t
maxLength
(
void
)
const
noexcept
override
{
return
Length
;
}
/// 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
size_t
numberOfEntries
(
void
)
const
noexcept
{
return
size
();
}
/// 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
override
{
ASSERT
(
0
<=
I
&&
I
<
numberOfEntries
());
// Boundary check.
return
this
->
operator
[](
size
()
-
I
-
1
);
}
/// Removes all entries recorded in \p this object.
void
clear
()
noexcept
override
{
erase
(
begin
(),
end
());
}
private
:
/// 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
override
{
if
(
full
())
{
erase
(
begin
());
}
push_back
(
V
);
}
/// Replaces the most recent entry in the history.
///
/// \param V value to replace the most current value with
void
replaceFront
(
const
T
&
V
)
noexcept
override
{
(
void
)
pop_back
();
push_back
(
V
);
}
public
:
/// Resizes the History length. If the new length is smaller than the number
/// of currently stored values, values are deleted according to the
/// HistoryPolicy.
///
/// @param NewLength The new Length of the History.
void
setLength
(
size_t
NewLength
)
noexcept
{
Length
=
NewLength
;
if
(
NewLength
<
numberOfEntries
())
{
switch
(
P
)
{
default
:
ROSA_CRITICAL
(
"unkown HistoryPolicy"
);
case
HistoryPolicy
::
LIFO
:
case
HistoryPolicy
::
SRWF
:
// Delete last numberOfEntries() - NewLength items from the back
erase
(
begin
()
+
NewLength
,
end
());
break
;
case
HistoryPolicy
::
FIFO
:
// Delete last numberOfEntries() - NewLength items from the front
erase
(
begin
(),
begin
()
+
(
numberOfEntries
()
-
NewLength
));
break
;
}
}
this
->
resize
(
Length
);
}
};
/// Adds a new entry to a \c rosa::agent::DynamicLengthHistory instance.
///
/// \note The result of \c rosa::agent::DynamicLengthHistory::addEntry is
/// ignored.
///
/// \tparam T type of values stored in \p H
/// \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
,
HistoryPolicy
P
>
DynamicLengthHistory
<
T
,
P
>
&
operator
<<
(
DynamicLengthHistory
<
T
,
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, 2:15 AM (1 d, 4 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
150329
Default Alt Text
History.hpp (17 KB)
Attached To
Mode
R20 SoC_Rosa_repo
Attached
Detach File
Event Timeline
Log In to Comment