Task: Pisano period




Go: package main

import "fmt"

func gcd(a, b uint) uint {
    if b == 0 {
        return a
    }
    return gcd(b, a%b)
}

func lcm(a, b uint) uint {
    return a / gcd(a, b) * b
}

func ipow(x, p uint) uint {
    prod := uint(1)
    for p > 0 {
        if p&1 != 0 {
            prod *= x
        }
        p >>= 1
        x *= x
    }
    return prod
}


func getPrimes(n uint) []uint {
    var primes []uint
    for i := uint(2); i <= n; i++ {
        div := n / i
        mod := n % i
        for mod == 0 {
            primes = append(primes, i)
            n = div
            div = n / i
            mod = n % i
        }
    }
    return primes
}


func isPrime(n uint) bool {
    switch {
    case n < 2:
        return false
    case n%2 == 0:
        return n == 2
    case n%3 == 0:
        return n == 3
    default:
        d := uint(5)
        for d*d <= n {
            if n%d == 0 {
                return false
            }
            d += 2
            if n%d == 0 {
                return false
            }
            d += 4
        }
        return true
    }
}


func pisanoPeriod(m uint) uint {
    var p, c uint = 0, 1
    for i := uint(0); i < m*m; i++ {
        p, c = c, (p+c)%m
        if p == 0 && c == 1 {
            return i + 1
        }
    }
    return 1
}


func pisanoPrime(p uint, k uint) uint {
    if !isPrime(p) || k == 0 {
        return 0 
    }
    return ipow(p, k-1) * pisanoPeriod(p)
}


func pisano(m uint) uint {
    primes := getPrimes(m)
    primePowers := make(map[uint]uint)
    for _, p := range primes {
        primePowers[p]++
    }
    var pps []uint
    for k, v := range primePowers {
        pps = append(pps, pisanoPrime(k, v))
    }
    if len(pps) == 0 {
        return 1
    }
    if len(pps) == 1 {
        return pps[0]
    }    
    f := pps[0]
    for i := 1; i < len(pps); i++ {
        f = lcm(f, pps[i])
    }
    return f
}

func main() {
    for p := uint(2); p < 15; p++ {
        pp := pisanoPrime(p, 2)
        if pp > 0 {
            fmt.Printf("pisanoPrime(%2d: 2) = %d\n", p, pp)
        }
    }
    fmt.Println()
    for p := uint(2); p < 180; p++ {
        pp := pisanoPrime(p, 1)
        if pp > 0 {
            fmt.Printf("pisanoPrime(%3d: 1) = %d\n", p, pp)
        }
    }
    fmt.Println()
    fmt.Println("pisano(n) for integers 'n' from 1 to 180 are:")
    for n := uint(1); n <= 180; n++ {
        fmt.Printf("%3d ", pisano(n))
        if n != 1 && n%15 == 0 {
            fmt.Println()
        }
    }    
    fmt.Println()
}


Java: import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

public class PisanoPeriod {

    public static void main(String[] args) {
        System.out.printf("Print pisano(p^2) for every prime p lower than 15%n");
        for ( long i = 2 ; i < 15 ; i++ ) { 
            if ( isPrime(i) ) {
                long n = i*i; 
                System.out.printf("pisano(%d) = %d%n", n, pisano(n));
            }
        }

        System.out.printf("%nPrint pisano(p) for every prime p lower than 180%n");
        for ( long n = 2 ; n < 180 ; n++ ) { 
            if ( isPrime(n) ) { 
                System.out.printf("pisano(%d) = %d%n", n, pisano(n));
            }
        }

        System.out.printf("%nPrint pisano(n) for every integer from 1 to 180%n");
        for ( long n = 1 ; n <= 180 ; n++ ) { 
            System.out.printf("%3d  ", pisano(n));
            if ( n % 10 == 0 ) {
                System.out.printf("%n");
            }
        }

        
    }
    
