-- -*- Mode: Sather; -*- -- File: dist_ocean.sa -- Author: Chu-Cheow Lim (clim@ICSI.Berkeley.EDU) -- Copyright (C) International Computer Science Institute, 1993 -- -- COPYRIGHT NOTICE: This code is provided "AS IS" WITHOUT ANY WARRANTY -- and is subject to the terms of the SATHER LIBRARY GENERAL PUBLIC -- LICENSE contained in the file: "sather/doc/license.txt" of the Sather -- distribution. The license is also available from ICSI, 1947 Center -- St., Suite 600, Berkeley CA 94704, USA. --*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ --* FUNCTION: --* --* CLASSES: --* --* REQUIRED FILES: --* --* RELATED FILES: --* --* HISTORY: --* Last edited: Jun 1 20:41 1993 (clim) --* Created: Thu May 27 17:40:04 1993 (clim) --*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ class OCEAN_HEADER{CT,T} is chunks:ARRAY{CT}; height, width:INT; num_chunks:INT; avg_size:INT; threshold_chunk_index:INT; threshold_index:INT; locks:ARRAY{MONITOR0}; -- Protect each chunk when an element is inserted/deleted from corresponding -- chunk. end; -- class OCEAN_HEADER{CT,T} --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ class DIST_VERT_OCEAN{CT,T} is ABSTRACT_OCEAN{T}; -- Type inheritance. SPREAD{OCEAN_HEADER{CT,T}}; -- Simple solution: Divide the ocean into vertical strips of size (h X w/n) -- We can also divide into horizontal strips (h/n X w), if h > w. -- Basically this is a distributed hash table, but at this point, we just -- hack the code since it's not clear how the code reuse should work in the -- following related classes: -- -- HASH_TABLE --- DIST_HASH_TABLE -- | | -- | | -- OCEAN --- DIST_OCEAN iter_index:INT; -- For emulating `dist'-statement. constant num_clusters:INT := CONFIG::current_num_clusters; constant current_cluster:INT := CONFIG::current_cluster_id; constant min_dim_size:INT := 3; -- Divide the chunks into 2 distinct sets. constant num_disjoint:INT := 2; ------------------------------ -- Routines to access local attribute copies of attributes. -- The ocean is divided into chunks of size (h X (w/n)+1)... (h X w/n) ... -- "threshold_chunk_index" is the index of the first chunk that has -- width = w/n. "threshold_index" is the column index of the first column -- of this chunk. -- "avg_size" = w/n. height:INT is res := [current_cluster].height end; width:INT is res := [current_cluster].width end; num_chunks:INT is res := [current_cluster].num_chunks end; avg_size:INT is res := [current_cluster].avg_size end; threshold_chunk_index:INT is res := [current_cluster].threshold_chunk_index; end; threshold_index:INT is res := [current_cluster].threshold_index; end; ------------------------------ create(h,w:INT):SELF_TYPE is res := create_dir(h,w); num_chunks:INT := res.num_chunks; threshold:INT := res.threshold_chunk_index; s0:INT := res.avg_size+1; i:INT; loc:INT; count:INT; array_of_chunks:ARRAY{CT} := ARRAY{CT}::new(num_chunks); array_of_locks:ARRAY{MONITOR0} := ARRAY{MONITOR0}::new(num_chunks); until (i >= num_chunks) loop if (i >= threshold) then s0 := res.avg_size end; array_of_chunks[i] := CT::create(h,s0) @ loc; array_of_locks[i] := MONITOR0::new @ loc; count := count+1; if (count >= num_disjoint) then count := 0; loc := loc+1; if (loc >= num_clusters) then loc := 0 end; end; -- if i := i+1; end; -- loop res[current_cluster].chunks := array_of_chunks; res[current_cluster].locks := array_of_locks; res[current_cluster].height := h; res[current_cluster].width := w; j:INT; until (j >= num_clusters) loop if (j /= current_cluster) then res[j] := res[current_cluster].copy @ j; res[j].chunks := array_of_chunks.copy @ j; res[j].locks := array_of_locks.copy @ j; end; -- if j := j+1; end; -- loop end; -- create create_dir(h,w:INT):SELF_TYPE is -- Create a distributed ocean, but do not allocate the sub-components. res := new; avg_size:INT := w/(num_clusters*num_disjoint); num_chunks:INT; threshold_chunk_index, threshold_index:INT; if (avg_size < min_dim_size) then avg_size := min_dim_size; num_chunks := w/min_dim_size; if (num_chunks.u_mod(2) /= 0) then ERR::start; ERR::s("Distributed ocean has ").i(num_chunks).s(" chunks;\n") .s(" --> Cannot form 2 disjoint sets\n"); ERR::stop; loop end; -- Spin on error. end; -- if threshold_chunk_index := w.u_mod(num_chunks); threshold_index := (threshold_chunk_index)*(avg_size+1); else num_chunks := num_clusters*num_disjoint; threshold_chunk_index := w.u_mod(num_chunks); threshold_index := (w.u_mod(num_chunks)*(avg_size+1)); end; -- if header:OCEAN_HEADER{CT,T} := OCEAN_HEADER{CT,T}::new; header.avg_size := avg_size; header.num_chunks := num_chunks; header.threshold_chunk_index := threshold_chunk_index; header.threshold_index := threshold_index; res[current_cluster] := header; end; -- create_dir chunk(i:INT):CT is -- ith sub-component of distributed ocean. res := [current_cluster].chunks[i]; end; -- chunk gate(i:INT):MONITOR0 is -- ith lock of corresponding sub-component of distributed ocean. res := [current_cluster].locks[i]; end; -- gate loc_of(x,y:INT):INT is -- Location of (x,y)th element of distributed ocean. Because of -- the slicing, it is determined only by x. Returns chunk-id. if (x < threshold_index) then res:=x/(avg_size+1); else res:=(x-threshold_index)/avg_size+threshold_chunk_index end; end; -- loc_of cluster_id_of(x,y:INT):INT is -- Returns cluster-id where the (x,y) element is located. res := loc_of(x,y)/num_disjoint; end; -- cluster_id_of global_x_to_local_x(x,y:INT):INT is -- Convert x-component of global index to index within its -- chunk. if (x < threshold_index) then res:=x.u_mod(avg_size+1); else res:=(x-threshold_index).u_mod(avg_size) end; end; -- global_x_to_local_x global_y_to_local_y(x,y:INT):INT is -- Convert y-component of global index to index within its -- chunk. res := y; end; -- global_y_to_local_y get(x,y:INT):T is i:INT := loc_of(x,y); local_x:INT := global_x_to_local_x(x,y); local_y:INT := global_y_to_local_y(x,y); ith_chunk:CT := chunk(i); res := ith_chunk.get(local_x,local_y) @ ith_chunk.where; end; -- get protect_get(x,y:INT):T is i:INT := loc_of(x,y); local_x:INT := global_x_to_local_x(x,y); local_y:INT := global_y_to_local_y(x,y); ith_chunk:CT := chunk(i); ith_lock:MONITOR0 := gate(i); res := help_protect_get(ith_chunk, ith_lock, local_x,local_y) @ ith_chunk.where; end; -- protect_get private help_protect_get(ith_chunk:CT; ith_lock:MONITOR0; local_x, local_y:INT):T is lock ith_lock then res := ith_chunk.get(local_x,local_y); end; end; -- help_protect_get test(x,y:INT):BOOL is -- Returns `true' when there is a creature at (x,y); `false' otherwise. res := (get(x,y) /= void); end; -- test protect_test(x,y:INT):BOOL is -- Returns `true' when there is a creature at (x,y); `false' otherwise. res := (protect_get(x,y) /= void); end; -- protect_test insert(x,y:INT; e:T) is i:INT := loc_of(x,y); local_x:INT := global_x_to_local_x(x,y); local_y:INT := global_y_to_local_y(x,y); ith_chunk:CT := chunk(i); ith_chunk.insert(local_x, local_y, e) @ ith_chunk.where; end; -- insert protect_insert(x,y:INT; e:T) is i:INT := loc_of(x,y); local_x:INT := global_x_to_local_x(x,y); local_y:INT := global_y_to_local_y(x,y); ith_chunk:CT := chunk(i); ith_lock:MONITOR0 := gate(i); help_protect_insert(ith_chunk, ith_lock, local_x, local_y, e) @ ith_chunk.where; end; -- protect_insert private help_protect_insert(ith_chunk:CT; ith_lock:MONITOR0; local_x, local_y:INT; e:T) is lock ith_lock then ith_chunk.insert(local_x,local_y,e); end; end; -- help_protect_insert delete(x,y:INT) is i:INT := loc_of(x,y); local_x:INT := global_x_to_local_x(x,y); local_y:INT := global_y_to_local_y(x,y); ith_chunk:CT := chunk(i); ith_chunk.delete(local_x, local_y) @ ith_chunk.where; end; -- delete protect_delete(x,y:INT) is i:INT := loc_of(x,y); local_x:INT := global_x_to_local_x(x,y); local_y:INT := global_y_to_local_y(x,y); ith_chunk:CT := chunk(i); ith_lock:MONITOR0 := gate(i); help_protect_delete(ith_chunk, ith_lock, local_x, local_y) @ ith_chunk.where; end; -- protect_delete private help_protect_delete(ith_chunk:CT; ith_lock:MONITOR0; local_x, local_y:INT) is lock ith_lock then ith_chunk.delete(local_x,local_y); end; end; -- help_protect_delete difference_with(x:SELF_TYPE):SELF_TYPE is -- With a dist-statement, this is simply: -- dist res as res_c, x as x_c do -- res_c.difference_with(x_c); -- end; -- -- The precondition ensures that the blocks are aligned. -- assert (pre) (height = x.height) and (width = x.width) end; res := self; res.init_dist; assert (pre) (res.num_chunks = x.num_chunks) end; cobegin loop if (res.is_done) then break end; ind:INT := res.curr_c_index; chunk_res:CT := res.chunk(ind); chunk_x:CT := x.chunk(ind); :- rout_df(ind, chunk_res, chunk_x) @ chunk_res.where; res.next_dist; end; -- loop end; end; -- difference_with private rout_df(dist_index:INT; result, x:CT) is -- Emulates body of dist-statement in "difference_with". c:OCEAN_CURSOR{T} := x.cursor; until c.is_done loop if (result.get(c.key_x,c.key_y) = c.item) then result.delete(c.key_x,c.key_y); end; -- if c.next; end; -- loop -- * GC: Deallocate cursor object. c.invalidate; end; -- rout_df union_with(x:SELF_TYPE):SELF_TYPE is -- With a dist-statement, this is simply: -- dist res as res_c, x as x_c do -- res_c.union_with(x_c); -- end; -- -- The precondition ensures that the blocks are aligned. -- assert (pre) (height = x.height) and (width = x.width) end; res := self; res.init_dist; assert (pre) (res.num_chunks = x.num_chunks) end; cobegin loop if (res.is_done) then break end; ind:INT := res.curr_c_index; chunk_res:CT := res.chunk(ind); chunk_x:CT := x.chunk(ind); :- rout_u(ind, chunk_res, chunk_x) @ chunk_res.where; res.next_dist; end; -- loop end; end; -- union_with private rout_u(dist_index:INT; result, x:CT) is -- Emulates body of dist-statement in "union_with". c:OCEAN_CURSOR{T} := x.cursor; until c.is_done loop e:T := c.item; if (e.is_far) then e:=e.copy end; result.insert(c.key_x,c.key_y,e); c.next; end; -- loop -- * GC: Deallocate cursor object. c.invalidate; end; -- rout_u -- The following features deal with directions. May be better -- encapsulated in a "DIR" class (ref: Ben's code). constant DirUp:INT := 1; constant DirDown:INT := 3; constant DirLeft:INT := 2; constant DirRight:INT := 0; constant DirNone:INT := -1; discrete_dir(c:COMPLEX):INT is -- Convert to one of four discrete directions: DirUp, DirDown, -- DirLeft, DirRight. res := (c.theta*2.0/MATH::pi).round; if (res < 0) then res := res + 4 end; end; -- discrete_dir update_x_dir(pos:INT; dir:INT):INT is -- Update the new X-position after moving along given direction. if (dir = DirLeft) then res := pos-1; if (res = -1) then res:=width-1 end; elsif (dir = DirRight) then res := pos+1; if (res = width) then res:=0 end; else res := pos; end; -- if end; -- update_x_dir update_y_dir(pos:INT; dir:INT):INT is -- Update the new Y-position after moving along given direction. if (dir = DirDown) then res := pos-1; if (res = -1) then res:=height-1 end; elsif (dir = DirUp) then res := pos+1; if (res = height) then res:=0 end; else res := pos; end; -- if end; -- update_y_dir up_of(y_pos:INT):INT is res := y_pos+1; if (res = height) then res:=0 end; end; -- up_of down_of(y_pos:INT):INT is res := y_pos-1; if (res = -1) then res:=height-1 end; end; -- down_of left_of(x_pos:INT):INT is res := x_pos-1; if (res = -1) then res:=width-1 end; end; -- left_of right_of(x_pos:INT):INT is res := x_pos+1; if (res = width) then res:=0 end; end; -- right_of dirname(dir:INT):STR is switch (dir) when DirUp then res:="UP"; when DirDown then res:="DOWN"; when DirLeft then res:="LEFT"; when DirRight then res:="RIGHT"; when DirNone then res:="NONE"; else res:="UNKNOWN"; end; -- switch end; -- dirname -- Support functions for emulating `dist'-statement. -- When `dist'-statements are available, some of these should be available -- as "iters" and known by the compiler. init_dist is iter_index:=0; end; -- init_dist is_done:BOOL is res:=(iter_index>=num_chunks); end; -- is_done curr_c_index:INT is res:=iter_index; end; -- curr_c_index next_dist is iter_index:=iter_index+1; end; -- next_dist end; -- class DIST_VERT_OCEAN{CT,T} --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ class DIST_OCEAN_OF_PREDATORS is -- This class encapsulates the distribution of predators based on -- the DIST_VERT_OCEAN{CT,T} abstraction. The actions of predators -- (eg eat, move) is really encapsulated in OCEAN_OF_PREDATORS -- (a sequential class), so that we illustrate a common paradigm that -- is coming up again and again -- building distributed data structures -- on top of sequential ones, so that if the sequential ones are -- well encapsulated, things will be simpler. ABSTRACT_OCEAN_OF_PREDATORS; -- Type inheritance DIST_VERT_OCEAN{OCEAN_OF_PREDATORS,$ABSTRACT_PREDATOR}; -- Code inheritance alias old_create create; -- Where the creatures in this set are located. shared_grounds:$ABSTRACT_OCEAN{$ABSTRACT_CREATURE}; -- No random number generator needed; they are allocated on each chunk. -- For use during computation. dead_predators:DIST_OCEAN_OF_PREDATORS; baby_predators:DIST_OCEAN_OF_PREDATORS; from_set:DIST_OCEAN_OF_PREDATORS; to_set:DIST_OCEAN_OF_PREDATORS; non_recurs_create(ht,width:INT):SELF_TYPE is res := old_create(ht,width); end; -- non_recurs_create create(ht,width:INT):SELF_TYPE is res := old_create(ht,width); res.dead_predators := non_recurs_create(ht,width); res.baby_predators := non_recurs_create(ht,width); res.from_set := non_recurs_create(ht,width); res.to_set := non_recurs_create(ht,width); end; -- create init_shared_grounds(sg:$ABSTRACT_OCEAN{$ABSTRACT_CREATURE}) is shared_grounds := sg; i:INT; nc:INT := num_chunks; until (i >= nc) loop ith_chunk:OCEAN_OF_PREDATORS := chunk(i); ith_chunk.init_shared_grounds(sg); i := i+1; end; -- loop end; -- init_shared_grounds -- Prey's repulsion from predator. shared repel_scale:DOUBLE := -1.0; -- Two lists to keep track of SPREAD-objects for use in reduction. shared x_spread_objects:LIST{SPREAD{DOUBLE}} := LIST{SPREAD{DOUBLE}}::create; shared y_spread_objects:LIST{SPREAD{DOUBLE}} := LIST{SPREAD{DOUBLE}}::create; -- Gate to protect the list of spread-objects. shared gate_spread:MONITOR0 := MONITOR0::new; repel_prey(prey_pos_x,prey_pos_y:DOUBLE):COMPLEX is -- Should use the dist-statement when it is available. -- -- Implementation: Cannot use the cursor, because (i) it will become -- a bottleneck, (ii) multiple prey will be asking the ocean-of-predators -- to compute its repulsion. iter_index:INT := 0; nc:INT := num_chunks; res := COMPLEX::create(0.0, 0.0); x_spread, y_spread:SPREAD{DOUBLE}; lock gate_spread then if (x_spread_objects.size = 0) then x_spread := SPREAD{DOUBLE}::new; y_spread := SPREAD{DOUBLE}::new; else x_spread := x_spread_objects.pop; y_spread := y_spread_objects.pop; end; -- if end; -- lock cobegin loop if (iter_index>=nc) then break end; chunk_self:OCEAN_OF_PREDATORS := self.chunk(iter_index); :- rout0(chunk_self,prey_pos_x,prey_pos_y,x_spread,y_spread) @ chunk_self.where; iter_index := iter_index+1; end; -- loop end; i:INT; nc:INT := num_clusters; until (i >= nc) loop res.x := res.x + x_spread[i]; res.y := res.y + y_spread[i]; x_spread[i] := 0.0; y_spread[i] := 0.0; i := i+1; end; -- loop lock gate_spread then x_spread_objects := x_spread_objects.push(x_spread); y_spread_objects := y_spread_objects.push(y_spread); end; -- lock -- * GC: At some point, we need to de-allocate "res". end; -- repel_prey rout0(predators:OCEAN_OF_PREDATORS; prey_pos_x, prey_pos_y:DOUBLE; x_spread, y_spread:SPREAD{DOUBLE}) is f:COMPLEX := predators.repel_prey(prey_pos_x,prey_pos_y); x_spread[current_cluster] := f.x; y_spread[current_cluster] := f.y; end; -- rout0 eat_and_move(prey:$ABSTRACT_OCEAN_OF_PREY) is -- Update the predators, eg breed, increment age etc. -- Use the dist-statement to work on disjoint sections of the -- ocean. This is an example where we can benefit from different -- iterators that generate chunks differently. -- -- dist self as self_c.chunks(0,3) do -- changes[chix] := self_c.eat_and_move(prey); -- end; -- dist self as self_c.chunks(1,3) do -- changes[chix] := self_c.eat_and_move(prey); -- end; -- dist self as self_c.chunks(2,3) do -- changes[chix] := self_c.eat_and_move(prey); -- end; -- self.difference_with(changes.dead_predators). -- difference_with(changes.from_set). -- union_with(changes.baby_predators). -- union_with(changes.to_set); -- -- Note: The chunks has to know about their parent (eg when dead/baby -- predators etc), so we pass "self" as a parameter. -- dist dead as dead_c, baby as baby_c ... -- dead_c.clear; baby_c.clear ... -- end; -- cobegin dead_predators.init_dist; loop if (dead_predators.is_done) then break end; ind:INT := dead_predators.curr_c_index; chunk_dead_predators:OCEAN_OF_PREDATORS := dead_predators.chunk(ind); chunk_baby_predators:OCEAN_OF_PREDATORS := baby_predators.chunk(ind); chunk_from_set:OCEAN_OF_PREDATORS := from_set.chunk(ind); chunk_to_set:OCEAN_OF_PREDATORS := to_set.chunk(ind); :- rout1(chunk_dead_predators, chunk_baby_predators, chunk_from_set, chunk_to_set) @ chunk_dead_predators.where; dead_predators.next_dist; end; -- loop end; i:INT := 0; until (i >= num_disjoint) loop self.init_dist; cobegin loop if (self.is_done) then break end; ind:INT := self.curr_c_index; if (ind.u_mod(num_disjoint) = i) then chunk_self:OCEAN_OF_PREDATORS := self.chunk(ind); :- rout2(chunk_self, prey, dead_predators, baby_predators, from_set, to_set) @ chunk_self.where; end; -- if self.next_dist; end; -- loop end; i := i+1; end; -- loop -- Remove the dead predators and add the baby predators. difference_with(dead_predators) .difference_with(from_set) .union_with(baby_predators) .union_with(to_set); end; -- eat_and_move rout1(dead_predators, baby_predators, from_set, to_set:OCEAN_OF_PREDATORS) is with dead_predators, baby_predators, from_set, to_set near dead_predators.clear; baby_predators.clear; from_set.clear; to_set.clear; end; end; -- rout1 rout2(chunk_self:OCEAN_OF_PREDATORS; prey:$ABSTRACT_OCEAN_OF_PREY; dead_predators, baby_predators, from_set, to_set:$ABSTRACT_OCEAN_OF_PREDATORS) is chunk_self.help_eat_and_move(prey, dead_predators, baby_predators, from_set, to_set); end; -- rout2 end; -- class DIST_OCEAN_OF_PREDATORS --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ class DIST_OCEAN_OF_PREY is -- Inherit abstract classes first, because of overwrite rule in -- inheritance. ABSTRACT_OCEAN_OF_PREY; -- Type inheritance. DIST_VERT_OCEAN{OCEAN_OF_PREY,$ABSTRACT_PREY}; -- Code inheritance. alias old_create create; -- Where the creatures in this set are located. shared_grounds:$ABSTRACT_OCEAN{$ABSTRACT_CREATURE}; -- For use during computation. baby_prey:DIST_OCEAN_OF_PREY; from_set:DIST_OCEAN_OF_PREY; to_set:DIST_OCEAN_OF_PREY; non_recurs_create(ht,width:INT):SELF_TYPE is res := old_create(ht,width); end; -- non_recurs_create create(ht,width:INT):SELF_TYPE is res := old_create(ht,width); res.baby_prey := non_recurs_create(ht,width); res.from_set := non_recurs_create(ht,width); res.to_set := non_recurs_create(ht,width); end; -- create init_shared_grounds(sg:$ABSTRACT_OCEAN{$ABSTRACT_CREATURE}) is shared_grounds := sg; i:INT; nc:INT := num_chunks; until (i >= nc) loop ith_chunk:OCEAN_OF_PREY := chunk(i); ith_chunk.init_shared_grounds(sg); i := i+1; end; -- loop end; -- init_shared_grounds -- Predators' attraction to prey. shared attract_scale:DOUBLE := 100.0; -- Two lists to keep track of SPREAD-objects for use in reduction. shared x_spread_objects:LIST{SPREAD{DOUBLE}} := LIST{SPREAD{DOUBLE}}::create; shared y_spread_objects:LIST{SPREAD{DOUBLE}} := LIST{SPREAD{DOUBLE}}::create; -- Gate to protect the list of spread-objects. shared gate_spread:MONITOR0 := MONITOR0::new; attract_predator(predator_pos_x,predator_pos_y:DOUBLE):COMPLEX is -- By using abstract class interface, we can deal with a different -- computation (compared with OCEAN_OF_PREY::attract_predator), -- without changing the client. -- -- all_values::=#REDUCTOR{COMPLEX}(redex:=#ROUT(COMPLEX::add(_,_)); -- dist self as self_c do -- all_values.accum(self_c.attract_predator(predator_pos)); -- end; -- res := all_values.result; -- -- Note: No copying is needed if COMPLEX is a value object. -- iter_index:INT := 0; nc:INT := num_chunks; res := COMPLEX::create(0.0, 0.0); x_spread, y_spread:SPREAD{DOUBLE}; lock gate_spread then if (x_spread_objects.size = 0) then x_spread := SPREAD{DOUBLE}::new; y_spread := SPREAD{DOUBLE}::new; else x_spread := x_spread_objects.pop; y_spread := y_spread_objects.pop; end; -- if end; -- lock cobegin loop if (iter_index>=nc) then break end; chunk_self:OCEAN_OF_PREY := self.chunk(iter_index); :- rout0(chunk_self,predator_pos_x,predator_pos_y,x_spread,y_spread) @ chunk_self.where; iter_index := iter_index+1; end; -- loop end; i:INT; nc:INT := num_clusters; until (i >= nc) loop res.x := res.x + x_spread[i]; res.y := res.y + y_spread[i]; x_spread[i] := 0.0; y_spread[i] := 0.0; i := i+1; end; -- loop lock gate_spread then x_spread_objects := x_spread_objects.push(x_spread); y_spread_objects := y_spread_objects.push(y_spread); end; -- lock -- * GC: At some point, we need to de-allocate "res". end; -- attract_predator rout0(prey:OCEAN_OF_PREY; predator_pos_x, predator_pos_y:DOUBLE; x_spread, y_spread:SPREAD{DOUBLE}) is f:COMPLEX := prey.attract_predator(predator_pos_x,predator_pos_y); x_spread[current_cluster] := f.x; y_spread[current_cluster] := f.y; end; -- rout0 move_from(predators:$ABSTRACT_OCEAN_OF_PREDATORS) is -- Similar to the structure in "DIST_OCEAN_OF_PREDATORS::eat_and_move". cobegin baby_prey.init_dist; loop if (baby_prey.is_done) then break end; ind:INT := baby_prey.curr_c_index; chunk_baby_prey:OCEAN_OF_PREY := baby_prey.chunk(ind); chunk_from_set:OCEAN_OF_PREY := from_set.chunk(ind); chunk_to_set:OCEAN_OF_PREY := to_set.chunk(ind); :- rout1(chunk_baby_prey, chunk_from_set, chunk_to_set) @ chunk_baby_prey.where; baby_prey.next_dist; end; -- loop end; i:INT := 0; until (i >= num_disjoint) loop self.init_dist; cobegin loop if (self.is_done) then break end; ind:INT := self.curr_c_index; if (ind.u_mod(num_disjoint) = i) then chunk_self:OCEAN_OF_PREY := self.chunk(ind); :- rout2(chunk_self, predators, baby_prey, from_set, to_set) @ chunk_self.where; end; -- if self.next_dist; end; -- loop end; i := i+1; end; -- loop difference_with(from_set).union_with(to_set).union_with(baby_prey); end; -- move_from rout1(baby_prey, from_set, to_set:OCEAN_OF_PREY) is with baby_prey, from_set, to_set near baby_prey.clear; from_set.clear; to_set.clear; end; end; -- rout1 rout2(chunk_self:OCEAN_OF_PREY; predators:$ABSTRACT_OCEAN_OF_PREDATORS; baby_prey, from_set, to_set:$ABSTRACT_OCEAN_OF_PREY) is i:INT := chunk_self.help_move_from(predators, baby_prey, from_set, to_set); OUT::start; OUT::s("No. of fish at ").i(chunk_self.where).s(" = ").i(i).nl; OUT::stop; end; -- rout2 end; -- class DIST_OCEAN_OF_PREY --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~