/**
 * Kelas Logarithm untuk menghitung logaritma. Metode perhitungan menggunakan Binary
 * Logarithm, yaitu logaritma dalam basis 2.
 * 
 * @author Hendra Jaya
 * @since 17 Oktober 2010
 */
class Logarithm {
	/**
	 * Nilai lb(10) dan lb(e).
	 * Dipersiapkan untuk Common Logarithm dan Natural Logarithm.
	 * Kedua nilai dihitung menggunakan toleransi galat {10}^{-20} 
	 */
    private static final double lb_10 = 3.321928094887362D;
    private static final double lb_e  = 1.4426950408889634D;

    /**
     * Toleransi galat. Di-set ke {10}^{-20}. Cukup untuk penggunaan sehari-hari
     */
    private static final double err = 1E-20D;
    
    /**
     * Common Logarithm. Logaritma dalam basis 10
     * @param x bilangan real positif
     * @return log_10(x)
     */
    public static double log(double x){
        return lb(x) / lb_10;
    }
    
    /**
     * Logarithm. Logaritma dalam basis sesuai keinginan user
     * @param base bilangan real positif yang akan dijadikan basis logaritma
     * @param x bilangan real positif
     * @return log_base(x)
     */
    public static double log(double base, double x){
        return lb(x) / lb(base);
    }
    
    /**
     * Natural Logarithm. Logaritma dalam basis e. Yang dimaksud dengan e adalah
     * bilangan Euler, yaitu 2.71828182845904523536...
     * @param x bilangan real positif
     * @return log_e(x)
     */
    public static double ln(double x){
        return lb(x) / lb_e;
    }
    
    /**
     * Binary Logarithm. Logaritma dalam basis 2
     * @param x
     * @return log_2(x)
     */
    public static double lb(double x){
        assert(x > 0D);
        
        double result = 0D;

        while (x < 1){
            result -= 1D;
            x *= 2D;
        }
        
        while (x >= 2){
            result += 1D;
            x /= 2D;
        }
        
        double frac = 1D;
        while(frac > err){
            frac /= 2D;
            x *= x;

            if (x >= 2D){
                x /= 2D;
                result += frac;
            }
        }

        return result;
    }
    
