/* * Frozen * Copyright 2016 QuarksLab * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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. */ #ifndef FROZEN_LETITGO_ALGORITHM_H #define FROZEN_LETITGO_ALGORITHM_H #include "frozen/bits/basic_types.h" #include "frozen/bits/version.h" #include "frozen/string.h" namespace frozen { // 'search' implementation if C++17 is not available // https://en.cppreference.com/w/cpp/algorithm/search template ForwardIterator search(ForwardIterator first, ForwardIterator last, const Searcher & searcher) { return searcher(first, last).first; } // text book implementation from // https://en.wikipedia.org/wiki/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm template class knuth_morris_pratt_searcher { bits::carray step_; bits::carray needle_; static constexpr bits::carray build_kmp_cache(char const (&needle)[size + 1]) { std::ptrdiff_t cnd = 0; bits::carray cache(-1); for (std::size_t pos = 1; pos < size; ++pos) { if (needle[pos] == needle[cnd]) { cache[pos] = cache[cnd]; cnd += 1; } else { cache[pos] = cnd; cnd = cache[cnd]; while (cnd >= 0 && needle[pos] != needle[cnd]) cnd = cache[cnd]; cnd += 1; } } return cache; } public: constexpr knuth_morris_pratt_searcher(char const (&needle)[size + 1]) : step_{build_kmp_cache(needle)}, needle_(needle) {} template constexpr std::pair operator()(ForwardIterator first, ForwardIterator last) const { std::size_t i = 0; ForwardIterator iter = first; while (iter != last) { if (needle_[i] == *iter) { if (i == (size - 1)) return { iter - i, iter - i + size }; ++i; ++iter; } else { if (step_[i] > -1) { i = step_[i]; } else { ++iter; i = 0; } } } return { last, last }; } }; template constexpr knuth_morris_pratt_searcher make_knuth_morris_pratt_searcher(char const (&needle)[N]) { return {needle}; } // text book implementation from // https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore%E2%80%93Horspool_algorithm template class boyer_moore_searcher { using skip_table_type = bits::carray; using suffix_table_type = bits::carray; skip_table_type skip_table_; suffix_table_type suffix_table_; bits::carray needle_; constexpr auto build_skip_table(char const (&needle)[size + 1]) { skip_table_type skip_table(size); for (std::size_t i = 0; i < size - 1; ++i) skip_table[needle[i]] -= i + 1; return skip_table; } constexpr bool is_prefix(char const (&needle)[size + 1], std::size_t pos) { std::size_t suffixlen = size - pos; for (std::size_t i = 0; i < suffixlen; i++) { if (needle[i] != needle[pos + i]) return false; } return true; } constexpr std::size_t suffix_length(char const (&needle)[size + 1], std::size_t pos) { // increment suffix length slen to the first mismatch or beginning // of the word for (std::size_t slen = 0; slen < pos ; slen++) if (needle[pos - slen] != needle[size - 1 - slen]) return slen; return pos; } constexpr auto build_suffix_table(char const (&needle)[size + 1]) { suffix_table_type suffix; std::ptrdiff_t last_prefix_index = size - 1; // first loop for (std::ptrdiff_t p = size - 1; p >= 0; p--) { if (is_prefix(needle, p + 1)) last_prefix_index = p + 1; suffix[p] = last_prefix_index + (size - 1 - p); } // second loop for (std::size_t p = 0; p < size - 1; p++) { auto slen = suffix_length(needle, p); if (needle[p - slen] != needle[size - 1 - slen]) suffix[size - 1 - slen] = size - 1 - p + slen; } return suffix; } public: constexpr boyer_moore_searcher(char const (&needle)[size + 1]) : skip_table_{build_skip_table(needle)}, suffix_table_{build_suffix_table(needle)}, needle_(needle) {} template constexpr std::pair operator()(RandomAccessIterator first, RandomAccessIterator last) const { if (size == 0) return { first, first }; if (size > size_t(last - first)) return { last, last }; RandomAccessIterator iter = first + size - 1; while (true) { std::ptrdiff_t j = size - 1; while (j > 0 && (*iter == needle_[j])) { --iter; --j; } if (j == 0 && *iter == needle_[0]) return { iter, iter + size}; std::ptrdiff_t jump = std::max(skip_table_[*iter], suffix_table_[j]); if (jump >= last - iter) return { last, last }; iter += jump; } } }; template constexpr boyer_moore_searcher make_boyer_moore_searcher(char const (&needle)[N]) { return {needle}; } } // namespace frozen #endif