jnodes={{27},{3,27},{11,16},{2,19,23},{3,6,27},{10,13,18},{10,18,44},{11,12,16},{13,14,15},{13,15,18},{15,16,18},{27,28,29},{3,4,20,27},{3,6,27,28},{5,17,21,22},{8,22,23,24},{9,22,23,24},{22,23,24,25},{30,31,32,35},{30,32,35,36},{32,40,43,47},{44,45,47,48},{45,46,47,48},{1,3,5,6,28},{3,6,23,27,28},{6,20,32,40,44},{7,17,19,20,24},{20,40,44,47,48},{26,27,28,32,44},{32,33,38,39,41},{32,34,35,37,40},{32,34,40,41,43},{32,40,41,43,47},{40,41,42,43,47},{3,5,17,22,23,24},{3,6,17,23,27,28},{6,18,20,27,28,44},{6,20,27,28,32,44},{6,20,32,40,44,47},{32,34,35,36,37,40},{32,34,37,39,41,43},{32,34,37,40,41,43},{32,37,38,39,41,43},{3,5,6,17,23,24,28},{3,6,17,20,23,27,28},{6,17,18,19,20,27,28},{3,6,17,20,23,24,27,28},{6,17,19,20,23,24,27,28}};

jedges={{{27},{3,27}},{{3,27},{27}},{{3,27},{3,6,27}},{{11,16},{11,12,16}},{{11,16},{15,16,18}},{{2,19,23},{6,17,19,20,23,24,27,28}},{{3,6,27},{3,27}},{{3,6,27},{3,6,27,28}},{{10,13,18},{10,18,44}},{{10,13,18},{13,15,18}},{{10,18,44},{10,13,18}},{{10,18,44},{6,18,20,27,28,44}},{{11,12,16},{11,16}},{{13,14,15},{13,15,18}},{{13,15,18},{10,13,18}},{{13,15,18},{13,14,15}},{{13,15,18},{15,16,18}},{{15,16,18},{11,16}},{{15,16,18},{13,15,18}},{{27,28,29},{3,6,27,28}},{{3,4,20,27},{3,6,17,20,23,27,28}},{{3,6,27,28},{3,6,27}},{{3,6,27,28},{27,28,29}},{{3,6,27,28},{3,6,23,27,28}},{{5,17,21,22},{3,5,17,22,23,24}},{{8,22,23,24},{3,5,17,22,23,24}},{{9,22,23,24},{3,5,17,22,23,24}},{{22,23,24,25},{3,5,17,22,23,24}},{{30,31,32,35},{30,32,35,36}},{{30,32,35,36},{30,31,32,35}},{{30,32,35,36},{32,34,35,36,37,40}},{{32,40,43,47},{32,40,41,43,47}},{{32,40,43,47},{6,20,32,40,44,47}},{{44,45,47,48},{45,46,47,48}},{{44,45,47,48},{20,40,44,47,48}},{{45,46,47,48},{44,45,47,48}},{{1,3,5,6,28},{3,5,6,17,23,24,28}},{{3,6,23,27,28},{3,6,27,28}},{{3,6,23,27,28},{3,6,17,23,27,28}},{{6,20,32,40,44},{6,20,27,28,32,44}},{{6,20,32,40,44},{6,20,32,40,44,47}},{{7,17,19,20,24},{6,17,19,20,23,24,27,28}},{{20,40,44,47,48},{44,45,47,48}},{{20,40,44,47,48},{6,20,32,40,44,47}},{{26,27,28,32,44},{6,20,27,28,32,44}},{{32,33,38,39,41},{32,37,38,39,41,43}},{{32,34,35,37,40},{32,34,35,36,37,40}},{{32,34,35,37,40},{32,34,37,40,41,43}},{{32,34,40,41,43},{32,40,41,43,47}},{{32,34,40,41,43},{32,34,37,40,41,43}},{{32,40,41,43,47},{32,40,43,47}},{{32,40,41,43,47},{32,34,40,41,43}},{{32,40,41,43,47},{40,41,42,43,47}},{{40,41,42,43,47},{32,40,41,43,47}},{{3,5,17,22,23,24},{5,17,21,22}},{{3,5,17,22,23,24},{8,22,23,24}},{{3,5,17,22,23,24},{9,22,23,24}},{{3,5,17,22,23,24},{22,23,24,25}},{{3,5,17,22,23,24},{3,5,6,17,23,24,28}},{{3,6,17,23,27,28},{3,6,23,27,28}},{{3,6,17,23,27,28},{3,6,17,20,23,27,28}},{{6,18,20,27,28,44},{10,18,44}},{{6,18,20,27,28,44},{6,20,27,28,32,44}},{{6,18,20,27,28,44},{6,17,18,19,20,27,28}},{{6,20,27,28,32,44},{6,20,32,40,44}},{{6,20,27,28,32,44},{26,27,28,32,44}},{{6,20,27,28,32,44},{6,18,20,27,28,44}},{{6,20,32,40,44,47},{32,40,43,47}},{{6,20,32,40,44,47},{6,20,32,40,44}},{{6,20,32,40,44,47},{20,40,44,47,48}},{{32,34,35,36,37,40},{30,32,35,36}},{{32,34,35,36,37,40},{32,34,35,37,40}},{{32,34,37,39,41,43},{32,34,37,40,41,43}},{{32,34,37,39,41,43},{32,37,38,39,41,43}},{{32,34,37,40,41,43},{32,34,35,37,40}},{{32,34,37,40,41,43},{32,34,40,41,43}},{{32,34,37,40,41,43},{32,34,37,39,41,43}},{{32,37,38,39,41,43},{32,33,38,39,41}},{{32,37,38,39,41,43},{32,34,37,39,41,43}},{{3,5,6,17,23,24,28},{1,3,5,6,28}},{{3,5,6,17,23,24,28},{3,5,17,22,23,24}},{{3,5,6,17,23,24,28},{3,6,17,20,23,24,27,28}},{{3,6,17,20,23,27,28},{3,4,20,27}},{{3,6,17,20,23,27,28},{3,6,17,23,27,28}},{{3,6,17,20,23,27,28},{3,6,17,20,23,24,27,28}},{{6,17,18,19,20,27,28},{6,18,20,27,28,44}},{{6,17,18,19,20,27,28},{6,17,19,20,23,24,27,28}},{{3,6,17,20,23,24,27,28},{3,5,6,17,23,24,28}},{{3,6,17,20,23,24,27,28},{3,6,17,20,23,27,28}},{{3,6,17,20,23,24,27,28},{6,17,19,20,23,24,27,28}},{{6,17,19,20,23,24,27,28},{2,19,23}},{{6,17,19,20,23,24,27,28},{7,17,19,20,24}},{{6,17,19,20,23,24,27,28},{6,17,18,19,20,27,28}},{{6,17,19,20,23,24,27,28},{3,6,17,20,23,24,27,28}}};
jedgesU=Union[Sort/@jedges];


