This commit is contained in:
2025-03-12 12:37:19 +03:00
parent 1c851baa7e
commit 6a4040be3e
426 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,135 @@
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cstdint>
#include <string>
#include <fstream>
#include <iostream>
#include <algorithm>
#include <vector>
#include <map>
#include <set>
#include <utility>
#include <assert.h>
#include "../GraphCall/graph_calls_func.h"
#include "graphLayout/nodesoup.hpp"
#include "BuildGraph.h"
using std::map;
using std::vector;
using std::string;
using std::pair;
using std::set;
using namespace nodesoup;
static int getNextRandNum(int &N, const int SIZE, int curr)
{
int ret = N % SIZE;
++N;
while (ret == curr)
{
if (SIZE == 1)
break;
ret = N % SIZE;
++N;
}
return ret;
}
map<string, pair<int, int>>
buildLocationOfGraph(const map<string, vector<FuncInfo*>>& allFuncInfo, const int iters, const int coef,
const unsigned int width, const unsigned int height, const set<string>* visible)
{
if (visible && visible->size() == 0)
return map<string, pair<int, int>>();
map<string, FuncInfo*> mapOfFuncs;
createMapOfFunc(allFuncInfo, mapOfFuncs);
map<string, int> num;
int z = 0;
for (auto& elem : mapOfFuncs)
{
if (visible && visible->find(elem.first) == visible->end())
continue;
num[elem.first] = z++;
}
for (auto& elem : mapOfFuncs)
{
set<string> callsFrom;
for (auto& callFromInfo : elem.second->callsFromDetailed)
{
auto& callFrom_ = callFromInfo.detailCallsFrom;
callsFrom.insert(callFrom_.first);
}
callsFrom.insert(elem.second->externalCalls.begin(), elem.second->externalCalls.end());
for (auto& call : callsFrom)
{
if (visible && visible->find(call) == visible->end())
continue;
if (num.find(call) == num.end())
num[call] = z++;
}
}
adj_list_t g;
g.resize(z);
vector<Point2D> positions;
vector<double> radiuses = size_radiuses(g);
set<pair<string, string>> edges;
for (auto& elem : mapOfFuncs)
{
if (visible && visible->find(elem.first) == visible->end())
continue;
set<string> callsFrom;
for (auto& callFromInfo : elem.second->callsFromDetailed)
{
auto& callFrom_ = callFromInfo.detailCallsFrom;
callsFrom.insert(callFrom_.first);
}
callsFrom.insert(elem.second->externalCalls.begin(), elem.second->externalCalls.end());
for (auto& call : callsFrom)
{
if (visible && visible->find(call) == visible->end())
continue;
const pair<string, string> key = make_pair(elem.first, call);
if (edges.find(key) == edges.end())
{
edges.insert(key);
g[num[elem.first]].push_back(num[call]);
g[num[call]].push_back(num[elem.first]);
}
}
}
if (z == 1)
{
map<string, pair<int, int>> result;
if (num.size() != 1)
printInternalError(convertFileName(__FILE__).c_str(), __LINE__);
for (auto& elem : num)
result[elem.first] = std::make_pair(width / 2, height / 2);
return result;
}
positions = fruchterman_reingold(g, width, height, iters, coef);
map<string, pair<int, int>> result;
for (auto& elem : num)
result[elem.first] = positions[num[elem.first]].toPair();
return result;
}

View File

@@ -0,0 +1,3 @@
#pragma once
std::map<std::string, std::pair<int, int>> buildLocationOfGraph(const std::map<std::string, std::vector<FuncInfo*>>& allFuncInfo, const int iters = 1000, const int coef = 100, const unsigned int width = 1920, const unsigned int height = 1080, const std::set<std::string>* visible = NULL);

View File

