# A parallel algorithm for generation of RNA secondary structures with length *n* and *k* base-pairs

- 295 Downloads

## Abstract

RNA secondary structures are important in many biological processes since there is a close relationship between structure and function in biology. There is a known bijection between the possible shapes of a single-stranded RNA molecule of fixed length and trees with *n* nodes and *m* leaves, \(\mathcal {T}(n, m)\). In this article, we present a sequential algorithm for generating such trees in A-order. The worst case time complexity of the presented algorithm is *O*(*n*). We also develop a parallel version of the algorithm which is both cost-optimal and adaptive. We use Exclusive Read Exclusive Write Shared Memory Single Instruction Multiple Data multi-processor architecture. Moreover, the generated trees are in A-order.

## Keywords

Parallel algorithms Combinatorial object Tree generation RNA structure## 1 Introduction

Constructing a list of all non-isomorphic combinatorial objects of particular type is one of the classical problems in theoretical computing science that has attracted many studies during last decades. One can use such a list to test a hypothesis, search for a specific object, or gather statistics about a class of objects. Hence, a significant amount of knowledge exists concerning the combinatorial objects generation [8, 10, 12, 14, 22, 28, 39].

Trees are one of the fundamental combinatorial objects; they can be used to maintain any ordered set that must be accessed and updated. Thus, numerous sequential algorithms have been developed to generate different types of trees [9, 15, 16, 17, 20, 23, 25, 31, 32, 33, 35, 37]. In most of these algorithms, trees are encoded as integer sequences, and then, these sequences are generated with a certain order, and consequently, their corresponding trees are generated in a specific order [1, 13, 18, 21, 34, 36]. The most well-known orderings on trees are A-order and B-order [38]. The A-order definition uses global information concerning the tree nodes and appears to be a natural ordering of trees, whereas the B-order definition uses local information.

In addition to the sequential algorithms, a few parallel generation techniques are proposed to improve the speed between the generation of any two consecutive combinatorial objects [3, 4, 11, 26, 29]. Recently, in this order, a few parallel algorithms for the generation of trees have been published for various parallel models [2, 5].

Here, we consider generation algorithms for ordered trees with *n* nodes and *m* leaves. There is a one-to-one mapping between such trees and the secondary structures of a single-stranded RNA sequence of length \(2 n-m-2\), with exactly \(n-m-1\) base-pairs (without pseudoknots) [24]. A sequential algorithm with time complexity \(O(n m - m^2)\) is presented by Pallo [16], which generates such trees in B-order by embedding them in a subset of m-ary trees. Later on, Tabari et al. [27] proposed a new encoding that could be used to generate these trees in A-order with *O*(*n*) time complexity.

In this article, we present a new sequential algorithm for generating trees with *n* nodes and *m* leaves in A-order, which improves the amortized time of the Tabari et al. algorithm. We also develop a cost-optimal and adaptive algorithm for generating all shapes of such trees. The parallel computational model is a EREW Shared Memory Single Instruction Stream, Multiple Data Stream (SM SIMD) model.

The rest of the paper is organized as follows. Section 2 introduces the definitions and notions. In Sect. 3, a new sequential algorithm based on the generation algorithm given in [27] is discussed. In Sect. 4, the parallel generation algorithm is presented. Finally, some concluding remarks are offered in Sect. 5.

## 2 Notations and definitions

### 2.1 RNA secondary structure

*primary structure*. By folding back into itself, an RNA molecule forms the

*secondary structure*, and is stabilized by the formation of hydrogen bonds between certain pairs of bases and dense stacking of neighboring base-pairs. Figure 1a shows the sugar phosphate backbone as a solid line with bases as dots. Hydrogen bonds or base-pairs are represented as lines between non-neighboring dots [30]. Figure 1a illustrates an example of a so-called cloverleaf structure of the strand

*secondary structure*of an RNA can be presented as a planar graph, which can be embedded in the plane, i.e., it can be drawn on the plane in such a way that its edges intersect only at their endpoints. There is a known bijection between such structures onto the set of trees with

*n*nodes and

*m*leaves (for more details on trees, see next section). The bijection relation between the graph representation of RNA secondary structure and such trees is illustrated with the example given in Fig. 2a [30].

First, a node is inserted outside of all loops as the root of the tree. Then, inside of each arc, we insert a node as internal node of the tree. Each of internal nodes is connected to the nodes in the upper loops as their parents, and to any unpaired base in their corresponding loops as their children. Finally, all unpaired bases are considered as leaves of the tree. With regard to this bijection, the tree corresponding to the secondary structure of the RNA sequence given in Fig. 2a is shown in Fig. 2b [30].

With regard to the above discussion, the generation of the trees with *n* nodes and *m* leaves is equivalent to the generation of secondary structures of a single-stranded RNA sequence of length \(2n-m-2\), with exactly \(n-m-1\) base-pairs (without pseudoknots). For this reason, in this paper, we introduce a new generation algorithm for trees with *n* internal nodes and *m* external nodes.

### 2.2 Tree notations

A *tree* or *free tree* is a connected acyclic graph. A *rooted tree* is defined as a free tree with a specific node distinguished as the *root* node. There are known algorithms to generate free and rooted trees in constant average time (CAT) [23, 31]. An *ordered tree* is a rooted tree with a left-to-right ordering specified for children of each node. There are also known algorithms for generating ordered trees [25, 36].

*n*nodes and exactly

*m*leaves. It is proven that the total number of such trees can be counted using the Narayana numbers [6, 7, 19]:

*T*| denotes the number of leaves in a given tree

*T*. In addition, let \(r_T\) denotes the root node degree. The formal definition of A-order is as follows.

## Definition 1

