@@ -1173,8 +1173,18 @@ fn physical_value_methods(methods: &mut MethodsBuilder) {
11731173 this : & PhysicalValue ,
11741174 #[ starlark( require = pos) ] other : Value < ' v > ,
11751175 ) -> starlark:: Result < bool > {
1176- // Thin wrapper around is_in
1177- this. is_in ( other)
1176+ // Check if this fits within other
1177+ let ( other_min, other_max) = extract_bounds ( other, this. unit ) ?;
1178+
1179+ let self_lower = this. value * ( Decimal :: ONE - this. tolerance ) ;
1180+ let self_upper = this. value * ( Decimal :: ONE + this. tolerance ) ;
1181+ let ( self_min, self_max) = if self_lower < self_upper {
1182+ ( self_lower, self_upper)
1183+ } else {
1184+ ( self_upper, self_lower)
1185+ } ;
1186+
1187+ Ok ( self_min >= other_min && self_max <= other_max)
11781188 }
11791189}
11801190
@@ -4057,4 +4067,98 @@ mod tests {
40574067 let result = volts. downcast_ref :: < PhysicalRange > ( ) . unwrap ( ) . is_in ( amps) ;
40584068 assert ! ( result. is_err( ) ) ;
40594069 }
4070+
4071+ #[ test]
4072+ fn test_within_method ( ) {
4073+ use starlark:: environment:: Module ;
4074+ use starlark:: eval:: Evaluator ;
4075+ use starlark:: values:: Heap ;
4076+
4077+ let module = Module :: new ( ) ;
4078+ let heap = module. heap ( ) ;
4079+ let mut eval = Evaluator :: new ( & module) ;
4080+
4081+ // Test case from the regression: 4.7uF ±10% (house part) should fit within 4.7uF requirement
4082+ let house_part = heap. alloc ( physical_value ( 4.7e-6 , 0.1 , PhysicalUnit :: Farads ) ) ; // 4.7uF ±10%
4083+ let requirement = heap. alloc_str ( "4.7uF" ) ; // 4.7uF (no tolerance = 0%)
4084+
4085+ // within() should check if house_part fits within requirement
4086+ let result = eval. eval_function (
4087+ house_part. get_attr ( "within" , heap) . unwrap ( ) . unwrap ( ) ,
4088+ & [ requirement. to_value ( ) ] ,
4089+ & [ ] ,
4090+ ) ;
4091+ assert ! ( result. is_ok( ) ) ;
4092+ assert_eq ! ( result. unwrap( ) . unpack_bool( ) , Some ( false ) ) ; // 10% tolerance doesn't fit in 0% tolerance
4093+
4094+ // Test case: tight tolerance fits within loose tolerance
4095+ let tight = heap. alloc ( physical_value ( 5.5 , 0.01 , PhysicalUnit :: Volts ) ) ; // 5.5V ±1%
4096+ let loose = heap. alloc_str ( "6V 10%" ) ; // 6V ±10% = [5.4V, 6.6V]
4097+
4098+ let result = eval. eval_function (
4099+ tight. get_attr ( "within" , heap) . unwrap ( ) . unwrap ( ) ,
4100+ & [ loose. to_value ( ) ] ,
4101+ & [ ] ,
4102+ ) ;
4103+ assert ! ( result. is_ok( ) ) ;
4104+ assert_eq ! ( result. unwrap( ) . unpack_bool( ) , Some ( true ) ) ; // [5.445, 5.555] fits in [5.4, 6.6]
4105+
4106+ // Test case: loose tolerance doesn't fit within tight tolerance
4107+ let loose_val = heap. alloc ( physical_value ( 6.0 , 0.1 , PhysicalUnit :: Volts ) ) ; // 6V ±10%
4108+ let tight_val = heap. alloc_str ( "5.5V 1%" ) ; // 5.5V ±1%
4109+
4110+ let result = eval. eval_function (
4111+ loose_val. get_attr ( "within" , heap) . unwrap ( ) . unwrap ( ) ,
4112+ & [ tight_val. to_value ( ) ] ,
4113+ & [ ] ,
4114+ ) ;
4115+ assert ! ( result. is_ok( ) ) ;
4116+ assert_eq ! ( result. unwrap( ) . unpack_bool( ) , Some ( false ) ) ; // [5.4, 6.6] doesn't fit in [5.445, 5.555]
4117+ }
4118+
4119+ #[ test]
4120+ fn test_within_vs_is_in_semantics ( ) {
4121+ use starlark:: environment:: Module ;
4122+ use starlark:: eval:: Evaluator ;
4123+ use starlark:: values:: Heap ;
4124+
4125+ let module = Module :: new ( ) ;
4126+ let heap = module. heap ( ) ;
4127+ let mut eval = Evaluator :: new ( & module) ;
4128+
4129+ // Create values: tight = 5.5V ±1%, loose = 6V ±10%
4130+ let tight = heap. alloc ( physical_value ( 5.5 , 0.01 , PhysicalUnit :: Volts ) ) ;
4131+ let loose = heap. alloc ( physical_value ( 6.0 , 0.1 , PhysicalUnit :: Volts ) ) ;
4132+
4133+ // tight.within(loose) should be true (tight fits in loose)
4134+ let within_result = eval
4135+ . eval_function (
4136+ tight. get_attr ( "within" , heap) . unwrap ( ) . unwrap ( ) ,
4137+ & [ loose. to_value ( ) ] ,
4138+ & [ ] ,
4139+ )
4140+ . unwrap ( ) ;
4141+ assert_eq ! ( within_result. unpack_bool( ) , Some ( true ) ) ;
4142+
4143+ // "tight in loose" (Starlark syntax) should also be true
4144+ // This calls loose.is_in(tight), checking if tight is in loose
4145+ let is_in_result = loose. downcast_ref :: < PhysicalValue > ( ) . unwrap ( ) . is_in ( tight) ;
4146+ assert ! ( is_in_result. is_ok( ) ) ;
4147+ assert_eq ! ( is_in_result. unwrap( ) , true ) ;
4148+
4149+ // loose.within(tight) should be false (loose doesn't fit in tight)
4150+ let within_result2 = eval
4151+ . eval_function (
4152+ loose. get_attr ( "within" , heap) . unwrap ( ) . unwrap ( ) ,
4153+ & [ tight. to_value ( ) ] ,
4154+ & [ ] ,
4155+ )
4156+ . unwrap ( ) ;
4157+ assert_eq ! ( within_result2. unpack_bool( ) , Some ( false ) ) ;
4158+
4159+ // tight.is_in(loose) checks if loose is in tight, should be false
4160+ let is_in_result2 = tight. downcast_ref :: < PhysicalValue > ( ) . unwrap ( ) . is_in ( loose) ;
4161+ assert ! ( is_in_result2. is_ok( ) ) ;
4162+ assert_eq ! ( is_in_result2. unwrap( ) , false ) ;
4163+ }
40604164}
0 commit comments