#
# stmt-dist-fishing.R, 26 Jul 26
#
# Data from:
# Extracted using CodeQL's variant analysis 100 repos

library("colorspace")
library("plyr")
library(gamlss)

# library("glmmTMB")


par(bty="l")
par(las=1)
par(pch="+")
pal_col=rainbow(2)


fit_NBII=function(num_stmts, num_LOC)
{

# po_mod=gamlss(num_calls ~ 1, family=PO)
# summary(po_mod)
#
# PO.mu=exp(coef(po_mod, "mu"))

# print(c(length(num_stmts), num_LOC, length(which(is.na(num_stmts)))))

nb2_mod=gamlss(num_stmts ~ 1, family=NBII, control=gamlss.control(trace=FALSE))
# summary(nb_mod)

NBII.mu=exp(coef(nb2_mod, "mu")) # get mean coefficient
NBII.sigma=exp(coef(nb2_mod, "sigma")) # standard deviation

# print(c(loc_mu[num_LOC-3], NBII.mu, loc_sigma[num_LOC-3], NBII.sigma))

plot(count(num_stmts), log="y", main=paste0("LOC=", num_LOC), cex.main=0.9,
	xlab="if-statements", ylab="Functions")

# lines(0:15, dPO(0:15, mu=PO.mu)*length(num_stmts), col="red")
lines(0:15, dNBII(0:15, mu=NBII.mu, sigma=NBII.sigma)*length(num_stmts), col=pal_col[2])

lines(0:15, dNBII(0:15, mu=loc_mu[num_LOC-3], sigma=loc_sigma[num_LOC-3])*length(num_stmts), col=pal_col[1])
# lines(0:15, dNBI(0:15, mu=fac_mu[num_LOC-1], sigma=fac_sigma[num_LOC-1])*length(num_stmts), col="red")

# nb1_mod=gamlss(num_stmts ~ 1, family=NBI, control=gamlss.control(trace=FALSE))
# summary(nb_mod)

# NBI.mu=exp(coef(nb1_mod, "mu")) # get mean coefficient
# NBI.sigma=exp(coef(nb1_mod, "sigma")) # standard deviation

# The only difference is that sigma increases to adjust for the variance
# depending on mu rather than mu^2
#
# lines(0:15, dPO(0:15, mu=PO.mu)*length(num_stmts), col="red")
# lines(0:15, dNBI(0:15, mu=NBI.mu, sigma=NBI.sigma)*length(num_stmts), col="blue")

# print(c(NBII.mu, NBII.sigma))

return(data.frame(mu=NBII.mu, sigma=NBII.sigma))
}


call_distrib=function(num_LOC)
{
fd4=subset(fdLOC, nLOC == num_LOC)

num_calls=subset(fd4, ncall < 15)$ncall

return(fit_NBII(num_calls, num_LOC))
}


if_distrib=function(num_LOC)
{
fd4=subset(fdLOC, nLOC == num_LOC)

num_calls=subset(fd4, nif < 15)$nif

return(fit_NBII(num_calls, num_LOC))
}

for_distrib=function(num_LOC)
{
fd4=subset(fdLOC, nLOC == num_LOC)

num_calls=subset(fd4, nfor < 15)$nfor

return(fit_NBII(num_calls, num_LOC))
}


while_distrib=function(num_LOC)
{
fd4=subset(fdLOC, nLOC == num_LOC)

num_calls=subset(fd4, nwhile < 15)$nwhile

return(fit_NBII(num_calls, num_LOC))
}

return_distrib=function(num_LOC)
{
fd4=subset(fdLOC, nLOC == num_LOC)

num_calls=subset(fd4, nreturn < 15)$nreturn

return(fit_NBII(num_calls, num_LOC))
}

lvar_distrib=function(num_LOC)
{
fd4=subset(fdLOC, nLOC == num_LOC)

num_calls=subset(fd4, nloclvar < 15)$nloclvar

return(fit_NBII(num_calls, num_LOC))
}


# Count functions containing a given number of statement-kind
# and fit a power law
stmt_fit=function(vec, col_str="black")
{
vec_cnt=count(vec)

points(vec_cnt, col=col_str)

p_bounds=4:16

vec_mod=glm(log(freq) ~ log(x), data=vec_cnt, subset=(x %in% p_bounds))
print(summary(vec_mod))

pred=predict(vec_mod, newdata=data.frame(x=p_bounds))

lines(p_bounds, exp(pred), col=col_str)
}


fd=read.csv("funcdef-100.csv.xz")

fd=subset(fd, repo != "realsenseai/librealsense") # Data extraction for this repo is broken

x_bounds=4:19  # sigma flutuates a lot for higher values
fdLOC=subset(fd, (nLOC %in% x_bounds) & (clinkage == 1))

