io.hpp 20.1 KB
Newer Older
Gauthier Quesnel's avatar
Gauthier Quesnel committed
1
2
3
4
5
6
7
8
9
10
// Copyright (c) 2020 INRA Distributed under the Boost Software License,
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#ifndef ORG_VLEPROJECT_IRRITATOR_IO_2020
#define ORG_VLEPROJECT_IRRITATOR_IO_2020

#include <irritator/core.hpp>

#include <algorithm>
11
12
#include <istream>
#include <ostream>
Gauthier Quesnel's avatar
Gauthier Quesnel committed
13
14
15
16
17
18

namespace irt {

class reader
{
private:
19
    std::istream& is;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
20

Gauthier Quesnel's avatar
Gauthier Quesnel committed
21
    array<model_id> map;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
22
23
24
    int model_error = 0;
    int connection_error = 0;

25
26
27
    char temp_1[32];
    char temp_2[32];

Gauthier Quesnel's avatar
Gauthier Quesnel committed
28
public:
29
30
    reader(std::istream& is_) noexcept
      : is(is_)
Gauthier Quesnel's avatar
Gauthier Quesnel committed
31
32
    {}

33
    ~reader() noexcept = default;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
34
35
36
37
38

    status operator()(simulation& sim) noexcept
    {
        int model_number = 0;

39
        irt_return_if_fail((is >> model_number), status::io_file_format_error);
Gauthier Quesnel's avatar
Gauthier Quesnel committed
40
41
42
43
44
        irt_return_if_fail(model_number > 0,
                           status::io_file_format_model_number_error);

        irt_return_if_bad(map.init(model_number));

Gauthier Quesnel's avatar
Gauthier Quesnel committed
45
        std::fill_n(std::begin(map), std::size(map), static_cast<model_id>(0));
Gauthier Quesnel's avatar
Gauthier Quesnel committed
46
47
48
49

        int id;

        for (int i = 0; i != model_number; ++i, ++model_error) {
50
51
            irt_return_if_fail((is >> id >> temp_1 >> temp_2),
                               status::io_file_format_model_error);
Gauthier Quesnel's avatar
Gauthier Quesnel committed
52

Gauthier Quesnel's avatar
Gauthier Quesnel committed
53
54
55
            irt_return_if_fail(0 <= id && id < model_number,
                               status::io_file_format_model_error);

56
            irt_return_if_bad(read(sim, id, temp_1, temp_2));
Gauthier Quesnel's avatar
Gauthier Quesnel committed
57
58
        }

59
        while (is) {
Gauthier Quesnel's avatar
Gauthier Quesnel committed
60
61
            int mdl_src_index, port_src_index, mdl_dst_index, port_dst_index;

62
63
64
65
            if (!(is >> mdl_src_index >> port_src_index >> mdl_dst_index >>
                  port_dst_index)) {
                if (is.eof())
                    break;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
66

67
68
                irt_bad_return(status::io_file_format_error);
            }
Gauthier Quesnel's avatar
Gauthier Quesnel committed
69

70
71
            auto* mdl_src = sim.models.try_to_get(mdl_src_index);
            irt_return_if_fail(mdl_src, status::io_file_format_model_unknown);
Gauthier Quesnel's avatar
Gauthier Quesnel committed
72

73
74
            auto* mdl_dst = sim.models.try_to_get(mdl_dst_index);
            irt_return_if_fail(mdl_dst, status::io_file_format_model_unknown);
Gauthier Quesnel's avatar
Gauthier Quesnel committed
75

76
77
            output_port_id output_port;
            input_port_id input_port;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
78

79
80
81
82
            irt_return_if_bad(
              sim.get_output_port_id(*mdl_src, port_src_index, &output_port));
            irt_return_if_bad(
              sim.get_input_port_id(*mdl_dst, port_dst_index, &input_port));
Gauthier Quesnel's avatar
Gauthier Quesnel committed
83

84
85
            irt_return_if_bad(sim.connect(output_port, input_port));
            ++connection_error;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
86
87
88
89
90
91
        }

        return status::success;
    }

private:
92
93
    bool convert(const std::string_view dynamics_name,
                 dynamics_type* type) noexcept
Gauthier Quesnel's avatar
Gauthier Quesnel committed
94
    {
Gauthier Quesnel's avatar
Gauthier Quesnel committed
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
        struct string_to_type
        {
            constexpr string_to_type(const std::string_view n,
                                     const dynamics_type t)
              : name(n)
              , type(t)
            {}

            const std::string_view name;
            dynamics_type type;
        };

        static constexpr string_to_type table[] = {
            { "accumulator_2", dynamics_type::accumulator_2 },
            { "adder_2", dynamics_type::adder_2 },
            { "adder_3", dynamics_type::adder_3 },
            { "adder_4", dynamics_type::adder_4 },
            { "constant", dynamics_type::constant },
            { "counter", dynamics_type::counter },
            { "cross", dynamics_type::cross },
            { "generator", dynamics_type::generator },
            { "integrator", dynamics_type::integrator },
            { "mult_2", dynamics_type::mult_2 },
            { "mult_3", dynamics_type::mult_3 },
            { "mult_4", dynamics_type::mult_4 },
            { "none", dynamics_type::none },
            { "quantifier", dynamics_type::quantifier },
            { "qss1_integrator", dynamics_type::qss1_integrator },
            { "qss2_integrator", dynamics_type::qss2_integrator },
            { "qss2_multiplier", dynamics_type::qss2_multiplier },
            { "qss2_sum_2", dynamics_type::qss2_sum_2 },
            { "qss2_sum_3", dynamics_type::qss2_sum_3 },
            { "qss2_sum_4", dynamics_type::qss2_sum_4 },
            { "qss2_wsum_2", dynamics_type::qss2_wsum_2 },
            { "qss2_wsum_3", dynamics_type::qss2_wsum_3 },
            { "qss2_wsum_4", dynamics_type::qss2_wsum_4 },
            { "time_func", dynamics_type::time_func }
        };

        static_assert(std::size(table) ==
                      static_cast<size_t>(dynamics_type::accumulator_2) + 1);

        const auto it =
          std::lower_bound(std::begin(table),
                           std::end(table),
                           dynamics_name,
                           [](const string_to_type& l,
                              const std::string_view r) { return l.name < r; });

        if (it != std::end(table) && it->name == dynamics_name) {
            *type = it->type;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
146
147
148
149
150
151
            return true;
        }

        return false;
    }

Gauthier Quesnel's avatar
Gauthier Quesnel committed
152
153
154
155
    status read(simulation& sim,
                int id,
                const char* name,
                const char* dynamics_name) noexcept
Gauthier Quesnel's avatar
Gauthier Quesnel committed
156
157
158
159
160
161
162
    {
        dynamics_type type;

        irt_return_if_fail(convert(dynamics_name, &type),
                           status::io_file_format_dynamics_unknown);

        model_id mdl = static_cast<model_id>(0);
Gauthier Quesnel's avatar
Gauthier Quesnel committed
163
        auto ret = sim.dispatch(type, [this, &sim, name](auto& dyn_models) {
Gauthier Quesnel's avatar
Gauthier Quesnel committed
164
165
166
167
168
169
170
171
172
173
174
175
176
            irt_return_if_fail(dyn_models.can_alloc(1),
                               status::io_file_format_dynamics_limit_reach);
            auto& dyn = dyn_models.alloc();
            auto dyn_id = dyn_models.get_id(dyn);

            sim.alloc(dyn, dyn_id, name);

            irt_return_if_fail(read(dyn),
                               status::io_file_format_dynamics_init_error);

            return status::success;
        });

Gauthier Quesnel's avatar
Gauthier Quesnel committed
177
178
        irt_return_if_bad(ret);

Gauthier Quesnel's avatar
Gauthier Quesnel committed
179
        map[id] = mdl;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
180
181

        return status::success;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
182
183
184
185
186
187
188
    }

