@@ -20,9 +20,11 @@ use std::cell::Cell;
2020use std:: cmp:: Ordering ;
2121use std:: hash:: { Hash , Hasher } ;
2222use std:: iter;
23- use std:: ops:: { Add , Deref , RangeInclusive , Sub } ;
23+ use std:: ops:: { Deref , RangeInclusive } ;
2424use std:: rc:: Rc ;
2525
26+ mod capacity;
27+ pub use capacity:: AssetCapacity ;
2628mod pool;
2729pub use pool:: AssetPool ;
2830
@@ -117,155 +119,6 @@ pub enum AssetState {
117119 Candidate ,
118120}
119121
120- /// Capacity of an asset, which may be continuous or a discrete number of indivisible units
121- #[ derive( Clone , PartialEq , Copy , Debug ) ]
122- pub enum AssetCapacity {
123- /// Continuous capacity
124- Continuous ( Capacity ) ,
125- /// Discrete capacity represented by a number of indivisible units
126- /// Stores: (number of units, unit size)
127- Discrete ( u32 , Capacity ) ,
128- }
129-
130- impl Add for AssetCapacity {
131- type Output = Self ;
132-
133- // Add two AssetCapacity values together
134- fn add ( self , rhs : AssetCapacity ) -> Self {
135- match ( self , rhs) {
136- ( AssetCapacity :: Continuous ( cap1) , AssetCapacity :: Continuous ( cap2) ) => {
137- AssetCapacity :: Continuous ( cap1 + cap2)
138- }
139- ( AssetCapacity :: Discrete ( units1, size1) , AssetCapacity :: Discrete ( units2, size2) ) => {
140- Self :: check_same_unit_size ( size1, size2) ;
141- AssetCapacity :: Discrete ( units1 + units2, size1)
142- }
143- _ => panic ! ( "Cannot add different types of AssetCapacity ({self:?} and {rhs:?})" ) ,
144- }
145- }
146- }
147-
148- impl Sub for AssetCapacity {
149- type Output = Self ;
150-
151- // Subtract rhs from self, ensuring that the result is non-negative
152- fn sub ( self , rhs : AssetCapacity ) -> Self {
153- match ( self , rhs) {
154- ( AssetCapacity :: Continuous ( cap1) , AssetCapacity :: Continuous ( cap2) ) => {
155- AssetCapacity :: Continuous ( ( cap1 - cap2) . max ( Capacity ( 0.0 ) ) )
156- }
157- ( AssetCapacity :: Discrete ( units1, size1) , AssetCapacity :: Discrete ( units2, size2) ) => {
158- Self :: check_same_unit_size ( size1, size2) ;
159- AssetCapacity :: Discrete ( units1 - units2. min ( units1) , size1)
160- }
161- _ => panic ! ( "Cannot subtract different types of AssetCapacity ({self:?} and {rhs:?})" ) ,
162- }
163- }
164- }
165-
166- impl Eq for AssetCapacity { }
167-
168- impl PartialOrd for AssetCapacity {
169- fn partial_cmp ( & self , other : & Self ) -> Option < Ordering > {
170- Some ( self . cmp ( other) )
171- }
172- }
173-
174- impl Ord for AssetCapacity {
175- fn cmp ( & self , other : & Self ) -> Ordering {
176- match ( self , other) {
177- ( AssetCapacity :: Continuous ( a) , AssetCapacity :: Continuous ( b) ) => a. total_cmp ( b) ,
178- ( AssetCapacity :: Discrete ( units1, size1) , AssetCapacity :: Discrete ( units2, size2) ) => {
179- Self :: check_same_unit_size ( * size1, * size2) ;
180- units1. cmp ( units2)
181- }
182- _ => panic ! ( "Cannot compare different types of AssetCapacity ({self:?} and {other:?})" ) ,
183- }
184- }
185- }
186-
187- impl AssetCapacity {
188- /// Validates that two discrete capacities have the same unit size.
189- fn check_same_unit_size ( size1 : Capacity , size2 : Capacity ) {
190- assert_eq ! (
191- size1, size2,
192- "Can't perform operation on capacities with different unit sizes ({size1} and {size2})" ,
193- ) ;
194- }
195-
196- /// Create an `AssetCapacity` from a total capacity and optional unit size
197- ///
198- /// If a unit size is provided, the capacity is represented as a discrete number of units,
199- /// calculated as the ceiling of (capacity / `unit_size`). If no unit size is provided, the
200- /// capacity is represented as continuous.
201- pub fn from_capacity ( capacity : Capacity , unit_size : Option < Capacity > ) -> Self {
202- #[ allow( clippy:: cast_possible_truncation, clippy:: cast_sign_loss) ]
203- match unit_size {
204- Some ( size) => {
205- let num_units = ( capacity / size) . value ( ) . ceil ( ) as u32 ;
206- AssetCapacity :: Discrete ( num_units, size)
207- }
208- None => AssetCapacity :: Continuous ( capacity) ,
209- }
210- }
211-
212- /// Create an `AssetCapacity` from a total capacity and optional unit size
213- ///
214- /// If a unit size is provided, the capacity is represented as a discrete number of units,
215- /// calculated as the floor of (capacity / `unit_size`). If no unit size is provided, the
216- /// capacity is represented as continuous.
217- pub fn from_capacity_floor ( capacity : Capacity , unit_size : Option < Capacity > ) -> Self {
218- #[ allow( clippy:: cast_possible_truncation, clippy:: cast_sign_loss) ]
219- match unit_size {
220- Some ( size) => {
221- let num_units = ( capacity / size) . value ( ) . floor ( ) as u32 ;
222- AssetCapacity :: Discrete ( num_units, size)
223- }
224- None => AssetCapacity :: Continuous ( capacity) ,
225- }
226- }
227-
228- /// Returns the total capacity represented by this `AssetCapacity`.
229- pub fn total_capacity ( & self ) -> Capacity {
230- match self {
231- AssetCapacity :: Continuous ( cap) => * cap,
232- AssetCapacity :: Discrete ( units, size) => * size * Dimensionless ( * units as f64 ) ,
233- }
234- }
235-
236- /// Returns the number of units if this is a discrete capacity, or `None` if continuous.
237- pub fn n_units ( & self ) -> Option < u32 > {
238- match self {
239- AssetCapacity :: Continuous ( _) => None ,
240- AssetCapacity :: Discrete ( units, _) => Some ( * units) ,
241- }
242- }
243-
244- /// Asserts that both capacities are the same type (both continuous or both discrete).
245- pub fn assert_same_type ( & self , other : AssetCapacity ) {
246- assert ! (
247- matches!( self , AssetCapacity :: Continuous ( _) )
248- == matches!( other, AssetCapacity :: Continuous ( _) ) ,
249- "Cannot change capacity type"
250- ) ;
251- }
252-
253- /// Applies a limit factor to the capacity, scaling it accordingly.
254- ///
255- /// For discrete capacities, the number of units is scaled by the limit factor and rounded up to
256- /// the nearest integer.
257- pub fn apply_limit_factor ( self , limit_factor : Dimensionless ) -> Self {
258- #[ allow( clippy:: cast_possible_truncation, clippy:: cast_sign_loss) ]
259- match self {
260- AssetCapacity :: Continuous ( cap) => AssetCapacity :: Continuous ( cap * limit_factor) ,
261- AssetCapacity :: Discrete ( units, size) => {
262- let new_units = ( units as f64 * limit_factor. value ( ) ) . ceil ( ) as u32 ;
263- AssetCapacity :: Discrete ( new_units, size)
264- }
265- }
266- }
267- }
268-
269122/// An asset controlled by an agent.
270123#[ derive( Clone , PartialEq ) ]
271124pub struct Asset {
@@ -1388,62 +1241,6 @@ mod tests {
13881241 use rstest:: { fixture, rstest} ;
13891242 use std:: rc:: Rc ;
13901243
1391- #[ rstest]
1392- #[ case:: exact_multiple( Capacity ( 12.0 ) , Some ( Capacity ( 4.0 ) ) , Some ( 3 ) , Capacity ( 12.0 ) ) ]
1393- #[ case:: rounded_up( Capacity ( 11.0 ) , Some ( Capacity ( 4.0 ) ) , Some ( 3 ) , Capacity ( 12.0 ) ) ]
1394- #[ case:: unit_size_greater_than_capacity(
1395- Capacity ( 3.0 ) ,
1396- Some ( Capacity ( 4.0 ) ) ,
1397- Some ( 1 ) ,
1398- Capacity ( 4.0 )
1399- ) ]
1400- #[ case:: continuous( Capacity ( 5.5 ) , None , None , Capacity ( 5.5 ) ) ]
1401- fn from_capacity (
1402- #[ case] capacity : Capacity ,
1403- #[ case] unit_size : Option < Capacity > ,
1404- #[ case] expected_n : Option < u32 > ,
1405- #[ case] expected_total : Capacity ,
1406- ) {
1407- let got = AssetCapacity :: from_capacity ( capacity, unit_size) ;
1408- assert_eq ! ( got. n_units( ) , expected_n) ;
1409- assert_eq ! ( got. total_capacity( ) , expected_total) ;
1410- }
1411-
1412- #[ rstest]
1413- #[ case:: exact_multiple( Capacity ( 12.0 ) , Some ( Capacity ( 4.0 ) ) , Some ( 3 ) , Capacity ( 12.0 ) ) ]
1414- #[ case:: rounded_down( Capacity ( 11.0 ) , Some ( Capacity ( 4.0 ) ) , Some ( 2 ) , Capacity ( 8.0 ) ) ]
1415- #[ case:: unit_size_greater_than_capacity(
1416- Capacity ( 3.0 ) ,
1417- Some ( Capacity ( 4.0 ) ) ,
1418- Some ( 0 ) ,
1419- Capacity ( 0.0 )
1420- ) ]
1421- #[ case:: continuous( Capacity ( 5.5 ) , None , None , Capacity ( 5.5 ) ) ]
1422- fn from_capacity_floor (
1423- #[ case] capacity : Capacity ,
1424- #[ case] unit_size : Option < Capacity > ,
1425- #[ case] expected_n : Option < u32 > ,
1426- #[ case] expected_total : Capacity ,
1427- ) {
1428- let got = AssetCapacity :: from_capacity_floor ( capacity, unit_size) ;
1429- assert_eq ! ( got. n_units( ) , expected_n) ;
1430- assert_eq ! ( got. total_capacity( ) , expected_total) ;
1431- }
1432-
1433- #[ rstest]
1434- #[ case:: round_up( 3u32 , Capacity ( 4.0 ) , Dimensionless ( 0.5 ) , 2u32 ) ]
1435- #[ case:: exact( 3u32 , Capacity ( 4.0 ) , Dimensionless ( 0.33 ) , 1u32 ) ]
1436- fn apply_limit_factor (
1437- #[ case] start_units : u32 ,
1438- #[ case] unit_size : Capacity ,
1439- #[ case] factor : Dimensionless ,
1440- #[ case] expected_units : u32 ,
1441- ) {
1442- let orig = AssetCapacity :: Discrete ( start_units, unit_size) ;
1443- let got = orig. apply_limit_factor ( factor) ;
1444- assert_eq ! ( got, AssetCapacity :: Discrete ( expected_units, unit_size) ) ;
1445- }
1446-
14471244 #[ rstest]
14481245 fn get_input_cost_from_prices_works (
14491246 region_id : RegionID ,
0 commit comments