DIRECTORY Asserting, LichenArrayStuff, LichenDataOps, LichenDataStructure, LichenSetTheory, LichenTransforms, LichenTransformsPrivate, RefTab, Rope; LichenSplitMerge: CEDAR PROGRAM IMPORTS Asserting, LichenArrayStuff, LichenDataOps, LichenDataStructure, LichenSetTheory, LichenTransformsPrivate, RefTab, Rope EXPORTS LichenTransforms = BEGIN OPEN LichenArrayStuff, LichenDataStructure, LichenTransforms, LichenDataOps, LichenSetTheory, LichenTransformsPrivate; PortPair: TYPE = REF PortPairPrivate; PortPairPrivate: TYPE = RECORD [from, to: Port]; ArrayWireAns: TYPE = REF ArrayWireAnsPrivate; ArrayWireAnsPrivate: TYPE = RECORD [ proto: ArrayWire, analyzed: BOOL _ FALSE, fromPort: Port _ NIL, toPort: Port _ NIL, sawBord: BOOL _ FALSE, sawBords: BOOL _ FALSE, sawElse: BOOL _ FALSE ]; SplitType: PUBLIC PROC [design: Design, fizz: Set--of CellInstance--] RETURNS [from, to: CellType, pairs: OneToOne--instances of from f instances of to--] ~ { analysis: Analysis ~ NEW [AnalysisPrivate _ [ roles: fizz.Size[], subjTypes: CreateRefSeq[fizz.Size[]], wag: NEW [WireAnsweringPrivate _ [ oldSubjConnectionss: CreateRefSeq[fizz.Size[]], anses: CreateHashMapper[] ]], doomedPorts: CreateHashSet[] ]]; {OPEN analysis; cs: RefSeq--role _ fizzee-- ~ CreateRefSeq[roles]; toRole: Mapper--fizzee _ role-- ~ CreateHashMapper[]; addedFromPorts: Set--of Port of from-- ~ CreateHashSet[]; addedToPorts: Set--of Port of to-- ~ CreateHashSet[]; nameParts: LOLORA _ NIL; toName: ROPE; IF roles=0 THEN Error["Null fission"]; from _ NIL; {i: INT _ 0; PerElt: PROC [ra: REF ANY] ~ {v: CellInstance ~ NARROW[ra]; IF v.type=NIL OR v.containingCT=NIL THEN ERROR; IF from=NIL THEN from _ v.containingCT ELSE IF from#v.containingCT THEN Error["Fission of vertices not all having same parent"]; nameParts _ CONS[Select[nameReln, 1, v.other], nameParts]; cs[i] _ v; subjTypes[i] _ v.type; IF NOT toRole.PutMapping[v, NEW [INT _ i]] THEN ERROR; i _ i + 1; }; fizz.Enumerate[PerElt]; IF i # cs.length THEN ERROR; }; IF NOT from.designs.HasMember[design] THEN ERROR; IF from.firstInstance # NIL THEN ERROR nyet--we only split arrays--; nameParts _ CONS[Select[nameReln, 1, from.otherPublic], nameParts]; toName _ RopeCrossCat[nameParts]; to _ CreateCellType[design: design, cellTypeName: toName, class: NIL, internals: TRUE]; pairs _ CreateHashOTO[]; IF Survey[from, NIL, cs, analysis, TRUE] THEN ERROR; {MoveWire: PROC [domain, range: REF ANY] ~ { wire: Wire ~ NARROW[domain]; wa: WireAns ~ NARROW[range]; IF NOT wa.analyzed THEN ERROR; IF wa.counterpart # NIL THEN ERROR; wa.counterpart _ CreateWire[containingCT: to, copy: wire, other: Asserting.Filter[nameReln, wire.other].about]; SELECT wa.doFrom FROM addPort => { wa.fromPort _ AddPort[[parent: from.port, wire: wire]]; [] _ UnionSingleton[addedFromPorts, wa.fromPort]; AddEdge[[from.asUnorganized.mirror, wire], wa.fromPort]}; leave, dePort => NULL; ENDCASE => ERROR; SELECT wa.doTo FROM addPort => { wa.toPort _ AddPort[[parent: to.port, wire: wa.counterpart]]; [] _ UnionSingleton[addedToPorts, wa.toPort]; AddEdge[[to.asUnorganized.mirror, wa.counterpart], wa.toPort]}; ignore => NULL; ENDCASE => ERROR; }; wag.anses.EnumerateMapping[MoveWire]; }; FOR role: NATURAL IN [0 .. roles) DO fci: CellInstance ~ NARROW[cs[role]]; tci: CellInstance ~ Instantiate[fci.type, to, fci.other]; MoveConnection: PROC [subjPort: Port, fwire: Wire] ~ { wa: WireAns = GetRPAns[wag, role, subjPort, fwire, FALSE]; IF NOT wa.analyzed THEN ERROR; AddEdges[[tci, wa.counterpart], subjPort]; }; EnumerateTopConnections[fci, MoveConnection]; ENDLOOP; {SplitArray: PROC [fromArrayCT: CellType] ~ { fa: Array ~ fromArrayCT.asArray; faName: ROPE ~ NARROW[Asserting.FnVal[nameReln, fromArrayCT.otherPublic]]; taName: ROPE ~ faName.Cat["#", toName]; toArrayCT: CellType ~ CreateArray[design, taName, NIL, to, fa.size, fa.jointsPeriod, GetBorders[fa], NIL, NIL]; ta: Array ~ toArrayCT.asArray; toNewGroup: Mapper--group of fa _ group of ta-- ~ CreateHashMapper[]; toOldGroup: Mapper--group of ta _ group of fa-- ~ CreateHashMapper[]; toNewTie: Mapper--tie of ta _ tie of fa-- ~ CreateHashMapper[]; toFromPort: Mapper--port of ta _ port of fa-- ~ CreateHashMapper[]; tpToWire: Mapper--port of ta _ wire-- ~ CreateHashMapper[]; doomedArrayPorts: Set--of port of fa-- ~ CreateHashSet[]; toAns: Mapper--ArrayWire of from _ ArrayWireAns-- ~ CreateHashMapper[]; AddConnection: PROC [fai: ArrayIndex, fg: Group, tai: ArrayIndex, tg: Group] ~ { IF fg=NIL OR tg=NIL THEN ERROR; {fp: Port ~ GetArrayPortForGroup[fromArrayCT, fa, fai, fg, TRUE]; tp: Port ~ GetArrayPortForGroup[toArrayCT, ta, tai, tg, TRUE]; w: Wire ~ IF tg.ports # NIL THEN tg.ports.first.wire ELSE fg.ports.first.wire; IF fp=NIL OR tp=NIL THEN ERROR; SELECT toFromPort.Map[tp] FROM NIL => IF NOT toFromPort.PutMapping[tp, fp] THEN ERROR; fp => NULL; ENDCASE => ERROR; IF w=NIL THEN ERROR; [] _ tpToWire.PutMapping[tp, w]; }}; IF IsIncompleteArray[fromArrayCT] THEN ERROR; IF ta.groupingParmses # fa.groupingParmses THEN ERROR; IF fromArrayCT.firstArray # NIL THEN ERROR nyet--we only split one level of arraying--; {MaybeAddGroup: PROC [domain, range: REF ANY] ~ { wire: Wire ~ NARROW[domain]; wa: WireAns ~ NARROW[range]; IF NOT wa.analyzed THEN ERROR; IF wa.counterpart=NIL THEN ERROR; SELECT wa.doFrom FROM addPort => MakeGroupsForPort[fa, wa.fromPort]; leave => NULL; dePort => UnrolePort[fa, wa.fromPort]; ENDCASE => ERROR; SELECT wa.doTo FROM addPort => IF wa.fromPort=NIL THEN ERROR; ignore => NULL; ENDCASE => ERROR; }; wag.anses.EnumerateMapping[MaybeAddGroup]; }; {PromoteAnswers: PROC [domain, range: REF ANY] ~ { wire: Wire ~ NARROW[domain]; wa: WireAns ~ NARROW[range]; PerPort: PROC [p: Port--of from--] ~ { EnumerateGroupsContainingPort[fa, p, PerGroup]; }; PerGroup: PROC [gi2: Nat2, gi: NATURAL, g: Group] ~ { EnumerateArrayWiresContainingGroup[fa, gi2, gi, g, PerAW, TRUE]; }; PerAW: PROC [aw: ArrayWire] ~ { awa: ArrayWireAns _ NARROW[toAns.Map[aw]]; ExploreGroup: PROC [gi2: Nat2, gi: NATURAL, g: Group, membership: BoolSeq--group instance index _ isMember--] ~ { FOR pl: PortList _ g.ports, pl.rest WHILE pl # NIL DO ep: Port--of from-- ~ pl.first; wa: WireAns ~ NARROW[wag.anses.Map[ep.wire]]; IF ep.wire=NIL THEN ERROR; IF wa.sawElse THEN awa.sawElse _ TRUE; ENDLOOP; }; IF awa=NIL AND NOT toAns.PutMapping[aw, awa _ NEW [ArrayWireAnsPrivate _ [aw]]] THEN ERROR; IF NOT awa.analyzed THEN RETURN; awa.analyzed _ TRUE; IF (awa.sawBord _ aw.ports.Size[]>0) THEN awa.fromPort _ NARROW[aw.ports.PickOne[]]; awa.sawBords _ aw.ports.Size[] > 1; EnumerateGroupsOfArrayWire[fa, aw, ExploreGroup]; }; IF NOT wa.analyzed THEN ERROR; IF wa.counterpart=NIL THEN ERROR; EnumeratePortsForWire[wire, PerPort]; domain _ domain}; wag.anses.EnumerateMapping[PromoteAnswers]; }; FOR gi: NATURAL IN [0 .. fa.groupingses.length) DO fgs: Groupings ~ NARROW[fa.groupingses[gi]]; tgs: Groupings ~ NARROW[ta.groupingses[gi]]; MoveGroup: PROC [ra: REF ANY] ~ { fg: Group ~ NARROW[ra]; tg: Group ~ MakeGroup[ta, fg.gi2, tgs]; IF NOT toNewGroup.PutMapping[fg, tg] THEN ERROR; IF NOT toOldGroup.PutMapping[tg, fg] THEN ERROR; FOR pl: PortList _ fg.ports, pl.rest WHILE pl # NIL DO fp: Port ~ NARROW[pl.first]; fw: Wire ~ fp.wire; wa: WireAns ~ NARROW[wag.anses.Map[fw]]; IF fw=NIL THEN ERROR; IF wa # NIL THEN { tp: Port ~ wa.toPort; SELECT wa.doTo FROM addPort => AddPortToGroup[ta, gi, tp, tg, FALSE]; ignore => NULL; ENDCASE => ERROR; SELECT wa.doFrom FROM dePort => RemovePortFromGroup[fa, gi, fp, fg]; leave, addPort => NULL; ENDCASE => ERROR; }; ENDLOOP; ra _ ra; }; fgs.groups.Enumerate[MoveGroup]; ENDLOOP; {MoveTie: PROC [d: Dim, phase: Nat2, jgi: NATURAL, jgi2: Nat2, j: Joint, tie: Tie] ~ { fj: Joint ~ j; ftie: Tie ~ tie; IF ftie.groups[low]#NIL AND ftie.groups[high]#NIL THEN { tj: Joint ~ GetArrayJoint[ta, d, phase]; ttie: Tie ~ NEW [TiePrivate _ [groups: [ low: NARROW[toNewGroup.Map[ftie.groups[low]]], high: NARROW[toNewGroup.Map[ftie.groups[high]]]]]]; IF ttie.groups[low]=NIL OR ttie.groups[high]=NIL THEN ERROR; AddTie[tj, jgi, ttie]; IF NOT toNewTie.PutMapping[ftie, ttie] THEN ERROR; }; }; EnumerateTies[fa, MoveTie]; }; FlushArrayWires[fa, doomedArrayPorts]; {ConnectGroup: PROC [domain, range: REF ANY] ~ { fg: Group ~ NARROW[domain]; tg: Group ~ NARROW[range]; IF tg.gi2#fg.gi2 THEN ERROR; IF tg.ports#NIL AND (fg.ports#NIL OR fa.toWire.Fetch[fg].val#NIL) THEN { air: Range2 ~ Gi2ToAir[fa, fg.gi2].air; FOR f: INT IN [air[Foo].min .. air[Foo].maxPlusOne) DO FOR b: INT IN [air[Bar].min .. air[Bar].maxPlusOne) DO ai: ArrayIndex ~ [f, b]; IF fg.ports#NIL OR GetArrayPortForGroup[fromArrayCT, fa, ai, fg, FALSE]#NIL THEN AddConnection[ai, fg, ai, tg]; ENDLOOP ENDLOOP; }; }; toNewGroup.EnumerateMapping[ConnectGroup]; }; {ConnectTie: PROC [d: Dim, phase: Nat2, jgi: NATURAL, jgi2: Nat2, j: Joint, tie: Tie] ~ { ftie: Tie ~ tie; ttie: Tie ~ NARROW[toNewTie.Map[ftie]]; IF ttie#NIL AND ((ttie.groups[low].ports=NIL) # (ttie.groups[high].ports=NIL)) AND ((ttie.groups[low].ports=NIL) # (ftie.groups[low].ports=NIL)) AND ((ttie.groups[high].ports=NIL) # (ftie.groups[high].ports=NIL)) THEN { o: Dim ~ OtherDim[d]; toHigh: BOOL ~ ttie.groups[low]=NIL; FOR perp: NATURAL IN [0 .. fa.size[o]) DO FOR para: NATURAL IN [0 .. fa.size[d]-1) DO lai: ArrayIndex ~ ConsInt2[d, para, perp]; hai: ArrayIndex ~ Int2Tweak[lai, d, 1]; IF toHigh THEN AddConnection[lai, ftie.groups[low], hai, ttie.groups[high]] ELSE AddConnection[lai, ftie.groups[high], hai, ttie.groups[low]]; ENDLOOP ENDLOOP; }; }; EnumerateTies[fa, ConnectTie]; }; TrimEmptyGroups[fa]; TrimEmptyGroups[ta]; {FixInstance: PROC [fci: CellInstance] ~ { parentCT: CellType ~ fci.containingCT; tci: CellInstance ~ Instantiate[toArrayCT, parentCT]; ConnectChildren: PROC [parent: Port, do: BOOL] RETURNS [done: BOOL] ~ { done _ FALSE; FOR tp: Port _ FirstChildPort[parent], NextChildPort[tp] WHILE tp # NIL DO IF toFromPort.Map[tp] # NIL THEN done _ TRUE; ENDLOOP; IF done OR do THEN { FOR tp: Port _ FirstChildPort[parent], NextChildPort[tp] WHILE tp # NIL DO fp: Port--of fa-- ~ NARROW[toFromPort.Map[tp]]; IF fp # NIL THEN { w: Wire ~ FindTransitiveConnection[fci, fp]; IF w=NIL THEN ERROR; AddEdges[[tci, w], tp]; } ELSE IF NOT ConnectChildren[tp, FALSE] THEN { w: Wire ~ CreateWire[containingCT: parentCT, copy: NARROW[tpToWire.Map[tp]]]; AddEdges[[tci, w], tp]; }; ENDLOOP; }; }; [] _ ConnectChildren[toArrayCT.port, TRUE]; UnlinkPorts[fci, doomedArrayPorts]; IF pairs.PutOTOMapping[fci, tci]#[TRUE, TRUE] THEN ERROR; }; EnumerateInstances[fromArrayCT, FixInstance]; }; {PerDoomedArrayPort: PROC [ra: REF ANY] ~ { p: Port--of fromArrayCT-- ~ NARROW[ra]; RemovePort[p]; }; doomedArrayPorts.Enumerate[PerDoomedArrayPort]; }; }; from.EnumerateArrays[SplitArray]; }; FOR i: NATURAL IN [0 .. cs.length) DO DeleteVertex[NARROW[cs[i]]]; ENDLOOP; {KillWireAndPort: PROC [domain, range: REF ANY] ~ { wire: Wire ~ NARROW[domain]; wa: WireAns ~ NARROW[range]; IF NOT wa.analyzed THEN ERROR; IF wa.counterpart=NIL THEN ERROR; SELECT wa.doFrom FROM addPort => NULL; leave => NULL; dePort => RemovePort[wa.fromPort]; ENDCASE => ERROR; IF NOT (wa.sawElse OR wa.sawBords) THEN DeleteVertex[wire]; domain _ domain; }; wag.anses.EnumerateMapping[KillWireAndPort]; }; to _ to; }}; Select: PROC [reln: Asserting.Term, position: NATURAL, from: Assertions] RETURNS [terms: Asserting.Terms] ~ { Filter: PROC [assertion: Asserting.Assertion] ~ { these: Asserting.Terms _ Asserting.TermsOf[assertion]; THROUGH [1 .. position) DO these _ these.rest ENDLOOP; terms _ CONS[these.first, terms]; }; Asserting.EnumerateAssertionsAbout[reln, from, Filter]; }; RopeCrossCat: PROC [lolora: LOLORA] RETURNS [ans: ROPE] ~ { ans _ NIL; FOR lolora _ lolora, lolora.rest WHILE lolora # NIL DO lora: LORA _ NARROW[lolora.first]; subAns: ROPE _ NIL; n: NATURAL _ 0; FOR lora _ lora, lora.rest WHILE lora # NIL DO n _ n + 1; SELECT n FROM 1 => NULL; 2 => subAns _ Rope.Cat["{", subAns, "|"]; ENDCASE => subAns _ subAns.Concat["|"]; subAns _ subAns.Concat[NARROW[lora.first]]; ENDLOOP; IF n > 1 THEN subAns _ subAns.Concat["}"]; IF ans # NIL THEN ans _ ans.Concat["-"]; ans _ ans.Concat[subAns]; ENDLOOP; ans _ ans; }; PortPairEqual: PROC [key1, key2: REF ANY] RETURNS [BOOL] --RefTab.EqualProc-- ~ { pp1: PortPair ~ NARROW[key1]; pp2: PortPair ~ NARROW[key2]; RETURN [pp1^ = pp2^]; }; HashPortPair: PROC [key: REF ANY] RETURNS [CARDINAL] -- RefTab.HashProc-- ~ { pp: PortPair ~ NARROW[key]; RETURN [HashRefI[pp.from] + HashRefI[pp.to]]}; END. zLichenSplitMerge.Mesa Last tweaked by Mike Spreitzer on April 29, 1987 2:53:03 pm PDT Data for the wire connected to a port of a vertex to be moved. A border port found, if any. The border port created on the splinter cell type. Connected to the border at least once Connected to the border by more than one port. Connected to something other than the border & subjects. Κr˜™Icodešœ?™?—J˜KšΟk œ‹˜”K˜šΡbnxœœ˜Kšœx˜Kšœ˜—K˜Kšœœr˜|K˜Kšœ œœ˜%Kšœœœ˜0K˜šœœœ˜-K™>—šœœœ˜$K˜Kšœ œœ˜šœœ˜K™—šœœ˜K™2—šœ œœ˜K™%—šœ œœ˜K™.—šœ œ˜K™8—Kšœ˜—K˜šΟn œœœΟcœœ% Πcm œ˜žšœœ˜-Kšœ˜Kšœ%˜%šœœ˜"Kšœ/˜/Kšœ˜Kšœ˜—K˜Kšœ˜—Kšœœ ˜Kšœ  ‘  œ˜2Kšœ  ‘ œ˜5Kšœ œ˜9Kšœ œ˜5Kšœ œœ˜Kšœœ˜ Kšœ œ˜&Kšœœ˜ Kšœœ˜ š Ÿœœœœœ˜;Kš œœœœœœ˜/Kš œœœœœœ9˜€Kšœ œ*˜:K˜ Kšœ˜Kš œœœœœœ˜6K˜ K˜—Kšœ˜Kšœœœ˜K˜Kšœœ œœ˜1Kš œœœœ œ˜DKšœ œ3˜CKšœ!˜!KšœAœ œ˜WKšœ˜Kš œœœœœ˜4š œŸœœœœ˜,Kšœ œ ˜Kšœœ˜Kšœœ œœ˜Kšœœœœ˜#Kšœo˜ošœ ˜šœ ˜ Kšœ7˜7Kšœ1˜1Kšœ9˜9—Kšœœ˜Kšœœ˜—šœ ˜šœ ˜ Kšœ=˜=Kšœ-˜-Kšœ?˜?—Kšœ œ˜Kšœœ˜—K˜—Kšœ%˜%K˜šœœœ˜$Kšœœ ˜%Kšœ9˜9šŸœœ"˜6Kšœ3œ˜:Kšœœ œœ˜Kšœ*˜*K˜—K˜-Kšœ˜—šœŸ œœ˜-K˜ Kšœœœ5˜JKšœœ˜'Kšœ2œ0œœ˜oKšœ˜Kšœ ‘ œ˜EKšœ ‘ œ˜EKšœ  ‘  œ˜?Kšœ  ‘  œ˜CKšœ  ‘ œ˜;Kšœ œ˜9Kšœ  ‘ œ˜GšŸ œœ=˜PKš œœœœœœ˜Kšœ;œ˜AKšœ8œ˜>Kš œ œ œœœ˜NKš œœœœœœ˜šœ˜Kš œœœœœ˜7Kšœœ˜ Kšœœ˜—Kšœœœœ˜Kšœ ˜ K˜—Kšœ œœ˜-Kšœ)œœ˜6Kš œœœœ 'œ˜Wš œŸ œœœœ˜1Kšœ œ ˜Kšœœ˜Kšœœ œœ˜Kšœœœœ˜!šœ ˜Kšœ.˜.Kšœ œ˜Kšœ&˜&Kšœœ˜—šœ ˜Kš œ œ œœœ˜)Kšœ œ˜Kšœœ˜—K˜—Kšœ*˜*K˜š œŸœœœœ˜2Kšœ œ ˜Kšœœ˜šŸœœ   œ˜&Kšœ/˜/K˜—šŸœœœ˜5Kšœ:œ˜@K˜—šŸœœ˜Kšœœ˜*š Ÿ œœœ ‘  œ˜qšœ!œœ˜5Kšœ  œ ˜Kšœœ˜-Kšœ œœœ˜Kšœ œœ˜&Kšœ˜—K˜—Kšœœœœœœœ˜[Kšœœœœ˜ Kšœœ˜Kšœ#œœ˜TKšœ#˜#K˜1K˜—Kšœœ œœ˜Kšœœœœ˜!Kšœ%˜%Kšœ˜—Kšœ+˜+K˜šœœœ˜2Kšœœ˜,Kšœœ˜,šŸ œœœœ˜!Kšœ œ˜Kšœ'˜'Kšœœœœ˜0Kšœœœœ˜0šœ"œœ˜6Kšœ œ ˜K˜Kšœœ˜(Kšœœœœ˜šœœœ˜K˜šœ ˜Kšœ*œ˜1Kšœ œ˜Kšœœ˜—šœ ˜Kšœ.˜.Kšœœ˜Kšœœ˜—K˜—Kšœ˜—K˜K˜—Kšœ ˜ Kšœ˜—šœŸœœœ%˜VK˜K˜š œœœœœ˜8Kšœ(˜(šœ œ˜(Kšœœ#˜.Kšœœ'˜3—Kš œœœœœœ˜œ>˜ŽKšœœ˜—K˜—K˜—Kšœ˜K˜K˜K˜šœŸ œœ˜*K˜&K˜5š Ÿœœœœœ˜GKšœœ˜ šœ6œœ˜JKšœœœœ˜-Kšœ˜—šœœœ˜šœ6œœ˜JKšœ  œœ˜/šœœœ˜Kšœ,˜,Kšœœœœ˜K˜K˜—š œœœœœ˜-Kšœ3œ˜MK˜K˜—Kšœ˜—K˜—K˜—Kšœ%œ˜+Kšœ#˜#Kš œ œœœœ˜9K˜—K˜-K˜š œŸœœœœ˜+Kšœ œœ˜'Kšœ˜K˜—Kšœ/˜/K˜K˜—K˜!K˜šœœœ˜%Kšœ œ ˜Kšœ˜—š œŸœœœœ˜3Kšœ œ ˜Kšœœ˜Kšœœ œœ˜Kšœœœœ˜!šœ ˜Kšœ œ˜Kšœ œ˜Kšœ"˜"Kšœœ˜—Kšœœ œœ˜;K˜K˜—Kšœ,˜,K˜K˜K˜—K˜šŸœœ"œœ˜mšŸœœ%˜1Kšœ6˜6Kšœœœ˜6Kšœœ˜!K˜—Kšœ7˜7K˜—K˜š Ÿ œœ œœœ˜;Kšœœ˜ šœœ œ˜6Kšœœœ˜"Kšœœœ˜Kšœœ˜šœœœ˜.K˜ šœ˜ Kšœœ˜ K˜)Kšœ ˜'—Kšœœ˜+Kšœ˜—Kšœœ˜*Kšœœœ˜(K˜Kšœ˜—K˜ K˜—K˜šŸ œœœœœœ œ˜QKšœœ˜Kšœœ˜Kšœ˜K˜—K˜šŸ œœœœœœ œ˜MKšœœ˜Kšœ(˜.—K˜Kšœ˜—…—0˜B„