    bool read(none& /*dyn*/) noexcept
    {
        return true;
    }

Gauthier Quesnel's avatar
Gauthier Quesnel committed
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
    bool read(qss1_integrator& dyn) noexcept
    {
        double& x1 = *(const_cast<double*>(&dyn.default_X));
        double& x2 = *(const_cast<double*>(&dyn.default_dQ));

        return !!(is >> x1 >> x2);
    }

    bool read(qss2_integrator& dyn) noexcept
    {
        double& x1 = *(const_cast<double*>(&dyn.default_X));
        double& x2 = *(const_cast<double*>(&dyn.default_dQ));

        return !!(is >> x1 >> x2);
    }

    bool read(qss2_multiplier& /*dyn*/) noexcept
    {
        return true;
    }

    bool read(qss2_sum_2& /*dyn*/) noexcept
    {
        return true;
    }

    bool read(qss2_sum_3& /*dyn*/) noexcept
    {
        return true;
    }

    bool read(qss2_sum_4& /*dyn*/) noexcept
    {
        return true;
    }

    bool read(qss2_wsum_2& dyn) noexcept
    {
        double& x1 = *(const_cast<double*>(&dyn.default_input_coeffs[0]));
        double& x2 = *(const_cast<double*>(&dyn.default_input_coeffs[1]));

        return !!(is >> x1 >> x2);
    }

