//===-- 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/Common/float128.h" #include "flang/Runtime/type-code.h" #include using namespace Fortran::runtime; using Fortran::common::TypeCategory; template using BesselFuncType = std::function, CppTypeFor, CppTypeFor, const char *, int)>; template using BesselX0FuncType = std::function; template constexpr CppTypeFor besselEpsilon = CppTypeFor(1e-4); template static void testBesselJn(BesselFuncType rtFunc, int32_t n1, int32_t n2, CppTypeFor x, const std::vector> &expected) { StaticDescriptor<1> desc; Descriptor &result{desc.descriptor()}; unsigned len = expected.size(); CppTypeFor anc0 = len > 0 ? expected[len - 1] : 0.0; CppTypeFor 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)); 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>( j)), expected[j], besselEpsilon); } } template static void testBesselJnX0( BesselX0FuncType 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>( 0)), (n1 == 0) ? 1.0 : 0.0, 1e-5); for (int j{1}; j < (n2 - n1 + 1); ++j) { EXPECT_NEAR( (*result.ZeroBasedIndexedElement>( j)), 0.0, besselEpsilon); } } template static void testBesselJn(BesselFuncType rtFunc) { testBesselJn(rtFunc, 1, 0, 1.0, {}); testBesselJn(rtFunc, 0, 0, 1.0, {0.765197694}); testBesselJn(rtFunc, 0, 1, 1.0, {0.765197694, 0.440050572}); testBesselJn( rtFunc, 0, 2, 1.0, {0.765197694, 0.440050572, 0.114903487}); testBesselJn(rtFunc, 1, 5, 5.0, {-0.327579111, 0.046565145, 0.364831239, 0.391232371, 0.261140555}); } template static void testBesselJnX0(BesselX0FuncType rtFunc) { testBesselJnX0(rtFunc, 1, 0); testBesselJnX0(rtFunc, 0, 0); testBesselJnX0(rtFunc, 1, 1); testBesselJnX0(rtFunc, 0, 3); testBesselJnX0(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 static void testBesselYn(BesselFuncType rtFunc, int32_t n1, int32_t n2, CppTypeFor x, const std::vector> &expected) { StaticDescriptor<1> desc; Descriptor &result{desc.descriptor()}; unsigned len = expected.size(); CppTypeFor anc0 = len > 0 ? expected[0] : 0.0; CppTypeFor 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)); 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>( j)), expected[j], besselEpsilon); } } template static void testBesselYnX0( BesselX0FuncType 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>( j)), (-std::numeric_limits< CppTypeFor>::infinity())); } } template static void testBesselYn(BesselFuncType rtFunc) { testBesselYn(rtFunc, 1, 0, 1.0, {}); testBesselYn(rtFunc, 0, 0, 1.0, {0.08825695}); testBesselYn(rtFunc, 0, 1, 1.0, {0.08825695, -0.7812128}); testBesselYn(rtFunc, 0, 2, 1.0, {0.0882569555, -0.7812128, -1.6506826}); testBesselYn(rtFunc, 1, 5, 1.0, {-0.7812128, -1.6506826, -5.8215175, -33.278423, -260.40588}); } template static void testBesselYnX0(BesselX0FuncType rtFunc) { testBesselYnX0(rtFunc, 1, 0); testBesselYnX0(rtFunc, 0, 0); testBesselYnX0(rtFunc, 1, 1); testBesselYnX0(rtFunc, 0, 3); testBesselYnX0(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( std::vector{2, 3}, std::vector{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( std::vector{3}, std::vector{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(j), cshiftExpect1[j]); } result.Destroy(); auto shift2{MakeArray( std::vector{2}, std::vector{1, -1})}; shift2->GetDimension(0).SetLowerBound(-1); // shouldn't matter 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(j), cshiftExpect2[j]); } result.Destroy(); // VECTOR 1 3 5 2 4 6 auto vector{MakeArray( std::vector{6}, std::vector{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(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(j), cshiftExpect4[j]); } vectorResult.Destroy(); // VECTOR 1 3 5 2 4 6 WITH non zero lower bound in a negative cshift. auto vectorWithLowerBounds{MakeArray( std::vector{6}, std::vector{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(j), cshiftExpect5[j]); } vectorResult.Destroy(); auto boundary{MakeArray( std::vector{3}, std::vector{-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(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(reinterpret_cast(&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(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(j), eoshiftVectorExpect2[j]); } vectorResult.Destroy(); } TEST(Transformational, Pack) { // ARRAY 1 3 5 // 2 4 6 auto array{MakeArray( std::vector{2, 3}, std::vector{1, 2, 3, 4, 5, 6})}; array->GetDimension(0).SetLowerBound(2); // shouldn't matter array->GetDimension(1).SetLowerBound(-1); auto mask{MakeArray(std::vector{2, 3}, std::vector{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(j), packExpect1[j]) << " at " << j; } result.Destroy(); auto vector{MakeArray( std::vector{5}, std::vector{-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(j), packExpect2[j]) << " at " << j; } result.Destroy(); } TEST(Transformational, Spread) { auto array{MakeArray( std::vector{3}, std::vector{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(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(j), 1 + j % 3); } result.Destroy(); auto scalar{MakeArray( std::vector{}, std::vector{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(j), 1); } result.Destroy(); } TEST(Transformational, Transpose) { // ARRAY 1 3 5 // 2 4 6 auto array{MakeArray( std::vector{2, 3}, std::vector{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(j), expect[j]); } result.Destroy(); } TEST(Transformational, Unpack) { auto vector{MakeArray( std::vector{4}, std::vector{1, 2, 3, 4})}; vector->GetDimension(0).SetLowerBound(2); // shouldn't matter auto mask{MakeArray(std::vector{2, 3}, std::vector{false, true, true, false, false, true})}; mask->GetDimension(0).SetLowerBound(0); // shouldn't matter mask->GetDimension(1).SetLowerBound(2); auto field{MakeArray(std::vector{2, 3}, std::vector{-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(j), expect[j]); } result.Destroy(); // Test for scalar value of the "field" argument auto scalarField{MakeArray( std::vector{}, std::vector{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(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(std::vector{2, 3}, std::vector{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(j), expect[j]); } result.Destroy(); } #endif