1use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
7pub enum TargetArch {
8 ARMCortexM(CortexMVariant),
10
11 RISCV(RISCVVariant),
13}
14
15#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
17pub enum CortexMVariant {
18 M3,
20
21 M4,
23
24 M4F,
26
27 M7,
29
30 M7DP,
32
33 M33,
35
36 M55,
38}
39
40#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
42pub enum RISCVVariant {
43 RV32I,
45
46 RV32IMAC,
48
49 RV32GC,
51
52 RV64I,
54
55 RV64IMAC,
57
58 RV64GC,
60}
61
62#[derive(Debug, Clone, Serialize, Deserialize)]
64pub struct HardwareCapabilities {
65 pub arch: TargetArch,
67
68 pub has_mpu: bool,
70
71 pub mpu_regions: u8,
73
74 pub has_pmp: bool,
76
77 pub pmp_entries: u8,
79
80 pub has_fpu: bool,
82
83 pub fpu_precision: Option<FPUPrecision>,
85
86 pub has_simd: bool,
88
89 pub simd_level: Option<SIMDLevel>,
91
92 pub xip_capable: bool,
94
95 pub flash_size: u64,
97
98 pub ram_size: u64,
100}
101
102#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
104pub enum FPUPrecision {
105 Single,
107
108 Double,
110}
111
112#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
114pub enum SIMDLevel {
115 Helium,
117
118 RISCVVector,
120
121 WASMSIMD,
123}
124
125impl TargetArch {
126 pub fn target_triple(&self) -> &str {
128 match self {
129 TargetArch::ARMCortexM(variant) => match variant {
130 CortexMVariant::M3 => "thumbv7m-none-eabi",
131 CortexMVariant::M4 | CortexMVariant::M4F => "thumbv7em-none-eabi",
132 CortexMVariant::M7 | CortexMVariant::M7DP => "thumbv7em-none-eabihf",
133 CortexMVariant::M33 => "thumbv8m.main-none-eabi",
134 CortexMVariant::M55 => "thumbv8.1m.main-none-eabi",
135 },
136 TargetArch::RISCV(variant) => match variant {
137 RISCVVariant::RV32I | RISCVVariant::RV32IMAC | RISCVVariant::RV32GC => {
138 "riscv32imac-unknown-none-elf"
139 }
140 RISCVVariant::RV64I | RISCVVariant::RV64IMAC | RISCVVariant::RV64GC => {
141 "riscv64gc-unknown-none-elf"
142 }
143 },
144 }
145 }
146
147 pub fn cpu_name(&self) -> &str {
149 match self {
150 TargetArch::ARMCortexM(variant) => match variant {
151 CortexMVariant::M3 => "cortex-m3",
152 CortexMVariant::M4 | CortexMVariant::M4F => "cortex-m4",
153 CortexMVariant::M7 | CortexMVariant::M7DP => "cortex-m7",
154 CortexMVariant::M33 => "cortex-m33",
155 CortexMVariant::M55 => "cortex-m55",
156 },
157 TargetArch::RISCV(variant) => match variant {
158 RISCVVariant::RV32I => "generic-rv32",
159 RISCVVariant::RV32IMAC | RISCVVariant::RV32GC => "generic-rv32",
160 RISCVVariant::RV64I => "generic-rv64",
161 RISCVVariant::RV64IMAC | RISCVVariant::RV64GC => "generic-rv64",
162 },
163 }
164 }
165
166 pub fn has_hardware_fp(&self) -> bool {
168 match self {
169 TargetArch::ARMCortexM(variant) => matches!(
170 variant,
171 CortexMVariant::M4F
172 | CortexMVariant::M7
173 | CortexMVariant::M7DP
174 | CortexMVariant::M55
175 ),
176 TargetArch::RISCV(variant) => {
177 matches!(variant, RISCVVariant::RV32GC | RISCVVariant::RV64GC)
178 }
179 }
180 }
181}
182
183impl HardwareCapabilities {
184 pub fn cortex_m4f_typical() -> Self {
186 Self {
187 arch: TargetArch::ARMCortexM(CortexMVariant::M4F),
188 has_mpu: true,
189 mpu_regions: 8,
190 has_pmp: false,
191 pmp_entries: 0,
192 has_fpu: true,
193 fpu_precision: Some(FPUPrecision::Single),
194 has_simd: false,
195 simd_level: None,
196 xip_capable: true,
197 flash_size: 1024 * 1024, ram_size: 192 * 1024, }
200 }
201
202 pub fn nrf52840() -> Self {
204 Self {
205 arch: TargetArch::ARMCortexM(CortexMVariant::M4F),
206 has_mpu: true,
207 mpu_regions: 8,
208 has_pmp: false,
209 pmp_entries: 0,
210 has_fpu: true,
211 fpu_precision: Some(FPUPrecision::Single),
212 has_simd: false,
213 simd_level: None,
214 xip_capable: true,
215 flash_size: 1024 * 1024, ram_size: 256 * 1024, }
218 }
219
220 pub fn stm32f407() -> Self {
222 Self {
223 arch: TargetArch::ARMCortexM(CortexMVariant::M4F),
224 has_mpu: true,
225 mpu_regions: 8,
226 has_pmp: false,
227 pmp_entries: 0,
228 has_fpu: true,
229 fpu_precision: Some(FPUPrecision::Single),
230 has_simd: false,
231 simd_level: None,
232 xip_capable: true,
233 flash_size: 1024 * 1024, ram_size: 192 * 1024, }
236 }
237
238 pub fn stm32h743() -> Self {
242 Self {
243 arch: TargetArch::ARMCortexM(CortexMVariant::M7DP),
244 has_mpu: true,
245 mpu_regions: 16,
246 has_pmp: false,
247 pmp_entries: 0,
248 has_fpu: true,
249 fpu_precision: Some(FPUPrecision::Double),
250 has_simd: false,
251 simd_level: None,
252 xip_capable: true,
253 flash_size: 2 * 1024 * 1024, ram_size: 1024 * 1024, }
256 }
257
258 pub fn imxrt1062() -> Self {
265 Self {
266 arch: TargetArch::ARMCortexM(CortexMVariant::M7),
267 has_mpu: true,
268 mpu_regions: 16,
269 has_pmp: false,
270 pmp_entries: 0,
271 has_fpu: true,
272 fpu_precision: Some(FPUPrecision::Single),
273 has_simd: false,
274 simd_level: None,
275 xip_capable: true,
276 flash_size: 8 * 1024 * 1024, ram_size: 1024 * 1024, }
279 }
280}
281
282#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
288pub enum ArchFamily {
289 ArmCortexM,
290 ArmCortexR,
291 ArmCortexA,
292 RiscV,
293}
294
295#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
297pub enum IsaVariant {
298 Thumb,
300 Thumb2,
302 Arm32,
304 AArch64,
306 RiscV32 { extensions: String },
308 RiscV64 { extensions: String },
310}
311
312#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
314pub enum MemProtection {
315 Mpu { regions: u8 },
317 Mmu,
319 Pmp { entries: u8 },
321 SoftwareOnly,
323}
324
325#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
327pub struct TargetSpec {
328 pub family: ArchFamily,
330 pub triple: String,
332 pub isa: IsaVariant,
334 pub mem_protection: MemProtection,
336 pub fpu: Option<FPUPrecision>,
338}
339
340impl CortexMVariant {
345 pub fn has_dsp(&self) -> bool {
348 !matches!(self, Self::M3)
349 }
350
351 pub fn has_single_precision_fpu(&self) -> bool {
353 matches!(self, Self::M4F | Self::M7 | Self::M7DP | Self::M55)
354 }
355
356 pub fn has_double_precision_fpu(&self) -> bool {
358 matches!(self, Self::M7DP)
359 }
360
361 pub fn has_trustzone(&self) -> bool {
363 matches!(self, Self::M33 | Self::M55)
364 }
365
366 pub fn has_helium(&self) -> bool {
368 matches!(self, Self::M55)
369 }
370}
371
372impl TargetSpec {
373 pub fn has_single_precision_fpu(&self) -> bool {
375 matches!(
376 self.fpu,
377 Some(FPUPrecision::Single) | Some(FPUPrecision::Double)
378 )
379 }
380
381 pub fn has_double_precision_fpu(&self) -> bool {
383 matches!(self.fpu, Some(FPUPrecision::Double))
384 }
385}
386
387impl TargetSpec {
388 pub fn cortex_m3() -> Self {
390 Self {
391 family: ArchFamily::ArmCortexM,
392 triple: "thumbv7m-none-eabi".to_string(),
393 isa: IsaVariant::Thumb2,
394 mem_protection: MemProtection::Mpu { regions: 8 },
395 fpu: None,
396 }
397 }
398
399 pub fn cortex_m4() -> Self {
401 Self {
402 family: ArchFamily::ArmCortexM,
403 triple: "thumbv7em-none-eabi".to_string(),
404 isa: IsaVariant::Thumb2,
405 mem_protection: MemProtection::Mpu { regions: 8 },
406 fpu: None,
407 }
408 }
409
410 pub fn cortex_m4f() -> Self {
412 Self {
413 family: ArchFamily::ArmCortexM,
414 triple: "thumbv7em-none-eabihf".to_string(),
415 isa: IsaVariant::Thumb2,
416 mem_protection: MemProtection::Mpu { regions: 8 },
417 fpu: Some(FPUPrecision::Single),
418 }
419 }
420
421 pub fn cortex_m7() -> Self {
423 Self {
424 family: ArchFamily::ArmCortexM,
425 triple: "thumbv7em-none-eabihf".to_string(),
426 isa: IsaVariant::Thumb2,
427 mem_protection: MemProtection::Mpu { regions: 16 },
428 fpu: Some(FPUPrecision::Single),
429 }
430 }
431
432 pub fn cortex_m7dp() -> Self {
434 Self {
435 family: ArchFamily::ArmCortexM,
436 triple: "thumbv7em-none-eabihf".to_string(),
437 isa: IsaVariant::Thumb2,
438 mem_protection: MemProtection::Mpu { regions: 16 },
439 fpu: Some(FPUPrecision::Double),
440 }
441 }
442
443 pub fn cortex_r5() -> Self {
445 Self {
446 family: ArchFamily::ArmCortexR,
447 triple: "armv7r-none-eabihf".to_string(),
448 isa: IsaVariant::Arm32,
449 mem_protection: MemProtection::Mpu { regions: 12 },
450 fpu: None,
451 }
452 }
453
454 pub fn cortex_a53() -> Self {
456 Self {
457 family: ArchFamily::ArmCortexA,
458 triple: "aarch64-none-elf".to_string(),
459 isa: IsaVariant::AArch64,
460 mem_protection: MemProtection::Mmu,
461 fpu: None,
462 }
463 }
464
465 pub fn riscv32(extensions: &str) -> Self {
470 Self {
471 family: ArchFamily::RiscV,
472 triple: format!("riscv32{extensions}-unknown-none-elf"),
473 isa: IsaVariant::RiscV32 {
474 extensions: extensions.to_string(),
475 },
476 mem_protection: MemProtection::Pmp { entries: 16 },
477 fpu: None,
478 }
479 }
480
481 pub fn riscv32imac() -> Self {
482 Self::riscv32("imac")
483 }
484
485 pub fn cortex_m55() -> Self {
487 Self {
488 family: ArchFamily::ArmCortexM,
489 triple: "thumbv8.1m.main-none-eabi".to_string(),
490 isa: IsaVariant::Thumb2,
491 mem_protection: MemProtection::Mpu { regions: 16 },
492 fpu: Some(FPUPrecision::Single),
493 }
494 }
495
496 pub fn from_triple(triple: &str) -> std::result::Result<Self, String> {
498 match triple {
499 "thumbv7m-none-eabi" | "cortex-m3" => Ok(Self::cortex_m3()),
500 "thumbv7em-none-eabi" | "cortex-m4" => Ok(Self::cortex_m4()),
501 "thumbv7em-none-eabihf" | "cortex-m4f" => Ok(Self::cortex_m4f()),
502 "cortex-m7" => Ok(Self::cortex_m7()),
503 "cortex-m7dp" => Ok(Self::cortex_m7dp()),
504 "thumbv8.1m.main-none-eabi" | "cortex-m55" => Ok(Self::cortex_m55()),
505 "armv7r-none-eabihf" | "cortex-r5" => Ok(Self::cortex_r5()),
506 "aarch64-none-elf" | "cortex-a53" => Ok(Self::cortex_a53()),
507 "riscv32imac-unknown-none-elf" | "riscv32imac" | "rv32imac" => {
511 Ok(Self::riscv32("imac"))
512 }
513 "riscv32imc" | "rv32imc" | "esp32c3" => Ok(Self::riscv32("imc")),
514 "riscv32im" | "rv32im" => Ok(Self::riscv32("im")),
515 "riscv32i" | "rv32i" => Ok(Self::riscv32("i")),
516 "riscv32gc" | "rv32gc" => Ok(Self::riscv32("gc")),
517 "riscv32" | "rv32" => Ok(Self::riscv32("imac")),
519 _ => Err(format!(
522 "unknown target triple: {triple}. Supported: \
523 cortex-m3, cortex-m4, cortex-m4f, cortex-m7, cortex-m7dp, \
524 cortex-m55, cortex-r5 (ARM); \
525 rv32imac, rv32imc, rv32im, rv32i, rv32gc, esp32c3, riscv32 \
526 (RISC-V, -b riscv); cortex-a53 (-b aarch64)"
527 )),
528 }
529 }
530
531 pub fn has_fpu(&self) -> bool {
533 self.fpu.is_some()
534 }
535
536 pub fn is_thumb2(&self) -> bool {
538 self.isa == IsaVariant::Thumb2
539 }
540}
541
542#[cfg(test)]
543mod tests {
544 use super::*;
545
546 #[test]
551 fn test_rv32_short_target_names_218() {
552 for (name, ext) in [
553 ("rv32imac", "imac"),
554 ("rv32imc", "imc"),
555 ("rv32im", "im"),
556 ("rv32i", "i"),
557 ("rv32gc", "gc"),
558 ("esp32c3", "imc"), ("riscv32", "imac"), ("riscv32imac", "imac"),
561 ] {
562 let spec = TargetSpec::from_triple(name)
563 .unwrap_or_else(|e| panic!("RV32 target '{name}' must resolve: {e}"));
564 assert_eq!(spec.family, ArchFamily::RiscV, "{name}");
565 assert_eq!(
566 spec.isa,
567 IsaVariant::RiscV32 {
568 extensions: ext.to_string()
569 },
570 "{name}"
571 );
572 }
573 let err = TargetSpec::from_triple("rv99zzz").unwrap_err();
575 assert!(
576 err.contains("rv32imac"),
577 "error should list RV32 targets: {err}"
578 );
579 }
580
581 #[test]
582 fn test_target_spec_from_triple() {
583 let spec = TargetSpec::from_triple("cortex-m4").unwrap();
584 assert_eq!(spec.family, ArchFamily::ArmCortexM);
585 assert!(spec.is_thumb2());
586 assert_eq!(spec.mem_protection, MemProtection::Mpu { regions: 8 });
587 assert!(!spec.has_fpu());
588 }
589
590 #[test]
591 fn test_target_spec_unknown() {
592 assert!(TargetSpec::from_triple("mips-unknown-none").is_err());
593 }
594
595 #[test]
596 fn test_cortex_m3() {
597 let spec = TargetSpec::from_triple("cortex-m3").unwrap();
598 assert_eq!(spec.triple, "thumbv7m-none-eabi");
599 assert!(!spec.has_fpu());
600 }
601
602 #[test]
603 fn test_cortex_m4f() {
604 let spec = TargetSpec::from_triple("cortex-m4f").unwrap();
605 assert_eq!(spec.triple, "thumbv7em-none-eabihf");
606 assert!(spec.has_fpu());
607 assert_eq!(spec.fpu, Some(FPUPrecision::Single));
608 }
609
610 #[test]
611 fn test_cortex_m7dp() {
612 let spec = TargetSpec::from_triple("cortex-m7dp").unwrap();
613 assert!(spec.has_fpu());
614 assert_eq!(spec.fpu, Some(FPUPrecision::Double));
615 assert_eq!(spec.mem_protection, MemProtection::Mpu { regions: 16 });
616 }
617
618 #[test]
619 fn test_cortex_m7_has_fpu() {
620 let spec = TargetSpec::cortex_m7();
621 assert!(spec.has_fpu());
622 assert_eq!(spec.fpu, Some(FPUPrecision::Single));
623 }
624
625 #[test]
630 fn test_cortex_m3_isa_features() {
631 let m3 = CortexMVariant::M3;
632 assert!(!m3.has_dsp(), "M3 should not have DSP");
633 assert!(!m3.has_single_precision_fpu(), "M3 should not have FPU");
634 assert!(
635 !m3.has_double_precision_fpu(),
636 "M3 should not have double FPU"
637 );
638 assert!(!m3.has_trustzone(), "M3 should not have TrustZone");
639 assert!(!m3.has_helium(), "M3 should not have Helium");
640 }
641
642 #[test]
643 fn test_cortex_m4_isa_features() {
644 let m4 = CortexMVariant::M4;
645 assert!(m4.has_dsp(), "M4 should have DSP");
646 assert!(
647 !m4.has_single_precision_fpu(),
648 "M4 (no F) should not have FPU"
649 );
650 assert!(
651 !m4.has_double_precision_fpu(),
652 "M4 should not have double FPU"
653 );
654 }
655
656 #[test]
657 fn test_cortex_m4f_isa_features() {
658 let m4f = CortexMVariant::M4F;
659 assert!(m4f.has_dsp(), "M4F should have DSP");
660 assert!(
661 m4f.has_single_precision_fpu(),
662 "M4F should have single-precision FPU"
663 );
664 assert!(
665 !m4f.has_double_precision_fpu(),
666 "M4F should not have double FPU"
667 );
668 }
669
670 #[test]
671 fn test_cortex_m7dp_isa_features() {
672 let m7dp = CortexMVariant::M7DP;
673 assert!(m7dp.has_dsp(), "M7DP should have DSP");
674 assert!(
675 m7dp.has_single_precision_fpu(),
676 "M7DP should have single-precision FPU"
677 );
678 assert!(
679 m7dp.has_double_precision_fpu(),
680 "M7DP should have double-precision FPU"
681 );
682 }
683
684 #[test]
685 fn test_cortex_m33_isa_features() {
686 let m33 = CortexMVariant::M33;
687 assert!(m33.has_dsp(), "M33 should have DSP");
688 assert!(m33.has_trustzone(), "M33 should have TrustZone");
689 assert!(!m33.has_helium(), "M33 should not have Helium");
690 }
691
692 #[test]
693 fn test_cortex_m55_isa_features() {
694 let m55 = CortexMVariant::M55;
695 assert!(m55.has_dsp(), "M55 should have DSP");
696 assert!(
697 m55.has_single_precision_fpu(),
698 "M55 should have single-precision FPU"
699 );
700 assert!(m55.has_trustzone(), "M55 should have TrustZone");
701 assert!(m55.has_helium(), "M55 should have Helium");
702 }
703
704 #[test]
705 fn test_target_spec_fpu_precision_queries() {
706 let m3 = TargetSpec::cortex_m3();
707 assert!(!m3.has_single_precision_fpu(), "M3 spec has no FPU");
708 assert!(!m3.has_double_precision_fpu(), "M3 spec has no double FPU");
709
710 let m4f = TargetSpec::cortex_m4f();
711 assert!(m4f.has_single_precision_fpu(), "M4F spec has single FPU");
712 assert!(
713 !m4f.has_double_precision_fpu(),
714 "M4F spec has no double FPU"
715 );
716
717 let m7dp = TargetSpec::cortex_m7dp();
718 assert!(m7dp.has_single_precision_fpu(), "M7DP spec has single FPU");
719 assert!(m7dp.has_double_precision_fpu(), "M7DP spec has double FPU");
720 }
721
722 #[test]
723 fn test_cortex_m55_target_spec() {
724 let m55 = TargetSpec::cortex_m55();
725 assert_eq!(m55.family, ArchFamily::ArmCortexM);
726 assert_eq!(m55.triple, "thumbv8.1m.main-none-eabi");
727 assert!(m55.is_thumb2());
728 assert!(m55.has_fpu());
729 assert!(m55.has_single_precision_fpu());
730 assert_eq!(m55.mem_protection, MemProtection::Mpu { regions: 16 });
731 }
732
733 #[test]
734 fn test_cortex_m55_from_triple() {
735 let m55 = TargetSpec::from_triple("cortex-m55").unwrap();
736 assert_eq!(m55.triple, "thumbv8.1m.main-none-eabi");
737 assert!(m55.has_fpu());
738
739 let m55_triple = TargetSpec::from_triple("thumbv8.1m.main-none-eabi").unwrap();
740 assert_eq!(m55_triple.triple, "thumbv8.1m.main-none-eabi");
741 }
742
743 #[test]
748 fn test_imxrt1062_capabilities() {
749 let caps = HardwareCapabilities::imxrt1062();
750 assert_eq!(caps.arch, TargetArch::ARMCortexM(CortexMVariant::M7));
751 assert!(caps.has_mpu, "i.MX RT1062 has an MPU");
752 assert_eq!(caps.mpu_regions, 16, "M7 parts have 16 MPU regions");
753 assert!(!caps.has_pmp, "ARM parts do not have PMP");
754 assert_eq!(caps.pmp_entries, 0);
755 assert!(caps.has_fpu, "i.MX RT1062 has FPU");
756 assert_eq!(
757 caps.fpu_precision,
758 Some(FPUPrecision::Single),
759 "i.MX RT1062 has single-precision FPU"
760 );
761 assert!(!caps.has_simd);
762 assert!(caps.simd_level.is_none());
763 assert!(caps.xip_capable);
764 assert_eq!(caps.flash_size, 8 * 1024 * 1024, "8MB QSPI typical");
765 assert_eq!(caps.ram_size, 1024 * 1024, "1MB OCRAM");
766 }
767
768 #[test]
769 fn test_stm32h743_capabilities() {
770 let caps = HardwareCapabilities::stm32h743();
771 assert_eq!(caps.arch, TargetArch::ARMCortexM(CortexMVariant::M7DP));
772 assert!(caps.has_mpu);
773 assert_eq!(caps.mpu_regions, 16, "STM32H743 has 16 MPU regions");
774 assert!(!caps.has_pmp);
775 assert!(caps.has_fpu);
776 assert_eq!(
777 caps.fpu_precision,
778 Some(FPUPrecision::Double),
779 "STM32H743 has double-precision FPU"
780 );
781 assert_eq!(caps.flash_size, 2 * 1024 * 1024, "2MB Flash");
782 assert_eq!(caps.ram_size, 1024 * 1024, "1MB RAM total");
783 assert!(caps.xip_capable);
784 }
785
786 #[test]
787 fn test_imxrt1062_arch_target_triple() {
788 let caps = HardwareCapabilities::imxrt1062();
790 assert_eq!(caps.arch.target_triple(), "thumbv7em-none-eabihf");
791 assert_eq!(caps.arch.cpu_name(), "cortex-m7");
792 assert!(caps.arch.has_hardware_fp());
793 }
794
795 #[test]
796 fn test_stm32h743_arch_target_triple() {
797 let caps = HardwareCapabilities::stm32h743();
799 assert_eq!(caps.arch.target_triple(), "thumbv7em-none-eabihf");
800 assert_eq!(caps.arch.cpu_name(), "cortex-m7");
801 assert!(caps.arch.has_hardware_fp());
802 }
803
804 #[test]
805 fn test_m7_hardware_capabilities_distinct_from_m4() {
806 let m4 = HardwareCapabilities::nrf52840();
810 let m7 = HardwareCapabilities::imxrt1062();
811 let m7dp = HardwareCapabilities::stm32h743();
812 assert_eq!(m4.mpu_regions, 8);
813 assert_eq!(m7.mpu_regions, 16);
814 assert_eq!(m7dp.mpu_regions, 16);
815 assert_eq!(m4.fpu_precision, Some(FPUPrecision::Single));
817 assert_eq!(m7.fpu_precision, Some(FPUPrecision::Single));
818 assert_eq!(m7dp.fpu_precision, Some(FPUPrecision::Double));
819 }
820}