    bool read(qss2_wsum_3& dyn) noexcept
    {
        double& x1 = *(const_cast<double*>(&dyn.default_input_coeffs[0]));
        double& x2 = *(const_cast<double*>(&dyn.default_input_coeffs[1]));
        double& x3 = *(const_cast<double*>(&dyn.default_input_coeffs[2]));

        return !!(is >> x1 >> x2 >> x3);
    }

    bool read(qss2_wsum_4& dyn) noexcept
    {
        double& x1 = *(const_cast<double*>(&dyn.default_input_coeffs[0]));
        double& x2 = *(const_cast<double*>(&dyn.default_input_coeffs[1]));
        double& x3 = *(const_cast<double*>(&dyn.default_input_coeffs[2]));
        double& x4 = *(const_cast<double*>(&dyn.default_input_coeffs[2]));

        return !!(is >> x1 >> x2 >> x3 >> x4);
    }

Gauthier Quesnel's avatar
Gauthier Quesnel committed
252
253
    bool read(integrator& dyn) noexcept
    {
254
        return !!(is >> dyn.default_current_value >> dyn.default_reset_value);
Gauthier Quesnel's avatar
Gauthier Quesnel committed
255
256
257
258
    }

    bool read(quantifier& dyn) noexcept
    {
259
260
        if (!(is >> dyn.default_step_size >> dyn.default_past_length >>
              temp_1 >> temp_2))
Gauthier Quesnel's avatar
Gauthier Quesnel committed
261
262
            return false;

263
        if (std::strcmp(temp_1, "possible") == 0)
Gauthier Quesnel's avatar
Gauthier Quesnel committed
264
            dyn.default_adapt_state = quantifier::adapt_state::possible;
265
        else if (std::strcmp(temp_1, "impossible") == 0)
Gauthier Quesnel's avatar
Gauthier Quesnel committed
266
            dyn.default_adapt_state = quantifier::adapt_state::impossible;
267
        else if (std::strcmp(temp_1, "done") == 0)
Gauthier Quesnel's avatar
Gauthier Quesnel committed
268
269
270
271
            dyn.default_adapt_state = quantifier::adapt_state::done;
        else
            return false;

272
        if (std::strcmp(temp_2, "true") == 0)
Gauthier Quesnel's avatar
Gauthier Quesnel committed
273
            dyn.default_zero_init_offset = true;
274
        else if (std::strcmp(temp_2, "false") == 0)
Gauthier Quesnel's avatar
Gauthier Quesnel committed
275
276
277
278
279
280
281
282
283
            dyn.default_zero_init_offset = false;
        else
            return false;

        return true;
    }

    bool read(adder_2& dyn) noexcept
    {
284
285
        return !!(is >> dyn.default_values[0] >> dyn.default_values[1] >>
                  dyn.default_input_coeffs[0] >> dyn.default_input_coeffs[1]);
Gauthier Quesnel's avatar
Gauthier Quesnel committed
286
287
288
289
    }

    bool read(adder_3& dyn) noexcept
    {
290
291
292
        return !!(is >> dyn.default_values[0] >> dyn.default_values[1] >>
                  dyn.default_values[2] >> dyn.default_input_coeffs[0] >>
                  dyn.default_input_coeffs[1] >> dyn.default_input_coeffs[2]);
Gauthier Quesnel's avatar
Gauthier Quesnel committed
293
294
295
296
    }

    bool read(adder_4& dyn) noexcept
    {
297
298
299
300
        return !!(is >> dyn.default_values[0] >> dyn.default_values[1] >>
                  dyn.default_values[2] >> dyn.default_values[3] >>
                  dyn.default_input_coeffs[0] >> dyn.default_input_coeffs[1] >>
                  dyn.default_input_coeffs[2] >> dyn.default_input_coeffs[3]);
Gauthier Quesnel's avatar
Gauthier Quesnel committed
301
302
303
304
    }