(* DownValues<->Dicts and Assert from utils *) 
(* extracts key/value from the downvalue *)
index[downvalue_,dict_]:=(downvalue[[1]]/.HoldPattern[dict[x_]]->x)//ReleaseHold;
value[downvalue_]:=downvalue[[-1]];
indices[dict_]:=Map[#[[1]]/.{HoldPattern[dict[x_]]->x}&,DownValues[dict]]//ReleaseHold;
values[dict_]:=Map[#[[-1]]&,DownValues[dict]];
items[dict_]:=Map[{index[#,dict],value[#]}&,DownValues[dict]];
indexQ[dict_,index_]:=If[MatchQ[dict[index],HoldPattern[dict[index]]],False,True];

$AssertFunction=(Message[Assert::asrtf,#1];Print[#1];Abort[])&;

(* flattens one level of the list *)
fl1[a_List]:=Flatten[a,1];

(* finds average difference between values with some random numeric instantiations of unknowns *)
aveDiff[expr1_,expr2_,vars_]:=(
eval[vals_]:=expr1-expr2/.Thread[vars->vals];
numTries=10;
args=RandomReal[{-1,1},{numTries,\[LeftBracketingBar]vars\[RightBracketingBar]}];
Mean[eval/@args]
);

(* take dictionary with multiple values per key, find keys matching given value *)
(* u. in jtCheck *)
reverseLookup[multimap_,val_]:=Select[indices[multimap],val∈multimap[#]&]



(* used in consistency checks, jtCheck *)
SetAttributes[AllTrue,HoldRest];
SetAttributes[AnyTrue,HoldRest];

(* u. debugging AllTrue *)
SetAttributes[myTable,HoldRest];
myTable[{var_Symbol,vals_List},expr_]:=ReleaseHold[Hold[expr]/.HoldPattern[var]->#]&/@vals;

AllTrue[{var_Symbol,lis_List},expr_]:=LengthWhile[lis,TrueQ[ReleaseHold[Hold[expr]/.HoldPattern[var]->#]]&]==Length[lis];
AnyTrue[{var_Symbol,lis_List},expr_]:=LengthWhile[lis,Not[TrueQ[ReleaseHold[Hold[expr]/.HoldPattern[var]->#]]]&]<Length[lis];
(* u. extracting value lists in sumProduct *)
restrict[vars_,svars_,vals_]:=(Assert[\[LeftBracketingBar]vars\[RightBracketingBar]==\[LeftBracketingBar]vals\[RightBracketingBar],"restrict: val/var mismatch"];Extract[vals,Flatten[Position[vars,#]&/@svars,1]])

(* u. in sumProduct *)
Tilde[a_,b_]:=Join[a,b];
(* used in building messages *)
isSet[a_]:=(Head[a]==List);
Subset[a_,b_]:=(Assert[isSet[a]∧isSet[b],"Subset: not a set ",a,b];a⋂b==a)
NotSubset[a_,b_]:=Not[Subset[a,b]];

(* u. in sequenceElimination, order-preserving intersection *)
intersect[a_,b_]:=Select[a,MemberQ[b,#]&];

Backslash[a_,b_]:=(Assert[ListQ[a]∧ListQ[b]];Complement[a,b])
BracketingBar[a_List]:=Length[a];
(* u. in sumProduct *)
(*Unprotect[Element];Element[a_,list_]:=MemberQ[list,a];*)
(* u. jtCheck (replaced with alltrue, not needed?) *)
SubsetAny[a_,sets_]:=Module[{s},AnyTrue[{s,sets},a⊂s]];

(* custom ∈ *)
Needs["Notation`"];
MyElement[a_,b_]:=MemberQ[b,a];
NotMyElement[a_,b_]:=Not[MyElement[a,b]];
InfixNotation[(NotationTemplateTag[∈]),MyElement];
InfixNotation[(NotationTemplateTag[∉]),NotMyElement];
(* add custom alias to all open notebooks *)
AddInputAlias[(NotationTemplateTag[∈]),"in",#]&/@Notebooks[];

jchildren[i_,j_]:=Select[jedgesU,(j∈# ∧i∉#)&];
jchildren2[i_,j_]:=Flatten[#∖{j}]&/@jchildren[i,j]

extendLink[bag_,parentBag_,node_]:=(
If[node∈bag,Sow[bag];extendLink[#,bag,node]&/@jchildren2[parentBag,bag]]
);
extendLink2[node_]:=extendLink2[First[Select[jnodes,node∈#&]],node];
extendLink2[bag_,node_]:=
Reap[extendLink[bag,{},node]]//Last//Last

markLink[i_]:=
AppendTo[linkAssign[#],i]&/@links[[i]];

getOffset[bag_,k_]:=(
localPos=Position[linkAssign[bag],k]//Flatten//First;
{localPos*.20,0}
)


drawLink[k_]:=(
getCoords[bag_]:=getOffset[bag,k]+(bag/.bcoords);
lines=Line[getCoords/@#]&/@linkTree[links[[k]]];
Graphics[{linkColors[[k]],Thickness[.010],Opacity[.5],JoinForm["Round"],CapForm["Round"]}~Join~lines]
)

drawBag[k_]:=(
bag=jnodes[[k]];
len=Length[linkAssign[bag]];
offsets={#*.20,0}&/@Range[len];
points=Point[#+(bag/.bcoords)&/@offsets];
line=Line[{First[offsets]+(bag/.bcoords),Last[offsets]+(bag/.bcoords)}];
lendpoint=Circle[First[offsets]+(bag/.bcoords),.05];
rendpoint=Circle[Last[offsets]+(bag/.bcoords),.05];
Graphics[{Opacity[.5],Thickness[.005],line,lendpoint,rendpoint}]
);

getLabelCoordinateMap[gp1_]:=Module[{points,labels,actualLabels,coordAliases,actualCoords},points=Cases[gp1,GraphicsComplex[points_,__]:>points,Infinity]//First;
labels=Cases[gp1,Text[___],Infinity];
actualLabels=labels[[All,1,1]];
coordAliases=labels[[All,2]];
actualCoords=points[[coordAliases]];
Thread[actualLabels->actualCoords]];

(* get a tree of link *)
linkTree[link_]:=(Select[jedges,#⊂link&]);
plotLink[lnum_]:=GraphPlot[Rule@@@linkTree[links[[lnum]]],VertexCoordinateRules->bcoords,VertexLabeling->False,EdgeRenderingFunction->({linkColors[[lnum]],Thickness[.05],Opacity[.5],Line[#1]}&)];

leafCount[link_]:=(
tr=linkTree[link];
deg[node_]:=Length[Select[tr,node∈#&]];
Length[Select[link,deg[#]==1&]]
)


gp=TreePlot[Rule@@@jedges,VertexLabeling->True];
bcoords=getLabelCoordinateMap[gp];

links=extendLink2[#]&/@Union[Flatten[jnodes]];
links=Select[links,Length[#]>1&];

Clear[linkAssign];
(linkAssign[#]={})&/@jnodes;
markLink/@Range[Length[links]];

allColors=ColorData["Crayola","ColorList"];
SeedRandom[3];
linkColors=RandomSample[allColors,Length[links]];
linkG=drawLink/@Range[Length[links]];
bagG=drawBag/@Range[Length[jnodes]];
Show[linkG,bagG]