1
0
This repository has been archived on 2025-03-06. You can view files and clone it, but cannot push or open issues or pull requests.
Jip J. Dekker 3e72b0e857 Squashed 'software/gecode_on_record/' content from commit 37ed9bda4
git-subtree-dir: software/gecode_on_record
git-subtree-split: 37ed9bda495ea87e63217c19a374b5a93bb0078e
2021-06-16 14:03:52 +10:00

897 lines
21 KiB
C++
Executable File

/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
* Main authors:
* Christian Schulte <schulte@gecode.org>
*
* Copyright:
* Christian Schulte, 2004
*
* This file is part of Gecode, the generic constraint
* development environment:
* http://www.gecode.org
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
#include <gecode/minimodel.hh>
namespace Gecode {
namespace MiniModel {
class PosSet;
/**
* \brief Allocator for position sets
*/
typedef Support::BlockAllocator<PosSet,Region> PosSetAllocator;
class NodeInfo;
class PosInfo;
}
/// Implementation of the actual expression tree
class REG::Exp {
public:
/// Reference counter
unsigned int use_cnt;
/// Number of positions
int _n_pos;
/**
* \brief Type of regular expression
*/
enum ExpType {
ET_SYMBOL,
ET_CONC,
ET_OR,
ET_STAR
};
/// Type of regular expression
ExpType type;
/// Symbol or subexpressions
union {
/// Symbol
int symbol;
/// Subexpressions
Exp* kids[2];
} data;
/// Compute the follow positions
MiniModel::PosSet*
followpos(MiniModel::PosSetAllocator&,MiniModel::PosInfo*);
/// Increment use counter of \a e
static void inc(Exp* e);
/// Decrement use counter of \a e
static void dec(Exp* e);
/// Return number of positions of \a e
static int n_pos(Exp* e);
/// Print expression to \a os
void toString(std::ostringstream& os) const;
/// Print expression
std::string toString(void) const;
static void* operator new(size_t);
static void operator delete(void*);
private:
/// Deallocate memory
void dispose(void);
};
/*
* Operations on expression nodes
*
*/
forceinline void*
REG::Exp::operator new(size_t s) {
return heap.ralloc(s);
}
forceinline void
REG::Exp::operator delete(void*) {
// Deallocation happens in dispose
}
void
REG::Exp::dispose(void) {
Region region;
Support::DynamicStack<Exp*,Region> todo(region);
todo.push(this);
while (!todo.empty()) {
Exp* e = todo.pop();
switch (e->type) {
case ET_OR:
case ET_CONC:
if ((e->data.kids[1] != nullptr) && (--e->data.kids[1]->use_cnt == 0))
todo.push(e->data.kids[1]);
// fall through
case ET_STAR:
if ((e->data.kids[0] != nullptr) && (--e->data.kids[0]->use_cnt == 0))
todo.push(e->data.kids[0]);
default: ;
}
heap.rfree(e);
}
}
forceinline void
REG::Exp::inc(Exp* e) {
if (e != nullptr)
e->use_cnt++;
}
forceinline void
REG::Exp::dec(Exp* e) {
if ((e != nullptr) && (--e->use_cnt == 0))
e->dispose();
}
forceinline int
REG::Exp::n_pos(Exp* e) {
return (e != nullptr) ? e->_n_pos : 0;
}
void
REG::Exp::toString(std::ostringstream& os) const {
switch (type) {
case ET_SYMBOL:
os << "[" << data.symbol << "]";
return;
case ET_STAR:
{
bool par = ((data.kids[0] != nullptr) &&
((data.kids[0]->type == ET_CONC) ||
(data.kids[0]->type == ET_OR)));
os << (par ? "*(" : "*");
if (data.kids[0]==nullptr) {
os << "[]";
} else {
data.kids[0]->toString(os);
}
os << (par ? ")" : "");
return;
}
case ET_CONC:
{
bool par0 = ((data.kids[0] != nullptr) &&
(data.kids[0]->type == ET_OR));
os << (par0 ? "(" : "");
if (data.kids[0]==nullptr) {
os << "[]";
} else {
data.kids[0]->toString(os);
}
os << (par0 ? ")+" : "+");
bool par1 = ((data.kids[1] != nullptr) &&
(data.kids[1]->type == ET_OR));
os << (par1 ? "(" : "");
if (data.kids[1]==nullptr) {
os << "[]";
} else {
data.kids[1]->toString(os);
}
os << (par1 ? ")" : "");
return;
}
case ET_OR:
if (data.kids[0]==nullptr) {
os << "[]";
} else {
data.kids[0]->toString(os);
}
os << "|";
if (data.kids[1]==nullptr) {
os << "[]";
} else {
data.kids[1]->toString(os);
}
return;
default: GECODE_NEVER;
}
GECODE_NEVER;
return;
}
std::string
REG::Exp::toString(void) const {
std::ostringstream os;
toString(os);
return os.str();
}
/*
* Regular expressions
*
*/
forceinline
REG::REG(Exp* f) : e(f) {}
REG::REG(void) : e(nullptr) {}
REG::REG(const REG& r) : e(r.e) {
REG::Exp::inc(e);
}
const REG&
REG::operator =(const REG& r) {
if (&r != this) {
REG::Exp::inc(r.e);
REG::Exp::dec(e);
e = r.e;
}
return *this;
}
REG::~REG(void) {
REG::Exp::dec(e);
}
REG::REG(int s) : e(new Exp) {
e->use_cnt = 1;
e->_n_pos = 1;
e->type = REG::Exp::ET_SYMBOL;
e->data.symbol = s;
}
REG::REG(const IntArgs& x) {
int n = x.size();
if (n < 1)
throw MiniModel::TooFewArguments("REG");
Region region;
Exp** a = region.alloc<Exp*>(n);
// Initialize with symbols
for (int i=n; i--; ) {
a[i] = new Exp();
a[i]->use_cnt = 1;
a[i]->_n_pos = 1;
a[i]->type = REG::Exp::ET_SYMBOL;
a[i]->data.symbol = x[i];
}
// Build a balanced tree of alternative nodes
for (int m=n; m>1; ) {
if (m & 1) {
m -= 1;
Exp* e1 = a[m];
Exp* e2 = a[0];
a[0] = new Exp;
a[0]->use_cnt = 1;
a[0]->_n_pos = REG::Exp::n_pos(e1) + REG::Exp::n_pos(e2);
a[0]->type = REG::Exp::ET_OR;
a[0]->data.kids[0] = e1;
a[0]->data.kids[1] = e2;
} else {
m >>= 1;
for (int i=0; i<m; i++) {
Exp* e1 = a[2*i];
Exp* e2 = a[2*i+1];
a[i] = new Exp;
a[i]->use_cnt = 1;
a[i]->_n_pos = REG::Exp::n_pos(e1) + REG::Exp::n_pos(e2);
a[i]->type = REG::Exp::ET_OR;
a[i]->data.kids[0] = e1;
a[i]->data.kids[1] = e2;
}
}
}
e = a[0];
}
REG
REG::operator |(const REG& r2) {
if (e == r2.e)
return *this;
Exp* f = new Exp();
f->use_cnt = 1;
f->_n_pos = REG::Exp::n_pos(e) + REG::Exp::n_pos(r2.e);
f->type = REG::Exp::ET_OR;
f->data.kids[0] = e; REG::Exp::inc(e);
f->data.kids[1] = r2.e; REG::Exp::inc(r2.e);
REG r(f);
return r;
}
REG&
REG::operator |=(const REG& r2) {
if (e == r2.e)
return *this;
Exp* f = new Exp();
f->use_cnt = 1;
f->_n_pos = REG::Exp::n_pos(e) + REG::Exp::n_pos(r2.e);
f->type = REG::Exp::ET_OR;
f->data.kids[0] = e;
f->data.kids[1] = r2.e; REG::Exp::inc(r2.e);
e=f;
return *this;
}
REG
REG::operator +(const REG& r2) {
if (e == nullptr) return r2;
if (r2.e == nullptr) return *this;
Exp* f = new Exp();
f->use_cnt = 1;
f->_n_pos = REG::Exp::n_pos(e) + REG::Exp::n_pos(r2.e);
f->type = REG::Exp::ET_CONC;
f->data.kids[0] = e; REG::Exp::inc(e);
f->data.kids[1] = r2.e; REG::Exp::inc(r2.e);
REG r(f);
return r;
}
REG&
REG::operator +=(const REG& r2) {
if (r2.e == nullptr)
return *this;
if (e == nullptr) {
e=r2.e; REG::Exp::inc(e);
} else {
Exp* f = new Exp();
f->use_cnt = 1;
f->_n_pos = REG::Exp::n_pos(e) + REG::Exp::n_pos(r2.e);
f->type = REG::Exp::ET_CONC;
f->data.kids[0] = e;
f->data.kids[1] = r2.e; REG::Exp::inc(r2.e);
e=f;
}
return *this;
}
REG
REG::operator *(void) {
if ((e == nullptr) || (e->type == REG::Exp::ET_STAR))
return *this;
Exp* f = new Exp();
f->use_cnt = 1;
f->_n_pos = REG::Exp::n_pos(e);
f->type = REG::Exp::ET_STAR;
f->data.kids[0] = e; REG::Exp::inc(e);
REG r(f);
return r;
}
REG
REG::operator ()(unsigned int n, unsigned int m) {
REG r;
if ((n>m) || (m == 0))
return r;
if (n>0) {
unsigned int i = n;
REG r0 = *this;
while (i>0)
if (i & 1) {
r = r0+r; i--;
} else {
r0 = r0+r0; i >>= 1;
}
}
if (m > n) {
unsigned int i = m-n;
REG s0;
s0 = s0 | *this;
REG s;
while (i>0)
if (i & 1) {
s = s0+s; i--;
} else {
s0 = s0+s0; i >>= 1;
}
r = r + s;
}
return r;
}
REG
REG::operator ()(unsigned int n) {
REG r;
if (n > 0) {
REG r0 = *this;
unsigned int i = n;
while (i>0)
if (i & 1) {
r = r0+r; i--;
} else {
r0 = r0+r0; i >>= 1;
}
}
return r+**this;
}
REG
REG::operator +(void) {
return this->operator ()(1);
}
std::string
REG::toString(void) const {
if (e==nullptr) {
return "[]";
}
return e->toString();
}
namespace MiniModel {
/*
* Sets of positions
*
*/
/**
* \brief Order on position sets
*/
enum PosSetCmp {
PSC_LE,
PSC_EQ,
PSC_GR
};
/**
* \brief Sets of positions
*/
class PosSet : public Support::BlockClient<PosSet,Region> {
// Maintain sets of positions in inverse order
// This makes the check whether the last position is included
// more efficient.
public:
int pos; PosSet* next;
PosSet(void);
PosSet(int);
bool in(int) const;
static PosSetCmp cmp(PosSet*,PosSet*);
static PosSet* cup(PosSetAllocator&,PosSet*,PosSet*);
};
forceinline
PosSet::PosSet(void) {}
forceinline
PosSet::PosSet(int p) : pos(p), next(nullptr) {}
forceinline bool
PosSet::in(int p) const {
for (const PosSet* ps = this; ps != nullptr; ps = ps->next)
if (ps->pos == p) {
return true;
} else if (ps->pos < p) {
return false;
}
return false;
}
forceinline PosSetCmp
PosSet::cmp(PosSet* ps1, PosSet* ps2) {
while ((ps1 != nullptr) && (ps2 != nullptr)) {
if (ps1 == ps2)
return PSC_EQ;
if (ps1->pos < ps2->pos)
return PSC_LE;
if (ps1->pos > ps2->pos)
return PSC_GR;
ps1 = ps1->next; ps2 = ps2->next;
}
if (ps1 == ps2)
return PSC_EQ;
return ps1 == nullptr ? PSC_LE : PSC_GR;
}
PosSet*
PosSet::cup(PosSetAllocator& psm, PosSet* ps1, PosSet* ps2) {
PosSet* ps;
PosSet** p = &ps;
while ((ps1 != nullptr) && (ps2 != nullptr)) {
if (ps1 == ps2) {
*p = ps1; return ps;
}
PosSet* n = new (psm) PosSet;
*p = n; p = &n->next;
if (ps1->pos == ps2->pos) {
n->pos = ps1->pos;
ps1 = ps1->next; ps2 = ps2->next;
} else if (ps1->pos > ps2->pos) {
n->pos = ps1->pos; ps1 = ps1->next;
} else {
n->pos = ps2->pos; ps2 = ps2->next;
}
}
*p = (ps1 != nullptr) ? ps1 : ps2;
return ps;
}
/// Node information computed during traversal of the expressions
class NodeInfo {
public:
bool nullable;
PosSet* firstpos;
PosSet* lastpos;
NodeInfo(bool n=false, PosSet* fp=nullptr, PosSet* lp=nullptr);
};
/// Expression information
class ExpInfo {
public:
REG::Exp* exp;
bool open;
ExpInfo(REG::Exp* e=nullptr);
};
/**
* \brief Information on positions collected during traversal
*
*/
class PosInfo {
public:
int symbol;
PosSet* followpos;
};
forceinline
NodeInfo::NodeInfo(bool n, PosSet* fp, PosSet* lp)
: nullable(n), firstpos(fp), lastpos(lp) {}
forceinline
ExpInfo::ExpInfo(REG::Exp* e)
: exp(e), open(true) {}
}
forceinline MiniModel::PosSet*
REG::Exp::followpos(MiniModel::PosSetAllocator& psm,
MiniModel::PosInfo* pi) {
int p=0;
using MiniModel::PosSet;
using MiniModel::NodeInfo;
using MiniModel::ExpInfo;
Region region;
Support::DynamicStack<ExpInfo,Region> todo(region);
Support::DynamicStack<NodeInfo,Region> done(region);
// Start with first expression to be processed
todo.push(ExpInfo(this));
do {
if (todo.top().exp == nullptr) {
todo.pop();
done.push(NodeInfo(true,nullptr,nullptr));
} else {
switch (todo.top().exp->type) {
case ET_SYMBOL:
{
pi[p].symbol = todo.pop().exp->data.symbol;
PosSet* ps = new (psm) PosSet(p++);
done.push(NodeInfo(false,ps,ps));
}
break;
case ET_STAR:
if (todo.top().open) {
// Evaluate subexpression recursively
todo.top().open = false;
todo.push(todo.top().exp->data.kids[0]);
} else {
todo.pop();
NodeInfo ni = done.pop();
for (PosSet* ps = ni.lastpos; ps != nullptr; ps = ps->next)
pi[ps->pos].followpos =
PosSet::cup(psm,pi[ps->pos].followpos,ni.firstpos);
done.push(NodeInfo(true,ni.firstpos,ni.lastpos));
}
break;
case ET_CONC:
if (todo.top().open) {
// Evaluate subexpressions recursively
todo.top().open = false;
REG::Exp* e = todo.top().exp;
todo.push(e->data.kids[1]);
todo.push(e->data.kids[0]);
} else {
todo.pop();
NodeInfo ni1 = done.pop();
NodeInfo ni0 = done.pop();
for (PosSet* ps = ni0.lastpos; ps != nullptr; ps = ps->next)
pi[ps->pos].followpos =
PosSet::cup(psm,pi[ps->pos].followpos,ni1.firstpos);
done.push(NodeInfo(ni0.nullable & ni1.nullable,
ni0.nullable ?
PosSet::cup(psm,ni0.firstpos,ni1.firstpos) : ni0.firstpos,
ni1.nullable ?
PosSet::cup(psm,ni0.lastpos,ni1.lastpos) : ni1.lastpos));
}
break;
case ET_OR:
if (todo.top().open) {
// Evaluate subexpressions recursively
todo.top().open = false;
REG::Exp* e = todo.top().exp;
todo.push(e->data.kids[1]);
todo.push(e->data.kids[0]);
} else {
todo.pop();
NodeInfo ni1 = done.pop();
NodeInfo ni0 = done.pop();
done.push(NodeInfo(ni0.nullable | ni1.nullable,
PosSet::cup(psm,ni0.firstpos,ni1.firstpos),
PosSet::cup(psm,ni0.lastpos,ni1.lastpos)));
}
break;
default: GECODE_NEVER;
}
}
} while (!todo.empty());
return done.top().firstpos;
}
namespace MiniModel {
class StateNode;
/**
* \brief Allocator for state nodes
*/
typedef Support::BlockAllocator<StateNode,Heap> StatePoolAllocator;
/**
* \brief %Node together with state information
*/
class StateNode : public Support::BlockClient<StateNode,Heap> {
public:
PosSet* pos;
int state;
StateNode* next;
StateNode* left;
StateNode* right;
};
/**
* \brief %State pool combines a tree of states together with yet unprocessed states
*/
class StatePool {
public:
int n_states;
StateNode root;
StateNode* next;
StateNode* all;
StatePool(PosSet*);
StateNode* pop(void);
bool empty(void) const;
int state(StatePoolAllocator&, PosSet*);
};
forceinline
StatePool::StatePool(PosSet* ps) {
next = &root;
all = nullptr;
n_states = 1;
root.pos = ps;
root.state = 0;
root.next = nullptr;
root.left = nullptr;
root.right = nullptr;
}
forceinline StateNode*
StatePool::pop(void) {
StateNode* n = next;
next = n->next;
n->next = all;
all = n;
return n;
}
forceinline bool
StatePool::empty(void) const {
return next == nullptr;
}
forceinline int
StatePool::state(StatePoolAllocator& spm, PosSet* ps) {
int d = 0;
StateNode** p = nullptr;
StateNode* n = &root;
do {
switch (PosSet::cmp(ps,n->pos)) {
case PSC_EQ: return n->state;
case PSC_LE: p = &n->left; n = *p; break;
case PSC_GR: p = &n->right; n = *p; break;
default: GECODE_NEVER;
}
d++;
} while (n != nullptr);
n = new (spm) StateNode; *p = n;
n->pos = ps;
n->state = n_states++;
n->next = next;
n->left = nullptr;
n->right = nullptr;
next = n;
return n->state;
}
/**
* \brief Sort symbols
*/
class SymbolsInc {
public:
forceinline bool
operator ()(int x, int y) {
return x < y;
}
forceinline static void
sort(int s[], int n) {
SymbolsInc o;
Support::quicksort<int,SymbolsInc>(s,n,o);
}
};
/**
* \brief For collecting transitions while constructing a %DFA
*
*/
class TransitionBag {
private:
Support::DynamicArray<DFA::Transition,Heap> t;
int n;
public:
TransitionBag(void);
void add(int,int,int);
void finish(void);
DFA::Transition* transitions(void);
};
forceinline
TransitionBag::TransitionBag(void) : t(heap), n(0) {}
forceinline void
TransitionBag::add(int i_state, int symbol, int o_state) {
t[n].i_state = i_state;
t[n].symbol = symbol;
t[n].o_state = o_state;
n++;
}
forceinline void
TransitionBag::finish(void) {
t[n].i_state = -1;
}
forceinline DFA::Transition*
TransitionBag::transitions(void) {
return &t[0];
}
/**
* \brief For collecting final states while constructing a %DFA
*
*/
class FinalBag {
private:
Support::DynamicArray<int,Heap> f;
int n;
public:
FinalBag(void);
void add(int);
void finish(void);
int* finals(void);
};
forceinline
FinalBag::FinalBag(void) : f(heap), n(0) {}
forceinline void
FinalBag::add(int state) {
f[n++] = state;
}
forceinline void
FinalBag::finish(void) {
f[n] = -1;
}
forceinline int*
FinalBag::finals(void) {
return &f[0];
}
}
REG::operator DFA(void) {
using MiniModel::PosSetAllocator;
using MiniModel::StatePoolAllocator;
using MiniModel::PosInfo;
using MiniModel::PosSet;
using MiniModel::NodeInfo;
using MiniModel::StatePool;
using MiniModel::StateNode;
using MiniModel::TransitionBag;
using MiniModel::FinalBag;
using MiniModel::SymbolsInc;
Region region;
PosSetAllocator psm(region);
StatePoolAllocator spm(heap);
REG r = *this + REG(Int::Limits::max+1);
int n_pos = REG::Exp::n_pos(r.e);
PosInfo* pi = region.alloc<PosInfo>(n_pos);
for (int i=n_pos; i--; )
pi[i].followpos = nullptr;
PosSet* firstpos = r.e->followpos(psm,&pi[0]);
// Compute symbols
int* symbols = region.alloc<int>(n_pos);
for (int i=n_pos; i--; )
symbols[i] = pi[i].symbol;
SymbolsInc::sort(&symbols[0],n_pos-1);
int n_symbols = 1;
for (int i = 1; i<n_pos-1; i++)
if (symbols[i-1] != symbols[i])
symbols[n_symbols++] = symbols[i];
// Compute states and transitions
TransitionBag tb;
StatePool sp(firstpos);
while (!sp.empty()) {
StateNode* sn = sp.pop();
for (int i = n_symbols; i--; ) {
PosSet* u = nullptr;
for (PosSet* ps = sn->pos; ps != nullptr; ps = ps->next)
if (pi[ps->pos].symbol == symbols[i])
u = PosSet::cup(psm,u,pi[ps->pos].followpos);
if (u != nullptr)
tb.add(sn->state,symbols[i],sp.state(spm,u));
}
}
tb.finish();
// Compute final states
FinalBag fb;
for (StateNode* n = sp.all; n != nullptr; n = n->next)
if (n->pos->in(n_pos-1))
fb.add(n->state);
fb.finish();
return DFA(0,tb.transitions(),fb.finals(),true);
}
}
// STATISTICS: minimodel-any