DIRECTORY AbSets, BiRelBasics, BiRels, IntStuff, SetBasics, ValueHashTables; BiRelsHashed: CEDAR MONITOR LOCKS NARROW[br.data, HashRelation] USING br: BiRel IMPORTS AbSets, BiRelBasics, BiRels, IntStuff, SetBasics, ValueHashTables EXPORTS BiRels = BEGIN OPEN IntStuff, SetBasics, Sets:AbSets, Sets, BiRels, HT:ValueHashTables; Ability: TYPE ~ {none, image, map}; Abilities: TYPE ~ ARRAY Direction OF Ability; HashRelation: TYPE ~ REF HashRelationPrivate; HashRelationPrivate: TYPE ~ MONITORED RECORD [ spaces: SpacePair, ability: Abilities, bestAbility: Ability, bestDir: Direction, tables: ARRAY Direction OF HT.HashTable, size: LNAT _ 0, freezeCount: NATURAL _ 0 ]; Classes: TYPE ~ ARRAY --leftToRight--Ability OF ARRAY --rightToLeft--Ability OF ARRAY Mutability OF BiRelClass; classes: PUBLIC REF Classes ~ NEW[Classes]; CreateHashReln: PUBLIC PROC [spaces: SpacePair _ [refs, refs], functional: BoolPair _ [FALSE, FALSE], mappable: BoolPair _ [TRUE, TRUE]] RETURNS [VarBiRel] ~ { ability: Abilities; FOR dir: Direction IN Direction DO ability[dir] _ IF functional[dir] THEN map ELSE IF mappable[dir] THEN image ELSE none; ENDLOOP; {bestDir: Direction ~ IF ability[rightToLeft] > ability[leftToRight] THEN rightToLeft ELSE leftToRight; hr: HashRelation ~ NEW [HashRelationPrivate _ [ spaces: spaces, ability: ability, bestDir: bestDir, bestAbility: ability[bestDir], tables: [HT.Create[spaces[left]], HT.Create[spaces[right]]] ]]; RETURN [AsVar[[FetchClass[hr.ability, variable], hr]]]; }}; CreateHashCopy: PUBLIC PROC [br: BiRel, spaces: SpacePair _ [NIL, NIL], mappable: BoolPair _ [FALSE, FALSE]] RETURNS [copy: HashFn] ~ { FOR side: Side IN Side DO IF spaces[side]=NIL THEN spaces[side] _ br.Spaces[][side]; ENDLOOP; copy _ CreateHashReln[spaces, br.Functional, mappable]; [] _ copy.AddSet[br]; RETURN}; FetchClass: PROC [ability: Abilities, mutability: Mutability] RETURNS [class: BiRelClass] ~ INLINE {RETURN [classes[ability[leftToRight]][ability[rightToLeft]][mutability]]}; Frigid: PROC [class: BiRelClass] RETURNS [BOOL] ~ INLINE {RETURN [class.data = $Frigid]}; HasherPrimitive: PROC [br: BiRel, op: ATOM, arg1, arg2: REF ANY _ NIL] RETURNS [PrimitiveAnswer] ~ { hr: HashRelation ~ NARROW[br.data]; SELECT op FROM $HasPair => RETURN [IF Frigid[br.class] AND hr.freezeCount=0 OR hr.bestAbility#none THEN yes ELSE no]; $Apply => RETURN [IF Frigid[br.class] AND hr.freezeCount=0 OR hr.ability[ToDir[arg1]]#none THEN yes ELSE no]; $ScanRestriction => {sets: RefSetPair ~ ToSets[arg1]; ro: RelOrder ~ ToRO[arg2].CanonizeRelOrder[br.Functional]; RETURN [IF Frigid[br.class] AND hr.freezeCount=0 OR ro.sub[ro.first]=no AND sets^=ALL[nilSet] AND hr.bestAbility#none THEN yes ELSE no]}; $RestrictionSize => RETURN [IF ToSets[arg1]^=ALL[nilSet] THEN yes ELSE no]; $DeleteSet => RETURN [IF hr.ability[From[ToSide[arg2]]]#none THEN yes ELSE no]; ENDCASE => RETURN [pass]; }; HasherHasPair: ENTRY PROC [br: BiRel, pair: Pair] RETURNS [BOOL] ~ { ENABLE UNWIND => NULL; hr: HashRelation ~ NARROW[br.data]; IF Frigid[br.class] AND hr.freezeCount=0 THEN RETURN WITH ERROR Error[Sets.unfrozen, LIST[AV[br.Refify]]]; {map: MaybeValue ~ hr.tables[hr.bestDir].Map[pair[Source[hr.bestDir]]]; y: Value ~ pair[Dest[hr.bestDir]]; IF NOT map.found THEN RETURN [FALSE]; SELECT hr.bestAbility FROM none => ERROR; image => RETURN [Sets.DeRef[map.it.VA].HasMember[y]]; map => RETURN [hr.spaces[Dest[hr.bestDir]].SEqual[map.it, y]]; ENDCASE => ERROR; }}; HasherApply: PROC [br: BiRel, v: Value, dir: Direction] RETURNS [MaybeValue] ~ { hr: HashRelation ~ NARROW[br.data]; EasyApply: ENTRY PROC [br: BiRel] RETURNS [MaybeValue] ~ { ENABLE UNWIND => NULL; IF Frigid[br.class] AND hr.freezeCount=0 THEN RETURN WITH ERROR Error[Sets.unfrozen, LIST[AV[br.Refify]]]; RETURN hr.tables[dir].Map[v]; }; SELECT hr.ability[dir] FROM none => RETURN DefaultApply[br, v, dir]; image => {br.Complain["%g not functional in this direction"]; ERROR}; map => RETURN EasyApply[br]; ENDCASE => ERROR; }; HasherScanRestriction: PROC [br: BiRel, sets: SetPair, Test: Tester, ro: RelOrder] RETURNS [mp: MaybePair _ noMaybePair] ~ { ENABLE UNWIND => NULL; hr: HashRelation ~ NARROW[br.data]; cro: RelOrder ~ ro.CanonizeRelOrder[br.Functional]; PassSet: PROC [pair: Pair] RETURNS [pass: BOOL _ FALSE] ~ { x: Value ~ pair[left]; set: Set ~ Sets.DeRef[pair[right].VA]; subj: Pair _ ALL[x]; PassElt: PROC [val: Value] RETURNS [pass: BOOL _ FALSE] ~ { subj[Dest[hr.bestDir]] _ val; IF (pass _ Test[subj]) THEN mp _ [TRUE, subj]; RETURN}; pass _ set.Scan[PassElt].found; RETURN}; InvTest: PROC [pair: Pair] RETURNS [pass: BOOL _ FALSE] ~ { rev: Pair ~ BiRelBasics.InvertPair[pair]; IF (pass _ Test[rev]) THEN mp _ [TRUE, rev]; RETURN}; IF Frigid[br.class] AND hr.freezeCount=0 THEN ERROR Error[Sets.unfrozen, LIST[AV[br.Refify]]]; IF ro.sub[ro.first]#no OR sets#ALL[nilSet] THEN RETURN DefaultScanRestriction[br, sets, Test, ro]; SELECT hr.bestAbility FROM none => ERROR; image => hr.tables[hr.bestDir] _ hr.tables[hr.bestDir].Scan[PassSet].same; map => SELECT hr.bestDir FROM leftToRight => [hr.tables[hr.bestDir], mp] _ hr.tables[hr.bestDir].Scan[Test]; rightToLeft => hr.tables[hr.bestDir] _ hr.tables[hr.bestDir].Scan[InvTest].same; ENDCASE => ERROR; ENDCASE => ERROR; RETURN}; HasherRestrictionSize: PROC [br: BiRel, sets: SetPair, limit: EINT] RETURNS [EINT] ~ { hr: HashRelation ~ NARROW[br.data]; WithLock: ENTRY PROC [br: BiRel] RETURNS [EINT] ~ { ENABLE UNWIND => NULL; IF Frigid[br.class] AND hr.freezeCount=0 THEN RETURN WITH ERROR Error[Sets.unfrozen, LIST[AV[br.Refify]]]; SELECT hr.bestAbility FROM none => ERROR; image => RETURN [IE[hr.size]]; map => RETURN [IE[hr.tables[hr.bestDir].Size]]; ENDCASE => ERROR}; IF sets#ALL[nilSet] THEN RETURN DefaultRestrictionSize[br, sets, limit]; RETURN WithLock[br]}; HasherCopy: PROC [br: BiRel] RETURNS [VarBiRel] ~ { mappable: BoolPair ~ [ leftToRight: br.GoodImpl[$Image, LIST[$leftToRight]], rightToLeft: br.GoodImpl[$Image, LIST[$rightToLeft]]]; RETURN CreateHashCopy[br, ALL[NIL], mappable]}; HasherInsulate: ENTRY PROC [br: BiRel] RETURNS [UWBiRel] ~ { ENABLE UNWIND => NULL; hr: HashRelation ~ NARROW[br.data]; RETURN [AsUW[[FetchClass[hr.ability, readonly], hr]]]; }; HasherFreeze: ENTRY PROC [br: BiRel] RETURNS [ConstBiRel] ~ { ENABLE UNWIND => NULL; hr: HashRelation ~ NARROW[br.data]; IF br.MutabilityOf # variable THEN RETURN WITH ERROR Error[notVariable, LIST[AV[br.Refify]]]; hr.freezeCount _ hr.freezeCount + 1; RETURN [AsConst[[FetchClass[hr.ability, constant], hr]]]; }; HasherThaw: ENTRY PROC [br: BiRel] ~ { ENABLE UNWIND => NULL; hr: HashRelation ~ NARROW[br.data]; IF br.MutabilityOf # variable THEN RETURN WITH ERROR Error[notVariable, LIST[AV[br.Refify]]]; IF hr.freezeCount = 0 THEN RETURN WITH ERROR Error["thawing unfrozen collection", LIST[AV[br.Refify]]]; hr.freezeCount _ hr.freezeCount - 1; }; HasherAddPair: ENTRY PROC [br: BiRel, pair: Pair, if: IfHadPair] RETURNS [had: HadPair] ~ { ENABLE UNWIND => NULL; hr: HashRelation ~ NARROW[br.data]; fnl: BoolPair ~ br.Functional[]; remove: ARRAY Direction OF MaybeValue _ ALL[Sets.noMaybe]; inc, dont: BOOL _ FALSE; dirs: ARRAY [0 .. 1] OF Direction _ [leftToRight, rightToLeft]; IF br.MutabilityOf#variable THEN RETURN WITH ERROR Error[notVariable, LIST[AV[br.Refify]]]; IF hr.freezeCount # 0 THEN RETURN WITH ERROR Error[Sets.frozen, LIST[AV[br.Refify]]]; IF fnl=ALL[TRUE] THEN { dir: Direction ~ rightToLeft; src: Side ~ Source[dir]; dst: Side ~ Dest[dir]; dom: Value ~ pair[src]; rng: Value ~ pair[dst]; map: MaybeValue ~ hr.tables[dir].Map[dom]; had[dir] _ IF map.found THEN IF hr.spaces[dst].SEqual[map.it, rng] THEN same ELSE different ELSE none; IF NOT if[dir][had[dir]=none] THEN dont _ TRUE; } ELSE IF fnl = [FALSE, TRUE] THEN dirs _ [rightToLeft, leftToRight]; FOR i: [0 .. 1] IN [0 .. 1] DO dir: Direction ~ dirs[i]; src: Side ~ Source[dir]; dst: Side ~ Dest[dir]; dom: Value ~ pair[src]; rng: Value ~ pair[dst]; AddIDecide: PROC [map: MaybeValue] RETURNS [MaybeValue] ~ { IF map.found THEN { set: Set ~ Sets.DeRef[map.it.VA]; IF set.AddElt[rng] THEN inc _ TRUE; RETURN [map]} ELSE { set: Sets.HashSet ~ Sets.CreateHashSet[hr.spaces[dst]]; [] _ set.AddElt[rng]; inc _ TRUE; RETURN [[TRUE, AV[set.Refify] ]]; }; }; AddMDecide: PROC [map: MaybeValue] RETURNS [MaybeValue] ~ { IF i=0 THEN { had[dir] _ IF map.found THEN IF hr.spaces[dst].SEqual[map.it, rng] THEN same ELSE different ELSE none; IF NOT if[dir][had[dir]=none] THEN dont _ TRUE; }; IF dont THEN RETURN [map]; IF had[dir]=different THEN remove[OtherDirection[dir]] _ map; RETURN [[TRUE, rng]]}; SELECT hr.ability[dir] FROM none => NULL; image => hr.tables[dir] _ hr.tables[dir].Update[dom, AddIDecide]; map => hr.tables[dir] _ hr.tables[dir].Update[dom, AddMDecide]; ENDCASE => ERROR; IF dont THEN RETURN; ENDLOOP; IF inc THEN hr.size _ hr.size+1; FOR dir: Direction IN Direction DO IF remove[dir].found THEN [] _ HalfRem[hr, dir, remove[dir].it, pair[Dest[dir]], FALSE]; ENDLOOP; RETURN}; HalfRem: INTERNAL PROC [hr: HashRelation, dir: Direction, dom, rng: Value, decr: BOOL] RETURNS [had: Had] ~ { doit: BOOL _ FALSE; MRemIfNecessary: PROC [old: MaybeValue] RETURNS [MaybeValue] ~ { had _ SELECT TRUE FROM NOT old.found => none, hr.spaces[Dest[dir]].SEqual[old.it, rng] => same, ENDCASE => different; RETURN [Sets.noMaybe]}; IRemIfNecessary: PROC [map: MaybeValue] RETURNS [MaybeValue] ~ { IF map.found THEN { set: Set ~ Sets.DeRef[map.it.VA]; had _ IF (doit _ set.RemElt[rng]) THEN same ELSE different; RETURN [IF set.Empty THEN noMaybe ELSE map]} ELSE {had _ none; RETURN [map]}; }; SELECT hr.ability[dir] FROM none => had _ none--well, nothing is appropriate, but an un-initialized variable can lead to intermittent behavior, which is hard to debug--; image => hr.tables[dir] _ hr.tables[dir].Update[dom, IRemIfNecessary]; map => hr.tables[dir] _ hr.tables[dir].Update[dom, MRemIfNecessary]; ENDCASE => ERROR; IF decr AND doit THEN hr.size _ hr.size-1; RETURN}; HasherRemPair: ENTRY PROC [br: BiRel, pair: Pair] RETURNS [had: HadPair] ~ { ENABLE UNWIND => NULL; hr: HashRelation ~ NARROW[br.data]; IF br.MutabilityOf#variable THEN RETURN WITH ERROR Error[notVariable, LIST[AV[br.Refify]]]; IF hr.freezeCount # 0 THEN RETURN WITH ERROR Error[Sets.frozen, LIST[AV[br.Refify]]]; FOR dir: Direction IN Direction DO had[dir] _ HalfRem[hr, dir, pair[Source[dir]], pair[Dest[dir]], dir=hr.bestDir]; ENDLOOP; RETURN}; HasherDeleteSet: PROC [br: BiRel, set: Set, side: Side] RETURNS [had: SomeAll _ []] ~ { hr: HashRelation ~ NARROW[br.data]; dir: Direction ~ From[side]; WithLock: ENTRY PROC [br: BiRel] ~ { ENABLE UNWIND => NULL; odir: Direction ~ To[side]; EltRem: INTERNAL PROC [val: Value] ~ { remove: Set ~ HalfDelete[hr, val, dir]; InnerRem: INTERNAL PROC [back: Value] ~ { IF HalfRem[hr, odir, back, val, FALSE]#same THEN ERROR; RETURN}; IF remove=nilSet OR remove.Empty THEN had.all _ FALSE ELSE { had.some _ TRUE; IF hr.ability[odir]#none THEN remove.Enumerate[InnerRem]; }; RETURN}; IF br.MutabilityOf#variable THEN RETURN WITH ERROR Error[notVariable, LIST[AV[br.Refify]]]; IF hr.freezeCount # 0 THEN RETURN WITH ERROR Error[Sets.frozen, LIST[AV[br.Refify]]]; set.Enumerate[EltRem]; RETURN}; IF hr.ability[dir]=none THEN RETURN DefaultDeleteSet[br, set, side]; WithLock[br]; RETURN}; HalfDelete: INTERNAL PROC [hr: HashRelation, val: Value, dir: Direction] RETURNS [remove: Set _ nilSet] ~ { SELECT hr.ability[dir] FROM none => ERROR; image => { DIRemIfNecessary: PROC [old: MaybeValue] RETURNS [MaybeValue] ~ { IF old.found THEN remove _ Sets.DeRef[old.it.VA]; RETURN [Sets.noMaybe]}; hr.tables[dir] _ hr.tables[dir].Update[val, DIRemIfNecessary]; }; map => { DMRemIfNecessary: PROC [old: MaybeValue] RETURNS [MaybeValue] ~ { IF old.found THEN remove _ Sets.CreateSingleton[old.it, hr.spaces[Dest[dir]]]; RETURN [Sets.noMaybe]}; hr.tables[dir] _ hr.tables[dir].Update[val, DMRemIfNecessary]; }; ENDCASE => ERROR; hr.size _ hr.size - remove.Size[].EI; }; HasherSpaces: PROC [br: BiRel] RETURNS [SpacePair] ~ { hr: HashRelation ~ NARROW[br.data]; RETURN [hr.spaces]; }; Start: PROC ~ { FOR lr: Ability IN Ability DO FOR rl: Ability IN Ability DO classes[lr][rl][variable] _ CreateClass[[ Primitive: HasherPrimitive, HasPair: HasherHasPair, Apply: HasherApply, ScanRestriction: HasherScanRestriction, RestrictionSize: HasherRestrictionSize, Copy: HasherCopy, Insulate: HasherInsulate, Freeze: HasherFreeze, Thaw: HasherThaw, AddPair: HasherAddPair, RemPair: HasherRemPair, DeleteSet: HasherDeleteSet, Spaces: HasherSpaces, functional: [lr=map, rl=map], mutability: variable]]; classes[lr][rl][readonly] _ CreateClass[[ Primitive: HasherPrimitive, HasPair: HasherHasPair, Apply: HasherApply, ScanRestriction: HasherScanRestriction, RestrictionSize: HasherRestrictionSize, Copy: HasherCopy, Spaces: HasherSpaces, functional: [lr=map, rl=map], mutability: readonly]]; classes[lr][rl][constant] _ CreateClass[[ Primitive: HasherPrimitive, HasPair: HasherHasPair, Apply: HasherApply, ScanRestriction: HasherScanRestriction, RestrictionSize: HasherRestrictionSize, Copy: HasherCopy, Spaces: HasherSpaces, functional: [lr=map, rl=map], mutability: constant, data: $Frigid]]; ENDLOOP ENDLOOP; }; Start[]; END. \BiRelsHashed.Mesa Last tweaked by Mike Spreitzer on December 15, 1987 11:06:46 am PST ส – "cedar" style˜code™KšœC™C—K˜Kšฯk œC˜LK˜šฯn œœ˜Kšœœœ ˜3KšœB˜IKšœ˜K˜—K˜Kšœœžœœ˜NK˜Kšœ œ˜#Kšœ œœ œ ˜-K˜Kšœœœ˜-šœœ œœ˜.K˜Kšœ˜Kšœ˜K˜Kšœœ œœ ˜(Kšœœ˜Kšœ œ˜K˜—K˜Kšœ œœฯcœœœŸœœœ œ ˜oKšœ œœ œ ˜+K˜šžœœœ<œœœœœ˜ŸKšœ˜šœœ ˜"Kš œœœœœœœ˜VKšœ˜—Kšœœ-œ œ ˜gšœœ˜/K˜Kšœ˜Kšœ˜Kšœ˜Kšœ œœ˜;K˜—Kšœ1˜7K˜—K˜šžœœœ"œœœœœ˜‡šœ œ˜Kšœœœ"˜:Kšœ˜—Kšœ7˜7Kšœ˜Kšœ˜—K˜Kš ž œœ.œœœD˜ฎK˜šžœœœœ˜/Kšœœœ˜)—K˜šžœœœœœœœ˜dKšœœ ˜#šœ˜Kš œ œœœœœœ˜fKš œ œœœœœœ˜mšœ5˜5K˜:Kšœœœœœœ œœœ˜‰—Kš œœœœ œœ˜KKš œœœ%œœ˜OKšœœ˜—K˜—K˜š ž œœœœœ˜DKšœœœ˜Kšœœ ˜#Kšœœœœœœœ˜jKšœG˜GKšœ"˜"Kš œœ œœœ˜%šœ˜Kšœœ˜Kšœ œœ˜5Kšœœ1˜>Kšœœ˜—K˜—K˜šž œœ'œ˜PKšœœ ˜#šž œœœ œ˜:Kšœœœ˜Kšœœœœœœœ˜jKšœ˜K˜—šœ˜Kšœœ˜(Kšœ>œ˜EKšœœ˜Kšœœ˜—K˜—K˜šžœœžœœ"˜|Kšœœœ˜Kšœœ ˜#Kšœ3˜3š žœœœœœ˜;Kšœ˜Kšœ"œ˜&Kšœ œ˜š žœœœœœ˜;Kšœ˜Kšœœœ˜.Kšœ˜—K˜Kšœ˜—š žœœœœœ˜;Kšœ)˜)Kšœœœ˜,Kšœ˜—Kš œœœœœ˜^Kš œœœ œœ,˜bšœ˜Kšœœ˜KšœJ˜Jšœœ ˜KšœN˜NKšœP˜PKšœœ˜—Kšœœ˜—Kšœ˜—K˜š žœœ#œœœ˜VKšœœ ˜#š žœœœ œœ˜3Kšœœœ˜Kšœœœœœœœ˜jšœ˜Kšœœ˜Kšœ œœ ˜Kšœœœ˜/Kšœœ˜——Kšœœ œœ)˜HKšœ˜—K˜šž œœ œ˜3šœ˜Kšœ!œ˜5Kšœ!œ˜6—Kšœœœ˜/—K˜šžœœœ œ˜˜>Kšœ˜—šœ˜šžœœœ˜AKšœ œ=˜NKšœ˜—Kšœ>˜>Kšœ˜—Kšœœ˜—Kšœ"œ˜%K˜—K˜šž œœ œ˜6Kšœœ ˜#Kšœ ˜K˜—K˜šžœœ˜š œ œ œœ œ ˜;šœ)˜)Kšž œ˜Kšžœ˜Kšžœ˜Kšžœ˜'Kšžœ˜'Kšžœ ˜Kšžœ˜Kšžœ˜Kšžœ ˜Kšžœ˜Kšžœ˜Kšž œ˜Kšžœ˜Kšœ˜Kšœ˜—šœ)˜)Kšž œ˜Kšžœ˜Kšžœ˜Kšžœ˜'Kšžœ˜'Kšžœ ˜Kšžœ˜Kšœ˜Kšœ˜—šœ)˜)Kšž œ˜Kšžœ˜Kšžœ˜Kšžœ˜'Kšžœ˜'Kšžœ ˜Kšžœ˜Kšœ˜Kšœ˜Kšœ˜—Kšœœ˜—K˜—K˜K˜K˜Kšœ˜—…—3ŠE