#include "fiko/zeka/graf.hpp" #include #include namespace fiko::zeka { bool degerEsit(const Deger& a, const Deger& b) { if (a.index() != b.index()) { // bool/i64/f64 çapraz karşılaştırma sayısal yapılır (sorgu ergonomisi) f64 x, y; if (degerSayi(a, x) && degerSayi(b, y)) return x == y; return false; } switch (a.index()) { case 0: return true; // monostate case 1: return std::get(a) == std::get(b); case 2: return std::get(a) == std::get(b); case 3: return std::get(a) == std::get(b); case 4: { // Vec3d'nin operator=='u yok — bileşen bazlı const auto& u = std::get(a); const auto& v = std::get(b); return u.x == v.x && u.y == v.y && u.z == v.z; } case 5: return std::get(a) == std::get(b); case 6: return std::get>(a) == std::get>(b); case 7: return std::get>(a) == std::get>(b); } return false; } bool degerSayi(const Deger& d, f64& sonuc) { if (std::holds_alternative(d)) { sonuc = std::get(d) ? 1.0 : 0.0; return true; } if (std::holds_alternative(d)) { sonuc = (f64)std::get(d); return true; } if (std::holds_alternative(d)) { sonuc = std::get(d); return true; } return false; } // ---- Ad havuzu ------------------------------------------------------------- u32 GrafDeposu::ad(std::string_view s) { auto it = adIndeks_.find(std::string(s)); if (it != adIndeks_.end()) return it->second; adlar_.emplace_back(s); u32 id = (u32)adlar_.size(); adIndeks_.emplace(adlar_.back(), id); return id; } u32 GrafDeposu::adBul(std::string_view s) const { auto it = adIndeks_.find(std::string(s)); return it == adIndeks_.end() ? 0 : it->second; } const std::string& GrafDeposu::adMetin(u32 id) const { static const std::string bos; return (id == 0 || id > adlar_.size()) ? bos : adlar_[id - 1]; } // ---- Düğüm ----------------------------------------------------------------- u64 GrafDeposu::dugumEkle(std::string_view tip) { ZDugum d; d.kimlik = dugumler_.size() + 1; d.tip = ad(tip); dugumler_.push_back(std::move(d)); tipIndeks_[dugumler_.back().tip].push_back(dugumler_.back().kimlik); ++canliDugum_; ++surum_; return dugumler_.back().kimlik; } bool GrafDeposu::dugumSil(u64 kimlik) { ZDugum* d = dugum(kimlik); if (!d) return false; // Bağlı ilişkiler düşer (kopya üzerinden — iliskiSil listeleri değiştirir) for (u64 il : dugumIliskileri(kimlik, 0)) iliskiSil(il); if (d->brep.govde) { brepIndeks_.erase(brepAnahtar(d->brep.govde, d->brep.eleman, d->brep.kid)); auto& liste = govdeIndeks_[d->brep.govde]; liste.erase(std::remove(liste.begin(), liste.end(), kimlik), liste.end()); } auto& tl = tipIndeks_[d->tip]; tl.erase(std::remove(tl.begin(), tl.end(), kimlik), tl.end()); d->silik = true; d->oz.clear(); --canliDugum_; ++surum_; return true; } ZDugum* GrafDeposu::dugum(u64 kimlik) { if (kimlik == 0 || kimlik > dugumler_.size()) return nullptr; ZDugum& d = dugumler_[kimlik - 1]; return d.silik ? nullptr : &d; } const ZDugum* GrafDeposu::dugum(u64 kimlik) const { return const_cast(this)->dugum(kimlik); } void GrafDeposu::ozAyarla(u64 dugumKimlik, std::string_view anahtar, Deger v) { ZDugum* d = dugum(dugumKimlik); if (!d) return; d->oz[ad(anahtar)] = std::move(v); ++surum_; } const Deger* GrafDeposu::oz(u64 dugumKimlik, std::string_view anahtar) const { const ZDugum* d = dugum(dugumKimlik); if (!d) return nullptr; u32 k = adBul(anahtar); if (!k) return nullptr; auto it = d->oz.find(k); return it == d->oz.end() ? nullptr : &it->second; } f64 GrafDeposu::ozSayi(u64 dugumKimlik, std::string_view anahtar, f64 varsayilan) const { const Deger* v = oz(dugumKimlik, anahtar); f64 s; return (v && degerSayi(*v, s)) ? s : varsayilan; } // ---- İlişki ---------------------------------------------------------------- u64 GrafDeposu::iliskiEkle(std::string_view tip, u64 kaynak, u64 hedef) { if (!dugum(kaynak) || !dugum(hedef)) return 0; ZIliski il; il.kimlik = iliskiler_.size() + 1; il.tip = ad(tip); il.kaynak = kaynak; il.hedef = hedef; iliskiler_.push_back(std::move(il)); cikan_[kaynak].push_back(iliskiler_.back().kimlik); giren_[hedef].push_back(iliskiler_.back().kimlik); ++canliIliski_; ++surum_; return iliskiler_.back().kimlik; } bool GrafDeposu::iliskiSil(u64 kimlik) { ZIliski* il = iliski(kimlik); if (!il) return false; auto& c = cikan_[il->kaynak]; c.erase(std::remove(c.begin(), c.end(), kimlik), c.end()); auto& g = giren_[il->hedef]; g.erase(std::remove(g.begin(), g.end(), kimlik), g.end()); il->silik = true; il->oz.clear(); --canliIliski_; ++surum_; return true; } ZIliski* GrafDeposu::iliski(u64 kimlik) { if (kimlik == 0 || kimlik > iliskiler_.size()) return nullptr; ZIliski& il = iliskiler_[kimlik - 1]; return il.silik ? nullptr : &il; } const ZIliski* GrafDeposu::iliski(u64 kimlik) const { return const_cast(this)->iliski(kimlik); } void GrafDeposu::iliskiOzAyarla(u64 iliskiKimlik, std::string_view anahtar, Deger v) { ZIliski* il = iliski(iliskiKimlik); if (!il) return; il->oz[ad(anahtar)] = std::move(v); ++surum_; } // ---- Gezinti --------------------------------------------------------------- std::vector GrafDeposu::tipteki(std::string_view tip) const { u32 t = adBul(tip); if (!t) return {}; auto it = tipIndeks_.find(t); return it == tipIndeks_.end() ? std::vector{} : it->second; } std::vector GrafDeposu::dugumIliskileri(u64 dugumKimlik, int yon) const { std::vector sonuc; if (yon >= 0) { auto it = cikan_.find(dugumKimlik); if (it != cikan_.end()) sonuc.insert(sonuc.end(), it->second.begin(), it->second.end()); } if (yon <= 0) { auto it = giren_.find(dugumKimlik); if (it != giren_.end()) sonuc.insert(sonuc.end(), it->second.begin(), it->second.end()); } return sonuc; } std::vector GrafDeposu::komsular(u64 dugumKimlik, std::string_view iliskiTip, int yon) const { u32 t = iliskiTip.empty() ? 0 : adBul(iliskiTip); if (!iliskiTip.empty() && !t) return {}; std::vector sonuc; for (u64 ik : dugumIliskileri(dugumKimlik, yon)) { const ZIliski* il = iliski(ik); if (!il || (t && il->tip != t)) continue; sonuc.push_back(il->kaynak == dugumKimlik ? il->hedef : il->kaynak); } return sonuc; } // ---- BREP indeksi ---------------------------------------------------------- u64 GrafDeposu::brepAnahtar(u64 govde, u8 eleman, u64 kid) { // FNV benzeri karışım — üç alan tek u64 anahtara (çakışma pratikte imkânsız // değil ama brepIndeks_ değeri düğüm kimliği; doğrulama dugum() üzerinden). u64 h = 1469598103934665603ull; auto kar = [&h](u64 v) { h ^= v; h *= 1099511628211ull; }; kar(govde); kar(eleman); kar(kid); return h; } void GrafDeposu::brepBagla(u64 dugumKimlik, BrepRef ref) { ZDugum* d = dugum(dugumKimlik); if (!d) return; if (d->brep.govde) { // eski bağ çözülür brepIndeks_.erase(brepAnahtar(d->brep.govde, d->brep.eleman, d->brep.kid)); auto& liste = govdeIndeks_[d->brep.govde]; liste.erase(std::remove(liste.begin(), liste.end(), dugumKimlik), liste.end()); } d->brep = ref; if (ref.govde) { brepIndeks_[brepAnahtar(ref.govde, ref.eleman, ref.kid)] = dugumKimlik; govdeIndeks_[ref.govde].push_back(dugumKimlik); } ++surum_; } u64 GrafDeposu::brepDugum(u64 govde, u8 eleman, u64 kid) const { auto it = brepIndeks_.find(brepAnahtar(govde, eleman, kid)); if (it == brepIndeks_.end()) return 0; return dugum(it->second) ? it->second : 0; } std::vector GrafDeposu::govdeDugumleri(u64 govde) const { auto it = govdeIndeks_.find(govde); return it == govdeIndeks_.end() ? std::vector{} : it->second; } // ---- JSON ------------------------------------------------------------------ namespace { void jsonKac(std::string& c, const std::string& s) { c += '"'; for (char ch : s) { if (ch == '"' || ch == '\\') { c += '\\'; c += ch; } else if (ch == '\n') c += "\\n"; else c += ch; } c += '"'; } void jsonDeger(std::string& c, const Deger& d) { if (std::holds_alternative(d)) c += "null"; else if (std::holds_alternative(d)) c += std::get(d) ? "true" : "false"; else if (std::holds_alternative(d)) c += std::to_string(std::get(d)); else if (std::holds_alternative(d)) c += std::to_string(std::get(d)); else if (std::holds_alternative(d)) { const auto& v = std::get(d); c += '['; c += std::to_string(v.x); c += ','; c += std::to_string(v.y); c += ','; c += std::to_string(v.z); c += ']'; } else if (std::holds_alternative(d)) { jsonKac(c, std::get(d)); } else if (std::holds_alternative>(d)) { c += '['; const auto& v = std::get>(d); for (size_t i = 0; i < v.size(); ++i) { if (i) c += ','; c += std::to_string(v[i]); } c += ']'; } else if (std::holds_alternative>(d)) { c += '['; const auto& v = std::get>(d); for (size_t i = 0; i < v.size(); ++i) { if (i) c += ','; c += std::to_string(v[i]); } c += ']'; } } } // namespace std::string GrafDeposu::dugumJson(u64 kimlik) const { const ZDugum* d = dugum(kimlik); if (!d) return {}; std::string c = "{\"kimlik\":" + std::to_string(d->kimlik); c += ",\"tip\":"; jsonKac(c, adMetin(d->tip)); if (d->brep.govde) { c += ",\"brep\":{\"govde\":" + std::to_string(d->brep.govde) + ",\"eleman\":" + std::to_string(d->brep.eleman) + ",\"kid\":" + std::to_string(d->brep.kid) + ",\"ordinal\":" + std::to_string(d->brep.ordinal) + "}"; } c += ",\"oz\":{"; bool ilk = true; for (const auto& [k, v] : d->oz) { if (!ilk) c += ','; ilk = false; jsonKac(c, adMetin(k)); c += ':'; jsonDeger(c, v); } c += "},\"iliskiler\":["; ilk = true; for (u64 ik : dugumIliskileri(kimlik, 0)) { const ZIliski* il = iliski(ik); if (!il) continue; if (!ilk) c += ','; ilk = false; c += "{\"tip\":"; jsonKac(c, adMetin(il->tip)); c += ",\"yon\":\""; c += (il->kaynak == kimlik ? "cikan" : "giren"); c += "\",\"dugum\":" + std::to_string(il->kaynak == kimlik ? il->hedef : il->kaynak) + "}"; } c += "]}"; return c; } } // namespace fiko::zeka