@@ -0,0 +1,531 @@
#include <string>
#include <locale>
#include <vector>
#include <iostream>
#include <ctime>
#include <chrono>
#include "SendMessage.h"
#include "../Utils/utils.h"
#include "get_information.h"
#ifdef _WIN32
#include <Windows.h>
#include <winsock.h>
#define WM_USER 0x0400 //<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
#define STATUS_MESSAGE WM_USER + 16
// link with Ws2_32.lib
#pragma comment(lib,"Ws2_32.lib")
#else
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define SOCKET int
#define INVALID_SOCKET (SOCKET)(~0)
#define SOCKADDR sockaddr
#define closesocket close
void WSACleanup() { }
int WSAGetLastError() { return 0; }
void Sleep(int millisec) { usleep(millisec * 1000); }
#endif
using std::wstring;
using std::string;
using std::vector;
using std::chrono::high_resolution_clock;
using std::chrono::duration_cast;
using std::chrono::milliseconds;
static SOCKET clientSocket = INVALID_SOCKET;
static FILE* logFile = NULL;
#if _WIN32
#define FILE_LOG "Components\\Sapfor_log.txt"
#else
#define FILE_LOG "Components/Sapfor_log.txt"
#endif
#define __print(prefix, format, ...) do { } while (0)
#define __print_log(file, format, ...) do { } while (0)
/*#define __print(prefix, format, ...) do {\
printf((string("%s: ") + format + string("\n")).c_str(), prefix, ##__VA_ARGS__); \
fflush(NULL); \
} while (0)
#define __print_log(file, format, ...) do {\
if (file) { \
time_t now = time(0);\
auto dt = string(ctime(&now)); \
dt = dt.erase(dt.size() - 1); \
fprintf(file, (string("%s: ") + format + string("\n")).c_str(), dt.c_str(), ##__VA_ARGS__); \
fflush(file); \
} \
} while (0)
*/
#define CLIENT "[SAPFOR]"
static int doRecv(SOCKET& soc, string& command)
{
int count = 0; //<2F><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
int err = 0; //<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>.
const int maxSize = 4096; //<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>.
char* buf = NULL; //<2F><><EFBFBD><EFBFBD><EFBFBD>
do
{
buf = new char[maxSize + 1];
err = recv(soc, buf, maxSize, 0);
if (err > 0)
{
count += err;
buf[err] = '\0';
command += buf;
}
delete[] buf;
buf = NULL;
} while (err > 0 && command.back() != '\n');
__print(CLIENT, "'%s' length='%d'\n", command.c_str(), command.length());
return err < 0 ? err : count;
}
static int decodeMessage(const string& message, vector<string>& pars, int &winH, int countPars)
{
int sI = 0;
for (int z = 0; z < countPars; ++z)
{
string d = "";
for ( ;sI != message.size() && message[sI] != ' '; ++sI)
d += message[sI];
if (sI == message.size())
return -1;
++sI;
int countElems = -1;
if (sscanf(d.c_str(), "%d", &countElems) != 1)
return -2;
string par = "";
for (int z = 0; sI != message.size() && z < countElems; ++z, ++sI)
par += message[sI];
if (sI == message.size())
return -3;
pars.push_back(par);
}
string d = "", toPrint = "";
for (; sI != message.size() && message[sI] != ' '; ++sI)
d += message[sI];
if (d[d.size() - 1] == '\n' && d.size() >= 2)
toPrint = d.substr(0, d.size() - 2);
else
toPrint = d;
__print(CLIENT, "Read port from '%s'", toPrint.c_str());
if (sscanf(d.c_str(), "%d", &winH) != 1)
{
__print(CLIENT, "PORT ERROR");
return -4;
}
else
__print(CLIENT, "PORT = %d", winH);
return 0;
}
void sendMessage_1lvl(const wstring& toSend) { MessageManager::sendFirstLvl (toSend); }
void sendMessage_2lvl(const wstring& toSend) { MessageManager::sendSecondLvl(toSend); }
void sendMessage_progress(const wstring& toSend) { MessageManager::sendProgress (toSend); }
static string utf8_encode(const wstring& wstr)
{
if (wstr.empty())
return string();
#ifdef _WIN32
int size_needed = WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), NULL, 0, NULL, NULL);
string strTo(size_needed, 0);
WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), &strTo[0], size_needed, NULL, NULL);
#else
/*char* buf = NULL;
int size_needed = wcstombs(buf, wstr.c_str(), sizeof(char));
printf("%d for \n", size_needed);
buf = new char[size_needed + 1];
wcstombs(buf, wstr.c_str(), sizeof(char));
string strTo(buf);
delete[] buf;*/
string strTo(wstr.size(), 0);
for (int z = 0; z < wstr.size(); ++z)
strTo[z] = wstr[z];
#endif
return strTo;
}
/*static wstring utf8_decode(const string& str)
{
if (str.empty())
return wstring();
int size_needed = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), str.length(), NULL, 0);
wstring wstr;
wstr.resize(size_needed, 0);
MultiByteToWideChar(CP_UTF8, 0, str.c_str(), str.length(), &wstr[0], size_needed);
return wstr;
}*/
void MessageManager::sendMessage(const wstring& toSend)
{
if (WinHandler > 0) //to C#
{
#ifdef _WIN32
cachedMessages.push_back(toSend);
PostMessage((HWND)WinHandler, STATUS_MESSAGE, (WPARAM)(cachedMessages.back().c_str()), (LPARAM)cachedMessages.back().size());
#endif
}
else if (WinHandler < -1) // to JAVA
{
vector<wstring> splited;
splitString(toSend, '\n', splited);
int i = 1;
for (auto& elem : splited)
{
string result = utf8_encode(L"message_" + std::to_wstring(i++) + L":" + elem + L"\n");
const char* ret = result.c_str();
int size = result.size();
#ifdef _WIN32
int iResult = send(clientSocket, ret, size, 0);
#else
int iResult = write(clientSocket, ret, size);
#endif
if (iResult < 0)
printf("Wrong send is %d\n", iResult);
}
}
}
static int connectAndCreate(SOCKET& socket_t, string address, int port)
{
socket_t = socket(AF_INET, SOCK_STREAM, 0);
if (socket_t == INVALID_SOCKET)
{
printf("Wrong socket\n");
return -1;
}
sockaddr_in clientService;
clientService.sin_family = AF_INET;
clientService.sin_addr.s_addr = inet_addr(address.c_str());
clientService.sin_port = htons(port);
int result = connect(socket_t, (SOCKADDR*)(&clientService), sizeof(clientService));
if (result != 0)
{
__print(CLIENT, "Wrong connect to socket %d\n", WSAGetLastError());
return -1;
}
return 0;
}
void MessageManager::setWinHandler(const int winH)
{
if (winH < -1) // to JAVA
{
if (MessageManager::init() != 0)
{
WinHandler = -1;
return;
}
__print(CLIENT, "set handler with PORT = %d", winH);
if (connectAndCreate(clientSocket, "127.0.0.1", winH == -2 ? 8888 : -winH) != 0)
{
WinHandler = -1;
return;
}
}
else if (winH == -1 && WinHandler < -1) // close JAVA
{
if (Started == 0)
{
Sleep(100);
closesocket(clientSocket);
clientSocket == INVALID_SOCKET;
}
}
WinHandler = winH;
}
//from get_information.cpp
extern bool showDebug;
static int send(SOCKET& client, const wstring& messageIn)
{
wstring message = messageIn;
for (int z = 0; z < message.size(); ++z)
if (message[z] == L'\n')
message[z] = 1;
message += L"\n";
string result = utf8_encode(message);
string size = std::to_string(result.size());
if (showDebug)
{
wprintf(L"%s: Original message '%s'", CLIENT, message.c_str());
printf("%s: Converted message '%s'", CLIENT, result.c_str());
}
#ifdef _WIN32
int err = send(client, size.c_str(), size.size(), 0);
#else
size += "\n";
int err = write(client, size.c_str(), size.size());
#endif
__print(CLIENT, "Send message size: %d", (int)result.size());
if (err != size.size())
{
__print(CLIENT, "Error of send(): %d", err); // exit
return -1;
}
char buf;
recv(client, &buf, 1, 0);
__print(CLIENT, "send start\n", CLIENT);
auto timeForPass = high_resolution_clock::now();
#ifdef _WIN32
err = send(client, result.c_str(), result.size(), 0);
#else
err = write(client, result.c_str(), result.size());
#endif
const float elapsed = duration_cast<milliseconds>(high_resolution_clock::now() - timeForPass).count() / 1000.;
__print(CLIENT, "send end with time %f sec\n", elapsed);
__print(CLIENT, "Send message with size %d", (int)result.size());
if (err != result.size())
{
__print(CLIENT, "Error of send(): %d", err); // exit
return -1;
}
else
{
err = recv(client, &buf, 1, 0);
if (err != 1)
{
__print(CLIENT, "Error of recv(): %d", err); // exit
return -1;
}
else
return 0;
}
}
void MessageManager::sendProgress(const std::wstring& str)
{
if (WinHandler < -1) // to JAVA
{
string result = utf8_encode(L"progress:" + str + L"\n");
const char* ret = result.c_str();
int size = result.size();
#ifdef _WIN32
int iResult = send(clientSocket, ret, size, 0);
#else
int iResult = write(clientSocket, ret, size);
#endif
if (iResult == -1)
__print(CLIENT, "Error of send(): %d", iResult);
}
}
int MessageManager::init()
{
#if _WIN32
if (Started == -1)
{
WSADATA wsaData = { 0 };
int result = WSAStartup(MAKEWORD(2, 0), &wsaData);
if (result != 0)
{
__print(CLIENT, "Wrong WSAStartup(): %d", result);
Started = -2;
}
else
Started = 0;
}
#else
Started = 0;
#endif
return Started;
}
SOCKET client = INVALID_SOCKET;
void sendErrorCode(const wstring& message)
{
send(client, message);
closesocket(client);
client = INVALID_SOCKET;
}
void RunSapforAsClient(int serverPort)
{
if (MessageManager::init() != 0)
return;
logFile = fopen(FILE_LOG, "w");
if (logFile == NULL)
__print(CLIENT, "Can not open log file '%s'", FILE_LOG);
client = INVALID_SOCKET;
__print_log(logFile, "create and connect to server socket with port %d", serverPort > 0 ? serverPort : 8889);
if (connectAndCreate(client, "127.0.0.1", serverPort > 0 ? serverPort : 8889) != 0)
return;
__print_log(logFile, "done");
__print_log(logFile, "start main communications");
while (true)
{
wstring result = L"";
string message = "";
__print(CLIENT, "start recv message");
__print_log(logFile, "wait for command from server");
const int count = doRecv(client, message);
__print(CLIENT, "end recv message with size %d", count);
__print_log(logFile, "done with message size %d", count);
if (count > 0)
{
string code;
/*string copy = message;
if (copy.back() == '\n')
copy = copy.erase(copy.size() - 1);
__print(CLIENT, "Recv message: '%s'", copy.c_str());*/
int z = 0;
for (; z < message.size(); ++z)
{
if (message[z] == ':')
{
z++;
break;
}
else
code += message[z];
}
if (z == message.size()) // wrong message
{
__print(CLIENT, "Wrong message");
__print_log(logFile, "send WRONG message to server ");
int err = send(client, L"WRONG");
__print_log(logFile, "done with code %d", err);
if (err != 0)
break;
continue;
}
message = message.substr(z);
vector<string> pars;
int winHandler = -1;
if (code == "analysis")
{
__print_log(logFile, "decode message as analysis");
int err = decodeMessage(message, pars, winHandler, 3);
if (err == 0)
result = Sapfor_RunAnalysis(pars[0].c_str(), pars[1].c_str(), pars[2].c_str(), winHandler);
else
result = Sapfor_RunAnalysis(NULL, NULL, NULL, -1);
}
else if (code == "transformation")
{
__print_log(logFile, "decode message as transformation");
int err = decodeMessage(message, pars, winHandler, 5);
if (err == 0)
result = Sapfor_RunTransformation(pars[0].c_str(), pars[1].c_str(), pars[2].c_str(), pars[3].c_str(), pars[4].c_str(), winHandler);
else
result = Sapfor_RunTransformation(NULL, NULL, NULL, NULL, NULL, -1);
}
else if (code == "modification")
{
__print_log(logFile, "decode message as modification");
int err = decodeMessage(message, pars, winHandler, 6);
if (err == 0)
result = Sapfor_RunModification(pars[0].c_str(), pars[1].c_str(), pars[2].c_str(), pars[3].c_str(), pars[4].c_str(), pars[5].c_str(), winHandler);
else
result = Sapfor_RunModification(NULL, NULL, NULL, NULL, NULL, NULL, -1);
}
else
{
__print(CLIENT, "Wrong code of message: %s", code.c_str()); // exit
__print_log(logFile, "wrong code of message, send WRONG");
int err = send(client, L"WRONG");
__print_log(logFile, "done with code %d", err);
if (err != 0)
break;
continue;
}
__print_log(logFile, "send results to server");
int err = send(client, result.c_str());
__print_log(logFile, "done with code %d", err);
if (err != 0)
break;
}
else if (count == 0) // close and exit
{
closesocket(client);
__print(CLIENT, "Socket was closed");
__print_log(logFile, "done with normal exit");
break;
}
else // error
{
closesocket(client);
__print(CLIENT, "Recv return error code %d", count);
__print_log(logFile, "done with error exit");
break;
}
}
}
unsigned int GetPid()
{
unsigned int pid = 0;
#ifdef _WIN32
pid = GetCurrentProcessId();
#else
pid = getpid();
#endif
return pid;
}
vector<wstring> MessageManager::cachedMessages = vector<wstring>();
int MessageManager::WinHandler = -1;
wstring MessageManager::firstLvlMessage = L"";
wstring MessageManager::secondLvlMessage = L"";
int MessageManager::Started = -1;

