1
c $Id: testspd.F,v 1.8 2004-11-12 22:19:10 edo Exp $
2
c***********************************************************************
4
c* Scope : test LLT SCALAPACK routines
6
c* 04/12/96 GVT First Implementation
7
c* Giuseppe Vitillaro peppe@unipg.it
8
c***********************************************************************
13
#define ga_dnormF(g_a) sqrt(ga_ddot(g_a, g_a))
15
#define BLOCK_CYCLIC 0
18
c***********************************************************************
20
c***********************************************************************
25
#include "mafdecls.fh"
32
c*** Intitialize a message passing library
38
integer required, provided
39
required=MPI_THREAD_MULTIPLE
40
call mpi_init_thread(required, provided, ierr)
41
if (provided.ne.MPI_THREAD_MULTIPLE) then
42
call ga_error('provided.ne.MPI_THREAD_MULTIPLE',provided)
51
c**** Initialize GA package
54
c**** get number of nodes and calculate memory allocation
56
hsize = 6*SIZE*SIZE + 3*SIZE
57
ssize = 3*SIZE*SIZE + 3*SIZE
60
hsize = (hsize/nproc) + 1
61
ssize = (ssize/nproc) + 1 + 3*256*256
63
c**** Initialize MA package
64
if (.not. ma_init(MT_DBL, ssize, hsize))
65
$ call ga_error("ma init failed",ssize+hsize)
70
c**** Exit from the GA package
74
call mpi_finalize(ierr)
81
c***********************************************************************
83
c* print informations about this run
84
c***********************************************************************
85
subroutine infos(me, nproc, uplo, eps)
87
#include "mafdecls.fh"
98
print *, 'Number of nodes : ', nproc
99
print *, 'Problem size : ', SIZE
100
print *, 'Uplo : ', uplo
101
print *, 'Epsilon : ', eps
109
c***********************************************************************
111
c* emit test header output
112
c***********************************************************************
113
subroutine thead(header)
115
#include "mafdecls.fh"
125
print *, '> ', header, ' '
133
c***********************************************************************
134
c* subroutine: dthtest
135
c* test a double precision against the THRESH parameter
136
c***********************************************************************
137
subroutine dthtest(msg, dval)
139
#include "mafdecls.fh"
143
double precision dval
150
print *, ' ', msg, dval, ' '
151
if (dval.lt.TRESH) then
152
print *, '> success '
154
print *, '> failure '
163
c***********************************************************************
165
c* test solver result
166
c***********************************************************************
167
subroutine stest(irc, ierc)
169
#include "mafdecls.fh"
183
elseif (irc.gt.0) then
187
if (irc.eq.ierc .or. (irc.gt.0 .and. ierc.gt.0)) then
188
print *, '> success: expected factorization'
190
print *, '> failure: not expected factorization',irc,ierc
201
c***********************************************************************
204
c***********************************************************************
205
subroutine emsg(msg, ival)
207
#include "mafdecls.fh"
218
print *, ' >>> ', msg, ival, ' '
219
print *, '> failure '
229
c***********************************************************************
230
c* subroutine: ctests
231
c* do coretests for LLT Cholesky factorization, solver
232
c***********************************************************************
233
subroutine ctests(uplo)
237
#include "mafdecls.fh"
247
external ga_llt_f, ga_llt_s, ga_llt_i
248
integer ga_llt_f, ga_llt_i
250
double precision A(n,n), X(n,n)
252
integer g_A, g_B, g_X, g_Y
253
integer g_A1, g_X1, g_Y1, g_Y2
255
integer g_AA, g_BB, g_CC, g_YY, g_ZZ
256
integer dims(2), proc_grid(2), block(2)
264
double precision buf(n)
266
double precision dnA, dnX, dnX1, dnY1, dnS, dnF, dnI
269
double precision eps, dlamch
276
call infos(me, nproc, uplo, eps)
279
c****************************************
280
c* Initialize tests variables
281
c* Generate a Lower triangula matrix
282
c* with large positive diagonal elements
283
c* so the LU decomposition will be
285
c****************************************
286
c**** Initialize local arrays A, X
287
c**** they are local copies of the corrispondent
291
X(i,j) = dsin(1.d0 * (i * j))
294
A(i,j) = dsin(1.d0 * (i + j))
297
A(i,j) = SIZE*dabs(dsin(10.d0 * i))
302
c**** Create global arrays
306
write(6,*) '* Creating Block-Cyclic Arrays'
313
call factor(nproc,g1,g2)
317
g_A = ga_create_handle()
318
call ga_set_data(g_A,2,dims,MT_DBL)
319
call ga_set_block_cyclic_proc_grid(g_A,block,proc_grid)
320
if (.not.ga_allocate(g_A))
321
& call ga_error(' ga_create A failed ', 2)
323
g_B = ga_create_handle()
324
call ga_set_data(g_B,2,dims,MT_DBL)
325
call ga_set_block_cyclic_proc_grid(g_B,block,proc_grid)
326
if (.not.ga_allocate(g_B))
327
& call ga_error(' ga_create B failed ', 2)
329
g_A1 = ga_create_handle()
330
call ga_set_data(g_A1,2,dims,MT_DBL)
331
call ga_set_block_cyclic_proc_grid(g_A1,block,proc_grid)
332
if (.not.ga_allocate(g_A1))
333
& call ga_error(' ga_create A1 failed ', 2)
335
g_X = ga_create_handle()
336
call ga_set_data(g_X,2,dims,MT_DBL)
337
call ga_set_block_cyclic_proc_grid(g_X,block,proc_grid)
338
if (.not.ga_allocate(g_X))
339
& call ga_error(' ga_create X failed ', 2)
341
g_X1 = ga_create_handle()
342
call ga_set_data(g_X1,2,dims,MT_DBL)
343
call ga_set_block_cyclic_proc_grid(g_X1,block,proc_grid)
344
if (.not.ga_allocate(g_X1))
345
& call ga_error(' ga_create X1 failed ', 2)
347
g_AA = ga_create_handle()
348
call ga_set_data(g_AA,2,dims,MT_DBL)
349
if (.not.ga_allocate(g_AA))
350
& call ga_error(' ga_create AA failed ', 2)
352
g_BB = ga_create_handle()
353
call ga_set_data(g_BB,2,dims,MT_DBL)
354
if (.not.ga_allocate(g_BB))
355
& call ga_error(' ga_create BB failed ', 2)
357
g_CC = ga_create_handle()
358
call ga_set_data(g_CC,2,dims,MT_DBL)
359
if (.not.ga_allocate(g_CC))
360
& call ga_error(' ga_create CC failed ', 2)
364
g_Y = ga_create_handle()
365
call ga_set_data(g_Y,2,dims,MT_DBL)
366
call ga_set_block_cyclic_proc_grid(g_Y,block,proc_grid)
367
if (.not.ga_allocate(g_Y))
368
& call ga_error(' ga_create Y failed ', 2)
370
g_Y1 = ga_create_handle()
371
call ga_set_data(g_Y1,2,dims,MT_DBL)
372
call ga_set_block_cyclic_proc_grid(g_Y1,block,proc_grid)
373
if (.not.ga_allocate(g_Y1))
374
& call ga_error(' ga_create Y1 failed ', 2)
376
g_Y2 = ga_create_handle()
377
call ga_set_data(g_Y2,2,dims,MT_DBL)
378
call ga_set_block_cyclic_proc_grid(g_Y2,block,proc_grid)
379
if (.not.ga_allocate(g_Y2))
380
& call ga_error(' ga_create Y1 failed ', 2)
382
g_YY = ga_create_handle()
383
call ga_set_data(g_YY,2,dims,MT_DBL)
384
if (.not.ga_allocate(g_YY))
385
& call ga_error(' ga_create YY failed ', 2)
387
g_ZZ = ga_create_handle()
388
call ga_set_data(g_ZZ,2,dims,MT_DBL)
389
if (.not.ga_allocate(g_ZZ))
390
& call ga_error(' ga_create YY failed ', 2)
392
if (.not. ga_create(MT_DBL, n, n, 'A', 1, 1, g_A))
393
& call ga_error(' ga_create A failed ', 2)
395
if (.not. ga_create(MT_DBL, n, n, 'B', 1, 1, g_B))
396
& call ga_error(' ga_create B failed ', 2)
398
if (.not. ga_create(MT_DBL, n, n, 'A1', 1, 1, g_A1))
399
& call ga_error(' ga_create A1 failed ', 2)
401
if (.not. ga_create(MT_DBL, n, n, 'X', 1, 1, g_X))
402
& call ga_error(' ga_create X failed ', 2)
404
if (.not. ga_create(MT_DBL, n, n, 'X1', 1, 1, g_X1))
405
& call ga_error(' ga_create X1 failed ', 2)
407
if (.not. ga_create(MT_DBL, n, 1, 'Y', 1, 1, g_Y))
408
& call ga_error(' ga_create Y failed ', 2)
410
if (.not. ga_create(MT_DBL, n, 1, 'Y1', 1, 1, g_Y1))
411
& call ga_error(' ga_create Y1 failed ', 2)
413
if (.not. ga_create(MT_DBL, n, 1, 'Y2', 1, 1, g_Y2))
414
& call ga_error(' ga_create Y2 failed ', 2)
417
c**** Fill in arrays A, X
418
do j = me+1, n, nproc
419
call ga_put(g_A, 1, n, j, j, A(1,j), n)
420
call ga_put(g_X, 1, n, j, j, X(1,j), n)
424
c**** It is granted that A, B, Y will not change
425
c**** during the execution of the test LLT driver
429
call ga_copy(g_A, g_AA)
430
call ga_dgemm('N', 'T', n, n, n, 1.d0, g_AA, g_AA, 0.d0, g_BB)
431
call ga_copy(g_BB, g_B)
433
call ga_dgemm('N', 'T', n, n, n, 1.d0, g_A, g_A, 0.d0, g_B)
437
call ga_copy(g_B, g_A)
441
call ga_copy(g_A, g_AA)
442
call ga_copy(g_X, g_CC)
443
call ga_dgemm('N', 'N', n, n, n, 1.d0, g_AA, g_CC, 0.d0, g_BB)
444
call ga_copy(g_BB,g_B)
446
call ga_dgemm('N', 'N', n, n, n, 1.d0, g_A, g_X, 0.d0, g_B)
449
c**** Copy B(1:n,1:1) to Y(1:n) so Y will X column 1
450
call ga_copy_patch('N', g_B, 1, n, 1, 1, g_Y, 1, n, 1, 1)
456
c****************************************************
457
c Test Cholesky factorization external interface
458
c****************************************************
459
call thead("Test Cholesky factorization")
462
call ga_copy(g_A, g_X)
464
c**** call Cholesky factorization routine in ScaLAPACK PDPOTRF
465
c**** to obtain an LL'/U'U factorization (external interface)
466
irc = ga_cholesky(uplo, g_X)
468
c**** if return code 'zero' is OK
471
c**** A1 = X * X^ or A1 = X^ * X
472
if (uplo.eq.'L') then
474
call ga_copy(g_X, g_CC)
475
call ga_dgemm('N', 'T', n, n, n, 1.d0, g_CC, g_CC, 0.d0, g_AA)
476
call ga_copy(g_AA, g_A1)
478
call ga_dgemm('N', 'T', n, n, n, 1.d0, g_X, g_X, 0.d0, g_A1)
482
call ga_copy(g_X, g_CC)
483
call ga_dgemm('T', 'N', n, n, n, 1.d0, g_CC, g_CC, 0.d0, g_AA)
484
call ga_copy(g_AA, g_A1)
486
call ga_dgemm('T', 'N', n, n, n, 1.d0, g_X, g_X, 0.d0, g_A1)
490
c**** A1 = A - A1 = A - X * X^ or A1 = A - A1 = A - X^ * X
491
call ga_add(1.0d0, g_A1, -1.0d0, g_A, g_A1)
493
c**** dnF = ||A - A1*A1^|| / (||A|| * N * eps) : SHAPE L
494
c**** dnF = ||A - A1^*A1|| / (||A|| * N * eps) : SHAPE U
495
dnF = ga_dnormF(g_A1) / (dnA * n * eps)
497
if (uplo.eq.'L') then
498
call dthtest("||LL' - A|| / (||A|| * N * eps) =", dnF)
500
call dthtest("||U'U - A|| / (||A|| * N * eps) =", dnF)
503
c**** if return code is > 0
505
call emsg('It is not positive definite the minor of order:',
509
c****************************************************
510
c Test Cholesky factorization and solver
511
c internal interfaces with a NxN RHS array
512
c****************************************************
513
call thead("Test II Cholesky solver with a NxN RHS array")
515
c**** call Cholesky factorization routine in ScaLAPACK PDPOTRF
516
c**** to obtain an LL'/U'U factorization
517
c**** (internal interface: it will not destroy A)
519
call ga_copy(g_A, g_AA)
522
irc = ga_llt_f(uplo, g_A, hsA)
524
c**** if return code from ga_llt_f is zero
528
call ga_copy(g_B, g_X)
530
c**** call Cholesky solver routine in ScaLAPACK PDPOTRS
531
c**** internal interface with an NxN RHS GA
532
call ga_llt_s(uplo, g_A, g_X, hsA)
536
call ga_copy(g_X, g_BB)
537
call ga_dgemm('N', 'N', n, n, n, 1.d0,
538
& g_AA, g_BB, 0.d0, g_CC)
539
call ga_copy(g_CC,g_A1)
541
call ga_dgemm('N', 'N', n, n, n, 1.d0,
542
& g_A, g_X, 0.d0, g_A1)
545
c**** A1 = A1 - B = A * X - B
546
call ga_add(1.d0, g_A1, -1.d0, g_B, g_A1)
551
c**** dnS = ||AX - B|| / (||A|| * ||X|| * N * eps)
552
dnS = ga_dnormF(G_A1) / (dnA * dnX * n * eps)
554
call dthtest("||A*X - B||/(||A||*||X||*n*eps) =", dnS)
556
c**** if return code is > 0
558
call emsg('It is not positive definite the minor of order:',
563
c****************************************************
564
c Test Cholesky factorization and solver
565
c internal interfaces with a single vector RHS
566
c****************************************************
567
call thead("Test II Cholesky solver with a single vector RHS")
569
c**** call Cholesky factorization routine in ScaLAPACK PDPOTRF
570
c**** to obtain an LL'/U'U factorization
571
c**** (internal interface: it will not destroy A)
573
call ga_copy(g_AA, g_A)
576
irc = ga_llt_f(uplo, g_A, hsA)
578
c**** if return code from ga_llt_f is zero
582
call ga_copy(g_Y, g_Y1)
584
c**** call Cholesky solver routine in ScaLAPACK PDPOTRS
585
c**** internal interface with a single vector RHS
586
call ga_llt_s(uplo, g_A, g_Y1, hsA)
589
dnY1 = ga_dnormF(g_Y1)
593
call ga_copy(g_Y1, g_YY)
594
call ga_dgemm('N', 'N', n, 1, n, 1.d0,
595
& g_AA, g_YY, 0.d0, g_ZZ)
596
call ga_copy(g_ZZ,g_Y2)
598
call ga_dgemm('N', 'N', n, 1, n, 1.d0,
599
& g_A, g_Y1, 0.d0, g_Y2)
602
c**** Y1 = Y1 - Y = A * Y1 - Y
603
call ga_add(1.d0, g_Y2, -1.d0, g_Y, g_Y1)
605
c**** dnS = ||AY1 - Y|| / (||A|| * ||Y1|| * N * eps)
606
dnS = ga_dnormF(G_A1) / (dnA * dnY1 * n * eps)
608
call dthtest("||A*X - V||/(||A||*||X||*n*eps) =", dnS)
610
c**** if return code is > 0
612
call emsg('It is not positive definite the minor of order:',
616
c****************************************************
617
c Test Cholesky solver with a NxN RHS array
618
c****************************************************
619
call thead("Test EI Cholesky solver with a NxN RHS array")
622
call ga_copy(g_B, g_X)
624
c**** call Cholesky L/U solver with a NxN RHS
626
call ga_copy(g_AA,g_A)
628
irc = ga_llt_solve(g_A, g_X)
630
c**** if return code from ga_llt_solve is zero
635
call ga_copy(g_X,g_BB)
636
call ga_dgemm('N', 'N', n, n, n, 1.d0,
637
& g_AA, g_BB, 0.d0, g_CC)
638
call ga_copy(g_CC,g_A1)
640
call ga_dgemm('N', 'N', n, n, n, 1.d0,
641
& g_A, g_X, 0.d0, g_A1)
644
c**** A1 = A1 - B = A * X - B
645
call ga_add(1.d0, g_A1, -1.d0, g_B, g_A1)
650
c**** dnS = ||AX - B|| / (||A|| * ||X|| * N * eps)
651
dnS = ga_dnormF(G_A1) / (dnA * dnX * n * eps)
653
call dthtest("||A*X - B||/(||A||*||X||*n*eps) =", dnS)
655
c**** if return code is > 0
657
call emsg('It is not positive definite the minor of order:',
662
c****************************************************
663
c Test Cholesky solver with a single vector RHS
664
c****************************************************
665
call thead("Test EI Cholesky solver with a single vector RHS")
668
call ga_copy(g_Y, g_Y1)
670
c**** call Cholesky L/U solver with a single vector RHS
672
call ga_copy(g_AA,g_A)
674
irc = ga_llt_solve(g_A, g_Y1)
676
c**** if return code from ga_llt_solve is zero
680
dnY1 = ga_dnormF(g_Y1)
684
call ga_copy(g_Y1,g_YY)
685
call ga_dgemm('N', 'N', n, 1, n, 1.d0,
686
& g_AA, g_YY, 0.d0, g_ZZ)
687
call ga_copy(g_ZZ,g_Y2)
689
call ga_dgemm('N', 'N', n, 1, n, 1.d0,
690
& g_A, g_Y1, 0.d0, g_Y2)
693
c**** Y1 = Y1 - Y = A * Y1 - Y
694
call ga_add(1.d0, g_Y2, -1.d0, g_Y, g_Y1)
696
c**** dnS = ||AY1 - Y|| / (||A|| * ||Y1|| * N * eps)
697
dnS = ga_dnormF(G_A1) / (dnA * dnY1 * n * eps)
699
call dthtest("||A*X - V||/(||A||*||X||*n*eps) =", dnS)
701
c**** if return code is > 0
703
call emsg('It is not positive definite the minor of order:',
708
c****************************************************
709
c Test Cholesky factorization and inversion
710
c internal interfaces
711
c****************************************************
712
call thead("Test II inversion of an SPD matrix")
716
call ga_copy(g_AA, g_A)
718
call ga_copy(g_A, g_X)
720
c**** call Cholesky factorization routine in ScaLAPACK PDPOTRF
721
c**** to obtain an LL'/U'U factorization
722
c**** (internal interface: it will not destroy X1)
724
irc = ga_llt_f(uplo, g_X, hsA)
726
c**** if return code from ga_llt_f is zero
729
c**** call Cholesky inversion routine in ScaLAPACK PDPOTRI
730
c**** internal interface
731
irc = ga_llt_i(uplo, g_X, hsA)
733
c**** if the inversion could be done
736
c**** dnX = ||X|| = ||invA||
739
c**** A1 = A * X = A * invA
741
call ga_copy(g_X,g_BB)
742
call ga_dgemm('N', 'N', n, n, n, 1.d0,
743
& g_AA, g_BB, 0.d0, g_CC)
744
call ga_copy(g_CC,g_A1)
746
call ga_dgemm('N', 'N', n, n, n, 1.d0,
747
& g_A, g_X, 0.d0, g_A1)
750
c**** A1 = A1 - I = A * invA - I
751
do j = me+1, n, nproc
752
call ga_get(g_A1, j, j, j, j, buf, 1)
753
buf(1) = buf(1) - 1.d0
754
call ga_put(g_A1, j, j, j, j, buf, 1)
757
c**** dnI = ||A*invA - I|| / (||A| * ||invA|| * N * eps)
758
dnI = ga_dnormF(g_A1) / (dnA * dnX * n * eps)
760
call dthtest('||A*invA - I||/(||A||*||invA||*n*eps) =',
764
c**** otherwise if the ga_llt_i return code is > 0
765
call emsg('there is a zero diagonal element:', irc)
768
c**** if return code is > 0
770
call emsg('It is not positive definite the minor of order:',
775
c****************************************************
776
c Test Cholesky inversion
777
c****************************************************
778
call thead("Test inversion of an SPD matrix")
782
call ga_copy(g_AA, g_A)
784
call ga_copy(g_A, g_X)
786
c**** call Cholesky L/U inversion
787
irc = ga_spd_invert(g_X)
789
c**** if return code from ga_spd_invert is zero
792
c**** dnX = ||X|| = ||invA||
795
c**** A1 = A * X = A * invA
797
call ga_copy(g_X, g_BB)
798
call ga_dgemm('N', 'N', n, n, n, 1.d0,
799
& g_AA, g_BB, 0.d0, g_CC)
800
call ga_copy(g_CC, g_A1)
802
call ga_dgemm('N', 'N', n, n, n, 1.d0,
803
& g_A, g_X, 0.d0, g_A1)
806
c**** A1 = A1 - I = A * invA - I
807
do j = me+1, n, nproc
808
call ga_get(g_A1, j, j, j, j, buf, 1)
809
buf(1) = buf(1) - 1.d0
810
call ga_put(g_A1, j, j, j, j, buf, 1)
813
c**** dnI = ||A*invA - I|| / (||A|| * ||invA|| * N * eps)
814
dnI = ga_dnormF(g_A1) / (dnA * dnX * n * eps)
816
call dthtest('||A*invA - I||/(||A||*||invA||*n*eps) =',
819
c**** if return code is < 0
820
elseif (irc.lt.0) then
821
call emsg('there is a zero diagonal element:', irc)
822
c**** if return code is > 0
823
elseif (irc.gt.0) then
824
call emsg('It is not positive definite the minor of order:',
829
c****************************************************
830
c* Test general solver with a NxN RSH
831
c* simmetric positive definite array
832
c****************************************************
834
& 'Test solver for a symmetric P.D. matrix and NxN RHS'
838
call ga_copy(g_B, g_X)
840
c**** call general solver with a NxN RHS p.d. symmetric array
842
call ga_copy(g_AA, g_A)
844
irc = ga_solve(g_A, g_X)
848
call ga_copy(g_X, g_BB)
849
call ga_dgemm('N', 'N', n, n, n, 1.d0,
850
& g_AA, g_BB, 0.d0, g_CC)
851
call ga_copy(g_CC, g_A1)
853
call ga_dgemm('N', 'N', n, n, n, 1.d0,
854
& g_A, g_X, 0.d0, g_A1)
857
c**** A1 = A1 - B = A * X - B
858
call ga_add(1.d0, g_A1, -1.d0, g_B, g_A1)
863
c**** dnS = ||AX - B|| / (||A|| * ||X|| * N * eps)
864
dnS = ga_dnormF(G_A1) / (dnA * dnX * n * eps)
866
call dthtest("||A*X - B||/(||A||*||X||*n*eps) =", dnS)
871
c****************************************************
872
c* Test general solver with a NxN RSH
873
c* simmetric non positive definite array
874
c****************************************************
876
& 'Test solver for a symmetric NON P.D. matrix and NxN RHS')
879
call ga_copy(g_B, g_X1)
881
c**** and now symmetrize X1
882
c**** so we obtain a symmetric matrix in X1
883
call ga_symmetrize(g_X1)
886
call ga_copy(g_B, g_X)
888
c**** call general solver with a NxN RHS symmetric array
890
call ga_copy(g_X1, g_BB)
891
irc = ga_solve(g_BB, g_X)
893
irc = ga_solve(g_X1, g_X)
897
dnX1 = ga_dnormF(g_X1)
904
call ga_copy(g_X1,g_AA)
905
call ga_copy(g_X,g_BB)
906
call ga_dgemm('N', 'N', n, n, n, 1.d0,
907
& g_AA, g_BB, 0.d0, g_CC)
908
call ga_copy(g_CC,g_A1)
910
call ga_dgemm('N', 'N', n, n, n, 1.d0,
911
& g_X1, g_X, 0.d0, g_A1)
914
c**** A1 = A1 - B = X1 * X - B
915
call ga_add(1.d0, g_A1, -1.d0, g_B, g_A1)
917
c**** dnS = ||X1*X - B|| / (||X1|| * ||X|| * N * eps)
918
dnS = ga_dnormF(G_A1) / (dnX1 * dnX * n * eps)
920
call dthtest("||A*X - B||/(||A||*||X||*n*eps) =", dnS)
925
c****************************************************
927
c****************************************************
928
c**** just print a newline and return
937
subroutine factor(p,idx,idy)
939
integer i,j,p,idx,idy,it
940
integer ip,ifac,pmax,prime(1280)
946
c factor p completely
947
c first, find all prime numbers less than or equal to p
952
if (mod(i,prime(j)).eq.0) go to 100
959
c find all prime factors of p
963
200 if (mod(ip,prime(i)).eq.0) then
971
c determine two factors of p of approximately the