ngraph.leiden
Version:
Leiden/Louvain community detection for ngraph.graph (JS)
2 lines (1 loc) • 17.1 kB
JavaScript
;Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const tt=require("ngraph.graph");function K(t,o={}){const n=o.linkWeight||(w=>w.data&&typeof w.data.weight=="number"?w.data.weight:1),s=o.nodeSize||(w=>w.data&&typeof w.data.size=="number"?w.data.size:1),i=!!o.directed,l=o.baseNodeIds,m=[],g=new Map;if(Array.isArray(l)&&l.length>0)for(let w=0;w<l.length;w++){const S=l[w];if(!(t.getNode?t.getNode(S):null))throw new Error("Multilayer graphs must share identical node ids. Missing: "+S);g.set(S,w),m.push(S)}else t.forEachNode(w=>{g.set(w.id,m.length),m.push(w.id)});const e=m.length,u=new Float64Array(e),d=new Float64Array(e),f=new Float64Array(e),y=new Float64Array(e),h=new Array(e),a=new Array(e);for(let w=0;w<e;w++)h[w]=[],a[w]=[];if(i)t.forEachLink(w=>{const S=g.get(w.fromId),I=g.get(w.toId);if(S==null||I==null)return;const z=+n(w)||0;S===I&&(d[S]+=z),h[S].push({to:I,w:z}),a[I].push({from:S,w:z}),f[S]+=z,y[I]+=z});else{const w=new Map;t.forEachLink(S=>{const I=g.get(S.fromId),z=g.get(S.toId);if(I==null||z==null)return;const T=+n(S)||0;if(I===z){d[I]+=T;return}const M=I<z?I:z,p=I<z?z:I,q=M+":"+p;let N=w.get(q);N||(N={sum:0,seenAB:0,seenBA:0},w.set(q,N)),N.sum+=T,I===M?N.seenAB=1:N.seenBA=1});for(const[S,I]of w.entries()){const[z,T]=S.split(":"),M=+z,p=+T,q=(I.seenAB?1:0)+(I.seenBA?1:0),N=q>0?I.sum/q:0;N!==0&&(h[M].push({to:p,w:N}),h[p].push({to:M,w:N}),a[M].push({from:p,w:N}),a[p].push({from:M,w:N}),f[M]+=N,f[p]+=N,y[M]+=N,y[p]+=N)}for(let S=0;S<e;S++){const I=d[S];I!==0&&(h[S].push({to:S,w:I}),a[S].push({from:S,w:I}),f[S]+=I,y[S]+=I)}}t.forEachNode(w=>{const S=g.get(w.id);u[S]=+s(w)||0});const b=f.reduce((w,S)=>w+S,0);function F(w,S){const I=h[w];for(let z=0;z<I.length;z++)S(I[z].to,I[z].w)}return{n:e,nodeIds:m,idToIndex:g,size:u,selfLoop:d,strengthOut:f,strengthIn:y,outEdges:h,inEdges:a,directed:i,totalWeight:b,forEachNeighbor:F}}function Y(t){const o=t.n,n=new Int32Array(o);for(let r=0;r<o;r++)n[r]=r;let s=o,i=new Float64Array(s),l=new Int32Array(s),m=new Float64Array(s),g=new Float64Array(s),e=new Float64Array(s),u=new Float64Array(s),d=new Int32Array(o),f=0,y=new Float64Array(o),h=new Float64Array(o),a=new Float64Array(o),b=new Uint8Array(o);function F(r){if(r<=i.length)return;const c=Math.max(r,Math.ceil(i.length*1.5));i=H(i,c),l=ut(l,c),m=H(m,c),g=H(g,c),e=H(e,c),u=H(u,c)}function w(){i.fill(0),l.fill(0),m.fill(0),g.fill(0),e.fill(0),u.fill(0);for(let r=0;r<o;r++){const c=n[r];i[c]+=t.size[r],l[c]+=1,t.directed?(e[c]+=t.strengthOut[r],u[c]+=t.strengthIn[r]):g[c]+=t.strengthOut[r],t.selfLoop[r]!==0&&(m[c]+=t.selfLoop[r])}if(t.directed)for(let r=0;r<o;r++){const c=n[r],x=t.outEdges[r];for(let A=0;A<x.length;A++){const{to:E,w:_}=x[A];c===n[E]&&(m[c]+=_)}}else for(let r=0;r<o;r++){const c=n[r],x=t.outEdges[r];for(let A=0;A<x.length;A++){const{to:E,w:_}=x[A];E<=r||c===n[E]&&(m[c]+=_)}}}function S(){for(let r=0;r<f;r++){const c=d[r];b[c]=0,y[c]=0,h[c]=0,a[c]=0}f=0}function I(r){b[r]||(b[r]=1,d[f++]=r)}function z(r){S();const c=n[r];if(I(c),t.directed){const x=t.outEdges[r];for(let E=0;E<x.length;E++){const _=x[E].to,W=x[E].w,k=n[_];I(k),h[k]+=W}const A=t.inEdges[r];for(let E=0;E<A.length;E++){const _=A[E].from,W=A[E].w,k=n[_];I(k),a[k]+=W}}else{const x=t.outEdges[r];for(let A=0;A<x.length;A++){const E=x[A].to,_=x[A].w,W=n[E];I(W),y[W]+=_}}return f}const T=t.totalWeight;function M(r,c){const x=n[r];if(c===x)return 0;const A=t.strengthOut[r],E=c<y.length&&y[c]||0,_=y[x]||0,W=c<g.length?g[c]:0,k=g[x],G=-(_/T-A*k/(T*T)),V=E/T-A*W/(T*T);return G+V}function p(r,c){const x=n[r];if(c===x)return 0;const A=t.totalWeight,E=t.strengthOut[r],_=t.strengthIn[r],W=c<a.length&&a[c]||0,k=c<h.length&&h[c]||0,G=a[x]||0,V=h[x]||0,C=c<u.length?u[c]:0,O=c<e.length?e[c]:0,D=u[x],P=e[x],B=(W+k-G-V)/A,X=(E*(C-D)+_*(O-P))/(A*A);return B-X}function q(r,c,x=1){const A=n[r];if(c===A)return 0;const E=y[A]||0,_=c<y.length&&y[c]||0,W=t.size[r]||1,k=i[A]||0,G=c<i.length?i[c]:0;return _-E-x*W*(G-k+W)}function N(r,c){const x=n[r];if(x===c)return!1;c>=s&&(F(c+1),s=c+1);const A=t.strengthOut[r],E=t.strengthIn[r],_=t.selfLoop[r],W=t.size[r];if(l[x]-=1,l[c]+=1,i[x]-=W,i[c]+=W,t.directed?(e[x]-=A,e[c]+=A,u[x]-=E,u[c]+=E):(g[x]-=A,g[c]+=A),t.directed){const k=h[x]||0,G=a[x]||0,V=c<h.length&&h[c]||0,C=c<a.length&&a[c]||0;m[x]-=k+G+_,m[c]+=V+C+_}else{const k=y[x]||0,G=y[c]||0;m[x]-=2*k+_,m[c]+=2*G+_}return n[r]=c,!0}function R(r={}){const c=[];for(let C=0;C<s;C++)l[C]>0&&c.push(C);r.keepOldOrder?c.sort((C,O)=>C-O):r.preserveMap instanceof Map?c.sort((C,O)=>{const D=r.preserveMap.get(C),P=r.preserveMap.get(O);return D!=null&&P!=null&&D!==P?D-P:D!=null&&P==null?-1:P!=null&&D==null?1:i[O]-i[C]||l[O]-l[C]||C-O}):c.sort((C,O)=>i[O]-i[C]||l[O]-l[C]||C-O);const x=new Int32Array(s).fill(-1);c.forEach((C,O)=>{x[C]=O});for(let C=0;C<n.length;C++)n[C]=x[n[C]];const A=c.length,E=new Float64Array(A),_=new Int32Array(A),W=new Float64Array(A),k=new Float64Array(A),G=new Float64Array(A),V=new Float64Array(A);for(let C=0;C<o;C++){const O=n[C];E[O]+=t.size[C],_[O]+=1,t.directed?(G[O]+=t.strengthOut[C],V[O]+=t.strengthIn[C]):k[O]+=t.strengthOut[C]}if(t.directed)for(let C=0;C<o;C++){const O=n[C],D=t.outEdges[C];for(let P=0;P<D.length;P++){const{to:B,w:X}=D[P];O===n[B]&&(W[O]+=X)}}else for(let C=0;C<o;C++){const O=n[C],D=t.outEdges[C];for(let P=0;P<D.length;P++){const{to:B,w:X}=D[P];B<=C||O===n[B]&&(W[O]+=X)}}s=A,i=E,l=_,m=W,g=k,e=G,u=V}function L(){const r=new Array(s);for(let c=0;c<s;c++)r[c]=[];for(let c=0;c<o;c++)r[n[c]].push(c);return r}function v(r){return r<i.length?i[r]:0}function j(r){return r<l.length?l[r]:0}return{n:o,get communityCount(){return s},nodeCommunity:n,communityTotalSize:i,communityNodeCount:l,communityInternalEdgeWeight:m,communityTotalStrength:g,communityTotalOutStrength:e,communityTotalInStrength:u,initializeAggregates:w,accumulateNeighborCommunityEdgeWeights:z,getCandidateCommunityCount:()=>f,getCandidateCommunityAt:r=>d[r],getNeighborEdgeWeightToCommunity:r=>y[r]||0,getOutEdgeWeightToCommunity:r=>h[r]||0,getInEdgeWeightFromCommunity:r=>a[r]||0,deltaModularityUndirected:M,deltaModularityDirected:p,deltaCPM:q,moveNodeToCommunity:N,compactCommunityIds:R,getCommunityMembers:L,getCommunityTotalSize:v,getCommunityNodeCount:j}}function H(t,o){const n=new Float64Array(o);return n.set(t),n}function ut(t,o){const n=new Int32Array(o);return n.set(t),n}function lt(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var J={exports:{}},Z;function at(){if(Z)return J.exports;Z=1,J.exports=t,J.exports.random=t,J.exports.randomIterator=g;function t(e){var u=typeof e=="number"?e:+new Date;return new o(u)}function o(e){this.seed=e}o.prototype.next=m,o.prototype.nextDouble=l,o.prototype.uniform=l,o.prototype.gaussian=n,o.prototype.random=l;function n(){var e,u,d;do u=this.nextDouble()*2-1,d=this.nextDouble()*2-1,e=u*u+d*d;while(e>=1||e===0);return u*Math.sqrt(-2*Math.log(e)/e)}o.prototype.levy=s;function s(){var e=1.5,u=Math.pow(i(1+e)*Math.sin(Math.PI*e/2)/(i((1+e)/2)*e*Math.pow(2,(e-1)/2)),1/e);return this.gaussian()*u/Math.pow(Math.abs(this.gaussian()),1/e)}function i(e){return Math.sqrt(2*Math.PI/e)*Math.pow(1/Math.E*(e+1/(12*e-1/(10*e))),e)}function l(){var e=this.seed;return e=e+2127912214+(e<<12)&4294967295,e=(e^3345072700^e>>>19)&4294967295,e=e+374761393+(e<<5)&4294967295,e=(e+3550635116^e<<9)&4294967295,e=e+4251993797+(e<<3)&4294967295,e=(e^3042594569^e>>>16)&4294967295,this.seed=e,(e&268435455)/268435456}function m(e){return Math.floor(this.nextDouble()*e)}function g(e,u){var d=u||t();if(typeof d.next!="function")throw new Error("customRandom does not match expected API: next() function is missing");return{forEach:y,shuffle:f};function f(){var h,a,b;for(h=e.length-1;h>0;--h)a=d.next(h+1),b=e[a],e[a]=e[h],e[h]=b;return e}function y(h){var a,b,F;for(a=e.length-1;a>0;--a)b=d.next(a+1),F=e[b],e[b]=e[a],e[a]=F,h(F);e.length&&h(e[0])}}return J.exports}var dt=at();const mt=lt(dt);function ft(t,o,n,s){if(o.directed)return et(t,o,n,s);const i=t.nodeCommunity[n];if(s===i)return 0;const l=o.strengthOut[n],m=o.totalWeight,g=t.getNeighborEdgeWeightToCommunity(s)||0,e=t.getNeighborEdgeWeightToCommunity(i)||0,u=s<t.communityTotalStrength.length?t.communityTotalStrength[s]:0,d=t.communityTotalStrength[i],f=-(e/m-l*d/(m*m)),y=g/m-l*u/(m*m);return f+y}function et(t,o,n,s){const i=t.nodeCommunity[n];if(s===i)return 0;const l=o.totalWeight,m=o.strengthOut[n],g=o.strengthIn[n],e=s<o.n&&t.getInEdgeWeightFromCommunity(s)||0,u=s<o.n&&t.getOutEdgeWeightToCommunity(s)||0,d=t.getInEdgeWeightFromCommunity(i)||0,f=t.getOutEdgeWeightToCommunity(i)||0,y=s<t.communityTotalInStrength.length?t.communityTotalInStrength[s]:0,h=s<t.communityTotalOutStrength.length?t.communityTotalOutStrength[s]:0,a=t.communityTotalInStrength[i],b=t.communityTotalOutStrength[i],F=(e+u-d-f)/l,w=(m*(y-a)+g*(h-b))/(l*l);return F-w}function nt(t,o){const n=o.totalWeight;let s=0;if(o.directed)for(let i=0;i<t.communityCount;i++)s+=t.communityInternalEdgeWeight[i]/n-t.communityTotalOutStrength[i]*t.communityTotalInStrength[i]/(n*n);else for(let i=0;i<t.communityCount;i++){const l=t.communityInternalEdgeWeight[i],m=t.communityTotalStrength[i];s+=l/n-m*m/(n*n)}return s}function gt(t,o,n,s,i=1){const l=t.nodeCommunity[n];if(s===l)return 0;const m=t.getNeighborEdgeWeightToCommunity(l)||0,g=s<o.n&&t.getNeighborEdgeWeightToCommunity(s)||0,e=o.size[n]||1,u=t.communityTotalSize[l]||0,d=s<t.communityTotalSize.length?t.communityTotalSize[s]:0;return g-m-i*e*(d-u+e)}function ot(t,o,n=1){let s=0;for(let i=0;i<t.communityCount;i++)s+=t.communityInternalEdgeWeight[i]-n*(t.communityNodeCount[i]*(t.communityNodeCount[i]-1))/2;return s}function it(t,o,n=1){let s=0;for(let i=0;i<t.communityCount;i++){const l=t.communityTotalSize[i]||0;s+=t.communityInternalEdgeWeight[i]-n*(l*(l-1))/2}return s}const ht=50,rt=20,st=1e-12,U={Neighbors:0,All:1,RandomAny:2,RandomNeighbor:3};function yt(t,o={}){const n=St(o);let s=t;const i=[],l=mt(n.randomSeed),m=()=>l.nextDouble(),g=K(s,{directed:n.directed,...o}),e=g.n,u=new Int32Array(e);for(let y=0;y<e;y++)u[y]=y;let d=null;if(n.fixedNodes){const y=new Uint8Array(e),h=n.fixedNodes instanceof Set?n.fixedNodes:new Set(n.fixedNodes);for(const a of h){const b=g.idToIndex.get(a);b!=null&&(y[b]=1)}d=y}for(let y=0;y<n.maxLevels;y++){const h=y===0?g:K(s,{directed:n.directed,...o}),a=Y(h);a.graph=h,a.initializeAggregates();const b=new Int32Array(h.n);for(let T=0;T<h.n;T++)b[T]=T;let F=!0,w=0;const S=n.candidateStrategyCode;for(;F;){F=!1,w++,ct(b,m);for(let T=0;T<b.length;T++){const M=b[T];if(y===0&&d&&d[M])continue;const p=a.accumulateNeighborCommunityEdgeWeights(M);let q=a.nodeCommunity[M],N=0;const R=n.maxCommunitySize;if(S===U.All)for(let L=0;L<a.communityCount;L++){if(L===a.nodeCommunity[M]||R<1/0&&a.getCommunityTotalSize(L)+h.size[M]>R)continue;const v=Q(a,M,L,n);v>N&&(N=v,q=L)}else if(S===U.RandomAny){const L=Math.min(10,Math.max(1,a.communityCount));for(let v=0;v<L;v++){const j=m()*a.communityCount|0;if(j===a.nodeCommunity[M]||R<1/0&&a.getCommunityTotalSize(j)+h.size[M]>R)continue;const r=Q(a,M,j,n);r>N&&(N=r,q=j)}}else if(S===U.RandomNeighbor){const L=Math.min(10,Math.max(1,p));for(let v=0;v<L;v++){const j=a.getCandidateCommunityAt(m()*p|0);if(j===a.nodeCommunity[M]||R<1/0&&a.getCommunityTotalSize(j)+h.size[M]>R)continue;const r=Q(a,M,j,n);r>N&&(N=r,q=j)}}else for(let L=0;L<p;L++){const v=a.getCandidateCommunityAt(L);if(R<1/0&&a.getCommunityTotalSize(v)+h.size[M]>R)continue;const j=Q(a,M,v,n);j>N&&(N=j,q=v)}if(n.allowNewCommunity){const L=a.communityCount,v=Q(a,M,L,n);v>N&&(N=v,q=L)}q!==a.nodeCommunity[M]&&N>st&&(a.moveNodeToCommunity(M,q),F=!0)}if(w>n.maxLocalPasses)break}$(a,n.preserveLabels);let I=a;if(n.refine){const T=Ct(h,a,m,n,y===0?d:null);$(T,n.preserveLabels),I=T}i.push({graph:h,partition:I});const z=I.nodeCommunity;for(let T=0;T<u.length;T++)u[T]=z[u[T]];if(a.communityCount===h.n)break;s=wt(h,I)}const f=i[i.length-1];return{graph:f.graph,partition:f.partition,levels:i,originalToCurrent:u,originalNodeIds:g.nodeIds}}function wt(t,o){const n=tt();for(let i=0;i<o.communityCount;i++)n.addNode(i,{size:o.communityTotalSize[i]});const s=new Map;for(let i=0;i<t.n;i++){const l=o.nodeCommunity[i],m=t.outEdges[i];for(let g=0;g<m.length;g++){const e=m[g].to,u=m[g].w,d=o.nodeCommunity[e],f=l+":"+d;s.set(f,(s.get(f)||0)+u)}}for(const[i,l]of s.entries()){const[m,g]=i.split(":"),e=+m,u=+g;n.addLink(e,u,{weight:l})}return n}function Ct(t,o,n,s,i){const l=Y(t);l.initializeAggregates(),l.graph=t;const m=o.nodeCommunity;let g=new Int32Array(l.communityCount);for(let f=0;f<l.communityCount;f++)g[f]=m[f];const e=new Int32Array(t.n);for(let f=0;f<t.n;f++)e[f]=f;let u=!0,d=0;for(;u;){u=!1,d++,ct(e,n);for(let f=0;f<e.length;f++){const y=e[f];if(i&&i[y])continue;const h=m[y],a=l.accumulateNeighborCommunityEdgeWeights(y);let b=l.nodeCommunity[y],F=0;const w=Number.isFinite(s.maxCommunitySize)?s.maxCommunitySize:1/0;for(let S=0;S<a;S++){const I=l.getCandidateCommunityAt(S);if(g[I]!==h||w<1/0&&l.getCommunityTotalSize(I)+t.size[y]>w)continue;const z=Q(l,y,I,s);z>F&&(F=z,b=I)}b!==l.nodeCommunity[y]&&F>st&&(l.moveNodeToCommunity(y,b),u=!0)}if(d>(s.maxLocalPasses||rt))break}return l}function Q(t,o,n,s){if((s.quality||"modularity").toLowerCase()==="cpm"){const l=typeof s.resolution=="number"?s.resolution:1;return gt(t,t.graph||{},o,n,l)||t.deltaCPM?.(o,n,l)||0}return s.directed?et(t,t.graph||{},o,n)||t.deltaModularityDirected?.(o,n)||0:ft(t,t.graph||{},o,n)||t.deltaModularityUndirected?.(o,n)||0}function ct(t,o=Math.random){for(let n=t.length-1;n>0;n--){const s=Math.floor(o()*(n+1)),i=t[n];t[n]=t[s],t[s]=i}return t}function It(t){const o=t.candidateStrategy;if(typeof o!="string")return U.Neighbors;switch(o){case"neighbors":return U.Neighbors;case"all":return U.All;case"random":return U.RandomAny;case"random-neighbor":return U.RandomNeighbor;default:return U.Neighbors}}function St(t={}){const o=!!t.directed,n=Number.isFinite(t.randomSeed)?t.randomSeed:42,s=Number.isFinite(t.maxLevels)?t.maxLevels:ht,i=Number.isFinite(t.maxLocalPasses)?t.maxLocalPasses:rt,l=!!t.allowNewCommunity,m=It(t),g=(t.quality||"modularity").toLowerCase(),e=typeof t.resolution=="number"?t.resolution:1,u=t.refine!==!1,d=t.preserveLabels,f=Number.isFinite(t.maxCommunitySize)?t.maxCommunitySize:1/0;return{directed:o,randomSeed:n,maxLevels:s,maxLocalPasses:i,allowNewCommunity:l,candidateStrategyCode:m,quality:g,resolution:e,refine:u,preserveLabels:d,maxCommunitySize:f,fixedNodes:t.fixedNodes}}function $(t,o){o&&o instanceof Map?t.compactCommunityIds({preserveMap:o}):o===!0?t.compactCommunityIds({keepOldOrder:!0}):t.compactCommunityIds()}function xt(t,o={}){if(!Array.isArray(t)||t.length===0)throw new Error("layers must be a non-empty array");o.directed;const n=t[0].graph,s=[],i=new Set;n.forEachNode(e=>{s.push(e.id),i.add(e.id)});for(let e=1;e<t.length;e++)t[e].graph.forEachNode(d=>{if(!i.has(d.id))throw new Error("All layers must share identical node ids. Missing in base: "+d.id)});const l=tt(),m=t[0].nodeSize||(e=>e.data&&typeof e.data.size=="number"?e.data.size:1);for(let e=0;e<s.length;e++){const u=s[e],d=n.getNode?n.getNode(u):null,f=d?m(d):1;l.addNode(u,{size:f})}const g=new Map;for(let e=0;e<t.length;e++){const{graph:u,weight:d=1,linkWeight:f}=t[e],y=f||(h=>h.data&&typeof h.data.weight=="number"?h.data.weight:1);u.forEachLink(h=>{const a=h.fromId,b=h.toId;if(!i.has(a)||!i.has(b))return;const F=d*y(h);if(F===0)return;const w=a+"\0"+b;g.set(w,(g.get(w)||0)+F)})}for(const[e,u]of g.entries()){const d=e.indexOf("\0"),f=e.substring(0,d),y=e.substring(d+1);l.addLink(f,y,{weight:u})}return l}function At(t,o={}){let n=t;Array.isArray(t)&&(n=xt(t,{directed:!!o.directed}));const{graph:s,partition:i,levels:l,originalToCurrent:m,originalNodeIds:g}=yt(n,o),e=new Map;for(let u=0;u<g.length;u++){const d=m[u];e.set(g[u],d)}return{getClass(u){return e.get(u)},getCommunities(){const u=new Map;for(const[d,f]of e)u.has(f)||u.set(f,[]),u.get(f).push(d);return u},quality(){if((o.quality||"modularity").toLowerCase()==="cpm"){const d=typeof o.resolution=="number"?o.resolution:1;return(o.cpmMode||"unit")==="size-aware"?it(i,s,d):ot(i,s,d)}else return nt(i,s)},toJSON(){const u={};for(const[d,f]of e)u[d]=f;return{membership:u,meta:{levels:l.length,quality:this.quality(),options:o}}}}}function Nt(t,o,n={}){const s=!!n.directed,i=K(t,{directed:s,...n}),l=Y(i);l.graph=i;const m=i.idToIndex,g=u=>o instanceof Map?o.get(u):o?.[u];for(let u=0;u<i.nodeIds.length;u++){const d=i.nodeIds[u],f=g(d);if(f==null){if(n.requireMembership)throw new Error("Missing membership for node: "+d);continue}const y=bt(f);l.nodeCommunity[m.get(d)]=y}if(l.compactCommunityIds(),(n.quality||"modularity").toLowerCase()==="cpm"){const u=typeof n.resolution=="number"?n.resolution:1;return(n.cpmMode||"unit")==="size-aware"?it(l,i,u):ot(l,i,u)}return nt(l,i)}function bt(t){if(typeof t=="number")return t|0;if(typeof t=="string"){const o=Number(t);return Number.isFinite(o)?o|0:Tt(t)}return 0}function Tt(t){let o=2166136261;for(let n=0;n<t.length;n++)o^=t.charCodeAt(n),o=Math.imul(o,16777619);return o>>>0}exports.detectClusters=At;exports.evaluateQuality=Nt;