Skip to content

Commit 4ae366a

Browse files
Merge pull request #23 from jacobwilliams/22-dqtcrt
added refactored dqtcrt from NSWC library
2 parents 094d55e + 006c433 commit 4ae366a

File tree

3 files changed

+312
-5
lines changed

3 files changed

+312
-5
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ Method name | Polynomial type | Coefficients | Roots | Reference
2525
[`cpzero`](https://jacobwilliams.github.io/polyroots-fortran/proc/cpzero.html) | General | complex | complex | [SLATEC](https://netlib.org/slatec/src/cpzero.f)
2626
[`rpqr79`](https://jacobwilliams.github.io/polyroots-fortran/proc/rpqr79.html) | General | real | complex | [SLATEC](https://netlib.org/slatec/src/rpqr79.f)
2727
[`cpqr79`](https://jacobwilliams.github.io/polyroots-fortran/proc/cpqr79.html) | General | complex | complex | [SLATEC](https://netlib.org/slatec/src/cpqr79.f)
28+
[`dqtcrt`](https://jacobwilliams.github.io/polyroots-fortran/proc/dqtcrt.html) | Quartic | real | complex | [NSWC Library](https://github.com/jacobwilliams/nswc)
2829
[`dcbcrt`](https://jacobwilliams.github.io/polyroots-fortran/proc/dcbcrt.html) | Cubic | real | complex | [NSWC Library](https://github.com/jacobwilliams/nswc)
2930
[`dqdcrt`](https://jacobwilliams.github.io/polyroots-fortran/proc/dqdcrt.html) | Quadratic | real | complex | [NSWC Library](https://github.com/jacobwilliams/nswc)
3031
[`dpolz`](https://jacobwilliams.github.io/polyroots-fortran/proc/dpolz.html) | General | real | complex | [MATH77 Library](https://netlib.org/math/)

src/polyroots_module.F90

Lines changed: 297 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ module polyroots_module
6262
! special polynomial routines:
6363
public :: dcbcrt
6464
public :: dqdcrt
65+
public :: dqtcrt
6566
public :: rroots_chebyshev_cubic
6667

6768
! utility routines:
@@ -1254,6 +1255,302 @@ pure subroutine dqdcrt(a, zr, zi)
12541255
end subroutine dqdcrt
12551256
!*****************************************************************************************
12561257

1258+
!*****************************************************************************************
1259+
!>
1260+
! dqtcrt computes the roots of the real polynomial
1261+
!```
1262+
! a(1) + a(2)*z + ... + a(5)*z**4
1263+
!```
1264+
! and stores the results in `zr` and `zi`. it is assumed
1265+
! that `a(5)` is nonzero.
1266+
!
1267+
!### History
1268+
! * Original version written by alfred h. morris,
1269+
! naval surface weapons center, dahlgren, virginia
1270+
!
1271+
!### See also
1272+
! * A. H. Morris, "NSWC Library of Mathematical Subroutines",
1273+
! Naval Surface Warfare Center, NSWCDD/TR-92/425, January 1993
1274+
1275+
subroutine dqtcrt(a,zr,zi)
1276+
1277+
real(wp) :: a(5) , zr(4) , zi(4)
1278+
real(wp) :: b , b2 , c , d , e , h , p , q , r , t , temp(4) , &
1279+
u , v , v1 , v2 , w(2) , x , x1 , x2 , x3
1280+
1281+
if ( a(1)==0.0_wp ) then
1282+
zr(1) = 0.0_wp
1283+
zi(1) = 0.0_wp
1284+
call dcbcrt(a(2),zr(2),zi(2))
1285+
else
1286+
b = a(4)/(4.0_wp*a(5))
1287+
c = a(3)/a(5)
1288+
d = a(2)/a(5)
1289+
e = a(1)/a(5)
1290+
b2 = b*b
1291+
1292+
p = 0.5_wp*(c-6.0_wp*b2)
1293+
q = d - 2.0_wp*b*(c-4.0_wp*b2)
1294+
r = b2*(c-3.0_wp*b2) - b*d + e
1295+
1296+
! solve the resolvent cubic equation. the cubic has
1297+
! at least one nonnegative real root. if w1, w2, w3
1298+
! are the roots of the cubic then the roots of the
1299+
! originial equation are
1300+
!
1301+
! z = -b + csqrt(w1) + csqrt(w2) + csqrt(w3)
1302+
!
1303+
! where the signs of the square roots are chosen so
1304+
! that csqrt(w1) * csqrt(w2) * csqrt(w3) = -q/8.
1305+
1306+
temp(1) = -q*q/64.0_wp
1307+
temp(2) = 0.25_wp*(p*p-r)
1308+
temp(3) = p
1309+
temp(4) = 1.0_wp
1310+
call dcbcrt(temp,zr,zi)
1311+
if ( zi(2)/=0.0_wp ) then
1312+
1313+
! the resolvent cubic has complex roots
1314+
1315+
t = zr(1)
1316+
x = 0.0_wp
1317+
if ( t<0 ) then
1318+
h = abs(zr(2)) + abs(zi(2))
1319+
if ( abs(t)>h ) then
1320+
v = sqrt(abs(t))
1321+
zr(1) = -b
1322+
zr(2) = -b
1323+
zr(3) = -b
1324+
zr(4) = -b
1325+
zi(1) = v
1326+
zi(2) = -v
1327+
zi(3) = v
1328+
zi(4) = -v
1329+
return
1330+
endif
1331+
elseif ( t/=0 ) then
1332+
x = sqrt(t)
1333+
if ( q>0.0_wp ) x = -x
1334+
endif
1335+
w(1) = zr(2)
1336+
w(2) = zi(2)
1337+
call dcsqrt(w,w)
1338+
u = 2.0_wp*w(1)
1339+
v = 2.0_wp*abs(w(2))
1340+
t = x - b
1341+
x1 = t + u
1342+
x2 = t - u
1343+
if ( abs(x1)>abs(x2) ) then
1344+
t = x1
1345+
x1 = x2
1346+
x2 = t
1347+
endif
1348+
u = -x - b
1349+
h = u*u + v*v
1350+
if ( x1*x1<1.0e-2_wp*min(x2*x2,h) ) x1 = e/(x2*h)
1351+
zr(1) = x1
1352+
zr(2) = x2
1353+
zi(1) = 0.0_wp
1354+
zi(2) = 0.0_wp
1355+
zr(3) = u
1356+
zr(4) = u
1357+
zi(3) = v
1358+
zi(4) = -v
1359+
1360+
else
1361+
1362+
! the resolvent cubic has only real roots
1363+
! reorder the roots in increasing order
1364+
x1 = zr(1)
1365+
x2 = zr(2)
1366+
x3 = zr(3)
1367+
if ( x1>x2 ) then
1368+
t = x1
1369+
x1 = x2
1370+
x2 = t
1371+
endif
1372+
if ( x2>x3 ) then
1373+
t = x2
1374+
x2 = x3
1375+
x3 = t
1376+
if ( x1>x2 ) then
1377+
t = x1
1378+
x1 = x2
1379+
x2 = t
1380+
endif
1381+
endif
1382+
1383+
u = 0.0_wp
1384+
if ( x3>0.0_wp ) u = sqrt(x3)
1385+
tmp : block
1386+
if ( x2<=0.0_wp ) then
1387+
v1 = sqrt(abs(x1))
1388+
v2 = sqrt(abs(x2))
1389+
if ( q<0.0_wp ) u = -u
1390+
else
1391+
if ( x1<0.0_wp ) then
1392+
if ( abs(x1)>x2 ) then
1393+
v1 = sqrt(abs(x1))
1394+
v2 = 0.0_wp
1395+
exit tmp
1396+
else
1397+
x1 = 0.0_wp
1398+
endif
1399+
endif
1400+
x1 = sqrt(x1)
1401+
x2 = sqrt(x2)
1402+
if ( q>0.0_wp ) x1 = -x1
1403+
zr(1) = ((x1+x2)+u) - b
1404+
zr(2) = ((-x1-x2)+u) - b
1405+
zr(3) = ((x1-x2)-u) - b
1406+
zr(4) = ((-x1+x2)-u) - b
1407+
call daord(zr,4)
1408+
if ( abs(zr(1))<0.1_wp*abs(zr(4)) ) then
1409+
t = zr(2)*zr(3)*zr(4)
1410+
if ( t/=0.0_wp ) zr(1) = e/t
1411+
endif
1412+
zi(1) = 0.0_wp
1413+
zi(2) = 0.0_wp
1414+
zi(3) = 0.0_wp
1415+
zi(4) = 0.0_wp
1416+
return
1417+
endif
1418+
end block tmp
1419+
zr(1) = -u - b
1420+
zi(1) = v1 - v2
1421+
zr(2) = zr(1)
1422+
zi(2) = -zi(1)
1423+
zr(3) = u - b
1424+
zi(3) = v1 + v2
1425+
zr(4) = zr(3)
1426+
zi(4) = -zi(3)
1427+
endif
1428+
1429+
endif
1430+
1431+
end subroutine dqtcrt
1432+
!*****************************************************************************************
1433+
1434+
!*****************************************************************************************
1435+
!>
1436+
! Used to reorder the elements of the double precision
1437+
! array a so that abs(a(i)) <= abs(a(i+1)) for i = 1,...,n-1.
1438+
! it is assumed that n >= 1.
1439+
1440+
subroutine daord(a,n)
1441+
1442+
integer,intent(in) :: n
1443+
real(wp),intent(inout) :: a(n)
1444+
1445+
integer :: i , ii , imax , j , jmax , ki , l , ll
1446+
real(wp) :: s
1447+
1448+
integer,dimension(10),parameter :: k = [1, 4, 13, 40, 121, 364, &
1449+
1093, 3280, 9841, 29524]
1450+
1451+
! selection of the increments k(i) = (3**i-1)/2
1452+
if ( n<2 ) return
1453+
imax = 1
1454+
do i = 3 , 10
1455+
if ( n<=k(i) ) exit
1456+
imax = imax + 1
1457+
enddo
1458+
1459+
! stepping through the increments k(imax),...,k(1)
1460+
i = imax
1461+
do ii = 1 , imax
1462+
ki = k(i)
1463+
! sorting elements that are ki positions apart
1464+
! so that abs(a(j)) <= abs(a(j+ki))
1465+
jmax = n - ki
1466+
do j = 1 , jmax
1467+
l = j
1468+
ll = j + ki
1469+
s = a(ll)
1470+
do while ( abs(s)<abs(a(l)) )
1471+
a(ll) = a(l)
1472+
ll = l
1473+
l = l - ki
1474+
if ( l<=0 ) exit
1475+
enddo
1476+
a(ll) = s
1477+
enddo
1478+
i = i - 1
1479+
enddo
1480+
1481+
end subroutine daord
1482+
!*****************************************************************************************
1483+
1484+
!*****************************************************************************************
1485+
!>
1486+
! `w = sqrt(z)` for the double precision complex number `z`
1487+
!
1488+
! z and w are interpreted as double precision complex numbers.
1489+
! it is assumed that z(1) and z(2) are the real and imaginary
1490+
! parts of the complex number z, and that w(1) and w(2) are
1491+
! the real and imaginary parts of w.
1492+
1493+
subroutine dcsqrt(z,w)
1494+
1495+
real(wp),intent(in) :: z(2)
1496+
real(wp),intent(out) :: w(2)
1497+
1498+
real(wp) :: x , y , r
1499+
1500+
x = z(1)
1501+
y = z(2)
1502+
if ( x<0 ) then
1503+
if ( y/=0.0_wp ) then
1504+
r = dcpabs(x,y)
1505+
w(2) = sqrt(0.5_wp*(r-x))
1506+
w(2) = sign(w(2),y)
1507+
w(1) = 0.5_wp*y/w(2)
1508+
else
1509+
w(1) = 0.0_wp
1510+
w(2) = sqrt(abs(x))
1511+
endif
1512+
elseif ( x==0.0_wp ) then
1513+
if ( y/=0.0_wp ) then
1514+
w(1) = sqrt(0.5_wp*abs(y))
1515+
w(2) = sign(w(1),y)
1516+
else
1517+
w(1) = 0.0_wp
1518+
w(2) = 0.0_wp
1519+
endif
1520+
elseif ( y/=0.0_wp ) then
1521+
r = dcpabs(x,y)
1522+
w(1) = sqrt(0.5_wp*(r+x))
1523+
w(2) = 0.5_wp*y/w(1)
1524+
else
1525+
w(1) = sqrt(x)
1526+
w(2) = 0.0_wp
1527+
endif
1528+
1529+
end subroutine dcsqrt
1530+
!*****************************************************************************************
1531+
1532+
!*****************************************************************************************
1533+
!>
1534+
! evaluation of `sqrt(x*x + y*y)`
1535+
1536+
real(wp) function dcpabs(x,y)
1537+
1538+
real(wp),intent(in) :: x , y
1539+
real(wp) :: a
1540+
1541+
if ( abs(x)>abs(y) ) then
1542+
a = y/x
1543+
dcpabs = abs(x)*sqrt(1.0_wp+a*a)
1544+
elseif ( y==0.0_wp ) then
1545+
dcpabs = 0.0_wp
1546+
else
1547+
a = x/y
1548+
dcpabs = abs(y)*sqrt(1.0_wp+a*a)
1549+
end if
1550+
1551+
end function dcpabs
1552+
!*****************************************************************************************
1553+
12571554
!*****************************************************************************************
12581555
!>
12591556
! Solve the real coefficient algebraic equation by the qr-method.

test/dcbcrt_test.f90

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,26 @@ program dcbcrt_test
99

1010
implicit none
1111

12-
real(wp), dimension(4) :: a !! coefficients
13-
real(wp), dimension(3) :: zr !! real components of roots
14-
real(wp), dimension(3) :: zi !! imaginary components of roots
12+
real(wp), dimension(5) :: a !! coefficients
13+
real(wp), dimension(4) :: zr !! real components of roots
14+
real(wp), dimension(4) :: zi !! imaginary components of roots
1515

1616
integer :: i !! counter
1717
complex(wp) :: z, root
1818

19-
a = [1.0_wp, 2.0_wp, 3.0_wp, 4.0_wp]
19+
a = [1.0_wp, 2.0_wp, 3.0_wp, 4.0_wp, 5.0_wp]
20+
21+
write(*,'(/A)') 'dqtcrt test:'
22+
call dqtcrt(a, zr(1:4), zi(1:4))
23+
do i = 1, 4
24+
z = cmplx(zr(i), zi(i), wp)
25+
root = a(1) + a(2)*z + a(3)*z**2 + a(4)*z**3 + a(5)*z**4
26+
write(*,'(A,1x,*(e22.15,1x))') 'root is: ', zr(i), zi(i), abs(root)
27+
if (abs(root) > 100*epsilon(1.0_wp)) error stop 'Error: insufficient accuracy'
28+
end do
2029

2130
write(*,'(/A)') 'dcbcrt test:'
22-
call dcbcrt(a, zr, zi)
31+
call dcbcrt(a, zr(1:3), zi(1:3))
2332
do i = 1, 3
2433
z = cmplx(zr(i), zi(i), wp)
2534
root = a(1) + a(2)*z + a(3)*z**2 + a(4)*z**3

0 commit comments

Comments
 (0)