LCOV - code coverage report
Current view: top level - Core - OOProbabilitySet.m (source / functions) Hit Total Coverage
Test: coverxygen.info Lines: 0 91 0.0 %
Date: 2025-05-28 07:50:54 Functions: 0 0 -

          Line data    Source code
       1           0 : /*
       2             : 
       3             : OOProbabilitySet.m
       4             : 
       5             : 
       6             : Copyright (C) 2008-2013 Jens Ayton
       7             : 
       8             : Permission is hereby granted, free of charge, to any person obtaining a copy
       9             : of this software and associated documentation files (the "Software"), to deal
      10             : in the Software without restriction, including without limitation the rights
      11             : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      12             : copies of the Software, and to permit persons to whom the Software is
      13             : furnished to do so, subject to the following conditions:
      14             : 
      15             : The above copyright notice and this permission notice shall be included in all
      16             : copies or substantial portions of the Software.
      17             : 
      18             : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      19             : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      20             : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      21             : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      22             : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      23             : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
      24             : SOFTWARE.
      25             : 
      26             : 
      27             : IMPLEMENTATION NOTES
      28             : OOProbabilitySet is implemented as a class cluster with two abstract classes,
      29             : two special-case implementations for immutable sets with zero or one object,
      30             : and two general implementations (one mutable and one immutable). The general
      31             : implementations are the only non-trivial ones.
      32             : 
      33             : The general immutable implementation, OOConcreteProbabilitySet, consists of
      34             : two parallel arrays, one of objects and one of cumulative weights. The
      35             : "cumulative weight" for an entry is the sum of its weight and the cumulative
      36             : weight of the entry to the left (i.e., with a lower index), with the implicit
      37             : entry -1 having a cumulative weight of 0. Since weight cannot be negative,
      38             : this means that cumulative weights increase to the right (not strictly
      39             : increasing, though, since weights may be zero). We can thus find an object
      40             : with a given cumulative weight through a binary search.
      41             : 
      42             : OOConcreteMutableProbabilitySet is a naïve implementation using arrays. It
      43             : could be optimized, but isn't expected to be used much except for building
      44             : sets that will then be immutablized.
      45             : 
      46             : */
      47             : 
      48             : #import "OOProbabilitySet.h"
      49             : #import "OOFunctionAttributes.h"
      50             : #import "OOCollectionExtractors.h"
      51             : #import "legacy_random.h"
      52             : 
      53             : 
      54           0 : static NSString * const kObjectsKey = @"objects";
      55           0 : static NSString * const kWeightsKey = @"weights";
      56             : 
      57             : 
      58             : @protocol OOProbabilitySetEnumerable <NSObject>
      59             : 
      60           0 : - (id) privObjectAtIndex:(NSUInteger)index;
      61             : 
      62             : @end
      63             : 
      64             : 
      65             : @interface OOProbabilitySet (OOPrivate)
      66             : 
      67             : // Designated initializer. This must be used by subclasses, since init is overriden for public use.
      68           0 : - (id) initPriv;
      69             : 
      70             : @end
      71             : 
      72             : 
      73           0 : @interface OOEmptyProbabilitySet: OOProbabilitySet
      74             : 
      75           0 : + (OOEmptyProbabilitySet *) singleton OO_RETURNS_RETAINED;
      76             : 
      77             : @end
      78             : 
      79             : 
      80           0 : @interface OOSingleObjectProbabilitySet: OOProbabilitySet
      81             : {
      82             : @private
      83           0 :         id                                      _object;
      84           0 :         float                           _weight;
      85             : }
      86             : 
      87           0 : - (id) initWithObject:(id)object weight:(float)weight;
      88             : 
      89             : @end
      90             : 
      91             : 
      92           0 : @interface OOConcreteProbabilitySet: OOProbabilitySet <OOProbabilitySetEnumerable>
      93             : {
      94             : @private
      95             :         NSUInteger                      _count;
      96           0 :         id                                      *_objects;
      97           0 :         float                           *_cumulativeWeights;    // Each cumulative weight is weight of object at this index + weight of all objects to left.
      98           0 :         float                           _sumOfWeights;
      99           0 : }
     100             : @end
     101             : 
     102             : 
     103             : @interface OOConcreteMutableProbabilitySet: OOMutableProbabilitySet
     104           0 : {
     105             : @private
     106             :         NSMutableArray          *_objects;
     107           0 :         NSMutableArray          *_weights;
     108           0 :         float                           _sumOfWeights;
     109           0 : }
     110             : 
     111             : - (id) initPrivWithObjectArray:(NSMutableArray *)objects weightsArray:(NSMutableArray *)weights sum:(float)sumOfWeights;
     112           0 : 
     113             : @end
     114             : 
     115             : 
     116             : @interface OOProbabilitySetEnumerator: NSEnumerator
     117           0 : {
     118             : @private
     119             :         id                                      _enumerable;
     120           0 :         NSUInteger                      _index;
     121           0 : }
     122             : 
     123             : - (id) initWithEnumerable:(id<OOProbabilitySetEnumerable>)enumerable;
     124           0 : 
     125             : @end
     126             : 
     127             : 
     128             : static void ThrowAbstractionViolationException(id obj)  GCC_ATTR((noreturn));
     129             : 
     130             : 
     131             : @implementation OOProbabilitySet
     132             : 
     133             : // Abstract class just tosses allocations over to concrete class, and throws exception if you try to use it directly.
     134             : 
     135             : + (id) probabilitySet
     136             : {
     137             :         return [[OOEmptyProbabilitySet singleton] autorelease];
     138             : }
     139             : 
     140             : 
     141             : + (id) probabilitySetWithObjects:(id *)objects weights:(float *)weights count:(NSUInteger)count
     142             : {
     143             :         return [[[self alloc] initWithObjects:objects weights:weights count:count] autorelease];
     144             : }
     145             : 
     146             : 
     147             : + (id) probabilitySetWithPropertyListRepresentation:(NSDictionary *)plist
     148             : {
     149             :         return [[[self alloc] initWithPropertyListRepresentation:plist] autorelease];
     150             : }
     151             : 
     152             : 
     153             : - (id) init
     154             : {
     155             :         [self release];
     156             :         return [OOEmptyProbabilitySet singleton];
     157             : }
     158             : 
     159             : 
     160             : - (id) initWithObjects:(id *)objects weights:(float *)weights count:(NSUInteger)count
     161             : {
     162             :         NSZone *zone = [self zone];
     163             :         DESTROY(self);
     164             :         
     165             :         // Zero objects: return empty-set singleton.
     166             :         if (count == 0)  return [OOEmptyProbabilitySet singleton];
     167             :         
     168             :         // If count is not zero and one of the paramters is nil, we've got us a programming error.
     169             :         if (objects == NULL || weights == NULL)
     170             :         {
     171             :                 [NSException raise:NSInvalidArgumentException format:@"Attempt to create %@ with non-zero count but nil objects or weights.", @"OOProbabilitySet"];
     172             :         }
     173             :         
     174             :         // Single object: simple one-object set. Expected to be quite common.
     175             :         if (count == 1)  return [[OOSingleObjectProbabilitySet allocWithZone:zone] initWithObject:objects[0] weight:weights[0]];
     176             :         
     177             :         // Otherwise, use general implementation.
     178             :         return [[OOConcreteProbabilitySet allocWithZone:zone] initWithObjects:objects weights:weights count:count];
     179             : }
     180             : 
     181             : 
     182             : - (id) initWithPropertyListRepresentation:(NSDictionary *)plist
     183             : {
     184             :         NSArray                                 *objects = nil;
     185             :         NSArray                                 *weights = nil;
     186             :         NSUInteger                              i = 0, count = 0;
     187             :         id                                              *rawObjects = NULL;
     188             :         float                                   *rawWeights = NULL;
     189             :         
     190             :         objects = [plist oo_arrayForKey:kObjectsKey];
     191             :         weights = [plist oo_arrayForKey:kWeightsKey];
     192             :         
     193             :         // Validate
     194             :         if (objects == nil || weights == nil)
     195             :         {
     196             :                 [self release];
     197             :                 return nil;
     198             :         }
     199             :         count = [objects count];
     200             :         if (count != [weights count])
     201             :         {
     202             :                 [self release];
     203             :                 return nil;
     204             :         }
     205             :         
     206             :         // Extract contents.
     207             :         rawObjects = malloc(sizeof *rawObjects * count);
     208             :         rawWeights = malloc(sizeof *rawWeights * count);
     209             :         
     210             :         if (rawObjects != NULL || rawWeights != NULL)
     211             :         {
     212             :                 // Extract objects.
     213             :                 [objects getObjects:rawObjects];
     214             :                 
     215             :                 // Extract and convert weights.
     216             :                 for (i = 0; i < count; ++i)
     217             :                 {
     218             :                         rawWeights[i] = fmax([weights oo_floatAtIndex:i], 0.0f);
     219             :                 }
     220             :                 
     221             :                 self = [self initWithObjects:rawObjects weights:rawWeights count:count];
     222             :         }
     223             :         else
     224             :         {
     225             :                 [self release];
     226             :                 self = nil;
     227             :         }
     228             :         
     229             :         // Clean up.
     230             :         free(rawObjects);
     231             :         free(rawWeights);
     232             :         
     233             :         return self;
     234             : }
     235             : 
     236             : 
     237             : - (id) initPriv
     238           0 : {
     239             :         return [super init];
     240             : }
     241             : 
     242             : 
     243             : - (NSString *) descriptionComponents
     244           0 : {
     245             :         return [NSString stringWithFormat:@"count=%lu", [self count]];
     246             : }
     247             : 
     248             : 
     249             : - (NSDictionary *) propertyListRepresentation
     250             : {
     251             :         ThrowAbstractionViolationException(self);
     252             : }
     253             : 
     254             : - (id) randomObject
     255             : {
     256             :         ThrowAbstractionViolationException(self);
     257             : }
     258             : 
     259             : 
     260             : - (float) weightForObject:(id)object
     261             : {
     262             :         ThrowAbstractionViolationException(self);
     263             : }
     264             : 
     265             : 
     266             : - (float) sumOfWeights
     267             : {
     268             :         ThrowAbstractionViolationException(self);
     269             : }
     270             : 
     271             : 
     272             : - (NSUInteger) count
     273             : {
     274             :         ThrowAbstractionViolationException(self);
     275             : }
     276             : 
     277             : 
     278             : - (NSArray *) allObjects
     279             : {
     280             :         ThrowAbstractionViolationException(self);
     281             : }
     282             : 
     283             : 
     284             : - (id) copyWithZone:(NSZone *)zone
     285           0 : {
     286             :         if (zone == [self zone])
     287             :         {
     288             :                 return [self retain];
     289             :         }
     290             :         else
     291             :         {
     292             :                 return [[OOProbabilitySet allocWithZone:zone] initWithPropertyListRepresentation:[self propertyListRepresentation]];
     293             :         }
     294             : }
     295             : 
     296             : 
     297             : - (id) mutableCopyWithZone:(NSZone *)zone
     298           0 : {
     299             :         return [[OOMutableProbabilitySet allocWithZone:zone] initWithPropertyListRepresentation:[self propertyListRepresentation]];
     300             : }
     301             : 
     302             : @end
     303             : 
     304             : 
     305             : @implementation OOProbabilitySet (OOExtendedProbabilitySet)
     306             : 
     307             : - (BOOL) containsObject:(id)object
     308             : {
     309             :         return [self weightForObject:object] >= 0.0f;
     310             : }
     311             : 
     312             : 
     313             : - (NSEnumerator *) objectEnumerator
     314             : {
     315             :         return [[self allObjects] objectEnumerator];
     316             : }
     317             : 
     318             : 
     319             : - (float) probabilityForObject:(id)object
     320           0 : {
     321             :         float weight = [self weightForObject:object];
     322             :         if (weight > 0)  weight /= [self sumOfWeights];
     323             :         
     324             :         return weight;
     325             : }
     326             : 
     327             : @end
     328             : 
     329             : 
     330             : static OOEmptyProbabilitySet *sOOEmptyProbabilitySetSingleton = nil;
     331           0 : 
     332             : @implementation OOEmptyProbabilitySet: OOProbabilitySet
     333             : 
     334             : + (OOEmptyProbabilitySet *) singleton
     335             : {
     336             :         if (sOOEmptyProbabilitySetSingleton == nil)
     337             :         {
     338             :                 sOOEmptyProbabilitySetSingleton = [[self alloc] init];
     339             :         }
     340             :         
     341             :         return sOOEmptyProbabilitySetSingleton;
     342           0 : }
     343             : 
     344             : 
     345             : - (NSDictionary *) propertyListRepresentation
     346           0 : {
     347             :         NSArray *empty = [NSArray array];
     348             :         return [NSDictionary dictionaryWithObjectsAndKeys:empty, kObjectsKey, empty, kWeightsKey, nil];
     349             : }
     350             : 
     351             : 
     352             : - (id) randomObject
     353           0 : {
     354             :         return nil;
     355             : }
     356             : 
     357             : 
     358             : - (float) weightForObject:(id)object
     359           0 : {
     360             :         return -1.0f;
     361             : }
     362             : 
     363             : 
     364             : - (float) sumOfWeights
     365           0 : {
     366             :         return 0.0f;
     367             : }
     368             : 
     369             : 
     370             : - (NSUInteger) count
     371           0 : {
     372             :         return 0;
     373             : }
     374             : 
     375             : 
     376             : - (NSArray *) allObjects
     377           0 : {
     378             :         return [NSArray array];
     379             : }
     380             : 
     381             : 
     382             : - (id) mutableCopyWithZone:(NSZone *)zone
     383           0 : {
     384             :         // A mutable copy of an empty probability set is equivalent to a new empty mutable probability set.
     385             :         return [[OOConcreteMutableProbabilitySet allocWithZone:zone] initPriv];
     386             : }
     387             : 
     388             : @end
     389             : 
     390             : 
     391             : @implementation OOEmptyProbabilitySet (Singleton)
     392             : 
     393             : /*      Canonical singleton boilerplate.
     394             :         See Cocoa Fundamentals Guide: Creating a Singleton Instance.
     395             :         See also +singleton above.
     396             :         
     397             :         NOTE: assumes single-threaded access.
     398             : */
     399             : 
     400             : + (id) allocWithZone:(NSZone *)inZone
     401           0 : {
     402             :         if (sOOEmptyProbabilitySetSingleton == nil)
     403             :         {
     404             :                 sOOEmptyProbabilitySetSingleton = [super allocWithZone:inZone];
     405             :                 return sOOEmptyProbabilitySetSingleton;
     406             :         }
     407             :         return nil;
     408             : }
     409             : 
     410             : 
     411             : - (id) copyWithZone:(NSZone *)inZone
     412           0 : {
     413             :         return self;
     414             : }
     415             : 
     416             : 
     417             : - (id) retain
     418           0 : {
     419             :         return self;
     420             : }
     421             : 
     422             : 
     423             : - (NSUInteger) retainCount
     424           0 : {
     425             :         return UINT_MAX;
     426             : }
     427             : 
     428             : 
     429             : - (void) release
     430           0 : {}
     431             : 
     432             : 
     433             : - (id) autorelease
     434           0 : {
     435             :         return self;
     436             : }
     437             : 
     438             : @end
     439             : 
     440             : 
     441             : @implementation OOSingleObjectProbabilitySet: OOProbabilitySet
     442             : 
     443             : - (id) initWithObject:(id)object weight:(float)weight
     444             : {
     445             :         if (object == nil)
     446             :         {
     447             :                 [self release];
     448             :                 return nil;
     449           0 :         }
     450             :         
     451             :         if ((self = [super initPriv]))
     452           0 :         {
     453             :                 _object = [object retain];
     454             :                 _weight = fmax(weight, 0.0f);
     455             :         }
     456             :         
     457             :         return self;
     458           0 : }
     459             : 
     460             : 
     461             : - (void) dealloc
     462           0 : {
     463             :         [_object release];
     464             :         
     465             :         [super dealloc];
     466             : }
     467             : 
     468             : 
     469             : - (NSDictionary *) propertyListRepresentation
     470           0 : {
     471             :         return [NSDictionary dictionaryWithObjectsAndKeys:
     472             :                         [NSArray arrayWithObject:_object], kObjectsKey,
     473             :                         [NSArray arrayWithObject:[NSNumber numberWithFloat:_weight]], kWeightsKey,
     474             :                         nil];
     475             : }
     476             : 
     477             : 
     478             : - (id) randomObject
     479           0 : {
     480             :         return _object;
     481             : }
     482             : 
     483             : 
     484             : - (float) weightForObject:(id)object
     485           0 : {
     486             :         if ([_object isEqual:object])  return _weight;
     487             :         else return -1.0f;
     488             : }
     489             : 
     490             : 
     491             : - (float) sumOfWeights
     492           0 : {
     493             :         return _weight;
     494             : }
     495             : 
     496             : 
     497             : - (NSUInteger) count
     498           0 : {
     499             :         return 1;
     500             : }
     501             : 
     502             : 
     503             : - (NSArray *) allObjects
     504           0 : {
     505             :         return [NSArray arrayWithObject:_object];
     506             : }
     507             : 
     508             : 
     509             : - (id) mutableCopyWithZone:(NSZone *)zone
     510           0 : {
     511             :         return [[OOConcreteMutableProbabilitySet allocWithZone:zone] initWithObjects:&_object weights:&_weight count:1];
     512             : }
     513             : 
     514             : @end
     515             : 
     516             : 
     517             : @implementation OOConcreteProbabilitySet
     518             : 
     519             : - (id) initWithObjects:(id *)objects weights:(float *)weights count:(NSUInteger)count
     520           0 : {
     521             :         NSUInteger                              i = 0;
     522             :         float                                   cuWeight = 0.0f;
     523             :         
     524             :         assert(count > 1 && objects != NULL && weights != NULL);
     525             :         
     526             :         if ((self = [super initPriv]))
     527             :         {
     528             :                 // Allocate arrays
     529             :                 _objects = malloc(sizeof *objects * count);
     530             :                 _cumulativeWeights = malloc(sizeof *_cumulativeWeights * count);
     531             :                 if (_objects == NULL || _cumulativeWeights == NULL)
     532             :                 {
     533             :                         [self release];
     534             :                         return nil;
     535             :                 }
     536             :                 
     537             :                 // Fill in arrays, retain objects, add up weights.
     538             :                 for (i = 0; i != count; ++i)
     539             :                 {
     540             :                         _objects[i] = [objects[i] retain];
     541             :                         cuWeight += weights[i];
     542             :                         _cumulativeWeights[i] = cuWeight;
     543             :                 }
     544             :                 _count = count;
     545             :                 _sumOfWeights = cuWeight;
     546             :         }
     547             :         
     548             :         return self;
     549             : }
     550             : 
     551             : 
     552             : - (void) dealloc
     553           0 : {
     554             :         NSUInteger                              i = 0;
     555             :         
     556             :         if (_objects != NULL)
     557             :         {
     558             :                 for (i = 0; i < _count; ++i)
     559             :                 {
     560             :                         [_objects[i] release];
     561             :                 }
     562             :                 free(_objects);
     563             :                 _objects = NULL;
     564             :         }
     565             :         
     566             :         if (_cumulativeWeights != NULL)
     567             :         {
     568             :                 free(_cumulativeWeights);
     569             :                 _cumulativeWeights = NULL;
     570             :         }
     571             :         
     572             :         [super dealloc];
     573             : }
     574             : 
     575             : 
     576             : - (NSDictionary *) propertyListRepresentation
     577           0 : {
     578             :         NSArray                                 *objects = nil;
     579             :         NSMutableArray                  *weights = nil;
     580             :         float                                   cuWeight = 0.0f, sum = 0.0f;
     581             :         NSUInteger                              i = 0;
     582             :         
     583             :         objects = [NSArray arrayWithObjects:_objects count:_count];
     584             :         weights = [NSMutableArray arrayWithCapacity:_count];
     585             :         for (i = 0; i < _count; ++i)
     586             :         {
     587             :                 cuWeight = _cumulativeWeights[i];
     588             :                 [weights oo_addFloat:cuWeight - sum];
     589             :                 sum = cuWeight;
     590             :         }
     591             :         
     592             :         return [NSDictionary dictionaryWithObjectsAndKeys:
     593             :                         objects, kObjectsKey,
     594             :                         [[weights copy] autorelease], kWeightsKey,
     595             :                         nil];
     596             : }
     597             : 
     598             : - (NSUInteger) count
     599           0 : {
     600             :         return _count;
     601             : }
     602             : 
     603             : 
     604             : - (id) privObjectForWeight:(float)target
     605           0 : {
     606             :         /*      Select an object at random. This is a binary search in the cumulative
     607             :                 weights array. Since weights of zero are allowed, there may be several
     608             :                 objects with the same cumulative weight, in which case we select the
     609             :                 leftmost, i.e. the one where the delta is non-zero.
     610             :         */
     611             :         
     612             :         NSUInteger                                      low = 0, high = _count - 1, idx = 0;
     613             :         float                                           weight = 0.0f;
     614             :         
     615             :         while (low < high)
     616             :         {
     617             :                 idx = (low + high) / 2;
     618             :                 weight = _cumulativeWeights[idx];
     619             :                 if (weight > target)
     620             :                 {
     621             :                         if (EXPECT_NOT(idx == 0))  break;
     622             :                         high = idx - 1;
     623             :                 }
     624             :                 else if (weight < target)  low = idx + 1;
     625             :                 else break;
     626             :         }
     627             :         
     628             :         if (weight > target)
     629             :         {
     630             :                 while (idx > 0 && _cumulativeWeights[idx - 1] >= target)  --idx;
     631             :         }
     632             :         else
     633             :         {
     634             :                 while (idx < (_count - 1) && _cumulativeWeights[idx] < target)  ++idx;
     635             :         }
     636             :         
     637             :         assert(idx < _count);
     638             :         id result = _objects[idx];
     639             :         return result;
     640             : }
     641             : 
     642             : 
     643             : - (id) randomObject
     644           0 : {
     645             :         if (_sumOfWeights <= 0.0f)  return nil;
     646             :         return [self privObjectForWeight:randf() * _sumOfWeights];
     647             : }
     648             : 
     649             : 
     650             : - (float) weightForObject:(id)object
     651           0 : {
     652             :         NSUInteger                                      i;
     653             :         
     654             :         // Can't have nil in collection.
     655             :         if (object == nil)  return -1.0f;
     656             :         
     657             :         // Perform linear search, then get weight by subtracting cumulative weight from cumulative weight to left.
     658             :         for (i = 0; i < _count; ++i)
     659             :         {
     660             :                 if ([_objects[i] isEqual:object])
     661             :                 {
     662             :                         float leftWeight = (i != 0) ? _cumulativeWeights[i - 1] : 0.0f;
     663             :                         return _cumulativeWeights[i] - leftWeight;
     664             :                 }
     665             :         }
     666             :         
     667             :         // If we got here, object not found.
     668             :         return -1.0f;
     669             : }
     670             : 
     671             : 
     672             : - (float) sumOfWeights
     673           0 : {
     674             :         return _sumOfWeights;
     675             : }
     676             : 
     677             : 
     678             : - (NSArray *) allObjects
     679           0 : {
     680             :         return [NSArray arrayWithObjects:_objects count:_count];
     681             : }
     682             : 
     683             : 
     684             : - (NSEnumerator *) objectEnumerator
     685           0 : {
     686             :         return [[[OOProbabilitySetEnumerator alloc] initWithEnumerable:self] autorelease];
     687             : }
     688             : 
     689             : 
     690             : - (id) privObjectAtIndex:(NSUInteger)index
     691           0 : {
     692             :         return (index < _count) ? _objects[index] : nil;
     693             : }
     694             : 
     695             : 
     696             : - (id) mutableCopyWithZone:(NSZone *)zone
     697           0 : {
     698             :         id                                              result = nil;
     699             :         float                                   *weights = NULL;
     700             :         NSUInteger                              i = 0;
     701             :         float                                   weight = 0.0f, sum = 0.0f;
     702             :         
     703             :         // Convert cumulative weights to "plain" weights.
     704             :         weights = malloc(sizeof *weights * _count);
     705             :         if (weights == NULL)  return nil;
     706             :         
     707             :         for (i = 0; i < _count; ++i)
     708             :         {
     709             :                 weight = _cumulativeWeights[i];
     710             :                 weights[i] = weight - sum;
     711             :                 sum += weights[i];
     712             :         }
     713             :         
     714             :         result = [[OOConcreteMutableProbabilitySet allocWithZone:zone] initWithObjects:_objects weights:weights count:_count];
     715             :         free(weights);
     716             :         
     717             :         return result;
     718             : }
     719             : 
     720             : @end
     721             : 
     722             : 
     723             : @implementation OOMutableProbabilitySet
     724             : 
     725             : + (id) probabilitySet
     726           0 : {
     727             :         return [[[OOConcreteMutableProbabilitySet alloc] initPriv] autorelease];
     728             : }
     729             : 
     730             : 
     731             : - (id) init
     732           0 : {
     733             :         NSZone *zone = [self zone];
     734             :         [self release];
     735             :         return [[OOConcreteMutableProbabilitySet allocWithZone:zone] initPriv];
     736             : }
     737             : 
     738             : 
     739             : - (id) initWithObjects:(id *)objects weights:(float *)weights count:(NSUInteger)count
     740           0 : {
     741             :         NSZone *zone = [self zone];
     742             :         [self release];
     743             :         return [[OOConcreteMutableProbabilitySet allocWithZone:zone] initWithObjects:objects weights:weights count:count];
     744             : }
     745             : 
     746             : 
     747             : - (id) initWithPropertyListRepresentation:(NSDictionary *)plist
     748           0 : {
     749             :         NSZone *zone = [self zone];
     750             :         [self release];
     751             :         return [[OOConcreteMutableProbabilitySet allocWithZone:zone] initWithPropertyListRepresentation:plist];
     752             : }
     753             : 
     754             : 
     755             : - (id) copyWithZone:(NSZone *)zone
     756           0 : {
     757             :         return [[OOProbabilitySet allocWithZone:zone] initWithPropertyListRepresentation:[self propertyListRepresentation]];
     758             : }
     759             : 
     760             : 
     761             : - (void) setWeight:(float)weight forObject:(id)object
     762             : {
     763             :         ThrowAbstractionViolationException(self);
     764             : }
     765             : 
     766             : 
     767             : - (void) removeObject:(id)object
     768             : {
     769             :         ThrowAbstractionViolationException(self);
     770             : }
     771             : 
     772             : @end
     773             : 
     774             : 
     775             : @implementation OOConcreteMutableProbabilitySet
     776             : 
     777             : - (id) initPriv
     778           0 : {
     779             :         if ((self = [super initPriv]))
     780             :         {
     781             :                 _objects = [[NSMutableArray alloc] init];
     782             :                 _weights = [[NSMutableArray alloc] init];
     783             :         }
     784             :         
     785             :         return self;
     786             : }
     787             : 
     788             : 
     789             : // For internal use by mutableCopy
     790             : - (id) initPrivWithObjectArray:(NSMutableArray *)objects weightsArray:(NSMutableArray *)weights sum:(float)sumOfWeights
     791             : {
     792             :         assert(objects != nil && weights != nil && [objects count] == [weights count] && sumOfWeights >= 0.0f);
     793             :         
     794             :         if ((self = [super initPriv]))
     795             :         {
     796             :                 _objects = [objects retain];
     797             :                 _weights = [weights retain];
     798             :                 _sumOfWeights = sumOfWeights;
     799             :         }
     800             :         
     801             :         return self;
     802             : }
     803             : 
     804             : 
     805             : - (id) initWithObjects:(id *)objects weights:(float *)weights count:(NSUInteger)count
     806           0 : {
     807             :         NSUInteger                              i = 0;
     808             :         
     809             :         // Validate parameters.
     810             :         if (count != 0 && (objects == NULL || weights == NULL))
     811             :         {
     812             :                 [self release];
     813             :                 [NSException raise:NSInvalidArgumentException format:@"Attempt to create %@ with non-zero count but nil objects or weights.", @"OOMutableProbabilitySet"];
     814             :         }
     815             :         
     816             :         // Set up & go.
     817             :         if ((self = [self initPriv]))
     818             :         {
     819             :                 for (i = 0; i != count; ++i)
     820             :                 {
     821             :                         [self setWeight:fmax(weights[i], 0.0f) forObject:objects[i]];
     822             :                 }
     823             :         }
     824             :         
     825             :         return self;
     826             : }
     827             : 
     828             : 
     829             : - (id) initWithPropertyListRepresentation:(NSDictionary *)plist
     830           0 : {
     831             :         BOOL                                    OK = YES;
     832             :         NSArray                                 *objects = nil;
     833             :         NSArray                                 *weights = nil;
     834             :         NSUInteger                              i = 0, count = 0;
     835             :         
     836             :         if (!(self = [super initPriv]))  OK = NO;
     837             :         
     838             :         if (OK)
     839             :         {
     840             :                 objects = [plist oo_arrayForKey:kObjectsKey];
     841             :                 weights = [plist oo_arrayForKey:kWeightsKey];
     842             :                 
     843             :                 // Validate
     844             :                 if (objects == nil || weights == nil)  OK = NO;
     845             :                 count = [objects count];
     846             :                 if (count != [weights count])  OK = NO;
     847             :         }
     848             :         
     849             :         if (OK)
     850             :         {
     851             :                 for (i = 0; i < count; ++i)
     852             :                 {
     853             :                         [self setWeight:[weights oo_floatAtIndex:i] forObject:[objects objectAtIndex:i]];
     854             :                 }
     855             :         }
     856             :         
     857             :         if (!OK)
     858             :         {
     859             :                 [self release];
     860             :                 self = nil;
     861             :         }
     862             :         
     863             :         return self;
     864             : }
     865             : 
     866             : 
     867             : - (void) dealloc
     868           0 : {
     869             :         [_objects release];
     870             :         [_weights release];
     871             :         
     872             :         [super dealloc];
     873             : }
     874             : 
     875             : 
     876             : - (NSDictionary *) propertyListRepresentation
     877           0 : {
     878             :         return [NSDictionary dictionaryWithObjectsAndKeys:
     879             :                         _objects, kObjectsKey,
     880             :                         _weights, kWeightsKey,
     881             :                         nil];
     882             : }
     883             : 
     884             : 
     885             : - (NSUInteger) count
     886           0 : {
     887             :         return [_objects count];
     888             : }
     889             : 
     890             : 
     891             : - (id) randomObject
     892           0 : {
     893             :         float                                   target = 0.0f, sum = 0.0f, sumOfWeights;
     894             :         NSUInteger                              i = 0, count = 0;
     895             :         
     896             :         sumOfWeights = [self sumOfWeights];
     897             :         target = randf() * sumOfWeights;
     898             :         count = [_objects count];
     899             :         if (count == 0 || sumOfWeights <= 0.0f)  return nil;
     900             :         
     901             :         for (i = 0; i < count; ++i)
     902             :         {
     903             :                 sum += [_weights oo_floatAtIndex:i];
     904             :                 if (sum >= target)  return [_objects objectAtIndex:i];
     905             :         }
     906             :         
     907             :         OOLog(@"probabilitySet.broken", @"%s fell off end, returning first object. Nominal sum = %f, target = %f, actual sum = %f, count = %lu. %@", __PRETTY_FUNCTION__, sumOfWeights, target, sum, count,@"This is an internal error, please report it.");
     908             :         return [_objects objectAtIndex:0];
     909             : }
     910             : 
     911             : 
     912             : - (float) weightForObject:(id)object
     913           0 : {
     914             :         float                                   result = -1.0f;
     915             :         
     916             :         if (object != nil)
     917             :         {
     918             :                 NSUInteger index = [_objects indexOfObject:object];
     919             :                 if (index != NSNotFound)
     920             :                 {
     921             :                         result = [_weights oo_floatAtIndex:index];
     922             :                         if (index != 0)  result -= [_weights oo_floatAtIndex:index - 1];
     923             :                 }
     924             :         }
     925             :         return result;
     926             : }
     927             : 
     928             : 
     929             : - (float) sumOfWeights
     930           0 : {
     931             :         if (_sumOfWeights < 0.0f)
     932             :         {
     933             :                 NSUInteger                      i, count;
     934             :                 count = [self count];
     935             :                 
     936             :                 _sumOfWeights = 0.0f;
     937             :                 for (i = 0; i < count; ++i)
     938             :                 {
     939             :                         _sumOfWeights += [_weights oo_floatAtIndex:i];
     940             :                 }
     941             :         }
     942             :         return _sumOfWeights;
     943             : }
     944             : 
     945             : 
     946             : - (NSArray *) allObjects
     947           0 : {
     948             :         return [[_objects copy] autorelease];
     949             : }
     950             : 
     951             : 
     952             : - (NSEnumerator *) objectEnumerator
     953           0 : {
     954             :         return [_objects objectEnumerator];
     955             : }
     956             : 
     957             : 
     958             : - (void) setWeight:(float)weight forObject:(id)object
     959           0 : {
     960             :         if (object == nil)  return;
     961             :         
     962             :         weight = fmax(weight, 0.0f);
     963             :         NSUInteger index = [_objects indexOfObject:object];
     964             :         if (index == NSNotFound)
     965             :         {
     966             :                 [_objects addObject:object];
     967             :                 [_weights oo_addFloat:weight];
     968             :                 if (_sumOfWeights >= 0)
     969             :                 {
     970             :                         _sumOfWeights += weight;
     971             :                 }
     972             :                 // Else, _sumOfWeights is invalid and will need to be recalculated on demand.
     973             :         }
     974             :         else
     975             :         {
     976             :                 _sumOfWeights = -1.0f;  // Simply subtracting the relevant weight doesn't work if the weight is large, due to floating-point precision issues.
     977             :                 [_weights replaceObjectAtIndex:index withObject:[NSNumber numberWithFloat:weight]];
     978             :         }
     979             : }
     980             : 
     981             : 
     982             : - (void) removeObject:(id)object
     983           0 : {
     984             :         if (object == nil)  return;
     985             :         
     986             :         NSUInteger index = [_objects indexOfObject:object];
     987             :         if (index != NSNotFound)
     988             :         {
     989             :                 [_objects removeObjectAtIndex:index];
     990             :                 _sumOfWeights = -1.0f;  // Simply subtracting the relevant weight doesn't work if the weight is large, due to floating-point precision issues.
     991             :                 [_weights removeObjectAtIndex:index];
     992             :         }
     993             : }
     994             : 
     995             : 
     996             : - (id) copyWithZone:(NSZone *)zone
     997           0 : {
     998             :         id                                              result = nil;
     999             :         id                                              *objects = NULL;
    1000             :         float                                   *weights = NULL;
    1001             :         NSUInteger                              i = 0, count = 0;
    1002             :         
    1003             :         count = [_objects count];
    1004             :         if (EXPECT_NOT(count == 0))  return [OOEmptyProbabilitySet singleton];
    1005             :         
    1006             :         objects = malloc(sizeof *objects * count);
    1007             :         weights = malloc(sizeof *weights * count);
    1008             :         if (objects != NULL && weights != NULL)
    1009             :         {
    1010             :                 [_objects getObjects:objects];
    1011             :                 
    1012             :                 for (i = 0; i < count; ++i)
    1013             :                 {
    1014             :                         weights[i] = [_weights oo_floatAtIndex:i];
    1015             :                 }
    1016             :                 
    1017             :                 result = [[OOProbabilitySet probabilitySetWithObjects:objects weights:weights count:count] retain];
    1018             :         }
    1019             :         
    1020             :         if (objects != NULL)  free(objects);
    1021             :         if (weights != NULL)  free(weights);
    1022             :         
    1023             :         return result;
    1024             : }
    1025             : 
    1026             : 
    1027             : - (id) mutableCopyWithZone:(NSZone *)zone
    1028           0 : {
    1029             :         return [[OOConcreteMutableProbabilitySet alloc] initPrivWithObjectArray:[[_objects mutableCopyWithZone:zone] autorelease]
    1030             :                                                                                                                            weightsArray:[[_weights mutableCopyWithZone:zone] autorelease]
    1031             :                                                                                                                                                 sum:_sumOfWeights];
    1032             : }
    1033             : 
    1034             : @end
    1035             : 
    1036             : 
    1037             : @implementation OOProbabilitySetEnumerator
    1038             : 
    1039             : - (id) initWithEnumerable:(id<OOProbabilitySetEnumerable>)enumerable
    1040             : {
    1041             :         if ((self = [super init]))
    1042             :         {
    1043             :                 _enumerable = [enumerable retain];
    1044             :         }
    1045             :         
    1046             :         return self;
    1047             : }
    1048             : 
    1049             : 
    1050             : - (void) dealloc
    1051           0 : {
    1052             :         [_enumerable release];
    1053             :         
    1054             :         [super dealloc];
    1055             : }
    1056             : 
    1057             : 
    1058             : - (id) nextObject
    1059           0 : {
    1060             :         if (_index < [_enumerable count])
    1061             :         {
    1062             :                 return [_enumerable privObjectAtIndex:_index++];
    1063             :         }
    1064             :         else
    1065             :         {
    1066             :                 [_enumerable release];
    1067             :                 _enumerable = nil;
    1068             :                 return nil;
    1069             :         }
    1070             : }
    1071             : 
    1072             : @end
    1073             : 
    1074             : 
    1075             : static void ThrowAbstractionViolationException(id obj)
    1076           0 : {
    1077             :         [NSException raise:NSGenericException format:@"Attempt to use abstract class %@ - this indicates an incorrect initialization.", [obj class]];
    1078             :         abort();        // unreachable
    1079             : }

Generated by: LCOV version 1.14