001/*
002 * Java Genetic Algorithm Library (jenetics-7.1.0).
003 * Copyright (c) 2007-2022 Franz Wilhelmstötter
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 *
017 * Author:
018 *    Franz Wilhelmstötter (franz.wilhelmstoetter@gmail.com)
019 */
020package io.jenetics.prog.op;
021
022import static java.util.Objects.requireNonNull;
023
024import java.util.Objects;
025import java.util.Optional;
026import java.util.function.Function;
027import java.util.stream.Stream;
028
029import io.jenetics.ext.util.Tree;
030import io.jenetics.ext.util.TreeNode;
031
032/**
033 * This class contains basic and secondary boolean operations.
034 *
035 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
036 * @version 5.0
037 * @since 5.0
038 */
039public enum BoolOp implements Op<Boolean> {
040
041        /**
042         * Conjunction. <em>This operation has arity 2.</em>
043         */
044        AND("and", 2, v -> v[0] && v[1]),
045
046        /**
047         * Disjunction. <em>This operation has arity 2.</em>
048         */
049        OR("or", 2, v -> v[0] || v[1]),
050
051        /**
052         * Negation. <em>This operation has arity 1.</em>
053         */
054        NOT("not", 1, v -> !v[0]),
055
056        /**
057         * Implication. <em>This operation has arity 2.</em>
058         */
059        IMP("imp", 2, v -> !v[0] || v[1]),
060
061        /**
062         * Exclusive or. <em>This operation has arity 2.</em>
063         */
064        XOR("xor", 2, v -> (v[0] || v[1]) && !(v[0] && v[1])),
065
066        /**
067         * Equivalence. <em>This operation has arity 2.</em>
068         */
069        EQU("equ", 2, v -> (v[0] && v[1]) || (!v[0] && !v[1]));
070
071        /**
072         * Represents the constant {@code true}.
073         */
074        public static final Const<Boolean> TRUE = Const.of("true", true);
075
076        /**
077         * Represents the constant {@code true}.
078         */
079        public static final Const<Boolean> FALSE = Const.of("false", false);
080
081
082        private final String _name;
083        private final int _arity;
084        private final Function<Boolean[], Boolean> _function;
085
086        BoolOp(
087                final String name,
088                final int arity,
089                final Function<Boolean[], Boolean> function
090        ) {
091                assert name != null;
092                assert arity >= 0;
093                assert function != null;
094
095                _name = name;
096                _function = function;
097                _arity = arity;
098        }
099
100        @Override
101        public int arity() {
102                return _arity;
103        }
104
105        @Override
106        public Boolean apply(final Boolean[] args) {
107                return _function.apply(args);
108        }
109
110        /**
111         * Evaluates the operation with the given arguments.
112         *
113         * @see #apply(Boolean[])
114         *
115         * @param args the operation arguments
116         * @return the evaluated operation
117         */
118        public boolean eval(final boolean... args) {
119                final Boolean[] v = new Boolean[args.length];
120                for (int i = 0; i < args.length; ++i) {
121                        v[i] = args[i];
122                }
123
124                return apply(v);
125        }
126
127        @Override
128        public String toString() {
129                return _name;
130        }
131
132
133        /**
134         * Converts the string representation of an operation to the operation
135         * object. It is used for converting the string representation of a tree to
136         * an operation tree. If you use it that way, you should not forget to
137         * re-index the tree variables.
138         *
139         * <pre>{@code
140         * final TreeNode<Op<Boolean>> tree = TreeNode.parse(
141         *     "and(or(x,y),not(y))",
142         *     BoolOp::toBoolOp
143         * );
144         *
145         * assert Program.eval(tree, false, false) == false;
146         * Var.reindex(tree);
147         * assert Program.eval(tree, false, false) == true;
148         * }</pre>
149         *
150         * @since 5.0
151         *
152         * @see Var#reindex(TreeNode)
153         * @see Program#eval(Tree, Object[])
154         *
155         * @param string the string representation of an operation which should be
156         *        converted
157         * @return the operation, converted from the given string
158         * @throws IllegalArgumentException if the given {@code value} doesn't
159         *         represent a mathematical expression
160         * @throws NullPointerException if the given string {@code value} is
161         *         {@code null}
162         */
163        public static Op<Boolean> toBoolOp(final String string) {
164                requireNonNull(string);
165
166                final Op<Boolean> result;
167                final Optional<Const<Boolean>> cop = toConst(string);
168                if (cop.isPresent()) {
169                        result = cop.orElseThrow(AssertionError::new);
170                } else {
171                        final Optional<Op<Boolean>> mop = toOp(string);
172                        result = mop.isPresent()
173                                ? mop.orElseThrow(AssertionError::new)
174                                : Var.parse(string);
175                }
176
177                return result;
178        }
179
180        static Optional<Const<Boolean>> toConst(final String string) {
181                return tryParseBoolean(string)
182                        .map(Const::of);
183        }
184
185        private static Optional<Boolean> tryParseBoolean(final String value) {
186                switch (value) {
187                        case "true":
188                        case "1": return Optional.of(true);
189                        case "false":
190                        case "0": return Optional.of(false);
191                        default: return Optional.empty();
192                }
193        }
194
195        private static Optional<Op<Boolean>> toOp(final String string) {
196                return Stream.of(values())
197                        .filter(op -> Objects.equals(op._name, string))
198                        .map(op -> (Op<Boolean>)op)
199                        .findFirst();
200        }
201
202}