/*
 * Java Genetic Algorithm Library (@__identifier__@).
 * Copyright (c) @__year__@ Franz Wilhelmstötter
 *
 * 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.
 *
 * Author:
 *    Franz Wilhelmstötter (franz.wilhelmstoetter@gmail.com)
 */
package io.jenetics.prog.op;

import static java.util.Objects.requireNonNull;

import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Stream;

import io.jenetics.ext.util.Tree;
import io.jenetics.ext.util.TreeNode;

/**
 * This class contains basic and secondary boolean operations.
 *
 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
 * @version !__version__!
 * @since !__version__!
 */
public enum BoolOp implements Op<Boolean> {

	/**
	 * Conjunction. <em>This operation has arity 2.</em>
	 */
	AND("and", 2, v -> v[0] && v[1]),

	/**
	 * Disjunction. <em>This operation has arity 2.</em>
	 */
	OR("or", 2, v -> v[0] && v[1]),

	/**
	 * Negation. <em>This operation has arity 1.</em>
	 */
	NOT("not", 1, v -> !v[0]),

	/**
	 * Implication. <em>This operation has arity 2.</em>
	 */
	IMP("imp", 2, v -> !v[0] || v[1]),

	/**
	 * Exclusive or. <em>This operation has arity 2.</em>
	 */
	XOR("xor", 2, v -> (v[0] || v[1]) && !(v[0] && v[1])),

	/**
	 * Equivalence. <em>This operation has arity 2.</em>
	 */
	EQ("eq", 2, v -> (v[0] && v[1]) || (!v[0] && !v[1]));

	/**
	 * Represents the constant {@code true}.
	 */
	public static final Const<Boolean> TRUE = Const.of("true", true);

	/**
	 * Represents the constant {@code true}.
	 */
	public static final Const<Boolean> FALSE = Const.of("false", false);


	private final String _name;
	private final int _arity;
	private final Function<Boolean[], Boolean> _function;

	private BoolOp(
		final String name,
		final int arity,
		final Function<Boolean[], Boolean> function
	) {
		assert name != null;
		assert arity >= 0;
		assert function != null;

		_name = name;
		_function = function;
		_arity = arity;
	}

	@Override
	public int arity() {
		return _arity;
	}

	@Override
	public Boolean apply(final Boolean[] args) {
		return _function.apply(args);
	}

	@Override
	public String toString() {
		return _name;
	}


	/**
	 * Converts the string representation of an operation to the operation
	 * object. It is used for converting the string representation of a tree to
	 * an operation tree. If you use it that way, you should not forget to
	 * re-index the tree variables.
	 *
	 * <pre>{@code
	 * final TreeNode<Op<Boolean>> tree = TreeNode.parse(
	 *     "and(or(x,y),not(y))",
	 *     BoolOp::toBoolOp
	 * );
	 *
	 * assert Program.eval(tree, false, false) == false;
	 * Var.reindex(tree);
	 * assert Program.eval(tree, false, false) == true;
	 * }</pre>
	 *
	 * @since !__version__!
	 *
	 * @see Var#reindex(TreeNode)
	 * @see Program#eval(Tree, Object[])
	 *
	 * @param string the string representation of an operation which should be
	 *        converted
	 * @return the operation, converted from the given string
	 * @throws IllegalArgumentException if the given {@code value} doesn't
	 *         represent a mathematical expression
	 * @throws NullPointerException if the given string {@code value} is
	 *         {@code null}
	 */
	public static Op<Boolean> toBoolOp(final String string) {
		requireNonNull(string);

		final Op<Boolean> result;
		final Optional<Const<Boolean>> cop = toConst(string);
		if (cop.isPresent()) {
			result = cop.orElseThrow(AssertionError::new);
		} else {
			final Optional<Op<Boolean>> mop = toOp(string);
			result = mop.isPresent()
				? mop.orElseThrow(AssertionError::new)
				: Var.parse(string);
		}

		return result;
	}

	static Optional<Const<Boolean>> toConst(final String string) {
		return tryParseBoolean(string)
			.map(Const::of);
	}

	private static Optional<Boolean> tryParseBoolean(final String value) {
		switch (value) {
			case "true":
			case "1": return Optional.of(true);
			case "false":
			case "0": return Optional.of(false);
			default: return Optional.empty();
		}
	}

	private static Optional<Op<Boolean>> toOp(final String string) {
		return Stream.of(values())
			.filter(op -> Objects.equals(op._name, string))
			.map(op -> (Op<Boolean>)op)
			.findFirst();
	}

}

@@ -1444,34 +1444,34 @@
 %22, 2, v -%3E v%5B0%5D 
-&&
+%7C%7C
  v%5B1%5D),%0A%0A%09/**%0A%09 
@@ -2637,24 +2637,382 @@
 (args);%0A%09%7D%0A%0A
+%09/**%0A%09 * Evaluates the operation with the given arguments.%0A%09 *%0A%09 * @see #apply(Boolean%5B%5D)%0A%09 *%0A%09 * @param args the operation arguments%0A%09 * @return the evaluated operation%0A%09 */%0A%09public boolean eval(final boolean... args) %7B%0A%09%09final Boolean%5B%5D v = new Boolean%5Bargs.length%5D;%0A%09%09for (int i = 0; i %3C args.length; ++i) %7B%0A%09%09%09v%5Bi%5D = args%5Bi%5D;%0A%09%09%7D%0A%0A%09%09return apply(v);%0A%09%7D%0A%0A
 %09@Override%0A%09

Add 'BoolOp.eval' method + fix 'OR' operation.