001/*
002 * $Id$
003 */
004
005package edu.jas.root;
006
007
008import java.util.ArrayList;
009import java.util.List;
010
011import edu.jas.arith.BigDecimal;
012import edu.jas.arith.BigInteger;
013import edu.jas.arith.BigRational;
014import edu.jas.poly.GenPolynomial;
015import edu.jas.poly.GenPolynomialRing;
016import edu.jas.poly.PolyUtil;
017import edu.jas.structure.NotInvertibleException;
018import edu.jas.structure.Power;
019
020import junit.framework.Test;
021import junit.framework.TestCase;
022import junit.framework.TestSuite;
023
024
025/**
026 * RealAlgebraicNumber Test using JUnit.
027 * @author Heinz Kredel
028 */
029
030public class RealAlgebraicTest extends TestCase {
031
032
033    /**
034     * main.
035     */
036    public static void main(String[] args) {
037        junit.textui.TestRunner.run(suite());
038    }
039
040
041    /**
042     * Constructs a <CODE>RealAlgebraicTest</CODE> object.
043     * @param name String.
044     */
045    public RealAlgebraicTest(String name) {
046        super(name);
047    }
048
049
050    /**
051     * suite.
052     */
053    public static Test suite() {
054        TestSuite suite = new TestSuite(RealAlgebraicTest.class);
055        return suite;
056    }
057
058
059    RealAlgebraicRing<BigRational> fac;
060
061
062    GenPolynomialRing<BigRational> mfac;
063
064
065    RealAlgebraicNumber<BigRational> a, b, c, d, e;
066
067
068    RealAlgebraicNumber<BigRational> alpha;
069
070
071    int rl = 1;
072
073
074    int kl = 10;
075
076
077    int ll = 10;
078
079
080    int el = ll;
081
082
083    float q = 0.5f;
084
085
086    @Override
087    protected void setUp() {
088        a = b = c = d = e = null;
089        BigRational l = new BigRational(1);
090        BigRational r = new BigRational(2);
091        Interval<BigRational> positive = new Interval<BigRational>(l, r);
092        String[] vars = new String[] { "alpha" };
093        mfac = new GenPolynomialRing<BigRational>(new BigRational(1), rl, vars);
094        GenPolynomial<BigRational> mo = mfac.univariate(0, 2);
095        mo = mo.subtract(mfac.fromInteger(2)); // alpha^2 -2 
096        fac = new RealAlgebraicRing<BigRational>(mo, positive);
097        alpha = fac.getGenerator();
098    }
099
100
101    @Override
102    protected void tearDown() {
103        a = b = c = d = e = null;
104        fac = null;
105        alpha = null;
106    }
107
108
109    /**
110     * Test constructor and toString.
111     * 
112     */
113    public void testConstruction() {
114        c = fac.getONE();
115        //System.out.println("c = " + c);
116        //System.out.println("c.getVal() = " + c.getVal());
117        assertTrue("length( c ) = 1", c.number.getVal().length() == 1);
118        assertTrue("isZERO( c )", !c.isZERO());
119        assertTrue("isONE( c )", c.isONE());
120
121        d = fac.getZERO();
122        //System.out.println("d = " + d);
123        //System.out.println("d.getVal() = " + d.getVal());
124        assertTrue("length( d ) = 0", d.number.getVal().length() == 0);
125        assertTrue("isZERO( d )", d.isZERO());
126        assertTrue("isONE( d )", !d.isONE());
127    }
128
129
130    /**
131     * Test random polynomial.
132     */
133    public void testRandom() {
134        for (int i = 0; i < 7; i++) {
135            a = fac.random(el);
136            //System.out.println("a = " + a);
137            if (a.isZERO() || a.isONE()) {
138                continue;
139            }
140            // fac.random(kl*(i+1), ll+2*i, el+i, q );
141            assertTrue("length( a" + i + " ) <> 0", a.number.getVal().length() >= 0);
142            assertTrue(" not isZERO( a" + i + " )", !a.isZERO());
143            assertTrue(" not isONE( a" + i + " )", !a.isONE());
144        }
145    }
146
147
148    /**
149     * Test addition.
150     */
151    public void testAddition() {
152        a = fac.random(ll);
153        b = fac.random(ll);
154
155        c = a.sum(b);
156        d = c.subtract(b);
157        assertEquals("a+b-b = a", a, d);
158
159        c = a.sum(b);
160        d = b.sum(a);
161        assertEquals("a+b = b+a", c, d);
162
163        c = fac.random(ll);
164        d = c.sum(a.sum(b));
165        e = c.sum(a).sum(b);
166        assertEquals("c+(a+b) = (c+a)+b", d, e);
167
168
169        c = a.sum(fac.getZERO());
170        d = a.subtract(fac.getZERO());
171        assertEquals("a+0 = a-0", c, d);
172
173        c = fac.getZERO().sum(a);
174        d = fac.getZERO().subtract(a.negate());
175        assertEquals("0+a = 0+(-a)", c, d);
176    }
177
178
179    /**
180     * Test multiplication.
181     */
182    public void testMultiplication() {
183        a = fac.random(ll);
184        assertTrue("not isZERO( a )", !a.isZERO());
185
186        b = fac.random(ll);
187        assertTrue("not isZERO( b )", !b.isZERO());
188
189        c = b.multiply(a);
190        d = a.multiply(b);
191        assertTrue("not isZERO( c )", !c.isZERO());
192        assertTrue("not isZERO( d )", !d.isZERO());
193
194        //System.out.println("a = " + a);
195        //System.out.println("b = " + b);
196        e = d.subtract(c);
197        assertTrue("isZERO( a*b-b*a ) " + e, e.isZERO());
198
199        assertTrue("a*b = b*a", c.equals(d));
200        assertEquals("a*b = b*a", c, d);
201
202        c = fac.random(ll);
203        //System.out.println("c = " + c);
204        d = a.multiply(b.multiply(c));
205        e = (a.multiply(b)).multiply(c);
206
207        //System.out.println("d = " + d);
208        //System.out.println("e = " + e);
209
210        //System.out.println("d-e = " + d.subtract(c) );
211
212        assertEquals("a(bc) = (ab)c", d, e);
213        assertTrue("a(bc) = (ab)c", d.equals(e));
214
215        c = a.multiply(fac.getONE());
216        d = fac.getONE().multiply(a);
217        assertEquals("a*1 = 1*a", c, d);
218
219        c = a.inverse();
220        d = c.multiply(a);
221        //System.out.println("a = " + a);
222        //System.out.println("c = " + c);
223        //System.out.println("d = " + d);
224        assertEquals("a*1/a = 1", fac.getONE(), d);
225
226        try {
227            a = fac.getZERO().inverse();
228            fail("0 invertible");
229        } catch (NotInvertibleException expected) {
230            // pass
231        }
232    }
233
234
235    /**
236     * Test distributive law.
237     */
238    public void testDistributive() {
239        a = fac.random(ll);
240        b = fac.random(ll);
241        c = fac.random(ll);
242
243        d = a.multiply(b.sum(c));
244        e = a.multiply(b).sum(a.multiply(c));
245
246        assertEquals("a(b+c) = ab+ac", d, e);
247    }
248
249
250    /**
251     * Test sign of real algebraic numbers.
252     */
253    public void testSignum() {
254        a = fac.random(ll);
255        b = fac.random(ll);
256        c = fac.random(ll);
257
258        int sa = a.signum();
259        int sb = b.signum();
260        int sc = c.signum();
261
262        d = a.multiply(b);
263        e = a.multiply(c);
264
265        int sd = d.signum();
266        int se = e.signum();
267
268        assertEquals("sign(a*b) = sign(a)*sign(b) ", sa * sb, sd);
269        assertEquals("sign(a*c) = sign(a)*sign(c) ", sa * sc, se);
270
271        b = a.negate();
272        sb = b.signum();
273        assertEquals("sign(-a) = -sign(a) ", -sa, sb);
274    }
275
276
277    /**
278     * Test compareTo of real algebraic numbers.
279     */
280    public void testCompare() {
281        a = fac.random(ll).abs();
282        b = a.sum(fac.getONE());
283        c = b.sum(fac.getONE());
284
285        int ab = a.compareTo(b);
286        int bc = b.compareTo(c);
287        int ac = a.compareTo(c);
288
289        assertTrue("a < a+1 ", ab < 0);
290        assertTrue("a+1 < a+2 ", bc < 0);
291        assertTrue("a < a+2 ", ac < 0);
292
293        a = a.negate();
294        b = a.sum(fac.getONE());
295        c = b.sum(fac.getONE());
296
297        ab = a.compareTo(b);
298        bc = b.compareTo(c);
299        ac = a.compareTo(c);
300
301        assertTrue("a < a+1 ", ab < 0);
302        assertTrue("a+1 < a+2 ", bc < 0);
303        assertTrue("a < a+2 ", ac < 0);
304    }
305
306
307    /**
308     * Test arithmetic of magnitude of real algebraic numbers.
309     */
310    public void testMagnitude() {
311        a = fac.random(ll);
312        b = fac.random(ll);
313        c = fac.random(ll);
314
315        //BigDecimal ad = new BigDecimal(a.magnitude());
316        //BigDecimal bd = new BigDecimal(b.magnitude());
317        //BigDecimal cd = new BigDecimal(c.magnitude());
318
319        d = a.multiply(b);
320        e = a.sum(b);
321
322        BigDecimal dd = new BigDecimal(d.magnitude());
323        BigDecimal ed = new BigDecimal(e.magnitude());
324
325        BigDecimal dd1 = new BigDecimal(a.magnitude().multiply(b.magnitude()));
326        BigDecimal ed1 = new BigDecimal(a.magnitude().sum(b.magnitude()));
327
328        //System.out.println("ad  = " + ad);
329        //System.out.println("bd  = " + bd);
330        //System.out.println("cd  = " + cd);
331        //System.out.println("dd  = " + dd);
332        //System.out.println("dd1 = " + dd1);
333        //System.out.println("ed  = " + ed);
334        //System.out.println("ed1 = " + ed1);
335
336        //BigRational eps = Power.positivePower(new BigRational(1L,10L),BigDecimal.DEFAULT_PRECISION);
337        BigRational eps = Power.positivePower(new BigRational(1L, 10L), 8);
338        BigDecimal epsd = new BigDecimal(eps);
339
340        BigDecimal rel = dd.abs().sum(dd1.abs());
341        BigDecimal err = dd.subtract(dd1).abs().divide(rel);
342        //System.out.println("rel = " + rel);
343        //System.out.println("|dd-dd1|/rel = " + err + ", eps = " + epsd);
344        assertTrue("mag(a*b) = mag(a)*mag(b): " + dd + ", " + dd1, err.compareTo(epsd) <= 0);
345        assertTrue("mag(a+b) = mag(a)+mag(b): " + ed + ", " + ed1,
346                        ed.subtract(ed1).abs().compareTo(epsd) <= 0);
347
348
349        d = a.divide(b);
350        e = a.subtract(b);
351
352        dd = new BigDecimal(d.magnitude());
353        ed = new BigDecimal(e.magnitude());
354
355        dd1 = new BigDecimal(a.magnitude().divide(b.magnitude()));
356        ed1 = new BigDecimal(a.magnitude().subtract(b.magnitude()));
357
358        //System.out.println("dd  = " + dd);
359        //System.out.println("dd1 = " + dd1);
360        //System.out.println("ed  = " + ed);
361        //System.out.println("ed1 = " + ed1);
362
363        assertTrue("mag(a/b) = mag(a)/mag(b)", dd.subtract(dd1).abs().compareTo(epsd) <= 0);
364        assertTrue("mag(a-b) = mag(a)-mag(b)", ed.subtract(ed1).abs().compareTo(epsd) <= 0);
365    }
366
367
368    /**
369     * Test real root isolation.
370     */
371    public void testRealRootIsolation() {
372        //System.out.println();
373        GenPolynomialRing<RealAlgebraicNumber<BigRational>> dfac;
374        dfac = new GenPolynomialRing<RealAlgebraicNumber<BigRational>>(fac, 1);
375
376        GenPolynomial<RealAlgebraicNumber<BigRational>> ar;
377        ar = dfac.random(3, 5, 7, q);
378        //System.out.println("ar = " + ar);
379        if (ar.degree() % 2 == 0) { // ensure existence of a root
380            ar = ar.multiply(dfac.univariate(0));
381        }
382        //System.out.println("ar = " + ar);
383
384        RealRoots<RealAlgebraicNumber<BigRational>> rrr = new RealRootsSturm<RealAlgebraicNumber<BigRational>>();
385        List<Interval<RealAlgebraicNumber<BigRational>>> R = rrr.realRoots(ar);
386        //System.out.println("R = " + R);
387        assertTrue("#roots >= 0 ", R.size() > 0);
388
389        BigRational eps = Power.positivePower(new BigRational(1L, 10L), BigDecimal.DEFAULT_PRECISION);
390        BigDecimal epsd = new BigDecimal(Power.positivePower(new BigRational(1L, 10L), 14 - ar.degree())); // less
391        //System.out.println("eps = " + epsd);
392
393        R = rrr.refineIntervals(R, ar, eps);
394        //System.out.println("R = " + R);
395        for (Interval<RealAlgebraicNumber<BigRational>> v : R) {
396            RealAlgebraicNumber<BigRational> m = v.middle();
397            //System.out.println("m = " + m);
398            RealAlgebraicNumber<BigRational> n;
399            n = PolyUtil.<RealAlgebraicNumber<BigRational>> evaluateMain(fac, ar, m);
400            //System.out.println("n = " + n);
401            BigRational nr = n.magnitude();
402            //System.out.println("nr = " + nr);
403            BigDecimal nd = new BigDecimal(nr);
404            //System.out.println("nd = " + nd);
405            assertTrue("|nd| < eps: " + nd + " " + epsd, nd.abs().compareTo(epsd) <= 0);
406            //no: assertTrue("n == 0: " + n, n.isZERO());
407        }
408    }
409
410
411    /**
412     * Test real root isolation for Wilkinson like polynomials.
413     * product_{i=1..n}( x - i * alpha )
414     */
415    public void testRealRootIsolationWilkinson() {
416        //System.out.println();
417        GenPolynomialRing<RealAlgebraicNumber<BigRational>> dfac;
418        dfac = new GenPolynomialRing<RealAlgebraicNumber<BigRational>>(fac, 1);
419
420        GenPolynomial<RealAlgebraicNumber<BigRational>> ar, br, cr, dr, er;
421
422        RealRoots<RealAlgebraicNumber<BigRational>> rrr = new RealRootsSturm<RealAlgebraicNumber<BigRational>>();
423
424        final int N = 3;
425        dr = dfac.getONE();
426        er = dfac.univariate(0);
427
428        List<Interval<RealAlgebraicNumber<BigRational>>> Rn;
429        Rn = new ArrayList<Interval<RealAlgebraicNumber<BigRational>>>(N);
430        ar = dr;
431        for (int i = 0; i < N; i++) {
432            cr = dfac.fromInteger(i).multiply(alpha); // i * alpha
433            Rn.add(new Interval<RealAlgebraicNumber<BigRational>>(cr.leadingBaseCoefficient()));
434            br = er.subtract(cr); // ( x - i * alpha )
435            ar = ar.multiply(br);
436        }
437        //System.out.println("ar = " + ar);
438
439        List<Interval<RealAlgebraicNumber<BigRational>>> R = rrr.realRoots(ar);
440        //System.out.println("R = " + R);
441
442        assertTrue("#roots = " + N + " ", R.size() == N);
443
444        BigRational eps = Power.positivePower(new BigRational(1L, 10L), BigDecimal.DEFAULT_PRECISION);
445        eps = eps.multiply(new BigRational("1/10"));
446        //System.out.println("eps = " + eps);
447
448        R = rrr.refineIntervals(R, ar, eps);
449        //System.out.println("R = " + R);
450        int i = 0;
451        for (Interval<RealAlgebraicNumber<BigRational>> v : R) {
452            BigDecimal dd = v.toDecimal();//.sum(eps1);
453            BigDecimal di = Rn.get(i++).toDecimal();
454            //System.out.println("v  = " + dd);
455            //System.out.println("vi = " + di);
456            assertTrue("|dd - di| < eps ", dd.compareTo(di) == 0);
457        }
458    }
459
460
461    /**
462     * Test continued fraction.
463     */
464    public void testContinuedFraction() {
465        final int M = 100;
466        RealAlgebraicNumber<BigRational> x = fac.random(15);
467        //x = fac.parse("5/12");
468        //x = fac.parse("-1");
469        //x = fac.getGenerator(); // alpha = sqrt(2)
470        //System.out.println("x = " + x + ", mag(x) = " + x.decimalMagnitude());
471
472        List<BigInteger> cf = RealArithUtil.continuedFraction(x, M);
473        //System.out.println("cf(" + x + ") = " + cf);
474
475        BigRational a = RealArithUtil.continuedFractionApprox(cf);
476        RealAlgebraicNumber<BigRational> ax = fac.fromRational(a);
477        //System.out.println("ax = " + ax + ", mag(ax) = " + ax.decimalMagnitude());
478
479        BigRational eps = (new BigRational("1/10")).power(M / 2);
480        RealAlgebraicNumber<BigRational> dx = x.subtract(ax).abs();
481        // check ax > 1:
482        dx = dx.divide(ax.abs());
483        //System.out.println("dx = " + dx + ", mag(dx) = " + dx.decimalMagnitude());
484        BigRational dr = dx.magnitude();
485        assertTrue("|a - approx(cf(a))| = " + dr, dr.compareTo(eps) < 1);
486    }
487
488}