Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions include/mata/nft/nft.hh
Original file line number Diff line number Diff line change
Expand Up @@ -1052,6 +1052,18 @@ inline Nft compose(const Nft& lhs, const Nft& rhs,
Nft concatenate(const Nft& lhs, const Nft& rhs, bool use_epsilon = false,
StateRenaming* lhs_state_renaming = nullptr, StateRenaming* rhs_state_renaming = nullptr);

/**
* @brief Compute NFT by concatenating @p nft_to_concatenate with itself @p power times.
*
* Uses exponentiation by squaring for efficiency. The result accepts the language L^power,
* where L is the language of the input automaton.
*
* @param[in] nfa_to_concatenate NFT to concatenate with itself.
* @param[in] power The nth power (number of times to concatenate).
* @return NFT accepting the nth power of the input language.
*/
Nft concatenate_nth_power(Nft nft_to_concatenate, unsigned power);

/**
* @brief Compute automaton accepting a complement of @p nft.
*
Expand Down
30 changes: 30 additions & 0 deletions src/nft/concatenation.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include "mata/nft/algorithms.hh"
#include "mata/nft/nft.hh"
#include "mata/nft/builder.hh"

using namespace mata::nft;

Expand Down Expand Up @@ -124,4 +125,33 @@ Nft algorithms::concatenate_eps(
if (rhs_state_renaming != nullptr) { *rhs_state_renaming = rhs_states_renaming; }
return result;
} // concatenate_eps().

Nft concatenate_nth_power(Nft nft_to_concatenate, unsigned power) {
// `result` holds the accumulating product (starts as identity — empty-string NFT)
Nft result = builder::create_empty_string_nft(nft_to_concatenate.levels.num_of_levels);

// `base` is the current power of the original NFT.
Nft base = std::move(nft_to_concatenate);

// Exponentiation by squaring (binary exponentiation)
// For each binary digit (LSB first): if the bit is 1, multiply `result` by `base`.
// Then square `base` for the next bit.
while (power > 0) {
// If current least-significant bit is set, append `base` to `result`.
if (power & 1u) {
result.concatenate(base);
}

// Shift to the next bit.
power >>= 1;

// If there are still bits to process, square `base` (i.e. base = base * base).
if (power) {
base.concatenate(base);
}
}

return result;
}

} // Namespace mata::nft.
60 changes: 60 additions & 0 deletions tests/nft/nft-concatenation.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "mata/nfa/builder.hh"
#include "mata/nft/nft.hh"
#include "mata/applications/strings.hh"
#include "mata/nft/builder.hh"

using namespace mata::nft;
using namespace mata::applications::strings;
Expand Down Expand Up @@ -1081,3 +1082,62 @@ TEST_CASE("mata::nft::Concat_inplace performance", "[.profiling]") {
base.concatenate(concat);
}
}

TEST_CASE("mata::nft::concatenate_nth_power()") {
SECTION("Exponent 0 returns empty-string automaton") {
Nfa aut{};
aut.add_state(1);
aut.initial.insert(0);
aut.final.insert(1);
aut.delta.add(0, 'a', 1);

Nft nft(aut);

CHECK(are_equivalent(concatenate_nth_power(nft, 0), mata::nft::builder::create_empty_string_nft(nft.levels.num_of_levels)));
}

SECTION("Exponent 1 returns the same language") {
Nfa aut{};
aut.add_state(1);
aut.initial.insert(0);
aut.final.insert(1);
aut.delta.add(0, 'a', 1);

Nft nft(aut);

CHECK(are_equivalent(concatenate_nth_power(nft, 1), nft));
}

SECTION("Exponent 2 matches normal concatenate") {
Nfa aut{};
aut.add_state(2);
aut.initial.insert(0);
aut.final.insert(2);
aut.delta.add(0, 'a', 1);
aut.delta.add(1, 'b', 2);

Nft nft(aut);

Nft expected = nft;
expected.concatenate(nft);

CHECK(are_equivalent(concatenate_nth_power(nft, 2), expected));
}

SECTION("Exponent 4 matches repeated normal concatenate") {
Nfa aut{};
aut.add_state(1);
aut.initial.insert(0);
aut.final.insert(1);
aut.delta.add(0, 'c', 1);

Nft nft(aut);

Nft expected = nft;
expected.concatenate(nft);
expected.concatenate(nft);
expected.concatenate(nft);

CHECK(are_equivalent(concatenate_nth_power(nft, 4), expected));
}
}
Loading