Skip to content

Commit 9952997

Browse files
committed
Shared: Add DenseRank library
1 parent 1f1b1b7 commit 9952997

File tree

1 file changed

+128
-0
lines changed

1 file changed

+128
-0
lines changed

shared/util/codeql/util/DenseRank.qll

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
/** Provides modules for computing dense `rank`s. */
2+
3+
/** Provides the input to `DenseRank`. */
4+
signature module DenseRankInputSig {
5+
/** An element that is ranked. */
6+
bindingset[this]
7+
class Ranked;
8+
9+
/** Gets the rank of `r`. */
10+
int getRank(Ranked r);
11+
}
12+
13+
/**
14+
* Provides the `denseRank` predicate for computing dense `rank`s. For example,
15+
* if we have
16+
*
17+
* ```ql
18+
* query predicate names(string name) {
19+
* name = ["Alice", "Bob", "Charles", "Charlie", "David"]
20+
* }
21+
*
22+
* int rankByFirstLetter(string name) {
23+
* name = rank[result](string n | names(n) | n order by n.charAt(0))
24+
* }
25+
* ```
26+
*
27+
* then `rankByFirstLetter` computes the following relation
28+
*
29+
* ```
30+
* Alice 1
31+
* Bob 2
32+
* Charles 3
33+
* Charlie 3
34+
* David 5
35+
* ```
36+
*
37+
* Note that `"David"` has rank 5 instead of 4. If we want a dense ranking instead,
38+
* we can do
39+
*
40+
* ```ql
41+
* module M implements DenseRankInputSig {
42+
* class Ranked = string;
43+
*
44+
* predicate getRank = rankByFirstLetter/1;
45+
* }
46+
*
47+
* predicate denseRank = DenseRank<M>::denseRank/1;
48+
* ```
49+
*/
50+
module DenseRank<DenseRankInputSig Input> {
51+
private import Input
52+
53+
private int rankRank(Ranked r, int rnk) {
54+
rnk = getRank(r) and
55+
rnk = rank[result](int rnk0 | rnk0 = getRank(_) | rnk0)
56+
}
57+
58+
/** Gets the dense rank of `r`. */
59+
int denseRank(Ranked r) { result = rankRank(r, getRank(r)) }
60+
}
61+
62+
/** Provides the input to `DenseRank2`. */
63+
signature module DenseRankInputSig2 {
64+
/** A ranking context. */
65+
bindingset[this]
66+
class C;
67+
68+
/** An element that is ranked. */
69+
bindingset[this]
70+
class Ranked;
71+
72+
/** Gets the rank of `r` in the context provided by `c`. */
73+
int getRank(C c, Ranked r);
74+
}
75+
76+
/** Same as `DenseRank`, but allows for a context consisting of one element. */
77+
module DenseRank2<DenseRankInputSig2 Input> {
78+
private import Input
79+
80+
private int rank0(C c, Ranked r, int rnk) {
81+
rnk = getRank(c, r) and
82+
rnk = rank[result](int rnk0 | rnk0 = getRank(c, _) | rnk0)
83+
}
84+
85+
/** Gets the dense rank of `r` in the context provided by `c`. */
86+
int denseRank(C c, Ranked r) {
87+
exists(int rnk |
88+
result = rank0(c, r, rnk) and
89+
rnk = getRank(c, r)
90+
)
91+
}
92+
}
93+
94+
/** Provides the input to `DenseRank3`. */
95+
signature module DenseRankInputSig3 {
96+
/** A ranking context. */
97+
bindingset[this]
98+
class C1;
99+
100+
/** A ranking context. */
101+
bindingset[this]
102+
class C2;
103+
104+
/** An element that is ranked. */
105+
bindingset[this]
106+
class Ranked;
107+
108+
/** Gets the rank of `r` in the context provided by `c1` and `c2`. */
109+
int getRank(C1 c1, C2 c2, Ranked r);
110+
}
111+
112+
/** Same as `DenseRank`, but allows for a context consisting of two elements. */
113+
module DenseRank3<DenseRankInputSig3 Input> {
114+
private import Input
115+
116+
private int rank0(C1 c1, C2 c2, Ranked r, int rnk) {
117+
rnk = getRank(c1, c2, r) and
118+
rnk = rank[result](int rnk0 | rnk0 = getRank(c1, c2, _) | rnk0)
119+
}
120+
121+
/** Gets the dense rank of `r` in the context provided by `c1` and `c2`. */
122+
int denseRank(C1 c1, C2 c2, Ranked r) {
123+
exists(int rnk |
124+
result = rank0(c1, c2, r, rnk) and
125+
rnk = getRank(c1, c2, r)
126+
)
127+
}
128+
}

0 commit comments

Comments
 (0)