    private static final boolean isPrime(long test) {
        if ( test == 2 ) {
            return true;
        }
        if ( test % 2 == 0 ) {
            return false;
        }
        for ( long i = 3 ; i <= Math.sqrt(test) ; i += 2 ) {
            if ( test % i == 0 ) {
                return false;
            }
        }
        return true;
    }

    
    private static Map<Long,Long> PERIOD_MEMO = new HashMap<>();
    static {
        PERIOD_MEMO.put(2L, 3L);
        PERIOD_MEMO.put(3L, 8L);
        PERIOD_MEMO.put(5L, 20L);        
    }
    
    
    private static long pisano(long n) {
        if ( PERIOD_MEMO.containsKey(n) ) {
            return PERIOD_MEMO.get(n);
        }
        if ( n == 1 ) {
            return 1;
        }
        Map<Long,Long> factors = getFactors(n);
        
        
        
        if ( factors.size() == 1 & factors.get(2L) != null && factors.get(2L) > 0 ) {
            long result = 3 * n / 2;
            PERIOD_MEMO.put(n, result);
            return result;
        }
        
        if ( factors.size() == 1 & factors.get(5L) != null && factors.get(5L) > 0 ) {
            long result = 4*n;
            PERIOD_MEMO.put(n, result);
            return result;
        }
        
        if ( factors.size() == 2 & factors.get(2L) != null && factors.get(2L) == 1 && factors.get(5L) != null && factors.get(5L) > 0 ) {
            long result = 6*n;
            PERIOD_MEMO.put(n, result);
            return result;
        }
        
        List<Long> primes = new ArrayList<>(factors.keySet());
        long prime = primes.get(0);
        if ( factors.size() == 1 && factors.get(prime) == 1 ) {
            List<Long> divisors = new ArrayList<>();
            if ( n % 10 == 1 || n % 10 == 9 ) {
                for ( long divisor : getDivisors(prime-1) ) {
                    if ( divisor % 2 == 0 ) {
                        divisors.add(divisor);
                    }
                }
            }
            else {
                List<Long> pPlus1Divisors = getDivisors(prime+1);
                for ( long divisor : getDivisors(2*prime+2) ) {
                    if ( !  pPlus1Divisors.contains(divisor) ) {
                        divisors.add(divisor);
                    }
                }
            }
            Collections.sort(divisors);
            for ( long divisor : divisors ) {
                if ( fibModIdentity(divisor, prime) ) {
                    PERIOD_MEMO.put(prime, divisor);
                    return divisor;
                }
            }
            throw new RuntimeException("ERROR 144: Divisor not found.");
        }
        long period = (long) Math.pow(prime, factors.get(prime)-1) * pisano(prime);
        for ( int i = 1 ; i < primes.size() ; i++ ) {
            prime = primes.get(i);
            period = lcm(period, (long) Math.pow(prime, factors.get(prime)-1) * pisano(prime));
        }
        PERIOD_MEMO.put(n, period);
        return period;
    }
    
    
    private static boolean fibModIdentity(long n, long mod) {
        long aRes = 0;
        long bRes = 1;
        long cRes = 1;
        long aBase = 0;
        long bBase = 1;
        long cBase = 1;
        while ( n > 0 ) {
            if ( n % 2 == 1 ) {
                long temp1 = 0, temp2 = 0, temp3 = 0;
                if ( aRes > SQRT || aBase > SQRT || bRes > SQRT || bBase > SQRT || cBase > SQRT || cRes > SQRT ) {
                    temp1 = (multiply(aRes, aBase, mod) + multiply(bRes, bBase, mod)) % mod;
                    temp2 = (multiply(aBase, bRes, mod) + multiply(bBase, cRes, mod)) % mod;
                    temp3 = (multiply(bBase, bRes, mod) + multiply(cBase, cRes, mod)) % mod;
                }
                else {
                    temp1 = ((aRes*aBase % mod) + (bRes*bBase % mod)) % mod;
                    temp2 = ((aBase*bRes % mod) + (bBase*cRes % mod)) % mod;
                    temp3 = ((bBase*bRes % mod) + (cBase*cRes % mod)) % mod;
                }
                aRes = temp1;
                bRes = temp2;
                cRes = temp3;
            }
            n >>= 1L;
            long temp1 = 0, temp2 = 0, temp3 = 0; 
            if ( aBase > SQRT || bBase > SQRT || cBase > SQRT ) {
                temp1 = (multiply(aBase, aBase, mod) + multiply(bBase, bBase, mod)) % mod;
                temp2 = (multiply(aBase, bBase, mod) + multiply(bBase, cBase, mod)) % mod;
                temp3 = (multiply(bBase, bBase, mod) + multiply(cBase, cBase, mod)) % mod;
            }
            else {
                temp1 = ((aBase*aBase % mod) + (bBase*bBase % mod)) % mod;
                temp2 = ((aBase*bBase % mod) + (bBase*cBase % mod)) % mod;
                temp3 = ((bBase*bBase % mod) + (cBase*cBase % mod)) % mod;
            }
            aBase = temp1;
            bBase = temp2;
            cBase = temp3;
        }
        return aRes % mod == 0 && bRes % mod == 1 && cRes % mod == 1;
    }

