001/*
002 * Java Genetic Algorithm Library (jenetics-7.2.0).
003 * Copyright (c) 2007-2023 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.lang.String.format;
023import static java.util.Objects.requireNonNull;
024
025import java.lang.reflect.Array;
026import java.util.Optional;
027import java.util.stream.Stream;
028
029import io.jenetics.ext.rewriting.TreeRewriter;
030import io.jenetics.ext.util.Tree;
031import io.jenetics.ext.util.TreeNode;
032
033/**
034 * This class rewrites constant expressions to its single value.
035 * <p>
036 * The following example shows how to use the rewriter for a double operation
037 * tree:
038 * <pre>{@code
039 * final TreeNode<Op<Double>> tree = MathExpr.parseTree("1 + 2 + 3 + 4");
040 * ConstRewriter.ofType(Double.class).rewrite(tree);
041 * assert tree.getValue().equals(Const.of(10.0));
042 * }</pre>
043 *
044 * @param <T> the operation type the rewriter is working on
045 *
046 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
047 * @version 5.2
048 * @since 5.2
049 */
050public final class ConstRewriter<T> implements TreeRewriter<Op<T>> {
051
052        /**
053         * Const rewriter for double operation trees.
054         */
055        public static final ConstRewriter<Double> DOUBLE = ofType(Double.class);
056
057        private final Class<T> _type;
058
059        @SuppressWarnings("unchecked")
060        private ConstRewriter(final Class<? extends T> type) {
061                _type = (Class<T>)requireNonNull(type);
062        }
063
064        /**
065         * Return the operation type this rewriter is working on.
066         *
067         * @return the operation type this rewriter is working on
068         */
069        public Class<T> type() {
070                return _type;
071        }
072
073        @Override
074        public int rewrite(final TreeNode<Op<T>> node, final int limit) {
075                requireNonNull(node);
076
077                int rewritten = 0;
078                int res;
079                Optional<TreeNode<Op<T>>> result;
080                do {
081                        result = results(node).findFirst();
082
083                        res = result.map(this::rewriting).orElse(0);
084                        rewritten += res;
085                } while (result.isPresent() && rewritten < limit);
086
087                return rewritten;
088        }
089
090        private int rewriting(final TreeNode<Op<T>> node) {
091                if (matches(node)) {
092                        final T[] args = newArray(node.childCount());
093                        for (int i = 0, n = node.childCount(); i < n; ++i) {
094                                args[i] = ((Val<T>)node.childAt(i).value()).value();
095                        }
096
097                        final T value = node.value().apply(args);
098                        node.removeAllChildren();
099                        node.value(Const.of(value));
100
101                        return 1;
102                } else {
103                        return 0;
104                }
105        }
106
107        @SuppressWarnings("unchecked")
108        private T[] newArray(final int length) {
109                return (T[])Array.newInstance(_type, length);
110        }
111
112        private static <T> Stream<TreeNode<Op<T>>>
113        results(final TreeNode<Op<T>> node) {
114                return node.stream()
115                        .filter(ConstRewriter::matches);
116        }
117
118        private static boolean matches(final Tree<?, ?> node) {
119                return
120                        !(node.value() instanceof Val) &&
121                        !(node.value() instanceof Var) &&
122                        node.childStream()
123                                .allMatch(child -> child.value() instanceof Val);
124        }
125
126        @Override
127        public String toString() {
128                return format("ConstRewriter<%s>", _type.getSimpleName());
129        }
130
131        /**
132         * Create a new rewriter for constant operation subtrees (expressions).
133         *
134         * @param type the type of the operation tree
135         * @param <T> the type of the operation tree
136         * @return a new rewriter for constant operation subtrees (expressions)
137         * @throws NullPointerException if the given {@code type} is {@code null}
138         */
139        public static <T> ConstRewriter<T> ofType(final Class<? extends T> type) {
140                return new ConstRewriter<>(type);
141        }
142
143}