View File

@@ -0,0 +1,38 @@
#pragma once
#include <string>
#include <vector>
class MessageManager
{
private:
static std::vector<std::wstring> cachedMessages;
static int WinHandler;
static int Started;
static std::wstring firstLvlMessage;
static std::wstring secondLvlMessage;
static void sendMessage(const std::wstring &toSend);
public:
static void clearCache() { cachedMessages.clear(); }
static void setWinHandler(const int winH);
static void sendFirstLvl(const std::wstring &str)
{
firstLvlMessage = str;
sendMessage(firstLvlMessage);
}
static void sendSecondLvl(const std::wstring &str)
{
secondLvlMessage = str;
sendMessage(firstLvlMessage + L"\n" + secondLvlMessage);
}
static void sendProgress(const std::wstring& str);
static int init();
};
void sendMessage_1lvl(const std::wstring& toSend);
void sendMessage_2lvl(const std::wstring& toSend);
void sendMessage_progress(const std::wstring& toSend);
unsigned int GetPid();

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,67 @@
#pragma once
#if _WIN32
// ANALISYS
extern "C" { __declspec(dllexport) int SPF_GetGraphLoops(void*& context, int winHandler, short *options, short *projName, short *&result, short *&output, int *&outputSize, short *&outputMessage, int *&outputMessageSize); }
extern "C" { __declspec(dllexport) int SPF_GetGraphFunctions(void*& context, int winHandler, short *options, short *projName, short *&result, short *&output, int *&outputSize, short *&outputMessage, int *&outputMessageSize); }
extern "C" { __declspec(dllexport) int SPF_GetGraphVizOfFunctions(void*& context, short *options, short *projName, short *&result, short *&output, int *&outputSize, short *&outputMessage, int *&outputMessageSize); }
extern "C" { __declspec(dllexport) int SPF_GetArrayDistribution(void*& context, int winHandler, short *options, short *projName, short *&result, short *&output, int *&outputSize, short *&outputMessage, int *&outputMessageSize, int onlyAnalysis); }
extern "C" { __declspec(dllexport) int SPF_SetFunctionsToInclude(void*& context, int winHandler, short *options, short *projName, short *&result, short *&output, int *&outputSize, short *&outputMessage, int *&outputMessageSize); }
extern "C" { __declspec(dllexport) int SPF_GetAllDeclaratedArrays(void*& context, int winHandler, short *options, short *projName, short *&result, short *&output, int *&outputSize, short *&outputMessage, int *&outputMessageSize); }
extern "C" { __declspec(dllexport) int SPF_GetFileLineInfo(void*& context, int winHandler, short *options, short *projName, short *&result, short *&output, int *&outputSize, short *&outputMessage, int *&outputMessageSize); }
extern "C" { __declspec(dllexport) int SPF_GetIncludeDependencies(void*& context, int winHandler, short *options, short *projName, short *&resultt, short*& output, int*& outputSize, short*& outputMessage, int*& outputMessageSize); }
extern "C" { __declspec(dllexport) int SPF_GetGCovInfo(void*& context, int winHandler, short *options, short *projName, short *&result, short *&output, int *&outputSize, short *&outputMessage, int *&outputMessageSize); }
extern "C" { __declspec(dllexport) int SPF_ParseFiles(void*& context, int winHandler, short *options, short* projName, short*& output, int*& outputSize, short*& outputMessage, int*& outputMessageSize); }
extern "C" { __declspec(dllexport) int SPF_StatisticAnalyzer(void*& context, int winHandler, short* options, short* pppaOptions, short*& output, int*& outputSize, short*& outputMessage, int*& outputMessageSize); }
extern "C" { __declspec(dllexport) int SPF_GetPassesState(void*& context, int *&passInfo); } // deprecated
extern "C" { __declspec(dllexport) int SPF_GetPassesStateStr(void*& context, short *&passInfo); }
extern "C" { __declspec(dllexport) int SPF_GetVersionAndBuildDate(void*& context, short *&result); }
extern "C" { __declspec(dllexport) int SPF_GetIntrinsics(void*& context, short *&result); }
extern "C" { __declspec(dllexport) void SPF_deleteAllAllocatedData(void*& context); }
// CODE TRANSFORMATION
extern "C" { __declspec(dllexport) int SPF_CorrectCodeStylePass (void*& context, int winHandler, short *options, short *projName, short *folderName, short *&output, int *&outputSize, short *&outputMessage, int *&outputMessageSize); }
extern "C" { __declspec(dllexport) int SPF_RemoveDvmDirectives (void*& context, int winHandler, short *options, short *projName, short *folderName, short *&output, int *&outputSize, short *&outputMessage, int *&outputMessageSize); }
extern "C" { __declspec(dllexport) int SPF_RemoveDvmDirectivesToComments (void*& context, int winHandler, short *options, short *projName, short *folderName, short *&output, int *&outputSize, short *&outputMessage, int *&outputMessageSize); }
extern "C" { __declspec(dllexport) int SPF_InsertIncludesPass (void*& context, int winHandler, short *options, short *projName, short *folderName, char *filesToInclude, short *&output, int *&outputSize, short *&outputMessage, int *&outputMessageSize); }
extern "C" { __declspec(dllexport) int SPF_ResolveParallelRegionConflicts(void*& context, int winHandler, short *options, short *projName, short *folderName, short *&output, int *&outputSize, short *&outputMessage, int *&outputMessageSize); }
extern "C" { __declspec(dllexport) int SPF_LoopEndDoConverterPass (void*& context, int winHandler, short *options, short *projName, short *folderName, short *&output, int *&outputSize, short *&outputMessage, int *&outputMessageSize); }
extern "C" { __declspec(dllexport) int SPF_CreateParallelVariant (void*& context, int winHandler, short *options, short *projName, short *folderName, int64_t *variants, int *varLen, short *&output, int *&outputSize, short *&outputMessage, int *&outputMessageSize, short *&predictorStats); }
extern "C" { __declspec(dllexport) int SPF_LoopFission (void*& context, int winHandler, short *options, short *projName, short *folderName, short *&output, int *&outputSize, short *&outputMessage, int *&outputMessageSize); }
extern "C" { __declspec(dllexport) int SPF_LoopUnion (void*& context, int winHandler, short *options, short *projName, short *folderName, short *&output, int *&outputSize, short *&outputMessage, int *&outputMessageSize); }
extern "C" { __declspec(dllexport) int SPF_PrivateExpansion (void*& context, int winHandler, short *options, short *projName, short *folderName, short *&output, int *&outputSize, short *&outputMessage, int *&outputMessageSize); }
extern "C" { __declspec(dllexport) int SPF_PrivateShrinking (void*& context, int winHandler, short *options, short *projName, short *folderName, short *&output, int *&outputSize, short *&outputMessage, int *&outputMessageSize); }
extern "C" { __declspec(dllexport) int SPF_PrivateRemoving (void*& context, int winHandler, short *options, short *projName, short *folderName, short *&output, int *&outputSize, short *&outputMessage, int *&outputMessageSize); }
extern "C" { __declspec(dllexport) int SPF_CreateIntervalsTree (void*& context, int winHandler, short *options, short *projName, short *folderName, short *&output, int *&outputSize, short *&outputMessage, int *&outputMessageSize); }
extern "C" { __declspec(dllexport) int SPF_RemoveDvmIntervals (void*& context, int winHandler, short *options, short *projName, short *folderName, short *&output, int *&outputSize, short *&outputMessage, int *&outputMessageSize); }
extern "C" { __declspec(dllexport) int SPF_DuplicateFunctionChains (void*& context, int winHandler, short *options, short *projName, short *folderName, short *&output, int *&outputSize, short *&outputMessage, int *&outputMessageSize); }
extern "C" { __declspec(dllexport) int SPF_InlineProcedures (void*& context, int winHandler, short *options, short *projName, short *folderName, short *names, short *&output, int*& outputSize, short*& outputMessage, int*& outputMessageSize, int type); }
int SPF_CreateCheckpoints (void*& context, int winHandler, short *options, short *projName, short *folderName, short *&output, int*& outputSize, short*& outputMessage, int*& outputMessageSize);
int SPF_InitDeclsWithZero (void*& context, int winHandler, short *options, short *projName, short *folderName, short *&output, int*& outputSize, short*& outputMessage, int*& outputMessageSize);
//CODE MODIFICATION
extern "C" { __declspec(dllexport) int SPF_ModifyArrayDistribution (void*& context, int winHandler, short *options, short* projName, short*& output, int*& outputSize, short*& outputMessage, int*& outputMessageSize, int regId, int64_t* toModify); }
extern "C" { __declspec(dllexport) int SPF_InlineProcedure (void*& context, int winHandler, short* options, short* projName, short* folderName, short* name, short* file, int line, short*& output, int*& outputSize, short*& outputMessage, int*& outputMessageSize, int& size, int*& sizes, short*& newFilesNames, short*& newFiles); }
extern "C" { __declspec(dllexport) int SPF_LoopUnionCurrent (void*& context, int winHandler, short* options, short* projName, short* folderName, short* file, int line, short*& output, int*& outputSize, short*& outputMessage, int*& outputMessageSize, int& size, int*& sizes, short*& newFilesNames, short*& newFiles); }
extern "C" { __declspec(dllexport) int SPF_ChangeSpfIntervals (void*& context, int winHandler, short* options, short* projName, short* folderName, short*& output, int*& outputSize, short*& outputMessage, int*& outputMessageSize, short* fileNameToMod, int* toModifyLines, int& size, int*& sizes, short*& newFilesNames, short*& newFiles); }
extern "C" { __declspec(dllexport) int SPF_SetDistributionFlagToArray (void*& context, char* key, int flag); }
extern "C" { __declspec(dllexport) int SPF_SetDistributionFlagToArrays(void*& context, const char* keys, const char* flags); }
#endif
void createNeededException();
void RunSapforAsClient(int);
const std::wstring Sapfor_RunAnalysis(const char* name, const char* options_c, const char* projName_c, int winHandler);
const std::wstring Sapfor_RunTransformation(const char* name, const char* options_c, const char* projName_c, const char* folder_c, const char* addOpt_c, int winHandler);
const std::wstring Sapfor_RunModification(const char* name, const char* options_c, const char* projName_c, const char* folder_c, const char* addOpt1_c, const char* addOpt2_c, int winHandler);
void sendErrorCode(const std::wstring& message);
#ifdef JAVA
#include <jni.h>
extern "C"
{
JNIEXPORT jcharArray JNICALL Java_components_Sapfor_SPF_1RunAnalysis (JNIEnv*, jobject, jstring, jint, jstring, jstring);
JNIEXPORT jcharArray JNICALL Java_components_Sapfor_SPF_1RunTransformation(JNIEnv*, jobject, jstring, jint, jstring, jstring, jstring, jstring);
JNIEXPORT jcharArray JNICALL Java_components_Sapfor_SPF_1RunModification (JNIEnv*, jobject, jstring, jint, jstring, jstring, jstring, jstring, jstring);
}
#endif

