C4Script: Accept array parameter for Min, Max

Min/Max with array parameter will return the smallest/largest value of
all elements of the array. If any array element is not an integer, nor
convertible to integer, the function will fail.
objectmenu
Nicolas Hake 2016-01-23 13:45:22 +01:00
parent b7cffa5e82
commit 4fddda20f9
5 changed files with 150 additions and 8 deletions

View File

@ -23,11 +23,29 @@
</params>
</syntax>
<desc>Returns the greater of two values.</desc>
</func>
<func>
<title>Max</title>
<category>Arithmetics</category>
<version>7.0 OC</version>
<syntax>
<rtype>int</rtype>
<params>
<param>
<type>array</type>
<name>values</name>
<desc>array of integers</desc>
</param>
</params>
</syntax>
<desc>
Returns the largest value from an array. All array elements must be
integers (or convertible to integer).
</desc>
<related>
<funclink>Min</funclink>
<funclink>Inside</funclink>
<funclink>BoundBy</funclink>
</related>
</func>
<author>PeterW</author><date>2001-12</date>
</funcs>

View File

@ -23,11 +23,30 @@
</params>
</syntax>
<desc>Returns the lesser of two values.</desc>
</func>
<func>
<title>Min</title>
<category>Arithmetics</category>
<version>7.0 OC</version>
<syntax>
<rtype>int</rtype>
<params>
<param>
<type>array</type>
<name>values</name>
<desc>array of integers</desc>
</param>
</params>
</syntax>
<desc>
Returns the smallest value from an array. All array elements must be
integers (or convertible to integer).
</desc>
<related>
<funclink>Max</funclink>
<funclink>Inside</funclink>
<funclink>BoundBy</funclink>
</related>
</func>
<author>PeterW</author><date>2001-12</date>
</funcs>

View File

@ -47,6 +47,7 @@ class Nillable
public:
inline Nillable(const T &value) : _nil(!value && !C4Value::IsNullableType(C4ValueConv<T>::Type())), _val(value) {}
inline Nillable() : _nil(true), _val(T()) {}
inline Nillable(std::nullptr_t) : _nil(true), _val(T()) {}
template <typename T2> inline Nillable(const Nillable<T2> & n2) : _nil(n2._nil), _val(n2._val) {}
inline Nillable(const Nillable<void> & n2) : _nil(true), _val(T()) {}
inline bool IsNil() const { return _nil; }

View File

@ -458,14 +458,56 @@ static long FnArcCos(C4PropList * _this, long iVal, long iRadius)
return (long) floor(f1+0.5);
}
static long FnMin(C4PropList * _this, long iVal1, long iVal2)
static std::pair<Nillable<int32_t>, Nillable<int32_t>> minmax(const char *func, const C4Value &a_val, const Nillable<int32_t> &b_opt)
{
return std::min(iVal1,iVal2);
if (a_val.CheckConversion(C4V_Int))
{
int32_t a = a_val.getInt();
int32_t b = b_opt;
if (a > b)
std::swap(a, b);
return std::make_pair(a, b);
}
else if (a_val.CheckConversion(C4V_Array))
{
const C4ValueArray *a = a_val.getArray();
if (a->GetSize() == 0)
return std::make_pair(nullptr, nullptr);
if (!a->GetItem(0).CheckConversion(C4V_Int))
{
throw C4AulExecError(FormatString("%s: argument 1 must be int or array-of-int, but element %d of array is of type %s", func, 0, a->GetItem(0).GetTypeName()).getData());
}
int32_t min, max;
min = max = a->GetItem(0).getInt();
for (int32_t i = 1; i < a->GetSize(); ++i)
{
if (!a->GetItem(i).CheckConversion(C4V_Int))
{
throw C4AulExecError(FormatString("%s: argument 1 must be int or array-of-int, but element %d of array is of type %s", func, i, a->GetItem(i).GetTypeName()).getData());
}
int32_t value = a->GetItem(i).getInt();
min = std::min(min, value);
max = std::max(max, value);
}
return std::make_pair(min, max);
}
else
{
throw C4AulExecError(FormatString("%s: argument 1 must be int or array-of-int, but is of type %s", func, a_val.GetTypeName()).getData());
}
}
static long FnMax(C4PropList * _this, long iVal1, long iVal2)
static Nillable<int32_t> FnMin(C4PropList * _this, const C4Value &a, Nillable<int32_t> b)
{
return std::max(iVal1,iVal2);
return minmax("Min", a, b).first;
}
static Nillable<int32_t> FnMax(C4PropList * _this, const C4Value &a, Nillable<int32_t> b)
{
return minmax("Max", a, b).second;
}
static long FnDistance(C4PropList * _this, long iX1, long iY1, long iX2, long iY2)

View File