    public static void main(String[] args) {
        System.out.println(4.5 + "\t" + lb(4.5) + "\t" + ln(4.5) + "\t" + log(4.5) + "\t" + log(4.5, Math.PI) + "\t" + log(4.5, 4.5));
        System.out.println(1.125 + "\t" + lb(1.125) + "\t" + ln(1.125) + "\t" + log(1.125) + "\t" + log(1.125, Math.PI) + "\t" + log(1.125, 1.125));
        System.out.println(1.28289 + "\t" + lb(1.28289) + "\t" + ln(1.28289) + "\t" + log(1.28289) + "\t" + log(1.28289, Math.PI) + "\t" + log(1.28289, 1.28289));
        System.out.println(1.6 + "\t" + lb(1.6) + "\t" + ln(1.6) + "\t" + log(1.6) + "\t" + log(1.6, Math.PI) + "\t" + log(1.6, 1.6));
        System.out.println(1.999999 + "\t" + lb(1.999999) + "\t" + ln(1.999999) + "\t" + log(1.999999) + "\t" + log(1.999999, Math.PI) + "\t" + log(1.999999, 1.999999));
        System.out.println(0.999999 + "\t" + lb(0.999999) + "\t" + ln(0.999999) + "\t" + log(0.999999) + "\t" + log(0.999999, Math.PI) + "\t" + log(0.999999, 0.999999));
        System.out.println(9.999999 + "\t" + lb(9.999999) + "\t" + ln(9.999999) + "\t" + log(9.999999) + "\t" + log(9.999999, Math.PI) + "\t" + log(9.999999, 9.999999));
        System.out.println(0.3 + "\t" + lb(0.3) + "\t" + ln(0.3) + "\t" + log(0.3) + "\t" + log(0.3, Math.PI) + "\t" + log(0.3, 0.3));
        System.out.println(0.9 + "\t" + lb(0.9) + "\t" + ln(0.9) + "\t" + log(0.9) + "\t" + log(0.9, Math.PI) + "\t" + log(0.9, 0.9));
        System.out.println(0.8 + "\t" + lb(0.8) + "\t" + ln(0.8) + "\t" + log(0.8) + "\t" + log(0.8, Math.PI) + "\t" + log(0.8, 0.8));
        System.out.println(1.4 + "\t" + lb(1.4) + "\t" + ln(1.4) + "\t" + log(1.4) + "\t" + log(1.4, Math.PI) + "\t" + log(1.4, 1.4));
        System.out.println(10 + "\t" + lb(10) + "\t" + ln(10) + "\t" + log(10) + "\t" + log(10, Math.PI) + "\t" + log(10, 10));
        System.out.println(100 + "\t" + lb(100) + "\t" + ln(100) + "\t" + log(100) + "\t" + log(100, Math.PI) + "\t" + log(100, 100));
        System.out.println(1 + "\t" + lb(1) + "\t" + ln(1) + "\t" + log(1) + "\t" + log(1, Math.PI) + "\t" + log(1, 1));
        System.out.println(1000 + "\t" + lb(1000) + "\t" + ln(1000) + "\t" + log(1000) + "\t" + log(1000, Math.PI) + "\t" + log(1000, 1000));
        System.out.println(0.1 + "\t" + lb(0.1) + "\t" + ln(0.1) + "\t" + log(0.1) + "\t" + log(0.1, Math.PI) + "\t" + log(0.1, 0.1));
        System.out.println(0.01 + "\t" + lb(0.01) + "\t" + ln(0.01) + "\t" + log(0.01) + "\t" + log(0.01, Math.PI) + "\t" + log(0.01, 0.01));
        System.out.println(0.001 + "\t" + lb(0.001) + "\t" + ln(0.001) + "\t" + log(0.001) + "\t" + log(0.001, Math.PI) + "\t" + log(0.001, 0.001));
        System.out.println(2.71828182845904523536 + "\t" + lb(2.71828182845904523536) + "\t" + ln(2.71828182845904523536) + "\t" + log(2.71828182845904523536) + "\t" + log(2.71828182845904523536, Math.PI) + "\t" + log(2.71828182845904523536, 2.71828182845904523536));
        System.out.println(2 + "\t" + lb(2) + "\t" + ln(2) + "\t" + log(2) + "\t" + log(2, Math.PI) + "\t" + log(2, 2));
        System.out.println(8 + "\t" + lb(8) + "\t" + ln(8) + "\t" + log(8) + "\t" + log(8, Math.PI) + "\t" + log(8, 8));
        System.out.println(1024 + "\t" + lb(1024) + "\t" + ln(1024) + "\t" + log(1024) + "\t" + log(1024, Math.PI) + "\t" + log(1024, 1024));
        System.out.println(11 + "\t" + lb(11) + "\t" + ln(11) + "\t" + log(11) + "\t" + log(11, Math.PI) + "\t" + log(11, 11));
        System.out.println(13 + "\t" + lb(13) + "\t" + ln(13) + "\t" + log(13) + "\t" + log(13, Math.PI) + "\t" + log(13, 13));
        System.out.println(2.13 + "\t" + lb(2.13) + "\t" + ln(2.13) + "\t" + log(2.13) + "\t" + log(2.13, Math.PI) + "\t" + log(2.13, 2.13));
        System.out.println(1.212121212 + "\t" + lb(1.212121212) + "\t" + ln(1.212121212) + "\t" + log(1.212121212) + "\t" + log(1.212121212, Math.PI) + "\t" + log(1.212121212, 1.212121212));
        System.out.println(1.001 + "\t" + lb(1.001) + "\t" + ln(1.001) + "\t" + log(1.001) + "\t" + log(1.001, Math.PI) + "\t" + log(1.001, 1.001));
        System.out.println(694 + "\t" + lb(694) + "\t" + ln(694) + "\t" + log(694) + "\t" + log(694, Math.PI) + "\t" + log(694, 694));
        System.out.println(1.6180339887 + "\t" + lb(1.6180339887) + "\t" + ln(1.6180339887) + "\t" + log(1.6180339887) + "\t" + log(1.6180339887, Math.PI) + "\t" + log(1.6180339887, 1.6180339887));
        System.out.println(2.4142135623 + "\t" + lb(2.4142135623) + "\t" + ln(2.4142135623) + "\t" + log(2.4142135623) + "\t" + log(2.4142135623, Math.PI) + "\t" + log(2.4142135623, 2.4142135623));
        System.out.println(1.4142135623 + "\t" + lb(1.4142135623) + "\t" + ln(1.4142135623) + "\t" + log(1.4142135623) + "\t" + log(1.4142135623, Math.PI) + "\t" + log(1.4142135623, 1.4142135623));
        System.out.println(17.1085 + "\t" + lb(17.1085) + "\t" + ln(17.1085) + "\t" + log(17.1085) + "\t" + log(17.1085, Math.PI) + "\t" + log(17.1085, 17.1085));
        System.out.println(27.0684 + "\t" + lb(27.0684) + "\t" + ln(27.0684) + "\t" + log(27.0684) + "\t" + log(27.0684, Math.PI) + "\t" + log(27.0684, 27.0684));
        System.out.println(17.1008 + "\t" + lb(17.1008) + "\t" + ln(17.1008) + "\t" + log(17.1008) + "\t" + log(17.1008, Math.PI) + "\t" + log(17.1008, 17.1008));
        System.out.println(10.1112 + "\t" + lb(10.1112) + "\t" + ln(10.1112) + "\t" + log(10.1112) + "\t" + log(10.1112, Math.PI) + "\t" + log(10.1112, 10.1112));
    }
}