View File

@@ -0,0 +1,89 @@
#include <cmath>
#include "algebra.hpp"
namespace nodesoup {
Point2D::operator Vector2D() const {
return { x, y };
}
Point2D& Point2D::operator+=(const Vector2D& vector) {
x += vector.dx;
y += vector.dy;
return *this;
}
Point2D& Point2D::operator-=(const Vector2D& vector) {
x -= vector.dx;
y -= vector.dy;
return *this;
}
Vector2D::operator Point2D() const {
return { dx, dy };
}
Vector2D& Vector2D::operator+=(const Vector2D& other) {
dx += other.dx;
dy += other.dy;
return *this;
}
Vector2D& Vector2D::operator-=(const Vector2D& other) {
dx -= other.dx;
dy -= other.dy;
return *this;
}
Vector2D& Vector2D::operator*=(double scalar) {
dx *= scalar;
dy *= scalar;
return *this;
}
Vector2D& Vector2D::operator*(const Vector2D& other)
{
dx *= other.dx;
dy *= other.dy;
return *this;
}
Vector2D& Vector2D::operator/=(double scalar) {
dx /= scalar;
dy /= scalar;
return *this;
}
Point2D operator+(const Point2D& point, const Vector2D& vector) {
return { point.x + vector.dx, point.y + vector.dy };
}
Point2D operator-(const Point2D& point, const Vector2D& vector) {
return { point.x - vector.dx, point.y - vector.dy };
}
Vector2D operator-(const Point2D& lhs, const Point2D& rhs) {
return { lhs.x - rhs.x, lhs.y - rhs.y };
}
Vector2D operator+(const Vector2D& lhs, const Vector2D& rhs) {
return { lhs.dx + rhs.dx, lhs.dy + rhs.dy };
}
Vector2D operator-(const Vector2D& lhs, const Vector2D& rhs) {
return { lhs.dx - rhs.dx, lhs.dy - rhs.dy };
}
Vector2D operator*(const Vector2D& vector, double scalar) {
return { vector.dx * scalar, vector.dy * scalar };
}
Vector2D operator*(double scalar, const Vector2D& vector) {
return vector * scalar;
}
Vector2D operator/(const Vector2D& vector, double scalar) {
return { vector.dx / scalar, vector.dy / scalar };
}
}

