@@ -110,15 +110,21 @@ pub fn CartesianTreeMap(
110110 /// Inserts a key-value pair with a random priority.
111111 ///
112112 /// Uses per-instance PRNG priorities to ensure expected O(log n) performance.
113- /// If the key already exists, updates its value and priority.
113+ /// If the key already exists, updates its value in place; the node keeps
114+ /// its current priority, so the tree shape (and balance) is unchanged.
114115 ///
115116 /// Time complexity: O(log n) expected
116117 ///
117118 /// ## Errors
118119 /// Returns `error.OutOfMemory` if node allocation fails.
119120 pub fn put (self : * Self , key : K , value : V ) ! void {
120- const priority = self .prng .random ().int (u32 );
121- try self .putWithPriority (key , value , priority );
121+ // A value-only update keeps the existing priority: re-randomising it
122+ // on every update would churn the tree structure for no benefit.
123+ if (self .findNode (key )) | existing | {
124+ existing .value = value ;
125+ return ;
126+ }
127+ try self .insertNew (key , value , self .prng .random ().int (u32 ));
122128 }
123129
124130 /// Inserts a key-value pair with an explicit priority.
@@ -134,17 +140,20 @@ pub fn CartesianTreeMap(
134140 /// ## Errors
135141 /// Returns `error.OutOfMemory` if node allocation fails.
136142 pub fn putWithPriority (self : * Self , key : K , value : V , priority : u32 ) ! void {
137- // Update in place if the key already exists. Resolving duplicates up
138- // front is required for correctness: `insertNode` only detects an
139- // equal key at the node it is currently visiting, so once the new
140- // priority exceeds an ancestor's priority it would split and insert
141- // a second node with the same key.
142- if (self .findNode (key )) | existing | {
143- existing .value = value ;
144- existing .priority = priority ;
145- return ;
143+ // To honor the explicit priority on an update, remove the existing
144+ // node and reinsert it. Reinsertion positions the node by its new
145+ // priority and preserves the max-heap invariant, whereas writing the
146+ // priority in place would leave a node out of heap order relative to
147+ // its parent or children.
148+ if (self .contains (key )) {
149+ _ = self .remove (key );
146150 }
151+ try self .insertNew (key , value , priority );
152+ }
147153
154+ /// Inserts a key known not to be present. Callers must ensure the key is
155+ /// absent so the insertion never produces a duplicate.
156+ fn insertNew (self : * Self , key : K , value : V , priority : u32 ) ! void {
148157 const new_node = try self .allocator .create (Node );
149158 new_node .* = Node .init (key , value , priority );
150159
@@ -403,9 +412,28 @@ fn i32Compare(lhs: i32, rhs: i32) std.math.Order {
403412
404413const MapOracle = @import ("oracle.zig" ).MapOracle ;
405414
415+ const TreapI32 = CartesianTreeMap (i32 , i32 , i32Compare );
416+
417+ /// Recursively asserts the two invariants every treap node must satisfy: BST
418+ /// ordering on keys and the max-heap property on priorities (a parent's
419+ /// priority is greater than or equal to each child's).
420+ fn expectTreapInvariants (node : ? * const TreapI32.Node ) ! void {
421+ const n = node orelse return ;
422+ if (n .left ) | l | {
423+ try testing .expect (i32Compare (l .key , n .key ) == .lt );
424+ try testing .expect (l .priority <= n .priority );
425+ try expectTreapInvariants (l );
426+ }
427+ if (n .right ) | r | {
428+ try testing .expect (i32Compare (r .key , n .key ) == .gt );
429+ try testing .expect (r .priority <= n .priority );
430+ try expectTreapInvariants (r );
431+ }
432+ }
433+
406434test "CartesianTreeMap: differential test against sorted-array oracle" {
407435 const allocator = testing .allocator ;
408- var tree = CartesianTreeMap ( i32 , i32 , i32Compare ) .init (allocator );
436+ var tree = TreapI32 .init (allocator );
409437 defer tree .deinit ();
410438
411439 var oracle : MapOracle (i32 , i32 , i32Compare ) = .{};
@@ -456,10 +484,53 @@ test "CartesianTreeMap: differential test against sorted-array oracle" {
456484 while (k < @as (i32 , @intCast (key_space ))) : (k += 1 ) {
457485 try testing .expectEqual (oracle .contains (k ), tree .contains (k ));
458486 }
487+ // Every put, remove, and value-update must leave the treap a valid
488+ // max-heap on priorities and a valid BST on keys.
489+ try expectTreapInvariants (tree .root );
459490 }
460491 }
461492}
462493
494+ test "CartesianTreeMap: putWithPriority update preserves heap invariant" {
495+ var tree = TreapI32 .init (testing .allocator );
496+ defer tree .deinit ();
497+
498+ try tree .putWithPriority (5 , 50 , 10 );
499+ try tree .putWithPriority (3 , 30 , 20 );
500+ try tree .putWithPriority (8 , 80 , 5 );
501+ try tree .putWithPriority (1 , 10 , 15 );
502+ try tree .putWithPriority (7 , 70 , 25 );
503+ try expectTreapInvariants (tree .root );
504+
505+ // Raising key 5's priority above all others must lift it to the root while
506+ // keeping the structure a valid treap.
507+ try tree .putWithPriority (5 , 55 , 100 );
508+ try testing .expectEqual (@as (usize , 5 ), tree .count ());
509+ try testing .expectEqual (@as (i32 , 55 ), tree .get (5 ).? );
510+ try testing .expectEqual (@as (i32 , 5 ), tree .root .? .key );
511+ try testing .expectEqual (@as (u32 , 100 ), tree .root .? .priority );
512+ try expectTreapInvariants (tree .root );
513+
514+ // Lowering it again must re-sink it without breaking the invariants, and the
515+ // value update must persist.
516+ try tree .putWithPriority (5 , 555 , 1 );
517+ try testing .expectEqual (@as (usize , 5 ), tree .count ());
518+ try testing .expectEqual (@as (i32 , 555 ), tree .get (5 ).? );
519+ try expectTreapInvariants (tree .root );
520+ }
521+
522+ test "CartesianTreeMap: put updates value without changing priority" {
523+ var tree = TreapI32 .init (testing .allocator );
524+ defer tree .deinit ();
525+
526+ try tree .putWithPriority (10 , 100 , 42 );
527+ // A value-only `put` must leave the node's priority untouched.
528+ try tree .put (10 , 200 );
529+ try testing .expectEqual (@as (usize , 1 ), tree .count ());
530+ try testing .expectEqual (@as (i32 , 200 ), tree .get (10 ).? );
531+ try testing .expectEqual (@as (u32 , 42 ), tree .root .? .priority );
532+ }
533+
463534test "CartesianTreeMap basic operations" {
464535 var tree = CartesianTreeMap (i32 , []const u8 , i32Compare ).init (testing .allocator );
465536 defer tree .deinit ();
0 commit comments