par(mfcol=c(1, 1))

plot(1, type="n", log="xy",
	xlim=range(x_bounds), ylim=c(1e1, 5e4),
	xlab="Occurrences", ylab="Functions")

pal_col=rainbow(5)

stmt_fit(fdLOC$nLOC, pal_col[1])
stmt_fit(fdLOC$nexprstmt, pal_col[2])
stmt_fit(fdLOC$ncall, pal_col[3])
stmt_fit(fdLOC$nif, pal_col[4])
# stmt_fit(fdLOC$nfor, pal_col[4])
stmt_fit(fdLOC$nreturn, pal_col[5])

legend("bottomleft", legend=c("LOC", "expression-statements", "function calls", "if-statement", "return-statement"), cex=1.2,
                        fill=pal_col, border="white", bty="n")

dev.copy(dev=png, file="func-stmt-freq.png")
dev.off()


par(mfcol=c(4, 4))
par(oma=c(0.5, 1, 1, 0))
par(mar=c(4, 4, 1, 0))

pal_col=rainbow(2)

#
# loc_mod = gamlss(ncall ~ nLOC+I(nLOC^0.5), sigma.formula = ~ nLOC+I(nLOC^0.5), family = NBII, data = subset(fdLOC, (nLOC < 13) & (ncall < 15)))
loc_mod = gamlss(ncall ~ log(nLOC), sigma.formula = ~ log(nLOC), family = NBII, data = subset(fdLOC, (nLOC < 13) & (ncall < 15)))
summary(loc_mod)

pred=predictAll(loc_mod, newdata=data.frame(nLOC=x_bounds), type="response")
loc_mu=pred$mu
loc_sigma=pred$sigma

nb_fits=adply(x_bounds, .mar=1, call_distrib)

dev.copy(dev=png, file="LOC-call-freq.png")
dev.off()

# all if-statements
# loc_mod = gamlss(nif ~ nLOC+I(nLOC^0.5), sigma.formula = ~ nLOC+I(nLOC^0.5), family = NBII, data = subset(fdLOC, (nLOC < 13) & (nif < 15)))
loc_mod = gamlss(nif ~ log(nLOC), sigma.formula = ~ log(nLOC), family = NBII, data = subset(fdLOC, (nLOC < 13) & (nif < 15)))
summary(loc_mod)

pred=predictAll(loc_mod, newdata=data.frame(nLOC=x_bounds), type="response")
loc_mu=pred$mu
loc_sigma=pred$sigma

nb_fits=adply(x_bounds, .mar=1, if_distrib)

dev.copy(dev=png, file="LOC-if-freq.png")
dev.off()


# Local variables defined
# loc_mod = gamlss(nloclvar ~ nLOC+I(nLOC^0.5), sigma.formula = ~ nLOC+I(nLOC^0.5), family = NBII, data = subset(fdLOC, (nLOC < 13) & (nloclvar < 15)))
loc_mod = gamlss(nloclvar ~ log(nLOC), sigma.formula = ~ log(nLOC), family = NBII, data = subset(fdLOC, (nLOC < 13) & (nloclvar < 15)))
summary(loc_mod)

pred=predictAll(loc_mod, newdata=data.frame(nLOC=x_bounds), type="response")
loc_mu=pred$mu
loc_sigma=pred$sigma

nb_fits=adply(x_bounds, .mar=1, lvar_distrib)


# for-statements
# loc_mod = gamlss(nfor ~ nLOC+I(nLOC^0.5), sigma.formula = ~ nLOC+I(nLOC^0.5), family = NBII, data = subset(fdLOC, (nLOC < 13) & (nfor < 15)))
loc_mod = gamlss(nfor ~ log(nLOC), sigma.formula = ~ log(nLOC), family = NBII, data = subset(fdLOC, (nLOC < 13) & (nfor < 15)))
summary(loc_mod)

pred=predictAll(loc_mod, newdata=data.frame(nLOC=x_bounds), type="response")
loc_mu=pred$mu
loc_sigma=pred$sigma

# The following reports:
#   There is a discrepancy  between the original and the re-fit 
# used to achieve 'safe' predictions 
nb_fits=adply(x_bounds, .mar=1, for_distrib)

# while-statements
# loc_mod = gamlss(nwhile ~ nLOC+I(nLOC^0.5), sigma.formula = ~ nLOC+I(nLOC^0.5), family = NBII, data = subset(fdLOC, (nLOC < 13) & (nwhile < 15)))
loc_mod = gamlss(nwhile ~ log(nLOC), sigma.formula = ~ log(nLOC), family = NBII, data = subset(fdLOC, (nLOC < 13) & (nwhile < 15)))
summary(loc_mod)

