001/*
002 * $Id: Power.java 5636 2016-11-16 20:45:57Z kredel $
003 */
004
005package edu.jas.structure;
006
007
008import java.util.List;
009
010import org.apache.log4j.Logger;
011
012
013/**
014 * Power class to compute powers of RingElem.
015 * @author Heinz Kredel
016 */
017public class Power<C extends RingElem<C>> {
018
019
020    private static final Logger logger = Logger.getLogger(Power.class);
021
022
023    private static final boolean debug = logger.isDebugEnabled();
024
025
026    private final RingFactory<C> fac;
027
028
029    /**
030     * The constructor creates a Power object.
031     */
032    public Power() {
033        this(null);
034    }
035
036
037    /**
038     * The constructor creates a Power object.
039     * @param fac ring factory
040     */
041    public Power(RingFactory<C> fac) {
042        this.fac = fac;
043    }
044
045
046    /**
047     * power of a to the n-th, n positive.
048     * @param a element.
049     * @param n integer exponent > 0.
050     * @return a^n.
051     */
052    public static <C extends RingElem<C>> C positivePower(C a, long n) {
053        if (n <= 0) {
054            throw new IllegalArgumentException("only positive n allowed");
055        }
056        if (a.isZERO() || a.isONE()) {
057            return a;
058        }
059        C b = a;
060        long i = n - 1;
061        C p = b;
062        do {
063            if (i % 2 == 1) {
064                p = p.multiply(b);
065            }
066            i = i / 2;
067            if (i > 0) {
068                b = b.multiply(b);
069            }
070        } while (i > 0);
071        return p;
072    }
073
074
075    /**
076     * power of a to the n-th, n positive.
077     * @param a element.
078     * @param n java.math.BigInteger exponent > 0.
079     * @return a^n.
080     */
081    public static <C extends RingElem<C>> C positivePower(C a, java.math.BigInteger n) {
082        if (n.signum() <= 0) {
083            throw new IllegalArgumentException("only positive n allowed");
084        }
085        if (a.isZERO() || a.isONE()) {
086            return a;
087        }
088        C b = a;
089        if (n.compareTo(java.math.BigInteger.ONE) == 0) {
090            return b;
091        }
092        if (n.bitLength() <= 63) {
093            long l = n.longValue();
094            return positivePower(a, l);
095        }
096        C p = a;
097        java.math.BigInteger i = n.subtract(java.math.BigInteger.ONE);
098        do {
099            if (i.testBit(0)) {
100                p = p.multiply(b);
101            }
102            i = i.shiftRight(1);
103            if (i.signum() > 0) {
104                b = b.multiply(b);
105            }
106        } while (i.signum() > 0);
107        return p;
108    }
109
110
111    /**
112     * power of a to the n-th, n positive, modulo m.
113     * @param a element.
114     * @param n integer exponent > 0.
115     * @param m modulus.
116     * @return a^n mod m.
117     */
118    public static <C extends RingElem<C>> C modPositivePower(C a, long n, C m) {
119        if (n <= 0) {
120            throw new IllegalArgumentException("only positive n allowed");
121        }
122        if (a.isZERO() || a.isONE()) {
123            return a;
124        }
125
126        C b = a.remainder(m);
127        long i = n - 1;
128        C p = b;
129        do {
130            if (i % 2 == 1) {
131                p = p.multiply(b).remainder(m);
132            }
133            i = i / 2;
134            if (i > 0) {
135                b = b.multiply(b).remainder(m);
136            }
137        } while (i > 0);
138        return p;
139    }
140
141
142    /**
143     * power of a to the n-th.
144     * @param a element.
145     * @param n integer exponent.
146     * @param fac ring factory.
147     * @return a^n, with 0^0 = 0 and a^{-n} = {1/a}^n.
148     */
149    @SuppressWarnings("unchecked")
150    public static <C extends RingElem<C>> C power(RingFactory<C> fac, C a, long n) {
151        if (a == null || a.isZERO()) {
152            return a;
153        }
154        //return a;
155        return (C) Power.<MonoidElem> power((MonoidFactory) fac, a, n);
156    }
157
158
159    /**
160     * power of a to the n-th.
161     * @param a element.
162     * @param n integer exponent.
163     * @param fac monoid factory.
164     * @return a^n, with a^{-n} = {1/a}^n.
165     */
166    public static <C extends MonoidElem<C>> C power(MonoidFactory<C> fac, C a, long n) {
167        if (n == 0) {
168            if (fac == null) {
169                throw new IllegalArgumentException("fac may not be null for a^0");
170            }
171            return fac.getONE();
172        }
173        if (a.isONE()) {
174            return a;
175        }
176        C b = a;
177        if (n < 0) {
178            b = a.inverse();
179            n = -n;
180        }
181        if (n == 1) {
182            return b;
183        }
184        C p = fac.getONE();
185        long i = n;
186        do {
187            if (i % 2 == 1) {
188                p = p.multiply(b);
189            }
190            i = i / 2;
191            if (i > 0) {
192                b = b.multiply(b);
193            }
194        } while (i > 0);
195        if (n > 11 && debug) {
196            logger.info("n  = " + n + ", p  = " + p);
197        }
198        return p;
199    }
200
201
202    /**
203     * power of a to the n-th modulo m.
204     * @param a element.
205     * @param n integer exponent.
206     * @param m modulus.
207     * @param fac monoid factory.
208     * @return a^n mod m, with a^{-n} = {1/a}^n.
209     */
210    public static <C extends MonoidElem<C>> C modPower(MonoidFactory<C> fac, C a, long n, C m) {
211        if (n == 0) {
212            if (fac == null) {
213                throw new IllegalArgumentException("fac may not be null for a^0");
214            }
215            return fac.getONE();
216        }
217        if (a.isONE()) {
218            return a;
219        }
220        C b = a.remainder(m);
221        if (n < 0) {
222            b = a.inverse().remainder(m);
223            n = -n;
224        }
225        if (n == 1) {
226            return b;
227        }
228        C p = fac.getONE();
229        long i = n;
230        do {
231            if (i % 2 == 1) {
232                p = p.multiply(b).remainder(m);
233            }
234            i = i / 2;
235            if (i > 0) {
236                b = b.multiply(b).remainder(m);
237            }
238        } while (i > 0);
239        if (n > 11 && debug) {
240            logger.info("n  = " + n + ", p  = " + p);
241        }
242        return p;
243    }
244
245
246    /**
247     * power of a to the n-th modulo m.
248     * @param a element.
249     * @param n integer exponent.
250     * @param m modulus.
251     * @param fac monoid factory.
252     * @return a^n mod m, with a^{-n} = {1/a}^n.
253     */
254    public static <C extends MonoidElem<C>> C modPower(MonoidFactory<C> fac, C a, java.math.BigInteger n, C m) {
255        if (n.signum() == 0) {
256            if (fac == null) {
257                throw new IllegalArgumentException("fac may not be null for a^0");
258            }
259            return fac.getONE();
260        }
261        if (a.isONE()) {
262            return a;
263        }
264        C b = a.remainder(m);
265        if (n.signum() < 0) {
266            b = a.inverse().remainder(m);
267            n = n.negate();
268        }
269        if (n.compareTo(java.math.BigInteger.ONE) == 0) {
270            return b;
271        }
272        if (n.bitLength() <= 63) {
273            long l = n.longValue();
274            return modPower(fac, a, l, m);
275        }
276        C p = fac.getONE();
277        java.math.BigInteger i = n;
278        do {
279            if (i.testBit(0)) {
280                p = p.multiply(b).remainder(m);
281            }
282            i = i.shiftRight(1);
283            if (i.signum() > 0) {
284                b = b.multiply(b).remainder(m);
285            }
286        } while (i.signum() > 0);
287        if (debug) {
288            logger.info("n  = " + n + ", p  = " + p);
289        }
290        return p;
291    }
292
293
294    /**
295     * power of a to the n-th.
296     * @param a element.
297     * @param n integer exponent.
298     * @return a^n, with 0^0 = 0.
299     */
300    public C power(C a, long n) {
301        return power(fac, a, n);
302    }
303
304
305    /**
306     * power of a to the n-th.
307     * @param a long.
308     * @param n integer exponent.
309     * @return a^n, with a^0 = 1.
310     */
311    public static long power(long a, long n) {
312        if (n == 0) {
313            return 1L;
314        }
315        if (a == 1L) {
316            return a;
317        }
318        long b = a;
319        if (n == 1L) {
320            return b;
321        }
322        long p = 1L;
323        long i = n;
324        do {
325            if (i % 2 == 1) {
326                p = p * b;
327            }
328            i = i / 2;
329            if (i > 0) {
330                b = b * b;
331            }
332        } while (i > 0);
333        if (n > 11 && debug) {
334            logger.info("n  = " + n + ", p  = " + p);
335        }
336        return p;
337    }
338
339
340    /**
341     * power of a to the n-th mod m.
342     * @param a element.
343     * @param n integer exponent.
344     * @param m modulus.
345     * @return a^n mod m, with 0^0 = 0.
346     */
347    public C modPower(C a, long n, C m) {
348        return modPower(fac, a, n, m);
349    }
350
351
352    /**
353     * power of a to the n-th mod m.
354     * @param a element.
355     * @param n integer exponent.
356     * @param m modulus.
357     * @return a^n mod m, with 0^0 = 0.
358     */
359    public C modPower(C a, java.math.BigInteger n, C m) {
360        return modPower(fac, a, n, m);
361    }
362
363
364    /**
365     * Logarithm.
366     * @param p logarithm base.
367     * @param a element.
368     * @return k &ge; 1 minimal with p^k &ge; b.
369     */
370    public static <C extends RingElem<C>> long logarithm(C p, C a) {
371        //if ( p.compareTo(a) < 0 ) {
372        //    return 0L;
373        //}
374        long k = 1L;
375        C m = p;
376        while (m.compareTo(a) < 0) {
377            m = m.multiply(p);
378            k++;
379        }
380        return k;
381    }
382
383
384    /**
385     * Multiply elements in list.
386     * @param A list of elements (a_0,...,a_k).
387     * @param fac ring factory.
388     * @return prod(i=0,...k) a_i.
389     */
390    public static <C extends RingElem<C>> C multiply(RingFactory<C> fac, List<C> A) {
391        return multiply((MonoidFactory<C>) fac, A);
392    }
393
394
395    /**
396     * Multiply elements in list.
397     * @param A list of elements (a_0,...,a_k).
398     * @param fac monoid factory.
399     * @return prod(i=0,...k) a_i.
400     */
401    public static <C extends MonoidElem<C>> C multiply(MonoidFactory<C> fac, List<C> A) {
402        if (fac == null) {
403            throw new IllegalArgumentException("fac may not be null for empty list");
404        }
405        C res = fac.getONE();
406        if (A == null || A.isEmpty()) {
407            return res;
408        }
409        for (C a : A) {
410            res = res.multiply(a);
411        }
412        return res;
413    }
414
415
416    /**
417     * Sum elements in list.
418     * @param A list of elements (a_0,...,a_k).
419     * @param fac ring factory.
420     * @return sum(i=0,...k) a_i.
421     */
422    public static <C extends RingElem<C>> C sum(RingFactory<C> fac, List<C> A) {
423        return sum((AbelianGroupFactory<C>) fac, A);
424    }
425
426
427    /**
428     * Sum elements in list.
429     * @param A list of elements (a_0,...,a_k).
430     * @param fac monoid factory.
431     * @return sum(i=0,...k) a_i.
432     */
433    public static <C extends AbelianGroupElem<C>> C sum(AbelianGroupFactory<C> fac, List<C> A) {
434        if (fac == null) {
435            throw new IllegalArgumentException("fac may not be null for empty list");
436        }
437        C res = fac.getZERO();
438        if (A == null || A.isEmpty()) {
439            return res;
440        }
441        for (C a : A) {
442            res = res.sum(a);
443        }
444        return res;
445    }
446
447}