graphFileReader.hpp
7.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
#ifndef GRAPH_FILE_READER_HPP
#define GRAPH_FILE_READER_HPP
#include <cctype>
#include <chrono>
#include <string>
#include <cstring>
#include <fstream>
#include "graph.hpp"
constexpr unsigned int LINE_BUFFER_SIZE = 127;
class GraphFileReader
{
public:
GraphFileReader(const std::string& path): m_stream(path), m_path(path)
{
if (!m_stream.is_open())
throw std::logic_error("ERROR: cannot open '" + path + "'");
std::memset((void*)m_lineBuffer,'\0',LINE_BUFFER_SIZE);
}
//TODO: faire une petite gestion des erreurs dans le parser
std::pair<Vertices,Edges> readFile (VertexStructContainer& container)
{
std::pair<Vertices,Edges> ret;
std::vector<Vertex*> vertexStructToVertexPtr (1000000);
container.clear();
ret.first.reserve(1000000); ret.second.reserve(1000000);
container.resize(vertexStructToVertexPtr.size());
std::cout << "Begin reading... ";
auto begin = std::chrono::system_clock::now();
passComment();
readLine();
if (isVertexInfoLine())
{
unsigned int vertexCount = 0;
unsigned int weightCount = 1;
extractVertexCountAndWeightCount(vertexCount,weightCount);
if (weightCount != WEIGHTS_SIZE)
throw std::logic_error("WLMC is compiled for vertex with " + std::to_string(WEIGHTS_SIZE) +
" weights, but '" + m_path + "' contains vertices with " + std::to_string(weightCount) + " weights");
parseVertices(ret.first,container,vertexCount,weightCount,vertexStructToVertexPtr);
}
else
parseEdge(ret,container,vertexStructToVertexPtr);
do
{
readLine();
if (*m_lineBufferPtr == '\0')
break;
if (!isCommentLine())
parseEdge(ret,container,vertexStructToVertexPtr);
} while (!m_stream.eof());
auto end = std::chrono::system_clock::now();
std::cout << "took: " << std::chrono::duration_cast<std::chrono::seconds>(end - begin).count() << "s" << std::endl;
ret.first.shrink_to_fit();
ret.second.shrink_to_fit();
container.shrink_to_fit();
const size_t vertices = ret.first.size();
const size_t edges = ret.second.size();
const float density = 2.f*(float)edges / (float)(vertices * vertices - vertices);
std::cout << "|V|=" << vertices << " |E|=" << edges << " d=" << density << std::endl;
return ret;
}
private:
bool isVertexInfoLine(void) const { return *m_lineBuffer == 'i'; }
//Certains fichier utilise un 'n' pour enregistrer un sommet. Pour l'instant on ne lit pas les sommets de cette façon, mais directement depuis les arrêtes
bool isCommentLine(void) const { return (*m_lineBuffer == '%') || (*m_lineBuffer == 'c') || (*m_lineBuffer == 'n'); }
//retourne true si elle la fin du buffer n'a pas été rencontré
bool passWhites(void)
{
while (!std::isdigit(*m_lineBufferPtr))
{
if ((*m_lineBufferPtr == '\0') || (m_lineBufferPtr - m_lineBuffer) > LINE_BUFFER_SIZE)
return false;
++m_lineBufferPtr;
}
return true;
}
void readLine(void)
{
m_stream.getline(m_lineBuffer,LINE_BUFFER_SIZE);
m_lineBufferPtr = m_lineBuffer;
}
void passComment(void)
{
while (m_stream.peek() == '%')
m_stream.ignore(std::numeric_limits<std::streamsize>::max(),'\n');
};
float getFloat(void)
{
bool foundMinus = false;
float result = 0.f;
float INTPart = 0.f;
float DECPart = 0.f;
float mult = .1f;
if (*m_lineBufferPtr == '-')
{
++m_lineBufferPtr;
foundMinus = true;
}
while (std::isdigit(*m_lineBufferPtr))
{
INTPart *= 10.f;
INTPart += (float)(*m_lineBufferPtr - '0');
++m_lineBufferPtr;
}
if (*m_lineBufferPtr == '.')
++m_lineBufferPtr;
while (std::isdigit(*m_lineBufferPtr))
{
const float read = (*m_lineBufferPtr - '0') * mult;
DECPart += read;
mult /= 10.f;
++m_lineBufferPtr;
}
result = INTPart + DECPart;
return foundMinus ? -result : result;
}
void parseVertexWeights(Vertices& vertices, VertexStructContainer& container, std::vector<Vertex*>& vertexStructToVertexPtr, unsigned int vertexNumber, const unsigned int weightCount)
{
Weight w;
for (size_t i = 0; i < weightCount; ++i)
{
w[i] = getFloat();
passWhites();
}
findVertexAndEmplaceIfNot(vertexNumber,vertices,container,vertexStructToVertexPtr,w);
}
void parseVertices (Vertices& vertices, VertexStructContainer& container, const unsigned int vertexCount, const unsigned int weightCount, std::vector<Vertex*>& vertexStructToVertexPtr)
{
for (size_t i = 0; i < vertexCount; ++i)
{
readLine();
parseVertexWeights(vertices,container,vertexStructToVertexPtr, i+1,weightCount);
}
}
void extractVertexCountAndWeightCount(unsigned int& vertexCount, unsigned int& weightCount)
{
weightCount = 1;
passWhites();
vertexCount = extractUInt();
if (passWhites())
weightCount = extractUInt();
}
Vertex& findVertexAndEmplaceIfNot(const unsigned int vertexNumber, Vertices& vertices, VertexStructContainer& container, std::vector<Vertex*>& vertexStructToVertexPtr, const Weight w = {1.f})
{
//On utilise le numéro du sommet pour trouver sa place dans le graphe, du coup il faut être sûr que le container est assez grand pour contenir tous les sommets
if (vertexNumber >= container.size())
{
container.resize(container.size() + 100000);
vertexStructToVertexPtr.resize(container.size());
}
if (container[vertexNumber].get() == nullptr)
{
container[vertexNumber] = std::unique_ptr<VertexStruct>(new VertexStruct(vertexNumber,w));
vertices.emplace_back(container[vertexNumber].get());
vertexStructToVertexPtr[vertexNumber] = &vertices.back();
}
return *vertexStructToVertexPtr[vertexNumber];
}
void findEdgeFromVerticesAndEmplaceIfNot(Vertex& v1, Vertex&v2, Edges& edges) const
{
//Si une arête existe entre ces sommets, alors chacun des sommets à l'autre dans ses voisins. Pour vérifier
//si une arrête existe ou pas, il suffit donc de chercher un des sommets dans la liste des voisins de l'autre.
//Il n'y a pas besoin de tester les deux listes de voisins que si un sommet est dans les voisins d'un autre
//les deux sont forcément voisin l'un de l'autre (voir makeEdge)
//Wouah *_*
auto found = std::find_if(v1.neighbors.begin(), v1.neighbors.end(),
[&v2](const VertexStructPtr& vs)
{ return vs == v2; });
if (found == v1.neighbors.end())
edges.emplace_back(makeEdge(v1,v2));
}
void parseEdge(std::pair<Vertices,Edges>& pair, VertexStructContainer& container, std::vector<Vertex*>& vertexStructContainer)
{
//A priori le fichier n'est pas trié, il n'y a donc aucune garantie que le noeud lu n'ait pas
//déjà été trouvé, il faut donc le rechercher et le créer s'il n'existe pas
passWhites();
const unsigned int n1 = extractUInt();
passWhites();
const unsigned int n2 = extractUInt();
Vertex& v1 = findVertexAndEmplaceIfNot(n1,pair.first,container,vertexStructContainer);
Vertex& v2 = findVertexAndEmplaceIfNot(n2,pair.first,container,vertexStructContainer);
//Une fois que l'on a trouvé les vertex correspondant aux valeurs que l'on a lu du fichier,
//il faut vérifier que l'arrête qu'ils forment n'existe pas déjà car rien ne garantit
//qu'une arrête ne soit pas mise deux fois (par exemple (1,0) et (0,1) sont la même arrête et on
//ne veut pas la mettre deux fois)
findEdgeFromVerticesAndEmplaceIfNot(v1,v2,pair.second);
}
unsigned int extractUInt (void)
{
unsigned int ret = 0;
while (std::isdigit(*m_lineBufferPtr))
{
ret *= 10;
ret += *m_lineBufferPtr - '0';
++m_lineBufferPtr;
}
return ret;
}
private:
std::ifstream m_stream;
std::string m_path;
char m_lineBuffer [LINE_BUFFER_SIZE];
const char* m_lineBufferPtr;
};
#endif