    bool read(mult_2& dyn) noexcept
    {
305
306
        return !!(is >> dyn.default_values[0] >> dyn.default_values[1] >>
                  dyn.default_input_coeffs[0] >> dyn.default_input_coeffs[1]);
Gauthier Quesnel's avatar
Gauthier Quesnel committed
307
308
309
310
    }

    bool read(mult_3& dyn) noexcept
    {
311
312
313
        return !!(is >> dyn.default_values[0] >> dyn.default_values[1] >>
                  dyn.default_values[2] >> dyn.default_input_coeffs[0] >>
                  dyn.default_input_coeffs[1] >> dyn.default_input_coeffs[2]);
Gauthier Quesnel's avatar
Gauthier Quesnel committed
314
315
316
317
    }

    bool read(mult_4& dyn) noexcept
    {
318
319
320
321
        return !!(is >> dyn.default_values[0] >> dyn.default_values[1] >>
                  dyn.default_values[2] >> dyn.default_values[3] >>
                  dyn.default_input_coeffs[0] >> dyn.default_input_coeffs[1] >>
                  dyn.default_input_coeffs[2] >> dyn.default_input_coeffs[3]);
Gauthier Quesnel's avatar
Gauthier Quesnel committed
322
323
324
325
326
327
328
    }

    bool read(counter& /*dyn*/) noexcept
    {
        return true;
    }

329
    bool read(generator& dyn) noexcept
Gauthier Quesnel's avatar
Gauthier Quesnel committed
330
    {
331
        return !!(is >> dyn.default_value >> dyn.default_period >>
332
                  dyn.default_offset);
Gauthier Quesnel's avatar
Gauthier Quesnel committed
333
334
335
336
    }

    bool read(constant& dyn) noexcept
    {
337
        return !!(is >> dyn.default_value);
Gauthier Quesnel's avatar
Gauthier Quesnel committed
338
339
340
341
    }

    bool read(cross& dyn) noexcept
    {
342
        return !!(is >> dyn.default_threshold);
Gauthier Quesnel's avatar
Gauthier Quesnel committed
343
344
    }

345
346
347
348
349
    bool read(accumulator_2& /*dyn*/) noexcept
    {
        return true;
    }

Gauthier Quesnel's avatar
Gauthier Quesnel committed
350
351
    bool read(time_func& dyn) noexcept
    {
352
        if (!(is >> temp_1))
Gauthier Quesnel's avatar
Gauthier Quesnel committed
353
354
            return false;

355
        if (std::strcmp(temp_1, "square") == 0)
356
            dyn.default_f = &square_time_function;
357
        else
358
            dyn.default_f = &time_function;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
359
360
361
362
363
364
365

        return true;
    }
};

struct writer
{
366
    std::ostream& os;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
367

Gauthier Quesnel's avatar
Gauthier Quesnel committed
368
369
    array<model_id> map;

370
371
    writer(std::ostream& os_) noexcept
      : os(os_)
Gauthier Quesnel's avatar
Gauthier Quesnel committed
372
373
374
375
    {}

