source: cpp/frams/genetics/geno.cpp @ 1097

Last change on this file since 1097 was 999, checked in by Maciej Komosinski, 4 years ago

More consistent usage of "shapetype" (vs. "shape")

  • Property svn:eol-style set to native
File size: 9.2 KB
RevLine 
[286]1// This file is a part of Framsticks SDK.  http://www.framsticks.com/
[955]2// Copyright (C) 1999-2020  Maciej Komosinski and Szymon Ulatowski.
[286]3// See LICENSE.txt for details.
[109]4
5#include "geno.h"
[145]6#include "genoconv.h"
[452]7#include <common/loggers/loggers.h>
[841]8#include <common/util-string.h>
[109]9#include <frams/model/model.h>
10
[348]11THREAD_LOCAL_DEF_PTR(Geno::Validators, geno_validators);
12THREAD_LOCAL_DEF_PTR(GenoConvManager, geno_converters);
[109]13
[522]14Geno::Validators* Geno::getValidators() { return tlsGetPtr(geno_validators); }
15GenoConvManager* Geno::getConverters() { return tlsGetPtr(geno_converters); }
[346]16
[348]17Geno::Validators* Geno::useValidators(Validators* val)
[522]18{
19        return tlsSetPtr(geno_validators, val);
20}
[348]21GenoConvManager* Geno::useConverters(GenoConvManager* gcm)
[522]22{
23        return tlsSetPtr(geno_converters, gcm);
24}
[346]25
[972]26bool Geno::formatIsOneOf(const SString& format, const SString& format_list)
27{
28        if (strchr(format_list.c_str(), ',') == NULL)
29                return format == format_list;
30        else
31        {
32                SString item; int pos = 0;
33                while (format_list.getNextToken(pos, item, ','))
34                        if (item == format)
35                                return true;
36        }
37        return false;
38}
39
[988]40const SString Geno::FORMAT_INVALID = "invalid";
41const SString Geno::FORMAT_UNKNOWN = "";
[972]42const SString Geno::F0_FORMAT_LIST = "0,0s";
[955]43
[972]44bool Geno::isF0Format(const SString& format_list)
45{
46        if (strchr(format_list.c_str(), ',') == NULL)
47                return formatIsOneOf(format_list, F0_FORMAT_LIST);
48        SString item; int pos = 0;
49        while (format_list.getNextToken(pos, item, ','))
50                if (!formatIsOneOf(item, F0_FORMAT_LIST))
51                        return false;
52        return true;
53}
54
[955]55void Geno::init(const SString& genstring, const SString& genformat, const SString& genname, const SString& comment)
[109]56{
[150]57        refcount = 1;
58        owner = 0;
59        f0gen = 0;
[530]60        isvalid = -1;
61        name = genname;
62        txt = comment;
[732]63        setGenesAndFormat(genstring, genformat);
[530]64}
65
[955]66static SString trimAndValidateFormat(const SString& input) //the new requirement for genotype format name: no whitespace
[530]67{
[955]68        SString format = trim(input);
[972]69        if (format.length() == 0 || strContainsOneOf(format.c_str(), " \r\n\t"))
[988]70                return Geno::FORMAT_INVALID;
[955]71        return format;
72}
73
74void Geno::setGenesAndFormat(const SString& genstring, const SString& in_genformat)
75{
[150]76        mapinshift = 0;
77        mapoutshift = 0;
78        SString gencopy(genstring);
[955]79        SString genformat = in_genformat;
[988]80        if (genformat == FORMAT_UNKNOWN)
[109]81        { // unknown format
[955]82                genformat = "1";
[150]83                if (genstring.charAt(0) == '/')
[109]84                {
[522]85                        int end, error_end = -1;
[150]86                        switch (genstring.charAt(1))
[109]87                        {
88                        case '/':
[150]89                                if ((end = genstring.indexOf('\n')) >= 0)
90                                {
[955]91                                        genformat = trimAndValidateFormat(genstring.substr(2, end - 2));
[530]92                                        mapinshift = end + 1;
93                                        gencopy = genstring.substr(end + 1);
[732]94                                        if ((end > 0) && (genstring[end - 1] == '\r')) end--;
[522]95                                        error_end = end;
[150]96                                }
[109]97                                else
[150]98                                {
[955]99                                        genformat = trimAndValidateFormat(genstring.substr(2));
100                                        gencopy = "";
[972]101                                        mapinshift = genstring.length();
[150]102                                }
[109]103                                break;
104                        case '*':
[150]105                                if ((end = genstring.indexOf("*/")) >= 0)
106                                {
[955]107                                        genformat = trimAndValidateFormat(genstring.substr(2, end - 2));
[522]108                                        error_end = end + 2;
[150]109                                        gencopy = genstring.substr(end + 2);
110                                        mapinshift = end + 2;
111                                }
[109]112                                else
[150]113                                {
[955]114                                        genformat = trimAndValidateFormat(genstring.substr(2));
115                                        gencopy = "";
[972]116                                        mapinshift = genstring.length();
[150]117                                }
[109]118                                break;
119                        }
[988]120                        if (genformat == FORMAT_INVALID)
[521]121                        {
[522]122                                SString cut;
[972]123                                if (error_end < 0) error_end = genstring.length();
[522]124                                static const int MAX_ERROR = 20;
[955]125                                if (error_end > MAX_ERROR)
[522]126                                        cut = genstring.substr(0, MAX_ERROR) + "...";
127                                else
128                                        cut = genstring.substr(0, error_end);
129                                int lf = cut.indexOf('\n');
[732]130                                if (lf >= 0) { if ((lf > 0) && (cut[lf - 1] == '\r')) lf--; cut = cut.substr(0, lf); }
[530]131                                sstringQuote(cut);
[972]132                                logPrintf("Geno", "init", LOG_ERROR, "Invalid genotype format declaration: '%s'%s", cut.c_str(), name.length() ? SString::sprintf(" in '%s'", name.c_str()).c_str() : "");
[521]133                        }
134
[109]135                }
136        }
[150]137        gen = gencopy;
[530]138        multiline = (strchr(gen.c_str(), '\n') != 0);
[150]139        format = genformat;
[530]140        freeF0();
141        isvalid = -1;
[150]142        // mapoutshift...?
[109]143}
144
145void Geno::freeF0()
146{
[150]147        if (f0gen) { delete f0gen; f0gen = 0; }
[109]148}
149
[955]150Geno::Geno(const char *genstring, const char* genformat, const char *genname, const char *comment)
151{
152        init(SString(genstring), SString(genformat), SString(genname), SString(comment));
153}
154
[150]155Geno::Geno(const char *genstring, char genformat, const char *genname, const char *comment)
[109]156{
[955]157        SString genformat_string;
158        if (genformat > 0)
159                genformat_string = SString(&genformat, 1);
160        init(genstring, genformat_string, genname, comment);
[109]161}
162
[955]163Geno::Geno(const SString& genstring, const SString& genformat, const SString& genname, const SString& comment)
[109]164{
[150]165        init(genstring, genformat, genname, comment);
[109]166}
167
168Geno::Geno(const Geno& src)
[522]169        :gen(src.gen), name(src.name), format(src.format), txt(src.txt), isvalid(src.isvalid),
170        f0gen(0), mapinshift(src.mapinshift), mapoutshift(src.mapinshift),
171        multiline(src.multiline), owner(0)
[150]172{
173        f0gen = src.f0gen ? new Geno(*src.f0gen) : 0; refcount = 1;
174}
[109]175
176void Geno::operator=(const Geno& src)
177{
[150]178        freeF0();
179        gen = src.gen;
180        name = src.name;
181        format = src.format;
182        txt = src.txt;
183        isvalid = src.isvalid;
184        mapinshift = src.mapinshift;
185        mapoutshift = src.mapinshift;
186        multiline = src.multiline;
187        f0gen = src.f0gen ? new Geno(*src.f0gen) : 0;
188        owner = 0;
[109]189}
190
191Geno::Geno(const SString& src)
192{
[988]193        init(src, FORMAT_UNKNOWN, SString::empty(), SString::empty());
[109]194}
195
[534]196void Geno::setGenesAssumingSameFormat(const SString& g)
[109]197{
[150]198        gen = g;
199        isvalid = -1;
200        freeF0();
[109]201}
202
203void Geno::setString(const SString& g)
204{
[150]205        freeF0();
[988]206        init(g, FORMAT_UNKNOWN, SString::empty(), SString::empty());
[109]207}
208
209void Geno::setName(const SString& n)
210{
[150]211        name = n;
[109]212}
213
214void Geno::setComment(const SString& c)
215{
[150]216        txt = c;
[109]217}
218
[534]219SString Geno::getGenesAndFormat(void) const
[109]220{
[150]221        SString out;
[955]222        if (format != "1")
[109]223        {
[150]224                if (multiline)
225                        out += "//";
226                else
227                        out += "/*";
[955]228                out += format;
[150]229                if (multiline)
230                        out += "\n";
231                else
232                        out += "*/";
[109]233        }
[150]234        out += gen;
235        return out;
[109]236}
237
238int Geno::mapGenToString(int genpos) const
239{
[972]240        if (genpos > gen.length()) return -2;
[522]241        if (genpos < 0) return -1;
[150]242        return mapinshift + genpos;
[109]243}
244
245int Geno::mapStringToGen(int stringpos) const
246{
[150]247        stringpos -= mapinshift;
[972]248        if (stringpos > gen.length()) return -2;
[150]249        if (stringpos < 0) return -1;
250        return stringpos;
[109]251}
252
[534]253SString Geno::getGenes(void) const { return gen; }
[150]254SString Geno::getName(void) const { return name; }
[955]255SString Geno::getFormat(void) const { return format; }
[150]256SString Geno::getComment(void) const { return txt; }
[109]257
258int ModelGenoValidator::testGenoValidity(Geno& g)
259{
[972]260        if (Geno::formatIsOneOf(g.getFormat(), Geno::F0_FORMAT_LIST))
[109]261        {
[999]262                Model mod(g, Model::SHAPETYPE_UNKNOWN);
[150]263                return mod.isValid();
[109]264        }
[150]265        else
[109]266        {
[150]267                bool converter_missing;
[972]268                Geno f0geno = g.getConverted(Geno::F0_FORMAT_LIST, NULL, false, &converter_missing);
[150]269                if (converter_missing)
270                        return -1;//no result
271                return f0geno.isValid();
[109]272        }
273}
274
275void Geno::validate()
276{
[150]277        if (isvalid >= 0) return;
[972]278        if (gen.length() == 0) { isvalid = 0; return; }
[988]279        if (format == FORMAT_INVALID) { isvalid = 0; return; }
[522]280        Validators* vals = getValidators();
281        if (vals != NULL)
282        {
[508]283#ifdef WARN_VALIDATION_INCONSISTENCY
284                vector<int> results;
[955]285                int first_result = -1;
[508]286                FOREACH(GenoValidator*, v, (*vals))
[522]287                {
[955]288                        int r = v->testGenoValidity(*this);
289                        if (first_result < 0) first_result = r;
[508]290                        results.push_back(r);
[522]291                }
[955]292                int N = vals->size();
293                for (int i = 1; i < N; i++)
294                        if (results[i] != results[0])
[522]295                        {
[955]296                                SString txt = "Inconsistent validation results";
297                                for (int i = 0; i < N; i++)
298                                        txt += SString::sprintf(" %d", results[i]);
299                                txt += " for genotype '";
300                                txt += getGene();
301                                txt += "'";
302                                logPrintf("Geno", "validate", LOG_WARN, txt.c_str());
303                                break;
[522]304                        }
[955]305                isvalid = first_result;
306                if (isvalid >= 0)
[508]307                        return;
308#else
[522]309                FOREACH(GenoValidator*, v, (*vals))
310                        if ((isvalid = v->testGenoValidity(*this)) >= 0)
311                                return;
[508]312#endif
[522]313        }
[150]314        isvalid = 0;
[955]315        logPrintf("Geno", "validate", LOG_WARN, "Wrong configuration? No genotype validators defined for genetic format 'f%s'.", format.c_str());
[109]316}
317
318bool Geno::isValid(void)
319{
[522]320        if (isvalid < 0)
321        {
322                LoggerToMemory err(LoggerBase::Enable | LoggerToMemory::StoreAllMessages, LOG_INFO);
[452]323                validate();
324                err.disable();
[522]325                string msg = err.getCountSummary();
326                if (msg.size() > 0)
327                {
328                        msg += ssprintf(" while checking validity of '%s'", getName().c_str());
329                        msg += "\n";
330                        msg += err.getMessages();
331                        logMessage("Geno", "isValid", err.getErrorLevel(), msg.c_str());
[452]332                }
[522]333        }
334        return isvalid > 0;
[109]335}
336
[972]337Geno Geno::getConverted(SString otherformat_list, MultiMap *m, bool using_checkpoints, bool *converter_missing)
[109]338{
[972]339        if (formatIsOneOf(getFormat(), otherformat_list)) { if (converter_missing) *converter_missing = false; return *this; }
[109]340#ifndef NO_GENOCONVMANAGER
[522]341        GenoConvManager *converters = getConverters();
[150]342        if (converters)
[109]343        {
[972]344                if ((isF0Format(otherformat_list)) && (!m) && (!using_checkpoints))
[145]345                {
[150]346                        if (!f0gen)
[972]347                                f0gen = new Geno(converters->convert(*this, otherformat_list, NULL, using_checkpoints, converter_missing));
[500]348                        else
[522]349                        {
350                                if (converter_missing) *converter_missing = false;
351                        }
[150]352                        return *f0gen;
353                }
354                else
[972]355                        return converters->convert(*this, otherformat_list, m, using_checkpoints, converter_missing);
[109]356        }
[145]357#endif
[150]358        if (converter_missing) *converter_missing = true;
[972]359        return (formatIsOneOf(getFormat(), otherformat_list)) ? *this : Geno("", "", "", "GenConvManager not available");
[109]360}
361
362Geno::~Geno()
363{
[150]364        if (f0gen) delete f0gen;
[109]365}
Note: See TracBrowser for help on using the repository browser.