116116mod error;
117117#[ cfg( feature = "fzn" ) ]
118118mod fzn;
119+ pub mod helpers;
119120#[ cfg( any( feature = "fzn" , feature = "serde" ) ) ]
120121mod intermediate;
121122#[ cfg( feature = "serde" ) ]
122123mod serde_impl;
123124
124125use std:: {
126+ cmp:: Ordering ,
125127 collections:: HashSet ,
126128 fmt:: { Debug , Display } ,
129+ hash:: { Hash , Hasher } ,
127130 sync:: { Arc , Weak } ,
128131} ;
129132
@@ -132,6 +135,7 @@ pub use rangelist::RangeList;
132135use serde:: { Deserializer , Serialize } ;
133136
134137pub use crate :: error:: { FznParseError , LinkError } ;
138+ use crate :: helpers:: ArcKey ;
135139
136140/// Additional information provided in a standardized format for declarations,
137141/// constraints, or solve objectives
@@ -381,9 +385,15 @@ pub enum Method<Identifier = String> {
381385/// It is possible for an [`Array`] to exist without an `name` attribute, if a
382386/// reference to such an [`Array`] is used as a [`NamedRef`], serialization can
383387/// panic.
384- #[ derive( Clone , PartialEq , Debug ) ]
388+ ///
389+ /// [`NamedRef`] compares, hashes, and orders by the referenced declaration
390+ /// name. As a consequence, two values that refer to different allocations but
391+ /// expose the same name are considered equal, and a variable and array with
392+ /// the same name are also treated as equal for these trait implementations.
393+ /// Note that this cannot occur in valid FlatZinc models.
394+ #[ derive( Clone , Debug ) ]
385395pub enum NamedRef < Identifier = String > {
386- /// Reference to a variable
396+ /// Reference to a variable.
387397 Variable ( Arc < Variable < Identifier > > ) ,
388398 /// Reference to an array.
389399 Array ( Arc < Array < Identifier > > ) ,
@@ -441,11 +451,6 @@ pub struct Variable<Identifier = String> {
441451 pub introduced : bool ,
442452}
443453
444- /// Return a unique key for a specific variable or array allocation.
445- fn arc_key < T > ( arc : & Arc < T > ) -> usize {
446- Arc :: as_ptr ( arc) as usize
447- }
448-
449454impl < Identifier : Display > Display for Annotation < Identifier > {
450455 fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
451456 match self {
@@ -613,6 +618,27 @@ impl<Identifier> Array<Identifier> {
613618 . any ( |lit| matches ! ( lit, Literal :: Variable ( _) ) ) ;
614619 ( ty, is_var)
615620 }
621+
622+ /// Converts an array reference into an [`ArcKey`](crate::helpers::ArcKey).
623+ ///
624+ /// This is useful when storing arrays in collections such as
625+ /// [`HashMap`](std::collections::HashMap),
626+ /// [`HashSet`](std::collections::HashSet), and
627+ /// [`BTreeMap`](std::collections::BTreeMap), where the key should identify
628+ /// the specific parsed array object rather than its contents or `name`.
629+ ///
630+ /// The resulting key uses the allocation / pointer identity of this
631+ /// [`Arc`]. Two arrays with the same name and equal contents will
632+ /// therefore compare as different keys if they are stored in different
633+ /// allocations.
634+ ///
635+ /// During FlatZinc parsing and deserialization, this crate guarantees that
636+ /// identical top-level arrays are allocated only once. In those cases,
637+ /// `ArcKey` is a good fit for keying collections by the canonical parsed
638+ /// array object.
639+ pub fn into_key ( self : Arc < Self > ) -> ArcKey < Self > {
640+ ArcKey :: new ( self )
641+ }
616642}
617643
618644impl < Identifier : Display > Display for Constraint < Identifier > {
@@ -706,12 +732,13 @@ impl<Identifier> Default for FlatZinc<Identifier> {
706732
707733impl < Identifier : Display > Display for FlatZinc < Identifier > {
708734 fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
709- let output_map: HashSet < _ > = self . output . iter ( ) . map ( |output| output . arc_key ( ) ) . collect ( ) ;
735+ let output_map: HashSet < _ > = self . output . iter ( ) . collect ( ) ;
710736
711737 for var in & self . variables {
712738 write ! ( f, "var {}" , var. ty) ?;
713739 write ! ( f, ": {}" , var. name) ?;
714- if output_map. contains ( & arc_key ( var) ) {
740+ let name_ref: NamedRef < _ > = Arc :: clone ( var) . into ( ) ;
741+ if output_map. contains ( & name_ref) {
715742 write ! ( f, " ::output_var" ) ?;
716743 }
717744 if var. defined {
@@ -734,7 +761,8 @@ impl<Identifier: Display> Display for FlatZinc<Identifier> {
734761 if is_var { "var " } else { "" } ,
735762 arr. name
736763 ) ?;
737- if output_map. contains ( & arc_key ( arr) ) {
764+ let name_ref: NamedRef < _ > = Arc :: clone ( arr) . into ( ) ;
765+ if output_map. contains ( & name_ref) {
738766 write ! ( f, " ::output_array([1..{}])" , arr. contents. len( ) ) ?;
739767 }
740768 if arr. defined {
@@ -789,14 +817,6 @@ impl<Identifier: Display> Display for Method<Identifier> {
789817}
790818
791819impl < Identifier > NamedRef < Identifier > {
792- /// Return a unique key for the referenced variable or array allocation.
793- fn arc_key ( & self ) -> usize {
794- match self {
795- NamedRef :: Variable ( arc) => Arc :: as_ptr ( arc) as usize ,
796- NamedRef :: Array ( arc) => Arc :: as_ptr ( arc) as usize ,
797- }
798- }
799-
800820 /// Return the identifier of the referenced output target.
801821 pub fn name ( & self ) -> & str {
802822 match self {
@@ -806,6 +826,44 @@ impl<Identifier> NamedRef<Identifier> {
806826 }
807827}
808828
829+ impl < Identifier > Eq for NamedRef < Identifier > { }
830+
831+ impl < Identifier > From < Arc < Array < Identifier > > > for NamedRef < Identifier > {
832+ fn from ( arc : Arc < Array < Identifier > > ) -> Self {
833+ NamedRef :: Array ( arc)
834+ }
835+ }
836+
837+ impl < Identifier > From < Arc < Variable < Identifier > > > for NamedRef < Identifier > {
838+ fn from ( arc : Arc < Variable < Identifier > > ) -> Self {
839+ NamedRef :: Variable ( arc)
840+ }
841+ }
842+
843+ impl < Identifier > Hash for NamedRef < Identifier > {
844+ fn hash < H : Hasher > ( & self , state : & mut H ) {
845+ self . name ( ) . hash ( state) ;
846+ }
847+ }
848+
849+ impl < Identifier > Ord for NamedRef < Identifier > {
850+ fn cmp ( & self , other : & Self ) -> Ordering {
851+ self . name ( ) . cmp ( other. name ( ) )
852+ }
853+ }
854+
855+ impl < Identifier > PartialEq for NamedRef < Identifier > {
856+ fn eq ( & self , other : & Self ) -> bool {
857+ self . name ( ) == other. name ( )
858+ }
859+ }
860+
861+ impl < Identifier > PartialOrd for NamedRef < Identifier > {
862+ fn partial_cmp ( & self , other : & Self ) -> Option < Ordering > {
863+ Some ( self . cmp ( other) )
864+ }
865+ }
866+
809867impl < Identifier > Default for SolveObjective < Identifier > {
810868 fn default ( ) -> Self {
811869 Self {
@@ -850,3 +908,27 @@ impl Display for Type {
850908 }
851909 }
852910}
911+
912+ impl < Identifier > Variable < Identifier > {
913+ /// Converts a variable reference into an
914+ /// [`ArcKey`](crate::helpers::ArcKey).
915+ ///
916+ /// This is useful when storing variables in collections such as
917+ /// [`HashMap`](std::collections::HashMap),
918+ /// [`HashSet`](std::collections::HashSet), and
919+ /// [`BTreeMap`](std::collections::BTreeMap), where the key should identify
920+ /// the specific parsed variable object rather than its fields or `name`.
921+ ///
922+ /// The resulting key uses the allocation / pointer identity of this
923+ /// [`Arc`]. Two variables with the same name and equal fields will
924+ /// therefore compare as different keys if they are stored in different
925+ /// allocations.
926+ ///
927+ /// During FlatZinc parsing and deserialization, this crate guarantees that
928+ /// identical top-level variables are allocated only once. In those cases,
929+ /// `ArcKey` is a good fit for keying collections by the canonical parsed
930+ /// variable object.
931+ pub fn into_key ( self : Arc < Self > ) -> ArcKey < Self > {
932+ ArcKey :: new ( self )
933+ }
934+ }
0 commit comments