View File

@@ -0,0 +1,14 @@
#pragma once
#include "nodesoup.hpp"
namespace nodesoup {
Point2D operator+(const Point2D& point, const Vector2D& vector);
Point2D operator-(const Point2D& point, const Vector2D& vector);
Vector2D operator-(const Point2D& lhs, const Point2D& rhs);
Vector2D operator+(const Vector2D lhs, const Vector2D& rhs);
Vector2D operator-(const Vector2D& l, const Vector2D& rhs);
Vector2D operator*(const Vector2D& vector, double scalar);
Vector2D operator*(double scalar, const Vector2D& vector);
Vector2D operator/(const Vector2D& vector, double scalar);
};

View File

@@ -0,0 +1,83 @@
#include "fruchterman_reingold.hpp"
#include "algebra.hpp"
#include <algorithm>
#include <cmath>
#include <iostream>
namespace nodesoup {
using std::vector;
FruchtermanReingold::FruchtermanReingold(const adj_list_t& g, double k)
: g_(g)
, k_(k)
, k_squared_(k * k)
, temp_(10 * sqrt(g.size()))
, mvmts_(g_.size()) {}
void FruchtermanReingold::operator()(vector<Point2D>& positions) {
Vector2D zero = { 0.0, 0.0 };
fill(mvmts_.begin(), mvmts_.end(), zero);
// Repulsion force between vertice pairs
for (vertex_id_t v_id = 0; v_id < g_.size(); v_id++) {
for (vertex_id_t other_id = v_id + 1; other_id < g_.size(); other_id++) {
if (v_id == other_id) {
continue;
}
Vector2D delta = positions[v_id] - positions[other_id];
double distance = delta.norm();
// TODO: handle distance == 0.0
// > 1000.0: not worth computing
if (distance > 1000.0) {
continue;
}
double repulsion = k_squared_ / distance;
mvmts_[v_id] += delta / distance * repulsion;
mvmts_[other_id] -= delta / distance * repulsion;
}
// Attraction force between edges
for (vertex_id_t adj_id : g_[v_id]) {
if (adj_id > v_id) {
continue;
}
Vector2D delta = positions[v_id] - positions[adj_id];
double distance = delta.norm();
if (distance == 0.0) {
continue;
}
double attraction = distance * distance / k_;
mvmts_[v_id] -= delta / distance * attraction;
mvmts_[adj_id] += delta / distance * attraction;
}
}
// Max movement capped by current temperature
for (vertex_id_t v_id = 0; v_id < g_.size(); v_id++) {
double mvmt_norm = mvmts_[v_id].norm();
// < 1.0: not worth computing
if (mvmt_norm < 1.0) {
continue;
}
double capped_mvmt_norm = std::min(mvmt_norm, temp_);
Vector2D capped_mvmt = mvmts_[v_id] / mvmt_norm * capped_mvmt_norm;
positions[v_id] += capped_mvmt;
}
// Cool down fast until we reach 1.5, then stay at low temperature
if (temp_ > 1.5) {
temp_ *= 0.85;
} else {
temp_ = 1.5;
}
}
}

