// Filtered on Sonntag 2022-März-13 at 23:14:46 by Tippfilter 0.4-4-g83a7d89 (development build)

/** Diese Klasse enthält eine Methode zur schnellen modularen Potenzierung,
 * die bei RSA und anderen modernen Kryptoverfahren zum Einsatz kommt.
 * 
 * Sie ist als Programmierübung für Oberstufenschüler gedacht. Dabei stehen
 * die rekursive Implementierung und die modulo-Operation im Vordergrund.
 * 
 * @author Urs Lautebach
 * @author Tobias Nopper
 * @version 2021-03-24
 * Dieses Projekt steht unter der Lizenz CC BY-NC-SA 4.0
 * This project is licensed under CC BY-NC-SA 4.0
 * (https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode) */
public class SchnellePotenzierung {

    /** Berechnet die modulare Potenz a^b mod m.
     * Der Modulo der Rest bei der Division; 16:6 = 2 Rest 4, also ist 
     * 16 mod 6 = 4.
     * Java hat dafür den Operator "%"; "16%6" liefert also 4.
     * @param a Basis der Potenzierung
     * @param b Exponent der Potenzierung
     * @param m Modul der Restklasse, in der gerechnet wird
     * @return Das Ergebnis der Rechnung a^b mod m.     */
    public static int modPotenz(int a, int b, int m) {
        // Tipp: Überlegen Sie, was bei der Potenzierung a^b der einfachste
        // Fall ist (oder die einfachsten Fälle); die gleiche Überlegung gilt
        // auch für a^b mod m. 
        // Überlegen Sie dann, wie Sie andere Fälle darauf reduzieren können.
        /* Man kann für b >= 0 die folgenden Fälle unterscheiden:
         * 
         *              |      1       , falls b == 0
         *              |      a       , falls b == 1
         *  a^b mod m = | (a^(b-1))*a  , falls b ungerade
         *              | (a^(b/2))^2  , sonst
         * 
         * Analysieren Sie, ob Sie statt mit vier Fällen auch mit drei auskommen.
         * 
         * Achtung: In der obigen Fallunterscheidung müssen die Zwischenergebnisse 
         * _aller_ Rechenoperationen jeweils modulo m verkleinert werden (das mod m
         * ist der Übersicht wegen nur nicht aufgeschrieben). Weil Zwischenergebnisse 
         * sehr groß werden können, nimmt man für sie den Datentyp long.
         * 
         * Auch den Test gerade/ungerade kann man mit Modulo realisieren.   */
        
        // Der Basisfall ist b=0, dann ist das Ergebnis 1:
        if(b == 0) {
            return 1;
        } 
        // Das Zwischenergebnis kann größer sein, als int-Variablen
        // aufnehmen können, deswegen nimmt man dafür den Datentyp long: 
        long zwischenergebnis;

        // o Wenn b gerade, also b%2==1, dann...
        if(b % 2 == 1) {
            // - berechne a^(b-1) mod m (rekursiv) und speichere das Ergebnis
            zwischenergebnis = modPotenz(a, b - 1, m);

            // - multipliziere einmal a "dazu": 
            zwischenergebnis = zwischenergebnis * a;

        // o sonst (wenn also b gerade), dann...
        } else {
            // - berechne a^(b/2) mod m (rekursiv!), merk dir das Ergebnis...
            zwischenergebnis = modPotenz(a, b/2, m);

            // - ... und quadriere es
            zwischenergebnis = zwischenergebnis * zwischenergebnis ;
        }
        // o verkleinere das Zwischenergebnis modulo m: 
        zwischenergebnis = zwischenergebnis % m;

        // o wandle es zurück nach int (woher wissen wir, dass es jetzt
        //   auf jeden Fall in einen int passt?) und gib es zurück:
        return (int) zwischenergebnis;
    }
}