*T*and \(T'\) from \(\mathcal {T}(n, m)\),

*T*precedes \(T'\) in A-order, \(T \prec _A T'\), if and only if:

- 1.
\(|T| < |T'|\), or

- 2.\(|T| = |T'|\), and \(\exists j \in \{1, 2,\ldots , \min (r_T, r_T')\}\) :
- (a)
\(T_i = T'_i: \forall i < j\), and

- (b)
\(T_j \prec _A T_j'\).

- (a)

In most generation algorithms, trees are encoded as integer sequences. These integer sequences are usually selected, such that generating them in specific order, like lexicographic order, which generate the corresponding trees in a specific order, like A-order. To encode trees with *n* nodes and *m* leaves, there are two known integer sequences, namely, P-sequence and E-sequence. P-sequence introduced by Pallo [16] embeds \(\mathcal {T}(n, m)\) in a subset of m-ary trees. On the other hand, E-sequence proposed by Tabari et al. [27] can be used to encode \(\mathcal {T}(n, m)\) directly.

## Definition 2

Given a tree *T* from the set \(\mathcal {T}(n, m)\), it can be represented by an integer sequence \(E_T = (e_1, e_2,\ldots , e_{n})\), where \(e_i\) is number of the leaves in the subtree whose root is the *i*th node of the *T* in the pre-order traversal.

*T*, the sequence \(Z_T = (z_1, z_2, \ldots , z_{n})\) can be constructed by evaluating \(z_i\) as the number of leaves visited after the

*i*th node in the pre-order traversal of

*T*. In other words, it can be calculated by counting the number of zeros between \(i+1\) to

*n*. In addition, Let \(P_T = (p_1, p_2, \ldots , p_n)\) be the parent sequence of the nodes in tree

*T*, where \(p_i\) denotes the parent of the

*i*th node. Figure 3 illustrates a sample tree from \(\mathcal {T}(13, 5)\) which corresponds to

The following theorems state the validity conditions and the relation between E-sequences and the ordering of the corresponding trees in \(\mathcal {T}(n, m)\) (see [27]).

## Theorem 1

*T*in \(\mathcal {T}(n, m)\), if and only if:

- 1.
\(e_1 = n-m\).

- 2.
\(\forall i \in \{1, \ldots , n - 1\}\) where \(0 < e_i, \exists j \in \{i + 1, \ldots , n\}\) such that \(e_j = 0\) and \(z_i - z_j = e_i\).

- 3.
\(\forall k \in {i + 1, \ldots , j - 1}: z_{i}- z_{k} \le e_i - e_k\).

## Theorem 2

Given two trees *T* and \(T'\) from \(\mathcal {T}(n, m)\), \(T \prec _A T'\) if and only if the \(E_T\) is lexicographically less than \(E_T'\).

For each internal node *i*, we define its left capacity with respect to a an arbitrary node *k* in subtree rooted by *i* as the number of leaves visited from the node *i* to the node *k* in pre-order traversal of tree *T*, which can be calculated using \(z_i - z_k\). The final node of each subtree is a leaf which satisfies equation \(e_i = z_i - z_k\) (according to Theorem 1). Respectively, the right capacity of the node *i* with respect to the node *k* is defined as the number of leaves in subtree rooted by *i*, which are visited after node *k* in pre-order traversal of *T*. We say that the node *i* is an “unsaturated ancestor” of node *k*, if it is ancestor of node *k* and its right capacity is not zero.

## 3 Sequential generation algorithm

*successor algorithm*. Generally speaking, the successor algorithm takes a combinatorial object and returns the next object in an specified order. Here, we design a successor algorithm, which takes the

*E*-sequence corresponding to a tree

*T*in \(\mathcal {T}(n, m)\), and produces the

*E*-sequence corresponding to the next tree in the A-order. The first and the last trees in the A-Order are shown in Fig. 4. The corresponding initial sequences are summarized as follows:

*rightmost incrementable*element. The rightmost incrementable element in the position

*k*is an element with the following properties: if the incrementable element is a leaf (\(E[k]=0\)), then it must satisfy the condition \(Z[k] < n-k\); otherwise, as mentioned in the validity conditions in Sect. 2, it must satisfy \(Z[P[k]] - Z[k] < E[P[k]] - E[k]\). In the former case, incrementing

*E*[

*k*] causes a leaf deletion; therefore, a leaf should be added after element

*E*[

*k*] to have a feasible sequence, while in the latter, the subtree rooted at

*k*should be able to accommodate more leaves. In other words, it should not be larger than the number of its parent’s leaves, excluding the leaves between them.

*k*th element is incremented. Afterwards, the remaining elements

*E*[

*j*] for \(k<j\le n\) are replaced by the smallest feasible E-sequence with \(n-k\) elements,

*Z*[

*k*] of which are zeros. After updating the E-sequence, \(Z_T\) and \(P_T\) can be updated consequently. The structure of incremented E-sequence is illustrated below:

*k*, \(s_2\) indicates the leaves which are distributed between ancestors of node

*k*, and \(s_3\) indicates a 1-chain of internal nodes, where a

*w*-chain is sequence of internal nodes followed by

*w*leaves. These subsequences (\(s_1, s_2\), and \(s_3\)) can be used to update the \(P_T\).

*E*-sequence is straightforward. Generally speaking,

*Z*[

*j*] for \(1 \le j \le n\) can be defined recursively as follows:

*E*-sequence after incrementation into account, we find that the

*Z*[

*j*] for \(k< j < k + Z[k]\) can be calculated directly from

*Z*[

*k*], since all the nodes between \(k+1\) and

*j*are leaves. As so, the update equation for

*Z*-sequence reduces to:

*j*in \(s_1\) and \(s_3\) subsequences is straightforward. The only tricky part is to update the nodes in the \(s_2\) subsequence. To calculate the parent of each node in \(s_2\), we start from the parent of the node

*k*, and in each iteration, we try to check the feasibility of the current ancestor. It is notable here, that all the internal nodes except the ancestors of node

*k*, have already saturated by leaves smaller than

*k*. To check feasibility of each ancestor, it is sufficient to evaluate their right capacity with respect to node

*k*.

Our algorithms differs from Tabari et al. [27] in its method of updating \(Z_T\) and \(P_T\). Tabari et al. algorithm calculates both the \(Z_T\) and \(P_T\) sequences from scratch in each iteration of the algorithm, whereas Algorithm 1 updates these sequences from the previous ones, by updating indices *k* to *n*. The running time of our algorithm is determined by the position of the rightmost incrementable element in the sequence. Although the worst case running time of the algorithms is the same, which is *O*(*n*), its amortized time complexity is much faster and there are statistical evidences to believe that the algorithm has *Constant Amortized Time* (CAT).

## 4 Parallel generation algorithm

*N*processors \(p_1, p_2, \ldots , p_N\), where \(1 \le N \le n\). Initially, the length of the E-sequence must be made known to all

*N*processors. This can be done using the

**Broadcast**procedure in \(O(\log (N))\) time complexity. The E-sequence is then subdivided into

*N*subsequences of length \(\lceil \frac{n-2}{N}\rceil \) each. All processors concurrently scan their subsequence to find the rightmost incrementable position and store it in

*M*[

*i*]. This process requires \(O(\frac{n-2}{N})\) time complexity. To find the increment position, between all the

*N*computed

*M*[

*i*] values, the maximum position is evaluated and broadcasted to all the processors using

**ParaMax**and

**Broadcast**procedures, which takes \(O(\log (N))\) time complexity. After finding the rightmost incrementable position

*k*and incrementing it, indices \(k+1\) to \(n-1\) of \(E_T, Z_T\), and \(P_T\) must be updated, respectively. As mentioned in the previous section, updating \(E_T\) and \(Z_T\) is straightforward. Similar approach can be taken to develop the parallel algorithm; lines 10–16 and 17–22 of the Algorithm 2 update these two sequences by distributing them between the

*N*processors. Recalling from the previous section that \(t_1\) and \(t_2\) indicate the number of leaves and internal nodes after node

*k*, respectively, this step takes \(O(\frac{n-k}{N})\), since we have \(t1 + t2 \le n - k\). Updating \(P_T\), which is illustrated in the Algorithm 3, involves updating parts \(s_1, s_2\) and \(s_3\) (for more information, see Sect. 3). Again, updating the parts \(s_1\) and \(s_3\) are easy. Lines 2–6 and 25–30 of the Algorithm 3 update the parts \(s_1\) and \(s_3\), respectively. It can be seen from the algorithm that these steps takes \(O(\frac{n - k}{N})\), since \(s_1 + s_3 \le n - k\). Updating part \(s_2\) needs more careful considerations. It is notable that the leaves between position \(k + E[k]\) and \(k + Z[k] - 1\) together with the first internal node are distributed among the unsaturated ancestors of node

*k*. To find the unsaturated ancestors of node

*k*and their right capacities with respect to node

*k*, we use the methods and definitions mentioned in Sect. 2. As mentioned there, the right capacity of each internal node

*j*with respect to node

*k*can be calculated using \(E[j] - Z[j] + Z[k] - E[k]\). After initializing the parent sequence for nodes in part \(s_2\), their right capacities with respect to node

*k*are calculated in lines 12–18 of the Algorithm 3. It has been done by dividing the range of indices less than

*k*, which are the corresponding indices to nodes which are seen before node

*k*in pre-order traversal of

*T*, between

*N*processors to calculate the indices of unsaturated ancestors of node

*k*. This step can be accomplished in \(O(\frac{k}{N})\) time complexity. Indices and right capacities of each unsaturated ancestor are stored in arrays

*I*and

*R*, respectively. Array

*T*shows the total amount of elements in each slice of work. After evaluating the

*T*,

*I*, and

*R*arrays in lines 12–18 of Algorithm 3, these arrays must be merged to find the final ancestor list. This is implemented in Algorithm 4, which takes

*T*,

*I*, and

*R*as input and generates

*Ancestor*and

*Index*sequences as output. This can also be done in \(O(\frac{k}{N})\) time complexity. Finally, we use

*Ancestor*and

*Index*arrays to fill the parent sequence of nodes in part \(s_2\). First, the initial child of each unsaturated ancestor is marked in lines 20–24 in \(O(\frac{n-m}{N})\) time complexity. Second, the gaps are filled by a simple swiping mechanism which is done by a parallel max in \(O(\log (n-k))\) time complexity, since \(Z[k] - E[k] \le n - k\). Taking the time complexity of each step into account, the worst case running time of Algorithm 2 is \(O(\frac{n}{N} + \log (N) + \log (n))\). The cost-optimality and adaptivity of the algorithm is proven by the following theorem.

## Theorem 3

Algorithm 2 for generating successor E-sequence of a given tree T is cost-optimal and adaptive.

## Proof

## 5 Conclusion

In this paper, we have presented a bijection between the set of trees with *n* nodes and *m* leaves, and the set of RNA secondary structures of a single-stranded RNA sequence of length \(2n-m-2\), with exactly \(n-m-1\) base-pairs (without pseudoknots). Later on, we have developed a new sequential algorithm, based on the encoding proposed in [27], for generating trees with *n* nodes and *m* leaves in A-order. Our algorithm has the same worst case running time, but enhances the amortized running time. Finally, a cost-optimal and adaptive parallel edition of the algorithm is designed. The computational model which is used in our algorithm is EREW SM SIMD multi-processor. Moreover, the generated trees are in A-order. The time complexity of our parallel algorithm for generating next tree in A-order using *N* processors is \(O(\frac{n}{N} + \log (N) + \log (n))\), which shows that the algorithm is cost-optimal for \(N\le \frac{n}{\log n}\).

Our future works are focused on proving the CAT property of our sequential algorithm. There are evidences which confirm this claim; however, there is no mathematical proof yet. This mathematical foundations might also help developing new rank/unrank algorithms for these kinds of trees.

## References

- 1.Ahmadi-Adl, A., Ahrabian, H., Nowzari-Dalini, A.: Ranking and unranking algorithms for loopless generation of \(t\)-ary trees. Log. J. IGPL
**19**, 33–43 (2011)MathSciNetCrossRefMATHGoogle Scholar - 2.Ahrabian, H., Nowzari-Dalini, A.: Parallel generation of \(t\)-ary trees in a-order. Comput. J.
**50**, 581–588 (2007)CrossRefMATHGoogle Scholar - 3.Akl, S.G., Gries, D., Stojmenovic, I.: An optimal parallel algorithm for generating combination. Inf. Process. Lett.
**33**, 135–139 (1990)MathSciNetCrossRefMATHGoogle Scholar - 4.Akl, S.G., Meijer, H., Stojmenovic, I.: An optimal systolic algorithm for generating permutations in lexicographic order. J. Parallel Distrib. Comput.
**20**, 84–91 (1994)CrossRefMATHGoogle Scholar - 5.Akl, S.G., Stojmenovic, I.: Generating \(t\)-ary trees in parallel. Nordic J. Comput.
**3**, 63–71 (1996)MathSciNetGoogle Scholar - 6.Chen, W.Y.: A general bijective algorithm for trees. PNAS
**87**, 9635–9639 (1990)MathSciNetCrossRefMATHGoogle Scholar - 7.Dershowitz, N., Zaks, S.: Enumerations of ordered trees. Discrete Math.
**31**, 9–28 (1980)MathSciNetCrossRefMATHGoogle Scholar - 8.Effler, S., Ruskey, F.: A CAT algorithm for generating permutations with a fixed number of inversions. Inf. Process. Lett.
**86**, 107–112 (2003)MathSciNetCrossRefMATHGoogle Scholar - 9.Er, M.C.: Efficient generation of \(k\)-ary trees in natural order. Comput. J.
**35**, 306–308 (1992)CrossRefMATHGoogle Scholar - 10.Heubach, S., Li, N., Mansour, T.: Staircase tilings and \(k\)-catalan structures. Discrete Math.
**308**, 5954–5964 (2008)MathSciNetCrossRefMATHGoogle Scholar - 11.Kapralski, A.: New methods for the generation of permutations, combinations and other combinatorial objects in parallel. J. Parallel Distrib. Comput.
**17**, 315–329 (1992)MathSciNetCrossRefMATHGoogle Scholar - 12.Knuth, D.E.: The art of computer programming, volume 4 combinatorial algorithms. Addison-Wesley, Reading (2006)Google Scholar
- 13.Korsh, J.F.: A-order generation of \(k\)-ary trees with \(4k-4\) letter alphabet. J. Inf. Optim. Sci.
**16**, 557–567 (1995)MathSciNetMATHGoogle Scholar - 14.Kreher, D.L., Stinson, D.R.: Combinatorial Algorithms. CRC Press, New York (1999)MATHGoogle Scholar
- 15.Manes, K., Sapounakis, A., Tasoulas, I., Tsikouras, P.: Recursive generation of \(k\)-ary trees. J. Integer Seq.
**12**, 1–18 (2009)MathSciNetMATHGoogle Scholar - 16.Pallo, J.: Generating trees with \(n\) nodes and \(m\) leaves. Int. J. Comput. Math.
**21**, 133–144 (1987)CrossRefMATHGoogle Scholar - 17.Pallo, J.: A simple algorithm for generating neuronal dendritic trees. Comput. Methods Progr. Biomed.
**33**, 165–169 (1990)CrossRefGoogle Scholar - 18.Pallo, J., Racca, R.: A note on generating binary tree in A-order and B-order. Int. J. Comput. Math.
**18**, 27–39 (1985)CrossRefMATHGoogle Scholar - 19.Prodinger, H.: A correspondence between orderd trees and nondecreasing partitions. Discrete Math.
**46**, 205–206 (1983)MathSciNetCrossRefMATHGoogle Scholar - 20.Roelants van Baronaigien, D., Ruskey, F.: Generating \(t\)-ary trees in A-order. Inf. Process. Lett.
**27**, 205–213 (1988)MathSciNetCrossRefMATHGoogle Scholar - 21.Ruskey, F., Hu, T.C.: Generating binary tree lexicographically. SIAM J. Comput.
**6**, 745–758 (1977)MathSciNetCrossRefMATHGoogle Scholar - 22.Ruskey, F., Williams, A.: The coolest way to generate combinations. Discrete Math.
**309**, 5305–5320 (2006)MathSciNetCrossRefMATHGoogle Scholar - 23.Sawada, J.: Generating rooted and free plane trees. ACM Trans. Algorithms
**2**, 1–13 (2006)MathSciNetCrossRefMATHGoogle Scholar - 24.Schmitt, W.R., Waterman, M.S.: Linear trees and RNA secondary structure. Discrete Appl. Math.
**51**, 317–323 (1994)MathSciNetCrossRefMATHGoogle Scholar - 25.Skarbek, W.: Generating ordered trees. Theor. Comput. Sci.
**57**, 153–159 (1988)MathSciNetCrossRefMATHGoogle Scholar - 26.Stojmenovic, I.: Listing combinatorial objects in parallel. Int. J. Parallel Emergent Distrib. Syst.
**21**, 127–146 (2006)MathSciNetCrossRefMATHGoogle Scholar - 27.Tabari, S., Ahrabian, H., Nowzari-Dalini, A.: A new algorithm for generation of different types of RNA with length \(n\). Int. J. Comput. Math.
**86**, 1197–1207 (2010)CrossRefMATHGoogle Scholar - 28.Takaoka, T.: \(O(1)\) time algorithms for combinatorial generation by tree traversal. Comput. J.
**42**, 400–408 (1999)CrossRefMATHGoogle Scholar - 29.Vajnovszki, V., Pallo, J.: Parallel algorithms for listing well-formed parentheses strings. Parallel Process. Lett.
**8**, 19–28 (1998)MathSciNetCrossRefGoogle Scholar - 30.Waterman, M.S.: Introduction to Computational Biology. CRC Press, New York (1995)CrossRefMATHGoogle Scholar
- 31.Wright, R.A., Richmond, B., Odlyzko, A., McKey, B.D.: Constant time generation of free trees. SIAM J. Comput.
**15**, 540–548 (1986)MathSciNetCrossRefMATHGoogle Scholar - 32.Wu, R., Chang, J., Chang, C.: Ranking and unranking of non-regular trees with a prescribed branching sequence. Math. Comput. Model.
**53**, 1331–1335 (2011)MathSciNetCrossRefMATHGoogle Scholar - 33.Wu, R., Chang, J., Wang, Y.: Loopless generation of non-regular trees with a prescribed branching sequence. Comput. J.
**53**, 661–666 (2010)CrossRefGoogle Scholar - 34.Wu, R., Chang, J., Chen, A., Liu, C.: Ranking and unranking t-ary trees in a Gray-code order. Comput. J.
**56**, 1388–1395 (2013)CrossRefGoogle Scholar - 35.Xiang, L., Ushijima, K., Akl, S.: Generating regular \(k\)-ary trees efficiently. Comput. J.
**43**, 290–300 (2000)CrossRefMATHGoogle Scholar - 36.Zaks, S.: Lexicographic generation of ordered tree. Theor. Comput. Sci.
**10**, 63–82 (1980)MathSciNetCrossRefMATHGoogle Scholar - 37.Zaks, S.: Generation and ranking of \(k\)-ary trees. Inf. Process. Lett.
**14**, 44–48 (1982)MathSciNetCrossRefGoogle Scholar - 38.Zaks, S., Richards, D.: Generating trees and other combinatorial objects lexicographically. SIAM J. Comput.
**8**, 73–81 (1979)MathSciNetCrossRefMATHGoogle Scholar - 39.Zoghbi, A., Stojmenovic, I.: Fast algorithms for generating integer partitions. Int. J. Comput. Math.
**70**, 319–332 (1998)MathSciNetCrossRefMATHGoogle Scholar