View File

@@ -0,0 +1,20 @@
#pragma once
#include "nodesoup.hpp"
#include <utility>
#include <vector>
namespace nodesoup {
class FruchtermanReingold {
public:
FruchtermanReingold(const adj_list_t& g, double k = 15.0);
void operator()(std::vector<Point2D>& positions);
private:
const adj_list_t& g_;
const double k_;
const double k_squared_;
double temp_;
std::vector<std::pair<vertex_id_t, vertex_id_t>> edges_;
std::vector<Vector2D> mvmts_;
};
}

View File

@@ -0,0 +1,188 @@
#include <algorithm>
#include <cassert>
#include <cmath>
#include <limits>
#include "algebra.hpp"
#include "kamada_kawai.hpp"
namespace nodesoup {
using std::vector;
KamadaKawai::KamadaKawai(const adj_list_t& g, double k, double energy_threshold)
: g_(g)
, energy_threshold_(energy_threshold) {
vector<vector<vertex_id_t>> distances = floyd_warshall_(g_);
// find biggest distance
size_t biggest_distance = 0;
for (vertex_id_t v_id = 0; v_id < g_.size(); v_id++) {
for (vertex_id_t other_id = 0; other_id < g_.size(); other_id++) {
if (distances[v_id][other_id] > biggest_distance) {
biggest_distance = distances[v_id][other_id];
}
}
}
// Ideal length for all edges. we don't really care, the layout is going to be scaled.
// Let's chose 1.0 as the initial positions will be on a 1.0 radius circle, so we're
// on the same order of magnitude
double length = 1.0 / biggest_distance;
// init springs lengths and strengths matrices
for (vertex_id_t v_id = 0; v_id < g_.size(); v_id++) {
vector<Spring> v_springs;
for (vertex_id_t other_id = 0; other_id < g_.size(); other_id++) {
Spring spring;
if (v_id == other_id) {
spring.length = 0.0;
spring.strength = 0.0;
} else {
size_t distance = distances[v_id][other_id];
spring.length = distance * length;
spring.strength = k / (distance * distance);
}
v_springs.push_back(spring);
}
springs_.push_back(v_springs);
}
}
vector<vector<vertex_id_t>> KamadaKawai::floyd_warshall_(const adj_list_t& g) {
// build adjacency matrix (infinity = no edge, 1 = edge)
unsigned int infinity = std::numeric_limits<unsigned int>::max() / 2;
vector<vector<vertex_id_t>> distances(g.size(), vector<vertex_id_t>(g.size(), infinity));
for (vertex_id_t v_id = 0; v_id < g.size(); v_id++) {
distances[v_id][v_id] = 0;
for (vertex_id_t adj_id : g[v_id]) {
if (adj_id > v_id) {
distances[v_id][adj_id] = 1;
distances[adj_id][v_id] = 1;
}
}
}
// floyd warshall itself, find length of shortest path for each pair of vertices
for (vertex_id_t k = 0; k < g.size(); k++) {
for (vertex_id_t i = 0; i < g.size(); i++) {
for (vertex_id_t j = 0; j < g.size(); j++) {
distances[i][j] = std::min(distances[i][j], distances[i][k] + distances[k][j]);
}
}
}
return distances;
}
#define MAX_VERTEX_ITERS_COUNT 50
#define MAX_STEADY_ENERGY_ITERS_COUNT 50
/**
Reduce the energy of the next vertex with most energy until all the vertices have
a energy below energy_threshold
*/
void KamadaKawai::operator()(vector<Point2D>& positions) const {
vertex_id_t v_id;
unsigned int steady_energy_count = 0;
double max_vertex_energy = find_max_vertex_energy_(positions, v_id);
while (max_vertex_energy > energy_threshold_ && steady_energy_count < MAX_STEADY_ENERGY_ITERS_COUNT) {
// move vertex step by step until its energy goes below threshold
// (apparently this is equivalent to the newton raphson method)
unsigned int vertex_count = 0;
do {
positions[v_id] = compute_next_vertex_position_(v_id, positions);
vertex_count++;
} while (compute_vertex_energy_(v_id, positions) > energy_threshold_ && vertex_count < MAX_VERTEX_ITERS_COUNT);
double max_vertex_energy_prev = max_vertex_energy;
max_vertex_energy = find_max_vertex_energy_(positions, v_id);
if (std::abs(max_vertex_energy - max_vertex_energy_prev) < 1e-20) {
steady_energy_count++;
} else {
steady_energy_count = 0;
}
}
}
/**
Find @p max_energy_v_id with the most potential energy and @return its energy
// https://gist.github.com/terakun/b7eff90c889c1485898ec9256ca9f91d
*/
double KamadaKawai::find_max_vertex_energy_(const vector<Point2D>& positions, vertex_id_t& max_energy_v_id) const {
double max_energy = -1.0;
for (vertex_id_t v_id = 0; v_id < g_.size(); v_id++) {
double energy = compute_vertex_energy_(v_id, positions);
if (energy > max_energy) {
max_energy_v_id = v_id;
max_energy = energy;
}
}
assert(max_energy != -1.0);
return max_energy;
}
/** @return the potential energies of springs between @p v_id and all other vertices */
double KamadaKawai::compute_vertex_energy_(vertex_id_t v_id, const vector<Point2D>& positions) const {
double x_energy = 0.0;
double y_energy = 0.0;
for (vertex_id_t other_id = 0; other_id < g_.size(); other_id++) {
if (v_id == other_id) {
continue;
}
Vector2D delta = positions[v_id] - positions[other_id];
double distance = delta.norm();
// delta * k * (1 - l / distance)
Spring spring = springs_[v_id][other_id];
x_energy += delta.dx * spring.strength * (1.0 - spring.length / distance);
y_energy += delta.dy * spring.strength * (1.0 - spring.length / distance);
}
return sqrt(x_energy * x_energy + y_energy * y_energy);
}
/**
@returns next position for @param v_id reducing its potential energy, ie the energy in the whole graph
caused by its position.
The position's delta depends on K (TODO bigger K = faster?).
This is the complicated part of the algorithm.
*/
Point2D KamadaKawai::compute_next_vertex_position_(vertex_id_t v_id, const vector<Point2D>& positions) const {
double xx_energy = 0.0, xy_energy = 0.0, yx_energy = 0.0, yy_energy = 0.0;
double x_energy = 0.0, y_energy = 0.0;
for (vertex_id_t other_id = 0; other_id < g_.size(); other_id++) {
if (v_id == other_id) {
continue;
}
Vector2D delta = positions[v_id] - positions[other_id];
double distance = delta.norm();
double cubed_distance = distance * distance * distance;
Spring spring = springs_[v_id][other_id];
x_energy += delta.dx * spring.strength * (1.0 - spring.length / distance);
y_energy += delta.dy * spring.strength * (1.0 - spring.length / distance);
xy_energy += spring.strength * spring.length * delta.dx * delta.dy / cubed_distance;
xx_energy += spring.strength * (1.0 - spring.length * delta.dy * delta.dy / cubed_distance);
yy_energy += spring.strength * (1.0 - spring.length * delta.dx * delta.dx / cubed_distance);
}
yx_energy = xy_energy;
Point2D position = positions[v_id];
double denom = xx_energy * yy_energy - xy_energy * yx_energy;
position.x += (xy_energy * y_energy - yy_energy * x_energy) / denom;
position.y += (xy_energy * x_energy - xx_energy * y_energy) / denom;
return position;
}
}