    status operator()(const simulation& sim) noexcept
    {
376
        os << sim.models.size() << '\n';
Gauthier Quesnel's avatar
Gauthier Quesnel committed
377

Gauthier Quesnel's avatar
Gauthier Quesnel committed
378
379
380
381
        irt_return_if_bad(map.init(sim.models.size()));

        std::fill_n(std::begin(map), std::size(map), static_cast<model_id>(0));

Gauthier Quesnel's avatar
Gauthier Quesnel committed
382
        model* mdl = nullptr;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
383
        int id = 0;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
384
385
386
        while (sim.models.next(mdl)) {
            const auto mdl_id = sim.models.get_id(mdl);

387
            os << id << ' ' << mdl->name.c_str() << ' ';
Gauthier Quesnel's avatar
Gauthier Quesnel committed
388
            map[id] = mdl_id;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
389
390
391
392
393

            sim.dispatch(mdl->type, [this, mdl](auto& dyn_models) {
                write(dyn_models.get(mdl->id));
                return status::success;
            });
Gauthier Quesnel's avatar
Gauthier Quesnel committed
394
395

            ++id;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
396
        }
Gauthier Quesnel's avatar
Gauthier Quesnel committed
397

Gauthier Quesnel's avatar
Gauthier Quesnel committed
398
399
400
401
402
403
        irt::output_port* out = nullptr;
        while (sim.output_ports.next(out)) {
            for (auto dst : out->connections) {
                if (auto* in = sim.input_ports.try_to_get(dst); in) {
                    auto* mdl_src = sim.models.try_to_get(out->model);
                    auto* mdl_dst = sim.models.try_to_get(in->model);
Gauthier Quesnel's avatar
Gauthier Quesnel committed
404

Gauthier Quesnel's avatar
Gauthier Quesnel committed
405
406
                    if (!(mdl_src && mdl_dst))
                        continue;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
407

Gauthier Quesnel's avatar
Gauthier Quesnel committed
408
409
                    int src_index = -1;
                    int dst_index = -1;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
410

Gauthier Quesnel's avatar
Gauthier Quesnel committed
411
                    irt_return_if_bad(
Gauthier Quesnel's avatar
Gauthier Quesnel committed
412
                      sim.get_input_port_index(*mdl_dst, dst, &dst_index));
Gauthier Quesnel's avatar
Gauthier Quesnel committed
413

Gauthier Quesnel's avatar
Gauthier Quesnel committed
414
                    irt_return_if_bad(sim.get_output_port_index(
Gauthier Quesnel's avatar
Gauthier Quesnel committed
415
416
417
418
419
420
421
                      *mdl_src, sim.output_ports.get_id(out), &src_index));

                    auto it_out = std::find(map.begin(), map.end(), out->model);
                    auto it_in = std::find(map.begin(), map.end(), in->model);

                    assert(it_out != map.end());
                    assert(it_in != map.end());
Gauthier Quesnel's avatar
Gauthier Quesnel committed
422

423
                    os << std::distance(map.begin(), it_out) << ' ' << src_index
424
425
                       << ' ' << std::distance(map.begin(), it_in) << ' '
                       << dst_index << '\n';
Gauthier Quesnel's avatar
Gauthier Quesnel committed
426
427
428
429
430
431
432
433
                }
            }
        }

        return status::success;
    }

private:
Gauthier Quesnel's avatar
Gauthier Quesnel committed
434
    void write(const none& /*dyn*/) noexcept
Gauthier Quesnel's avatar
Gauthier Quesnel committed
435
    {
436
        os << "none\n";
Gauthier Quesnel's avatar
Gauthier Quesnel committed
437
438
    }

Gauthier Quesnel's avatar
Gauthier Quesnel committed
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
    void write(const qss1_integrator& dyn) noexcept
    {
        os << "qss1_integrator " << dyn.default_X << ' ' << dyn.default_dQ
           << '\n';
    }

    void write(const qss2_integrator& dyn) noexcept
    {
        os << "qss2_integrator " << dyn.default_X << ' ' << dyn.default_dQ
           << '\n';
    }

    void write(const qss2_multiplier& /*dyn*/) noexcept
    {
        os << "qss2_multiplier\n";
    }

    void write(const qss2_sum_2& /*dyn*/) noexcept
    {
        os << "qss2_sum_2\n";
    }

    void write(const qss2_sum_3& /*dyn*/) noexcept
    {
        os << "qss2_sum_3\n";
    }

    void write(const qss2_sum_4& /*dyn*/) noexcept
    {
        os << "qss2_sum_4\n";
    }

    void write(const qss2_wsum_2& dyn) noexcept
    {
        os << "qss2_wsum_2 " << dyn.default_input_coeffs[0] << ' '
           << dyn.default_input_coeffs[1] << '\n';
    }

    void write(const qss2_wsum_3& dyn) noexcept
    {
        os << "qss2_wsum_3 " << dyn.default_input_coeffs[0] << ' '
           << dyn.default_input_coeffs[1] << ' ' << dyn.default_input_coeffs[2]
           << '\n';
    }

    void write(const qss2_wsum_4& dyn) noexcept
    {
        os << "qss2_wsum_3 " << dyn.default_input_coeffs[0] << ' '
           << dyn.default_input_coeffs[1] << ' ' << dyn.default_input_coeffs[2]
           << ' ' << dyn.default_input_coeffs[3] << '\n';
    }

Gauthier Quesnel's avatar
Gauthier Quesnel committed
491
492
    void write(const integrator& dyn) noexcept
    {
493
494
        os << "integrator " << dyn.default_current_value << ' '
           << dyn.default_reset_value << '\n';
Gauthier Quesnel's avatar
Gauthier Quesnel committed
495
496
497
498
    }

    void write(const quantifier& dyn) noexcept
    {
499
500
501
502
503
504
505
506
507
        os << "quantifier " << dyn.default_step_size << ' '
           << dyn.default_past_length << ' '
           << ((dyn.default_adapt_state == quantifier::adapt_state::possible)
                 ? "possible "
                 : dyn.default_adapt_state ==
                       quantifier::adapt_state::impossible
                     ? "impossibe "
                     : "done ")
           << (dyn.default_zero_init_offset == true ? "true\n" : "false\n");
Gauthier Quesnel's avatar
Gauthier Quesnel committed
508
509
510
511
    }

    void write(const adder_2& dyn) noexcept
    {
512
513
514
        os << "adder_2 " << dyn.default_values[0] << ' '
           << dyn.default_values[1] << ' ' << dyn.default_input_coeffs[0] << ' '
           << dyn.default_input_coeffs[1] << '\n';
Gauthier Quesnel's avatar
Gauthier Quesnel committed
515
516
517
518
    }

    void write(const adder_3& dyn) noexcept
    {
519
520
521
522
        os << "adder_3 " << dyn.default_values[0] << ' '
           << dyn.default_values[1] << ' ' << dyn.default_values[2] << ' '
           << dyn.default_input_coeffs[0] << ' ' << dyn.default_input_coeffs[1]
           << ' ' << dyn.default_input_coeffs[2] << '\n';
Gauthier Quesnel's avatar
Gauthier Quesnel committed
523
524
525
526
    }

    void write(const adder_4& dyn) noexcept
    {
527
528
529
530
531
        os << "adder_4 " << dyn.default_values[0] << ' '
           << dyn.default_values[1] << ' ' << dyn.default_values[2] << ' '
           << dyn.default_values[3] << ' ' << dyn.default_input_coeffs[0] << ' '
           << dyn.default_input_coeffs[1] << ' ' << dyn.default_input_coeffs[2]
           << ' ' << dyn.default_input_coeffs[3] << '\n';
Gauthier Quesnel's avatar
Gauthier Quesnel committed
532
533
534
535
    }

    void write(const mult_2& dyn) noexcept
    {
536
537
538
        os << "mult_2 " << dyn.default_values[0] << ' ' << dyn.default_values[1]
           << ' ' << dyn.default_input_coeffs[0] << ' '
           << dyn.default_input_coeffs[1] << '\n';
Gauthier Quesnel's avatar
Gauthier Quesnel committed
539
540
541
542
    }

    void write(const mult_3& dyn) noexcept
    {
543
544
545
546
        os << "mult_3 " << dyn.default_values[0] << ' ' << dyn.default_values[1]
           << ' ' << dyn.default_values[2] << ' ' << dyn.default_input_coeffs[0]
           << ' ' << dyn.default_input_coeffs[1] << ' '
           << dyn.default_input_coeffs[2] << '\n';
Gauthier Quesnel's avatar
Gauthier Quesnel committed
547
548
549
550
    }

    void write(const mult_4& dyn) noexcept
    {
551
552
553
554
555
        os << "mult_4 " << dyn.default_values[0] << ' ' << dyn.default_values[1]
           << ' ' << dyn.default_values[2] << ' ' << dyn.default_values[3]
           << ' ' << dyn.default_input_coeffs[0] << ' '
           << dyn.default_input_coeffs[1] << ' ' << dyn.default_input_coeffs[2]
           << ' ' << dyn.default_input_coeffs[3] << '\n';
Gauthier Quesnel's avatar
Gauthier Quesnel committed
556
557
558
559
    }

    void write(const counter& /*dyn*/) noexcept
    {
560
        os << "counter\n";
Gauthier Quesnel's avatar
Gauthier Quesnel committed
561
562
    }

563
    void write(const generator& dyn) noexcept
Gauthier Quesnel's avatar
Gauthier Quesnel committed
564
    {
565
566
567
        os << "generator " << dyn.default_value << ' ' << dyn.default_period
           << ' ' << dyn.default_offset << '\n';
        ;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
568
569
570
571
    }

    void write(const constant& dyn) noexcept
    {
572
        os << "constant " << dyn.default_value << '\n';
Gauthier Quesnel's avatar
Gauthier Quesnel committed
573
574
575
576
    }

    void write(const cross& dyn) noexcept
    {
577
        os << "cross " << dyn.default_threshold << '\n';
Gauthier Quesnel's avatar
Gauthier Quesnel committed
578
579
    }

580
581
582
583
584
    void write(const accumulator_2& /*dyn*/) noexcept
    {
        os << "accumulator_2\n";
    }

Gauthier Quesnel's avatar
Gauthier Quesnel committed
585
586
    void write(const time_func& dyn) noexcept
    {
587
588
        os << "time_func "
           << (dyn.default_f == &time_function ? "time\n" : "square\n");
Gauthier Quesnel's avatar
Gauthier Quesnel committed
589
590
591
592
593
594
    }
};

class dot_writer
{
private:
595
    std::ostream& os;
Gauthier Quesnel's avatar
Gauthier Quesnel committed
596
597

public:
598
599
    dot_writer(std::ostream& os_)
      : os(os_)
Gauthier Quesnel's avatar
Gauthier Quesnel committed
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
    {}

    /* With input and output port.

    digraph graphname{
        graph[rankdir = "LR"];
        node[shape = "record"];
        edge[];

        "sum_a"[label = "sum-a | <f0> | <f1>"];

        "sum_a":f0->int_a[id = 1];
        sum_b->int_b[label = "2-10"];
        prod->sum_b[label = "3-4"];
        prod -> "sum_a":f0[label = "3-2"];
        int_a->qua_a[label = "4-11"];
        int_a->prod[label = "4-5"];
        int_a -> "sum_a":f1[label = "4-1"];
        int_b->qua_b[label = "5-12"];
        int_b->prod[label = "5-6"];
        int_b->sum_b[label = "5-3"];
        qua_a->int_a[label = "6-7"];
        qua_b->int_b[label = "7-9"];
    }
    */

    void operator()(const simulation& sim) noexcept
    {
628
        os << "digraph graphname {\n";
Gauthier Quesnel's avatar
Gauthier Quesnel committed
629
630
631
632
633
634
635
636
637
638
639
640
641

        irt::output_port* output_port = nullptr;
        while (sim.output_ports.next(output_port)) {
            for (const irt::input_port_id dst : output_port->connections) {
                if (auto* input_port = sim.input_ports.try_to_get(dst);
                    input_port) {
                    auto* mdl_src = sim.models.try_to_get(output_port->model);
                    auto* mdl_dst = sim.models.try_to_get(input_port->model);

                    if (!(mdl_src && mdl_dst))
                        continue;

                    if (mdl_src->name.empty())
642
                        os << irt::get_key(output_port->model);
Gauthier Quesnel's avatar
Gauthier Quesnel committed
643
                    else
644
645
646
                        os << mdl_src->name.c_str();

                    os << " -> ";
Gauthier Quesnel's avatar
Gauthier Quesnel committed
647
648

                    if (mdl_dst->name.empty())
649
                        os << irt::get_key(input_port->model);
Gauthier Quesnel's avatar
Gauthier Quesnel committed
650
                    else
651
                        os << mdl_dst->name.c_str();
Gauthier Quesnel's avatar
Gauthier Quesnel committed
652

653
                    os << " [label=\"";
Gauthier Quesnel's avatar
Gauthier Quesnel committed
654
655

                    if (output_port->name.empty())
656
657
                        os << irt::get_key(
                          sim.output_ports.get_id(*output_port));
Gauthier Quesnel's avatar
Gauthier Quesnel committed
658
                    else
659
                        os << output_port->name.c_str();
Gauthier Quesnel's avatar
Gauthier Quesnel committed
660

661
                    os << " - ";
Gauthier Quesnel's avatar
Gauthier Quesnel committed
662
663

                    if (input_port->name.empty())
664
                        os << irt::get_key(sim.input_ports.get_id(*input_port));
Gauthier Quesnel's avatar
Gauthier Quesnel committed
665
                    else
666
                        os << input_port->name.c_str();
Gauthier Quesnel's avatar
Gauthier Quesnel committed
667

668
                    os << "\"];\n";
Gauthier Quesnel's avatar
Gauthier Quesnel committed
669
670
671
672
673
674
675
676
677
                }
            }
        }
    }
};

} // namespace irt

#endif