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

com.g2forge.alexandria.expression.numeric.BinaryNumericExpression Maven / Gradle / Ivy

Go to download

A library for expressions and their evaluation. Includes a basic implementation for math.

There is a newer version: 0.0.18
Show newest version
package com.g2forge.alexandria.expression.numeric;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import com.g2forge.alexandria.expression.eval.IEvaluator;
import com.g2forge.alexandria.java.close.ICloseable;
import com.g2forge.alexandria.java.core.helpers.HCollection;
import com.g2forge.alexandria.java.core.helpers.HStream;

import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.RequiredArgsConstructor;

@Data
@EqualsAndHashCode(callSuper = false)
@RequiredArgsConstructor
public class BinaryNumericExpression extends AActiveNumericExpression {
	@RequiredArgsConstructor
	@Getter
	public enum Associativity {
		Left(true),
		Right(true),
		None(false);

		protected final boolean associative;
	}

	@RequiredArgsConstructor
	@Getter
	public enum Operator {
		Add(Associativity.Left, null, true) {
			@Override
			public NumericLiteral literal(List arguments) {
				if (arguments.isEmpty()) return new NumericLiteral(0);
				if (arguments.size() == 1) HCollection.getOne(arguments);
				int current = 0;
				for (NumericLiteral literal : arguments) {
					current += literal.getValue();
				}
				return new NumericLiteral(current);
			}
		},
		Subtract(Associativity.Left, Add, false) {
			@Override
			public NumericLiteral literal(List arguments) {
				if (arguments.isEmpty()) return new NumericLiteral(0);
				if (arguments.size() == 1) HCollection.getOne(arguments);
				int current = 0;
				boolean first = true;
				for (NumericLiteral literal : arguments) {
					if (first) {
						current += literal.getValue();
						first = false;
					} else current -= literal.getValue();
				}
				return new NumericLiteral(current);
			}
		},
		Mod(Associativity.Left, null, false) {
			@Override
			public NumericLiteral literal(List arguments) {
				if (arguments.isEmpty()) return new NumericLiteral(0);
				if (arguments.size() == 1) HCollection.getOne(arguments);
				int current = 0;
				boolean first = true;
				for (NumericLiteral literal : arguments) {
					if (first) {
						current = literal.getValue();
						first = false;
					} else current = current % literal.getValue();
				}
				return new NumericLiteral(current);
			}
		};

		protected final Associativity associativity;

		protected final Operator reassociativeOperator;

		protected final boolean commutative;

		public abstract NumericLiteral literal(List arguments);

		public INumericExpression reduce(List reduced) {
			if (reduced.stream().filter(e -> !(e instanceof NumericLiteral)).findAny().isPresent()) {
				if (getAssociativity().isAssociative()) {
					if (isCommutative()) {
						final Map> grouped = reduced.stream().collect(Collectors.groupingBy(a -> a instanceof NumericLiteral));
						final List collection = grouped.entrySet().stream().flatMap(e -> {
							if (e.getKey()) {
								@SuppressWarnings({ "rawtypes", "unchecked" })
								final List cast = (List) e.getValue();
								return Stream.of(literal(cast));
							} else return e.getValue().stream();
						}).collect(Collectors.toList());
						if (collection.size() == 1) return HCollection.getOne(collection);
						return new BinaryNumericExpression(this, collection);
					} else if ((getReassociativeOperator() != null) && getReassociativeOperator().getAssociativity().isAssociative() && getReassociativeOperator().isCommutative()) {
						final boolean left = Associativity.Left.equals(getAssociativity());
						final INumericExpression single = reduced.get(left ? 0 : reduced.size() - 1);
						final INumericExpression multi = new BinaryNumericExpression(getReassociativeOperator(), reduced.subList(left ? 1 : 0, left ? reduced.size() : reduced.size() - 1)).reduce();
						return new BinaryNumericExpression(this, left ? Arrays.asList(single, multi) : Arrays.asList(multi, single));
					}
				}
				return new BinaryNumericExpression(this, reduced);
			} else {
				@SuppressWarnings({ "rawtypes", "unchecked" })
				final List cast = (List) reduced;
				return literal(cast);
			}
		}
	}

	protected final Operator operator;

	protected final List arguments;

	public BinaryNumericExpression(Operator operator, INumericExpression... arguments) {
		this(operator, Arrays.asList(arguments));
	}

	@Override
	protected INumericExpression applyInternal(IEvaluator evaluator, NumericEnvironment environment) {
		final List applied = HStream.toStreamIndexed(getArguments()).map(t -> evaluator.apply("Argument" + t.get0(), t.get1(), environment)).collect(Collectors.toList());
		return new BinaryNumericExpression(getOperator(), applied);
	}

	@Override
	protected INumericExpression reduceInternal(IEvaluator evaluator) {
		final List reduced = HStream.toStreamIndexed(getArguments()).map(t -> evaluator.reduce("Argument" + t.get0(), t.get1())).collect(Collectors.toList());
		return getOperator().reduce(reduced);
	}

	@Override
	public String toString() {
		try (final ICloseable debug = EVAL.get().debug()) {
			return new StringBuilder().append(getOperator()).append(getArguments()).toString();
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy