llvm-project/flang/unittests/Runtime/Transformational.cpp
Tarun Prabhu bef2bb34bf [flang] Lowering and runtime support for F08 transformational intrinsics: BESSEL_JN and BESSEL_YN
The runtime implementation uses the recurrence relations

`J(n-1, x) = (2.0 / x) * n * J(n, x) - J(n+1, x)`
`Y(n+1, x) = (2.0 / x) * n * Y(n, x) - Y(n-1, x)`

(see https://dlmf.nist.gov/10.74.iv and https://dlmf.nist.gov/10.6.E1).

Although the standard requires that `N1` and `N2` in `BESSEL_JN(N1, N2, x)`
and `BESSEL_YN(N1, N2, x)` be non-negative, this is not checked in the
runtime functions. This is in keeping with some other compilers which also
return some results when `N1` and/or `N2` are negative.

The special case for `x == 0` is  handled in different runtime functions
for each of `BESSEL_JN` and `BESSEL_YN`. The lowering code checks for this
case and inserts the checks and the appropriate runtime calls in FIR.

The existing tests for the two intrinsics was modified to keep the style
consistent with the additional lowering tests that were added.
2022-12-19 07:59:38 -07:00

553 lines
21 KiB
C++

//===-- flang/unittests/Runtime/Transformational.cpp ----------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "flang/Runtime/transformational.h"
#include "gtest/gtest.h"
#include "tools.h"
#include "flang/Runtime/type-code.h"
#include <vector>
using namespace Fortran::runtime;
using Fortran::common::TypeCategory;
template <int KIND>
using BesselFuncType = std::function<void(Descriptor &, int32_t, int32_t,
CppTypeFor<TypeCategory::Real, KIND>, CppTypeFor<TypeCategory::Real, KIND>,
CppTypeFor<TypeCategory::Real, KIND>, const char *, int)>;
template <int KIND>
using BesselX0FuncType =
std::function<void(Descriptor &, int32_t, int32_t, const char *, int)>;
template <int KIND>
constexpr CppTypeFor<TypeCategory::Real, KIND>
besselEpsilon = CppTypeFor<TypeCategory::Real, KIND>(1e-4);
template <int KIND>
static void testBesselJn(BesselFuncType<KIND> rtFunc, int32_t n1, int32_t n2,
CppTypeFor<TypeCategory::Real, KIND> x,
const std::vector<CppTypeFor<TypeCategory::Real, KIND>> &expected) {
StaticDescriptor<1> desc;
Descriptor &result{desc.descriptor()};
unsigned len = expected.size();
CppTypeFor<TypeCategory::Real, KIND> anc0 = len > 0 ? expected[len - 1] : 0.0;
CppTypeFor<TypeCategory::Real, KIND> anc1 = len > 1 ? expected[len - 2] : 0.0;
rtFunc(result, n1, n2, x, anc0, anc1, __FILE__, __LINE__);
EXPECT_EQ(result.type(), (TypeCode{TypeCategory::Real, KIND}));
EXPECT_EQ(result.rank(), 1);
EXPECT_EQ(
result.ElementBytes(), sizeof(CppTypeFor<TypeCategory::Real, KIND>));
EXPECT_EQ(result.GetDimension(0).LowerBound(), 1);
EXPECT_EQ(result.GetDimension(0).Extent(), len);
for (size_t j{0}; j < len; ++j) {
EXPECT_NEAR(
(*result.ZeroBasedIndexedElement<CppTypeFor<TypeCategory::Real, KIND>>(
j)),
expected[j], besselEpsilon<KIND>);
}
}
template <int KIND>
static void testBesselJnX0(
BesselX0FuncType<KIND> rtFunc, int32_t n1, int32_t n2) {
StaticDescriptor<1> desc;
Descriptor &result{desc.descriptor()};
rtFunc(result, n1, n2, __FILE__, __LINE__);
EXPECT_EQ(result.type(), (TypeCode{TypeCategory::Real, KIND}));
EXPECT_EQ(result.rank(), 1);
EXPECT_EQ(result.GetDimension(0).LowerBound(), 1);
EXPECT_EQ(result.GetDimension(0).Extent(), n2 >= n1 ? n2 - n1 + 1 : 0);
if (n2 < n1) {
return;
}
EXPECT_NEAR(
(*result.ZeroBasedIndexedElement<CppTypeFor<TypeCategory::Real, KIND>>(
0)),
(n1 == 0) ? 1.0 : 0.0, 1e-5);
for (int j{1}; j < (n2 - n1 + 1); ++j) {
EXPECT_NEAR(
(*result.ZeroBasedIndexedElement<CppTypeFor<TypeCategory::Real, KIND>>(
j)),
0.0, besselEpsilon<KIND>);
}
}
template <int KIND> static void testBesselJn(BesselFuncType<KIND> rtFunc) {
testBesselJn<KIND>(rtFunc, 1, 0, 1.0, {});
testBesselJn<KIND>(rtFunc, 0, 0, 1.0, {0.765197694});
testBesselJn<KIND>(rtFunc, 0, 1, 1.0, {0.765197694, 0.440050572});
testBesselJn<KIND>(
rtFunc, 0, 2, 1.0, {0.765197694, 0.440050572, 0.114903487});
testBesselJn<KIND>(rtFunc, 1, 5, 5.0,
{-0.327579111, 0.046565145, 0.364831239, 0.391232371, 0.261140555});
}
template <int KIND> static void testBesselJnX0(BesselX0FuncType<KIND> rtFunc) {
testBesselJnX0<KIND>(rtFunc, 1, 0);
testBesselJnX0<KIND>(rtFunc, 0, 0);
testBesselJnX0<KIND>(rtFunc, 1, 1);
testBesselJnX0<KIND>(rtFunc, 0, 3);
testBesselJnX0<KIND>(rtFunc, 1, 4);
}
static void testBesselJn() {
testBesselJn<4>(RTNAME(BesselJn_4));
testBesselJn<8>(RTNAME(BesselJn_8));
#if LDBL_MANT_DIG == 64
testBesselJn<10>(RTNAME(BesselJn_10));
#endif
#if LDBL_MANT_DIG == 113 || HAS_FLOAT128
testBesselJn<16>(RTNAME(BesselJn_16));
#endif
testBesselJnX0<4>(RTNAME(BesselJnX0_4));
testBesselJnX0<8>(RTNAME(BesselJnX0_8));
#if LDBL_MANT_DIG == 64
testBesselJnX0<10>(RTNAME(BesselJnX0_10));
#endif
#if LDBL_MANT_DIG == 113 || HAS_FLOAT128
testBesselJnX0<16>(RTNAME(BesselJnX0_16));
#endif
}
TEST(Transformational, BesselJn) { testBesselJn(); }
template <int KIND>
static void testBesselYn(BesselFuncType<KIND> rtFunc, int32_t n1, int32_t n2,
CppTypeFor<TypeCategory::Real, KIND> x,
const std::vector<CppTypeFor<TypeCategory::Real, KIND>> &expected) {
StaticDescriptor<1> desc;
Descriptor &result{desc.descriptor()};
unsigned len = expected.size();
CppTypeFor<TypeCategory::Real, KIND> anc0 = len > 0 ? expected[0] : 0.0;
CppTypeFor<TypeCategory::Real, KIND> anc1 = len > 1 ? expected[1] : 0.0;
rtFunc(result, n1, n2, x, anc0, anc1, __FILE__, __LINE__);
EXPECT_EQ(result.type(), (TypeCode{TypeCategory::Real, KIND}));
EXPECT_EQ(result.rank(), 1);
EXPECT_EQ(
result.ElementBytes(), sizeof(CppTypeFor<TypeCategory::Real, KIND>));
EXPECT_EQ(result.GetDimension(0).LowerBound(), 1);
EXPECT_EQ(result.GetDimension(0).Extent(), len);
for (size_t j{0}; j < len; ++j) {
EXPECT_NEAR(
(*result.ZeroBasedIndexedElement<CppTypeFor<TypeCategory::Real, KIND>>(
j)),
expected[j], besselEpsilon<KIND>);
}
}
template <int KIND>
static void testBesselYnX0(
BesselX0FuncType<KIND> rtFunc, int32_t n1, int32_t n2) {
StaticDescriptor<1> desc;
Descriptor &result{desc.descriptor()};
rtFunc(result, n1, n2, __FILE__, __LINE__);
EXPECT_EQ(result.type(), (TypeCode{TypeCategory::Real, KIND}));
EXPECT_EQ(result.rank(), 1);
EXPECT_EQ(result.GetDimension(0).LowerBound(), 1);
EXPECT_EQ(result.GetDimension(0).Extent(), n2 >= n1 ? n2 - n1 + 1 : 0);
if (n2 < n1) {
return;
}
for (int j{0}; j < (n2 - n1 + 1); ++j) {
EXPECT_EQ(
(*result.ZeroBasedIndexedElement<CppTypeFor<TypeCategory::Real, KIND>>(
j)),
(-std::numeric_limits<
CppTypeFor<TypeCategory::Real, KIND>>::infinity()));
}
}
template <int KIND> static void testBesselYn(BesselFuncType<KIND> rtFunc) {
testBesselYn<KIND>(rtFunc, 1, 0, 1.0, {});
testBesselYn<KIND>(rtFunc, 0, 0, 1.0, {0.08825695});
testBesselYn<KIND>(rtFunc, 0, 1, 1.0, {0.08825695, -0.7812128});
testBesselYn<KIND>(rtFunc, 0, 2, 1.0, {0.0882569555, -0.7812128, -1.6506826});
testBesselYn<KIND>(rtFunc, 1, 5, 1.0,
{-0.7812128, -1.6506826, -5.8215175, -33.278423, -260.40588});
}
template <int KIND> static void testBesselYnX0(BesselX0FuncType<KIND> rtFunc) {
testBesselYnX0<KIND>(rtFunc, 1, 0);
testBesselYnX0<KIND>(rtFunc, 0, 0);
testBesselYnX0<KIND>(rtFunc, 1, 1);
testBesselYnX0<KIND>(rtFunc, 0, 3);
testBesselYnX0<KIND>(rtFunc, 1, 4);
}
static void testBesselYn() {
testBesselYn<4>(RTNAME(BesselYn_4));
testBesselYn<8>(RTNAME(BesselYn_8));
#if LDBL_MANT_DIG == 64
testBesselYn<10>(RTNAME(BesselYn_10));
#endif
#if LDBL_MANT_DIG == 113 || HAS_FLOAT128
testBesselYn<16>(RTNAME(BesselYn_16));
#endif
testBesselYnX0<4>(RTNAME(BesselYnX0_4));
testBesselYnX0<8>(RTNAME(BesselYnX0_8));
#if LDBL_MANT_DIG == 64
testBesselYnX0<10>(RTNAME(BesselYnX0_10));
#endif
#if LDBL_MANT_DIG == 113 || HAS_FLOAT128
testBesselYnX0<16>(RTNAME(BesselYnX0_16));
#endif
}
TEST(Transformational, BesselYn) { testBesselYn(); }
TEST(Transformational, Shifts) {
// ARRAY 1 3 5
// 2 4 6
auto array{MakeArray<TypeCategory::Integer, 4>(
std::vector<int>{2, 3}, std::vector<std::int32_t>{1, 2, 3, 4, 5, 6})};
array->GetDimension(0).SetLowerBound(0); // shouldn't matter
array->GetDimension(1).SetLowerBound(-1);
StaticDescriptor<2, true> statDesc;
Descriptor &result{statDesc.descriptor()};
auto shift3{MakeArray<TypeCategory::Integer, 8>(
std::vector<int>{3}, std::vector<std::int64_t>{1, -1, 2})};
RTNAME(Cshift)(result, *array, *shift3, 1, __FILE__, __LINE__);
EXPECT_EQ(result.type(), array->type());
EXPECT_EQ(result.rank(), 2);
EXPECT_EQ(result.GetDimension(0).LowerBound(), 1);
EXPECT_EQ(result.GetDimension(0).Extent(), 2);
EXPECT_EQ(result.GetDimension(1).LowerBound(), 1);
EXPECT_EQ(result.GetDimension(1).Extent(), 3);
EXPECT_EQ(result.type(), (TypeCode{TypeCategory::Integer, 4}));
static std::int32_t cshiftExpect1[6]{2, 1, 4, 3, 5, 6};
for (int j{0}; j < 6; ++j) {
EXPECT_EQ(
*result.ZeroBasedIndexedElement<std::int32_t>(j), cshiftExpect1[j]);
}
result.Destroy();
auto shift2{MakeArray<TypeCategory::Integer, 1>(
std::vector<int>{2}, std::vector<std::int8_t>{1, -1})};
shift2->GetDimension(0).SetLowerBound(-1); // shouldn't matter
shift2->GetDimension(1).SetLowerBound(2);
RTNAME(Cshift)(result, *array, *shift2, 2, __FILE__, __LINE__);
EXPECT_EQ(result.type(), array->type());
EXPECT_EQ(result.rank(), 2);
EXPECT_EQ(result.GetDimension(0).LowerBound(), 1);
EXPECT_EQ(result.GetDimension(0).Extent(), 2);
EXPECT_EQ(result.GetDimension(1).LowerBound(), 1);
EXPECT_EQ(result.GetDimension(1).Extent(), 3);
EXPECT_EQ(result.type(), (TypeCode{TypeCategory::Integer, 4}));
static std::int32_t cshiftExpect2[6]{3, 6, 5, 2, 1, 4};
for (int j{0}; j < 6; ++j) {
EXPECT_EQ(
*result.ZeroBasedIndexedElement<std::int32_t>(j), cshiftExpect2[j]);
}
result.Destroy();
// VECTOR 1 3 5 2 4 6
auto vector{MakeArray<TypeCategory::Integer, 4>(
std::vector<int>{6}, std::vector<std::int32_t>{1, 2, 3, 4, 5, 6})};
vector->GetDimension(0).SetLowerBound(0);
StaticDescriptor<1, true> vectorDesc;
Descriptor &vectorResult{vectorDesc.descriptor()};
RTNAME(CshiftVector)(vectorResult, *vector, 2, __FILE__, __LINE__);
EXPECT_EQ(vectorResult.type(), array->type());
EXPECT_EQ(vectorResult.rank(), 1);
EXPECT_EQ(vectorResult.GetDimension(0).LowerBound(), 1);
EXPECT_EQ(vectorResult.GetDimension(0).Extent(), 6);
EXPECT_EQ(vectorResult.type(), (TypeCode{TypeCategory::Integer, 4}));
static std::int32_t cshiftExpect3[6]{3, 4, 5, 6, 1, 2};
for (int j{0}; j < 6; ++j) {
EXPECT_EQ(*vectorResult.ZeroBasedIndexedElement<std::int32_t>(j),
cshiftExpect3[j]);
}
vectorResult.Destroy();
RTNAME(CshiftVector)(vectorResult, *vector, -2, __FILE__, __LINE__);
EXPECT_EQ(vectorResult.type(), array->type());
EXPECT_EQ(vectorResult.rank(), 1);
EXPECT_EQ(vectorResult.GetDimension(0).LowerBound(), 1);
EXPECT_EQ(vectorResult.GetDimension(0).Extent(), 6);
EXPECT_EQ(vectorResult.type(), (TypeCode{TypeCategory::Integer, 4}));
static std::int32_t cshiftExpect4[6]{5, 6, 1, 2, 3, 4};
for (int j{0}; j < 6; ++j) {
EXPECT_EQ(*vectorResult.ZeroBasedIndexedElement<std::int32_t>(j),
cshiftExpect4[j]);
}
vectorResult.Destroy();
// VECTOR 1 3 5 2 4 6 WITH non zero lower bound in a negative cshift.
auto vectorWithLowerBounds{MakeArray<TypeCategory::Integer, 4>(
std::vector<int>{6}, std::vector<std::int32_t>{1, 2, 3, 4, 5, 6})};
vectorWithLowerBounds->GetDimension(0).SetLowerBound(2);
RTNAME(CshiftVector)
(vectorResult, *vectorWithLowerBounds, -2, __FILE__, __LINE__);
EXPECT_EQ(vectorResult.type(), array->type());
EXPECT_EQ(vectorResult.rank(), 1);
EXPECT_EQ(vectorResult.GetDimension(0).LowerBound(), 1);
EXPECT_EQ(vectorResult.GetDimension(0).Extent(), 6);
EXPECT_EQ(vectorResult.type(), (TypeCode{TypeCategory::Integer, 4}));
static std::int32_t cshiftExpect5[6]{5, 6, 1, 2, 3, 4};
for (int j{0}; j < 6; ++j) {
EXPECT_EQ(*vectorResult.ZeroBasedIndexedElement<std::int32_t>(j),
cshiftExpect5[j]);
}
vectorResult.Destroy();
auto boundary{MakeArray<TypeCategory::Integer, 4>(
std::vector<int>{3}, std::vector<std::int32_t>{-1, -2, -3})};
boundary->GetDimension(0).SetLowerBound(9); // shouldn't matter
RTNAME(Eoshift)(result, *array, *shift3, &*boundary, 1, __FILE__, __LINE__);
EXPECT_EQ(result.type(), array->type());
EXPECT_EQ(result.rank(), 2);
EXPECT_EQ(result.GetDimension(0).LowerBound(), 1);
EXPECT_EQ(result.GetDimension(0).Extent(), 2);
EXPECT_EQ(result.GetDimension(1).LowerBound(), 1);
EXPECT_EQ(result.GetDimension(1).Extent(), 3);
EXPECT_EQ(result.type(), (TypeCode{TypeCategory::Integer, 4}));
static std::int32_t eoshiftExpect1[6]{2, -1, -2, 3, -3, -3};
for (int j{0}; j < 6; ++j) {
EXPECT_EQ(
*result.ZeroBasedIndexedElement<std::int32_t>(j), eoshiftExpect1[j]);
}
result.Destroy();
// VECTOR EOSHIFT
StaticDescriptor<0> boundaryDescriptor;
Descriptor vectorBoundary{boundaryDescriptor.descriptor()};
std::int32_t boundaryValue{343};
vectorBoundary.Establish(TypeCategory::Integer, 4,
const_cast<void *>(reinterpret_cast<const void *>(&boundaryValue)), 0);
RTNAME(EoshiftVector)
(vectorResult, *vector, 2, &vectorBoundary, __FILE__, __LINE__);
EXPECT_EQ(vectorResult.type(), array->type());
EXPECT_EQ(vectorResult.rank(), 1);
EXPECT_EQ(vectorResult.GetDimension(0).LowerBound(), 1);
EXPECT_EQ(vectorResult.GetDimension(0).Extent(), 6);
EXPECT_EQ(vectorResult.type(), (TypeCode{TypeCategory::Integer, 4}));
static std::int32_t eoshiftVectorExpect[6]{3, 4, 5, 6, 343, 343};
for (int j{0}; j < 6; ++j) {
EXPECT_EQ(*vectorResult.ZeroBasedIndexedElement<std::int32_t>(j),
eoshiftVectorExpect[j]);
}
vectorResult.Destroy();
// VECTOR EOSHIFT on input with non zero lower bounds
RTNAME(EoshiftVector)
(vectorResult, *vectorWithLowerBounds, -2, &vectorBoundary, __FILE__,
__LINE__);
EXPECT_EQ(vectorResult.type(), array->type());
EXPECT_EQ(vectorResult.rank(), 1);
EXPECT_EQ(vectorResult.GetDimension(0).LowerBound(), 1);
EXPECT_EQ(vectorResult.GetDimension(0).Extent(), 6);
EXPECT_EQ(vectorResult.type(), (TypeCode{TypeCategory::Integer, 4}));
static std::int32_t eoshiftVectorExpect2[6]{343, 343, 1, 2, 3, 4};
for (int j{0}; j < 6; ++j) {
EXPECT_EQ(*vectorResult.ZeroBasedIndexedElement<std::int32_t>(j),
eoshiftVectorExpect2[j]);
}
vectorResult.Destroy();
}
TEST(Transformational, Pack) {
// ARRAY 1 3 5
// 2 4 6
auto array{MakeArray<TypeCategory::Integer, 4>(
std::vector<int>{2, 3}, std::vector<std::int32_t>{1, 2, 3, 4, 5, 6})};
array->GetDimension(0).SetLowerBound(2); // shouldn't matter
array->GetDimension(1).SetLowerBound(-1);
auto mask{MakeArray<TypeCategory::Logical, 1>(std::vector<int>{2, 3},
std::vector<std::uint8_t>{false, true, true, false, false, true})};
mask->GetDimension(0).SetLowerBound(0); // shouldn't matter
mask->GetDimension(1).SetLowerBound(2);
StaticDescriptor<1, true> statDesc;
Descriptor &result{statDesc.descriptor()};
RTNAME(Pack)(result, *array, *mask, nullptr, __FILE__, __LINE__);
EXPECT_EQ(result.type(), array->type());
EXPECT_EQ(result.rank(), 1);
EXPECT_EQ(result.GetDimension(0).LowerBound(), 1);
EXPECT_EQ(result.GetDimension(0).Extent(), 3);
static std::int32_t packExpect1[3]{2, 3, 6};
for (int j{0}; j < 3; ++j) {
EXPECT_EQ(*result.ZeroBasedIndexedElement<std::int32_t>(j), packExpect1[j])
<< " at " << j;
}
result.Destroy();
auto vector{MakeArray<TypeCategory::Integer, 4>(
std::vector<int>{5}, std::vector<std::int32_t>{-1, -2, -3, -4, -5})};
RTNAME(Pack)(result, *array, *mask, &*vector, __FILE__, __LINE__);
EXPECT_EQ(result.type(), array->type());
EXPECT_EQ(result.rank(), 1);
EXPECT_EQ(result.GetDimension(0).LowerBound(), 1);
EXPECT_EQ(result.GetDimension(0).Extent(), 5);
static std::int32_t packExpect2[5]{2, 3, 6, -4, -5};
for (int j{0}; j < 5; ++j) {
EXPECT_EQ(*result.ZeroBasedIndexedElement<std::int32_t>(j), packExpect2[j])
<< " at " << j;
}
result.Destroy();
}
TEST(Transformational, Spread) {
auto array{MakeArray<TypeCategory::Integer, 4>(
std::vector<int>{3}, std::vector<std::int32_t>{1, 2, 3})};
array->GetDimension(0).SetLowerBound(2); // shouldn't matter
StaticDescriptor<2, true> statDesc;
Descriptor &result{statDesc.descriptor()};
RTNAME(Spread)(result, *array, 1, 2, __FILE__, __LINE__);
EXPECT_EQ(result.type(), array->type());
EXPECT_EQ(result.rank(), 2);
EXPECT_EQ(result.GetDimension(0).LowerBound(), 1);
EXPECT_EQ(result.GetDimension(0).Extent(), 2);
EXPECT_EQ(result.GetDimension(1).LowerBound(), 1);
EXPECT_EQ(result.GetDimension(1).Extent(), 3);
for (int j{0}; j < 6; ++j) {
EXPECT_EQ(*result.ZeroBasedIndexedElement<std::int32_t>(j), 1 + j / 2);
}
result.Destroy();
RTNAME(Spread)(result, *array, 2, 2, __FILE__, __LINE__);
EXPECT_EQ(result.type(), array->type());
EXPECT_EQ(result.rank(), 2);
EXPECT_EQ(result.GetDimension(0).LowerBound(), 1);
EXPECT_EQ(result.GetDimension(0).Extent(), 3);
EXPECT_EQ(result.GetDimension(1).LowerBound(), 1);
EXPECT_EQ(result.GetDimension(1).Extent(), 2);
for (int j{0}; j < 6; ++j) {
EXPECT_EQ(*result.ZeroBasedIndexedElement<std::int32_t>(j), 1 + j % 3);
}
result.Destroy();
auto scalar{MakeArray<TypeCategory::Integer, 4>(
std::vector<int>{}, std::vector<std::int32_t>{1})};
RTNAME(Spread)(result, *scalar, 1, 2, __FILE__, __LINE__);
EXPECT_EQ(result.type(), array->type());
EXPECT_EQ(result.rank(), 1);
EXPECT_EQ(result.GetDimension(0).LowerBound(), 1);
EXPECT_EQ(result.GetDimension(0).Extent(), 2);
for (int j{0}; j < 2; ++j) {
EXPECT_EQ(*result.ZeroBasedIndexedElement<std::int32_t>(j), 1);
}
result.Destroy();
}
TEST(Transformational, Transpose) {
// ARRAY 1 3 5
// 2 4 6
auto array{MakeArray<TypeCategory::Integer, 4>(
std::vector<int>{2, 3}, std::vector<std::int32_t>{1, 2, 3, 4, 5, 6})};
array->GetDimension(0).SetLowerBound(2); // shouldn't matter
array->GetDimension(1).SetLowerBound(-6);
StaticDescriptor<2, true> statDesc;
Descriptor &result{statDesc.descriptor()};
RTNAME(Transpose)(result, *array, __FILE__, __LINE__);
EXPECT_EQ(result.type(), array->type());
EXPECT_EQ(result.rank(), 2);
EXPECT_EQ(result.GetDimension(0).LowerBound(), 1);
EXPECT_EQ(result.GetDimension(0).Extent(), 3);
EXPECT_EQ(result.GetDimension(1).LowerBound(), 1);
EXPECT_EQ(result.GetDimension(1).Extent(), 2);
static std::int32_t expect[6]{1, 3, 5, 2, 4, 6};
for (int j{0}; j < 6; ++j) {
EXPECT_EQ(*result.ZeroBasedIndexedElement<std::int32_t>(j), expect[j]);
}
result.Destroy();
}
TEST(Transformational, Unpack) {
auto vector{MakeArray<TypeCategory::Integer, 4>(
std::vector<int>{4}, std::vector<std::int32_t>{1, 2, 3, 4})};
vector->GetDimension(0).SetLowerBound(2); // shouldn't matter
auto mask{MakeArray<TypeCategory::Logical, 1>(std::vector<int>{2, 3},
std::vector<std::uint8_t>{false, true, true, false, false, true})};
mask->GetDimension(0).SetLowerBound(0); // shouldn't matter
mask->GetDimension(1).SetLowerBound(2);
auto field{MakeArray<TypeCategory::Integer, 4>(std::vector<int>{2, 3},
std::vector<std::int32_t>{-1, -2, -3, -4, -5, -6})};
field->GetDimension(0).SetLowerBound(-1); // shouldn't matter
StaticDescriptor<2, true> statDesc;
Descriptor &result{statDesc.descriptor()};
RTNAME(Unpack)(result, *vector, *mask, *field, __FILE__, __LINE__);
EXPECT_EQ(result.type(), vector->type());
EXPECT_EQ(result.rank(), 2);
EXPECT_EQ(result.GetDimension(0).LowerBound(), 1);
EXPECT_EQ(result.GetDimension(0).Extent(), 2);
EXPECT_EQ(result.GetDimension(1).LowerBound(), 1);
EXPECT_EQ(result.GetDimension(1).Extent(), 3);
static std::int32_t expect[6]{-1, 1, 2, -4, -5, 3};
for (int j{0}; j < 6; ++j) {
EXPECT_EQ(*result.ZeroBasedIndexedElement<std::int32_t>(j), expect[j]);
}
result.Destroy();
// Test for scalar value of the "field" argument
auto scalarField{MakeArray<TypeCategory::Integer, 4>(
std::vector<int>{}, std::vector<std::int32_t>{343})};
RTNAME(Unpack)(result, *vector, *mask, *scalarField, __FILE__, __LINE__);
EXPECT_EQ(result.rank(), 2);
EXPECT_EQ(result.GetDimension(0).LowerBound(), 1);
EXPECT_EQ(result.GetDimension(0).Extent(), 2);
EXPECT_EQ(result.GetDimension(1).LowerBound(), 1);
EXPECT_EQ(result.GetDimension(1).Extent(), 3);
static std::int32_t scalarExpect[6]{343, 1, 2, 343, 343, 3};
for (int j{0}; j < 6; ++j) {
EXPECT_EQ(
*result.ZeroBasedIndexedElement<std::int32_t>(j), scalarExpect[j]);
}
result.Destroy();
}
#if LDBL_MANT_DIG == 64
// Make sure the destination descriptor is created by the runtime
// with proper element size, when REAL*10 maps to 'long double'.
#define Real10CppType long double
TEST(Transformational, TransposeReal10) {
// ARRAY 1 3 5
// 2 4 6
auto array{MakeArray<TypeCategory::Real, 10>(std::vector<int>{2, 3},
std::vector<Real10CppType>{1.0, 2.0, 3.0, 4.0, 5.0, 6.0},
sizeof(Real10CppType))};
StaticDescriptor<2, true> statDesc;
Descriptor &result{statDesc.descriptor()};
RTNAME(Transpose)(result, *array, __FILE__, __LINE__);
EXPECT_EQ(result.ElementBytes(), sizeof(Real10CppType));
EXPECT_EQ(result.type(), array->type());
EXPECT_EQ(result.rank(), 2);
EXPECT_EQ(result.GetDimension(0).LowerBound(), 1);
EXPECT_EQ(result.GetDimension(0).Extent(), 3);
EXPECT_EQ(result.GetDimension(1).LowerBound(), 1);
EXPECT_EQ(result.GetDimension(1).Extent(), 2);
static Real10CppType expect[6]{1.0, 3.0, 5.0, 2.0, 4.0, 6.0};
for (int j{0}; j < 6; ++j) {
EXPECT_EQ(*result.ZeroBasedIndexedElement<Real10CppType>(j), expect[j]);
}
result.Destroy();
}
#endif