/ console / types / group / src / parse.rs
parse.rs
  1  // Copyright (c) 2025-2026 ACDC Network
  2  // This file is part of the alphavm library.
  3  //
  4  // Alpha Chain | Delta Chain Protocol
  5  // International Monetary Graphite.
  6  //
  7  // Derived from Aleo (https://aleo.org) and ProvableHQ (https://provable.com).
  8  // They built world-class ZK infrastructure. We installed the EASY button.
  9  // Their cryptography: elegant. Our modifications: bureaucracy-compatible.
 10  // Original brilliance: theirs. Robert's Rules: ours. Bugs: definitely ours.
 11  //
 12  // Original Aleo/ProvableHQ code subject to Apache 2.0 https://www.apache.org/licenses/LICENSE-2.0
 13  // All modifications and new work: CC0 1.0 Universal Public Domain Dedication.
 14  // No rights reserved. No permission required. No warranty. No refunds.
 15  //
 16  // https://creativecommons.org/publicdomain/zero/1.0/
 17  // SPDX-License-Identifier: CC0-1.0
 18  
 19  use super::*;
 20  
 21  impl<E: Environment> Parser for Group<E> {
 22      /// Parses a string into a group circuit.
 23      #[inline]
 24      fn parse(string: &str) -> ParserResult<'_, Self> {
 25          // Parse the optional negative sign '-' from the string.
 26          let (string, negation) = map(opt(tag("-")), |neg: Option<&str>| neg.is_some())(string)?;
 27          // Parse the digits from the string.
 28          let (string, primitive) = recognize(many1(terminated(one_of("0123456789"), many0(char('_')))))(string)?;
 29          // Parse the group from the string.
 30          let (string, group): (&str, Self) = map_res(tag(Self::type_name()), |_| {
 31              let x_coordinate = primitive.replace('_', "").parse()?;
 32              // Recover and negate the group element if the negative sign was present.
 33              match negation {
 34                  true => Ok(-Group::from_x_coordinate(Field::new(x_coordinate))?),
 35                  false => Group::from_x_coordinate(Field::new(x_coordinate)),
 36              }
 37          })(string)?;
 38  
 39          Ok((string, group))
 40      }
 41  }
 42  
 43  impl<E: Environment> FromStr for Group<E> {
 44      type Err = Error;
 45  
 46      /// Parses a string into a group.
 47      #[inline]
 48      fn from_str(string: &str) -> Result<Self> {
 49          match Self::parse(string) {
 50              Ok((remainder, object)) => {
 51                  // Ensure the remainder is empty.
 52                  ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
 53                  // Return the object.
 54                  Ok(object)
 55              }
 56              Err(error) => bail!("Failed to parse string. {error}"),
 57          }
 58      }
 59  }
 60  
 61  impl<E: Environment> Debug for Group<E> {
 62      fn fmt(&self, f: &mut Formatter) -> fmt::Result {
 63          Display::fmt(self, f)
 64      }
 65  }
 66  
 67  impl<E: Environment> Display for Group<E> {
 68      fn fmt(&self, f: &mut Formatter) -> fmt::Result {
 69          write!(f, "{}{}", self.group.to_affine().to_x_coordinate(), Self::type_name())
 70      }
 71  }
 72  
 73  #[cfg(test)]
 74  mod tests {
 75      use super::*;
 76      use alphavm_console_network_environment::Console;
 77  
 78      type CurrentEnvironment = Console;
 79  
 80      const ITERATIONS: u64 = 1_000;
 81  
 82      #[test]
 83      fn test_parse() -> Result<()> {
 84          let rng = &mut TestRng::default();
 85  
 86          // Ensure empty value fails.
 87          assert!(Group::<CurrentEnvironment>::parse(Group::<CurrentEnvironment>::type_name()).is_err());
 88          assert!(Group::<CurrentEnvironment>::parse("").is_err());
 89  
 90          for _ in 0..ITERATIONS {
 91              // Sample a random value.
 92              let group: <CurrentEnvironment as Environment>::Affine = Uniform::rand(rng);
 93  
 94              let expected = format!("{}{}", group.to_x_coordinate(), Group::<CurrentEnvironment>::type_name());
 95              let (remainder, candidate) = Group::<CurrentEnvironment>::parse(&expected).unwrap();
 96              assert_eq!(format!("{expected}"), candidate.to_string());
 97              assert_eq!("", remainder);
 98          }
 99          Ok(())
100      }
101  
102      #[test]
103      fn test_display() {
104          /// Attempts to construct a group from the given element,
105          /// format it in display mode, and recover a group from it.
106          fn check_display<E: Environment>(element: E::Affine) {
107              let candidate = Group::<E>::new(element);
108              assert_eq!(format!("{}{}", element.to_x_coordinate(), Group::<E>::type_name()), format!("{candidate}"));
109  
110              let candidate_recovered = Group::<E>::from_str(&format!("{candidate}")).unwrap();
111              assert_eq!(candidate, candidate_recovered);
112          }
113  
114          let mut rng = TestRng::default();
115  
116          for _ in 0..ITERATIONS {
117              let element = Uniform::rand(&mut rng);
118  
119              check_display::<CurrentEnvironment>(element);
120          }
121      }
122  
123      #[test]
124      fn test_display_zero() {
125          let zero = <CurrentEnvironment as Environment>::Affine::zero();
126  
127          let candidate = Group::<CurrentEnvironment>::new(zero);
128          assert_eq!("0group", &format!("{candidate}"));
129      }
130  }