1
0
mirror of https://github.com/kalmarek/Groups.jl.git synced 2024-12-26 18:30:29 +01:00
Groups.jl/src/findreplace.jl

183 lines
5.2 KiB
Julia
Raw Normal View History

2020-03-25 04:42:47 +01:00
###############################################################################
#
# Replacement of symbols / sub-words
#
issubsymbol(s::GSymbol, t::GSymbol) =
s.id == t.id && (0 s.pow t.pow || 0 s.pow t.pow)
function issubsymbol(s::FreeSymbol, w::GWord, sindex::Integer)
@boundscheck 1 sindex syllablelength(w) || throw(BoundsError(w, sindex))
return issubsymbol(s, syllables(w)[sindex])
end
function issubword(z::GWord, w::GWord, sindex::Integer)
isempty(z) && return true
@boundscheck 1 sindex syllablelength(w) || throw(BoundsError(w, sindex))
n = syllablelength(z)
n == 1 && return issubsymbol(first(syllables(z)), syllables(w)[sindex])
lastindex = sindex + n - 1
lastindex > syllablelength(w) && return false
issubsymbol(first(z), syllables(w)[sindex]) || return false
issubsymbol(syllables(z)[end], syllables(w)[lastindex]) || return false
for (zidx, widx) in zip(2:n-1, sindex+1:lastindex-1)
syllables(z)[zidx] == syllables(w)[widx] || return false
end
return true
end
"""doc
Find the first syllable index k>=i such that Z < syllables(W)[k:k+syllablelength(Z)-1]
"""
function findnext(subword::GWord, word::GWord, start::Integer)
@boundscheck 1 start syllablelength(word) || throw(BoundsError(word, start))
isempty(subword) && return start
stop = syllablelength(word) - syllablelength(subword) +1
for idx in start:1:stop
issubword(subword, word, idx) && return idx
end
return nothing
end
function findnext(s::FreeSymbol, word::GWord, start::Integer)
@boundscheck 1 start syllablelength(word) || throw(BoundsError(word, start))
isone(s) && return start
stop = syllablelength(word)
for idx in start:1:stop
issubsymbol(s, word, idx) && return idx
end
return nothing
end
function findprev(subword::GWord, word::GWord, start::Integer)
@boundscheck 1 start syllablelength(word) || throw(BoundsError(word, start))
isempty(subword) && return start
stop = 1
for idx in start:-1:1
issubword(subword, word, idx) && return idx
end
return nothing
end
function findprev(s::FreeSymbol, word::GWord, start::Integer)
@boundscheck 1 start syllablelength(word) || throw(BoundsError(word, start))
isone(s) && return start
stop = 1
for idx in start:-1:stop
issubsymbol(s, word, idx) && return idx
end
return nothing
end
findfirst(subword::GWord, word::GWord) = findnext(subword, word, 1)
findlast(subword::GWord, word::GWord) =
findprev(subword, word, syllablelength(word)-syllablelength(subword)+1)
function replace!(out::GW, W::GW, lhs_rhs::Pair{GS, T}; count::Integer=typemax(Int)) where
{GS<:GSymbol, T<:GWord, GW<:GWord}
(count == 0 || isempty(W)) && return W
count < 0 && throw(DomainError(count, "`count` must be non-negative."))
lhs, rhs = lhs_rhs
sW = syllables(W)
sW_idx = 1
r = something(findnext(lhs, W, sW_idx), 0)
sout = syllables(out)
resize!(sout, 0)
sizehint!(sout, syllablelength(W))
c = 0
while !iszero(r)
append!(sout, view(sW, sW_idx:r-1))
a, b = divrem(sW[r].pow, lhs.pow)
if b != 0
push!(sout, change_pow(sW[r], b))
end
append!(sout, repeat(syllables(rhs), a))
sW_idx = r+1
sW_idx > syllablelength(W) && break
r = something(findnext(lhs, W, sW_idx), 0)
c += 1
c == count && break
end
append!(sout, sW[sW_idx:end])
return freereduce!(out)
end
function replace!(out::GW, W::GW, lhs_rhs::Pair{T, T}; count::Integer=typemax(Int)) where
{GW<:GWord, T <: GWord}
(count == 0 || isempty(W)) && return W
count < 0 && throw(DomainError(count, "`count` must be non-negative."))
lhs, rhs = lhs_rhs
lhs_slen = syllablelength(lhs)
lhs_slen == 1 && return replace!(out, W, first(syllables(lhs))=>rhs; count=count)
sW = syllables(W)
sW_idx = 1
r = something(findnext(lhs, W, sW_idx), 0)
sout = syllables(out)
resize!(sout, 0)
sizehint!(sout, syllablelength(W))
c = 0
while !iszero(r)
append!(sout, view(sW, sW_idx:r-1))
exp = sW[r].pow - first(syllables(lhs)).pow
if exp != 0
push!(sout, change_pow(sW[r], exp))
end
append!(sout, syllables(rhs))
exp = sW[r+lhs_slen-1].pow - last(syllables(lhs)).pow
if exp != 0
push!(sout, change_pow(sW[r+lhs_slen-1], exp))
end
sW_idx = r+lhs_slen
sW_idx > syllablelength(W) && break
r = something(findnext(lhs, W, sW_idx), 0)
c += 1
c == count && break
end
# copy the rest
append!(sout, sW[sW_idx:end])
return freereduce!(out)
end
function replace(W::GW, lhs_rhs::Pair{T, T}; count::Integer=typemax(Int)) where
{GW<:GWord, T <: GWord}
return replace!(one(W), W, lhs_rhs; count=count)
end
function replace(W::GW, subst_dict::Dict{T,T}) where {GW<:GWord, T<:GWord}
out = W
for toreplace in reverse!(sort!(collect(keys(subst_dict)), by=length))
replacement = subst_dict[toreplace]
if length(toreplace) > length(out)
continue
end
out = replace(out, toreplace=>replacement)
end
return out
end