View File

@@ -0,0 +1,30 @@
#pragma once
#include "nodesoup.hpp"
#include <vector>
namespace nodesoup {
// https://gist.github.com/terakun/b7eff90c889c1485898ec9256ca9f91d
// https://graphsharp.codeplex.com/SourceControl/latest#Source/Graph#/Algorithms/Layout/Simple/FDP/KKLayoutAlgorithm.cs
class KamadaKawai {
public:
struct Spring {
double length;
double strength;
};
KamadaKawai(const adj_list_t& g, double k = 300.0, double energy_threshold = 1e-2);
void operator()(std::vector<Point2D>& positions) const;
private:
const adj_list_t& g_;
const double energy_threshold_;
std::vector<std::vector<Spring>> springs_;
static std::vector<std::vector<vertex_id_t>> floyd_warshall_(const adj_list_t& g);
// p m
double find_max_vertex_energy_(const std::vector<Point2D>& positions, vertex_id_t& max_energy_v_id) const;
// delta m
double compute_vertex_energy_(vertex_id_t v_id, const std::vector<Point2D>& positions) const;
Point2D compute_next_vertex_position_(vertex_id_t v_id, const std::vector<Point2D>& positions) const;
};
}

View File

@@ -0,0 +1,68 @@
#include "layout.hpp"
#include "algebra.hpp"
#include <cmath>
#include <limits>
namespace nodesoup {
using std::vector;
void circle(const adj_list_t& g, vector<Point2D>& positions) {
const double pi = 3.14159265358979323846;
double angle = 2.0 * pi / g.size();
for (vertex_id_t v_id = 0; v_id < g.size(); v_id++) {
positions[v_id].x = cos(v_id * angle);
positions[v_id].y = sin(v_id * angle);
}
}
void center_and_scale(const adj_list_t& g, unsigned int width, unsigned int height, vector<Point2D>& positions) {
// find current dimensions
double x_min = std::numeric_limits<double>::max();
double x_max = std::numeric_limits<double>::lowest();
double y_min = std::numeric_limits<double>::max();
double y_max = std::numeric_limits<double>::lowest();
for (vertex_id_t v_id = 0; v_id < g.size(); v_id++) {
if (positions[v_id].x < x_min)
x_min = positions[v_id].x;
if (positions[v_id].x > x_max)
x_max = positions[v_id].x;
if (positions[v_id].y < y_min)
y_min = positions[v_id].y;
if (positions[v_id].y > y_max)
y_max = positions[v_id].y;
}
double cur_width = x_max - x_min;
double cur_height = y_max - y_min;
// compute scale factor (0.9: keep some margin)
double x_scale = (cur_width > 0) ? width / cur_width : width;
double y_scale = (cur_height > 0) ? height / cur_height : height;
Vector2D scale = { x_scale, y_scale };
// compute offset and apply it to every position
Vector2D center = { x_max + x_min, y_max + y_min };
Vector2D offset = (center / 2.0) * scale;
double shiftMinX = std::numeric_limits<double>::max();
double shiftMinY = std::numeric_limits<double>::max();
for (vertex_id_t v_id = 0; v_id < g.size(); v_id++)
{
positions[v_id] = (Point2D)((Vector2D)positions[v_id] * scale - offset);
shiftMinX = std::min(shiftMinX, positions[v_id].x);
shiftMinY = std::min(shiftMinY, positions[v_id].y);
}
shiftMinX = abs(shiftMinX) + 50;
shiftMinY = abs(shiftMinY) + 50;
for (auto& pos : positions)
{
pos.x += shiftMinX;
pos.y += shiftMinY;
}
}
}

