Skip to content
Snippets Groups Projects
Commit 5a60892d authored by Jaime Arias's avatar Jaime Arias
Browse files

Add basic tests for leaf and OR gate translation

parent a6040c10
No related branches found
No related tags found
No related merge requests found
#include "translator.hpp"
int main(){
/* Treasure hunters example */
cout << "#### \n Treasure hunters\n####" << endl;
Nand* TS = new Nand({NodeType::Attack}, "TS");
Sand* TF = new Sand({NodeType::Attack}, "TF");
And* ST = new And({NodeType::Attack}, "ST");
Leaf* b = new Leaf({NodeType::Attack}, "b");
Leaf* f = new Leaf({NodeType::Attack}, "f");
Or* GA = new Or({NodeType::Attack}, "GA");
Leaf* h = new Leaf({NodeType::Attack}, "h");
Leaf* e = new Leaf({NodeType::Defence}, "e");
Leaf* p = new Leaf({NodeType::Defence}, "p");
ADtree *treasure_hunter = new ADtree(TS);
treasure_hunter->add_child(TF, TS);
treasure_hunter->add_child(p, TS);
treasure_hunter->add_child(ST, TF);
treasure_hunter->add_child(GA, TF);
treasure_hunter->add_child(b, ST);
treasure_hunter->add_child(f, ST);
treasure_hunter->add_child(h, GA);
treasure_hunter->add_child(e, GA);
cout << "Tree Treasure hunters: " << endl;
cout << treasure_hunter->get_info();
cout << "Translation" << endl;
Automata *amata = Translator::adt2amas_translate(treasure_hunter);
cout << amata->get_info();
/* Forestalling a software release example */
cout << "#### \n Forestalling\n####" << endl;
Sand* FS = new Sand({NodeType::Attack}, "FS");
Or* SC = new Or({NodeType::Attack}, "SC");
Leaf* icp = new Leaf({NodeType::Attack}, "icp");
Leaf* dtm = new Leaf({NodeType::Attack}, "dtm");
Sand* BRB = new Sand({NodeType::Attack}, "BRB");
Leaf* bp = new Leaf({NodeType::Attack}, "bp");
Leaf* psc = new Leaf({NodeType::Attack}, "psc");
Snand* NAS = new Snand({NodeType::Attack}, "NAS");
Sand* NA = new Sand({NodeType::Attack}, "NA");
Leaf* hh = new Leaf({NodeType::Attack}, "hh");
Leaf* sb = new Leaf({NodeType::Attack}, "sb");
Leaf* heb = new Leaf({NodeType::Attack}, "heb");
Leaf* id = new Leaf({NodeType::Defence}, "id");
Nand* PRS = new Nand({NodeType::Attack}, "PRS");
Sand* PR = new Sand({NodeType::Attack}, "PR");
Leaf* hr = new Leaf({NodeType::Attack}, "hr");
Leaf* reb = new Leaf({NodeType::Attack}, "reb");
Leaf* rfc = new Leaf({NodeType::Attack}, "rfc");
Leaf* scr = new Leaf({NodeType::Defence}, "scr");
ADtree *forestall = new ADtree(FS);
forestall->add_child(SC, FS);
forestall->add_child(icp, FS);
forestall->add_child(dtm, FS);
forestall->add_child(BRB, SC);
forestall->add_child(NAS, SC);
forestall->add_child(PRS, SC);
forestall->add_child(bp, BRB);
forestall->add_child(psc, BRB);
forestall->add_child(NA, NAS);
forestall->add_child(id, NAS);
forestall->add_child(hh, NA);
forestall->add_child(sb, NA);
forestall->add_child(heb, NA);
forestall->add_child(PR, PRS);
forestall->add_child(scr, PRS);
forestall->add_child(hr, PR);
forestall->add_child(reb, PR);
forestall->add_child(rfc, PR);
cout << "Tree Forestalling: " << endl;
cout << forestall->get_info();
cout << "Translation" << endl;
Automata *amata2 = Translator::adt2amas_translate(forestall);
cout << amata2->get_info();
/* Compromise IoT device */
cout << "#### \n Iot-dev\n####" << endl;
Sand* CIoTD = new Sand({NodeType::Attack}, "CIoTD");
Snand* APNS = new Snand({NodeType::Attack}, "APNS");
And* APN = new And({NodeType::Attack}, "APN");
Or* CPN = new Or({NodeType::Attack}, "CPN");
Sand* AL = new Sand({NodeType::Attack}, "AL");
Leaf* flp = new Leaf({NodeType::Attack}, "flp");
Leaf* sma = new Leaf({NodeType::Attack}, "sma");
Sand* AW = new Sand({NodeType::Attack}, "AW");
Leaf* fw = new Leaf({NodeType::Attack}, "fw");
Leaf* bwk = new Leaf({NodeType::Attack}, "bwk");
Nand* GVC = new Nand({NodeType::Attack}, "GVC");
Leaf* gc = new Leaf({NodeType::Attack}, "gc");
Leaf* tla = new Leaf({NodeType::Defence}, "tla");
Leaf* inc = new Leaf({NodeType::Defence}, "inc");
Leaf* esv = new Leaf({NodeType::Attack}, "esv");
Leaf* rms = new Leaf({NodeType::Attack}, "rms");
ADtree *iot_dev = new ADtree(CIoTD);
iot_dev->add_child(APNS, CIoTD);
iot_dev->add_child(APN, APNS);
iot_dev->add_child(CPN, APN);
iot_dev->add_child(AL, CPN);
iot_dev->add_child(flp, AL);
iot_dev->add_child(sma, AL);
iot_dev->add_child(AW, CPN);
iot_dev->add_child(fw, AW);
iot_dev->add_child(bwk, AW);
iot_dev->add_child(GVC, APN);
iot_dev->add_child(gc, GVC);
iot_dev->add_child(tla,GVC);
iot_dev->add_child(inc, APNS);
iot_dev->add_child(esv, CIoTD);
iot_dev->add_child(rms, CIoTD);
cout << "Tree iot_dev: " << endl;
cout << iot_dev->get_info();
cout << "Translation" << endl;
Automata *amata3 = Translator::adt2amas_translate(iot_dev);
cout << amata3->get_info();
/* Obtain admin privileges */
cout << "#### \n Gain-admin\n####" << endl;
Or* OAP = new Or({NodeType::Attack}, "OAP");
Or* ACLI = new Or({NodeType::Attack}, "ACLI");
Leaf* co = new Leaf({NodeType::Attack}, "co");
Nand* ECCS = new Nand({NodeType::Attack}, "ECCS");
Or* ECC = new Or({NodeType::Attack}, "ECC");
Leaf* bcc = new Leaf({NodeType::Attack}, "bcc");
Leaf* ccg = new Leaf({NodeType::Attack}, "ccg");
scr = new Leaf({NodeType::Defence}, "scr");
Or* GSAP = new Or({NodeType::Attack}, "GSAP");
Snand* GAPS = new Snand({NodeType::Attack}, "GAPS");
Sand* GAP = new Sand({NodeType::Attack}, "GAP");
Leaf* opf = new Leaf({NodeType::Attack}, "opf");
Leaf* fgp = new Leaf({NodeType::Attack}, "fgp");
tla = new Leaf({NodeType::Defence}, "tla");
Nand* LSAS = new Nand({NodeType::Attack}, "LSAS");
Sand* LSA = new Sand({NodeType::Attack}, "LSA");
Leaf* bsa = new Leaf({NodeType::Attack}, "bsa");
Leaf* vsa = new Leaf({NodeType::Attack}, "vsa");
Leaf* sat = new Leaf({NodeType::Attack}, "sat");
Leaf* nv = new Leaf({NodeType::Defence}, "nv");
Nand* TSA = new Nand({NodeType::Attack}, "TSA");
Leaf* th = new Leaf({NodeType::Attack}, "th");
Or* DTH = new Or({NodeType::Defence}, "DTH");
Leaf* wd = new Leaf({NodeType::Defence}, "wd");
Leaf* efw = new Leaf({NodeType::Defence}, "efw");
Leaf* csa = new Leaf({NodeType::Attack}, "csa");
ADtree *gain_admin = new ADtree(OAP);
gain_admin->add_child(ACLI, OAP);
gain_admin->add_child(co, ACLI);
gain_admin->add_child(ECCS, ACLI);
gain_admin->add_child(ECC, ECCS);
gain_admin->add_child(bcc, ECC);
gain_admin->add_child(ccg, ECC);
gain_admin->add_child(scr, ECCS);
gain_admin->add_child(GSAP, OAP);
gain_admin->add_child(GAPS, GSAP);
gain_admin->add_child(GAP, GAPS);
gain_admin->add_child(opf, GAP);
gain_admin->add_child(fgp, GAP);
gain_admin->add_child(tla, GAPS);
gain_admin->add_child(LSAS, GSAP);
gain_admin->add_child(LSA, LSAS);
gain_admin->add_child(bsa, LSA);
gain_admin->add_child(vsa, LSA);
gain_admin->add_child(sat, LSA);
gain_admin->add_child(nv, LSAS);
gain_admin->add_child(TSA, GSAP);
gain_admin->add_child(th, TSA);
gain_admin->add_child(DTH, TSA);
gain_admin->add_child(wd, DTH);
gain_admin->add_child(efw, DTH);
gain_admin->add_child(csa, GSAP);
cout << "Tree gain-admin " << endl;
cout << gain_admin->get_info();
cout << "Translation" << endl;
Automata *amata4 = Translator::adt2amas_translate(gain_admin);
cout << amata4->get_info();
return 0;
int main() {
/* Treasure hunters example */
cout << "#### \n Treasure hunters\n####" << endl;
// Nand* TS = new Nand({NodeType::Attack}, "TS");
// Sand* TF = new Sand({NodeType::Attack}, "TF");
// And* ST = new And({NodeType::Attack}, "ST");
// Leaf* b = new Leaf({NodeType::Attack}, "b");
// Leaf* f = new Leaf({NodeType::Attack}, "f");
// Or* GA = new Or({NodeType::Attack}, "GA");
// Leaf* h = new Leaf({NodeType::Attack}, "h");
// Leaf* e = new Leaf({NodeType::Defence}, "e");
// Leaf* p = new Leaf({NodeType::Defence}, "p");
// ADtree* treasure_hunter = new ADtree(TS);
// treasure_hunter->add_child(TF, TS);
// treasure_hunter->add_child(p, TS);
// treasure_hunter->add_child(ST, TF);
// treasure_hunter->add_child(GA, TF);
// treasure_hunter->add_child(b, ST);
// treasure_hunter->add_child(f, ST);
// treasure_hunter->add_child(h, GA);
// treasure_hunter->add_child(e, GA);
// cout << "Tree Treasure hunters: " << endl;
// cout << treasure_hunter->get_info();
// cout << "Translation" << endl;
// Automata* amata = Translator::adt2amas_translate(treasure_hunter);
// cout << amata->get_info();
// /* Forestalling a software release example */
// cout << "#### \n Forestalling\n####" << endl;
// Sand* FS = new Sand({NodeType::Attack}, "FS");
// Or* SC = new Or({NodeType::Attack}, "SC");
// Leaf* icp = new Leaf({NodeType::Attack}, "icp");
// Leaf* dtm = new Leaf({NodeType::Attack}, "dtm");
// Sand* BRB = new Sand({NodeType::Attack}, "BRB");
// Leaf* bp = new Leaf({NodeType::Attack}, "bp");
// Leaf* psc = new Leaf({NodeType::Attack}, "psc");
// Snand* NAS = new Snand({NodeType::Attack}, "NAS");
// Sand* NA = new Sand({NodeType::Attack}, "NA");
// Leaf* hh = new Leaf({NodeType::Attack}, "hh");
// Leaf* sb = new Leaf({NodeType::Attack}, "sb");
// Leaf* heb = new Leaf({NodeType::Attack}, "heb");
// Leaf* id = new Leaf({NodeType::Defence}, "id");
// Nand* PRS = new Nand({NodeType::Attack}, "PRS");
// Sand* PR = new Sand({NodeType::Attack}, "PR");
// Leaf* hr = new Leaf({NodeType::Attack}, "hr");
// Leaf* reb = new Leaf({NodeType::Attack}, "reb");
// Leaf* rfc = new Leaf({NodeType::Attack}, "rfc");
// Leaf* scr = new Leaf({NodeType::Defence}, "scr");
// ADtree* forestall = new ADtree(FS);
// forestall->add_child(SC, FS);
// forestall->add_child(icp, FS);
// forestall->add_child(dtm, FS);
// forestall->add_child(BRB, SC);
// forestall->add_child(NAS, SC);
// forestall->add_child(PRS, SC);
// forestall->add_child(bp, BRB);
// forestall->add_child(psc, BRB);
// forestall->add_child(NA, NAS);
// forestall->add_child(id, NAS);
// forestall->add_child(hh, NA);
// forestall->add_child(sb, NA);
// forestall->add_child(heb, NA);
// forestall->add_child(PR, PRS);
// forestall->add_child(scr, PRS);
// forestall->add_child(hr, PR);
// forestall->add_child(reb, PR);
// forestall->add_child(rfc, PR);
// cout << "Tree Forestalling: " << endl;
// cout << forestall->get_info();
// cout << "Translation" << endl;
// Automata* amata2 = Translator::adt2amas_translate(forestall);
// cout << amata2->get_info();
// /* Compromise IoT device */
// cout << "#### \n Iot-dev\n####" << endl;
// Sand* CIoTD = new Sand({NodeType::Attack}, "CIoTD");
// Snand* APNS = new Snand({NodeType::Attack}, "APNS");
// And* APN = new And({NodeType::Attack}, "APN");
// Or* CPN = new Or({NodeType::Attack}, "CPN");
// Sand* AL = new Sand({NodeType::Attack}, "AL");
// Leaf* flp = new Leaf({NodeType::Attack}, "flp");
// Leaf* sma = new Leaf({NodeType::Attack}, "sma");
// Sand* AW = new Sand({NodeType::Attack}, "AW");
// Leaf* fw = new Leaf({NodeType::Attack}, "fw");
// Leaf* bwk = new Leaf({NodeType::Attack}, "bwk");
// Nand* GVC = new Nand({NodeType::Attack}, "GVC");
// Leaf* gc = new Leaf({NodeType::Attack}, "gc");
// Leaf* tla = new Leaf({NodeType::Defence}, "tla");
// Leaf* inc = new Leaf({NodeType::Defence}, "inc");
// Leaf* esv = new Leaf({NodeType::Attack}, "esv");
// Leaf* rms = new Leaf({NodeType::Attack}, "rms");
// ADtree* iot_dev = new ADtree(CIoTD);
// iot_dev->add_child(APNS, CIoTD);
// iot_dev->add_child(APN, APNS);
// iot_dev->add_child(CPN, APN);
// iot_dev->add_child(AL, CPN);
// iot_dev->add_child(flp, AL);
// iot_dev->add_child(sma, AL);
// iot_dev->add_child(AW, CPN);
// iot_dev->add_child(fw, AW);
// iot_dev->add_child(bwk, AW);
// iot_dev->add_child(GVC, APN);
// iot_dev->add_child(gc, GVC);
// iot_dev->add_child(tla, GVC);
// iot_dev->add_child(inc, APNS);
// iot_dev->add_child(esv, CIoTD);
// iot_dev->add_child(rms, CIoTD);
// cout << "Tree iot_dev: " << endl;
// cout << iot_dev->get_info();
// cout << "Translation" << endl;
// Automata* amata3 = Translator::adt2amas_translate(iot_dev);
// cout << amata3->get_info();
// /* Obtain admin privileges */
// cout << "#### \n Gain-admin\n####" << endl;
// Or* OAP = new Or({NodeType::Attack}, "OAP");
// Or* ACLI = new Or({NodeType::Attack}, "ACLI");
// Leaf* co = new Leaf({NodeType::Attack}, "co");
// Nand* ECCS = new Nand({NodeType::Attack}, "ECCS");
// Or* ECC = new Or({NodeType::Attack}, "ECC");
// Leaf* bcc = new Leaf({NodeType::Attack}, "bcc");
// Leaf* ccg = new Leaf({NodeType::Attack}, "ccg");
// scr = new Leaf({NodeType::Defence}, "scr");
// Or* GSAP = new Or({NodeType::Attack}, "GSAP");
// Snand* GAPS = new Snand({NodeType::Attack}, "GAPS");
// Sand* GAP = new Sand({NodeType::Attack}, "GAP");
// Leaf* opf = new Leaf({NodeType::Attack}, "opf");
// Leaf* fgp = new Leaf({NodeType::Attack}, "fgp");
// tla = new Leaf({NodeType::Defence}, "tla");
// Nand* LSAS = new Nand({NodeType::Attack}, "LSAS");
// Sand* LSA = new Sand({NodeType::Attack}, "LSA");
// Leaf* bsa = new Leaf({NodeType::Attack}, "bsa");
// Leaf* vsa = new Leaf({NodeType::Attack}, "vsa");
// Leaf* sat = new Leaf({NodeType::Attack}, "sat");
// Leaf* nv = new Leaf({NodeType::Defence}, "nv");
// Nand* TSA = new Nand({NodeType::Attack}, "TSA");
// Leaf* th = new Leaf({NodeType::Attack}, "th");
// Or* DTH = new Or({NodeType::Defence}, "DTH");
// Leaf* wd = new Leaf({NodeType::Defence}, "wd");
// Leaf* efw = new Leaf({NodeType::Defence}, "efw");
// Leaf* csa = new Leaf({NodeType::Attack}, "csa");
// ADtree* gain_admin = new ADtree(OAP);
// gain_admin->add_child(ACLI, OAP);
// gain_admin->add_child(co, ACLI);
// gain_admin->add_child(ECCS, ACLI);
// gain_admin->add_child(ECC, ECCS);
// gain_admin->add_child(bcc, ECC);
// gain_admin->add_child(ccg, ECC);
// gain_admin->add_child(scr, ECCS);
// gain_admin->add_child(GSAP, OAP);
// gain_admin->add_child(GAPS, GSAP);
// gain_admin->add_child(GAP, GAPS);
// gain_admin->add_child(opf, GAP);
// gain_admin->add_child(fgp, GAP);
// gain_admin->add_child(tla, GAPS);
// gain_admin->add_child(LSAS, GSAP);
// gain_admin->add_child(LSA, LSAS);
// gain_admin->add_child(bsa, LSA);
// gain_admin->add_child(vsa, LSA);
// gain_admin->add_child(sat, LSA);
// gain_admin->add_child(nv, LSAS);
// gain_admin->add_child(TSA, GSAP);
// gain_admin->add_child(th, TSA);
// gain_admin->add_child(DTH, TSA);
// gain_admin->add_child(wd, DTH);
// gain_admin->add_child(efw, DTH);
// gain_admin->add_child(csa, GSAP);
// cout << "Tree gain-admin " << endl;
// cout << gain_admin->get_info();
// cout << "Translation" << endl;
// Automata* amata4 = Translator::adt2amas_translate(gain_admin);
// cout << amata4->get_info();
return 0;
}
......@@ -40,7 +40,7 @@ vector<TreeNode*> TreeNode::get_parents() { return this->parents_; }
void TreeNode::set_node_type(NodeType node_type) { node_type_ = node_type; }
// void TreeNode::set_goal(string goal) { goal_ = goal; }
void TreeNode::set_goal(string goal) { goal_ = goal; }
// void TreeNode::set_value(bool value) { value_ = value; }
......
......@@ -13,7 +13,7 @@ class TreeNode {
NodeType node_type_;
string goal_; // TODO: rethink about this
string goal_; // name on the node
bool value_; // TODO: should I define here?
set<int> child_associated_ids_;
......@@ -55,7 +55,7 @@ class TreeNode {
void set_parents(vector<TreeNode*> parents);
// void set_id();
// void set_goal(string goal);
void set_goal(string goal);
// void set_value(bool value);
/* Print methods */
......
This diff is collapsed.
......@@ -15,21 +15,22 @@
class Translator {
protected:
static Automata *translation(TreeNode *node, Automata *amata);
static void leaf_to_automaton(TreeNode *node, Automata *amata);
static void and_to_automaton(TreeNode *node, Automata *amata);
static void or_to_automaton(TreeNode *node, Automata *amata);
static void nand_to_automaton(TreeNode *node, Automata *amata);
static void nor_to_automaton(TreeNode *node, Automata *amata);
static void sand_to_automaton(TreeNode *node, Automata *amata);
static void snand_to_automaton(TreeNode *node, Automata *amata);
static void sor_to_automaton(TreeNode *node, Automata *amata);
static void snor_to_automaton(TreeNode *node, Automata *amata);
// static Automata *translation(TreeNode *node, Automata *amata);
// static void and_to_automaton(TreeNode *node, Automata *amata);
// static void nand_to_automaton(TreeNode *node, Automata *amata);
// static void nor_to_automaton(TreeNode *node, Automata *amata);
// static void sand_to_automaton(TreeNode *node, Automata *amata);
// static void snand_to_automaton(TreeNode *node, Automata *amata);
// static void sor_to_automaton(TreeNode *node, Automata *amata);
// static void snor_to_automaton(TreeNode *node, Automata *amata);
public:
Translator();
~Translator();
static void leaf_to_automaton(Leaf *leaf, Automata *amata);
static void or_to_automaton(Or *node, Automata *amata);
static Automata *adt2amas_translate(ADtree *adtree);
};
......
......@@ -22,6 +22,18 @@ TEST_CASE("A node has a role", "[tree node]") {
}
}
TEST_CASE("A node has a goal (name)", "[tree node]") {
TreeNode node(NodeType::Attack, "a");
REQUIRE(node.get_node_type() == NodeType::Attack);
REQUIRE(node.get_goal() == "a");
SECTION("a goal can be retrieved and modified") {
node.set_goal("b");
REQUIRE(node.get_goal() == "b");
}
}
TEST_CASE("A node has children", "[tree node]") {
TreeNode node;
REQUIRE(node.get_child_associated_ids().empty());
......
#include "translator.hpp"
#include <algorithm>
#include <catch.hpp>
#include <iostream>
#include "leaf.hpp"
using namespace std;
using Catch::Matchers::Contains;
vector<pair<Action *, State *>> get_transition_relation(State *s) {
vector<pair<Action *, State *>> targets;
vector<Transition *> transitions = s->get_transitions();
transform(transitions.begin(), transitions.end(), back_inserter(targets),
[](Transition *t) {
return make_pair(t->get_action(), t->get_destination_state());
});
return targets;
}
// global instance of the translator
Translator adt2amas;
TEST_CASE("Leaf translation", "[translator]") {}
TEST_CASE("Leaf translation", "[translator]") {
Automata *amata = new Automata;
Leaf *l = new Leaf;
adt2amas.leaf_to_automaton(l, amata);
REQUIRE(amata->get_vector_automaton().size() == 1);
REQUIRE(amata->get_automaton_ids().size() == 1);
Automaton *automaton = amata->get_vector_automaton()[0];
SECTION("it has 3 states") { REQUIRE(automaton->get_states().size() == 3); }
SECTION("it has 4 transitions") {
REQUIRE(automaton->get_transitions().size() == 4);
}
SECTION("initial states has transitions to 2 states") {
State *initial_s = automaton->get_initial_state();
vector<Transition *> transitions = initial_s->get_transitions();
REQUIRE(transitions.size() == 2);
}
SECTION("normal states has only 1 transition (a loop) ") {
auto targets = get_transition_relation(automaton->get_initial_state());
for (auto t : targets) {
State *s = t.second;
REQUIRE(s->get_transitions().size() == 1);
REQUIRE((s->get_transitions()[0])->get_destination_state() == s);
}
}
}
TEST_CASE("Or Gate (no children) translation", "[translator]") {
Automata *amata = new Automata;
Or *or_gate = new Or;
REQUIRE_THROWS_WITH(adt2amas.or_to_automaton(or_gate, amata),
Contains("two or more children"));
}
TEST_CASE("Or Gate translation", "[translator]") {
Automata *amata = new Automata;
Or *or_gate = new Or;
Leaf *right = new Leaf;
Leaf *left = new Leaf;
ADtree tree(or_gate);
tree.add_child(left, or_gate);
tree.add_child(right, or_gate);
adt2amas.or_to_automaton(or_gate, amata);
REQUIRE(amata->get_vector_automaton().size() == 1);
REQUIRE(amata->get_automaton_ids().size() == 1);
REQUIRE(or_gate->get_children().size() == 2);
Automaton *automaton = amata->get_vector_automaton()[0];
SECTION("it has 5 states") {
auto states = automaton->get_states();
REQUIRE(states.size() == 5);
}
SECTION("it has 7 transitions") {
auto transitions = automaton->get_transitions();
REQUIRE(transitions.size() == 7);
}
SECTION("initial state has 3 transitions") {
State *initial_s = automaton->get_initial_state();
REQUIRE(initial_s->get_transitions().size() == 3);
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment