Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
ca-io.H
Go to the documentation of this file.
1/*
2 Aleph_w
3
4 Data structures & Algorithms
5 version 2.0.0b
6 https://github.com/lrleon/Aleph-w
7
8 This file is part of Aleph-w library
9
10 Copyright (c) 2002-2026 Leandro Rabindranath Leon
11
12 Permission is hereby granted, free of charge, to any person obtaining a copy
13 of this software and associated documentation files (the "Software"), to deal
14 in the Software without restriction, including without limitation the rights
15 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 copies of the Software, and to permit persons to whom the Software is
17 furnished to do so, subject to the following conditions:
18
19 The above copyright notice and this permission notice shall be included in all
20 copies or substantial portions of the Software.
21
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 SOFTWARE.
29*/
30
52#ifndef CA_IO_H
53#define CA_IO_H
54
55#include <algorithm>
56#include <array>
57#include <cctype>
58#include <concepts>
59#include <cstdint>
60#include <iomanip>
61#include <istream>
62#include <limits>
63#include <ostream>
64#include <sstream>
65#include <string>
66#include <type_traits>
67#include <utility>
68
69#include <ah-cpp-compat.H>
70#include <ah-errors.H>
71#include <tpl_array.H>
72
73#include <ca-traits.H>
74#include <tpl_ca_concepts.H>
75
76namespace Aleph {
77namespace CA {
78
84struct RGB8
85{
86 std::uint8_t r = 0;
87 std::uint8_t g = 0;
88 std::uint8_t b = 0;
89
90 friend constexpr bool operator == (RGB8, RGB8) noexcept = default;
91};
92
114
117
126template <typename T>
128{
132
143 {
145 << "Grid_Snapshot::at: coordinate (" << row << ", " << col << ") out of " << height << "x"
146 << width;
147 return cells[row * width + col];
148 }
149};
150
159{
160 std::string rule = "B3/S23";
161 std::string name;
162 std::string comment;
163 bool trim_dead_border = false;
164 std::size_t line_width = 70;
165};
166
173{
174 char alive = 'O';
175 char dead = '.';
176 std::string comment;
177};
178
185{
186 char delimiter = ',';
187 bool skip_empty_lines = true;
188};
189
196{
197 bool pretty = false;
198 std::string rule;
199};
200
207{
208 std::uint16_t max_value = 255;
209};
210
219template <typename State>
221{
222 State dead{};
223 std::uint8_t dead_value = 255;
224 std::uint8_t alive_value = 0;
225
230 [[nodiscard]] std::uint8_t operator () (const State &v) const
231 {
232 return v == dead ? dead_value : alive_value;
233 }
234};
235
243template <typename State>
245{
246 State dead{};
247 RGB8 dead_colour{255, 255, 255};
249
254 [[nodiscard]] RGB8 operator () (const State &v) const
255 {
256 return v == dead ? dead_colour : alive_colour;
257 }
258};
259
260namespace ca_io_detail {
261
262template <typename Lattice>
263inline void require_rank2(const char *fn)
264{
265 static_assert(LatticeLike<Lattice>, "CA IO requires a LatticeLike frame");
266 static_assert(Lattice::rank == 2, "CA IO currently supports 2D frames");
267 (void) fn;
268}
269
270inline std::string trim_copy(const std::string &s)
271{
272 std::size_t b = 0;
273 while (b < s.size() and std::isspace(static_cast<unsigned char>(s[b])))
274 ++b;
275 std::size_t e = s.size();
276 while (e > b and std::isspace(static_cast<unsigned char>(s[e - 1])))
277 --e;
278 return s.substr(b, e - b);
279}
280
281inline std::string lower_copy(std::string s)
282{
283 for (char &c : s)
284 c = static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
285 return s;
286}
287
288inline bool blank(const std::string &s)
289{
290 for (const char c : s)
291 if (not std::isspace(static_cast<unsigned char>(c)))
292 return false;
293 return true;
294}
295
296inline ca_size_t parse_size_token(const std::string &s, const char *what)
297{
298 const std::string t = trim_copy(s);
299 ah_domain_error_if(t.empty()) << what << ": empty size";
300 ca_size_t value = 0;
301 for (const char c : t)
302 {
303 ah_domain_error_if(not std::isdigit(static_cast<unsigned char>(c)))
304 << what << ": invalid unsigned size '" << s << "'";
305 const ca_size_t digit = static_cast<ca_size_t>(c - '0');
306 ah_overflow_error_if(value > (std::numeric_limits<ca_size_t>::max() - digit) / 10)
307 << what << ": size overflows ca_size_t";
308 value = value * 10 + digit;
309 }
310 return value;
311}
312
313inline ca_index_t parse_index_token(const std::string &s, const char *what)
314{
315 const std::string t = trim_copy(s);
316 ah_domain_error_if(t.empty()) << what << ": empty coordinate";
317 std::size_t i = 0;
318 bool neg = false;
319 if (t[i] == '-' or t[i] == '+')
320 {
321 neg = t[i] == '-';
322 ++i;
323 }
324 ah_domain_error_if(i == t.size()) << what << ": missing coordinate digits";
325 ca_index_t value = 0;
326 for (; i < t.size(); ++i)
327 {
328 const char c = t[i];
329 ah_domain_error_if(not std::isdigit(static_cast<unsigned char>(c)))
330 << what << ": invalid coordinate '" << s << "'";
331 const ca_index_t digit = static_cast<ca_index_t>(c - '0');
332 ah_overflow_error_if(value > (std::numeric_limits<ca_index_t>::max() - digit) / 10)
333 << what << ": coordinate overflows ca_index_t";
334 value = value * 10 + digit;
335 }
336 return neg ? -value : value;
337}
338
339inline Array<std::string> split_comment_lines(const std::string &comment)
340{
342 std::istringstream in(comment);
343 std::string line;
344 while (std::getline(in, line))
345 lines.append(line);
346 if (comment.empty())
347 lines.clear();
348 return lines;
349}
350
351inline std::string json_escape(const std::string &s)
352{
353 std::ostringstream out;
354 for (const char c : s)
355 {
356 switch (c)
357 {
358 case '"':
359 out << "\\\"";
360 break;
361 case '\\':
362 out << "\\\\";
363 break;
364 case '\n':
365 out << "\\n";
366 break;
367 case '\r':
368 out << "\\r";
369 break;
370 case '\t':
371 out << "\\t";
372 break;
373 default:
374 if (static_cast<unsigned char>(c) < 0x20)
375 out << "\\u" << std::hex << std::setw(4) << std::setfill('0')
376 << static_cast<unsigned>(static_cast<unsigned char>(c)) << std::dec
377 << std::setfill(' ');
378 else
379 out << c;
380 break;
381 }
382 }
383 return out.str();
384}
385
386inline std::string rle_token(ca_size_t n, char glyph)
387{
388 if (n == 0)
389 return {};
390 std::string token;
391 if (n != 1)
392 token = std::to_string(n);
393 token.push_back(glyph);
394 return token;
395}
396
397inline void append_rle_token(std::string &body, ca_size_t n, char glyph)
398{
399 body += rle_token(n, glyph);
400}
401
402inline void write_wrapped_rle_body(std::ostream &out,
403 const std::string &body,
404 const std::size_t line_width)
405{
406 std::size_t col = 0;
407 std::size_t pos = 0;
408 const std::size_t width = line_width == 0 ? std::numeric_limits<std::size_t>::max() : line_width;
409 while (pos < body.size())
410 {
411 const std::size_t start = pos;
412 while (pos < body.size() and std::isdigit(static_cast<unsigned char>(body[pos])))
413 ++pos;
414 ah_domain_error_if(pos == body.size()) << "write_rle: malformed body token";
415 ++pos;
416 const std::size_t len = pos - start;
417 if (col != 0 and col + len > width)
418 {
419 out << '\n';
420 col = 0;
421 }
422 out.write(body.data() + static_cast<std::ptrdiff_t>(start), static_cast<std::streamsize>(len));
423 col += len;
424 if (body[pos - 1] == '!')
425 {
426 out << '\n';
427 col = 0;
428 }
429 }
430 if (col != 0)
431 out << '\n';
432}
433
434inline std::string header_value(const std::string &line, const std::string &key)
435{
436 std::istringstream parts(line);
437 std::string part;
438 while (std::getline(parts, part, ','))
439 {
440 const auto eq = part.find('=');
441 if (eq == std::string::npos)
442 continue;
443 const std::string lhs = lower_copy(trim_copy(part.substr(0, eq)));
444 if (lhs == key)
445 return trim_copy(part.substr(eq + 1));
446 }
447 return {};
448}
449
452 const bool have_extent,
453 const ca_index_t min_row,
454 const ca_index_t min_col,
455 const ca_index_t max_row,
456 const ca_index_t max_col)
457{
458 if (not have_extent)
459 {
460 pattern.width = 0;
461 pattern.height = 0;
462 pattern.origin_row = 0;
463 pattern.origin_col = 0;
464 pattern.alive.clear();
465 return;
466 }
467
468 pattern.origin_row = min_row;
469 pattern.origin_col = min_col;
470 pattern.height = static_cast<ca_size_t>(max_row - min_row + 1);
471 pattern.width = static_cast<ca_size_t>(max_col - min_col + 1);
472 pattern.alive.clear();
473 pattern.alive.reserve(absolute.size());
474 for (const auto &c : absolute)
475 pattern.alive.append(Coord_Vec<2>{c[0] - min_row, c[1] - min_col});
476}
477
478template <typename T>
479inline T parse_scalar(const std::string &token, const char *format)
480{
481 std::istringstream in(trim_copy(token));
482 T value{};
483 in >> value;
484 ah_domain_error_unless(in) << format << ": cannot parse scalar '" << token << "'";
485 std::string rest;
486 in >> rest;
488 << format << ": trailing characters in scalar '" << token << "'";
489 return value;
490}
491
492template <typename T>
493inline T parse_json_number(const std::string &s, std::size_t &pos)
494{
495 while (pos < s.size() and std::isspace(static_cast<unsigned char>(s[pos])))
496 ++pos;
497 const std::size_t start = pos;
498 if (pos < s.size() and (s[pos] == '-' or s[pos] == '+'))
499 ++pos;
500 while (pos < s.size() and std::isdigit(static_cast<unsigned char>(s[pos])))
501 ++pos;
502 if (pos < s.size() and s[pos] == '.')
503 {
504 ++pos;
505 while (pos < s.size() and std::isdigit(static_cast<unsigned char>(s[pos])))
506 ++pos;
507 }
508 if (pos < s.size() and (s[pos] == 'e' or s[pos] == 'E'))
509 {
510 ++pos;
511 if (pos < s.size() and (s[pos] == '-' or s[pos] == '+'))
512 ++pos;
513 while (pos < s.size() and std::isdigit(static_cast<unsigned char>(s[pos])))
514 ++pos;
515 }
516 ah_domain_error_if(start == pos) << "JSON snapshot: expected number";
517 return parse_scalar<T>(s.substr(start, pos - start), "JSON snapshot");
518}
519
520inline ca_size_t parse_json_size_field(const std::string &s, const std::string &key)
521{
522 const std::string needle = "\"" + key + "\"";
523 const std::size_t key_pos = s.find(needle);
524 ah_domain_error_if(key_pos == std::string::npos) << "JSON snapshot: missing field '" << key << "'";
525 std::size_t colon = s.find(':', key_pos + needle.size());
526 ah_domain_error_if(colon == std::string::npos)
527 << "JSON snapshot: missing ':' after '" << key << "'";
528 ++colon;
529 while (colon < s.size() and std::isspace(static_cast<unsigned char>(s[colon])))
530 ++colon;
531 const std::size_t start = colon;
532 while (colon < s.size() and std::isdigit(static_cast<unsigned char>(s[colon])))
533 ++colon;
534 ah_domain_error_if(start == colon)
535 << "JSON snapshot: field '" << key << "' is not an unsigned integer";
536 return parse_size_token(s.substr(start, colon - start), key.c_str());
537}
538
539inline void ensure_stream_ok(const std::ostream &out, const char *fn)
540{
541 ah_runtime_error_if(not out) << fn << ": output stream failed";
542}
543
544template <typename T>
545inline void write_text_scalar(std::ostream &out, const T &value)
546{
547 if constexpr (std::is_integral_v<T> and sizeof(T) == 1 and not std::is_same_v<T, bool>)
549 else
550 out << value;
551}
552
553} // namespace ca_io_detail
554
574template <typename Lattice>
575inline void write_rle(std::ostream &out,
576 const Lattice &frame,
577 const RLE_Write_Options &opts = {},
578 const typename Lattice::state_type &dead = typename Lattice::state_type{})
579{
580 ca_io_detail::require_rank2<Lattice>("write_rle");
581 using coord_t = typename Lattice::coord_type;
582
583 for (const auto &line : ca_io_detail::split_comment_lines(opts.name))
584 out << "#N " << line << '\n';
585 for (const auto &line : ca_io_detail::split_comment_lines(opts.comment))
586 out << "#C " << line << '\n';
587
588 const ca_size_t rows = frame.size(0);
589 const ca_size_t cols = frame.size(1);
590 ca_size_t r0 = 0, c0 = 0, r1 = rows, c1 = cols;
591
592 if (opts.trim_dead_border)
593 {
594 bool any = false;
595 ca_size_t min_r = rows, min_c = cols, max_r = 0, max_c = 0;
596 for (ca_size_t r = 0; r < rows; ++r)
597 for (ca_size_t c = 0; c < cols; ++c)
598 if (frame.at(coord_t{static_cast<ca_index_t>(r), static_cast<ca_index_t>(c)}) != dead)
599 {
600 any = true;
601 min_r = std::min(min_r, r);
602 min_c = std::min(min_c, c);
603 max_r = std::max(max_r, r);
604 max_c = std::max(max_c, c);
605 }
606 if (any)
607 {
608 r0 = min_r;
609 c0 = min_c;
610 r1 = max_r + 1;
611 c1 = max_c + 1;
612 }
613 else
614 {
615 r0 = c0 = r1 = c1 = 0;
616 }
617 }
618
619 const ca_size_t height = r1 - r0;
620 const ca_size_t width = c1 - c0;
621 out << "x = " << width << ", y = " << height << ", rule = " << opts.rule << '\n';
622
623 std::string body;
624 if (height == 0 or width == 0)
625 {
626 body = "!";
627 }
628 else
629 {
630 for (ca_size_t r = r0; r < r1; ++r)
631 {
632 bool run_alive =
633 frame.at(coord_t{static_cast<ca_index_t>(r), static_cast<ca_index_t>(c0)}) != dead;
634 ca_size_t run = 0;
635 for (ca_size_t c = c0; c < c1; ++c)
636 {
637 const bool alive =
638 frame.at(coord_t{static_cast<ca_index_t>(r), static_cast<ca_index_t>(c)}) != dead;
639 if (alive == run_alive)
640 ++run;
641 else
642 {
643 ca_io_detail::append_rle_token(body, run, run_alive ? 'o' : 'b');
644 run_alive = alive;
645 run = 1;
646 }
647 }
648 ca_io_detail::append_rle_token(body, run, run_alive ? 'o' : 'b');
649 if (r + 1 < r1)
650 ca_io_detail::append_rle_token(body, 1, '$');
651 }
652 body.push_back('!');
653 }
654
655 ca_io_detail::write_wrapped_rle_body(out, body, opts.line_width);
656 ca_io_detail::ensure_stream_ok(out, "write_rle");
657}
658
667template <typename Lattice>
668[[nodiscard]] inline std::string write_rle_string(
669 const Lattice &frame,
670 const RLE_Write_Options &opts = {},
671 const typename Lattice::state_type &dead = typename Lattice::state_type{})
672{
673 std::ostringstream out;
674 write_rle(out, frame, opts, dead);
675 return out.str();
676}
677
691[[nodiscard]] inline RLE_Pattern read_rle(std::istream &in)
692{
693 RLE_Pattern pattern;
694 std::string line;
695 bool header_seen = false;
696 ca_size_t cur_row = 0;
697 ca_size_t cur_col = 0;
698 ca_size_t run = 0;
699 bool ended = false;
700
701 while (not ended and std::getline(in, line))
702 {
703 if (line.empty())
704 continue;
705 if (line[0] == '#')
706 {
707 if (line.size() >= 2 and (line[1] == 'N' or line[1] == 'n'))
708 pattern.name = ca_io_detail::trim_copy(line.substr(2));
709 else if (line.size() >= 2 and (line[1] == 'C' or line[1] == 'c'))
710 pattern.comments.append(ca_io_detail::trim_copy(line.substr(2)));
711 continue;
712 }
713
714 if (not header_seen)
715 {
716 const std::string x = ca_io_detail::header_value(line, "x");
717 const std::string y = ca_io_detail::header_value(line, "y");
718 ah_domain_error_if(x.empty() or y.empty())
719 << "read_rle: missing x/y header in line '" << line << "'";
720 pattern.width = ca_io_detail::parse_size_token(x, "read_rle x");
721 pattern.height = ca_io_detail::parse_size_token(y, "read_rle y");
722 pattern.rule = ca_io_detail::header_value(line, "rule");
723 header_seen = true;
724 continue;
725 }
726
727 for (const char ch : line)
728 {
729 if (std::isdigit(static_cast<unsigned char>(ch)))
730 {
731 const ca_size_t digit = static_cast<ca_size_t>(ch - '0');
732 ah_overflow_error_if(run > (std::numeric_limits<ca_size_t>::max() - digit) / 10)
733 << "read_rle: run length overflows ca_size_t";
734 run = run * 10 + digit;
735 }
736 else if (ch == 'b' or ch == '.')
737 {
738 const ca_size_t n = run == 0 ? 1 : run;
739 ah_domain_error_if(cur_col + n > pattern.width)
740 << "read_rle: dead run exceeds row width";
741 cur_col += n;
742 run = 0;
743 }
744 else if (ch == 'o' or ch == 'A')
745 {
746 const ca_size_t n = run == 0 ? 1 : run;
747 ah_domain_error_if(cur_row >= pattern.height or cur_col + n > pattern.width)
748 << "read_rle: live run exceeds declared dimensions";
749 for (ca_size_t k = 0; k < n; ++k)
750 pattern.alive.append(Coord_Vec<2>{static_cast<ca_index_t>(cur_row),
751 static_cast<ca_index_t>(cur_col + k)});
752 cur_col += n;
753 run = 0;
754 }
755 else if (ch == '$')
756 {
757 const ca_size_t n = run == 0 ? 1 : run;
758 ah_domain_error_if(cur_row + n > pattern.height)
759 << "read_rle: row advance exceeds declared height";
760 cur_row += n;
761 cur_col = 0;
762 run = 0;
763 }
764 else if (ch == '!')
765 {
766 ended = true;
767 run = 0;
768 break;
769 }
770 else
771 ah_domain_error_unless(std::isspace(static_cast<unsigned char>(ch)))
772 << "read_rle: invalid body character '" << ch << "'";
773 }
774 }
775
776 ah_domain_error_if(not header_seen) << "read_rle: missing header";
777 ah_domain_error_if(run != 0) << "read_rle: dangling run length";
778 return pattern;
779}
780
785[[nodiscard]] inline RLE_Pattern read_rle_string(const std::string &s)
786{
787 std::istringstream in(s);
788 return read_rle(in);
789}
790
808template <typename Lattice>
810 const Binary_Cell_Pattern &pattern,
811 Lattice &frame,
812 const typename Lattice::state_type &alive = static_cast<typename Lattice::state_type>(1),
813 const typename Lattice::state_type &dead = typename Lattice::state_type{},
814 const ca_index_t origin_row = 0,
815 const ca_index_t origin_col = 0)
816{
817 ca_io_detail::require_rank2<Lattice>("load_binary_pattern");
818 using coord_t = typename Lattice::coord_type;
819 ah_domain_error_if(origin_row < 0 or origin_col < 0)
820 << "load_binary_pattern: negative destination origin";
821 ah_domain_error_if(static_cast<ca_size_t>(origin_row) + pattern.height > frame.size(0) or
822 static_cast<ca_size_t>(origin_col) + pattern.width > frame.size(1))
823 << "load_binary_pattern: pattern " << pattern.height << "x" << pattern.width
824 << " does not fit in frame " << frame.size(0) << "x" << frame.size(1);
825
826 frame.fill(dead);
827 for (const auto &cell : pattern.alive)
828 frame.set(coord_t{origin_row + cell[0], origin_col + cell[1]}, alive);
829}
830
842template <typename Lattice>
844 const Binary_Cell_Pattern &pattern,
845 const typename Lattice::state_type &alive = static_cast<typename Lattice::state_type>(1),
846 const typename Lattice::state_type &dead = typename Lattice::state_type{})
847{
848 ca_io_detail::require_rank2<Lattice>("make_lattice_from_pattern");
849 Lattice frame({pattern.height, pattern.width}, dead);
850 load_binary_pattern(pattern, frame, alive, dead);
851 return frame;
852}
853
866template <typename Lattice>
867inline void write_plaintext(std::ostream &out,
868 const Lattice &frame,
869 const Plaintext_Write_Options &opts = {},
870 const typename Lattice::state_type &dead = typename Lattice::state_type{})
871{
872 ca_io_detail::require_rank2<Lattice>("write_plaintext");
873 using coord_t = typename Lattice::coord_type;
874 for (const auto &line : ca_io_detail::split_comment_lines(opts.comment))
875 out << '!' << line << '\n';
876
877 for (ca_size_t r = 0; r < frame.size(0); ++r)
878 {
879 for (ca_size_t c = 0; c < frame.size(1); ++c)
880 out << (frame.at(coord_t{static_cast<ca_index_t>(r), static_cast<ca_index_t>(c)}) == dead
881 ? opts.dead
882 : opts.alive);
883 out << '\n';
884 }
885 ca_io_detail::ensure_stream_ok(out, "write_plaintext");
886}
887
896template <typename Lattice>
897[[nodiscard]] inline std::string write_plaintext_string(
898 const Lattice &frame,
899 const Plaintext_Write_Options &opts = {},
900 const typename Lattice::state_type &dead = typename Lattice::state_type{})
901{
902 std::ostringstream out;
903 write_plaintext(out, frame, opts, dead);
904 return out.str();
905}
906
920{
921 Binary_Cell_Pattern pattern;
923 std::string line;
924 while (std::getline(in, line))
925 {
926 if (line.empty())
927 {
928 rows.append(std::string{});
929 continue;
930 }
931 if (line[0] == '!')
932 {
933 pattern.comments.append(ca_io_detail::trim_copy(line.substr(1)));
934 continue;
935 }
936 std::string row;
937 for (const char c : line)
938 if (c == 'O' or c == 'o' or c == '*' or c == '.')
939 row.push_back(c);
940 else
941 ah_domain_error_unless(std::isspace(static_cast<unsigned char>(c)))
942 << "read_plaintext: invalid character '" << c << "'";
943 rows.append(std::move(row));
944 }
945
946 pattern.height = rows.size();
947 pattern.width = 0;
948 for (const auto &row : rows)
949 pattern.width = std::max(pattern.width, row.size());
950 for (ca_size_t r = 0; r < rows.size(); ++r)
951 for (ca_size_t c = 0; c < rows(r).size(); ++c)
952 if (rows(r)[c] == 'O' or rows(r)[c] == 'o' or rows(r)[c] == '*')
953 pattern.alive.append(Coord_Vec<2>{static_cast<ca_index_t>(r), static_cast<ca_index_t>(c)});
954 return pattern;
955}
956
962{
963 std::istringstream in(s);
964 return read_plaintext(in);
965}
966
980template <typename Lattice>
981inline void write_life_106(std::ostream &out,
982 const Lattice &frame,
983 const typename Lattice::state_type &dead = typename Lattice::state_type{})
984{
985 ca_io_detail::require_rank2<Lattice>("write_life_106");
986 using coord_t = typename Lattice::coord_type;
987 out << "#Life 1.06\n";
988 for (ca_size_t r = 0; r < frame.size(0); ++r)
989 for (ca_size_t c = 0; c < frame.size(1); ++c)
990 if (frame.at(coord_t{static_cast<ca_index_t>(r), static_cast<ca_index_t>(c)}) != dead)
991 out << c << ' ' << r << '\n';
992 ca_io_detail::ensure_stream_ok(out, "write_life_106");
993}
994
1008{
1009 Binary_Cell_Pattern pattern;
1010 std::string line;
1011 bool header_seen = false;
1013 ca_index_t min_r = 0, min_c = 0, max_r = 0, max_c = 0;
1014 bool have = false;
1015
1016 while (std::getline(in, line))
1017 {
1018 const std::string t = ca_io_detail::trim_copy(line);
1019 if (t.empty())
1020 continue;
1021 if (t[0] == '#')
1022 {
1023 if (t == "#Life 1.06")
1024 header_seen = true;
1025 else if (t.size() >= 2 and (t[1] == 'D' or t[1] == 'd'))
1026 pattern.comments.append(ca_io_detail::trim_copy(t.substr(2)));
1027 continue;
1028 }
1029 std::istringstream ls(t);
1030 std::string xs, ys, extra;
1031 ls >> xs >> ys >> extra;
1032 ah_domain_error_if(not extra.empty()) << "read_life_106: too many fields in '" << line << "'";
1033 const ca_index_t col = ca_io_detail::parse_index_token(xs, "read_life_106 x");
1034 const ca_index_t row = ca_io_detail::parse_index_token(ys, "read_life_106 y");
1035 absolute.append(Coord_Vec<2>{row, col});
1036 if (not have)
1037 {
1038 min_r = max_r = row;
1039 min_c = max_c = col;
1040 have = true;
1041 }
1042 else
1043 {
1044 min_r = std::min(min_r, row);
1045 min_c = std::min(min_c, col);
1046 max_r = std::max(max_r, row);
1047 max_c = std::max(max_c, col);
1048 }
1049 }
1050
1051 ah_domain_error_unless(header_seen) << "read_life_106: missing #Life 1.06 header";
1052 ca_io_detail::finalise_absolute_pattern(pattern, absolute, have, min_r, min_c, max_r, max_c);
1053 return pattern;
1054}
1055
1071template <typename Lattice>
1072inline void write_life_105(std::ostream &out,
1073 const Lattice &frame,
1074 const std::string &comment = {},
1075 const typename Lattice::state_type &dead = typename Lattice::state_type{})
1076{
1077 ca_io_detail::require_rank2<Lattice>("write_life_105");
1078 using coord_t = typename Lattice::coord_type;
1079 out << "#Life 1.05\n";
1080 for (const auto &line : ca_io_detail::split_comment_lines(comment))
1081 out << "#D " << line << '\n';
1082 out << "#P 0 0\n";
1083 for (ca_size_t r = 0; r < frame.size(0); ++r)
1084 {
1085 for (ca_size_t c = 0; c < frame.size(1); ++c)
1086 out << (frame.at(coord_t{static_cast<ca_index_t>(r), static_cast<ca_index_t>(c)}) == dead
1087 ? '.'
1088 : '*');
1089 out << '\n';
1090 }
1091 ca_io_detail::ensure_stream_ok(out, "write_life_105");
1092}
1093
1107{
1108 Binary_Cell_Pattern pattern;
1110 std::string line;
1111 bool header_seen = false;
1112 bool in_block = false;
1114 ca_index_t min_r = 0, min_c = 0, max_r = 0, max_c = 0;
1115 bool have_extent = false;
1116
1118 {
1119 if (not have_extent)
1120 {
1121 min_r = max_r = row;
1122 min_c = max_c = col;
1123 have_extent = true;
1124 }
1125 else
1126 {
1127 min_r = std::min(min_r, row);
1128 min_c = std::min(min_c, col);
1129 max_r = std::max(max_r, row);
1130 max_c = std::max(max_c, col);
1131 }
1132 };
1133
1134 while (std::getline(in, line))
1135 {
1136 const std::string t = ca_io_detail::trim_copy(line);
1137 if (t.empty())
1138 continue;
1139 if (t[0] == '#')
1140 {
1141 if (t == "#Life 1.05")
1142 {
1143 header_seen = true;
1144 continue;
1145 }
1146 if (t.size() >= 2 and (t[1] == 'D' or t[1] == 'd'))
1147 {
1148 pattern.comments.append(ca_io_detail::trim_copy(t.substr(2)));
1149 continue;
1150 }
1151 if (t.size() >= 2 and (t[1] == 'P' or t[1] == 'p'))
1152 {
1153 std::istringstream ps(t.substr(2));
1154 std::string xs, ys, extra;
1155 ps >> xs >> ys >> extra;
1156 ah_domain_error_if(xs.empty() or ys.empty() or not extra.empty())
1157 << "read_life_105: malformed #P line '" << line << "'";
1158 block_col = ca_io_detail::parse_index_token(xs, "read_life_105 #P x");
1159 block_row = ca_io_detail::parse_index_token(ys, "read_life_105 #P y");
1160 local_row = 0;
1161 in_block = true;
1162 continue;
1163 }
1164 continue;
1165 }
1166
1167 ah_domain_error_unless(in_block) << "read_life_105: pattern row before #P block";
1169 for (const char c : line)
1170 {
1171 if (c == '*' or c == 'O' or c == 'o' or c == '.')
1172 {
1176 if (c == '*' or c == 'O' or c == 'o')
1177 absolute.append(Coord_Vec<2>{row, col});
1178 ++local_col;
1179 }
1180 else
1181 ah_domain_error_unless(std::isspace(static_cast<unsigned char>(c)))
1182 << "read_life_105: invalid row character '" << c << "'";
1183 }
1184 ++local_row;
1185 }
1186
1187 ah_domain_error_unless(header_seen) << "read_life_105: missing #Life 1.05 header";
1188 ca_io_detail::finalise_absolute_pattern(pattern, absolute, have_extent, min_r, min_c, max_r, max_c);
1189 return pattern;
1190}
1191
1206template <typename Lattice>
1207inline void write_csv(std::ostream &out, const Lattice &frame, const CSV_Options &opts = {})
1208{
1209 ca_io_detail::require_rank2<Lattice>("write_csv");
1210 using coord_t = typename Lattice::coord_type;
1211 for (ca_size_t r = 0; r < frame.size(0); ++r)
1212 {
1213 for (ca_size_t c = 0; c < frame.size(1); ++c)
1214 {
1215 if (c != 0)
1216 out << opts.delimiter;
1217 ca_io_detail::write_text_scalar(
1218 out, frame.at(coord_t{static_cast<ca_index_t>(r), static_cast<ca_index_t>(c)}));
1219 }
1220 out << '\n';
1221 }
1222 ca_io_detail::ensure_stream_ok(out, "write_csv");
1223}
1224
1241template <typename T>
1243 const CSV_Options &opts = {})
1244{
1246 std::string line;
1247 ca_size_t row = 0;
1248 while (std::getline(in, line))
1249 {
1250 if (opts.skip_empty_lines and ca_io_detail::blank(line))
1251 continue;
1252 Array<T> values;
1253 std::string token;
1254 std::istringstream ls(line);
1255 while (std::getline(ls, token, opts.delimiter))
1256 values.append(ca_io_detail::parse_scalar<T>(token, "CSV snapshot"));
1257 if (line.empty() and not opts.skip_empty_lines)
1258 values.append(T{});
1259 if (row == 0)
1260 snap.width = values.size();
1261 ah_domain_error_if(values.size() != snap.width)
1262 << "read_csv_snapshot: row " << row << " has width " << values.size() << " instead of "
1263 << snap.width;
1264 for (const T &v : values)
1265 snap.cells.append(v);
1266 ++row;
1267 }
1268 snap.height = row;
1269 return snap;
1270}
1271
1282template <typename Lattice, typename T>
1284{
1285 ca_io_detail::require_rank2<Lattice>("make_lattice_from_snapshot");
1286 using state_t = typename Lattice::state_type;
1287 using coord_t = typename Lattice::coord_type;
1288 Lattice frame({snap.height, snap.width}, state_t{});
1289 for (ca_size_t r = 0; r < snap.height; ++r)
1290 for (ca_size_t c = 0; c < snap.width; ++c)
1291 frame.set(coord_t{static_cast<ca_index_t>(r), static_cast<ca_index_t>(c)},
1292 static_cast<state_t>(snap.at(r, c)));
1293 return frame;
1294}
1295
1313template <typename Lattice>
1314inline void write_json(std::ostream &out, const Lattice &frame, const JSON_Write_Options &opts = {})
1315{
1316 ca_io_detail::require_rank2<Lattice>("write_json");
1317 using coord_t = typename Lattice::coord_type;
1318
1319 const auto nl = opts.pretty ? "\n" : "";
1320 const auto i1 = opts.pretty ? " " : "";
1321 const auto i2 = opts.pretty ? " " : "";
1322 const auto sp = opts.pretty ? " " : "";
1323
1324 out << '{' << nl;
1325 out << i1 << "\"type\":" << sp << "\"Aleph::CA::frame\"," << nl;
1326 out << i1 << "\"rank\":" << sp << "2," << nl;
1327 out << i1 << "\"height\":" << sp << frame.size(0) << ',' << nl;
1328 out << i1 << "\"width\":" << sp << frame.size(1);
1329 if (not opts.rule.empty())
1330 out << ',' << nl << i1 << "\"rule\":" << sp << '"' << ca_io_detail::json_escape(opts.rule) << '"';
1331 out << ',' << nl << i1 << "\"cells\":" << sp << '[' << nl;
1332 for (ca_size_t r = 0; r < frame.size(0); ++r)
1333 {
1334 out << i2 << '[';
1335 for (ca_size_t c = 0; c < frame.size(1); ++c)
1336 {
1337 if (c != 0)
1338 out << ',';
1339 if (opts.pretty)
1340 out << ' ';
1341 ca_io_detail::write_text_scalar(
1342 out, frame.at(coord_t{static_cast<ca_index_t>(r), static_cast<ca_index_t>(c)}));
1343 }
1344 if (opts.pretty and frame.size(1) != 0)
1345 out << ' ';
1346 out << ']';
1347 if (r + 1 < frame.size(0))
1348 out << ',';
1349 out << nl;
1350 }
1351 out << i1 << ']' << nl << "}" << nl;
1352 ca_io_detail::ensure_stream_ok(out, "write_json");
1353}
1354
1369template <typename T>
1371{
1372 std::ostringstream ss;
1373 ss << in.rdbuf();
1374 const std::string s = ss.str();
1376 snap.height = ca_io_detail::parse_json_size_field(s, "height");
1377 snap.width = ca_io_detail::parse_json_size_field(s, "width");
1378
1379 const std::size_t key = s.find("\"cells\"");
1380 ah_domain_error_if(key == std::string::npos) << "JSON snapshot: missing cells field";
1381 std::size_t pos = s.find('[', key);
1382 ah_domain_error_if(pos == std::string::npos) << "JSON snapshot: missing cells array";
1383 ++pos;
1384
1385 ca_size_t rows = 0;
1386 while (true)
1387 {
1388 while (pos < s.size() and (std::isspace(static_cast<unsigned char>(s[pos])) or s[pos] == ','))
1389 ++pos;
1390 ah_domain_error_if(pos >= s.size()) << "JSON snapshot: unterminated cells array";
1391 if (s[pos] == ']')
1392 {
1393 ++pos;
1394 break;
1395 }
1396 ah_domain_error_if(s[pos] != '[') << "JSON snapshot: expected row array";
1397 ++pos;
1398 ca_size_t cols = 0;
1399 while (true)
1400 {
1401 while (pos < s.size() and std::isspace(static_cast<unsigned char>(s[pos])))
1402 ++pos;
1403 ah_domain_error_if(pos >= s.size()) << "JSON snapshot: unterminated row";
1404 if (s[pos] == ']')
1405 {
1406 ++pos;
1407 break;
1408 }
1409 if (cols != 0)
1410 {
1411 ah_domain_error_if(s[pos] != ',') << "JSON snapshot: expected comma";
1412 ++pos;
1413 }
1414 snap.cells.append(ca_io_detail::parse_json_number<T>(s, pos));
1415 ++cols;
1416 }
1417 ah_domain_error_if(cols != snap.width)
1418 << "JSON snapshot: row " << rows << " has width " << cols << " instead of " << snap.width;
1419 ++rows;
1420 }
1421
1422 ah_domain_error_if(rows != snap.height)
1423 << "JSON snapshot: got " << rows << " rows instead of " << snap.height;
1424 return snap;
1425}
1426
1443template <typename Lattice, typename Mapper>
1444inline void write_pgm(std::ostream &out,
1445 const Lattice &frame,
1446 Mapper &&mapper,
1447 const NetPBM_Write_Options &opts = {})
1448 requires requires(Mapper &&m, const typename Lattice::state_type &v) {
1449 { m(v) } -> std::convertible_to<std::uint8_t>;
1450 }
1451{
1452 ca_io_detail::require_rank2<Lattice>("write_pgm");
1453 using coord_t = typename Lattice::coord_type;
1454 ah_domain_error_if(opts.max_value == 0 or opts.max_value > 255)
1455 << "write_pgm: only 8-bit max_value in [1,255] is supported";
1456 out << "P5\n" << frame.size(1) << ' ' << frame.size(0) << '\n' << opts.max_value << '\n';
1457 for (ca_size_t r = 0; r < frame.size(0); ++r)
1458 for (ca_size_t c = 0; c < frame.size(1); ++c)
1459 {
1460 const std::uint8_t v = static_cast<std::uint8_t>(
1461 mapper(frame.at(coord_t{static_cast<ca_index_t>(r), static_cast<ca_index_t>(c)})));
1462 out.write(reinterpret_cast<const char *>(&v), 1);
1463 }
1464 ca_io_detail::ensure_stream_ok(out, "write_pgm");
1465}
1466
1474template <typename Lattice>
1475inline void write_pgm(std::ostream &out, const Lattice &frame, const NetPBM_Write_Options &opts = {})
1476{
1478}
1479
1495template <typename Lattice, typename Mapper>
1496inline void write_ppm(std::ostream &out,
1497 const Lattice &frame,
1498 Mapper &&mapper,
1499 const NetPBM_Write_Options &opts = {})
1500 requires requires(Mapper &&m, const typename Lattice::state_type &v) {
1501 { m(v) } -> std::convertible_to<RGB8>;
1502 }
1503{
1504 ca_io_detail::require_rank2<Lattice>("write_ppm");
1505 using coord_t = typename Lattice::coord_type;
1506 ah_domain_error_if(opts.max_value == 0 or opts.max_value > 255)
1507 << "write_ppm: only 8-bit max_value in [1,255] is supported";
1508 out << "P6\n" << frame.size(1) << ' ' << frame.size(0) << '\n' << opts.max_value << '\n';
1509 for (ca_size_t r = 0; r < frame.size(0); ++r)
1510 for (ca_size_t c = 0; c < frame.size(1); ++c)
1511 {
1512 const RGB8 rgb =
1513 mapper(frame.at(coord_t{static_cast<ca_index_t>(r), static_cast<ca_index_t>(c)}));
1514 const char bytes[3] = {static_cast<char>(rgb.r), static_cast<char>(rgb.g),
1515 static_cast<char>(rgb.b)};
1516 out.write(bytes, 3);
1517 }
1518 ca_io_detail::ensure_stream_ok(out, "write_ppm");
1519}
1520
1528template <typename Lattice>
1529inline void write_ppm(std::ostream &out, const Lattice &frame, const NetPBM_Write_Options &opts = {})
1530{
1532}
1533
1546{
1547 std::ostream *out_ = nullptr;
1548 bool first_ = true;
1549 bool closed_ = false;
1551
1552public:
1562 : out_(&out), opts_(std::move(opts))
1563 {
1564 *out_ << "{\n \"type\": \"Aleph::CA::frame-stream\",\n \"frames\": [\n";
1565 ca_io_detail::ensure_stream_ok(*out_, "JSON_Frame_Stream_Writer");
1566 }
1567
1570
1577 {
1578 if (out_ != nullptr and not closed_)
1579 {
1580 try
1581 {
1582 close();
1583 }
1584 catch (...)
1585 {}
1586 }
1587 }
1588
1599 template <typename Lattice>
1600 void accept(std::size_t step, const Lattice &frame)
1601 {
1602 ca_io_detail::require_rank2<Lattice>("JSON_Frame_Stream_Writer::accept");
1603 ah_runtime_error_if(closed_) << "JSON_Frame_Stream_Writer::accept: stream already closed";
1604 if (not first_)
1605 *out_ << ",\n";
1606 first_ = false;
1607 *out_ << " {\n \"step\": " << step << ",\n \"frame\": ";
1609 compact.pretty = false;
1610 write_json(*out_, frame, compact);
1611 *out_ << " }";
1612 ca_io_detail::ensure_stream_ok(*out_, "JSON_Frame_Stream_Writer::accept");
1613 }
1614
1622 void close()
1623 {
1624 if (closed_)
1625 return;
1626 *out_ << "\n ]\n}\n";
1627 closed_ = true;
1628 ca_io_detail::ensure_stream_ok(*out_, "JSON_Frame_Stream_Writer::close");
1629 }
1630};
1631
1632// Non-throwing parser overloads.
1633//
1634// Each `try_read_*` wraps the corresponding throwing reader above and returns
1635// `Aleph::expected<Result, std::string>`: the parsed value on success, or the
1636// error message on failure. Useful when parsing untrusted input where an
1637// invalid file is an expected outcome rather than an exceptional one. The
1638// throwing readers are unchanged.
1639// ---------------------------------------------------------------------------
1640
1641namespace ca_io_detail {
1642
1646template <class F>
1647[[nodiscard]] auto try_parse(F &&parse, const char *context)
1648 -> expected<std::invoke_result_t<F &>, std::string>
1649{
1650 try
1651 {
1652 return parse();
1653 }
1654 catch (const std::exception &e)
1655 {
1656 return unexpected<std::string>(std::string(context) + ": " + e.what());
1657 }
1658 catch (...)
1659 {
1660 return unexpected<std::string>(std::string(context) + ": unknown error");
1661 }
1662}
1663
1664} // namespace ca_io_detail
1665
1681{
1682 return ca_io_detail::try_parse([&]
1683 {
1684 return read_rle(in);
1685 }, "try_read_rle");
1686}
1687
1703{
1704 return ca_io_detail::try_parse([&]
1705 {
1706 return read_rle_string(s);
1707 }, "try_read_rle_string");
1708}
1709
1725{
1726 return ca_io_detail::try_parse([&]
1727 {
1728 return read_plaintext(in);
1729 }, "try_read_plaintext");
1730}
1731
1747{
1748 return ca_io_detail::try_parse([&]
1749 {
1750 return read_life_105(in);
1751 }, "try_read_life_105");
1752}
1753
1769{
1770 return ca_io_detail::try_parse([&]
1771 {
1772 return read_life_106(in);
1773 }, "try_read_life_106");
1774}
1775
1792template <class T>
1794 std::istream &in,
1795 const CSV_Options &opts = {})
1796{
1797 return ca_io_detail::try_parse([&]
1798 {
1799 return read_csv_snapshot<T>(in, opts);
1800 }, "try_read_csv_snapshot");
1801}
1802
1803} // namespace CA
1804} // namespace Aleph
1805
1806#endif // CA_IO_H
C++20/23 compatibility layer: feature detection + Aleph:: polyfills.
Exception handling system with formatted messages for Aleph-w.
#define ah_domain_error_unless(C)
Throws std::domain_error if condition does NOT hold.
Definition ah-errors.H:543
#define ah_out_of_range_error_if(C)
Throws std::out_of_range if condition holds.
Definition ah-errors.H:584
#define ah_overflow_error_if(C)
Throws std::overflow_error if condition holds.
Definition ah-errors.H:468
#define ah_domain_error_if(C)
Throws std::domain_error if condition holds.
Definition ah-errors.H:527
#define ah_runtime_error_if(C)
Throws std::runtime_error if condition holds.
Definition ah-errors.H:271
static int cell(const aleph_ca_engine_t *e, size_t r, size_t c)
Definition c_abi_smoke.c:53
size_t size_t int32_t value
Definition ca-c-api.h:116
size_t size_t int32_t * out
Definition ca-c-api.h:120
size_t row
Definition ca-c-api.h:115
size_t * rows
Definition ca-c-api.h:112
size_t size_t col
Definition ca-c-api.h:116
size_t cols
Definition ca-c-api.h:105
Common typedefs and tag types for the Cellular Automata module.
Simple dynamic array with automatic resizing and functional operations.
Definition tpl_array.H:138
constexpr size_t size() const noexcept
Return the number of elements stored in the stack.
Definition tpl_array.H:365
void clear() noexcept
Empties the container.
Definition tpl_array.H:350
T & append(const T &data)
Append a copy of data
Definition tpl_array.H:250
void reserve(size_t cap)
Reserves cap cells into the array.
Definition tpl_array.H:320
Streaming JSON writer for CA trajectories.
Definition ca-io.H:1546
JSON_Frame_Stream_Writer(const JSON_Frame_Stream_Writer &)=delete
void close()
Finish the stream.
Definition ca-io.H:1622
JSON_Frame_Stream_Writer(std::ostream &out, JSON_Write_Options opts={})
Start a JSON frame stream.
Definition ca-io.H:1561
~JSON_Frame_Stream_Writer()
Finish the JSON document if it is still open.
Definition ca-io.H:1576
void accept(std::size_t step, const Lattice &frame)
Append one frame to the stream.
Definition ca-io.H:1600
Lattice that adds boundary-aware access on top of a storage.
void set(const coord_type &c, const state_type &v)
Strict write: throws if c is out of range.
typename Storage::state_type state_type
typename Storage::coord_type coord_type
static constexpr std::size_t rank
ca_size_t size() const noexcept
void fill(const state_type &value)
Set every cell to value.
state_type at(const coord_type &c) const
Strict access: throws if c is out of range.
Minimal std::expected-style result type for C++20.
Storage + topology that carries the cell values.
size_t blossom_maximum_cardinality_matching(const GT &g, DynDlist< typename GT::Arc * > &matching, SA sa=SA())
Alias of compute_maximum_cardinality_general_matching().
Definition Blossom.H:466
static mpfr_t y
Definition mpfr_mul_d.c:3
std::string trim_copy(const std::string &s)
Definition ca-io.H:270
std::string rle_token(ca_size_t n, char glyph)
Definition ca-io.H:386
auto try_parse(F &&parse, const char *context) -> expected< std::invoke_result_t< F & >, std::string >
Invoke a parser, translating any thrown exception into the error channel of expected.
Definition ca-io.H:1647
void ensure_stream_ok(const std::ostream &out, const char *fn)
Definition ca-io.H:539
bool blank(const std::string &s)
Definition ca-io.H:288
T parse_scalar(const std::string &token, const char *format)
Definition ca-io.H:479
ca_index_t parse_index_token(const std::string &s, const char *what)
Definition ca-io.H:313
Array< std::string > split_comment_lines(const std::string &comment)
Definition ca-io.H:339
void finalise_absolute_pattern(Binary_Cell_Pattern &pattern, const Array< Coord_Vec< 2 > > &absolute, const bool have_extent, const ca_index_t min_row, const ca_index_t min_col, const ca_index_t max_row, const ca_index_t max_col)
Definition ca-io.H:450
ca_size_t parse_json_size_field(const std::string &s, const std::string &key)
Definition ca-io.H:520
void write_text_scalar(std::ostream &out, const T &value)
Definition ca-io.H:545
std::string header_value(const std::string &line, const std::string &key)
Definition ca-io.H:434
std::string json_escape(const std::string &s)
Definition ca-io.H:351
ca_size_t parse_size_token(const std::string &s, const char *what)
Definition ca-io.H:296
T parse_json_number(const std::string &s, std::size_t &pos)
Definition ca-io.H:493
std::string lower_copy(std::string s)
Definition ca-io.H:281
void require_rank2(const char *fn)
Definition ca-io.H:263
void append_rle_token(std::string &body, ca_size_t n, char glyph)
Definition ca-io.H:397
void write_wrapped_rle_body(std::ostream &out, const std::string &body, const std::size_t line_width)
Definition ca-io.H:402
expected< Binary_Cell_Pattern, std::string > try_read_life_106(std::istream &in)
Non-throwing variant of read_life_106.
Definition ca-io.H:1768
Lattice make_lattice_from_snapshot(const Grid_Snapshot< T > &snap)
Construct a lattice from a numeric snapshot.
Definition ca-io.H:1283
void write_pgm(std::ostream &out, const Lattice &frame, Mapper &&mapper, const NetPBM_Write_Options &opts={})
Write a binary PGM (P5) image.
Definition ca-io.H:1444
void write_life_105(std::ostream &out, const Lattice &frame, const std::string &comment={}, const typename Lattice::state_type &dead=typename Lattice::state_type{})
Write a Life 1.05 file.
Definition ca-io.H:1072
void write_plaintext(std::ostream &out, const Lattice &frame, const Plaintext_Write_Options &opts={}, const typename Lattice::state_type &dead=typename Lattice::state_type{})
Write a Life plaintext (.cells) pattern.
Definition ca-io.H:867
Binary_Cell_Pattern read_plaintext_string(const std::string &s)
Read a Life plaintext pattern from a string.
Definition ca-io.H:961
void write_life_106(std::ostream &out, const Lattice &frame, const typename Lattice::state_type &dead=typename Lattice::state_type{})
Write a Life 1.06 file.
Definition ca-io.H:981
std::ptrdiff_t ca_index_t
Signed coordinate component used by lattices and neighborhoods.
Definition ca-traits.H:60
std::array< ca_index_t, N > Coord_Vec
Default coordinate vector.
Definition ca-traits.H:69
expected< Binary_Cell_Pattern, std::string > try_read_plaintext(std::istream &in)
Non-throwing variant of read_plaintext.
Definition ca-io.H:1724
std::string write_plaintext_string(const Lattice &frame, const Plaintext_Write_Options &opts={}, const typename Lattice::state_type &dead=typename Lattice::state_type{})
Return a Life plaintext document.
Definition ca-io.H:897
void write_rle(std::ostream &out, const Lattice &frame, const RLE_Write_Options &opts={}, const typename Lattice::state_type &dead=typename Lattice::state_type{})
Write a 2D frame in Conway RLE format.
Definition ca-io.H:575
void write_ppm(std::ostream &out, const Lattice &frame, Mapper &&mapper, const NetPBM_Write_Options &opts={})
Write a binary PPM (P6) image.
Definition ca-io.H:1496
Binary_Cell_Pattern read_life_105(std::istream &in)
Read a Life 1.05 pattern.
Definition ca-io.H:1106
void write_csv(std::ostream &out, const Lattice &frame, const CSV_Options &opts={})
Write a numeric CSV snapshot.
Definition ca-io.H:1207
std::string write_rle_string(const Lattice &frame, const RLE_Write_Options &opts={}, const typename Lattice::state_type &dead=typename Lattice::state_type{})
Return a frame encoded as Conway RLE.
Definition ca-io.H:668
RLE_Pattern read_rle(std::istream &in)
Read a Conway RLE pattern.
Definition ca-io.H:691
Grid_Snapshot< T > read_json_snapshot(std::istream &in)
Read a JSON numeric snapshot produced by write_json.
Definition ca-io.H:1370
RLE_Pattern read_rle_string(const std::string &s)
Read a Conway RLE pattern from a string.
Definition ca-io.H:785
Binary_Cell_Pattern read_life_106(std::istream &in)
Read a Life 1.06 pattern.
Definition ca-io.H:1007
Binary_Cell_Pattern read_plaintext(std::istream &in)
Read a Life plaintext (.cells) pattern.
Definition ca-io.H:919
void write_json(std::ostream &out, const Lattice &frame, const JSON_Write_Options &opts={})
Write a JSON snapshot.
Definition ca-io.H:1314
std::size_t ca_size_t
Unsigned size component used for extents and counts.
Definition ca-traits.H:63
expected< Grid_Snapshot< T >, std::string > try_read_csv_snapshot(std::istream &in, const CSV_Options &opts={})
Non-throwing variant of read_csv_snapshot.
Definition ca-io.H:1793
Lattice make_lattice_from_pattern(const Binary_Cell_Pattern &pattern, const typename Lattice::state_type &alive=static_cast< typename Lattice::state_type >(1), const typename Lattice::state_type &dead=typename Lattice::state_type{})
Construct a lattice with the exact dimensions of a pattern.
Definition ca-io.H:843
expected< RLE_Pattern, std::string > try_read_rle_string(const std::string &s)
Non-throwing variant of read_rle_string.
Definition ca-io.H:1702
void load_binary_pattern(const Binary_Cell_Pattern &pattern, Lattice &frame, const typename Lattice::state_type &alive=static_cast< typename Lattice::state_type >(1), const typename Lattice::state_type &dead=typename Lattice::state_type{}, const ca_index_t origin_row=0, const ca_index_t origin_col=0)
Fill a lattice from a binary pattern.
Definition ca-io.H:809
expected< RLE_Pattern, std::string > try_read_rle(std::istream &in)
Non-throwing variant of read_rle.
Definition ca-io.H:1680
Grid_Snapshot< T > read_csv_snapshot(std::istream &in, const CSV_Options &opts={})
Read a numeric CSV snapshot.
Definition ca-io.H:1242
expected< Binary_Cell_Pattern, std::string > try_read_life_105(std::istream &in)
Non-throwing variant of read_life_105.
Definition ca-io.H:1746
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
bool eq(const C1 &c1, const C2 &c2, Eq e=Eq())
Check equality of two containers using a predicate.
and
Check uniqueness with explicit hash + equality functors.
std::decay_t< typename HeadC::Item_Type > T
Definition ah-zip.H:105
STL namespace.
Binary pattern shared by RLE, plaintext and Life formats.
Definition ca-io.H:104
std::string name
optional pattern name
Definition ca-io.H:110
ca_index_t origin_row
original row offset before normalisation
Definition ca-io.H:107
ca_size_t height
number of rows
Definition ca-io.H:106
ca_size_t width
number of columns
Definition ca-io.H:105
Array< std::string > comments
format comments without marker prefixes
Definition ca-io.H:111
ca_index_t origin_col
original column offset before normalisation
Definition ca-io.H:108
std::string rule
optional rule string, e.g. B3/S23
Definition ca-io.H:109
Array< Coord_Vec< 2 > > alive
live cells as {row, col}
Definition ca-io.H:112
Default binary grayscale mapper.
Definition ca-io.H:221
std::uint8_t alive_value
grayscale for live cells
Definition ca-io.H:224
std::uint8_t dead_value
grayscale for dead cells
Definition ca-io.H:223
State dead
state considered dead
Definition ca-io.H:222
std::uint8_t operator()(const State &v) const
Map a cell to one byte.
Definition ca-io.H:230
Default binary RGB mapper.
Definition ca-io.H:245
RGB8 dead_colour
colour for dead cells
Definition ca-io.H:247
State dead
state considered dead
Definition ca-io.H:246
RGB8 alive_colour
colour for live cells
Definition ca-io.H:248
RGB8 operator()(const State &v) const
Map a cell to RGB.
Definition ca-io.H:254
Options controlling CSV output and input.
Definition ca-io.H:185
char delimiter
separator between cells
Definition ca-io.H:186
bool skip_empty_lines
reader ignores empty/whitespace-only rows
Definition ca-io.H:187
Dense numeric snapshot used by CSV and JSON readers.
Definition ca-io.H:128
ca_size_t width
number of columns
Definition ca-io.H:129
ca_size_t height
number of rows
Definition ca-io.H:130
Array< T > cells
row-major payload
Definition ca-io.H:131
T at(ca_size_t row, ca_size_t col) const
Return the value at (row, col).
Definition ca-io.H:142
Options controlling JSON snapshot output.
Definition ca-io.H:196
bool pretty
pretty-print with indentation
Definition ca-io.H:197
std::string rule
optional rule field
Definition ca-io.H:198
Options controlling NetPBM output.
Definition ca-io.H:207
std::uint16_t max_value
NetPBM maximum sample value.
Definition ca-io.H:208
Options controlling Life plaintext output.
Definition ca-io.H:173
char dead
glyph for dead cells
Definition ca-io.H:175
char alive
glyph for live cells
Definition ca-io.H:174
std::string comment
optional ! comments, split on newlines
Definition ca-io.H:176
RGB byte triplet used by PPM exporters.
Definition ca-io.H:85
std::uint8_t g
green channel
Definition ca-io.H:87
friend constexpr bool operator==(RGB8, RGB8) noexcept=default
std::uint8_t b
blue channel
Definition ca-io.H:88
std::uint8_t r
red channel
Definition ca-io.H:86
Options controlling Conway RLE output.
Definition ca-io.H:159
std::string rule
rule field emitted in the header
Definition ca-io.H:160
std::size_t line_width
body wrap width; 0 disables wrapping
Definition ca-io.H:164
std::string comment
optional #C lines, split on newlines
Definition ca-io.H:162
bool trim_dead_border
crop to the live-cell bounding box
Definition ca-io.H:163
std::string name
optional #N line
Definition ca-io.H:161
FooMap m(5, fst_unit_pair_hash, snd_unit_pair_hash)
static int * k
gsl_rng * r
Dynamic array container with automatic resizing.
C++20 concepts for the Cellular Automata module.