pred=predictAll(loc_mod, newdata=data.frame(nLOC=x_bounds), type="response")
loc_mu=pred$mu
loc_sigma=pred$sigma

nb_fits=adply(x_bounds, .mar=1, while_distrib)

# return-statements
# loc_mod = gamlss(nreturn ~ nLOC+I(nLOC^0.5), sigma.formula = ~ nLOC+I(nLOC^0.5), family = NBII, data = subset(fdLOC, (nLOC < 13) & (nreturn < 15)))
loc_mod = gamlss(nreturn ~ log(nLOC), sigma.formula = ~ log(nLOC), family = NBII, data = subset(fdLOC, (nLOC < 13) & (nreturn < 15)))
summary(loc_mod)

pred=predictAll(loc_mod, newdata=data.frame(nLOC=x_bounds), type="response")
loc_mu=pred$mu
loc_sigma=pred$sigma

nb_fits=adply(x_bounds, .mar=1, return_distrib)

# pal_col=rainbow(3)
# 
# nb_fits$LOC=x_bounds
# plot(nb_fits$mu, col=pal_col[2],
# 	ylim=range(c(nb_fits$mu, nb_fits$sigma)),
# 	xlab="LOC")
# points(nb_fits$sigma, col=pal_col[3])
# 
# nb_mod=glm(mu ~ log(LOC), data=nb_fits)
# nb_mod=glm(mu ~ LOC+I(nLOC^0.5), data=nb_fits)
# summary(nb_mod)
# 
# pred=predict(nb_mod)
# lines(pred, col=pal_col[1])
# 
#  nb_mod=glm(sigma ~ log(LOC), data=nb_fits)
# nb_mod=glm(sigma ~ LOC+I(LOC^0.5), data=nb_fits)
# summary(nb_mod)
# 
# pred=predict(nb_mod)
# lines(pred, col=pal_col[1])


par(mfcol=c(1,1))

LOC_cnt=count(fd3_11$nLOC)
call_cnt=count(fd3_11$ncall)

plot(call_cnt, log="xy", col=pal_col[2])
points(LOC_cnt, col=pal_col[3])

call_mod=glm(log(freq) ~ log(x), data=call_cnt, subset=(x > 1))
summary(call_mod)

pred=predict(call_mod)
lines(call_cnt$x[-c(1, 2)], exp(pred), col=pal_col[1])

LOC_mod=glm(log(freq) ~ log(x), data=LOC_cnt, subset=(x > 4))
summary(LOC_mod)

pred=predict(LOC_mod)
lines(LOC_cnt$x[-c(1, 2)], exp(pred), col=pal_col[1])



mu=nb_fits$mu
nb_fits$variance=mu*(1+nb_fits$sigma)
nb_fits$std_dev=sqrt(nb_fits$variance)

# p - the probability of an event occurring
# r - the number of events that occurred out of n tries
nb_fits$p=mu/nb_fits$variance
nb_fits$r=mu*nb_fits$p/(1-nb_fits$p)

mu_mod=glm(mu ~ x_bounds, data=nb_fits)
summary(mu_mod)

pal_col=rainbow(3)

plot(x_bounds, nb_fits$mu, col=pal_col[2],
	ylim=range(c(nb_fits$mu, std_dev)))
pred=predict(mu_mod)

lines(x_bounds, pred, col=pal_col[1])

std_mod=glm(std_dev ~ x_bounds, data=nb_fits)
summary(std_mod)

points(x_bounds, std_dev, col=pal_col[3])
pred=predict(std_mod)

lines(x_bounds, pred, col=pal_col[1])

# 
# x_bounds=3:11
# 
# fd3_11=subset(fd, (nLOC %in% x_bounds) & (ncall <= 15))
# 
# fac_mod = glmmTMB(ncall ~ factor(nLOC),
# 			dispformula = ~ factor(nLOC),
# 			family = nbinom2,
# 			data = fd3_11
# 		)			
# summary(fac_mod)
# 
# b = fixef(fac_mod)$cond
# g = fixef(fac_mod)$disp
# 
# fac_mu = exp(b[1] + c(0, b[x_bounds-1]))
# fac_sigma = 1/exp(g[1] + c(0, g[x_bounds-1]))
# 
# 
# 
# loc_mod = glmmTMB(ncall ~ nLOC+I(nLOC^0.5),
# 			dispformula = ~ nLOC+I(nLOC^0.5),
# 			family = nbinom2,
# 			data = fd3_11
# 		)			
# summary(loc_mod)
# 
# b = fixef(loc_mod)$cond
# g = fixef(loc_mod)$disp
# 
# loc_mu = exp(b[1] + b[2]*x_bounds+b[3]*x_bounds^0.5)
# loc_sigma = 1/exp(g[1] + g[2]*x_bounds+g[3]*x_bounds^0.5)
# 
