1 #ifndef _ValueRefs_h_
2 #define _ValueRefs_h_
3 
4 #include "ScriptingContext.h"
5 #include "ValueRef.h"
6 #include "Condition.h"
7 #include "Universe.h"
8 #include "../util/Export.h"
9 #include "../util/i18n.h"
10 #include "../util/Random.h"
11 #include "../util/CheckSums.h"
12 
13 #include <boost/algorithm/string/case_conv.hpp>
14 #include <boost/lexical_cast.hpp>
15 #include <boost/format.hpp>
16 #include <boost/serialization/nvp.hpp>
17 
18 #include <iterator>
19 #include <map>
20 #include <set>
21 
22 namespace CheckSums {
23     template <typename T>
CheckSumCombine(unsigned int & sum,const typename ValueRef::ValueRef<T> & c)24     void CheckSumCombine(unsigned int& sum, const typename ValueRef::ValueRef<T>& c)
25     {
26         TraceLogger() << "CheckSumCombine(ValueRef::ValueRef<T>): " << typeid(c).name();
27         sum += c.GetCheckSum();
28         sum %= CHECKSUM_MODULUS;
29     }
30 }
31 
32 class UniverseObject;
33 
34 namespace ValueRef {
35 /** the constant value leaf ValueRef class. */
36 template <typename T>
37 struct FO_COMMON_API Constant final : public ValueRef<T>
38 {
39     explicit Constant(T value);
40 
41     bool operator==(const ValueRef<T>& rhs) const override;
42     T Eval(const ScriptingContext& context) const override;
43 
RootCandidateInvariantfinal44     bool RootCandidateInvariant() const override
45     { return true; }
46 
LocalCandidateInvariantfinal47     bool LocalCandidateInvariant() const override
48     { return true; }
49 
TargetInvariantfinal50     bool TargetInvariant() const override
51     { return true; }
52 
SourceInvariantfinal53     bool SourceInvariant() const override
54     { return true; }
55 
ConstantExprfinal56     bool ConstantExpr() const override
57     { return true; }
58 
59     std::string Description() const override;
60     std::string Dump(unsigned short ntabs = 0) const override;
61     void SetTopLevelContent(const std::string& content_name) override;
62     T Value() const;
63     unsigned int GetCheckSum() const override;
64 
65 private:
66     T           m_value;
67     std::string m_top_level_content;    // in the special case that T is std::string and m_value is "CurrentContent", return this instead
68 
69     friend class boost::serialization::access;
70     template <typename Archive>
71     void serialize(Archive& ar, const unsigned int version);
72 };
73 
74 enum ReferenceType : int {
75     INVALID_REFERENCE_TYPE = -1,
76     NON_OBJECT_REFERENCE,               // ValueRef::Variable is not evalulated on any specific object
77     SOURCE_REFERENCE,                   // ValueRef::Variable is evaluated on the source object
78     EFFECT_TARGET_REFERENCE,            // ValueRef::Variable is evaluated on the target object of an effect while it is being executed
79     EFFECT_TARGET_VALUE_REFERENCE,      // ValueRef::Variable is evaluated on the target object value of an effect while it is being executed
80     CONDITION_LOCAL_CANDIDATE_REFERENCE,// ValueRef::Variable is evaluated on an object that is a candidate to be matched by a condition.  In a subcondition, this will reference the local candidate, and not the candidate of an enclosing condition.
81     CONDITION_ROOT_CANDIDATE_REFERENCE  // ValueRef::Variable is evaluated on an object that is a candidate to be matched by a condition.  In a subcondition, this will still reference the root candidate, and not the candidate of the local condition.
82 };
83 
84 /** The variable value ValueRef class.  The value returned by this node is
85   * taken from the gamestate, most often from the Source or Target objects. */
86 template <typename T>
87 struct FO_COMMON_API Variable : public ValueRef<T>
88 {
89     explicit Variable(ReferenceType ref_type, const std::string& property_name = "",
90                       bool return_immediate_value = false);
91     Variable(ReferenceType ref_type, const std::vector<std::string>& property_name,
92              bool return_immediate_value = false);
93     Variable(ReferenceType ref_type,
94              const boost::optional<std::string>& container_name,
95              const std::string& property_name,
96              bool return_immediate_value = false);
97 
98     bool operator==(const ValueRef<T>& rhs) const override;
99     T Eval(const ScriptingContext& context) const override;
100     bool RootCandidateInvariant() const override;
101     bool LocalCandidateInvariant() const override;
102     bool TargetInvariant() const override;
103     bool SourceInvariant() const override;
104     std::string Description() const override;
105     std::string Dump(unsigned short ntabs = 0) const override;
106     ReferenceType GetReferenceType() const;
107     const std::vector<std::string>& PropertyName() const;
108     bool ReturnImmediateValue() const;
109     unsigned int GetCheckSum() const override;
110 
111 protected:
112     ReferenceType               m_ref_type = INVALID_REFERENCE_TYPE;
113     std::vector<std::string>    m_property_name;
114     bool                        m_return_immediate_value = false;
115 
116 private:
117     friend class boost::serialization::access;
118     template <typename Archive>
119     void serialize(Archive& ar, const unsigned int version);
120 };
121 
122 /** The variable statistic class.   The value returned by this node is
123   * computed from the general gamestate; the value of the indicated
124   * \a property_name is computed for each object that matches
125   * \a sampling_condition and the statistic indicated by \a stat_type is
126   * calculated from them and returned. */
127 template <typename T>
128 struct FO_COMMON_API Statistic final : public Variable<T>
129 {
130     Statistic(std::unique_ptr<ValueRef<T>>&& value_ref,
131               StatisticType stat_type,
132               std::unique_ptr<Condition::Condition>&& sampling_condition);
133 
134     bool        operator==(const ValueRef<T>& rhs) const override;
135     T           Eval(const ScriptingContext& context) const override;
136     bool        RootCandidateInvariant() const override;
137     bool        LocalCandidateInvariant() const override;
138     bool        TargetInvariant() const override;
139     bool        SourceInvariant() const override;
140     std::string Description() const override;
141     std::string Dump(unsigned short ntabs = 0) const override;
142     void        SetTopLevelContent(const std::string& content_name) override;
143 
GetStatisticTypefinal144     StatisticType GetStatisticType() const
145     { return m_stat_type; }
146 
GetSamplingConditionfinal147     const Condition::Condition* GetSamplingCondition() const
148     { return m_sampling_condition.get(); }
149 
GetValueReffinal150     const ValueRef<T>* GetValueRef() const
151     { return m_value_ref.get(); }
152 
153     unsigned int GetCheckSum() const override;
154 
155 protected:
156     /** Gets the set of objects in the Universe that match the sampling condition. */
157     void GetConditionMatches(const ScriptingContext& context,
158                              Condition::ObjectSet& condition_targets,
159                              Condition::Condition* condition) const;
160 
161     /** Evaluates the property for the specified objects. */
162     void  GetObjectPropertyValues(const ScriptingContext& context,
163                                   const Condition::ObjectSet& objects,
164                                   std::map<std::shared_ptr<const UniverseObject>, T>& object_property_values) const;
165 
166     /** Computes the statistic from the specified set of property values. */
167     T ReduceData(const std::map<std::shared_ptr<const UniverseObject>, T>& object_property_values) const;
168 
169 private:
170     StatisticType                             m_stat_type;
171     std::unique_ptr<Condition::Condition> m_sampling_condition;
172     std::unique_ptr<ValueRef<T>>          m_value_ref;
173 
174     friend class boost::serialization::access;
175     template <typename Archive>
176     void serialize(Archive& ar, const unsigned int version);
177 };
178 
179 /** The complex variable ValueRef class. The value returned by this node
180   * is taken from the gamestate. */
181 template <typename T>
182 struct FO_COMMON_API ComplexVariable final : public Variable<T>
183 {
184     explicit ComplexVariable(const std::string& variable_name,
185                              std::unique_ptr<ValueRef<int>>&& int_ref1 = nullptr,
186                              std::unique_ptr<ValueRef<int>>&& int_ref2 = nullptr,
187                              std::unique_ptr<ValueRef<int>>&& int_ref3 = nullptr,
188                              std::unique_ptr<ValueRef<std::string>>&& string_ref1 = nullptr,
189                              std::unique_ptr<ValueRef<std::string>>&& string_ref2 = nullptr,
190                              bool return_immediate_value = false);
191 
192     bool operator==(const ValueRef<T>& rhs) const override;
193     T Eval(const ScriptingContext& context) const override;
194     bool RootCandidateInvariant() const override;
195     bool LocalCandidateInvariant() const override;
196     bool TargetInvariant() const override;
197     bool SourceInvariant() const override;
198     std::string Description() const override;
199     std::string Dump(unsigned short ntabs = 0) const override;
200     void SetTopLevelContent(const std::string& content_name) override;
201     const ValueRef<int>* IntRef1() const;
202     const ValueRef<int>* IntRef2() const;
203     const ValueRef<int>* IntRef3() const;
204     const ValueRef<std::string>* StringRef1() const;
205     const ValueRef<std::string>* StringRef2() const;
206     unsigned int GetCheckSum() const override;
207 
208 protected:
209     std::unique_ptr<ValueRef<int>> m_int_ref1;
210     std::unique_ptr<ValueRef<int>> m_int_ref2;
211     std::unique_ptr<ValueRef<int>> m_int_ref3;
212     std::unique_ptr<ValueRef<std::string>> m_string_ref1;
213     std::unique_ptr<ValueRef<std::string>> m_string_ref2;
214 
215 private:
216     friend class boost::serialization::access;
217     template <typename Archive>
218     void serialize(Archive& ar, const unsigned int version);
219 };
220 
221 /** The variable static_cast class.  The value returned by this node is taken
222   * from the ctor \a value_ref parameter's FromType value, static_cast to
223   * ToType. */
224 template <typename FromType, typename ToType>
225 struct FO_COMMON_API StaticCast final : public Variable<ToType>
226 {
227     template <typename T>
228     StaticCast(T&& value_ref,
229                typename std::enable_if<std::is_convertible<T, std::unique_ptr<Variable<FromType>>>::value>::type* = nullptr);
230 
231     template <typename T>
232     StaticCast(T&& value_ref,
233                typename std::enable_if<
234                std::is_convertible<T, std::unique_ptr<ValueRef<FromType>>>::value
235                && !std::is_convertible<T, std::unique_ptr<Variable<FromType>>>::value>::type* = nullptr);
236 
237     bool operator==(const ValueRef<ToType>& rhs) const override;
238     ToType Eval(const ScriptingContext& context) const override;
239     bool RootCandidateInvariant() const override;
240     bool LocalCandidateInvariant() const override;
241     bool TargetInvariant() const override;
242     bool SourceInvariant() const override;
243     std::string Description() const override;
244     std::string Dump(unsigned short ntabs = 0) const override;
245     void SetTopLevelContent(const std::string& content_name) override;
246 
GetValueReffinal247     const ValueRef<FromType>* GetValueRef() const
248     { return m_value_ref.get(); }
249 
250     unsigned int GetCheckSum() const override;
251 
252 private:
253     std::unique_ptr<ValueRef<FromType>> m_value_ref;
254 
255     friend class boost::serialization::access;
256     template <typename Archive>
257     void serialize(Archive& ar, const unsigned int version);
258 };
259 
260 /** The variable lexical_cast to string class.  The value returned by this node
261   * is taken from the ctor \a value_ref parameter's FromType value,
262   * lexical_cast to std::string */
263 template <typename FromType>
264 struct FO_COMMON_API StringCast final : public Variable<std::string>
265 {
266     StringCast(std::unique_ptr<ValueRef<FromType>>&& value_ref);
267 
268     bool operator==(const ValueRef<std::string>& rhs) const override;
269     std::string Eval(const ScriptingContext& context) const override;
270     bool RootCandidateInvariant() const override;
271     bool LocalCandidateInvariant() const override;
272     bool TargetInvariant() const override;
273     bool SourceInvariant() const override;
274     std::string Description() const override;
275     std::string Dump(unsigned short ntabs = 0) const override;
276     void SetTopLevelContent(const std::string& content_name) override;
277 
GetValueReffinal278     const ValueRef<FromType>* GetValueRef() const
279     { return m_value_ref; }
280 
281     unsigned int GetCheckSum() const override;
282 
283 private:
284     std::unique_ptr<ValueRef<FromType>> m_value_ref;
285 
286     friend class boost::serialization::access;
287     template <typename Archive>
288     void serialize(Archive& ar, const unsigned int version);
289 };
290 
291 /** Looks up a string ValueRef or vector of string ValueRefs, and returns
292   * and returns the UserString equivalent(s). */
293 template <typename FromType>
294 struct FO_COMMON_API UserStringLookup final : public Variable<std::string> {
295     explicit UserStringLookup(std::unique_ptr<ValueRef<FromType>>&& value_ref);
296 
297     bool operator==(const ValueRef<std::string>& rhs) const override;
298     std::string Eval(const ScriptingContext& context) const override;
299     bool RootCandidateInvariant() const override;
300     bool LocalCandidateInvariant() const override;
301     bool TargetInvariant() const override;
302     bool SourceInvariant() const override;
303     std::string Description() const override;
304     std::string Dump(unsigned short ntabs = 0) const override;
305     void SetTopLevelContent(const std::string& content_name) override;
306 
GetValueReffinal307     const ValueRef<FromType>* GetValueRef() const
308     { return m_value_ref; }
309 
310     unsigned int GetCheckSum() const override;
311 
312 private:
313     std::unique_ptr<ValueRef<FromType>> m_value_ref;
314 
315     friend class boost::serialization::access;
316     template <typename Archive>
317     void serialize(Archive& ar, const unsigned int version);
318 };
319 
320 /** Returns the in-game name of the object / empire / etc. with a specified id. */
321 struct FO_COMMON_API NameLookup final : public Variable<std::string> {
322     enum LookupType : int {
323         INVALID_LOOKUP = -1,
324         OBJECT_NAME,
325         EMPIRE_NAME,
326         SHIP_DESIGN_NAME
327     };
328 
329     NameLookup(std::unique_ptr<ValueRef<int>>&& value_ref, LookupType lookup_type);
330 
331     bool operator==(const ValueRef<std::string>& rhs) const override;
332     std::string Eval(const ScriptingContext& context) const override;
333     bool RootCandidateInvariant() const override;
334     bool LocalCandidateInvariant() const override;
335     bool TargetInvariant() const override;
336     bool SourceInvariant() const override;
337     std::string Description() const override;
338     std::string Dump(unsigned short ntabs = 0) const override;
339     void SetTopLevelContent(const std::string& content_name) override;
340 
GetValueReffinal341     const ValueRef<int>* GetValueRef() const
342     { return m_value_ref.get(); }
343 
GetLookupTypefinal344     LookupType GetLookupType() const
345     { return m_lookup_type; }
346 
347     unsigned int GetCheckSum() const override;
348 
349 private:
350     std::unique_ptr<ValueRef<int>> m_value_ref;
351     LookupType m_lookup_type;
352 
353     friend class boost::serialization::access;
354     template <typename Archive>
355     void serialize(Archive& ar, const unsigned int version);
356 };
357 
358 enum OpType : int {
359     PLUS,
360     MINUS,
361     TIMES,
362     DIVIDE,
363     NEGATE,
364     EXPONENTIATE,
365     ABS,
366     LOGARITHM,
367     SINE,
368     COSINE,
369     MINIMUM,
370     MAXIMUM,
371     RANDOM_UNIFORM,
372     RANDOM_PICK,
373     SUBSTITUTION,
374     COMPARE_EQUAL,
375     COMPARE_GREATER_THAN,
376     COMPARE_GREATER_THAN_OR_EQUAL,
377     COMPARE_LESS_THAN,
378     COMPARE_LESS_THAN_OR_EQUAL,
379     COMPARE_NOT_EQUAL,
380     ROUND_NEAREST,
381     ROUND_UP,
382     ROUND_DOWN
383 };
384 
385 /** An arithmetic operation node ValueRef class. Unary or binary operations such
386   * as addition, mutiplication, negation, exponentiation, rounding,
387   * value substitution, value comparisons, or random value selection or
388   * random number generation are performed on the child(ren) of this node, and
389   * the result is returned. */
390 template <typename T>
391 struct FO_COMMON_API Operation final : public ValueRef<T>
392 {
393     /** Binary operation ctor. */
394     Operation(OpType op_type, std::unique_ptr<ValueRef<T>>&& operand1,
395               std::unique_ptr<ValueRef<T>>&& operand2);
396 
397     /** Unary operation ctor. */
398     Operation(OpType op_type, std::unique_ptr<ValueRef<T>>&& operand);
399 
400     /* N-ary operation ctor. */
401     Operation(OpType op_type, std::vector<std::unique_ptr<ValueRef<T>>>&& operands);
402 
403     bool operator==(const ValueRef<T>& rhs) const override;
404     T Eval(const ScriptingContext& context) const override;
405     bool RootCandidateInvariant() const override;
406     bool LocalCandidateInvariant() const override;
407     bool TargetInvariant() const override;
408     bool SourceInvariant() const override;
409     bool SimpleIncrement() const override;
ConstantExprfinal410     bool ConstantExpr() const override { return m_constant_expr; }
411     std::string Description() const override;
412     std::string Dump(unsigned short ntabs = 0) const override;
413     void SetTopLevelContent(const std::string& content_name) override;
414     OpType GetOpType() const;
415 
416     /** 1st operand (or 0 if none exists). */
417     const ValueRef<T>* LHS() const;
418 
419     /** 2nd operand (or 0 if only one exists) */
420     const ValueRef<T>* RHS() const;
421 
422     /** all operands */
423     const std::vector<ValueRef<T>*> Operands() const;
424 
425     unsigned int GetCheckSum() const override;
426 
427 private:
428     void    DetermineIfConstantExpr();
429     void    CacheConstValue();
430     T       EvalImpl(const ScriptingContext& context) const;
431 
432     OpType                                      m_op_type = TIMES;
433     std::vector<std::unique_ptr<ValueRef<T>>>   m_operands;
434     bool                                        m_constant_expr = false;
435     T                                           m_cached_const_value = T();
436 
437     friend class boost::serialization::access;
438     template <typename Archive>
439     void serialize(Archive& ar, const unsigned int version);
440 };
441 
442 FO_COMMON_API MeterType     NameToMeter(const std::string& name);
443 FO_COMMON_API std::string   MeterToName(MeterType meter);
444 FO_COMMON_API std::string   ReconstructName(const std::vector<std::string>& property_name,
445                                             ReferenceType ref_type,
446                                             bool return_immediate_value = false);
447 
448 FO_COMMON_API std::string FormatedDescriptionPropertyNames(
449     ReferenceType ref_type, const std::vector<std::string>& property_names,
450     bool return_immediate_value = false);
451 
452 FO_COMMON_API std::string ComplexVariableDescription(
453     const std::vector<std::string>& property_names,
454     const ValueRef<int>* int_ref1,
455     const ValueRef<int>* int_ref2,
456     const ValueRef<int>* int_ref3,
457     const ValueRef<std::string>* string_ref1,
458     const ValueRef<std::string>* string_ref2);
459 
460 FO_COMMON_API std::string ComplexVariableDump(
461     const std::vector<std::string>& property_names,
462     const ValueRef<int>* int_ref1,
463     const ValueRef<int>* int_ref2,
464     const ValueRef<int>* int_ref3,
465     const ValueRef<std::string>* string_ref1,
466     const ValueRef<std::string>* string_ref2);
467 
468 FO_COMMON_API std::string StatisticDescription(StatisticType stat_type,
469                                                const std::string& value_desc,
470                                                const std::string& condition_desc);
471 
472 // Template Implementations
473 ///////////////////////////////////////////////////////////
474 // ValueRef                                          //
475 ///////////////////////////////////////////////////////////
476 template <typename T>
477 bool ValueRef<T>::operator==(const ValueRef<T>& rhs) const
478 {
479     if (&rhs == this)
480         return true;
481     if (typeid(rhs) != typeid(*this))
482         return false;
483     return true;
484 }
485 
486 template <typename T>
487 template <typename Archive>
serialize(Archive & ar,const unsigned int version)488 void ValueRef<T>::serialize(Archive& ar, const unsigned int version)
489 {}
490 
491 ///////////////////////////////////////////////////////////
492 // Constant                                              //
493 ///////////////////////////////////////////////////////////
494 template <typename T>
Constant(T value)495 Constant<T>::Constant(T value) :
496     m_value(value)
497 {}
498 
499 template <typename T>
500 bool Constant<T>::operator==(const ValueRef<T>& rhs) const
501 {
502     if (&rhs == this)
503         return true;
504     if (typeid(rhs) != typeid(*this))
505         return false;
506     const Constant<T>& rhs_ = static_cast<const Constant<T>&>(rhs);
507 
508     return m_value == rhs_.m_value && m_top_level_content == rhs_.m_top_level_content;
509 }
510 
511 template <typename T>
Value()512 T Constant<T>::Value() const
513 { return m_value; }
514 
515 template <typename T>
Eval(const ScriptingContext & context)516 T Constant<T>::Eval(const ScriptingContext& context) const
517 { return m_value; }
518 
519 template <typename T>
Description()520 std::string Constant<T>::Description() const
521 { return UserString(boost::lexical_cast<std::string>(m_value)); }
522 
523 template <typename T>
SetTopLevelContent(const std::string & content_name)524 void Constant<T>::SetTopLevelContent(const std::string& content_name)
525 { m_top_level_content = content_name; }
526 
527 template <typename T>
GetCheckSum()528 unsigned int Constant<T>::GetCheckSum() const
529 {
530     unsigned int retval{0};
531 
532     CheckSums::CheckSumCombine(retval, "ValueRef::Constant");
533     CheckSums::CheckSumCombine(retval, m_value);
534     TraceLogger() << "GetCheckSum(Constant<T>): " << typeid(*this).name() << " value: " << m_value << " retval: " << retval;
535     return retval;
536 }
537 
538 template <>
539 FO_COMMON_API std::string Constant<int>::Description() const;
540 
541 template <>
542 FO_COMMON_API std::string Constant<double>::Description() const;
543 
544 template <>
545 FO_COMMON_API std::string Constant<std::string>::Description() const;
546 
547 template <>
548 FO_COMMON_API std::string Constant<PlanetSize>::Dump(unsigned short ntabs) const;
549 
550 template <>
551 FO_COMMON_API std::string Constant<PlanetType>::Dump(unsigned short ntabs) const;
552 
553 template <>
554 FO_COMMON_API std::string Constant<PlanetEnvironment>::Dump(unsigned short ntabs) const;
555 
556 template <>
557 FO_COMMON_API std::string Constant<UniverseObjectType>::Dump(unsigned short ntabs) const;
558 
559 template <>
560 FO_COMMON_API std::string Constant<StarType>::Dump(unsigned short ntabs) const;
561 
562 template <>
563 FO_COMMON_API std::string Constant<Visibility>::Dump(unsigned short ntabs) const;
564 
565 template <>
566 FO_COMMON_API std::string Constant<double>::Dump(unsigned short ntabs) const;
567 
568 template <>
569 FO_COMMON_API std::string Constant<int>::Dump(unsigned short ntabs) const;
570 
571 template <>
572 FO_COMMON_API std::string Constant<std::string>::Dump(unsigned short ntabs) const;
573 
574 template <>
575 FO_COMMON_API std::string Constant<std::string>::Eval(const ScriptingContext& context) const;
576 
577 template <typename T>
578 template <typename Archive>
serialize(Archive & ar,const unsigned int version)579 void Constant<T>::serialize(Archive& ar, const unsigned int version)
580 {
581     ar  & BOOST_SERIALIZATION_BASE_OBJECT_NVP(ValueRef)
582         & BOOST_SERIALIZATION_NVP(m_value)
583         & BOOST_SERIALIZATION_NVP(m_top_level_content);
584 }
585 
586 ///////////////////////////////////////////////////////////
587 // Variable                                              //
588 ///////////////////////////////////////////////////////////
589 template <typename T>
Variable(ReferenceType ref_type,const std::vector<std::string> & property_name,bool return_immediate_value)590 Variable<T>::Variable(ReferenceType ref_type, const std::vector<std::string>& property_name,
591                       bool return_immediate_value) :
592     m_ref_type(ref_type),
593     m_property_name(property_name.begin(), property_name.end()),
594     m_return_immediate_value(return_immediate_value)
595 {}
596 
597 template <typename T>
Variable(ReferenceType ref_type,const std::string & property_name,bool return_immediate_value)598 Variable<T>::Variable(ReferenceType ref_type, const std::string& property_name,
599                       bool return_immediate_value) :
600     m_ref_type(ref_type),
601     m_property_name(),
602     m_return_immediate_value(return_immediate_value)
603 {
604     m_property_name.push_back(property_name);
605 }
606 
607 template <typename T>
Variable(ReferenceType ref_type,const boost::optional<std::string> & container_name,const std::string & property_name,bool return_immediate_value)608 Variable<T>::Variable(ReferenceType ref_type,
609                       const boost::optional<std::string>& container_name,
610                       const std::string& property_name,
611                       bool return_immediate_value) :
612     m_ref_type(ref_type),
613     m_property_name(),
614     m_return_immediate_value(return_immediate_value)
615 {
616     if (container_name)
617         m_property_name.push_back(*container_name);
618 
619     m_property_name.push_back(property_name);
620 }
621 
622 template <typename T>
623 bool Variable<T>::operator==(const ValueRef<T>& rhs) const
624 {
625     if (&rhs == this)
626         return true;
627     if (typeid(rhs) != typeid(*this))
628         return false;
629     const Variable<T>& rhs_ = static_cast<const Variable<T>&>(rhs);
630     return (m_ref_type == rhs_.m_ref_type) &&
631            (m_property_name == rhs_.m_property_name) &&
632            (m_return_immediate_value == rhs_.m_return_immediate_value);
633 }
634 
635 template <typename T>
GetReferenceType()636 ReferenceType Variable<T>::GetReferenceType() const
637 { return m_ref_type; }
638 
639 template <typename T>
PropertyName()640 const std::vector<std::string>& Variable<T>::PropertyName() const
641 { return m_property_name; }
642 
643 template <typename T>
ReturnImmediateValue()644 bool Variable<T>::ReturnImmediateValue() const
645 { return m_return_immediate_value; }
646 
647 template <typename T>
RootCandidateInvariant()648 bool Variable<T>::RootCandidateInvariant() const
649 { return m_ref_type != CONDITION_ROOT_CANDIDATE_REFERENCE; }
650 
651 template <typename T>
LocalCandidateInvariant()652 bool Variable<T>::LocalCandidateInvariant() const
653 { return m_ref_type != CONDITION_LOCAL_CANDIDATE_REFERENCE; }
654 
655 template <typename T>
TargetInvariant()656 bool Variable<T>::TargetInvariant() const
657 { return m_ref_type != EFFECT_TARGET_REFERENCE && m_ref_type != EFFECT_TARGET_VALUE_REFERENCE; }
658 
659 template <typename T>
SourceInvariant()660 bool Variable<T>::SourceInvariant() const
661 { return m_ref_type != SOURCE_REFERENCE; }
662 
663 template <typename T>
Description()664 std::string Variable<T>::Description() const
665 { return FormatedDescriptionPropertyNames(m_ref_type, m_property_name, m_return_immediate_value); }
666 
667 template <typename T>
Dump(unsigned short ntabs)668 std::string Variable<T>::Dump(unsigned short ntabs) const
669 { return ReconstructName(m_property_name, m_ref_type, m_return_immediate_value); }
670 
671 template <typename T>
GetCheckSum()672 unsigned int Variable<T>::GetCheckSum() const
673 {
674     unsigned int retval{0};
675 
676     CheckSums::CheckSumCombine(retval, "ValueRef::Variable");
677     CheckSums::CheckSumCombine(retval, m_property_name);
678     CheckSums::CheckSumCombine(retval, m_ref_type);
679     CheckSums::CheckSumCombine(retval, m_return_immediate_value);
680     TraceLogger() << "GetCheckSum(Variable<T>): " << typeid(*this).name() << " retval: " << retval;
681     return retval;
682 }
683 
684 template <>
685 FO_COMMON_API PlanetSize Variable<PlanetSize>::Eval(const ScriptingContext& context) const;
686 
687 template <>
688 FO_COMMON_API PlanetType Variable<PlanetType>::Eval(const ScriptingContext& context) const;
689 
690 template <>
691 FO_COMMON_API PlanetEnvironment Variable<PlanetEnvironment>::Eval(const ScriptingContext& context) const;
692 
693 template <>
694 FO_COMMON_API UniverseObjectType Variable<UniverseObjectType>::Eval(const ScriptingContext& context) const;
695 
696 template <>
697 FO_COMMON_API StarType Variable<StarType>::Eval(const ScriptingContext& context) const;
698 
699 template <>
700 FO_COMMON_API Visibility Variable<Visibility>::Eval(const ScriptingContext& context) const;
701 
702 template <>
703 FO_COMMON_API double Variable<double>::Eval(const ScriptingContext& context) const;
704 
705 template <>
706 FO_COMMON_API int Variable<int>::Eval(const ScriptingContext& context) const;
707 
708 template <>
709 FO_COMMON_API std::string Variable<std::string>::Eval(const ScriptingContext& context) const;
710 
711 template <>
712 FO_COMMON_API std::vector<std::string> Variable<std::vector<std::string>>::Eval(const ScriptingContext& context) const;
713 
714 template <typename T>
715 template <typename Archive>
serialize(Archive & ar,const unsigned int version)716 void Variable<T>::serialize(Archive& ar, const unsigned int version)
717 {
718     ar  & BOOST_SERIALIZATION_BASE_OBJECT_NVP(ValueRef)
719         & BOOST_SERIALIZATION_NVP(m_ref_type)
720         & BOOST_SERIALIZATION_NVP(m_property_name)
721         & BOOST_SERIALIZATION_NVP(m_return_immediate_value);
722 }
723 
724 ///////////////////////////////////////////////////////////
725 // Statistic                                             //
726 ///////////////////////////////////////////////////////////
727 template <typename T>
Statistic(std::unique_ptr<ValueRef<T>> && value_ref,StatisticType stat_type,std::unique_ptr<Condition::Condition> && sampling_condition)728 Statistic<T>::Statistic(std::unique_ptr<ValueRef<T>>&& value_ref, StatisticType stat_type,
729                         std::unique_ptr<Condition::Condition>&& sampling_condition) :
730     Variable<T>(NON_OBJECT_REFERENCE, ""),
731     m_stat_type(stat_type),
732     m_sampling_condition(std::move(sampling_condition)),
733     m_value_ref(std::move(value_ref))
734 {}
735 
736 template <typename T>
737 bool Statistic<T>::operator==(const ValueRef<T>& rhs) const
738 {
739     if (&rhs == this)
740         return true;
741     if (typeid(rhs) != typeid(*this))
742         return false;
743     const Statistic<T>& rhs_ = static_cast<const Statistic<T>&>(rhs);
744 
745     if (m_stat_type != rhs_.m_stat_type)
746         return false;
747     if (this->m_value_ref != rhs_.m_value_ref)
748         return false;
749 
750     if (m_sampling_condition == rhs_.m_sampling_condition) {
751         // check next member
752     } else if (!m_sampling_condition || !rhs_.m_sampling_condition) {
753         return false;
754     } else {
755         if (*m_sampling_condition != *(rhs_.m_sampling_condition))
756             return false;
757     }
758 
759     return true;
760 }
761 
762 template <typename T>
GetConditionMatches(const ScriptingContext & context,Condition::ObjectSet & condition_targets,Condition::Condition * condition)763 void Statistic<T>::GetConditionMatches(const ScriptingContext& context,
764                                        Condition::ObjectSet& condition_targets,
765                                        Condition::Condition* condition) const
766 {
767     condition_targets.clear();
768     if (!condition)
769         return;
770     condition->Eval(context, condition_targets);
771 }
772 
773 template <typename T>
GetObjectPropertyValues(const ScriptingContext & context,const Condition::ObjectSet & objects,std::map<std::shared_ptr<const UniverseObject>,T> & object_property_values)774 void Statistic<T>::GetObjectPropertyValues(const ScriptingContext& context,
775                                            const Condition::ObjectSet& objects,
776                                            std::map<std::shared_ptr<const UniverseObject>, T>& object_property_values) const
777 {
778     object_property_values.clear();
779 
780     if (m_value_ref) {
781         // evaluate ValueRef with each condition match as the LocalCandidate
782         // TODO: Can / should this be paralleized?
783         for (auto& object : objects) {
784             T property_value = m_value_ref->Eval(ScriptingContext(context, object));
785             object_property_values[object] = property_value;
786         }
787     }
788 }
789 
790 template <typename T>
RootCandidateInvariant()791 bool Statistic<T>::RootCandidateInvariant() const
792 {
793     return Variable<T>::RootCandidateInvariant() &&
794            m_sampling_condition->RootCandidateInvariant() &&
795            (!m_value_ref || m_value_ref->RootCandidateInvariant());
796 }
797 
798 template <typename T>
LocalCandidateInvariant()799 bool Statistic<T>::LocalCandidateInvariant() const
800 {
801     // don't need to check if sampling condition is LocalCandidateInvariant, as
802     // all conditions aren't, but that refers to their own local candidate.  no
803     // condition is explicitly dependent on the parent context's local candidate.
804     return Variable<T>::LocalCandidateInvariant() &&
805            (!m_value_ref || m_value_ref->LocalCandidateInvariant());
806 }
807 
808 template <typename T>
TargetInvariant()809 bool Statistic<T>::TargetInvariant() const
810 {
811     return Variable<T>::TargetInvariant() &&
812            m_sampling_condition->TargetInvariant() &&
813            (!m_value_ref || m_value_ref->TargetInvariant());
814 }
815 
816 template <typename T>
SourceInvariant()817 bool Statistic<T>::SourceInvariant() const
818 {
819     return Variable<T>::SourceInvariant() &&
820            m_sampling_condition->SourceInvariant() &&
821            (!m_value_ref || m_value_ref->SourceInvariant());
822 }
823 
824 template <typename T>
Description()825 std::string Statistic<T>::Description() const
826 {
827     if (m_value_ref)
828         return StatisticDescription(m_stat_type, m_value_ref->Description(),
829                                     m_sampling_condition ? m_sampling_condition->Description() : "");
830 
831     auto temp = Variable<T>::Description();
832     if (!temp.empty())
833         return StatisticDescription(m_stat_type, temp, m_sampling_condition ? m_sampling_condition->Description() : "");
834 
835     return StatisticDescription(m_stat_type, "", m_sampling_condition ? m_sampling_condition->Description() : "");
836 }
837 
838 template <typename T>
Dump(unsigned short ntabs)839 std::string Statistic<T>::Dump(unsigned short ntabs) const
840 {
841     std::string retval = "Statistic ";
842 
843     switch (m_stat_type) {
844         case COUNT:              retval += "Count";         break;
845         case UNIQUE_COUNT:       retval += "CountUnique";   break;
846         case IF:                 retval += "If";            break;
847         case SUM:                retval += "Sum";           break;
848         case MEAN:               retval += "Mean";          break;
849         case RMS:                retval += "RMS";           break;
850         case MODE:               retval += "Mode";          break;
851         case MAX:                retval += "Max";           break;
852         case MIN:                retval += "Min";           break;
853         case SPREAD:             retval += "Spread";        break;
854         case STDEV:              retval += "StDev";         break;
855         case PRODUCT:            retval += "Product";       break;
856         default:                 retval += "???";           break;
857     }
858     if (m_value_ref)
859         retval += " value = " + m_value_ref->Dump();
860     if (m_sampling_condition)
861         retval += " condition = " + m_sampling_condition->Dump();
862     return retval;
863 }
864 
865 template <typename T>
SetTopLevelContent(const std::string & content_name)866 void Statistic<T>::SetTopLevelContent(const std::string& content_name)
867 {
868     if (m_sampling_condition)
869         m_sampling_condition->SetTopLevelContent(content_name);
870     if (m_value_ref)
871         m_value_ref->SetTopLevelContent(content_name);
872 }
873 
874 template <typename T>
Eval(const ScriptingContext & context)875 T Statistic<T>::Eval(const ScriptingContext& context) const
876 {
877     Condition::ObjectSet condition_matches;
878     GetConditionMatches(context, condition_matches, m_sampling_condition.get());
879 
880     // special case for IF statistic... return a T(1) for true.
881     if (m_stat_type == IF) {
882         if (condition_matches.empty())
883             return T(0);
884         else
885             return T(1);
886     }
887 
888     // todo: consider allowing MAX and MIN using string sorting?
889 
890     // the only other statistic that can be computed on non-number property
891     // types and that is itself of a non-number type is the most common value
892     if (m_stat_type != MODE) {
893         ErrorLogger() << "Statistic<std::string>::Eval has invalid statistic type: "
894                       << m_stat_type;
895         return T(-1);
896     }
897 
898     // evaluate property for each condition-matched object
899     std::map<std::shared_ptr<const UniverseObject>, T> object_property_values;
900     GetObjectPropertyValues(context, condition_matches, object_property_values);
901 
902     // count number of each result, tracking which has the most occurances
903     std::map<T, unsigned int> histogram;
904     auto most_common_property_value_it = histogram.begin();
905     unsigned int max_seen(0);
906 
907     for (const auto& entry : object_property_values) {
908         const T& property_value = entry.second;
909 
910         auto hist_it = histogram.find(property_value);
911         if (hist_it == histogram.end())
912             hist_it = histogram.insert({property_value, 0}).first;
913         unsigned int& num_seen = hist_it->second;
914 
915         num_seen++;
916 
917         if (num_seen > max_seen) {
918             most_common_property_value_it = hist_it;
919             max_seen = num_seen;
920         }
921     }
922 
923     // return result (property value) that occured most frequently
924     return most_common_property_value_it->first;
925 }
926 
927 template <typename T>
GetCheckSum()928 unsigned int Statistic<T>::GetCheckSum() const
929 {
930     unsigned int retval{0};
931 
932     CheckSums::CheckSumCombine(retval, "ValueRef::Statistic");
933     CheckSums::CheckSumCombine(retval, m_stat_type);
934     CheckSums::CheckSumCombine(retval, m_sampling_condition);
935     CheckSums::CheckSumCombine(retval, m_value_ref);
936     TraceLogger() << "GetCheckSum(Statisic<T>): " << typeid(*this).name() << " retval: " << retval;
937     return retval;
938 }
939 
940 template <>
941 FO_COMMON_API double Statistic<double>::Eval(const ScriptingContext& context) const;
942 
943 template <>
944 FO_COMMON_API int Statistic<int>::Eval(const ScriptingContext& context) const;
945 
946 template <>
947 FO_COMMON_API std::string Statistic<std::string>::Eval(const ScriptingContext& context) const;
948 
949 template <typename T>
ReduceData(const std::map<std::shared_ptr<const UniverseObject>,T> & object_property_values)950 T Statistic<T>::ReduceData(const std::map<std::shared_ptr<const UniverseObject>, T>& object_property_values) const
951 {
952     if (object_property_values.empty())
953         return T(0);
954 
955     switch (m_stat_type) {
956         case COUNT: {
957             return T(object_property_values.size());
958             break;
959         }
960         case UNIQUE_COUNT: {
961             std::set<T> observed_values;
962             for (const auto& entry : object_property_values) {
963                 observed_values.insert(entry.second);
964             }
965             return T(observed_values.size());
966             break;
967         }
968         case IF: {
969             if (object_property_values.empty())
970                 return T(0);
971             return T(1);
972             break;
973         }
974         case SUM: {
975             T accumulator(0);
976             for (const auto& entry : object_property_values) {
977                 accumulator += entry.second;
978             }
979             return accumulator;
980             break;
981         }
982 
983         case MEAN: {
984             T accumulator(0);
985             for (const auto& entry : object_property_values) {
986                 accumulator += entry.second;
987             }
988             return accumulator / static_cast<T>(object_property_values.size());
989             break;
990         }
991 
992         case RMS: {
993             T accumulator(0);
994             for (const auto& entry : object_property_values) {
995                 accumulator += (entry.second * entry.second);
996             }
997             accumulator /= static_cast<T>(object_property_values.size());
998 
999             double retval = std::sqrt(static_cast<double>(accumulator));
1000             return static_cast<T>(retval);
1001             break;
1002         }
1003 
1004         case MODE: {
1005             // count number of each result, tracking which has the most occurances
1006             std::map<T, unsigned int> histogram;
1007             auto most_common_property_value_it = histogram.begin();
1008             unsigned int max_seen(0);
1009 
1010             for (const auto& entry : object_property_values) {
1011                 const T& property_value = entry.second;
1012 
1013                auto hist_it = histogram.find(property_value);
1014                 if (hist_it == histogram.end())
1015                     hist_it = histogram.insert({property_value, 0}).first;
1016                 unsigned int& num_seen = hist_it->second;
1017 
1018                 num_seen++;
1019 
1020                 if (num_seen > max_seen) {
1021                     most_common_property_value_it = hist_it;
1022                     max_seen = num_seen;
1023                 }
1024             }
1025 
1026             // return result (property value) that occured most frequently
1027             return most_common_property_value_it->first;
1028             break;
1029         }
1030 
1031         case MAX: {
1032             auto max_it = object_property_values.begin();
1033 
1034             for (auto it = object_property_values.begin();
1035                  it != object_property_values.end(); ++it)
1036             {
1037                 const T& property_value = it->second;
1038                 if (property_value > max_it->second)
1039                     max_it = it;
1040             }
1041 
1042             // return maximal observed propery value
1043             return max_it->second;
1044             break;
1045         }
1046 
1047         case MIN: {
1048             auto min_it = object_property_values.begin();
1049 
1050             for (auto it = object_property_values.begin();
1051                  it != object_property_values.end(); ++it)
1052             {
1053                 const T& property_value = it->second;
1054                 if (property_value < min_it->second)
1055                     min_it = it;
1056             }
1057 
1058             // return minimal observed propery value
1059             return min_it->second;
1060             break;
1061         }
1062 
1063         case SPREAD: {
1064             auto max_it = object_property_values.begin();
1065             auto min_it = object_property_values.begin();
1066 
1067             for (auto it = object_property_values.begin();
1068                  it != object_property_values.end(); ++it)
1069             {
1070                 const T& property_value = it->second;
1071                 if (property_value > max_it->second)
1072                     max_it = it;
1073                 if (property_value < min_it->second)
1074                     min_it = it;
1075             }
1076 
1077             // return difference between maximal and minimal observed propery values
1078             return max_it->second - min_it->second;
1079             break;
1080         }
1081 
1082         case STDEV: {
1083             if (object_property_values.size() < 2)
1084                 return T(0);
1085 
1086             // find sample mean
1087             T accumulator(0);
1088             for (const auto& entry : object_property_values) {
1089                 accumulator += entry.second;
1090             }
1091             const T MEAN(accumulator / static_cast<T>(object_property_values.size()));
1092 
1093             // find average of squared deviations from sample mean
1094             accumulator = T(0);
1095             for (const auto& entry : object_property_values) {
1096                 accumulator += (entry.second - MEAN) * (entry.second - MEAN);
1097             }
1098             const T MEAN_DEV2(accumulator / static_cast<T>(static_cast<int>(object_property_values.size()) - 1));
1099             double retval = std::sqrt(static_cast<double>(MEAN_DEV2));
1100             return static_cast<T>(retval);
1101             break;
1102         }
1103 
1104         case PRODUCT: {
1105             T accumulator(1);
1106             for (const auto& entry : object_property_values) {
1107                 accumulator *= entry.second;
1108             }
1109             return accumulator;
1110             break;
1111         }
1112 
1113         default:
1114             throw std::runtime_error("ValueRef evaluated with an unknown or invalid StatisticType.");
1115             break;
1116     }
1117 }
1118 
1119 template <typename T>
1120 template <typename Archive>
serialize(Archive & ar,const unsigned int version)1121 void Statistic<T>::serialize(Archive& ar, const unsigned int version)
1122 {
1123     ar  & BOOST_SERIALIZATION_BASE_OBJECT_NVP(Variable)
1124         & BOOST_SERIALIZATION_NVP(m_stat_type)
1125         & BOOST_SERIALIZATION_NVP(m_sampling_condition)
1126         & BOOST_SERIALIZATION_NVP(m_value_ref);
1127 }
1128 
1129 ///////////////////////////////////////////////////////////
1130 // ComplexVariable                                       //
1131 ///////////////////////////////////////////////////////////
1132 template <typename T>
ComplexVariable(const std::string & variable_name,std::unique_ptr<ValueRef<int>> && int_ref1,std::unique_ptr<ValueRef<int>> && int_ref2,std::unique_ptr<ValueRef<int>> && int_ref3,std::unique_ptr<ValueRef<std::string>> && string_ref1,std::unique_ptr<ValueRef<std::string>> && string_ref2,bool return_immediate_value)1133 ComplexVariable<T>::ComplexVariable(const std::string& variable_name,
1134                                     std::unique_ptr<ValueRef<int>>&& int_ref1,
1135                                     std::unique_ptr<ValueRef<int>>&& int_ref2,
1136                                     std::unique_ptr<ValueRef<int>>&& int_ref3,
1137                                     std::unique_ptr<ValueRef<std::string>>&& string_ref1,
1138                                     std::unique_ptr<ValueRef<std::string>>&& string_ref2,
1139                                     bool return_immediate_value) :
1140     Variable<T>(NON_OBJECT_REFERENCE, std::vector<std::string>(1, variable_name), return_immediate_value),
1141     m_int_ref1(std::move(int_ref1)),
1142     m_int_ref2(std::move(int_ref2)),
1143     m_int_ref3(std::move(int_ref3)),
1144     m_string_ref1(std::move(string_ref1)),
1145     m_string_ref2(std::move(string_ref2))
1146 {}
1147 
1148 template <typename T>
1149 bool ComplexVariable<T>::operator==(const ValueRef<T>& rhs) const
1150 {
1151     if (&rhs == this)
1152         return true;
1153     if (typeid(rhs) != typeid(*this))
1154         return false;
1155     const ComplexVariable<T>& rhs_ = static_cast<const ComplexVariable<T>&>(rhs);
1156 
1157     if (this->m_property_name != rhs_.m_property_name)
1158         return false;
1159     if (this->m_return_immediate_value != rhs_.m_return_immediate_value)
1160         return false;
1161 
1162     if (m_int_ref1 == rhs_.m_int_ref1) {
1163         // check next member
1164     } else if (!m_int_ref1 || !rhs_.m_int_ref1) {
1165         return false;
1166     } else {
1167         if (*m_int_ref1 != *(rhs_.m_int_ref1))
1168             return false;
1169     }
1170 
1171     if (m_int_ref2 == rhs_.m_int_ref2) {
1172         // check next member
1173     } else if (!m_int_ref2 || !rhs_.m_int_ref2) {
1174         return false;
1175     } else {
1176         if (*m_int_ref2 != *(rhs_.m_int_ref2))
1177             return false;
1178     }
1179 
1180     if (m_int_ref3 == rhs_.m_int_ref3) {
1181         // check next member
1182     } else if (!m_int_ref3 || !rhs_.m_int_ref3) {
1183         return false;
1184     } else {
1185         if (*m_int_ref3 != *(rhs_.m_int_ref3))
1186             return false;
1187     }
1188 
1189     if (m_string_ref1 == rhs_.m_string_ref1) {
1190         // check next member
1191     } else if (!m_string_ref1 || !rhs_.m_string_ref1) {
1192         return false;
1193     } else {
1194         if (*m_string_ref1 != *(rhs_.m_string_ref1))
1195             return false;
1196     }
1197 
1198     if (m_string_ref2 == rhs_.m_string_ref2) {
1199         // check next member
1200     } else if (!m_string_ref2 || !rhs_.m_string_ref2) {
1201         return false;
1202     } else {
1203         if (*m_string_ref2 != *(rhs_.m_string_ref2))
1204             return false;
1205     }
1206 
1207     return true;
1208 }
1209 
1210 template <typename T>
IntRef1()1211 const ValueRef<int>* ComplexVariable<T>::IntRef1() const
1212 { return m_int_ref1.get(); }
1213 
1214 template <typename T>
IntRef2()1215 const ValueRef<int>* ComplexVariable<T>::IntRef2() const
1216 { return m_int_ref2.get(); }
1217 
1218 template <typename T>
IntRef3()1219 const ValueRef<int>* ComplexVariable<T>::IntRef3() const
1220 { return m_int_ref3.get(); }
1221 
1222 template <typename T>
StringRef1()1223 const ValueRef<std::string>* ComplexVariable<T>::StringRef1() const
1224 { return m_string_ref1.get(); }
1225 
1226 template <typename T>
StringRef2()1227 const ValueRef<std::string>* ComplexVariable<T>::StringRef2() const
1228 { return m_string_ref2.get(); }
1229 
1230 template <typename T>
RootCandidateInvariant()1231 bool ComplexVariable<T>::RootCandidateInvariant() const
1232 {
1233     return Variable<T>::RootCandidateInvariant()
1234         && (!m_int_ref1 || m_int_ref1->RootCandidateInvariant())
1235         && (!m_int_ref2 || m_int_ref2->RootCandidateInvariant())
1236         && (!m_int_ref3 || m_int_ref3->RootCandidateInvariant())
1237         && (!m_string_ref1 || m_string_ref1->RootCandidateInvariant())
1238         && (!m_string_ref2 || m_string_ref2->RootCandidateInvariant());
1239 }
1240 
1241 template <typename T>
LocalCandidateInvariant()1242 bool ComplexVariable<T>::LocalCandidateInvariant() const
1243 {
1244     return (!m_int_ref1 || m_int_ref1->LocalCandidateInvariant())
1245         && (!m_int_ref2 || m_int_ref2->LocalCandidateInvariant())
1246         && (!m_int_ref3 || m_int_ref3->LocalCandidateInvariant())
1247         && (!m_string_ref1 || m_string_ref1->LocalCandidateInvariant())
1248         && (!m_string_ref2 || m_string_ref2->LocalCandidateInvariant());
1249 }
1250 
1251 template <typename T>
TargetInvariant()1252 bool ComplexVariable<T>::TargetInvariant() const
1253 {
1254     return (!m_int_ref1 || m_int_ref1->TargetInvariant())
1255         && (!m_int_ref2 || m_int_ref2->TargetInvariant())
1256         && (!m_int_ref3 || m_int_ref3->TargetInvariant())
1257         && (!m_string_ref1 || m_string_ref1->TargetInvariant())
1258         && (!m_string_ref2 || m_string_ref2->TargetInvariant());
1259 }
1260 
1261 template <typename T>
SourceInvariant()1262 bool ComplexVariable<T>::SourceInvariant() const
1263 {
1264     return (!m_int_ref1 || m_int_ref1->SourceInvariant())
1265         && (!m_int_ref2 || m_int_ref2->SourceInvariant())
1266         && (!m_int_ref3 || m_int_ref3->SourceInvariant())
1267         && (!m_string_ref1 || m_string_ref1->SourceInvariant())
1268         && (!m_string_ref2 || m_string_ref2->SourceInvariant());
1269 }
1270 
1271 template <typename T>
Description()1272 std::string ComplexVariable<T>::Description() const
1273 {
1274     std::string retval = ComplexVariableDescription(
1275         this->m_property_name,
1276         m_int_ref1 ? m_int_ref1.get() : nullptr,
1277         m_int_ref2 ? m_int_ref2.get() : nullptr,
1278         m_int_ref3 ? m_int_ref3.get() : nullptr,
1279         m_string_ref1 ? m_string_ref1.get() : nullptr,
1280         m_string_ref2 ? m_string_ref2.get() : nullptr);
1281     if (retval.empty())
1282         return Dump();
1283     return retval;
1284 }
1285 
1286 template <typename T>
Dump(unsigned short ntabs)1287 std::string ComplexVariable<T>::Dump(unsigned short ntabs) const
1288 {
1289     return ComplexVariableDump(this->m_property_name,
1290                                m_int_ref1 ? m_int_ref1.get() : nullptr,
1291                                m_int_ref2 ? m_int_ref2.get() : nullptr,
1292                                m_int_ref3 ? m_int_ref3.get() : nullptr,
1293                                m_string_ref1 ? m_string_ref1.get() : nullptr,
1294                                m_string_ref2 ? m_string_ref2.get() : nullptr);
1295 }
1296 
1297 template <typename T>
SetTopLevelContent(const std::string & content_name)1298 void ComplexVariable<T>::SetTopLevelContent(const std::string& content_name)
1299 {
1300     if (m_int_ref1)
1301         m_int_ref1->SetTopLevelContent(content_name);
1302     if (m_int_ref2)
1303         m_int_ref2->SetTopLevelContent(content_name);
1304     if (m_int_ref3)
1305         m_int_ref3->SetTopLevelContent(content_name);
1306     if (m_string_ref1)
1307         m_string_ref1->SetTopLevelContent(content_name);
1308     if (m_string_ref2)
1309         m_string_ref2->SetTopLevelContent(content_name);
1310 }
1311 
1312 template <typename T>
GetCheckSum()1313 unsigned int ComplexVariable<T>::GetCheckSum() const
1314 {
1315     unsigned int retval{0};
1316 
1317     CheckSums::CheckSumCombine(retval, "ValueRef::ComplexVariable");
1318     CheckSums::CheckSumCombine(retval, m_int_ref1);
1319     CheckSums::CheckSumCombine(retval, m_int_ref2);
1320     CheckSums::CheckSumCombine(retval, m_int_ref3);
1321     CheckSums::CheckSumCombine(retval, m_string_ref1);
1322     CheckSums::CheckSumCombine(retval, m_string_ref2);
1323     TraceLogger() << "GetCheckSum(ComplexVariable<T>): " << typeid(*this).name() << " retval: " << retval;
1324     return retval;
1325 }
1326 
1327 template <>
1328 FO_COMMON_API PlanetSize ComplexVariable<PlanetSize>::Eval(const ScriptingContext& context) const;
1329 
1330 template <>
1331 FO_COMMON_API PlanetType ComplexVariable<PlanetType>::Eval(const ScriptingContext& context) const;
1332 
1333 template <>
1334 FO_COMMON_API PlanetEnvironment ComplexVariable<PlanetEnvironment>::Eval(const ScriptingContext& context) const;
1335 
1336 template <>
1337 FO_COMMON_API UniverseObjectType ComplexVariable<UniverseObjectType>::Eval(const ScriptingContext& context) const;
1338 
1339 template <>
1340 FO_COMMON_API StarType ComplexVariable<StarType>::Eval(const ScriptingContext& context) const;
1341 
1342 template <>
1343 FO_COMMON_API Visibility ComplexVariable<Visibility>::Eval(const ScriptingContext& context) const;
1344 
1345 template <>
1346 FO_COMMON_API double ComplexVariable<double>::Eval(const ScriptingContext& context) const;
1347 
1348 template <>
1349 FO_COMMON_API int ComplexVariable<int>::Eval(const ScriptingContext& context) const;
1350 
1351 template <>
1352 FO_COMMON_API std::string ComplexVariable<std::string>::Eval(const ScriptingContext& context) const;
1353 
1354 template <>
1355 FO_COMMON_API std::string ComplexVariable<Visibility>::Dump(unsigned short ntabs) const;
1356 
1357 template <>
1358 FO_COMMON_API std::string ComplexVariable<double>::Dump(unsigned short ntabs) const;
1359 
1360 template <>
1361 FO_COMMON_API std::string ComplexVariable<int>::Dump(unsigned short ntabs) const;
1362 
1363 template <>
1364 FO_COMMON_API std::string ComplexVariable<std::string>::Dump(unsigned short ntabs) const;
1365 
1366 template <typename T>
1367 template <typename Archive>
serialize(Archive & ar,const unsigned int version)1368 void ComplexVariable<T>::serialize(Archive& ar, const unsigned int version)
1369 {
1370     ar  & BOOST_SERIALIZATION_BASE_OBJECT_NVP(Variable)
1371         & BOOST_SERIALIZATION_NVP(m_int_ref1)
1372         & BOOST_SERIALIZATION_NVP(m_int_ref2)
1373         & BOOST_SERIALIZATION_NVP(m_int_ref3)
1374         & BOOST_SERIALIZATION_NVP(m_string_ref1)
1375         & BOOST_SERIALIZATION_NVP(m_string_ref2);
1376 }
1377 
1378 ///////////////////////////////////////////////////////////
1379 // StaticCast                                            //
1380 ///////////////////////////////////////////////////////////
1381 template <typename FromType, typename ToType>
1382 template <typename T>
StaticCast(T && value_ref,typename std::enable_if<std::is_convertible<T,std::unique_ptr<Variable<FromType>>>::value>::type *)1383 StaticCast<FromType, ToType>::StaticCast(
1384     T&& value_ref,
1385     typename std::enable_if<std::is_convertible<T, std::unique_ptr<Variable<FromType>>>::value>::type*) :
1386     Variable<ToType>(value_ref->GetReferenceType(), value_ref->PropertyName()),
1387     m_value_ref(std::move(value_ref))
1388 {}
1389 
1390 template <typename FromType, typename ToType>
1391 template <typename T>
StaticCast(T && value_ref,typename std::enable_if<std::is_convertible<T,std::unique_ptr<ValueRef<FromType>>>::value &&!std::is_convertible<T,std::unique_ptr<Variable<FromType>>>::value>::type *)1392 StaticCast<FromType, ToType>::StaticCast(
1393     T&& value_ref,
1394     typename std::enable_if<
1395     std::is_convertible<T, std::unique_ptr<ValueRef<FromType>>>::value
1396     && !std::is_convertible<T, std::unique_ptr<Variable<FromType>>>::value>::type*) :
1397     Variable<ToType>(NON_OBJECT_REFERENCE),
1398     m_value_ref(std::move(value_ref))
1399 {}
1400 
1401 template <typename FromType, typename ToType>
1402 bool StaticCast<FromType, ToType>::operator==(const ValueRef<ToType>& rhs) const
1403 {
1404     if (&rhs == this)
1405         return true;
1406     if (typeid(rhs) != typeid(*this))
1407         return false;
1408     const StaticCast<FromType, ToType>& rhs_ =
1409         static_cast<const StaticCast<FromType, ToType>&>(rhs);
1410 
1411     if (m_value_ref == rhs_.m_value_ref) {
1412         // check next member
1413     } else if (!m_value_ref || !rhs_.m_value_ref) {
1414         return false;
1415     } else {
1416         if (*m_value_ref != *(rhs_.m_value_ref))
1417             return false;
1418     }
1419 
1420     return true;
1421 }
1422 
1423 template <typename FromType, typename ToType>
Eval(const ScriptingContext & context)1424 ToType StaticCast<FromType, ToType>::Eval(const ScriptingContext& context) const
1425 { return static_cast<ToType>(m_value_ref->Eval(context)); }
1426 
1427 template <typename FromType, typename ToType>
RootCandidateInvariant()1428 bool StaticCast<FromType, ToType>::RootCandidateInvariant() const
1429 { return m_value_ref->RootCandidateInvariant(); }
1430 
1431 template <typename FromType, typename ToType>
LocalCandidateInvariant()1432 bool StaticCast<FromType, ToType>::LocalCandidateInvariant() const
1433 { return m_value_ref->LocalCandidateInvariant(); }
1434 
1435 template <typename FromType, typename ToType>
TargetInvariant()1436 bool StaticCast<FromType, ToType>::TargetInvariant() const
1437 { return m_value_ref->TargetInvariant(); }
1438 
1439 template <typename FromType, typename ToType>
SourceInvariant()1440 bool StaticCast<FromType, ToType>::SourceInvariant() const
1441 { return m_value_ref->SourceInvariant(); }
1442 
1443 template <typename FromType, typename ToType>
Description()1444 std::string StaticCast<FromType, ToType>::Description() const
1445 { return m_value_ref->Description(); }
1446 
1447 template <typename FromType, typename ToType>
Dump(unsigned short ntabs)1448 std::string StaticCast<FromType, ToType>::Dump(unsigned short ntabs) const
1449 { return m_value_ref->Dump(ntabs); }
1450 
1451 template <typename FromType, typename ToType>
SetTopLevelContent(const std::string & content_name)1452 void StaticCast<FromType, ToType>::SetTopLevelContent(const std::string& content_name)
1453 {
1454     if (m_value_ref)
1455         m_value_ref->SetTopLevelContent(content_name);
1456 }
1457 
1458 template <typename FromType, typename ToType>
GetCheckSum()1459 unsigned int StaticCast<FromType, ToType>::GetCheckSum() const
1460 {
1461     unsigned int retval{0};
1462 
1463     CheckSums::CheckSumCombine(retval, "ValueRef::StaticCast");
1464     CheckSums::CheckSumCombine(retval, m_value_ref);
1465     TraceLogger() << "GetCheckSum(StaticCast<FromType, ToType>): " << typeid(*this).name() << " retval: " << retval;
1466     return retval;
1467 }
1468 
1469 template <typename FromType, typename ToType>
1470 template <typename Archive>
serialize(Archive & ar,const unsigned int version)1471 void StaticCast<FromType, ToType>::serialize(Archive& ar, const unsigned int version)
1472 {
1473     ar  & BOOST_SERIALIZATION_BASE_OBJECT_NVP(ValueRef)
1474         & BOOST_SERIALIZATION_NVP(m_value_ref);
1475 }
1476 
1477 ///////////////////////////////////////////////////////////
1478 // StringCast                                            //
1479 ///////////////////////////////////////////////////////////
1480 template <typename FromType>
StringCast(std::unique_ptr<ValueRef<FromType>> && value_ref)1481 StringCast<FromType>::StringCast(std::unique_ptr<ValueRef<FromType>>&& value_ref) :
1482     Variable<std::string>(NON_OBJECT_REFERENCE),
1483     m_value_ref(std::move(value_ref))
1484 {
1485     auto raw_ref_ptr = m_value_ref.get();
1486     // if looking up a the results of ValueRef::Variable::Eval, can copy that
1487     // ValueRef's internals to expose the reference type and property name from
1488     // this ValueRef
1489     if (auto var_ref = dynamic_cast<Variable<FromType>*>(raw_ref_ptr)) {
1490         this->m_ref_type = var_ref->GetReferenceType();
1491         this->m_property_name = var_ref->PropertyName();
1492     }
1493 }
1494 
1495 template <typename FromType>
1496 bool StringCast<FromType>::operator==(const ValueRef<std::string>& rhs) const
1497 {
1498     if (&rhs == this)
1499         return true;
1500     if (typeid(rhs) != typeid(*this))
1501         return false;
1502     const StringCast<FromType>& rhs_ =
1503         static_cast<const StringCast<FromType>&>(rhs);
1504 
1505     if (m_value_ref == rhs_.m_value_ref) {
1506         // check next member
1507     } else if (!m_value_ref || !rhs_.m_value_ref) {
1508         return false;
1509     } else {
1510         if (*m_value_ref != *(rhs_.m_value_ref))
1511             return false;
1512     }
1513 
1514     return true;
1515 }
1516 
1517 template <typename FromType>
Eval(const ScriptingContext & context)1518 std::string StringCast<FromType>::Eval(const ScriptingContext& context) const
1519 {
1520     if (!m_value_ref)
1521         return "";
1522     std::string retval;
1523     try {
1524         retval = boost::lexical_cast<std::string>(m_value_ref->Eval(context));
1525     } catch (...) {
1526     }
1527     return retval;
1528 }
1529 
1530 template <typename FromType>
GetCheckSum()1531 unsigned int StringCast<FromType>::GetCheckSum() const
1532 {
1533     unsigned int retval{0};
1534 
1535     CheckSums::CheckSumCombine(retval, "ValueRef::StringCast");
1536     CheckSums::CheckSumCombine(retval, m_value_ref);
1537     TraceLogger() << "GetCheckSum(StringCast<FromType>): " << typeid(*this).name() << " retval: " << retval;
1538     return retval;
1539 }
1540 
1541 template <>
1542 FO_COMMON_API std::string StringCast<double>::Eval(const ScriptingContext& context) const;
1543 
1544 template <>
1545 FO_COMMON_API std::string StringCast<int>::Eval(const ScriptingContext& context) const;
1546 
1547 template <>
1548 FO_COMMON_API std::string StringCast<std::vector<std::string>>::Eval(const ScriptingContext& context) const;
1549 
1550 template <typename FromType>
RootCandidateInvariant()1551 bool StringCast<FromType>::RootCandidateInvariant() const
1552 { return m_value_ref->RootCandidateInvariant(); }
1553 
1554 template <typename FromType>
LocalCandidateInvariant()1555 bool StringCast<FromType>::LocalCandidateInvariant() const
1556 { return m_value_ref->LocalCandidateInvariant(); }
1557 
1558 template <typename FromType>
TargetInvariant()1559 bool StringCast<FromType>::TargetInvariant() const
1560 { return m_value_ref->TargetInvariant(); }
1561 
1562 template <typename FromType>
SourceInvariant()1563 bool StringCast<FromType>::SourceInvariant() const
1564 { return m_value_ref->SourceInvariant(); }
1565 
1566 template <typename FromType>
Description()1567 std::string StringCast<FromType>::Description() const
1568 { return m_value_ref->Description(); }
1569 
1570 template <typename FromType>
Dump(unsigned short ntabs)1571 std::string StringCast<FromType>::Dump(unsigned short ntabs) const
1572 { return m_value_ref->Dump(ntabs); }
1573 
1574 template <typename FromType>
SetTopLevelContent(const std::string & content_name)1575 void StringCast<FromType>::SetTopLevelContent(const std::string& content_name) {
1576     if (m_value_ref)
1577         m_value_ref->SetTopLevelContent(content_name);
1578 }
1579 
1580 template <typename FromType>
1581 template <typename Archive>
serialize(Archive & ar,const unsigned int version)1582 void StringCast<FromType>::serialize(Archive& ar, const unsigned int version)
1583 {
1584     ar  & BOOST_SERIALIZATION_BASE_OBJECT_NVP(ValueRef)
1585         & BOOST_SERIALIZATION_NVP(m_value_ref);
1586 }
1587 
1588 ///////////////////////////////////////////////////////////
1589 // UserStringLookup                                      //
1590 ///////////////////////////////////////////////////////////
1591 template <typename FromType>
UserStringLookup(std::unique_ptr<ValueRef<FromType>> && value_ref)1592 UserStringLookup<FromType>::UserStringLookup(std::unique_ptr<ValueRef<FromType>>&& value_ref) :
1593     Variable<std::string>(NON_OBJECT_REFERENCE),
1594     m_value_ref(std::move(value_ref))
1595 {
1596     auto raw_ref_ptr = m_value_ref.get();
1597     // if looking up a the results of ValueRef::Variable::Eval, can copy that
1598     // ValueRef's internals to expose the reference type and property name from
1599     // this ValueRef
1600     if (auto var_ref = dynamic_cast<Variable<FromType>*>(raw_ref_ptr)) {
1601         this->m_ref_type = var_ref->GetReferenceType();
1602         this->m_property_name = var_ref->PropertyName();
1603     }
1604 }
1605 
1606 template <typename FromType>
1607 bool UserStringLookup<FromType>::operator==(const ValueRef<std::string>& rhs) const {
1608     if (&rhs == this)
1609         return true;
1610     if (typeid(rhs) != typeid(*this))
1611         return false;
1612     const UserStringLookup& rhs_ = static_cast<const UserStringLookup&>(rhs);
1613 
1614     if (m_value_ref == rhs_.m_value_ref) {
1615         // check next member
1616     }
1617     else if (!m_value_ref || !rhs_.m_value_ref) {
1618         return false;
1619     }
1620     else {
1621         if (*m_value_ref != *(rhs_.m_value_ref))
1622             return false;
1623     }
1624 
1625     return true;
1626 }
1627 
1628 template <typename FromType>
Eval(const ScriptingContext & context)1629 std::string UserStringLookup<FromType>::Eval(const ScriptingContext& context) const {
1630     if (!m_value_ref)
1631         return "";
1632     std::string ref_val = boost::lexical_cast<std::string>(m_value_ref->Eval(context));
1633     if (ref_val.empty() || !UserStringExists(ref_val))
1634         return "";
1635     return UserString(ref_val);
1636 }
1637 
1638 template <>
1639 FO_COMMON_API std::string UserStringLookup<std::string>::Eval(const ScriptingContext& context) const;
1640 
1641 template <>
1642 FO_COMMON_API std::string UserStringLookup<std::vector<std::string>>::Eval(const ScriptingContext& context) const;
1643 
1644 template <typename FromType>
RootCandidateInvariant()1645 bool UserStringLookup<FromType>::RootCandidateInvariant() const
1646 {
1647     return m_value_ref->RootCandidateInvariant();
1648 }
1649 
1650 template <typename FromType>
LocalCandidateInvariant()1651 bool UserStringLookup<FromType>::LocalCandidateInvariant() const
1652 {
1653     return !m_value_ref || m_value_ref->LocalCandidateInvariant();
1654 }
1655 
1656 template <typename FromType>
TargetInvariant()1657 bool UserStringLookup<FromType>::TargetInvariant() const
1658 {
1659     return !m_value_ref || m_value_ref->TargetInvariant();
1660 }
1661 
1662 template <typename FromType>
SourceInvariant()1663 bool UserStringLookup<FromType>::SourceInvariant() const
1664 {
1665     return !m_value_ref || m_value_ref->SourceInvariant();
1666 }
1667 
1668 template <typename FromType>
Description()1669 std::string UserStringLookup<FromType>::Description() const
1670 {
1671     return m_value_ref->Description();
1672 }
1673 
1674 template <typename FromType>
Dump(unsigned short ntabs)1675 std::string UserStringLookup<FromType>::Dump(unsigned short ntabs) const
1676 {
1677     return m_value_ref->Dump(ntabs);
1678 }
1679 
1680 template <typename FromType>
SetTopLevelContent(const std::string & content_name)1681 void UserStringLookup<FromType>::SetTopLevelContent(const std::string& content_name) {
1682     if (m_value_ref)
1683         m_value_ref->SetTopLevelContent(content_name);
1684 }
1685 
1686 template <typename FromType>
GetCheckSum()1687 unsigned int UserStringLookup<FromType>::GetCheckSum() const
1688 {
1689     unsigned int retval{0};
1690 
1691     CheckSums::CheckSumCombine(retval, "ValueRef::UserStringLookup");
1692     CheckSums::CheckSumCombine(retval, m_value_ref);
1693     TraceLogger() << "GetCheckSum(UserStringLookup<FromType>): " << typeid(*this).name() << " retval: " << retval;
1694     return retval;
1695 }
1696 
1697 template <typename FromType>
1698 template <typename Archive>
serialize(Archive & ar,const unsigned int version)1699 void UserStringLookup<FromType>::serialize(Archive& ar, const unsigned int version)
1700 {
1701     ar  & BOOST_SERIALIZATION_BASE_OBJECT_NVP(ValueRef<std::string>)
1702         & BOOST_SERIALIZATION_NVP(m_value_ref);
1703 }
1704 
1705 ///////////////////////////////////////////////////////////
1706 // NameLookup                                            //
1707 ///////////////////////////////////////////////////////////
1708 template <typename Archive>
serialize(Archive & ar,const unsigned int version)1709 void NameLookup::serialize(Archive& ar, const unsigned int version)
1710 {
1711     ar  & BOOST_SERIALIZATION_BASE_OBJECT_NVP(ValueRef<std::string>)
1712         & BOOST_SERIALIZATION_NVP(m_value_ref)
1713         & BOOST_SERIALIZATION_NVP(m_lookup_type);
1714 }
1715 
1716 ///////////////////////////////////////////////////////////
1717 // Operation                                             //
1718 ///////////////////////////////////////////////////////////
1719 template <typename T>
Operation(OpType op_type,std::unique_ptr<ValueRef<T>> && operand1,std::unique_ptr<ValueRef<T>> && operand2)1720 Operation<T>::Operation(OpType op_type,
1721                         std::unique_ptr<ValueRef<T>>&& operand1,
1722                         std::unique_ptr<ValueRef<T>>&& operand2) :
1723     m_op_type(op_type)
1724 {
1725     if (operand1)
1726         m_operands.push_back(std::move(operand1));
1727     if (operand2)
1728         m_operands.push_back(std::move(operand2));
1729     DetermineIfConstantExpr();
1730     CacheConstValue();
1731 }
1732 
1733 template <typename T>
Operation(OpType op_type,std::unique_ptr<ValueRef<T>> && operand)1734 Operation<T>::Operation(OpType op_type, std::unique_ptr<ValueRef<T>>&& operand) :
1735     m_op_type(op_type)
1736 {
1737     if (operand)
1738         m_operands.push_back(std::move(operand));
1739     DetermineIfConstantExpr();
1740     CacheConstValue();
1741 }
1742 
1743 template <typename T>
Operation(OpType op_type,std::vector<std::unique_ptr<ValueRef<T>>> && operands)1744 Operation<T>::Operation(OpType op_type, std::vector<std::unique_ptr<ValueRef<T>>>&& operands) :
1745     m_op_type(op_type),
1746     m_operands(std::move(operands))
1747 {
1748     DetermineIfConstantExpr();
1749     CacheConstValue();
1750 }
1751 
1752 template <typename T>
DetermineIfConstantExpr()1753 void Operation<T>::DetermineIfConstantExpr()
1754 {
1755     if (m_op_type == RANDOM_UNIFORM || m_op_type == RANDOM_PICK) {
1756         m_constant_expr = false;
1757         return;
1758     }
1759 
1760     m_constant_expr = true; // may be overridden...
1761 
1762     for (auto& operand : m_operands) {
1763         if (operand && !operand->ConstantExpr()) {
1764             m_constant_expr = false;
1765             return;
1766         }
1767     }
1768 }
1769 
1770 template <typename T>
CacheConstValue()1771 void Operation<T>::CacheConstValue()
1772 {
1773     if (!m_constant_expr)
1774         return;
1775 
1776     m_cached_const_value = this->EvalImpl(ScriptingContext());
1777 }
1778 
1779 template <typename T>
1780 bool Operation<T>::operator==(const ValueRef<T>& rhs) const
1781 {
1782     if (&rhs == this)
1783         return true;
1784     if (typeid(rhs) != typeid(*this))
1785         return false;
1786     const Operation<T>& rhs_ = static_cast<const Operation<T>&>(rhs);
1787 
1788     if (m_operands == rhs_.m_operands)
1789         return true;
1790 
1791     if (m_operands.size() != rhs_.m_operands.size())
1792         return false;
1793 
1794     for (unsigned int i = 0; i < m_operands.size(); ++i) {
1795         if (m_operands[i] != rhs_.m_operands[i])
1796             return false;
1797         if (m_operands[i] && *(m_operands[i]) != *(rhs_.m_operands[i]))
1798             return false;
1799     }
1800 
1801     // should be redundant...
1802     if (m_constant_expr != rhs_.m_constant_expr)
1803         return false;
1804 
1805     return true;
1806 }
1807 
1808 template <typename T>
GetOpType()1809 OpType Operation<T>::GetOpType() const
1810 { return m_op_type; }
1811 
1812 template <typename T>
LHS()1813 const ValueRef<T>* Operation<T>::LHS() const
1814 {
1815     if (m_operands.empty())
1816         return nullptr;
1817     return m_operands[0].get();
1818 }
1819 
1820 template <typename T>
RHS()1821 const ValueRef<T>* Operation<T>::RHS() const
1822 {
1823     if (m_operands.size() < 2)
1824         return nullptr;
1825     return m_operands[1].get();
1826 }
1827 
1828 template <typename T>
Operands()1829 const std::vector<ValueRef<T>*> Operation<T>::Operands() const
1830 {
1831     std::vector<ValueRef<T>*> retval(m_operands.size());
1832     std::transform(m_operands.begin(), m_operands.end(), retval.begin(),
1833                    [](const auto& xx){ return xx.get(); });
1834     return retval;
1835 }
1836 
1837 template <typename T>
Eval(const ScriptingContext & context)1838 T Operation<T>::Eval(const ScriptingContext& context) const
1839 {
1840     if (m_constant_expr)
1841         return m_cached_const_value;
1842     return this->EvalImpl(context);
1843 }
1844 
1845 template <typename T>
EvalImpl(const ScriptingContext & context)1846 T Operation<T>::EvalImpl(const ScriptingContext& context) const
1847 {
1848     switch (m_op_type) {
1849     case TIMES: {
1850         // useful for writing a "Statistic If" expression with arbitrary types.
1851         // If returns T(0) or T(1) for nothing or something matching the
1852         // sampling condition. This can be checked here by returning T(0) if
1853         // the LHS operand is T(0) and just returning RHS() otherwise.
1854         if (!LHS()->Eval(context))
1855             return T(0);
1856         return RHS()->Eval(context);
1857         break;
1858     }
1859 
1860     case MAXIMUM:
1861     case MINIMUM: {
1862         // evaluate all operands, return smallest or biggest
1863         std::set<T> vals;
1864         for (auto& vr : m_operands) {
1865             if (vr)
1866                 vals.insert(vr->Eval(context));
1867         }
1868         if (m_op_type == MINIMUM)
1869             return vals.empty() ? T(-1) : *vals.begin();
1870         else
1871             return vals.empty() ? T(-1) : *vals.rbegin();
1872         break;
1873     }
1874 
1875     case RANDOM_PICK: {
1876         // select one operand, evaluate it, return result
1877         if (m_operands.empty())
1878             return T(-1);   // should be INVALID_T of enum types
1879         unsigned int idx = RandSmallInt(0, m_operands.size() - 1);
1880         auto& vr = *std::next(m_operands.begin(), idx);
1881         if (!vr)
1882             return T(-1);   // should be INVALID_T of enum types
1883         return vr->Eval(context);
1884         break;
1885     }
1886 
1887     case COMPARE_EQUAL:
1888     case COMPARE_GREATER_THAN:
1889     case COMPARE_GREATER_THAN_OR_EQUAL:
1890     case COMPARE_LESS_THAN:
1891     case COMPARE_LESS_THAN_OR_EQUAL:
1892     case COMPARE_NOT_EQUAL: {
1893         const T&& lhs_val = LHS()->Eval(context);
1894         const T&& rhs_val = RHS()->Eval(context);
1895         bool test_result = false;
1896         switch (m_op_type) {
1897             case COMPARE_EQUAL:                 test_result = lhs_val == rhs_val;   break;
1898             case COMPARE_GREATER_THAN:          test_result = lhs_val > rhs_val;    break;
1899             case COMPARE_GREATER_THAN_OR_EQUAL: test_result = lhs_val >= rhs_val;   break;
1900             case COMPARE_LESS_THAN:             test_result = lhs_val < rhs_val;    break;
1901             case COMPARE_LESS_THAN_OR_EQUAL:    test_result = lhs_val <= rhs_val;   break;
1902             case COMPARE_NOT_EQUAL:             test_result = lhs_val != rhs_val;   break;
1903             default:    break;  // ??? do nothing, default to false
1904         }
1905         if (m_operands.size() < 3) {
1906             return T(1);
1907         } else if (m_operands.size() < 4) {
1908             if (test_result)
1909                 return m_operands[2]->Eval(context);
1910             else
1911                 return T(0);
1912         } else {
1913             if (test_result)
1914                 return m_operands[2]->Eval(context);
1915             else
1916                 return m_operands[3]->Eval(context);
1917         }
1918         break;
1919     }
1920 
1921     default:
1922         break;
1923     }
1924 
1925     throw std::runtime_error("ValueRef::Operation<T>::EvalImpl evaluated with an unknown or invalid OpType.");
1926 }
1927 
1928 template <typename T>
GetCheckSum()1929 unsigned int Operation<T>::GetCheckSum() const
1930 {
1931     unsigned int retval{0};
1932 
1933     CheckSums::CheckSumCombine(retval, "ValueRef::Operation");
1934     CheckSums::CheckSumCombine(retval, m_op_type);
1935     CheckSums::CheckSumCombine(retval, m_operands);
1936     CheckSums::CheckSumCombine(retval, m_constant_expr);
1937     CheckSums::CheckSumCombine(retval, m_cached_const_value);
1938     TraceLogger() << "GetCheckSum(Operation<T>): " << typeid(*this).name() << " retval: " << retval;
1939     return retval;
1940 }
1941 
1942 template <>
1943 FO_COMMON_API std::string Operation<std::string>::EvalImpl(const ScriptingContext& context) const;
1944 
1945 template <>
1946 FO_COMMON_API double Operation<double>::EvalImpl(const ScriptingContext& context) const;
1947 
1948 template <>
1949 FO_COMMON_API int Operation<int>::EvalImpl(const ScriptingContext& context) const;
1950 
1951 template <typename T>
RootCandidateInvariant()1952 bool Operation<T>::RootCandidateInvariant() const
1953 {
1954     if (m_op_type == RANDOM_UNIFORM || m_op_type == RANDOM_PICK)
1955         return false;
1956     for (auto& operand : m_operands) {
1957         if (operand && !operand->RootCandidateInvariant())
1958             return false;
1959     }
1960     return true;
1961 }
1962 
1963 template <typename T>
LocalCandidateInvariant()1964 bool Operation<T>::LocalCandidateInvariant() const
1965 {
1966     if (m_op_type == RANDOM_UNIFORM || m_op_type == RANDOM_PICK)
1967         return false;
1968     for (auto& operand : m_operands) {
1969         if (operand && !operand->LocalCandidateInvariant())
1970             return false;
1971     }
1972     return true;
1973 }
1974 
1975 template <typename T>
TargetInvariant()1976 bool Operation<T>::TargetInvariant() const
1977 {
1978     if (m_op_type == RANDOM_UNIFORM || m_op_type == RANDOM_PICK)
1979         return false;
1980     for (auto& operand : m_operands) {
1981         if (operand && !operand->TargetInvariant())
1982             return false;
1983     }
1984     return true;
1985 }
1986 
1987 template <typename T>
SourceInvariant()1988 bool Operation<T>::SourceInvariant() const
1989 {
1990     if (m_op_type == RANDOM_UNIFORM || m_op_type == RANDOM_PICK)
1991         return false;
1992     for (auto& operand : m_operands) {
1993         if (operand && !operand->SourceInvariant())
1994             return false;
1995     }
1996     return true;
1997 }
1998 
1999 template <typename T>
SimpleIncrement()2000 bool Operation<T>::SimpleIncrement() const
2001 {
2002     if (m_op_type != PLUS && m_op_type != MINUS)
2003         return false;
2004     if (m_operands.size() < 2 || !m_operands[0] || !m_operands[1])
2005         return false;
2006     // RHS must be the same value for all targets
2007     if (!(m_operands[1]->TargetInvariant()))
2008         return false;
2009     // LHS must be just the immediate value of what's being incremented
2010     const auto lhs = dynamic_cast<const Variable<T>*>(m_operands[0].get());
2011     if (!lhs)
2012         return false;
2013     return lhs->GetReferenceType() == EFFECT_TARGET_VALUE_REFERENCE;
2014 }
2015 
2016 template <typename T>
Description()2017 std::string Operation<T>::Description() const
2018 {
2019     if (m_op_type == NEGATE) {
2020         if (auto rhs = dynamic_cast<const Operation<T>*>(LHS())) {
2021             OpType op_type = rhs->GetOpType();
2022             if (op_type == PLUS     || op_type == MINUS ||
2023                 op_type == TIMES    || op_type == DIVIDE ||
2024                 op_type == NEGATE   || op_type == EXPONENTIATE)
2025             return "-(" + LHS()->Description() + ")";
2026         } else {
2027             return "-" + LHS()->Description();
2028         }
2029     }
2030 
2031     if (m_op_type == ABS)
2032         return "abs(" + LHS()->Description() + ")";
2033     if (m_op_type == LOGARITHM)
2034         return "log(" + LHS()->Description() + ")";
2035     if (m_op_type == SINE)
2036         return "sin(" + LHS()->Description() + ")";
2037     if (m_op_type == COSINE)
2038         return "cos(" + LHS()->Description() + ")";
2039 
2040     if (m_op_type == MINIMUM) {
2041         std::string retval = "min(";
2042         for (auto it = m_operands.begin(); it != m_operands.end(); ++it) {
2043             if (it != m_operands.begin())
2044                 retval += ", ";
2045             retval += (*it)->Description();
2046         }
2047         retval += ")";
2048         return retval;
2049     }
2050     if (m_op_type == MAXIMUM) {
2051         std::string retval = "max(";
2052         for (auto it = m_operands.begin(); it != m_operands.end(); ++it) {
2053             if (it != m_operands.begin())
2054                 retval += ", ";
2055             retval += (*it)->Description();
2056         }
2057         retval += ")";
2058         return retval;
2059     }
2060 
2061     if (m_op_type == RANDOM_UNIFORM)
2062         return "RandomNumber(" + LHS()->Description() + ", " + RHS()->Description() + ")";
2063 
2064     if (m_op_type == RANDOM_PICK) {
2065         std::string retval = "OneOf(";
2066         for (auto it = m_operands.begin(); it != m_operands.end(); ++it) {
2067             if (it != m_operands.begin())
2068                 retval += ", ";
2069             retval += (*it)->Description();
2070         }
2071         retval += ")";
2072         return retval;
2073     }
2074 
2075     if (m_op_type == ROUND_NEAREST)
2076         return "round(" + LHS()->Description() + ")";
2077     if (m_op_type == ROUND_UP)
2078         return "ceil(" + LHS()->Description() + ")";
2079     if (m_op_type == ROUND_DOWN)
2080         return "floor(" + LHS()->Description() + ")";
2081 
2082     bool parenthesize_lhs = false;
2083     bool parenthesize_rhs = false;
2084     if (auto lhs = dynamic_cast<const Operation<T>*>(LHS())) {
2085         OpType op_type = lhs->GetOpType();
2086         if (
2087             (m_op_type == EXPONENTIATE &&
2088              (op_type == EXPONENTIATE   || op_type == TIMES     || op_type == DIVIDE ||
2089               op_type == PLUS           || op_type == MINUS     || op_type == NEGATE)
2090             ) ||
2091             (((m_op_type == TIMES        || m_op_type == DIVIDE) &&
2092               (op_type == PLUS           || op_type == MINUS))    || op_type == NEGATE)
2093            )
2094             parenthesize_lhs = true;
2095     }
2096     if (auto rhs = dynamic_cast<const Operation<T>*>(RHS())) {
2097         OpType op_type = rhs->GetOpType();
2098         if (
2099             (m_op_type == EXPONENTIATE &&
2100              (op_type == EXPONENTIATE   || op_type == TIMES     || op_type == DIVIDE ||
2101               op_type == PLUS           || op_type == MINUS     || op_type == NEGATE)
2102             ) ||
2103             (((m_op_type == TIMES        || m_op_type == DIVIDE) &&
2104               (op_type == PLUS           || op_type == MINUS))    || op_type == NEGATE)
2105            )
2106             parenthesize_rhs = true;
2107     }
2108 
2109     std::string retval;
2110     if (parenthesize_lhs)
2111         retval += '(' + LHS()->Description() + ')';
2112     else
2113         retval += LHS()->Description();
2114 
2115     switch (m_op_type) {
2116     case PLUS:          retval += " + "; break;
2117     case MINUS:         retval += " - "; break;
2118     case TIMES:         retval += " * "; break;
2119     case DIVIDE:        retval += " / "; break;
2120     case EXPONENTIATE:  retval += " ^ "; break;
2121     default:            retval += " ? "; break;
2122     }
2123 
2124     if (parenthesize_rhs)
2125         retval += '(' + RHS()->Description() + ')';
2126     else
2127         retval += RHS()->Description();
2128 
2129     return retval;
2130 }
2131 
2132 template <typename T>
Dump(unsigned short ntabs)2133 std::string Operation<T>::Dump(unsigned short ntabs) const
2134 {
2135     if (m_op_type == NEGATE) {
2136         if (auto rhs = dynamic_cast<const Operation<T>*>(LHS())) {
2137             OpType op_type = rhs->GetOpType();
2138             if (op_type == PLUS     || op_type == MINUS ||
2139                 op_type == TIMES    || op_type == DIVIDE ||
2140                 op_type == NEGATE   || op_type == EXPONENTIATE)
2141             return "-(" + LHS()->Dump(ntabs) + ")";
2142         } else {
2143             return "-" + LHS()->Dump(ntabs);
2144         }
2145     }
2146 
2147     if (m_op_type == ABS)
2148         return "abs(" + LHS()->Dump(ntabs) + ")";
2149     if (m_op_type == LOGARITHM)
2150         return "log(" + LHS()->Dump(ntabs) + ")";
2151     if (m_op_type == SINE)
2152         return "sin(" + LHS()->Dump(ntabs) + ")";
2153     if (m_op_type == COSINE)
2154         return "cos(" + LHS()->Dump(ntabs) + ")";
2155 
2156     if (m_op_type == MINIMUM) {
2157         std::string retval = "min(";
2158         for (auto it = m_operands.begin(); it != m_operands.end(); ++it) {
2159             if (it != m_operands.begin())
2160                 retval += ", ";
2161             retval += (*it)->Dump(ntabs);
2162         }
2163         retval += ")";
2164         return retval;
2165     }
2166     if (m_op_type == MAXIMUM) {
2167         std::string retval = "max(";
2168         for (auto it = m_operands.begin(); it != m_operands.end(); ++it) {
2169             if (it != m_operands.begin())
2170                 retval += ", ";
2171             retval += (*it)->Dump(ntabs);
2172         }
2173         retval += ")";
2174         return retval;
2175     }
2176 
2177     if (m_op_type == RANDOM_UNIFORM)
2178         return "random(" + LHS()->Dump(ntabs) + ", " + LHS()->Dump(ntabs) + ")";
2179 
2180     if (m_op_type == RANDOM_PICK) {
2181         std::string retval = "randompick(";
2182         for (auto it = m_operands.begin(); it != m_operands.end(); ++it) {
2183             if (it != m_operands.begin())
2184                 retval += ", ";
2185             retval += (*it)->Dump(ntabs);
2186         }
2187         retval += ")";
2188         return retval;
2189     }
2190 
2191     if (m_op_type == ROUND_NEAREST)
2192         return "round(" + LHS()->Dump(ntabs) + ")";
2193     if (m_op_type == ROUND_UP)
2194         return "ceil(" + LHS()->Dump(ntabs) + ")";
2195     if (m_op_type == ROUND_DOWN)
2196         return "floor(" + LHS()->Dump(ntabs) + ")";
2197 
2198     bool parenthesize_lhs = false;
2199     bool parenthesize_rhs = false;
2200     if (auto lhs = dynamic_cast<const Operation<T>*>(LHS())) {
2201         OpType op_type = lhs->GetOpType();
2202         if (
2203             (m_op_type == EXPONENTIATE &&
2204              (op_type == EXPONENTIATE   || op_type == TIMES     || op_type == DIVIDE ||
2205               op_type == PLUS           || op_type == MINUS     || op_type == NEGATE)
2206             ) ||
2207             (((m_op_type == TIMES        || m_op_type == DIVIDE) &&
2208               (op_type == PLUS           || op_type == MINUS))    || op_type == NEGATE)
2209            )
2210             parenthesize_lhs = true;
2211     }
2212     if (auto rhs = dynamic_cast<const Operation<T>*>(RHS())) {
2213         OpType op_type = rhs->GetOpType();
2214         if (
2215             (m_op_type == EXPONENTIATE &&
2216              (op_type == EXPONENTIATE   || op_type == TIMES     || op_type == DIVIDE ||
2217               op_type == PLUS           || op_type == MINUS     || op_type == NEGATE)
2218             ) ||
2219             (((m_op_type == TIMES        || m_op_type == DIVIDE) &&
2220               (op_type == PLUS           || op_type == MINUS))    || op_type == NEGATE)
2221            )
2222             parenthesize_rhs = true;
2223     }
2224 
2225     std::string retval;
2226     if (parenthesize_lhs)
2227         retval += '(' + LHS()->Dump(ntabs) + ')';
2228     else
2229         retval += LHS()->Dump(ntabs);
2230 
2231     switch (m_op_type) {
2232     case PLUS:          retval += " + "; break;
2233     case MINUS:         retval += " - "; break;
2234     case TIMES:         retval += " * "; break;
2235     case DIVIDE:        retval += " / "; break;
2236     case EXPONENTIATE:  retval += " ^ "; break;
2237     default:            retval += " ? "; break;
2238     }
2239 
2240     if (parenthesize_rhs)
2241         retval += '(' + RHS()->Dump(ntabs) + ')';
2242     else
2243         retval += RHS()->Dump(ntabs);
2244 
2245     return retval;
2246 }
2247 
2248 template <typename T>
SetTopLevelContent(const std::string & content_name)2249 void Operation<T>::SetTopLevelContent(const std::string& content_name) {
2250     for (auto& operand : m_operands) {
2251         if (operand)
2252             operand->SetTopLevelContent(content_name);
2253     }
2254 }
2255 
2256 template <typename T>
2257 template <typename Archive>
serialize(Archive & ar,const unsigned int version)2258 void Operation<T>::serialize(Archive& ar, const unsigned int version)
2259 {
2260     ar  & BOOST_SERIALIZATION_BASE_OBJECT_NVP(ValueRef)
2261         & BOOST_SERIALIZATION_NVP(m_op_type)
2262         & BOOST_SERIALIZATION_NVP(m_operands)
2263         & BOOST_SERIALIZATION_NVP(m_constant_expr)
2264         & BOOST_SERIALIZATION_NVP(m_cached_const_value);
2265 }
2266 
2267 } // namespace ValueRef
2268 
2269 #endif // _ValueRefs_h_
2270