@ -38,8 +38,9 @@ TEST_F(AulPredefFunctionTest, Translate)
EXPECT_EQ(C4VString("a"), RunExpr("Translate(\"a\")"));
}
TEST_F(AulPredefFunctionTest, Min)
TEST_F(AulPredefFunctionTest, Min_int_int)
{
// Test that Min(int, int) returns the smaller of two values.
EXPECT_EQ(C4VInt(0), RunExpr("Min(0, 1)"));
EXPECT_EQ(C4VInt(0), RunExpr("Min(1, 0)"));
@ -53,8 +54,39 @@ TEST_F(AulPredefFunctionTest, Min)
EXPECT_EQ(C4VInt(0), RunExpr("Min(0, 2147483647)"));
}
TEST_F(AulPredefFunctionTest, Max)
TEST_F(AulPredefFunctionTest, Min_array)
{
// Test that Min(array<int>) returns the smallest array item.
EXPECT_EQ(C4VNull, RunExpr("Min([])"));
EXPECT_EQ(C4VInt(0), RunExpr("Min([0, 1, 2])"));
EXPECT_EQ(C4VInt(0), RunExpr("Min([2, 1, 0])"));
EXPECT_EQ(C4VInt(-1), RunExpr("Min([-1, 0, 1])"));
EXPECT_EQ(C4VInt(-1), RunExpr("Min([1, 0, -1])"));
EXPECT_EQ(C4VINT_MIN, RunExpr("Min([-2147483648, 0, 2147483647])"));
EXPECT_EQ(C4VINT_MIN, RunExpr("Min([2147483647, 0, -2147483648])"));
}
TEST_F(AulPredefFunctionTest, Min_Invalid)
{
// Test that Min(array) fails if any item is not (convertible to) int.
EXPECT_THROW(RunExpr("Min([[]])"), C4AulExecError);
EXPECT_THROW(RunExpr("Min([0, []])"), C4AulExecError);
EXPECT_THROW(RunExpr("Min([0, 1, {}])"), C4AulExecError);
EXPECT_THROW(RunExpr(R"(Min([0, ""]))"), C4AulExecError);
// Test that Min(a, b) fails if a or b are not (convertible to) int.
EXPECT_THROW(RunExpr("Min({}, 1)"), C4AulExecError);
EXPECT_THROW(RunExpr("Min(0, {})"), C4AulExecError);
EXPECT_THROW(RunExpr(R"(Min("", 1))"), C4AulExecError);
EXPECT_THROW(RunExpr(R"(Min(0, ""))"), C4AulExecError);
}
TEST_F(AulPredefFunctionTest, Max_int_int)
{
// Test that Max(int, int) returns the larger of two values.
EXPECT_EQ(C4VInt(1), RunExpr("Max(0, 1)"));
EXPECT_EQ(C4VInt(1), RunExpr("Max(1, 0)"));
@ -67,3 +99,33 @@ TEST_F(AulPredefFunctionTest, Max)
EXPECT_EQ(C4VINT_MAX, RunExpr("Max(2147483647, 0)"));
EXPECT_EQ(C4VINT_MAX, RunExpr("Max(0, 2147483647)"));
}
TEST_F(AulPredefFunctionTest, Max_array)
{
// Test that Max(array<int>) returns the largest array item.
EXPECT_EQ(C4VNull, RunExpr("Max([])"));
EXPECT_EQ(C4VInt(2), RunExpr("Max([0, 1, 2])"));
EXPECT_EQ(C4VInt(2), RunExpr("Max([2, 1, 0])"));
EXPECT_EQ(C4VInt(1), RunExpr("Max([-1, 0, 1])"));
EXPECT_EQ(C4VInt(1), RunExpr("Max([1, 0, -1])"));
EXPECT_EQ(C4VINT_MAX, RunExpr("Max([-2147483648, 0, 2147483647])"));
EXPECT_EQ(C4VINT_MAX, RunExpr("Max([2147483647, 0, -2147483648])"));
}
TEST_F(AulPredefFunctionTest, Max_Invalid)
{
// Test that Max(array) fails if any item is not (convertible to) int.
EXPECT_THROW(RunExpr("Max([[]])"), C4AulExecError);
EXPECT_THROW(RunExpr("Max([0, []])"), C4AulExecError);
EXPECT_THROW(RunExpr("Max([0, 1, {}])"), C4AulExecError);
EXPECT_THROW(RunExpr(R"(Max([0, ""]))"), C4AulExecError);
// Test that Max(a, b) fails if a or b are not (convertible to) int.
EXPECT_THROW(RunExpr("Max({}, 1)"), C4AulExecError);
EXPECT_THROW(RunExpr("Max(0, {})"), C4AulExecError);
EXPECT_THROW(RunExpr(R"(Max("", 1))"), C4AulExecError);
EXPECT_THROW(RunExpr(R"(Max(0, ""))"), C4AulExecError);
}