    private static final long SQRT = (long) Math.sqrt(Long.MAX_VALUE);

    
    public static final long multiply(long a, long b, long modulus) {
        
        long x = 0;
        long y = a % modulus;
        long t;
        while ( b > 0 ) {
            if ( b % 2 == 1 ) {
                t = x + y;
                x = (t > modulus ? t-modulus : t);
            }
            t = y << 1;
            y = (t > modulus ? t-modulus : t);
            b >>= 1;
        }
        
        return x % modulus;
    }

    private static final List<Long> getDivisors(long number) {
        List<Long> divisors = new ArrayList<>();
        long sqrt = (long) Math.sqrt(number);
        for ( long i = 1 ; i <= sqrt ; i++ ) {
            if ( number % i == 0 ) {
                divisors.add(i);
                long div = number / i;
                if ( div != i ) {
                    divisors.add(div);
                }
            }
        }
        return divisors;
    }

    public static long lcm(long a, long b) {
        return a*b/gcd(a,b);
    }

    public static long gcd(long a, long b) {
        if ( b == 0 ) {
            return a;
        }
        return gcd(b, a%b);
    }

    private static final Map<Long,Map<Long,Long>> allFactors = new TreeMap<Long,Map<Long,Long>>();
    static {
        Map<Long,Long> factors = new TreeMap<Long,Long>();
        factors.put(2L, 1L);
        allFactors.put(2L, factors);
    }

    public static Long MAX_ALL_FACTORS = 100000L;

    public static final Map<Long,Long> getFactors(Long number) {
        if ( allFactors.containsKey(number) ) {
            return allFactors.get(number);
        }
        Map<Long,Long> factors = new TreeMap<Long,Long>();
        if ( number % 2 == 0 ) {
            Map<Long,Long> factorsdDivTwo = getFactors(number/2);
            factors.putAll(factorsdDivTwo);
            factors.merge(2L, 1L, (v1, v2) -> v1 + v2);
            if ( number < MAX_ALL_FACTORS ) {
                allFactors.put(number, factors);
            }
            return factors;
        }
        boolean prime = true;
        long sqrt = (long) Math.sqrt(number);
        for ( long i = 3 ; i <= sqrt ; i += 2 ) {
            if ( number % i == 0 ) {
                prime = false;
                factors.putAll(getFactors(number/i));
                factors.merge(i, 1L, (v1, v2) -> v1 + v2);
                if ( number < MAX_ALL_FACTORS ) {
                    allFactors.put(number, factors);
                }
                return factors;
            }
        }
        if ( prime ) {
            factors.put(number, 1L);
            if ( number < MAX_ALL_FACTORS ) { 
                allFactors.put(number, factors);
            }
        }
        return factors;
    }

}