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