View File

@@ -0,0 +1,10 @@
#pragma once
#include "nodesoup.hpp"
#include <vector>
namespace nodesoup {
/** Distribute vertices equally on a 1.0 radius circle */
void circle(const adj_list_t& g, std::vector<Point2D>& positions);
/** Center and scale vertices so the graph fits on a canvas of given dimensions */
void center_and_scale(const adj_list_t& g, unsigned int width, unsigned int height, std::vector<Point2D>& positions);
}

View File

@@ -0,0 +1,64 @@
#include "nodesoup.hpp"
#include "fruchterman_reingold.hpp"
#include "kamada_kawai.hpp"
#include "layout.hpp"
#include <algorithm>
#include <cmath>
namespace nodesoup {
using std::vector;
vector<Point2D> fruchterman_reingold(
const adj_list_t& g,
unsigned int width,
unsigned int height,
unsigned int iters_count,
double k,
iter_callback_t iter_cb) {
vector<Point2D> positions(g.size());
// Initial layout on a circle
circle(g, positions);
FruchtermanReingold fr(g, k);
for (unsigned int i = 0; i < iters_count; i++) {
fr(positions);
if (iter_cb != nullptr) {
vector<Point2D> scaled_positions = positions;
center_and_scale(g, width, height, scaled_positions);
iter_cb(scaled_positions, i);
}
}
center_and_scale(g, width, height, positions);
return positions;
}
vector<Point2D> kamada_kawai(
const adj_list_t& g,
unsigned int width,
unsigned int height,
double k,
double energy_threshold) {
vector<Point2D> positions(g.size());
// Initial layout on a circle
circle(g, positions);
KamadaKawai kk(g, k, energy_threshold);
kk(positions);
center_and_scale(g, width, height, positions);
return positions;
}
vector<double> size_radiuses(const adj_list_t& g, double min_radius, double k) {
vector<double> radiuses;
radiuses.reserve(g.size());
for (vertex_id_t v_id = 0; v_id < g.size(); v_id++) {
double delta = log2(k * (double) g[v_id].size() / g.size());
double radius = min_radius + std::max(0.0, delta);
radiuses.push_back(radius);
}
return radiuses;
}
}

View File

@@ -0,0 +1,69 @@
#pragma once
#include <cmath>
#include <functional>
#include <vector>
#include <algorithm>
namespace nodesoup {
/** Simple adjaceny list graph structure */
using vertex_id_t = std::size_t;
using adj_list_t = std::vector<std::vector<vertex_id_t>>;
/** Algebra types */
struct Vector2D;
struct Point2D {
double x;
double y;
explicit operator Vector2D() const;
Point2D& operator+=(const Vector2D& vector);
Point2D& operator-=(const Vector2D& vector);
std::pair<int, int> toPair() { return std::make_pair((int)x, (int)y); }
};
struct Vector2D {
double dx;
double dy;
double norm() const {
return sqrt(dx * dx + dy * dy);
}
explicit operator Point2D() const;
Vector2D& operator+=(const Vector2D& other);
Vector2D& operator-=(const Vector2D& other);
Vector2D& operator*=(double scalar);
Vector2D& operator/=(double scalar);
Vector2D& operator*(const Vector2D& other);
};
/** Main library functions */
using iter_callback_t = std::function<void(const std::vector<Point2D>&, int)>;
/**
Applies the Freuchterman Reingold algorithm to layout graph @p in a frame of dimensions
@p width and @p height, in @p iter-count iterations
*/
std::vector<Point2D> fruchterman_reingold(
const adj_list_t& g,
unsigned int width,
unsigned int height,
unsigned int iters_count = 300,
double k = 15.0,
iter_callback_t iter_cb = nullptr);
std::vector<Point2D> kamada_kawai(
const adj_list_t& g,
unsigned int width,
unsigned int height,
double k = 300.0,
double energy_threshold = 1e-2);
/** Assigns diameters to vertices based on their degree */
std::vector<double> size_radiuses(const adj_list_t& g, double min_radius = 4.0, double k = 300.0);
}