001/*
002 * Java Genetic Algorithm Library (jenetics-8.1.0).
003 * Copyright (c) 2007-2024 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.ext.rewriting;
021
022import static java.lang.String.format;
023import static java.util.Objects.requireNonNull;
024
025import java.util.List;
026
027import io.jenetics.ext.util.TreeNode;
028
029/**
030 * Interface for rewriting a given tree. The rewrite is performed on a mutable
031 * {@link TreeNode} and is done in place.
032 * <p>
033 * <b>Description from <a href="https://en.wikipedia.org/wiki/Rewriting">
034 *     Wikipedia: </a></b>
035 * <em>
036 *     In mathematics, computer science, and logic, rewriting covers a wide
037 *     range of (potentially non-deterministic) methods of replacing sub-terms
038 *     of a formula with other terms. In their most basic form, they consist of
039 *     a set of objects, plus relations on how to transform those objects.
040 *     Rewriting can be non-deterministic. One rule to rewrite a term could be
041 *     applied in many different ways to that term, or more than one rule could
042 *     be applicable. Rewriting systems then do not provide an algorithm for
043 *     changing one term to another, but a set of possible rule applications.
044 *     When combined with an appropriate algorithm, however, rewrite systems can
045 *     be viewed as computer programs, and several theorem provers and
046 *     declarative programming languages are based on term rewriting.
047 * </em>
048 * </p>
049 *
050 * @apiNote
051 * The rewriting is done in place, to a mutable {@link TreeNode} object.
052 *
053 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
054 * @version 5.0
055 * @since 5.0
056 */
057@FunctionalInterface
058public interface TreeRewriter<V> {
059
060        /**
061         * Applies the rewriting to the given (mutable) {@code tree}. The tree
062         * rewrite is done in place. Via the {@code limit} parameter, the termination
063         * of the tree-rewrite process can be guaranteed.
064         *
065         * @param tree the tree to be rewritten
066         * @param limit the maximal number this rewrite rule is applied to the given
067         *        tree. This guarantees the termination of the rewrite method.
068         * @return the number of rewrites applied to the input {@code tree}
069         * @throws NullPointerException if the given {@code tree} is {@code null}
070         * @throws IllegalArgumentException if the {@code limit} is smaller than
071         *         one
072         */
073        int rewrite(final TreeNode<V> tree, final int limit);
074
075        /**
076         * Applies the rewriting to the given (mutable) {@code tree}. The tree
077         * rewrite is done in place. The limit of the applied rewrites is set
078         * unlimited ({@link Integer#MAX_VALUE}).
079         *
080         * @see #rewrite(TreeNode, int)
081         *
082         * @param tree the tree to be rewritten
083         * @return {@code true} if the tree has been changed (rewritten) by this
084         *         method, {@code false} if the tree hasn't been changed
085         * @throws NullPointerException if the given {@code tree} is {@code null}
086         */
087        default int rewrite(final TreeNode<V> tree) {
088                return rewrite(tree, Integer.MAX_VALUE);
089        }
090
091
092        /* *************************************************************************
093         * Static helper functions.
094         * *************************************************************************/
095
096        /**
097         * Rewrites the given {@code tree} by applying the given {@code rewriters}.
098         * This method to apply all rewriters, in the order they are given in
099         * the sequence, until the tree stays unchanged.
100         *
101         * @param <V> the tree value type
102         * @param tree the tree to rewrite
103         * @param limit the maximal number this rewrite rule is applied to the given
104         *        tree. This guarantees the termination of the rewrite method.
105         * @param rewriters the rewriters applied to the tree
106         * @return {@code true} if the tree has been changed (rewritten) by this
107         *         method, {@code false} if the tree hasn't been changed
108         * @throws NullPointerException if one of the arguments is {@code null}
109         * @throws IllegalArgumentException if the {@code limit} is smaller than
110         *         zero
111         */
112        static <V> int rewrite(
113                final TreeNode<V> tree,
114                final int limit,
115                final Iterable<? extends TreeRewriter<V>> rewriters
116        ) {
117                requireNonNull(tree);
118                requireNonNull(rewriters);
119                if (limit < 0) {
120                        throw new IllegalArgumentException(format(
121                                "Limit is smaller then zero: %d", limit
122                        ));
123                }
124
125                int rewritten = 0;
126                int count = 0;
127                do {
128                        count = 0;
129                        for (TreeRewriter<V> rw : rewriters) {
130                                count += rw.rewrite(tree, limit - rewritten);
131                        }
132
133                        rewritten += count;
134                } while(count > 0 && rewritten < limit);
135
136                return rewritten;
137        }
138
139        /**
140         * Rewrites the given {@code tree} by applying the given {@code rewriters}.
141         * This method to apply all rewriters, in the order they are given in
142         * the sequence, until the tree stays unchanged.
143         *
144         * @see #rewrite(TreeNode, int, Iterable)
145         *
146         * @param tree the tree to rewrite
147         * @param rewriters the rewriters applied to the tree
148         * @param <V> the tree value type
149         * @return {@code true} if the tree has been changed (rewritten) by this
150         *         method, {@code false} if the tree hasn't been changed
151         * @throws NullPointerException if one of the arguments is {@code null}
152         */
153        static <V> int rewrite(
154                final TreeNode<V> tree,
155                final Iterable<? extends TreeRewriter<V>> rewriters
156        ) {
157                return rewrite(tree, Integer.MAX_VALUE, rewriters);
158        }
159
160        /**
161         * Concat the given {@code rewriters} to one tree-rewriter.
162         *
163         * @param rewriters the tree-rewriter to concatenate
164         * @param <V> the tree value type
165         * @return a new tree-rewriter which concatenates the given one
166         * @throws NullPointerException if the given {@code rewriters} are
167         *         {@code null}
168         * @throws IllegalArgumentException if the {@code limit} is smaller than
169         *         zero
170         */
171        @SafeVarargs
172        static <V> TreeRewriter<V>
173        concat(final TreeRewriter<V>... rewriters) {
174                if (rewriters.length == 0) {
175                        throw new IllegalArgumentException(
176                                "The given rewriter array must not be empty."
177                        );
178                }
179
180                return (tree, limit) -> rewrite(tree, limit, List.of(rewriters));
181        }
182}