All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sandius.rembulan.lib.impl.DefaultMathLib Maven / Gradle / Ivy

/*
 * Copyright 2016 Miroslav Janíček
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package net.sandius.rembulan.lib.impl;

import net.sandius.rembulan.Conversions;
import net.sandius.rembulan.lib.BadArgumentException;
import net.sandius.rembulan.lib.MathLib;
import net.sandius.rembulan.runtime.Dispatch;
import net.sandius.rembulan.runtime.ExecutionContext;
import net.sandius.rembulan.runtime.LuaFunction;
import net.sandius.rembulan.runtime.ResolvedControlThrowable;
import net.sandius.rembulan.runtime.UnresolvedControlThrowable;
import net.sandius.rembulan.util.Check;

import java.util.Random;

public class DefaultMathLib extends MathLib {

	private final LuaFunction _random;
	private final LuaFunction _randomseed;

	public DefaultMathLib(Random random) {
		Check.notNull(random);
		this._random = new Rand(random);
		this._randomseed = new RandSeed(random);
	}

	public DefaultMathLib() {
		this(new Random());
	}

	@Override
	public LuaFunction _abs() {
		return Abs.INSTANCE;
	}

	@Override
	public LuaFunction _acos() {
		return ACos.INSTANCE;
	}

	@Override
	public LuaFunction _asin() {
		return ASin.INSTANCE;
	}

	@Override
	public LuaFunction _atan() {
		return ATan.INSTANCE;
	}

	@Override
	public LuaFunction _ceil() {
		return Ceil.INSTANCE;
	}

	@Override
	public LuaFunction _cos() {
		return Cos.INSTANCE;
	}

	@Override
	public LuaFunction _deg() {
		return Deg.INSTANCE;
	}

	@Override
	public LuaFunction _exp() {
		return Exp.INSTANCE;
	}

	@Override
	public LuaFunction _floor() {
		return Floor.INSTANCE;
	}

	@Override
	public LuaFunction _fmod() {
		return FMod.INSTANCE;
	}

	@Override
	public Double _huge() {
		return Double.POSITIVE_INFINITY;
	}

	@Override
	public LuaFunction _log() {
		return Log.INSTANCE;
	}

	@Override
	public LuaFunction _max() {
		return MaxMin.MAX_INSTANCE;
	}

	@Override
	public Long _maxinteger() {
		return Long.MAX_VALUE;
	}

	@Override
	public LuaFunction _min() {
		return MaxMin.MIN_INSTANCE;
	}

	@Override
	public Long _mininteger() {
		return Long.MIN_VALUE;
	}

	@Override
	public LuaFunction _modf() {
		return ModF.INSTANCE;
	}

	@Override
	public Double _pi() {
		return Math.PI;
	}

	@Override
	public LuaFunction _rad() {
		return Rad.INSTANCE;
	}

	@Override
	public LuaFunction _random() {
		return _random;
	}

	@Override
	public LuaFunction _randomseed() {
		return _randomseed;
	}

	@Override
	public LuaFunction _sin() {
		return Sin.INSTANCE;
	}

	@Override
	public LuaFunction _sqrt() {
		return Sqrt.INSTANCE;
	}

	@Override
	public LuaFunction _tan() {
		return Tan.INSTANCE;
	}

	@Override
	public LuaFunction _tointeger() {
		return ToInteger.INSTANCE;
	}

	@Override
	public LuaFunction _type() {
		return Type.INSTANCE;
	}

	@Override
	public LuaFunction _ult() {
		return ULt.INSTANCE;
	}

	public static abstract class AbstractMathFunction1 extends AbstractLibFunction {

		protected abstract Number op(double x);

		protected Number op(long x) {
			return op((double) x);
		}

		@Override
		protected void invoke(ExecutionContext context, ArgumentIterator args) throws ResolvedControlThrowable {
			Number x = args.nextNumber();
			Number result = x instanceof Float || x instanceof Double ? op(x.doubleValue()) : op(x.longValue());
			context.getReturnBuffer().setTo(result);
		}

	}

	public static class Abs extends AbstractMathFunction1 {

		public static final Abs INSTANCE = new Abs();

		@Override
		protected String name() {
			return "abs";
		}

		@Override
		protected Number op(double x) {
			return Math.abs(x);
		}

		@Override
		protected Number op(long x) {
			return Math.abs(x);
		}

	}

	public static class ACos extends AbstractMathFunction1 {

		public static final ACos INSTANCE = new ACos();

		@Override
		protected String name() {
			return "acos";
		}

		@Override
		protected Number op(double x) {
			return Math.acos(x);
		}

	}

	public static class ASin extends AbstractMathFunction1 {

		public static final ASin INSTANCE = new ASin();

		@Override
		protected String name() {
			return "asin";
		}

		@Override
		protected Number op(double x) {
			return Math.asin(x);
		}

	}

	public static class ATan extends AbstractMathFunction1 {

		public static final ATan INSTANCE = new ATan();

		@Override
		protected String name() {
			return "atan";
		}

		@Override
		protected Number op(double x) {
			return Math.atan(x);
		}

	}

	public static class Ceil extends AbstractMathFunction1 {

		public static final Ceil INSTANCE = new Ceil();

		@Override
		protected String name() {
			return "ceil";
		}

		@Override
		protected Number op(double x) {
			double d = Math.ceil(x);
			long l = (long) d;
			return d == (double) l ? l : d;
		}

		@Override
		protected Number op(long x) {
			return x;
		}

	}

	public static class Cos extends AbstractMathFunction1 {

		public static final Cos INSTANCE = new Cos();

		@Override
		protected String name() {
			return "cos";
		}

		@Override
		protected Number op(double x) {
			return Math.cos(x);
		}

	}

	public static class Deg extends AbstractMathFunction1 {

		public static final Deg INSTANCE = new Deg();

		@Override
		protected String name() {
			return "deg";
		}

		@Override
		protected Number op(double x) {
			return Math.toDegrees(x);
		}

	}

	public static class Exp extends AbstractMathFunction1 {

		public static final Exp INSTANCE = new Exp();

		@Override
		protected String name() {
			return "exp";
		}

		@Override
		protected Number op(double x) {
			return Math.exp(x);
		}

	}

	public static class Floor extends AbstractMathFunction1 {

		public static final Floor INSTANCE = new Floor();

		@Override
		protected String name() {
			return "floor";
		}

		@Override
		protected Number op(double x) {
			double d = Math.floor(x);
			long l = (long) d;
			// Note: explicit type annotations are necessary: per JLS 15.25, the type of the
			// expression would be *double* (not java.lang.Number) without them
			return d == (double) l ? (Number) l : (Number) d;
		}

		@Override
		protected Number op(long x) {
			return x;
		}

	}

	public static class FMod extends AbstractLibFunction {

		public static final FMod INSTANCE = new FMod();

		@Override
		protected String name() {
			return "fmod";
		}

		@Override
		protected void invoke(ExecutionContext context, ArgumentIterator args) throws ResolvedControlThrowable {
			Number x = args.nextNumber();
			Number y = args.nextNumber();

			final Number result;

			if (x instanceof Float || x instanceof Double
					|| y instanceof Float || y instanceof Double) {

				result = Math.IEEEremainder(x.doubleValue(), y.doubleValue());
			}
			else {
				long xi = x.longValue();
				long yi = y.longValue();

				if (yi != 0) {
					result = xi % yi;
				}
				else {
					throw new BadArgumentException(2, name(), "zero");
				}
			}

			context.getReturnBuffer().setTo(result);
		}

	}

	public static class Log extends AbstractLibFunction {

		public static final Log INSTANCE = new Log();

		@Override
		protected String name() {
			return "log";
		}

		@Override
		protected void invoke(ExecutionContext context, ArgumentIterator args) throws ResolvedControlThrowable {
			Number x = args.nextNumber();
			double ln = Math.log(x.doubleValue());
			final double result;

			if (args.hasNext()) {
				// explicit base
				double base = args.nextNumber().doubleValue();
				result = ln / Math.log(base);
			}
			else {
				// no base specified
				result = ln;
			}

			context.getReturnBuffer().setTo(result);
		}

	}

	public static class MaxMin extends AbstractLibFunction {

		private final boolean isMax;

		public static final MaxMin MAX_INSTANCE = new MaxMin(true);
		public static final MaxMin MIN_INSTANCE = new MaxMin(false);

		public MaxMin(boolean isMax) {
			this.isMax = isMax;
		}

		private static class State {

			public final Object[] args;
			public final int idx;
			public final Object best;

			public State(Object[] args, int idx, Object best) {
				this.args = args;
				this.idx = idx;
				this.best = best;
			}

		}

		@Override
		protected String name() {
			return isMax ? "max" : "min";
		}

		private void run(ExecutionContext context, Object[] args, int idx, Object best) throws ResolvedControlThrowable {
			for ( ; idx < args.length; idx++) {
				Object o = args[idx];

				try {
					if (isMax) {
						Dispatch.lt(context, best, o);
					}
					else {
						Dispatch.lt(context, o, best);
					}
				}
				catch (UnresolvedControlThrowable ct) {
					throw ct.resolve(this, new State(args, idx, best));
				}

				if (Conversions.booleanValueOf(context.getReturnBuffer().get0())) {
					best = o;
				}
			}

			// we're done
			context.getReturnBuffer().setTo(best);
		}

		@Override
		protected void invoke(ExecutionContext context, ArgumentIterator args) throws ResolvedControlThrowable {
			Object initial = args.nextAny();
			run(context, args.getAll(), 1, initial);
		}

		@Override
		public void resume(ExecutionContext context, Object suspendedState) throws ResolvedControlThrowable {
			State ss = (State) suspendedState;

			Object[] args = ss.args;
			int idx = ss.idx;
			Object best = ss.best;

			// best <> args[idx] comparison has just finished
			if (Conversions.booleanValueOf(context.getReturnBuffer().get0())) {
				best = args[idx];
			}

			run(context, args, idx + 1, best);
		}

	}

	public static class ModF extends AbstractLibFunction {

		public static final ModF INSTANCE = new ModF();

		@Override
		protected String name() {
			return "modf";
		}

		@Override
		protected void invoke(ExecutionContext context, ArgumentIterator args) throws ResolvedControlThrowable {
			Number x = args.nextNumber();

			final Number intPart;
			final Number fltPart;

			double d = x.doubleValue();

			if (d == d) {
				double dd = d < 0 ? Math.ceil(d) : Math.floor(d);
				long l = (long) dd;
				if (dd == (double) l) {
					intPart = l;
					fltPart = d - l;
				}
				else {
					intPart = x;
					fltPart = 0.0;
				}
			}
			else {
				// NaN
				intPart = x;
				fltPart = x;
			}

			context.getReturnBuffer().setTo(intPart, fltPart);
		}

	}

	public static class Rad extends AbstractMathFunction1 {

		public static final Rad INSTANCE = new Rad();

		@Override
		protected String name() {
			return "rad";
		}

		@Override
		protected Number op(double x) {
			return Math.toRadians(x);
		}

	}

	public static class Rand extends AbstractLibFunction {

		protected final Random random;

		public Rand(Random random) {
			this.random = Check.notNull(random);
		}

		@Override
		protected String name() {
			return "random";
		}

		// return a long in the range [0, n)
		protected long nextLong(long n) {
			Check.nonNegative(n);
			if (n <= Integer.MAX_VALUE) {
				return random.nextInt((int) n);
			}
			else {
				long bits;
				long val;
				do {
					bits = random.nextLong() & Long.MAX_VALUE;
					val = bits % n;
				} while (bits - val + (n - 1) < 0);
				return val;
			}
		}

		@Override
		protected void invoke(ExecutionContext context, ArgumentIterator args) throws ResolvedControlThrowable {
			final Number result;

			if (!args.hasNext()) {
				// float in the range [0.0, 1.0)
				result = random.nextDouble();
			}
			else {
				long m = args.nextInteger();

				if (!args.hasNext()) {
					// integer in the range [1, m]
					if (m < 1) {
						throw new BadArgumentException(1, name(), "interval is empty");
					}
					result = 1L + nextLong(m);
				}
				else {
					// integer in the range [m, n]
					long n = args.nextInteger();

					if (n < m) {
						throw new BadArgumentException(1, name(), "interval is empty");
					}

					long limit = n - m + 1;  // including the upper bound

					if (limit <= 0) {
						throw new BadArgumentException(1, name(), "interval too large");
					}

					result = m + nextLong(limit);
				}
			}

			context.getReturnBuffer().setTo(result);
		}

	}

	public static class RandSeed extends AbstractLibFunction {

		protected final Random random;

		public RandSeed(Random random) {
			this.random = Check.notNull(random);
		}

		@Override
		protected String name() {
			return "randomseed";
		}

		@Override
		protected void invoke(ExecutionContext context, ArgumentIterator args) throws ResolvedControlThrowable {
			Number arg = args.nextNumber();

			long seed = arg instanceof Double || arg instanceof Float
					? Double.doubleToLongBits(arg.doubleValue())
					: arg.longValue();

			random.setSeed(seed);

			context.getReturnBuffer().setTo();
		}

	}

	public static class Sin extends AbstractMathFunction1 {

		public static final Sin INSTANCE = new Sin();

		@Override
		protected String name() {
			return "sin";
		}

		@Override
		protected Number op(double x) {
			return Math.sin(x);
		}

	}

	public static class Sqrt extends AbstractMathFunction1 {

		public static final Sqrt INSTANCE = new Sqrt();

		@Override
		protected String name() {
			return "sqrt";
		}

		@Override
		protected Number op(double x) {
			return Math.sqrt(x);
		}

	}

	public static class Tan extends AbstractMathFunction1 {

		public static final Tan INSTANCE = new Tan();

		@Override
		protected String name() {
			return "tan";
		}

		@Override
		protected Number op(double x) {
			return Math.tan(x);
		}

	}

	public static class ToInteger extends AbstractLibFunction {

		public static final ToInteger INSTANCE = new ToInteger();

		@Override
		protected String name() {
			return "tointeger";
		}

		@Override
		protected void invoke(ExecutionContext context, ArgumentIterator args) throws ResolvedControlThrowable {
			Object x = args.nextAny();
			context.getReturnBuffer().setTo(Conversions.integerValueOf(x));
		}

	}

	public static class Type extends AbstractLibFunction {

		public static final Type INSTANCE = new Type();

		@Override
		protected String name() {
			return "type";
		}

		@Override
		protected void invoke(ExecutionContext context, ArgumentIterator args) throws ResolvedControlThrowable {
			Object x = args.nextAny();

			String result = x instanceof Number
					? (x instanceof Float || x instanceof Double
							? "float"
							: "integer")
					: null;

			context.getReturnBuffer().setTo(result);
		}

	}

	public static class ULt extends AbstractLibFunction {

		public static final ULt INSTANCE = new ULt();

		@Override
		protected String name() {
			return "ult";
		}

		@Override
		protected void invoke(ExecutionContext context, ArgumentIterator args) throws ResolvedControlThrowable {
			long x = args.nextInteger();
			long y = args.nextInteger();
			context.getReturnBuffer().setTo((x - y) < 0);
		}

	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy