1use std::fmt::Write as _;
11use std::path::Path;
12
13use ndarray::{Array2, Array3, Axis};
14use num_complex::Complex64;
15
16use crate::error::{IoError, Result};
17use crate::format::write_fortran_zero_scaled_exp;
18
19const GG_DAT_PATH: &str = "gg.dat";
20
21#[derive(Debug, Clone, PartialEq)]
23pub struct GgDatSection {
24 pub section_number: usize,
26 pub values: Array2<Complex64>,
28 pub raw_prefix_lines: Option<Vec<Vec<u8>>>,
34}
35
36impl GgDatSection {
37 #[must_use]
39 pub fn shape(&self) -> (usize, usize) {
40 self.values.dim()
41 }
42
43 #[must_use]
45 pub fn row_count(&self) -> usize {
46 self.values.len_of(Axis(0))
47 }
48
49 #[must_use]
51 pub fn column_count(&self) -> usize {
52 self.values.len_of(Axis(1))
53 }
54}
55
56#[derive(Debug, Clone, PartialEq)]
58pub struct GgDatData {
59 pub sections: Vec<GgDatSection>,
61}
62
63impl GgDatData {
64 #[must_use]
66 pub fn section_count(&self) -> usize {
67 self.sections.len()
68 }
69
70 pub fn to_rixs_handoff(&self) -> Result<GgDatRixsHandoff> {
72 gg_dat_rixs_handoff(self)
73 }
74}
75
76#[derive(Debug, Clone, PartialEq)]
78pub struct GgDatRixsHandoff {
79 pub angular_count: usize,
81 pub energy_count: usize,
83 pub green: Array3<Complex64>,
85}
86
87pub fn parse_gg_dat(text: &str) -> Result<GgDatData> {
89 let lines = text.lines().enumerate().collect::<Vec<_>>();
90 let mut sections = Vec::new();
91 let mut position = 0;
92
93 while position < lines.len() {
94 let (line_number, raw) = lines[position];
95 let line = raw.trim();
96 if line.is_empty() {
97 position += 1;
98 continue;
99 }
100 if !line.starts_with("#SN#") {
101 return parse_error(
102 line_number + 1,
103 format!("expected #SN# section marker, found {line:?}"),
104 );
105 }
106 let section_number = parse_section_number(line_number + 1, line)?;
107 position += 1;
108
109 let (rows, columns) = find_section_shape(&lines, &mut position)?;
110 let values = parse_section_matrix(&lines, &mut position, rows, columns)?;
111 sections.push(GgDatSection {
112 section_number,
113 values,
114 raw_prefix_lines: None,
115 });
116 }
117
118 let data = GgDatData { sections };
119 validate_gg_dat(&data)?;
120 Ok(data)
121}
122
123pub fn parse_gg_bin(text: &str) -> Result<GgDatData> {
125 parse_gg_dat(text)
126}
127
128pub fn parse_gg_dat_bytes(bytes: &[u8]) -> Result<GgDatData> {
130 let text = semantic_gg_text(bytes);
131 let mut data = parse_gg_dat(&text)?;
132 let prefixes = collect_raw_prefix_lines(bytes)?;
133 if prefixes.len() != data.sections.len() {
134 return parse_error(
135 0,
136 format!(
137 "found {} raw section prefix block(s), expected {}",
138 prefixes.len(),
139 data.sections.len()
140 ),
141 );
142 }
143 for (section, prefix) in data.sections.iter_mut().zip(prefixes) {
144 section.raw_prefix_lines = Some(prefix);
145 }
146 Ok(data)
147}
148
149pub fn parse_gg_bin_bytes(bytes: &[u8]) -> Result<GgDatData> {
151 parse_gg_dat_bytes(bytes)
152}
153
154pub fn gg_dat_string(data: &GgDatData) -> Result<String> {
156 validate_gg_dat(data)?;
157 let mut out = String::new();
158 for section in &data.sections {
159 write_canonical_section_prefix(&mut out, section)?;
160 for row in section.values.rows() {
161 for value in row {
162 write_fortran_zero_scaled_exp(&mut out, value.re, 20, 10)?;
163 out.push(' ');
164 write_fortran_zero_scaled_exp(&mut out, value.im, 20, 10)?;
165 }
166 writeln!(out)?;
167 }
168 }
169 Ok(out)
170}
171
172pub fn gg_dat_bytes(data: &GgDatData) -> Result<Vec<u8>> {
175 validate_gg_dat(data)?;
176 let mut out = Vec::new();
177 for section in &data.sections {
178 if let Some(prefix_lines) = §ion.raw_prefix_lines {
179 for line in prefix_lines {
180 out.extend_from_slice(line);
181 out.push(b'\n');
182 }
183 } else {
184 let mut prefix = String::new();
185 write_canonical_section_prefix(&mut prefix, section)?;
186 out.extend_from_slice(prefix.as_bytes());
187 }
188
189 let mut row_text = String::new();
190 for row in section.values.rows() {
191 row_text.clear();
192 for value in row {
193 write_fortran_zero_scaled_exp(&mut row_text, value.re, 20, 10)?;
194 row_text.push(' ');
195 write_fortran_zero_scaled_exp(&mut row_text, value.im, 20, 10)?;
196 }
197 row_text.push('\n');
198 out.extend_from_slice(row_text.as_bytes());
199 }
200 }
201 Ok(out)
202}
203
204pub fn gg_bin_string(data: &GgDatData) -> Result<String> {
206 gg_dat_string(data)
207}
208
209pub fn gg_bin_bytes(data: &GgDatData) -> Result<Vec<u8>> {
211 gg_dat_bytes(data)
212}
213
214fn write_canonical_section_prefix(out: &mut String, section: &GgDatSection) -> Result<()> {
215 let (rows, columns) = section.shape();
216 writeln!(out, "#SN# Section: {:4}", section.section_number)?;
217 writeln!(out, "#DF# This section written in txt.")?;
218 writeln!(out, "#H#")?;
219 writeln!(
220 out,
221 "#DT# 2D complex array with sizes {:4}{:4}",
222 rows, columns
223 )?;
224 writeln!(
225 out,
226 "#H# File is organized as follows: Array(1,i) Array(1,i+1) Array(1,i+2) . . ."
227 )?;
228 writeln!(out, "#H# Array(2,i)")?;
229 writeln!(out, "#H# .")?;
230 writeln!(out, "#H# .")?;
231 writeln!(out, "#H# .")?;
232 Ok(())
233}
234
235pub fn read_gg_dat(path: impl AsRef<Path>) -> Result<GgDatData> {
241 let path = path.as_ref();
242 let bytes = std::fs::read(path).map_err(|source| IoError::io(path, source))?;
243 parse_gg_dat_bytes(&bytes)
244}
245
246pub fn read_gg_bin(path: impl AsRef<Path>) -> Result<GgDatData> {
251 read_gg_dat(path)
252}
253
254pub fn write_gg_dat(path: impl AsRef<Path>, data: &GgDatData) -> Result<()> {
256 let path = path.as_ref();
257 std::fs::write(path, gg_dat_bytes(data)?).map_err(|source| IoError::io(path, source))
258}
259
260pub fn write_gg_bin(path: impl AsRef<Path>, data: &GgDatData) -> Result<()> {
262 write_gg_dat(path, data)
263}
264
265pub fn gg_dat_rixs_handoff(data: &GgDatData) -> Result<GgDatRixsHandoff> {
270 validate_gg_dat(data)?;
271 let first = data
272 .sections
273 .first()
274 .ok_or_else(|| parse_error_value(0, "at least one section is required"))?;
275 let (rows, columns) = first.shape();
276 if rows != columns {
277 return parse_error(
278 first.section_number,
279 format!("RIXS Green-function matrix must be square, got {rows}x{columns}"),
280 );
281 }
282
283 let energy_count = data.section_count();
284 let mut green = Array3::zeros((rows, columns, energy_count));
285 for (energy, section) in data.sections.iter().enumerate() {
286 if section.shape() != (rows, columns) {
287 return parse_error(
288 section.section_number,
289 format!(
290 "RIXS Green-function section shape {:?} does not match first section shape {:?}",
291 section.shape(),
292 (rows, columns)
293 ),
294 );
295 }
296 for row in 0..rows {
297 for column in 0..columns {
298 green[(row, column, energy)] = section.values[(row, column)];
299 }
300 }
301 }
302
303 Ok(GgDatRixsHandoff {
304 angular_count: rows,
305 energy_count,
306 green,
307 })
308}
309
310fn collect_raw_prefix_lines(bytes: &[u8]) -> Result<Vec<Vec<Vec<u8>>>> {
311 let mut prefixes = Vec::new();
312 let mut current = Vec::new();
313 let mut in_prefix = false;
314 let mut in_descriptor = false;
315
316 for raw in bytes.split(|byte| *byte == b'\n') {
317 let line = strip_trailing_cr(raw);
318 let trimmed = trim_ascii(line);
319 if trimmed.is_empty() {
320 if in_prefix {
321 current.push(line.to_vec());
322 }
323 continue;
324 }
325
326 if trimmed.starts_with(b"#SN#") {
327 if in_prefix && !current.is_empty() {
328 return parse_error(0, "section prefix ended before a data row");
329 }
330 current.clear();
331 current.push(line.to_vec());
332 in_prefix = true;
333 in_descriptor = false;
334 continue;
335 }
336
337 if !in_prefix {
338 continue;
339 }
340
341 if in_descriptor {
342 if trimmed.starts_with(b"#H#") {
343 current.push(line.to_vec());
344 in_descriptor = false;
345 continue;
346 }
347 if trimmed.starts_with(b"#SN#")
348 || trimmed.starts_with(b"#DF#")
349 || trimmed.starts_with(b"#DT#")
350 {
351 in_descriptor = false;
352 } else {
353 current.push(line.to_vec());
354 continue;
355 }
356 }
357
358 if is_prefix_line(trimmed) {
359 current.push(line.to_vec());
360 in_descriptor = trimmed.starts_with(b"#DF#");
361 continue;
362 }
363
364 if current.is_empty() {
365 return parse_error(0, "data row appeared before a #SN# section marker");
366 }
367 prefixes.push(std::mem::take(&mut current));
368 in_prefix = false;
369 }
370
371 if in_prefix && !current.is_empty() {
372 return parse_error(0, "section prefix ended before a data row");
373 }
374
375 Ok(prefixes)
376}
377
378fn semantic_gg_text(bytes: &[u8]) -> String {
387 let lines = bytes
388 .split(|byte| *byte == b'\n')
389 .map(strip_trailing_cr)
390 .collect::<Vec<_>>();
391 let mut out = String::new();
392 let mut index = 0;
393
394 while index < lines.len() {
395 let line = lines[index];
396 let trimmed = trim_ascii(line);
397 if trimmed.starts_with(b"#DF#") {
398 out.push_str("#DF#\n");
399 let mut continuation_end = None;
400 let mut candidate_index = index + 1;
401 while candidate_index < lines.len() {
402 let candidate = trim_ascii(lines[candidate_index]);
403 if candidate.starts_with(b"#H#") {
404 continuation_end = Some(candidate_index);
405 break;
406 }
407 if candidate.starts_with(b"#SN#")
408 || candidate.starts_with(b"#DF#")
409 || candidate.starts_with(b"#DT#")
410 {
411 break;
412 }
413 candidate_index += 1;
414 }
415 index = continuation_end.unwrap_or(index + 1);
416 continue;
417 }
418 out.push_str(&String::from_utf8_lossy(line));
419 out.push('\n');
420 index += 1;
421 }
422 out
423}
424
425fn is_prefix_line(line: &[u8]) -> bool {
426 line.starts_with(b"#DF#") || line.starts_with(b"#H#") || line.starts_with(b"#DT#")
427}
428
429fn strip_trailing_cr(line: &[u8]) -> &[u8] {
430 if let Some(rest) = line.strip_suffix(b"\r") {
431 rest
432 } else {
433 line
434 }
435}
436
437fn trim_ascii(bytes: &[u8]) -> &[u8] {
438 let start = match bytes.iter().position(|byte| !byte.is_ascii_whitespace()) {
439 Some(index) => index,
440 None => bytes.len(),
441 };
442 let end = match bytes.iter().rposition(|byte| !byte.is_ascii_whitespace()) {
443 Some(index) => index + 1,
444 None => start,
445 };
446 &bytes[start..end]
447}
448
449fn find_section_shape(lines: &[(usize, &str)], position: &mut usize) -> Result<(usize, usize)> {
450 let mut shape = None;
451 while *position < lines.len() {
452 let (index, raw) = lines[*position];
453 let line_number = index + 1;
454 let line = raw.trim();
455 if line.is_empty() || is_header_line(line) {
456 *position += 1;
457 continue;
458 }
459 if line.starts_with("#SN#") {
460 return parse_error(line_number, "section is missing #DT# shape line");
461 }
462 if line.starts_with("#DT#") {
463 shape = Some(parse_shape_line(line_number, line)?);
464 *position += 1;
465 continue;
466 }
467 if let Some(shape) = shape {
468 return Ok(shape);
469 }
470 return parse_error(
471 line_number,
472 format!("expected #DT# shape line before data, found {line:?}"),
473 );
474 }
475 shape.ok_or_else(|| parse_error_value(0, "section is missing #DT# shape line"))
476}
477
478fn parse_section_matrix(
479 lines: &[(usize, &str)],
480 position: &mut usize,
481 rows: usize,
482 columns: usize,
483) -> Result<Array2<Complex64>> {
484 let mut values = Vec::with_capacity(checked_product(rows, columns)?);
485 for row_index in 0..rows {
486 let (line_number, line) = next_data_line(lines, position, row_index + 1)?;
487 let row = parse_complex_row(line_number, line, columns)?;
488 values.extend(row);
489 }
490 Array2::from_shape_vec((rows, columns), values)
491 .map_err(|source| parse_error_value(0, format!("invalid section matrix shape: {source}")))
492}
493
494fn next_data_line<'a>(
495 lines: &'a [(usize, &'a str)],
496 position: &mut usize,
497 row: usize,
498) -> Result<(usize, &'a str)> {
499 while *position < lines.len() {
500 let (index, raw) = lines[*position];
501 *position += 1;
502 let line_number = index + 1;
503 let line = raw.trim();
504 if line.is_empty() || is_header_line(line) {
505 continue;
506 }
507 if line.starts_with("#SN#") {
508 return parse_error(
509 line_number,
510 format!("section ended before matrix row {row} was read"),
511 );
512 }
513 if let Some(data) = line.strip_prefix("#HD#") {
514 return Ok((line_number, data.trim()));
515 }
516 if line.starts_with('#') {
517 return parse_error(line_number, format!("unexpected marker {line:?} in data"));
518 }
519 return Ok((line_number, line));
520 }
521 parse_error(0, format!("missing matrix row {row}"))
522}
523
524fn parse_complex_row(line_number: usize, line: &str, columns: usize) -> Result<Vec<Complex64>> {
525 let tokens = line.split_whitespace().collect::<Vec<_>>();
526 let expected = checked_product(columns, 2)?;
527 if tokens.len() != expected {
528 return parse_error(
529 line_number,
530 format!(
531 "matrix row has {} token(s), expected {expected}",
532 tokens.len()
533 ),
534 );
535 }
536
537 tokens
538 .chunks_exact(2)
539 .map(|pair| {
540 Ok(Complex64::new(
541 parse_f64(line_number, "real", pair[0])?,
542 parse_f64(line_number, "imaginary", pair[1])?,
543 ))
544 })
545 .collect()
546}
547
548fn parse_section_number(line_number: usize, line: &str) -> Result<usize> {
549 let token = line
550 .split_whitespace()
551 .last()
552 .ok_or_else(|| parse_error_value(line_number, "missing section number"))?;
553 parse_usize(line_number, "section_number", token)
554}
555
556fn parse_shape_line(line_number: usize, line: &str) -> Result<(usize, usize)> {
557 let tokens = line.split_whitespace().collect::<Vec<_>>();
558 let has_supported_type = tokens.iter().any(|token| token.eq_ignore_ascii_case("2D"))
559 && tokens
560 .iter()
561 .any(|token| token.eq_ignore_ascii_case("complex"))
562 && tokens
563 .iter()
564 .any(|token| token.eq_ignore_ascii_case("array"));
565 if !has_supported_type {
566 return parse_error(line_number, "expected 2D complex array shape line");
567 }
568 let rows_token = tokens
569 .get(tokens.len().saturating_sub(2))
570 .ok_or_else(|| parse_error_value(line_number, "missing matrix row count"))?;
571 let columns_token = tokens
572 .last()
573 .ok_or_else(|| parse_error_value(line_number, "missing matrix column count"))?;
574 let rows = parse_usize(line_number, "rows", rows_token)?;
575 let columns = parse_usize(line_number, "columns", columns_token)?;
576 if rows == 0 || columns == 0 {
577 return parse_error(line_number, "matrix dimensions must be positive");
578 }
579 Ok((rows, columns))
580}
581
582fn validate_gg_dat(data: &GgDatData) -> Result<()> {
583 if data.section_count() == 0 {
584 return parse_error(0, "at least one section is required");
585 }
586 for (index, section) in data.sections.iter().enumerate() {
587 let row = index + 1;
588 if section.section_number == 0 {
589 return parse_error(row, "section number must be positive");
590 }
591 if section.row_count() == 0 || section.column_count() == 0 {
592 return parse_error(row, "section matrix dimensions must be positive");
593 }
594 for value in §ion.values {
595 validate_complex("value", *value, row)?;
596 }
597 }
598 Ok(())
599}
600
601fn validate_complex(field: &'static str, value: Complex64, row: usize) -> Result<()> {
602 validate_finite(field, value.re, row)?;
603 validate_finite(field, value.im, row)
604}
605
606fn validate_finite(field: &'static str, value: f64, row: usize) -> Result<()> {
607 if value.is_finite() {
608 Ok(())
609 } else {
610 parse_error(row, format!("{field} must be finite"))
611 }
612}
613
614fn checked_product(rows: usize, columns: usize) -> Result<usize> {
615 rows.checked_mul(columns)
616 .ok_or_else(|| parse_error_value(0, "matrix shape overflows usize"))
617}
618
619fn is_header_line(line: &str) -> bool {
620 line.starts_with("#DF#") || line.starts_with("#H#")
621}
622
623fn parse_f64(line: usize, field: &'static str, token: &str) -> Result<f64> {
624 token
625 .replace(['D', 'd'], "E")
626 .parse::<f64>()
627 .map_err(|_| parse_error_value(line, format!("could not parse {field} from {token:?}")))
628}
629
630fn parse_usize(line: usize, field: &'static str, token: &str) -> Result<usize> {
631 token
632 .parse::<usize>()
633 .map_err(|_| parse_error_value(line, format!("could not parse {field} from {token:?}")))
634}
635
636fn parse_error<T>(line: usize, message: impl Into<String>) -> Result<T> {
637 Err(parse_error_value(line, message))
638}
639
640fn parse_error_value(line: usize, message: impl Into<String>) -> IoError {
641 IoError::Parse {
642 path: GG_DAT_PATH.into(),
643 line,
644 message: message.into(),
645 }
646}
647
648#[cfg(test)]
649mod tests {
650 use super::*;
651
652 #[test]
653 fn parses_gg_dat_sections() -> Result<()> {
654 let parsed = parse_gg_dat(GG_DAT)?;
655 assert_eq!(parsed.section_count(), 2);
656 assert_eq!(parsed.sections[0].section_number, 1);
657 assert_eq!(parsed.sections[0].shape(), (2, 2));
658 assert_eq!(parsed.sections[0].values[(0, 0)], Complex64::new(1.0, -0.5));
659 assert_eq!(
660 parsed.sections[0].values[(1, 1)],
661 Complex64::new(-4.0, 0.75)
662 );
663 assert_eq!(parsed.sections[1].shape(), (1, 2));
664 assert_eq!(parsed.sections[1].values[(0, 1)], Complex64::new(6.0, -6.5));
665
666 let rendered = gg_dat_string(&parsed)?;
667 assert_eq!(parse_gg_dat(&rendered)?, parsed);
668 Ok(())
669 }
670
671 #[test]
672 fn accepts_fortran_d_exponents_and_header_data_prefix() -> Result<()> {
673 let parsed = parse_gg_dat(
674 r#"#SN# Section: 1
675#DT# 2D double complex array with sizes 1 1
676#DT# 2D complex array with sizes 1 1
677#HD# 1.0D+00 -2.5D+00
678"#,
679 )?;
680 assert_eq!(parsed.section_count(), 1);
681 assert_eq!(parsed.sections[0].values[(0, 0)], Complex64::new(1.0, -2.5));
682 Ok(())
683 }
684
685 #[test]
686 fn preserves_non_utf_descriptor_bytes() -> Result<()> {
687 let bytes = b"#SN# Section: 1\n#DF# This section written in \0\xc0\xc2v.\n#H#\n#DT# 2D complex array with sizes 1 1\n#H# File is organized as follows: Array(1,i) Array(1,i+1) Array(1,i+2) . . .\n#H# Array(2,i)\n#H# .\n#H# .\n#H# .\n 0.1000000000E+01 -0.2500000000E+01\n";
688 let parsed = parse_gg_dat_bytes(bytes)?;
689 assert_eq!(parsed.section_count(), 1);
690 assert_eq!(parsed.sections[0].values[(0, 0)], Complex64::new(1.0, -2.5));
691 assert_eq!(gg_dat_bytes(&parsed)?, bytes);
692 Ok(())
693 }
694
695 #[test]
696 fn preserves_descriptor_bytes_with_embedded_line_feed() -> Result<()> {
697 let bytes = b"#SN# Section: 1\n#DF# This section written in \0\xc0\xc2v\n.\n#H#\n#DT# 2D complex array with sizes 1 1\n#H# File is organized as follows: Array(1,i) Array(1,i+1) Array(1,i+2) . . .\n#H# Array(2,i)\n#H# .\n#H# .\n#H# .\n 0.1000000000E+01 -0.2500000000E+01\n";
698 let parsed = parse_gg_dat_bytes(bytes)?;
699 assert_eq!(parsed.section_count(), 1);
700 assert_eq!(parsed.sections[0].values[(0, 0)], Complex64::new(1.0, -2.5));
701 assert_eq!(gg_dat_bytes(&parsed)?, bytes);
702 Ok(())
703 }
704
705 #[test]
706 fn gg_dat_builds_rixs_green_handoff() -> Result<()> {
707 let data = sample_square_gg_dat();
708 let handoff = data.to_rixs_handoff()?;
709
710 assert_eq!(handoff.angular_count, 2);
711 assert_eq!(handoff.energy_count, 2);
712 assert_eq!(handoff.green.dim(), (2, 2, 2));
713 assert_eq!(handoff.green[(0, 0, 0)], Complex64::new(1.0, -1.0));
714 assert_eq!(handoff.green[(1, 0, 1)], Complex64::new(7.0, -7.0));
715 Ok(())
716 }
717
718 #[test]
719 fn gg_dat_rixs_handoff_rejects_non_square_or_mismatched_sections() {
720 let non_square = GgDatData {
721 sections: vec![GgDatSection {
722 section_number: 1,
723 values: Array2::from_shape_vec(
724 (1, 2),
725 vec![Complex64::new(1.0, 0.0), Complex64::new(2.0, 0.0)],
726 )
727 .expect("sample shape"),
728 raw_prefix_lines: None,
729 }],
730 };
731 assert!(gg_dat_rixs_handoff(&non_square).is_err());
732
733 let mut mismatched = sample_square_gg_dat();
734 mismatched.sections.push(GgDatSection {
735 section_number: 3,
736 values: Array2::from_elem((3, 3), Complex64::new(0.0, 0.0)),
737 raw_prefix_lines: None,
738 });
739 assert!(gg_dat_rixs_handoff(&mismatched).is_err());
740 }
741
742 #[test]
743 fn rejects_bad_gg_dat_inputs() {
744 assert!(parse_gg_dat("").is_err());
745 assert!(
746 parse_gg_dat("#SN# Section: 0\n#DT# 2D complex array with sizes 1 1\n1 2\n").is_err()
747 );
748 assert!(parse_gg_dat("#SN# Section: 1\n1 2\n").is_err());
749 assert!(parse_gg_dat("#SN# Section: 1\n#DT# 2D real array with sizes 1 1\n1 2\n").is_err());
750 assert!(
751 parse_gg_dat("#SN# Section: 1\n#DT# 2D complex array with sizes 1 1\n1\n").is_err()
752 );
753 assert!(
754 parse_gg_dat("#SN# Section: 1\n#DT# 2D complex array with sizes 1 1\nNaN 1\n").is_err()
755 );
756 }
757
758 fn sample_square_gg_dat() -> GgDatData {
759 GgDatData {
760 sections: vec![
761 GgDatSection {
762 section_number: 1,
763 values: Array2::from_shape_vec(
764 (2, 2),
765 vec![
766 Complex64::new(1.0, -1.0),
767 Complex64::new(2.0, -2.0),
768 Complex64::new(3.0, -3.0),
769 Complex64::new(4.0, -4.0),
770 ],
771 )
772 .expect("sample shape"),
773 raw_prefix_lines: None,
774 },
775 GgDatSection {
776 section_number: 2,
777 values: Array2::from_shape_vec(
778 (2, 2),
779 vec![
780 Complex64::new(5.0, -5.0),
781 Complex64::new(6.0, -6.0),
782 Complex64::new(7.0, -7.0),
783 Complex64::new(8.0, -8.0),
784 ],
785 )
786 .expect("sample shape"),
787 raw_prefix_lines: None,
788 },
789 ],
790 }
791 }
792
793 const GG_DAT: &str = r#"#SN# Section: 1
794#DF# This section written in txt.
795#H#
796#DT# 2D complex array with sizes 2 2
797#H# File is organized as follows: Array(1,i) Array(1,i+1) Array(1,i+2) . . .
798#H# Array(2,i)
799 1.0000000000E+00 -5.0000000000E-01 2.0000000000E+00 2.5000000000E+00
800 3.0000000000E+00 0.0000000000E+00 -4.0000000000E+00 7.5000000000E-01
801#SN# Section: 2
802#DT# 2D complex array with sizes 1 2
803 5.0000000000E+00 -5.5000000000E+00 6.0000000000E+00 -6.5000000000E+00
804"#;
805}