19#ifdef __USE_DATABASE__
20#include "QwParitySchema.h"
28#ifdef __USE_DATABASE__
30SQLPP_ALIAS_PROVIDER(run_first);
31SQLPP_ALIAS_PROVIDER(run_last);
34SQLPP_CREATE_NAME_TAG(run_first);
35SQLPP_CREATE_NAME_TAG(run_last);
59 "Blindable",
"BlindableFail"};
70 options.
AddOptions(
"Blinder")(
"blinder.force-target-blindable", po::value<bool>()->default_bool_value(
false),
71 "Forces the blinder to interpret the target as being in a blindable position");
72 options.
AddOptions(
"Blinder")(
"blinder.force-target-out", po::value<bool>()->default_bool_value(
false),
73 "Forces the blinder to interpret the target position as target-out");
74 options.
AddOptions(
"Blinder")(
"blinder.beam-current-threshold", po::value<double>()->default_value(2.5),
75 "Beam current in microamps below which data will not be blinded");
124 QwError <<
"Invalid CREX target index for blindable events! Exiting!"
131 fSeed.Prepend(TString(
"[Using CREX positions!] "));
134 std::string strategy;
136 std::transform(strategy.begin(), strategy.end(), strategy.begin(), ::tolower);
145 std::string spin_direction;
147 std::transform(spin_direction.begin(), spin_direction.end(), spin_direction.begin(), ::tolower);
148 if (spin_direction ==
"spin-forward"){
149 QwWarning <<
"QwBlinder::QwBlinder: Spin direction forced with force-spin-direction==spin-forward" <<
QwLog::endl;
153 }
else if (spin_direction ==
"spin-backward"){
154 QwWarning <<
"QwBlinder::QwBlinder: Spin direction forced with force-spin-direction==spin-backward" <<
QwLog::endl;
158 }
else if (spin_direction ==
"spin-vertical"){
159 QwWarning <<
"QwBlinder::QwBlinder: Spin direction forced with force-spin-direction==spin-vertical" <<
QwLog::endl;
163 }
else if (spin_direction ==
"spin-horizontal"){
164 QwWarning <<
"QwBlinder::QwBlinder: Spin direction forced with force-spin-direction==spin-horizontal" <<
QwLog::endl;
169 QwError <<
"QwBlinder::QwBlinder: Unrecognized option given to force-spin-direction in blinder.map; "
170 <<
"force-spin-direction==" << spin_direction <<
". Exit and correct the file."
176 std::string target_type;
178 std::transform(target_type.begin(), target_type.end(), target_type.begin(), ::tolower);
179 if (target_type ==
"target-blindable"){
180 QwWarning <<
"QwBlinder::QwBlinder: Target position forced with force-target-type==target-blindable" <<
QwLog::endl;
183 }
else if (target_type ==
"target-out"){
184 QwWarning <<
"QwBlinder::QwBlinder: Target position forced with force-target-type==target-out" <<
QwLog::endl;
188 QwError <<
"QwBlinder::QwBlinder: Unrecognized option given to force-target-type in blinder.map; "
189 <<
"force-target-type==" << target_type <<
". Exit and correct the file."
232 if (options.
GetValue<
bool>(
"blinder.force-target-out")
233 && options.
GetValue<
bool>(
"blinder.force-target-blindable")){
234 QwError <<
"QwBlinder::ProcessOptions: Both blinder.force-target-blindable and blinder.force-target-out are set. "
235 <<
"Only one can be in force at one time. Exit and choose one option."
238 }
else if (options.
GetValue<
bool>(
"blinder.force-target-blindable")){
239 QwWarning <<
"QwBlinder::ProcessOptions: Target position forced with blinder.force-target-blindable." <<
QwLog::endl;
242 }
else if (options.
GetValue<
bool>(
"blinder.force-target-out")){
243 QwWarning <<
"QwBlinder::ProcessOptions: Target position forced with blinder.force-target-out." <<
QwLog::endl;
251#ifdef __USE_DATABASE__
307 Bool_t tmp_beam = kFALSE;
309 if (q_targ !=
nullptr) {
335 Double_t tgt_pos = epics.
GetDataValue(
"pcrex90BDSPOS.VAL");
336 QwDebug <<
"Target parameters used by the blinder: "
338 <<
"QWTGTPOS=" << tgt_pos <<
" "
344 (tgt_pos>14.5e6 && tgt_pos<18.0e6) ){
349 ( (tgt_pos>-1.0e3 && tgt_pos<14.5e6)
350 || (tgt_pos>18.0e6 && tgt_pos<61.e6) ) ){
358 (tgt_pos>11.5e6 && tgt_pos<14.5e6) ){
363 ( (tgt_pos>-1.0e3 && tgt_pos<11.5e6)
364 || (tgt_pos>14.5e6 && tgt_pos<61.e6) ) ){
372 ( (tgt_pos > 3e6 && tgt_pos < 6.9e6)
373 || (tgt_pos > 7.3e6 && tgt_pos < 7.7e6))
375 ||( (tgt_pos>30.e6 && tgt_pos<69e6)
376 || (tgt_pos>73e6 && tgt_pos<78e6)
383 ((tgt_pos > -1e3 && tgt_pos < 3e6)
384 || (tgt_pos > 6.8e6 && tgt_pos < 7.2e6)
385 || (tgt_pos > 7.7e6 && tgt_pos < 10e6))
387 || ( (tgt_pos>17e6 && tgt_pos<30e6)
388 || (tgt_pos>69e6 && tgt_pos<73e6)
389 || (tgt_pos>78e6 && tgt_pos<90e6)
397 QwWarning <<
"Target parameters used by the blinder are indeterminate: "
399 <<
"QWTGTPOS=" << tgt_pos <<
" "
433#ifdef __USE_DATABASE__
440 fSeed =
"Default seed, No database specified";
443 if (! db->AllowsReadAccess()){
444 QwDebug <<
"QwBlinder::ReadSeed(): Database access is turned off. Don't update the blinder." <<
QwLog::endl;
450 auto c = db->GetScopedConnection();
452 QwError <<
"QwBlinder::ReadSeed db->GetRunNumber() returns "
456 QwParitySchema::seeds seeds{};
457 QwParitySchema::run first_run{};
458 QwParitySchema::run last_run{};
461 auto rf_alias = first_run.as(run_first);
462 auto rl_alias = last_run.as(run_last);
463 auto query = sqlpp::select(seeds.seed_id, seeds.seed)
465 .join(rf_alias).on(seeds.first_run_id == rf_alias.run_id)
466 .join(rl_alias).on(seeds.last_run_id == rl_alias.run_id))
467 .where(rf_alias.run_number <= db->GetRunNumber()
468 and rl_alias.run_number >= db->GetRunNumber()
469 and seeds.seed_id > 2);
471 QwError <<
"QwBlinder::ReadSeed executing sqlpp11 query for run number "
474 auto results = c->QuerySelect(query);
475 size_t result_count = c->CountResults(results);
476 if (result_count == 1) {
478 c->ForFirstResult(results, [
this](
const auto& row) {
481 if (!is_null(row.seed)) {
482 fSeed = row.seed.value();
484 QwError <<
"QwBlinder::ReadSeed(): Seed value came back NULL from the database." <<
QwLog::endl;
490 std::cout <<
"QwBlinder::ReadSeed(): Successfully read "
491 << Form(
"the fSeed with ID %d from the database.",
fSeedID)
498 fSeed = Form(
"ERROR: There should be one and only one seed_id for each seed, but this had %zu.",
500 std::cerr <<
"QwBlinder::ReadSeed(): "<<
fSeed<<std::endl;
502 }
catch (
const std::exception& er) {
505 fSeed =
"ERROR: Unable to open the connection to the database.";
506 QwError <<
"QwBlinder::ReadSeed(): Unable to open connection to database: " << er.what() <<
QwLog::endl;
525 static const Char_t alphanum[] =
528 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
529 "abcdefghijklmnopqrstuvwxyz";
531 Int_t strLen =
sizeof(alphanum) - 1;
532 const size_t length = 20;
533 Char_t randomchar[length];
538 for (
size_t i = 0; i < length; ++i) {
539 randomchar[i] = alphanum[rand() % strLen];
542 TString frandomSeed(randomchar, length);
558#ifdef __USE_DATABASE__
565 fSeed =
"Default seed, No database specified";
571 auto c = db->GetScopedConnection();
574 QwParitySchema::seeds seeds{};
577 auto query = sqlpp::select(sqlpp::all_of(seeds))
579 .where(seeds.seed_id == seed_id);
580 auto results = db->QuerySelect(query);
583 UInt_t found_seed_id = 0;
584 TString found_seed =
"";
585 size_t result_count = db->CountResults(results);
587 db->ForFirstResult(results, [&](
const auto& row) {
588 found_seed_id = row.seed_id;
589 if (!is_null(row.seed)) {
590 found_seed = row.seed.value();
592 QwError <<
"QwBlinder::ReadSeed(): Seed value came back NULL from the database." <<
QwLog::endl;
599 if (result_count == 1) {
602 std::cout <<
"QwBlinder::ReadSeed(): Successfully read "
603 << Form(
"the fSeed with ID %d from the database.",
fSeedID)
607 fSeed = Form(
"ERROR: There should be one and only one seed_id for each seed, but this had %zu.",
609 std::cerr <<
"QwBlinder::ReadSeed(): " <<
fSeed << std::endl;
613 auto query = sqlpp::select(sqlpp::all_of(seeds))
615 .order_by(seeds.seed_id.desc())
617 .where(sqlpp::value(
true));
618 auto results = db->QuerySelect(query);
621 UInt_t found_seed_id2 = 0;
622 TString found_seed2 =
"";
624 size_t result_count2 = db->CountResults(results);
626 db->ForFirstResult(results, [&](
const auto& row) {
627 found_seed_id2 = row.seed_id;
628 if (!is_null(row.seed)) {
629 found_seed2 = row.seed.value();
631 QwError <<
"QwBlinder::ReadSeed(): Seed value came back NULL from the database." <<
QwLog::endl;
638 if (result_count2 == 1) {
641 std::cout <<
"QwBlinder::ReadSeed(): Successfully read "
642 << Form(
"the fSeed with ID %d from the database.",
fSeedID)
646 fSeed = Form(
"ERROR: There should be one and only one seed_id for each seed, but this had %zu.",
648 std::cerr <<
"QwBlinder::ReadSeed(): " <<
fSeed << std::endl;
651 }
catch (
const std::exception& er) {
655 fSeed =
"ERROR: Unable to open the connection to the database.";
656 QwError <<
"QwBlinder::ReadSeed(): Unable to open connection to database: " << er.what() <<
QwLog::endl;
685 if ((finalseed & 0x80000000) == 0x80000000) {
686 newtempout = -1.0 * (finalseed & 0x7FFFFFFF);
688 newtempout = 1.0 * (finalseed & 0x7FFFFFFF);
698 Double_t tmp1 = maximum_asymmetry_sqrt * (newtempout / Int_t(0x7FFFFFFF));
725#if __cplusplus < 202002L
734 hex_string.Form(
"%.16llx%.16llx", factor_bits, offset_bits);
737 for (
size_t i = 0; i <
fDigest.size(); i++)
741#ifdef __USE_DATABASE__
746 QwError <<
"QwBlinder::WriteFinalValuesToDB(): "
747 <<
"Blinded test values have changed; may be a problem in the analysis!!!"
771 for (
int i = 0; i < n; i++) {
774 for (Int_t j = 0; j < 16; j++) {
775 finalseed &= 0x7FFFFFFF;
776 if ((finalseed & 0x800000) == 0x800000) {
777 finalseed = ((finalseed ^ 0x00000d) << 1) | 0x1;
786 Int_t mask = 0xFFFFFF;
787 Double_t tempval = (1.0 - 2.0*(finalseed&mask)/mask) / (1.0e6);
798 <<
" test values have been calculated successfully." <<
QwLog::endl;
808 std::vector<UInt_t> choppedwords;
812 for (Int_t i = 0; i < barestring.Length(); i++)
814 if (i % 4 == 0) tmpword = 0;
815 tmpword |= (char(barestring[i]))<<(24-8*(i%4));
816 if (i%4 == 3 || i == barestring.Length()-1)
818 choppedwords.push_back(tmpword);
819 finalseed ^= (tmpword);
822 for (Int_t i=0; i<64; i++)
824 finalseed &= 0x7FFFFFFF;
825 if ((finalseed & 0x800000) == 0x800000)
827 finalseed = ((finalseed^0x00000d)<<1) | 0x1;
834 if ((finalseed&0x80000000) == 0x80000000)
836 finalseed = -1 * (finalseed&0x7FFFFFFF);
840 finalseed = (finalseed&0x7FFFFFFF);
860 longmask|=0xFFFFFFFF;
864 for (Int_t i=0; i<barestring.Length(); i++)
866 if ( ((barestring[i])&0xC0)!=0 && ((barestring[i])&0xC0)!=0xC0)
868 finalseed = ((finalseed&longmask)<<1) | (((barestring[i])&0x40)>>6);
871 if ( ((barestring[i])&0x30)!=0 && ((barestring[i])&0x30)!=0x30)
873 finalseed = ((finalseed&longmask)<<1) | (((barestring[i])&0x10)>>4);
876 if ( ((barestring[i])&0xC)!=0 && ((barestring[i])&0xC)!=0xC)
878 finalseed = ((finalseed&longmask)<<1) | (((barestring[i])&0x4)>>2);
881 if ( ((barestring[i])&0x3)!=0 && ((barestring[i])&0x3)!=0x3)
883 finalseed = ((finalseed&longmask)<<1) | ((barestring[i])&0x1);
887 for (Int_t i=0; i<(192-bitcount); i++)
889 if ((finalseed & 0x800000) == 0x800000)
891 finalseed = ((finalseed^0x00000d)<<1) | 0x1;
898 tempout = (finalseed&0xFFFFFFFF) ^ ((finalseed>>32)&0xFFFFFFFF);
899 if ((tempout&0x80000000) == 0x80000000)
901 tempout = -1 * (tempout&0x7FFFFFFF);
905 tempout = 1 * (tempout&0x7FFFFFFF);
922 for (
size_t i = 0; i < digest.size(); i++)
929 temp |= (digest[i])<<(24-(j*8));
930 if ( (j==3) || (i==(digest.size()-1)))
936 if ((tempout & 0x80000000) == 0x80000000) {
937 tempout = -1 * (tempout & 0x7FFFFFFF);
939 tempout = (tempout & 0x7FFFFFFF);
958#ifdef __USE_DATABASE__
964 QwParitySchema::analysis analysis{};
965 auto update_query = sqlpp::update(analysis)
966 .set(analysis.seed_id =
fSeedID,
968 .where(analysis.analysis_id == db->GetAnalysisID());
972 auto c = db->GetScopedConnection();
973 db->QueryExecute(update_query);
990 QwParitySchema::bf_test bf_test{};
998 auto insert_query = sqlpp::insert_into(bf_test)
999 .set(bf_test.analysis_id = db->GetAnalysisID(),
1000 bf_test.test_number =
static_cast<int>(i),
1004 auto c = db->GetScopedConnection();
1005 db->QueryExecute(insert_query);
1019 Bool_t status = kTRUE;
1023 double epsilon = std::numeric_limits<double>::epsilon();
1031 double test2 = checkval;
1032 if ((test1 - test2) <= -epsilon || (test1 - test2) >= epsilon) {
1033 QwError <<
"QwBlinder::CheckTestValues(): Unblinded test value "
1035 <<
" does not agree with original test value, "
1036 <<
"with a difference of "
1038 <<
" (epsilon==" << epsilon <<
")"
1046 if ((test1 - test2) <= -epsilon || (test1 - test2) >= epsilon) {
1047 QwError <<
"QwBlinder::CheckTestValues(): Unblinded test value "
1049 <<
" does not agree with original test value, "
1050 <<
"with a difference of "
1068 const UInt_t length = 16;
1069 UChar_t value[length];
1070 for (UInt_t i = 0; i < length; i++)
1075 md5.Update((UChar_t*) input.Data(), input.Length());
1079 std::vector<UChar_t> output;
1080 for (UInt_t i = 0; i < length; i++)
1081 output.push_back(value[i]);
1092 Int_t total_count = 0;
1096 if (total_count<=0)
return;
1106 QwMessage <<
"The blinding parameters checksum for seed ID "
1111 double epsilon = std::numeric_limits<double>::epsilon();
1115 << std::setw(16) <<
"Original value"
1116 << std::setw(16) <<
"Blinded value"
1117 << std::setw(22) <<
"Orig.-Unblind value"
1126 << std::setw(16) << Form(
" [CENSORED]")
1128 << std::setw(22) << diff
1173#ifdef __USE_DATABASE__
1174void QwBlinder::FillDB(QwParityDB *db, TString datatype)
1176 QwDebug <<
" --------------------------------------------------------------- " <<
QwLog::endl;
1178 QwDebug <<
" --------------------------------------------------------------- " <<
QwLog::endl;
1181 UInt_t analysis_id = db->GetAnalysisID();
1185 QwError <<
"QwBlinder::FillDB(): "
1186 <<
"Blinded test values have changed; "
1187 <<
"may be a problem in the analysis!!!"
1193 auto c = db->GetScopedConnection();
1197 QwParitySchema::analysis analysis{};
1199 auto update_query = sqlpp::update(analysis)
1200 .set(analysis.seed_id =
fSeedID,
1202 .where(analysis.analysis_id == analysis_id);
1205 db->QueryExecute(update_query);
1207 }
catch (
const std::exception& err) {
1214 QwParitySchema::bf_test bf_test{};
1216 auto insert_query = sqlpp::insert_into(bf_test)
1217 .set(bf_test.analysis_id = analysis_id,
1218 bf_test.test_number =
static_cast<int>(i),
1221 db->QueryExecute(insert_query);
1225 QwMessage <<
"QwBlinder::FillDB(): No bf_test entries to write."
1228 }
catch (
const std::exception& err) {
1233void QwBlinder::FillErrDB(QwParityDB *db, TString datatype)
1235 QwDebug <<
" --------------------------------------------------------------- " <<
QwLog::endl;
1237 QwDebug <<
" --------------------------------------------------------------- " <<
QwLog::endl;
1239 UInt_t analysis_id = db->GetAnalysisID();
1240 QwParitySchema::general_errors general_errors{};
1242 auto c = db->GetScopedConnection();
1248 auto insert_query = sqlpp::insert_into(general_errors)
1249 .set(general_errors.analysis_id = analysis_id,
1250 general_errors.error_code_id = index + 20,
1253 db->QueryExecute(insert_query);
1256 QwDebug <<
"Inserted blinder error counters for analysis " << analysis_id <<
QwLog::endl;
1258 }
catch (
const std::exception& err) {
1271 QwMessage <<
"QwBlinder: First set target blindability to "
1282 QwMessage <<
"QwBlinder: First set Wien state to "
1292 QwMessage <<
"QwBlinder: First set IHWP state to "
1305 QwDebug <<
"QwBlinder::CheckBlindability: The target blindability is not determined. "
1311 QwDebug <<
"QwBlinder::CheckBlindability: The target blindability has changed. "
1355 QwError <<
"QwBlinder::CheckBlindability: The pattern blindability is unclear. "
A logfile class, based on an identical class in the Hermes analyzer.
#define QwVerbose
Predefined log drain for verbose messages.
#define QwError
Predefined log drain for errors.
#define QwWarning
Predefined log drain for warnings.
#define QwMessage
Predefined log drain for regular messages.
#define QwDebug
Predefined log drain for debugging output.
Parameter file parsing and management.
Decoding and management for VQWK ADC channels (6x32-bit datawords)
std::string WienModeName(EQwWienMode type)
EQwWienMode
Double Wien configuration.
A class for blinding data, adapted from G0 blinder class.
unsigned long long ULong64_t
EQwBlinderErrorCounterIndices
Blinder event counter indices.
@ kBlinderCount_ChangedIHWP
@ kBlinderCount_UndefinedIHWP
@ kBlinderCount_NumCounters
@ kBlinderCount_Blindable
@ kBlinderCount_UnknownTarget
@ kBlinderCount_OtherFailure
@ kBlinderCount_NonBlindable
@ kBlinderCount_ChangedWien
@ kBlinderCount_ChangedTarget
@ kBlinderCount_Transverse
@ kBlinderCount_UndefinedWien
const VQwHardwareChannel * RequestExternalPointer(const TString &name) const
Retrieve a direct pointer to an external variable Searches for the named variable in external subsyst...
EPICS slow controls data management.
Int_t DetermineIHWPPolarity() const
EQwWienMode DetermineWienMode() const
Double_t GetDataValue(const string &tag) const
static std::ostream & endl(std::ostream &)
End of the line.
Command-line and configuration file options processor.
T GetValue(const std::string &key)
Get a templated value.
po::options_description_easy_init AddOptions(const std::string &blockname="Specialized options")
Add an option to a named block or create new block.
Configuration file parser with flexible tokenization and search capabilities.
Bool_t FileHasVariablePair(const std::string &separatorchars, const std::string &varname, std::string &varvalue)
Abstract base for concrete hardware channels implementing dual-operator pattern.
Double_t GetValue() const
QwBlinder(const EQwBlindingStrategy blinding_strategy=kAdditive)
Default constructor with optional database.
void InitTestValues(const int n)
Initializes fBlindingFactor from fSeed.
EQwBlinderStatus
Status of the blinding process or intermediate steps of the process.
Int_t ReadRandomSeed()
Read the seed string generated utilizing a random number generator.
std::vector< UChar_t > GenerateDigest(const TString &input) const
Writes fTestNumber and fBlindTestValue to DB for this analysis ID.
static const TString fStatusName[4]
EQwBlinderStatus fTargetBlindability_firstread
Indicates the first value received of the blindability of the target.
void WriteTestValues(QwParityDB *db)
Writes fSeedID and fBFChecksum to DB for this analysis ID.
Bool_t fTargetPositionForced
std::vector< double > fBlindTestValues
Vector of test values, original.
std::vector< double > fTestValues
Checksum in ASCII hex.
void UnBlindValue(Double_t &value) const
Asymmetry unblinding.
void PrintFinalValues(Int_t kVerbosity=1)
std::vector< UChar_t > fDigest
Default seed.
Double_t fBlindingOffset_Base
The term to be added to detector asymmetries.
static const TString kDefaultSeed
Seed string (seeds.seed)
std::vector< Int_t > fPatternCounters
Counts the number of events in each failure mode.
Int_t UsePseudorandom(const TString &barestring)
Double_t fBlindingFactor
The term to be added to detector asymmetries, before polarity correction.
void Update()
Update the status using a random number.
std::string fChecksum
Checksum in raw hex.
Double_t fBlindingOffset
Blinding strategy.
Double_t fBeamCurrentThreshold
std::vector< Int_t > fPairCounters
Counts the number of helicity pairs in each failure mode.
Double_t fMaximumBlindingAsymmetry
Default maximum blinding factor (in fraction from identity)
static const Double_t kDefaultMaximumBlindingAsymmetry
The factor to be multiplied to detector asymmetries.
static void DefineOptions(QwOptions &options)
void WriteChecksum(QwParityDB *db)
void SetTargetBlindability(EQwBlinderStatus status)
Set the current target blindability status.
void SetWienState(EQwWienMode wienmode)
void BlindValue(Double_t &value) const
Asymmetry blinding.
TString fSeed
ID of seed used (seeds.seed_id)
EQwWienMode fWienMode_firstread
void SetIHWPPolarity(Int_t ihwppolarity)
Bool_t fSpinDirectionForced
void WriteFinalValuesToDB(QwParityDB *db)
static const Double_t kDefaultMaximumBlindingFactor
Default maximum blinding asymmetry (in ppm)
EQwBlindingStrategy
Available blinding strategies.
@ kAdditiveMultiplicative
Int_t UseStringManip(const TString &barestring)
Returns an integer from a string using MD5.
void PrintCountersValues(std::vector< Int_t > fCounters, TString counter_type)
Int_t ReadSeed(QwParityDB *db, const UInt_t seed_id)
Reads the seed with specified id from the database object.
Int_t UseMD5(const TString &barestring)
Recomputes fBlindTestValue to check for memory errors.
Double_t fMaximumBlindingFactor
Maximum blinding asymmetry (in ppm)
EQwBlinderStatus CheckBlindability(std::vector< Int_t > &fCounters)
std::vector< double > fUnBlindTestValues
Vector of test values, after blinding.
void InitBlinders(const UInt_t seed_id)
Vector of test values, after unblinding.
EQwBlindingStrategy fBlindingStrategy
UInt_t fSeedID
Maximum blinding factor (in fraction from identity)
Int_t fIHWPPolarity_firstread
virtual ~QwBlinder()
Default destructor.
EQwBlinderStatus fTargetBlindability
void ProcessOptions(QwOptions &options)
Update the status with new external information.
Subsystem array container specialized for parity analysis with asymmetry calculations.