/*
 * This file is part of the Amalthea library.
 * Copyright (C) 2019, 2021, 2024, 2026 Eugene 'Vindex' Stulin
 * Distributed under the BSL 1.0 or the GNU LGPL 3.0 or later.
 */

/**
 * The module contains several mathematical functions.
 */
module amalthea.math;

unittest {
    assert(0 == gcd(0, 0));
    assert(gcd(4, 0) == 4 && 4 == gcd(0, 4));
    assert(gcd(1, 1) == 1);
    assert(gcd(1, 0) == 1 && gcd(0, 1) == 1);
    assert(gcd(1, 2) == 1 && gcd(2, 1) == 1);
    assert(gcd(70, 105) == 35 && gcd(105, 70) == 35);
}
/*******************************************************************************
 * Greatest common divisor.
 */
ulong gcd(ulong a, ulong b) {
    if (a == 0) {
        return b;
    }
    if (b == 0) {
        return a;
    }
    while (a != b) {
        if (a > b) {
            a -= b;
        } else {
            b -= a;
        }
    }
    return a;
}


/*******************************************************************************
 * Least common multiple.
 */
ulong lcm(ulong a, ulong b) {
    return a*b / gcd(a, b);
}


/*******************************************************************************
 * This function checks if a number is prime.
 */
bool isPrimeNumber(ulong n) {
    if (n < 2) {
        return false;
    }
    for (ulong i = 2; i <= n/2; ++i) {
        if (n%i == 0) {
            return false;
        }
    }
    return true;
}


/*******************************************************************************
 * Returns generator of Fibonacci numbers.
 */
FibonacciRange!T fibonacci(T=long)()
if (is(T : ulong)) {
	return FibonacciRange!T();
}
unittest {
    import std.range : take;
    import std.array : array;
    auto fibSequence = array(take(fibonacci(), 8));
    assert(fibSequence == [1, 1, 2, 3, 5, 8, 13, 21]);
}



/*******************************************************************************
 * Range of Fibonacci numbers for the 'fibonacci' function.
 */
struct FibonacciRange(T) if (is(T : ulong)) {
    T prevValue = 0;
    T currValue = 1;

    bool empty() const {
        return false;
    }

    T popFront() {
        currValue += prevValue;
        prevValue = currValue - prevValue;
        return prevValue;
    }

    T front() const {
        return currValue;
    }
}


/*******************************************************************************
 * Logarithm.
 */
real logarithm(real b, real n) {
    import std.math : log;
    return log(b)/log(n);
}
unittest {
    assert(logarithm(1024*1024, 1024) == 2);
}
