RubyBignum.java

/*
 * Copyright (c) 2014, 2015 Oracle and/or its affiliates. All rights reserved. This
 * code is released under a tri EPL/GPL/LGPL license. You can use it,
 * redistribute it and/or modify it under the terms of the:
 *
 * Eclipse Public License version 1.0
 * GNU General Public License version 2
 * GNU Lesser General Public License version 2.1
 */
package org.jruby.truffle.runtime.core;

import com.oracle.truffle.api.CompilerDirectives;
import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.nodes.objects.Allocator;
import org.jruby.truffle.runtime.RubyContext;

import java.math.BigInteger;

public class RubyBignum extends RubyBasicObject {

    private BigInteger value;

    public RubyBignum(RubyClass rubyClass, BigInteger value) {
        super(rubyClass);
        // TODO(CS): we fail this but we shouldn't
        //assert value.bitLength() < 64;
        this.value = value;
    }

    @Override
    public boolean hasNoSingleton() {
        return true;
    }

    @CompilerDirectives.TruffleBoundary
    public RubyBignum negate() {
        return create(value.negate());
    }

    @CompilerDirectives.TruffleBoundary
    public RubyBignum abs() {
        return create(value.abs());
    }

    @CompilerDirectives.TruffleBoundary
    public RubyBignum create(BigInteger value) {
        return new RubyBignum(getContext().getCoreLibrary().getBignumClass(), value);
    }

    @CompilerDirectives.TruffleBoundary
    public RubyBignum add(RubyBignum other) {
        return create(value.add(other.value));
    }

    @CompilerDirectives.TruffleBoundary
    public RubyBignum add(long other) {
        return create(value.add(BigInteger.valueOf(other)));
    }

    @CompilerDirectives.TruffleBoundary
    public RubyBignum subtract(RubyBignum other) {
        return create(value.subtract(other.value));
    }

    @CompilerDirectives.TruffleBoundary
    public RubyBignum subtract(long other) {
        return create(value.subtract(BigInteger.valueOf(other)));
    }

    @CompilerDirectives.TruffleBoundary
    public RubyBignum multiply(RubyBignum other) {
        return create(value.multiply(other.value));
    }

    @CompilerDirectives.TruffleBoundary
    public RubyBignum multiply(long other) {
        return create(value.multiply(BigInteger.valueOf(other)));
    }

    @CompilerDirectives.TruffleBoundary
    public RubyBignum pow(int other) {
        return create(value.pow(other));
    }

    @CompilerDirectives.TruffleBoundary
    public RubyBignum pow(long other) {
        if (other < Integer.MAX_VALUE) {
            return pow((int) other);
        } else {
            BigInteger result = BigInteger.ONE;

            for (long n = 0; n < other; n++) {
                result = result.multiply(value);
            }

            return create(result);
        }
    }

    @CompilerDirectives.TruffleBoundary
    public RubyBignum pow(RubyBignum other) {
        BigInteger result = BigInteger.ONE;

        for (BigInteger n = BigInteger.ZERO; other.value.compareTo(n) < 0; n = n.add(BigInteger.ONE)) {
            result = result.multiply(value);
        }

        return create(result);
    }

    @CompilerDirectives.TruffleBoundary
    public RubyBignum divide(RubyBignum other) {
        return create(value.divide(other.value));
    }

    @CompilerDirectives.TruffleBoundary
    public RubyBignum divide(long other) {
        return create(value.divide(BigInteger.valueOf(other)));
    }

    @CompilerDirectives.TruffleBoundary
    public RubyBignum mod(long other) {
        return create(value.mod(BigInteger.valueOf(other)));
    }

    @CompilerDirectives.TruffleBoundary
    public RubyBignum mod(RubyBignum other) {
        return create(value.mod(other.bigIntegerValue()));
    }

    @CompilerDirectives.TruffleBoundary
    public int compareTo(int other) {
        return value.compareTo(BigInteger.valueOf(other));
    }

    @CompilerDirectives.TruffleBoundary
    public int compareTo(long other) {
        return value.compareTo(BigInteger.valueOf(other));
    }

    @CompilerDirectives.TruffleBoundary
    public int compareTo(RubyBignum other) {
        return value.compareTo(other.value);
    }

    @CompilerDirectives.TruffleBoundary
    public int compareTo(double other) {
        return compareTo((long) other);
    }

    @CompilerDirectives.TruffleBoundary
    public boolean isEqualTo(int b) {
        return value.equals(BigInteger.valueOf(b));
    }

    @CompilerDirectives.TruffleBoundary
    public boolean isEqualTo(long b) {
        return value.equals(BigInteger.valueOf(b));
    }

    @CompilerDirectives.TruffleBoundary
    public boolean isEqualTo(RubyBignum b) {
        return value.equals(b.value);
    }

    @CompilerDirectives.TruffleBoundary
    public RubyBignum and(RubyBignum other) {
        return create(value.and(other.value));
    }

    @CompilerDirectives.TruffleBoundary
    public RubyBignum and(long other) {
        return create(value.and(BigInteger.valueOf(other)));
    }

    @CompilerDirectives.TruffleBoundary
    public RubyBignum or(RubyBignum other) {
        return create(value.or(other.value));
    }

    @CompilerDirectives.TruffleBoundary
    public RubyBignum or(long other) {
        return create(value.or(BigInteger.valueOf(other)));
    }

    @CompilerDirectives.TruffleBoundary
    public RubyBignum xor(RubyBignum other) {
        return create(value.xor(other.value));
    }

    @CompilerDirectives.TruffleBoundary
    public RubyBignum xor(long other) {
        return create(value.xor(BigInteger.valueOf(other)));
    }

    @CompilerDirectives.TruffleBoundary
    public RubyBignum shiftLeft(int n) {
        return create(value.shiftLeft(n));
    }

    @CompilerDirectives.TruffleBoundary
    public RubyBignum shiftRight(int n) {
        return create(value.shiftRight(n));
    }

    @CompilerDirectives.TruffleBoundary
    public long longValue() {
        return value.longValue();
    }

    @CompilerDirectives.TruffleBoundary
    public boolean isZero() {
        return value.equals(BigInteger.ZERO);
    }

    @CompilerDirectives.TruffleBoundary
    public String toHexString() {
        return value.toString(16);
    }

    @CompilerDirectives.TruffleBoundary
    public double doubleValue() {
        return value.doubleValue();
    }

    public BigInteger bigIntegerValue() {
        return value;
    }

    @Override
    public int hashCode() {
        return value.hashCode();
    }

    public static class BignumAllocator implements Allocator {

        @Override
        public RubyBasicObject allocate(RubyContext context, RubyClass rubyClass, RubyNode currentNode) {
            return new RubyBignum(rubyClass, BigInteger.ZERO);
        }

    }

}