text: /*
   *  Tamgu (탐구)
   *
   * Copyright 2019-present NAVER Corp.
   * under BSD 3-clause
   */
  /* --- CONTENTS ---
   Project    : Tamgu (탐구)
   Version    : See tamgu.cxx for the version number
   filename   : tamgutreemapii.cxx
   Date       : 2017/09/01
   Purpose    : map implementation
   Programmer : Claude ROUX (claude.roux@naverlabs.com)
   Reviewer   :
  */
  
  #include "tamgu.h"
  
  #include "tamguint.h"
  //#prime
  #include "tamgutreemapii.h"
  #include "tamgulist.h"
  #include "tamguivector.h"
  
  
  //We need to declare once again our local definitions.
  Exporting basebin_hash<treemapiiMethod>  Tamgutreemapii::methods;
  Exporting map<string, string> Tamgutreemapii::infomethods;
  Exporting basebin_hash<unsigned long> Tamgutreemapii::exported;
  
  Exporting short Tamgutreemapii::idtype = 0;
  
  //-------------------------------------------------------------------------
  //MethodInitialization will add the right references to "name", which is always a new method associated to the object we are creating
  void Tamgutreemapii::AddMethod(TamguGlobal* global, string name,treemapiiMethod func, unsigned long arity, string infos) {
      short idname = global->Getid(name);
      methods[idname] = func;
      infomethods[name] = infos;
      exported[idname] = arity;
  }
  
  
  
      void Tamgutreemapii::Setidtype(TamguGlobal* global) {
      Tamgutreemapii::InitialisationModule(global,"");
  }
  
  
     bool Tamgutreemapii::InitialisationModule(TamguGlobal* global, string version) {
      methods.clear();
      infomethods.clear();
      exported.clear();
  
      
      Tamgutreemapii::idtype = global->Getid("treemapii");
  
      Tamgutreemapii::AddMethod(global, "clear", &Tamgutreemapii::MethodClear, P_NONE, "clear(): clear the container.");
      
      Tamgutreemapii::AddMethod(global, "invert", &Tamgutreemapii::MethodInvert, P_NONE, "invert(): return a map with key/value inverted.");
      Tamgutreemapii::AddMethod(global, "find", &Tamgutreemapii::MethodFind, P_ONE, "find(value): test if a value belongs to the map and return 'true' or the corresponding keys.");
  
  
      Tamgutreemapii::AddMethod(global, "items", &Tamgutreemapii::MethodItems, P_NONE, "items(): Return a vector of {key:value} pairs.");
  
  
      Tamgutreemapii::AddMethod(global, "join", &Tamgutreemapii::MethodJoin, P_TWO, "join(string sepkey,string sepvalue): Produce a string representation for the container.");
      Tamgutreemapii::AddMethod(global, "test", &Tamgutreemapii::MethodTest, P_ONE, "test(key): Test if key belongs to the map container.");
      Tamgutreemapii::AddMethod(global, "keys", &Tamgutreemapii::MethodKeys, P_NONE, "keys(): Return the map container keys as a vector.");
      Tamgutreemapii::AddMethod(global, "values", &Tamgutreemapii::MethodValues, P_NONE, "values(): Return the map container values as a vector.");
      Tamgutreemapii::AddMethod(global, "sum", &Tamgutreemapii::MethodSum, P_NONE, "sum(): return the sum of elements.");
      Tamgutreemapii::AddMethod(global, "product", &Tamgutreemapii::MethodProduct, P_NONE, "product(): return the product of elements.");
      Tamgutreemapii::AddMethod(global, "pop", &Tamgutreemapii::MethodPop, P_ONE, "pop(key): Erase an element from the map");
      Tamgutreemapii::AddMethod(global, "merge", &Tamgutreemapii::MethodMerge, P_ONE, "merge(v): Merge v into the vector.");
  
      if (version != "") {
          global->newInstance[Tamgutreemapii::idtype] = new Tamgutreemapii(global);
          
          global->RecordMethods(Tamgutreemapii::idtype, Tamgutreemapii::exported);
      }
  
      return true;
  }
  
  
  Exporting TamguIteration* Tamgutreemapii::Newiteration(bool direction) {
      return new TamguIterationtreemapii(this, direction);
  }
  
  
  Exporting Tamgu* Tamgutreemapii::in(Tamgu* context, Tamgu* a, short idthread) {
      //Three cases along the container type...
      //It is a Boolean, we expect false or true
      //It is an integer, we expect a position in v
      //It is a container, we are looking for all positions...
      
      long val = a->Integer();
  
      if (context->isVectorContainer()) {
          Tamguivector* v = (Tamguivector*)Selectaivector(context);
          Doublelocking _lock(this, v);
          try {
              values.at(val);
              v->values.push_back(val);
          }
          catch(const std::out_of_range& oor) {}
  
          return v;
      }
  
      if (context->isNumber()) {
          locking();
          try {
              values.at(val);
              unlocking();
              return globalTamgu->Provideint(val);
          }
          catch(const std::out_of_range& oor) {
              unlocking();
              return aNOELEMENT;
          }
      }
      
      locking();
      try {
          values.at(val);
          unlocking();
          return aTRUE;
      }
      catch(const std::out_of_range& oor) {
          unlocking();
          return aFALSE;
      }
  
  }
  
  
  Exporting Tamgu* Tamgutreemapii::MethodFind(Tamgu* context, short idthread, TamguCall* callfunc) {
      //Three cases along the container type...
      //It is a Boolean, we expect false or true
      //It is an integer, we expect a position in v
      //It is a container, we are looking for all positions...
  
      
      long val = callfunc->Evaluate(0, context, idthread)->Integer();
      
      if (context->isBoolean()) {
          Locking _lock(this);
          for (auto& it : values) {
              if (it.second == val)
                  return aTRUE;
          }
          return aFALSE;
      }
      if (context->isVectorContainer()) {
          Tamguivector* v = (Tamguivector*)Selectaivector(context);
          Doublelocking _lock(this, v);
          for (auto& it : values) {
              if (it.second == val)
                  v->values.push_back(it.first);
          }
          return v;
      }
      
      Locking _lock(this);
      for (auto& it : values) {
          if (it.second == val)
              return globalTamgu->Provideint(it.first);
      }
      return aNULL;
  }
  
  
  
  Exporting Tamgu* Tamgutreemapii::Push(Tamgu* k, Tamgu* v) {
      locking();
      long s = k->Integer();
      values[s] = v->Integer();
      unlocking();
      return aTRUE;
  }
  
  Exporting Tamgu* Tamgutreemapii::Pop(Tamgu* kkey) {
      long k = kkey->Integer();
      locking();
      if (values.erase(k)) {
          unlocking();
          return aTRUE;
      }
      unlocking();
      return aFALSE;
  }
  
  Exporting void Tamgutreemapii::Clear() {
      locking();
      values.clear();
      unlocking();
  }
  
  
  
  Exporting string Tamgutreemapii::String() {
      locking();
      stringstream res;
  
      res << "{";
      bool beg = true;
      for (auto& it : values) {
          if (beg == false)
              res << ",";
          beg = false;
          res << it.first << ":" << it.second;
      }
      unlocking();
      res << "}";
      return res.str();
  }
  
  Exporting string Tamgutreemapii::JSonString() {
      locking();
      stringstream res;
  
      res << "{";
      bool beg = true;
      for (auto& it : values) {
          if (beg == false)
              res << ",";
          beg = false;
          res << '"' << it.first << '"' << ":" << it.second;
      }
      unlocking();
      res << "}";
      return res.str();
  }
  
  
  Exporting long Tamgutreemapii::Integer() {
      locking();
      long sz = values.size();
      unlocking();
      return sz;
  }
  
  Exporting double Tamgutreemapii::Float() {
      locking();
      long sz = values.size();
      unlocking();
      return sz;
  }
  
  Exporting BLONG Tamgutreemapii::Long() {
      locking();
      long sz = values.size();
      unlocking();
      return sz;
  }
  
  Exporting bool Tamgutreemapii::Boolean() {
      locking();
      bool b = values.empty();
      unlocking();
      return !b;
  }
  
  
  //Basic operations
  Exporting long Tamgutreemapii::Size() {
      locking();
      long sz = values.size();
      unlocking();
      return sz;
  }
  
  
  Exporting Tamgu*  Tamgutreemapii::Put(Tamgu* idx, Tamgu* ke, short idthread) {
      if (!idx->isIndex()) {
          if (ke == this)
              return aTRUE;
  
          if (ke->isNULL()) {
              Clear();
              return aTRUE;
          }
          
          if (ke->Type() == Tamgutreemapii::idtype) {
              Doublelocking _lock(this, ke);
              values = ((Tamgutreemapii*)ke)->values;
              return aTRUE;
          }
  
          if (ke->isMapContainer()) {
              Doublelocking _lock(this, ke);
              values.clear();
              TamguIteration* itr = ke->Newiteration(false);
              for (itr->Begin(); itr->End() == aFALSE; itr->Next())
                  values[itr->Keyinteger()] = itr->Valueinteger();
              itr->Release();
              return aTRUE;
          }
          if (ke->isVectorContainer()) {
              Doublelocking _lock(this, ke);
              values.clear();
              long nb = 0;
              for (long it = 0; it < ke->Size(); ++it) {
                  values[nb] = ke->getinteger(it);
                  nb++;
              }
              return aTRUE;
          }
          if (ke->Type() == a_list) {
              Doublelocking _lock(this, ke);
              Tamgulist* kvect = (Tamgulist*)ke;
              values.clear();
              long nb = 0;
  
              for (auto& it : kvect->values) {
                  values[nb] = it->Integer();
                  nb++;
              }
              return aTRUE;
          }
          ke = ke->Map(idthread);
          if (!ke->isMapContainer())
              return globalTamgu->Returnerror("Wrong map initialization", idthread);
          locking();
          values.clear();
          if (ke->Type() == Tamgutreemapii::idtype)
              values = ((Tamgutreemapii*)ke)->values;
          else {
              TamguIteration* itr = ke->Newiteration(false);
              for (itr->Begin(); itr->End() != aTRUE; itr->Next())
                  values[itr->Keyinteger()] = itr->Valueinteger();
              itr->Release();
          }
          ke->Release();
          unlocking();
          return aTRUE;
      }
      locking();
      values[idx->Getinteger(idthread)] = ke->Integer();
      unlocking();
      return aTRUE;
  }
  
  
  Exporting Tamgu* Tamgutreemapii::Eval(Tamgu* contextualpattern, Tamgu* idx, short idthread) {
  
  
      if (!idx->isIndex()) {
          //particular case, the contextualpattern is a vector, which means that we expect a set of keys
          //as a result
          if (contextualpattern->isMapContainer())
              return this;
          
          //particular case, the contextualpattern is a vector, which means that we expect a set of keys
          //as a result
          if (contextualpattern->isVectorContainer() || contextualpattern->Type() == a_list) {
              Tamgu* vect = contextualpattern->Newinstance(idthread);
              locking();
              map<long,long>::iterator it;
              for (it = values.begin(); it != values.end(); it++)
                  vect->Push(globalTamgu->Provideint(it->first));
              unlocking();
              return vect;
          }
  
          if (contextualpattern->isNumber()) {
              long v = Size();
              return globalTamgu->Provideint(v);
          }
  
          return this;
      }
  
      if (idx->isInterval()) {
          Locking _lock(this);
          Tamgutreemapii* kmap = new Tamgutreemapii;
          Tamgu* key = ((TamguIndex*)idx)->left->Eval(aNULL, aNULL, idthread);
          Tamgu* keyright = ((TamguIndex*)idx)->right->Eval(aNULL, aNULL, idthread);
          long vleft = key->Integer();
          long vright = keyright->Integer();
          map<long,long>::iterator it = values.find(vleft);
          if (it == values.end() && key != aNULL) {
              key->Release();
              return kmap;
          }
          if (key == aNULL)
              it = values.begin();
          key->Release();
          map<long, long>::iterator itr= values.end();
          if (keyright != aNULL) {
              itr = values.find(vright);
              if (itr == values.end()) {
                  if (keyright != ((TamguIndex*)idx)->right)
                      keyright->Release();
                  return kmap;
              }
          }
          if (keyright != ((TamguIndex*)idx)->right)
              keyright->Release();
          for (; it != values.end(); it++) {
              kmap->values[it->first] = it->second;
              if (it == itr)
                  return kmap;
          }
  
          if (itr != values.end())
              kmap->values.clear();
          return kmap;
  
      }
  
      long skey = idx->Getinteger(idthread);
  
      Tamgu* kval = Value(skey);
      if (kval == aNOELEMENT) {
          if (globalTamgu->erroronkey)
              return globalTamgu->Returnerror("Wrong index", idthread);
          return aNOELEMENT;
  
      }
      return kval;
  }
  
  Exporting Tamgu* Tamgutreemapii::same(Tamgu* a) {
  
      if (a->Type() != idtype)
          return Mapcompare(this, a, NULL);
  
      Tamgutreemapii* m = (Tamgutreemapii*)a;
  
      Doublelocking _lock(this, m);
      if (m->values.size() != values.size())
          return aFALSE;
      map<long,long>::iterator it = m->values.begin();
      while (it != m->values.end()) {
          if (values.find(it->first) == values.end())
              return aFALSE;
          if (it->second != values[it->first])
              return aFALSE;
          it++;
      }
      return aTRUE;
  }
  
  Exporting Tamgu* Tamgutreemapii::xorset(Tamgu* b, bool itself) {
      Doublelocking _lock(this, b);
      
      Tamgutreemapii* res;
      
      
      if (b->isMapContainer()) {
          TamguIteration* itr = b->Newiteration(false);
  
          res = new Tamgutreemapii;
          map<long, long> keys;
  
          for (auto& it : values)
              keys[it.first] = it.second;
              
          long v;
          for (itr->Begin(); itr->End() != aTRUE; itr->Next()) {
              v = itr->Keyinteger();
              if (keys.find(v) == keys.end())
                  keys[v]=itr->Valueinteger();
              else {
                  if (values[v]==itr->Valueinteger())
                      keys.erase(v);
              }
          }
          itr->Release();
  
          for (auto& a : keys)
              res->values[a.first]= a.second;
  
          return res;
  
  
      }
      if (itself)
          res = this;
      else
          res = (Tamgutreemapii*)Atom(true);
  
      long v = b->Integer();
      for (auto& it : values)
          it.second = it.second ^ v;
      return res;
  }
  
  Exporting Tamgu* Tamgutreemapii::orset(Tamgu* b, bool itself) {
      Doublelocking _lock(this, b);
  
      Tamgutreemapii* res;
      if (itself)
          res = this;
      else
          res = (Tamgutreemapii*)Atom(true);
      if (b->isMapContainer()) {
          res->Merging(b);
          return res;
      }
      long v = b->Integer();
      for (auto& it : res->values)
          it.second = it.second | v;
      return res;
  }
  
  Exporting Tamgu* Tamgutreemapii::andset(Tamgu* b, bool itself) {
      Doublelocking _lock(this, b);
  
      Tamgutreemapii* res;
      if (b->isMapContainer()) {
          TamguIteration* itr = b->Newiteration(false);
          res = new Tamgutreemapii;
          long v;
          for (itr->Begin(); itr->End() != aTRUE; itr->Next()) {
              v = itr->Valueinteger();
              if (values.find(itr->Keyinteger()) != values.end() && values[itr->Keyinteger()]==v)
                  res->values[itr->Keyinteger()] = v;
          }
          itr->Release();
          return res;
      }
      if (itself)
          res = this;
      else
          res = (Tamgutreemapii*)Atom(true);
      long v = b->Integer();
      for (auto& it : res->values)
          it.second = it.second & v;
      return res;
  }
  
  Exporting Tamgu* Tamgutreemapii::plus(Tamgu* b, bool itself) {
      Doublelocking _lock(this, b);
  
      Tamgutreemapii* res;
      if (b->isMapContainer()) {
          TamguIteration* itr = b->Newiteration(false);
  
          res = new Tamgutreemapii;
          long k;
          for (itr->Begin(); itr->End() != aTRUE; itr->Next()) {
              k = itr->Keyinteger();
              try {
                  res->values[k] = values.at(k) + itr->Valueinteger();
              }
              catch (const std::out_of_range& oor) {
              }
          }
          itr->Release();
          return res;
      }
  
  
      if (itself)
          res = this;
      else
          res = (Tamgutreemapii*)Atom(true);
  
      long v = b->Integer();
      for (auto& it : res->values)
          it.second += v;
      return res;
  }
  
  Exporting Tamgu* Tamgutreemapii::minus(Tamgu* b, bool itself) {
      Doublelocking _lock(this, b);
  
      Tamgutreemapii * res;
      if (b->isMapContainer()) {
          TamguIteration* itr = b->Newiteration(false);
  
          res = new Tamgutreemapii;
          long k;
          for (itr->Begin(); itr->End() != aTRUE; itr->Next()) {
              k = itr->Keyinteger();
              try {
                  res->values[k] = values.at(k) - itr->Valueinteger();
              }
              catch (const std::out_of_range& oor) {
              }
          }
          itr->Release();
          return res;
      }
  
  
      if (itself)
          res = this;
      else
          res = (Tamgutreemapii*)Atom(true);
  
      long v = b->Integer();
      for (auto& it : res->values)
          it.second -= v;
      return res;
  }
  
  Exporting Tamgu* Tamgutreemapii::multiply(Tamgu* b, bool itself) {
      Doublelocking _lock(this, b);
  
      Tamgutreemapii * res;
      if (b->isMapContainer()) {
          TamguIteration* itr = b->Newiteration(false);
  
          res = new Tamgutreemapii;
          long k;
          for (itr->Begin(); itr->End() != aTRUE; itr->Next()) {
              k = itr->Keyinteger();
              try {
                  res->values[k] = values.at(k) * itr->Valueinteger();
              }
              catch (const std::out_of_range& oor) {
              }
          }
          itr->Release();
          return res;
      }
  
  
      if (itself)
          res = this;
      else
          res = (Tamgutreemapii*)Atom(true);
  
      long v = b->Integer();
      for (auto& it : res->values)
          it.second *= v;
      return res;
  
  }
  
  Exporting Tamgu* Tamgutreemapii::divide(Tamgu* b, bool itself) {
      Doublelocking _lock(this, b);
  
      Tamgutreemapii * res;
      if (b->isMapContainer()) {
          TamguIteration* itr = b->Newiteration(false);
  
          res = new Tamgutreemapii;
          long k;
          for (itr->Begin(); itr->End() != aTRUE; itr->Next()) {
              k = itr->Keyinteger();
              try {
                  res->values[k] = values.at(k) / itr->Valueinteger();
              }
              catch (const std::out_of_range& oor) {
              }
          }
          itr->Release();
          return res;
      }
  
  
      if (itself)
          res = this;
      else
          res = (Tamgutreemapii*)Atom(true);
  
      long v = b->Integer();
      if (v == 0) {
          res->Release();
          return globalTamgu->Returnerror("Error: Divided by 0");
      }
      for (auto& it : res->values)
          it.second /= v;
      return res;
  
  }
  
  Exporting Tamgu* Tamgutreemapii::mod(Tamgu* b, bool itself) {
      Doublelocking _lock(this, b);
  
      Tamgutreemapii * res;
      if (b->isMapContainer()) {
          TamguIteration* itr = b->Newiteration(false);
  
          res = new Tamgutreemapii;
          long k;
          for (itr->Begin(); itr->End() != aTRUE; itr->Next()) {
              k = itr->Keyinteger();
              try {
                  res->values[k] = values.at(k) % itr->Valueinteger();
              }
              catch (const std::out_of_range& oor) {
              }
          }
          itr->Release();
          return res;
      }
  
  
      if (itself)
          res = this;
      else
          res = (Tamgutreemapii*)Atom(true);
  
      long v = b->Integer();
      if (v == 0) {
          res->Release();
          return globalTamgu->Returnerror("Error: Divided by 0");
      }
      for (auto& it : res->values)
          it.second = it.second % v;
      return res;
  }
  
  Exporting Tamgu* Tamgutreemapii::shiftright(Tamgu* b, bool itself) {
      Doublelocking _lock(this, b);
  
      Tamgutreemapii * res;
      if (b->isMapContainer()) {
          TamguIteration* itr = b->Newiteration(false);
  
          res = new Tamgutreemapii;
          long k;
          for (itr->Begin(); itr->End() != aTRUE; itr->Next()) {
              k = itr->Keyinteger();
              try {
                  res->values[k] = values.at(k)>> itr->Valueinteger();
              }
              catch (const std::out_of_range& oor) {
              }
          }
          itr->Release();
          return res;
      }
  
  
      if (itself)
          res = this;
      else
          res = (Tamgutreemapii*)Atom(true);
  
      long v = b->Integer();
      for (auto& it : res->values)
          it.second = it.second >> v;
      return res;
  }
  
  Exporting Tamgu* Tamgutreemapii::shiftleft(Tamgu* b, bool itself) {
      Doublelocking _lock(this, b);
  
      Tamgutreemapii * res;
      if (b->isMapContainer()) {
          TamguIteration* itr = b->Newiteration(false);
  
          res = new Tamgutreemapii;
          long k;
          for (itr->Begin(); itr->End() != aTRUE; itr->Next()) {
              k = itr->Keyinteger();
              try {
                  res->values[k] = values.at(k) << itr->Valueinteger();
              }
              catch (const std::out_of_range& oor) {
              }
          }
          itr->Release();
          return res;
      }
  
  
      if (itself)
          res = this;
      else
          res = (Tamgutreemapii*)Atom(true);
  
      long v = b->Integer();
      for (auto& it : res->values)
          it.second = it.second << v;
      return res;
  }
  
  Exporting Tamgu* Tamgutreemapii::power(Tamgu* b, bool itself) {
      Doublelocking _lock(this, b);
  
      Tamgutreemapii * res;
      if (b->isMapContainer()) {
          TamguIteration* itr = b->Newiteration(false);
  
          res = new Tamgutreemapii;
          long k;
          for (itr->Begin(); itr->End() != aTRUE; itr->Next()) {
              k = itr->Keyinteger();
              try {
                  res->values[k] = pow(values.at(k), itr->Valuefloat());
              }
              catch (const std::out_of_range& oor) {
              }
          }
          itr->Release();
          return res;
      }
  
  
      if (itself)
          res = this;
      else
          res = (Tamgutreemapii*)Atom(true);
  
      double v = b->Float();
      for (auto& it : res->values)
          it.second = pow(it.second, v);
      return res;
  }
  
  Exporting Tamgu* Tamgutreemapii::Loopin(TamguInstruction* ins, Tamgu* context, short idthread) {
      locking();
      Tamgu* var = ins->instructions.vecteur[0]->Instruction(0);
      var = var->Eval(context, aNULL, idthread);
  
      
      map<long, long>::iterator it;
      
      Tamgu* a;
      vector<long> keys;
  
      for (it=values.begin(); it != values.end(); it++)
          keys.push_back(it->first);
  
      long sz = keys.size();
      a = aNULL;
      bool testcond = false;
      for (long i = 0; i < sz && !testcond; i++) {
          a->Releasenonconst();
          var->storevalue(keys[i]);
  
          a = ins->instructions.vecteur[1]->Eval(context, aNULL, idthread);
  
          //Continue does not trigger needInvestigate
          testcond = a->needInvestigate();
      }
  
      unlocking();
      
      if (testcond) {
          if (a == aBREAK)
              return this;
          return a;
      }
  
      a->Releasenonconst();
      return this;
  
  }
  
  