# coding: utf-8

# #1. Ambientes de trabajo
#
# Lo primero es crear el ambiente de trabajo para todos los laboratorios. Hay dos opciones: realizarlo remotamente en COLAB de Google, o hacerlo localmente usando el programa RSTUDIO.
#
# Resumidamente, RSTUDIO es más costoso al principio pero al final se gana tiempo (principalmente si se va a realizar mucho cómputo). Por otro lado, COLAB permite rápidamente ponerse a trabajar. Supondremos que se realiza el práctico en COLAB, pudiendo cambiar más adelante.

# ##1.a) Ambiente COLAB remoto
#
# 1.   Abrir en navegador: https://colab.research.google.com/
# 2.   Abrir el notebook de la tarea:
#      File-> Open Notebook -> Github -> https://github.com/prbocca/na101_master -> homework_1_graphs
# 3.   Guardar el notebook en su Google Drive:
#      File -> Save a Copy in Drive...
# 4.   Renombrar el archivo `"cedula ID"_ar_hw1.ipynb`, por ejemplo *33484022_ar_hw1.ipynb*
# 5.   Al final usted deberá descargar el notebook. Asegurarse que se están guardando las salidas de ejecución en el notebook: File -> Download .ipynb
# 6.   Luego estos archivos deberán ser enviados a prbocca@fing.edu.uy
#

# ##1.b) Ambiente RSTUDIO local (opcional)
#
# Es posible instalar un ambiente de trabajo (IDE) local y los paquetes básicos necesarios para todos los prácticos.
# Este proceso puede demorar y se recomienda hacerlo fuera del práctico.
#
# Este ambiente de trabajo es opcional y recomendado para realizar mucho cómputo. En lo que resta del curso se asumirá que se utiliza la plataforma en linea: https://colab.research.google.com/, en donde se puede ejectuar y resolver todos los prácticos del curso.
#
#
# 1.   Instalar Rstudio. Descargar desde: https://www.rstudio.com/products/rstudio/
# 2.   Instalar Paquetes básicos. En una consola de R:
# ```
# #  cargar librerias
# load_libs <- function(libraries = libs, install=TRUE){
#   if (install){ # instalar librerias no instaladas
#     new.packages <- libs[!(libs %in% installed.packages()[,"Package"])]
#     if(length(new.packages)) install.packages(new.packages)
#   }
#   #cargo librerias
#   for (lib in libraries){
#     require(lib, character.only=TRUE, quietly = FALSE)
#   }
# }
# libs = c("igraph", "igraphdata", "GO.db", "GOstats", "ROCR", "ape", "car", "eigenmodel", "ergm", "fdrtool", "ggplot2", "huge", "kernlab", "lattice", "mixer", "network", "networkDynamic", "networkTomography", "ngspatial", "org.Sc.sgd.db", "sna", "vioplot")
# load_libs(libs)
# ```
#
# 3.    Abrir el .r de la tarea en: https://github.com/prbocca/na101_master/tree/master/homework_1_graphs

# ## 1.c) Cargar Librerias
#
# En esta primera ejecución se asignan los recursos. Además instalar las librerías lleva tiempo. Es de esperar que esto demore unos minutos.
#
# Lamentablemente, el entorno de Colab se pierde luego de un tiempo de inactividad. Esto incluye que se deben volver a cargar las librerías y a descargar los datos (se borran todos los datos temporales).
#
# Todas las librerías debe instalarse correctamente, si el proceso se interrumpe o alguna librería da error en la instalación, entonces habrá problemas en el código más adelante. Si tiene este tipo de problemas pruebe hacer `Runtime -> Factory reset runtime`, y volver a intentar.

# In[ ]:

# cargar librerias
load_libs <- function(libraries = libs, install=TRUE){
  if (install){ # instalar librerias no instaladas
    new.packages <- libs[!(libs %in% installed.packages()[,"Package"])]
    if(length(new.packages)) install.packages(new.packages)
  }
  #cargo librerias
  for (lib in libraries){
    require(lib, character.only=TRUE, quietly = FALSE)
  }
}

libs = c("R.matlab", "sand","igraph","igraphdata") #procesamiento en paralelo

load_libs(libs)

# ## 1.d) Descargar funciones auxiliares
#
# Para facilitar el trabajo a realizar en los prácticos, se ofrecen un conjunto de funciones auxiliares. El siguiente código carga dichas funciones.
#
# Es necesario repetir esta tarea siempre que se inicia una ejecución.

# In[ ]:

#directorio donde se va a trabajar
data_path = "/content/ar/hw1/"

dir.create(data_path, showWarnings = FALSE, recursive = TRUE)
setwd(data_path)
getwd()
list.files()

# cargo funciones auxiliares
source("https://raw.githubusercontent.com/prbocca/na101_master/master/homeworks_common.r")

# # 2. Tutorial del lenguaje *R*
#
# Es necesario conocer el lenguaje *R* para ganar fluidez en el práctico. Quien no conoce el lenguaje debe realizar algún tutorial rápido.
# , por ejemplo: `igraph_demo("crashR")` (que lamentablemente no anda en COLAB).
#

# In[ ]:

# lamentablemente el paquete tcltk de COLAB no permite interfaz interactiva, por lo que este tutorial solo anda localemente (en el ambiente RSTUDIO).
library("tcltk")
if (interactive()){
  igraph_demo("crashR")
  }else{
    print("Lo siento en su ambiente no funciona este tutorial.")
  }

# # 3. Tutorial de manipular grafos de redes
#
# Si aun no lo tiene, descargar el libro de práctico [SANDR]: Kolaczyk, E.D. and Csárdi, G. "Statistical Analysis of Network Data with R". Use R!, Springer New York, 2014. ISBN 9781493909834. Disponible
# en http://timbo.org.uy/.
# Ir a la descarga directamente siguiendo el link (Noviembre, 2019): https://link-springer-com.proxy.timbo.org.uy/book/10.1007%2F978-1-4939-0983-4
#
#
# Seguir el texto del Capítulo 2 del libro [SANDR], ejecutando el código fuente incluido. Utilizar el manual de referencia del paquete igraph para extender el conocimiento: http://igraph.org/r/doc/igraph.pdf
#
# **Aquí se incluyen las celdas de código del libro, pero no su explicación. Entonces, es importante seguir la lectura del libro. Esta sección no será evaluada, sientase libre de cambiar y probar nuevos códigos para aprender.**
#
# **Observación: algunos comandos han cambiado en las últimas versiones de igraph, aquí se incluye una actualización respecto al libro.**

# In[ ]:

g <- graph.formula(1-2, 1-3, 2-3, 2-4, 3-5, 4-5, 4-6, 4-7, 5-6, 6-7)

# In[ ]:

V(g)

# In[ ]:

E(g)

# In[ ]:

print_all(g)

# In[ ]:

plot(g)

# In[ ]:

dg <- graph.formula(1-+2, 1-+3, 2++3)
plot(dg)

# In[ ]:

dg <- graph.formula(Sam-+Mary, Sam-+Tom, Mary++Tom)
print_all(dg)

# In[ ]:

V(dg)$name <- c("Sam", "Mary", "Tom")

# In[ ]:

E(dg)

# In[ ]:

get.adjacency(g)

# In[ ]:

h <- induced.subgraph(g, 1:5)
print_all(h)

# In[ ]:

h <- g - vertices(c(6,7))

# In[ ]:

h <- h + vertices(c(6,7))
g <- h + edges(c(4,6),c(4,7),c(5,6),c(6,7))

# In[ ]:

h1 <- h
h2 <- graph.formula(4-6, 4-7, 5-6, 6-7)
g <- graph.union(h1,h2)

# In[ ]:

V(dg)$name

# In[ ]:

V(g)$color <- "red"

# In[ ]:

is.weighted(g)
wg <- g
E(wg)$weight <- runif(ecount(wg))
is.weighted(wg)

# In[ ]:

g$name <- "Toy Graph"

# In[ ]:

g.lazega <- graph.data.frame(elist.lazega,
                            directed="FALSE",
                            vertices=v.attr.lazega)
g.lazega$name <- "Lazega Lawyers"

# In[ ]:

vcount(g.lazega)

# In[ ]:

ecount(g.lazega)

# In[ ]:

list.vertex.attributes(g.lazega)

# In[ ]:

is.simple(g)

# In[ ]:

mg <- g + edge(2,3)
print_all(mg)
is.simple(mg)

# In[ ]:

E(mg)$weight <- 1
wg2 <- simplify(mg)
is.simple(wg2)

# In[ ]:

print_all(wg2)

# In[ ]:

E(wg2)$weight

# In[ ]:

neighbors(g, 5)

# In[ ]:

degree(g)

# In[ ]:

degree(dg, mode="in")
degree(dg, mode="out")

# In[ ]:

is.connected(g)

# In[ ]:

clusters(g)

# In[ ]:

is.connected(dg, mode="weak")
is.connected(dg, mode="strong")

# In[ ]:

diameter(g, weights=NA)

# In[ ]:

g.full <- graph.full(7)
g.ring <- graph.ring(7)
g.tree <- graph.tree(7, children=2, mode="undirected")
g.star <- graph.star(7, mode="undirected")
par(mfrow=c(2, 2))
plot(g.full)
plot(g.ring)
plot(g.tree)
plot(g.star)

# In[ ]:

is.dag(dg)

# In[ ]:

g.bip <- graph.formula(actor1:actor2:actor3,
                       movie1:movie2, actor1:actor2 - movie1,
                       actor2:actor3 - movie2)
V(g.bip)$type <- grepl('^movie', V(g.bip)$name)
print_all(g.bip, v=T)

# In[ ]:

proj <- bipartite.projection(g.bip)
print_all(proj[[1]])
print_all(proj[[2]])

# # 4. Explorar una red de comunicaciones: emails de la empresa Enron
#
# La red de emails de la empresa Enron se publicó durante una investigación federal de EE.UU (si usted esta interesado en leer más sobre el escándalo de Enron, ver http://en.wikipedia.org/wiki/Enron_scandal).
#
# En éste ejercicio haremos cálculos sencillos sobre el grafo de esta red usando *R*.
#
# La red completa está disponible online en http://www.cs.cmu.edu/~enron/ y contiene $517.431$ correos extraidos de los directorios de correo de $150$ usuarios. Aqui utilizaremos un grafo más pequeño, preparado por C. E. Priebe et al., que consiste en $34.427$ correos enviados entre $184$ correos de empleados de Enron (ver http://cis.jhu.edu/~parky/Enron/enron.html por más detalles). Los correos son del periodo 13/11/1998 al 21/06/2002 ($44$ meses).
#

# ## 4.a) Descargar los datos y crear matriz de adyacencia
#
# Descargar el subconjunto de datos de `Enron.zip` de github.com

# In[ ]:

# load data
download.file(url="https://github.com/prbocca/na101_master/blob/master/homework_1_graphs/Enron.zip?raw=true", destfile="Enron.zip", mode="wb")
unzip(zipfile="Enron.zip")

list.files()

# $\newcommand   \ind   [1] {{\mathbb I \left\{#1\right\}  } }$
# $\def\bbA{{\mathbf A}}$
# El .zip contiene dos archivos creados en `Matlab`.
# Para cargar archivos de `Matlab` en `R`, utilizar el paquete `R.matlab`.
#
# En el archivo `Y.mat` se encuentra el arreglo de tres dimensiones $\underline{\mathbf Y} \in \mathbb{R}^{184 \times 184 \times 44}$, que contiene la secuencia de matrices de adyacencia
# que representa la red de quién le envió correo a quién para cada uno de los $44$ meses.
# Notar que el grafo de la red es dirigido y ponderado,
# dado que el elemento $[\underline{\mathbf Y}]_{ijt}$ indica la cantidad de correos enviados por el empleado $i$ al empleado $j$ durante el mes $t$.
#
# Además, desde el otro archivo, cargar el arreglo `employees` que contiene el nombre, la cuenta y cargo en la empresa para cada uno de los $184$ empleados (vértices) en $G$.
#

# In[ ]:

# cargo la matriz de correos enviados
Y = readMat("Y.mat")$Y
str(Y)

# cargo los datos de cada empleado
employees = NA
##################################################################
#                       TU CÓDIGO ACÁ
# Tip: usar readMat() y algunas funciones auxiliares para transformar el texto
#  y obtener un vector de strings (chr [1:184])
#
#
#
##################################################################
str(employees)

# Construir la matriz binaria de adyacencia ${\mathbf A}\in\{0,1\}^{184\times184}$ que agrega todos los correos a lo largo del tiempo
# (es decir, sumar $\underline{\mathbf Y}$ en la tercera dimension, y definir $[{\mathbf A}]_{ij}=\ind{\sum_{t=1}^{44}[\underline{\mathbf Y}]_{ijt}>0}$).
#

# In[ ]:

# matriz de adyacencia con peso (cantidad de correos enviados en todos los meses)
A_weight = rowSums(Y, na.rm = FALSE, dims = 2)
str(A_weight)

# matriz de adyacencia binaria
A = ifelse(A_weight>0, 1, 0)
str(A)

# ## 4.b) Construir un grafo a partir de la matriz de adyacencia
#
# Construir el grafo (con `igraph`) a partir de la matriz de adyacencia $\bbA$ y agregar los atributos de vértices del arreglo `employees`.

# In[ ]:

# podría construise usando la matriz de adyacencia binaria
#   g = graph.adjacency(A)

# pero optamos por hacerlo usando la matriz de adyacencia con pesos, así nos quedan en el grafo
g = graph.adjacency(A_weight, weighted = TRUE)
V(g)$id = employees

summary(g)

# Más adelante veremos como graficar este grafo. Por ahora, la siguiente es una visualización sencilla.

# In[ ]:

plot(g, edge.color="gray", edge.width=1, edge.lty=1, edge.arrow.size=.5,
     vertex.size=3, vertex.label=NA,
     layout=layout_nicely(g))

# Ahora nos familiarizarnos con operaciones básicas sobre grafos.
#
# Para cada uno de las siguientes partes, realizar los cálculos usando la matriz de adyacencia $\bbA$ (con álegebra de matrices), y usando el grafo (con métodos de `igraph`).
#
#

# ## 4.c) Cantidad de aristas (arcos)
#
# Calcular la cantidad de aristas (arcos) en la red.
# Es decir, la cantidad de parejas únicas ordenadas $(u,v)\in E$ donde $u,v\in V$.

# In[ ]:

# numero de aristas dirigidas: 3007
cant_aristadirigida_matriz = sum(A)
printf("MATRIZ. las aristas dirigidas son: %s", cant_aristadirigida_matriz)

# con grafos
cant_aristadirigida_grafo = length(E(g))
printf("GRAFO. las aristas dirigidas también las calculamos con: %s o con %s", cant_aristadirigida_grafo, ecount(g))

# ## 4.d) Cantidad de aristas no dirigidas
#
# Calcular la cantidad de aristas no dirigidas en la red (es decir, la cantidad de parejas únicas no ordenadas $(u,v)\in E$
# donde $u,v\in V$. Esto significa que si $(u,v)\in E$ o $(v,u)\in E$, entonces se debe contar el par como una única arista.
#

# In[ ]:

# con matrices
#numero de aristas no dirigidas: 2097
A.und = ifelse((A + t(A)) > 0, 1, 0)
A.und.triang = A.und
A.und.triang[lower.tri(A.und.triang)] = 0
cant_aristanodirigida_matriz = sum(A.und.triang)
printf("MATRIZ. las aristas no dirigidas son: %s", sum(cant_aristanodirigida_matriz))

#con grafos
cant_aristanodirigida_grafo = NA
##################################################################
#                       TU CÓDIGO ACÁ
# Tip: convertir el grafo a no dirigído
#
#
##################################################################
printf("GRAFO. las aristas no dirigidas también las calculamos con: %s", cant_aristanodirigida_grafo)

# ## 4.e) Cantidad de arcos mutuos
#
# Calcular la cantidad de arcos mutuos en la red (es decir, la cantidad de parejas $(u,v)$ donde $\{(u,v),(v,u)\}\subseteq E$
# y $u,v\in V$). Esto significa que si existen ambas $(u,v)\in E$ y $(v,u)\in E$, debe contarse el par como mutual.

# In[ ]:

# con matrices
cant_aristamutua_matriz = NA
##################################################################
#                       TU CÓDIGO ACÁ
# Tip: usar una matriz triangular luego de transformar la matriz de adyacencia
#
#
##################################################################
printf("MATRIZ. las aristas mutuas son: %s", cant_aristamutua_matriz)

#con grafos
cant_aristamutua_grafo = NA
##################################################################
#                       TU CÓDIGO ACÁ
# Tip: convertir el grafo a no dirigído
#
#
##################################################################
printf("GRAFO. las aristas mutuas también las calculamos con: %s", cant_aristamutua_grafo)

# ## 4.f) Empleados que no recibieron ningún correo
#
# Calcular la cantidad de vértices con $d_v^{\text{in}}=0$, y listar los nombres de los empleados correspondientes.
#

# In[ ]:

#find people not geting any email: 3
# [1] "Vince Kaminski (j..kaminski) Manager Risk Management Head" "Mary Fischer (mary.fischer) Employee "
# [3] "xxx Smith (m..smith) xxx "
idx.notgetting = colSums(A)==0
cant_notgetting_matriz = sum(idx.notgetting)
printf("MATRIZ. los empleados que no recibieron correo son: %s", cant_notgetting_matriz)
employees[idx.notgetting]

#con grafos
cant_notgetting_grafo = sum(degree(g, mode = "in")==0)
printf("GRAFO. los empleados que no recibieron correo son: %s", cant_notgetting_grafo)
V(g)$id[degree(g, mode = "in")==0]

# ## 4.g) Empleados que no enviaron ningún correo
#
# Calcular la cantidad de vértices con $d_v^{\text{out}}=0$, y listar los nombres de los empleados correspondientes.
#

# In[ ]:

# con matrices
cant_notsending_matriz = NA
##################################################################
#                       TU CÓDIGO ACÁ
# Tip: Similar a quienes no recibieron
#
#
##################################################################
printf("MATRIZ. los empleados que no enviaron correo son: %s", cant_notsending_matriz)

# con grafos
cant_notsending_grafo = NA
##################################################################
#                       TU CÓDIGO ACÁ
# Tip: Similar a quienes no recibieron
#
#
##################################################################
printf("GRAFO. los empleados que no enviaron correo son: %s", cant_notsending_grafo)

# ## 4.h) Cantidad de empleados contactados por al menos otros 30 empleados
#
# Calcular la cantidad de empleados que fueron contactados por $30$ o más empleados.

# In[ ]:

# con matrices
cant_indegree30_matriz = NA
##################################################################
#                       TU CÓDIGO ACÁ
# Tip: Similar a quienes no recibieron
#
#
##################################################################
printf("MATRIZ. los contactados por al menos otros 30 empleados son: %s", cant_indegree30_matriz)

# con grafos
cant_indegree30_grafo = NA
##################################################################
#                       TU CÓDIGO ACÁ
# Tip: Similar a quienes no recibieron
#
#
##################################################################
printf("GRAFO. los contactados por al menos otros 30 empleados son: %s", cant_indegree30_grafo)

# ## 4.i) Cantidad de empleados que contactaron a al menos otros 30 empleados
#
# Calcular la cantidad de empleados que contactaron a $30$ o más empleados.

# In[ ]:

#con matrices
cant_outdegree30_matriz = NA
##################################################################
#                       TU CÓDIGO ACÁ
# Tip: Similar a quienes no recibieron
#
#
##################################################################
printf("MATRIZ. los que contactaron a al menos otros 30 empleados son: %s", cant_outdegree30_matriz)

#con grafos
cant_outdegree30_grafo = NA
##################################################################
#                       TU CÓDIGO ACÁ
# Tip: Similar a quienes no recibieron
#
#
##################################################################
printf("GRAFO. los que contactaron a al menos otros 30 empleados son: %s", cant_outdegree30_grafo)

# ## 4.j) Cantidad de triángulos dirigidos
#
# Calcular la cantidad de triángulos dirigidos en $G$.
# Recordar que $G$ es dirigido y la orientación de los triángulos importa, es decir, $i\to j \to k \to i$ es lo mismo que $k \to i \to j \to k $, pero diferente a $i\to k \to j \to i$.
#

# In[ ]:

#numero de triangulos dirigidos: 6819
printf("MATRIZ. Cantidad de triángulos dirigidos son: %s", sum(diag(A %*% A %*% A/3)))

# con matrices
cant_triangnodirigido_matriz = NA
##################################################################
#
#                       TU CÓDIGO ACÁ
# Tip: similar a anterior
#
#
##################################################################
printf("MATRIZ. Cantidad de triángulos no dirigidos son: %s", cant_triangnodirigido_matriz)

# con grafos
cant_triangnodirigido_grafo = NA
##################################################################
#                       TU CÓDIGO ACÁ
# Tip: Buscar una función en igraph que ayude
#
#
##################################################################
printf("GRAFO. Cantidad de triángulos no dirigidos son: %s", cant_triangnodirigido_grafo)

# ## 4.k) ¿Es el grafo conexo?
#
# Solo usando grafos, determinar si el grafo dirigido es débilmente y fuértemente conexo.
# En caso de no serlo obtener el subgrafo con la componente conexa más grande (llamada componente gigante).

# In[ ]:

printf("GRAFO. Es débilmente conexo: %s", is.connected(g, mode = "weak"))
printf("GRAFO. Es fuertemente conexo: %s", is.connected(g, mode = "strong"))

g_components = components(g, mode = c("weak", "strong"))
str(g_components)

c_gigante = which.max(g_components$csize) #la componente mas grande
vids_gigante =  which(g_components$membership == c_gigante)
g_gigante = induced_subgraph(g, vids_gigante)
summary(g_gigante)
V(g)$id[g_components$membership != c_gigante]
# sobran los vertices:
#'Vince Kaminski (j..kaminski) Manager Risk Management Head'
# 'Mary Fischer (mary.fischer) Employee '

plot(g_gigante, edge.color="gray", edge.width=1, edge.lty=1, edge.arrow.size=.5,
     vertex.size=3, vertex.label=NA,
     #vertex.color=nodes, vertex.shape = shapes,
     layout=layout_nicely(g_gigante))

# ## 4.l) Guardar y cargar grafos
#
# Usando el paquete `igraph` guardar el grafo $G$ en formato PAJEK.
# Luego cargar una copia del grafo en $G_\text{copia}$.
# ¿Porqué los dos grafos no son identicos?

# In[ ]:

#grabar els grafo G
filename_g = "enron_g.pjk"
##################################################################
#                       TU CÓDIGO ACÁ
# Tip: Buscar una función en igraph que ayude
#
#
##################################################################

# cargar el grafo
g_copia = read.graph(file=filename_g, format= "pajek")
summary(g_copia)

#son iguales?
identical(g, g_copia)


# food web functions modified from Owen Petchey
# original functions were found at thetrophiclink.org
# and are available on github here: https://github.com/opetchey/dumping_ground/blob/master/random_cascade_niche/FoodWebFunctions.r

## some code to plot a predation matrix
## some useful functions script has slightly different Plot.matrix code
Plot.matrix <- function(web, title=" ", point.cex=0.5, trait.cex=1,
                        diag.line=T, traits=F, by.consumer=T, axes.labels=F, sp.pt.ch=NA, pt.col="black"){

  S <- length(web[,1])

  ##point.cex <- 30/30
  ##trait.cex <- 30/30

  dimnames(web) <- list(1:S, 1:S)
  consumer <- rep(1:S, each=S)
  resource <- rep(1:S, S)
  web.list <- Matrix.to.list(web)
  par(xpd=T)
  plot(consumer, resource, pch=19, type="n", cex=0.1,
       ann=F, axes=F,
       xlim=c(1, S), ylim=c(1, S))
  if(length(traits)==1)
    points(web.list[,2], S+1-as.numeric(web.list[,1]),
           type="p", pch=19, cex=point.cex, col=pt.col)
  if(length(traits)==length(web)){

    colours.to.use <- rev(heat.colors(30)[c(-1:-5, -26:-30)])
    ##colours.to.use <- rev(gray(1:30/30)[c(-1:-5, -26:-30)])

    if(by.consumer){
      integer.traits <- matrix(0, S, S)
      for(i in 1:S){
        traits.01 <- traits[,i]-min(traits[,i])
        traits.01 <- traits.01/max(traits.01)
        integer.traits[,i] <- round(traits.01*19)+1
        integer.traits[traits[,i]==0,i] = NaN

      }
    }

    if(!by.consumer){
      colours.to.use <- heat.colors(20)
      traits.01 <- traits-min(traits)
      traits.01 <- traits.01/max(traits.01)
      integer.traits <- round(traits.01*19)+1
    }

    if(point.cex>trait.cex){
        points(web.list[,2], S+1-as.numeric(web.list[,1]),
               type="p", pch=19, cex=point.cex, col="black")##colours.to.use[integer.traits])
        points(rep(1:S, each=S), rep(S:1, times=S),
               pch=19, cex=trait.cex, col=colours.to.use[integer.traits])
    }
    if(point.cex<trait.cex){
        points(rep(1:S, each=S), rep(S:1, times=S),
               pch=19, cex=trait.cex, col=colours.to.use[integer.traits])
        points(web.list[,2], S+1-as.numeric(web.list[,1]),
               type="p", pch=19, cex=point.cex, col="black")##colours.to.use[integer.traits])
    }

    if(!is.na(sp.pt.ch))
        points(web.list[,2], S+1-as.numeric(web.list[,1]),
               type="p", pch=sp.pt.ch, cex=point.cex, col="black")##colours.to.use[integer.traits])

  }
  par(xpd=F)
  mtext(side=3, text=title, font=2, line=2, cex=0.5)
  if(axes.labels){
      mtext(side=2, "Resource", line=0.5, cex=1)
      mtext(side=3, "Consumer", line=0.5, cex=1)
  }
  if(diag.line==T)
    lines(1:S, S:1, lty="dashed")
}

## Get the structural properties of a food web (predation matrix)
## or a list of predation matrices
## specify which stats with the which.stats argument:
## 1: Bottom, intermediate, top, unconnected, cannibal fractions
## 2: SD(generalism) and SD(vulnerability)
## 3: Maximum diet similarity
## 4: Trophic levels
Get.web.stats <- function(web, which.stats=1:4, real.web=NA){
    if(!is.list(web)){
        result <- c(S = length(web[,1]),
                    L = sum(web),
                    C = sum(web)/length(web[,1])^2)

        if(sum(which.stats==1)==1){
            BITUC <- Bottom.Intermediate.Top(web, proportion=T)$Proportions.of.each
            result <- c(result,
                        B = BITUC[1],
                        I = BITUC[2],
                        T = BITUC[3],
                        U = BITUC[4],
                        CB = Cannibals(web),
                        PCB = BITUC[5])
        }

        if(sum(which.stats==2)==1){
            result <- c(result,
                        Gensd = Gen.sd(web),
                        Vulsd = Vul.sd(web))
        }

        if(sum(which.stats==3)==1)
            result <- c(result, Maxsim=Maxsim(web))

        if(sum(which.stats==4)==1){

            TLs <- GetTL(web)
            if(is.na(TLs[1])){
                mean.TL <- NA
                max.TL <- NA
                sd.TL <- NA
            }
            if(!is.na(TLs[1])){
                mean.TL <- mean(TLs, na.rm=T)
                max.TL <- max(TLs, na.rm=T)
                sd.TL <- sd(TLs, na.rm=T)
            }

            result <- c(result,
                        mean.TL = mean.TL,
                        max.TL=max.TL,
                        sd.TL=sd.TL)

        }

        if(sum(which.stats==5)==1){

            ## move this to which.stats==1 set
            Num.H <- Num.herbivores(web) / length(web[,1])

            ## Move this to which.stats==4
            frac.omniv <- Fraction.omnivores(web)

            lev.omniv <- Level.omnivory(web)

            ## igraph package required
            library(igraph, lib.loc="~/mylibs/R")

            iweb <- graph.adjacency(web, mode="directed")
            clust.coef <- transitivity(iweb)
            char.path.length <- mean(shortest.paths(iweb))

            result <- c(result,
                        Num.H = Num.H,
                        Prop.om = frac.omniv,
                        Lev.om = lev.omniv,
                        Clust = clust.coef,
                        Cpl = char.path.length)

        }
    }

    if(is.list(web)){
        for(i in 1:length(web)){

            t.result <- c(S = length(web[[i]][,1]),
                        L = sum(web[[i]]),
                        C = sum(web[[i]])/length(web[[i]][,1])^2)

            if(sum(which.stats==1)==1){
                BITUC <- Bottom.Intermediate.Top(web[[i]], proportion=T)$Proportions.of.each
                t.result <- c(t.result,
                            B = BITUC[1],
                            I = BITUC[2],
                            T = BITUC[3],
                            U = BITUC[4],
                            CB = Cannibals(web[[i]]),
                            PCB = BITUC[5])
            }

            if(sum(which.stats==2)==1){
                t.result <- c(t.result,
                            Gensd = Gen.sd(web[[i]]),
                            Vulsd = Vul.sd(web[[i]]))
            }

            if(sum(which.stats==3)==1)
                t.result <- c(t.result, Maxsim=Maxsim(web[[i]]))

            if(sum(which.stats==4)==1){

                TLs <- GetTL(web[[i]])
                if(is.na(TLs[1])){
                    mean.TL <- NA
                    max.TL <- NA
                    sd.TL <- NA
                }
                if(!is.na(TLs[1])){
                    mean.TL <- mean(TLs, na.rm=T)
                    max.TL <- max(TLs, na.rm=T)
                    sd.TL <- sd(TLs, na.rm=T)
                }

                t.result = c(t.result,
                    mean.TL = mean.TL,
                    max.TL=max.TL,
                    sd.TL=sd.TL)
            }

        if(sum(which.stats==5)==1){

            ## move this to which.stats==1 set
            Num.H <- Num.herbivores(web[[i]])  / length(web[[i]][,1])

            ## Move this to which.stats==4
            frac.omniv <- Fraction.omnivores(web[[i]])
            lev.omniv <- Level.omnivory(web[[i]])

            ## igraph package required
            library(igraph)
            iweb <- graph.adjacency(web[[i]], mode="directed")
            clust.coef <- transitivity(iweb)
            char.path.length <- mean(shortest.paths(iweb))

            t.result <- c(t.result,
                          Num.H = Num.H,
                          Prop.om = frac.omniv,
                          Lev.om = lev.omniv,
                          Clust = clust.coef,
                          Cpl = char.path.length)

        }

            if(i==1)
                result <- t.result
            if(i>1)
                result <- rbind(result, t.result)
        }
    }
    result
}

Fraction.omnivores <- function(web) {
    TLs <- GetTL(web)
    non.int.TL <- web[,TLs %% 1 != 0]
    if(is.matrix(non.int.TL))
        frac.omniv <- sum(apply(non.int.TL, 2, sum) > 1)  / length(web[,1])
    if(is.vector(non.int.TL))
        frac.omniv <- (sum(non.int.TL) > 1)  / length(web[,1])
    frac.omniv
}

Level.omnivory <- function(web) {

    TLs <- GetTL(web)

    if( sum(is.na(TLs)) == length(TLs) )
       rr <- NA

    if( sum(is.na(TLs)) != length(TLs) ) {

        web.TLs <- matrix(rep(TLs, length(web[,1])), length(web[,1]), length(web[,1]))
        lo.pc <- numeric(length=length(web[,1]))
        for(i in 1:length(web[,1])) {
            tt <- web.TLs[web[,i]==1,i]
            if(length(tt)==0 | sum(!is.na(tt))==0 )
                lo.pc[i] = NA
            if(length(tt)>0 & sum(!is.na(tt))!=0)
                lo.pc[i] <- sd(tt)
        }
        rr <- mean(lo.pc, na.rm=T)
    }
    rr
}

## return the proportion of bottom, intermediate, top,
## unconnected and purely cannibalistic specie
Bottom.Intermediate.Top <- function(web, proportion=TRUE, check.web=FALSE){

  ## find the names and numbers of BIT, unconnected and pure cannibals
  names.all <- 1:length(web[1,])
  dimnames(web) <- list(names.all, names.all)
  names.Bottom <-  names.all[apply(web, 2, sum)==0 & apply(web, 1, sum)!=0]#pulls out names where column sum ==0, and where row sum == >0. e.g. an alga does not consume anyhting (column sum == 0) but is consumed (row sum = >0)
  number.Bottom <- length(names.Bottom)
  names.Top <-   names.all[apply(web, 2, sum)!=0 & apply(web, 1, sum)==0]
  number.Top <- length(names.Top)
  names.Unconnected <- names.all[apply(web, 2, sum)==0 & apply(web, 1, sum)==0]
  number.Unconnected <- length(names.Unconnected)
  number.Pure.cannibals <- 0
  names.Pure.cannibals <- character(0)
  for(i in 1:length(web[,1]))
    if(web[i,i]!=0 && sum(web[-i,i]+web[i,-i])==0){
      if(number.Pure.cannibals==0){
        names.Pure.cannibals <- dimnames(web)[[1]][i]
        number.Pure.cannibals <- 1
      }
      else{
        names.Pure.cannibals <- c(names.Pure.cannibals, dimnames(web)[[1]][i])
        number.Pure.cannibals <- number.Pure.cannibals + 1
      }
    }
  names.Intermediate <- dimnames(web)[[1]][is.na(match(dimnames(web)[[1]],
                                                      c(names.Bottom, names.Top, names.Unconnected, names.Pure.cannibals)))]
  number.Intermediate <- length(web[1,])-number.Bottom-number.Top-number.Unconnected-number.Pure.cannibals

  if(proportion)
    result <- list(Bottom=names.Bottom,
                   Intermediate=names.Intermediate,
                   Top=names.Top,
                   Unconnected=names.Unconnected,
                   Pure.cannibals=names.Pure.cannibals,
                   Proportions.of.each=c(number.Bottom, number.Intermediate, number.Top, number.Unconnected, number.Pure.cannibals)/length(web[1,]))
  if(!proportion)
    result <- list(Bottom=names.Bottom,
                   Intermediate=names.Intermediate,
                   Top=names.Top,
                   Unconnected=names.Unconnected,
                   Pure.cannibals=names.Pure.cannibals,
                   Proportions.of.each=c(number.Bottom, number.Intermediate, number.Top, number.Unconnected, number.Pure.cannibals))
  result
}

Num.herbivores <- function(web) {

    S <- length(web[,1])
    ## Find the rows/columns with basal species
    B.rows <-  c(1:S)[apply(web, 2, sum)==0 & apply(web, 1, sum)!=0]
    ## Find r/c wth species that only eat basal
    if(length(B.rows)==0)
        Nh <- NA

    if(length(B.rows)>0) {
        if( length(B.rows)>1 & (S-length(B.rows))>1 )
            Nh <- sum( apply(web[B.rows,], 2, function(x) sum(x!=0)>0) &
                      apply(web[-B.rows,], 2, function(x) sum(x!=0)==0) )

        if( length(B.rows)==1 & (S-length(B.rows))>1 )
            Nh <- sum( web[B.rows,]!=0  &
                      apply(web[-B.rows,], 2, function(x) sum(x!=0)==0) )

        if( length(B.rows)>1 & (S-length(B.rows))==1 )
            Nh <- sum( apply(web[B.rows,], 2, function(x) sum(x!=0)>0) &
                      web[-B.rows,]==0)
    }
    Nh

}

Cannibals <- function(web)
  length(dimnames(web)[[1]][diag(web)==1]) / length(web[1,])

Gen.sd <- function(web)
  # below is Pomeranz correction feb 2020
  sd(colSums(web)/(sum(web)/length(web[,1])))
	## corrected, cf. Thomas Barnum, 24.1.1013
	## sd(colSums(web)/sum(web)/length(web[,1]))
    ##sd(colSums(web)/sum(web))

Vul.sd <- function(web)
  # below is Pomeranz correction feb 2020
  sd(rowSums(web)/(sum(web)/length(web[,1])))
## corrected, cf. Thomas Barnum, 24.1.1013
	## sd(rowSums(web)/sum(web)/length(web[,1]))
    ##sd(rowSums(web)/sum(web))

Maxsim <- function(web){
  sims <- matrix(0, length(web[,1]), length(web[,1]))
  for(i in 1:length(web[,1]))
    for(j in 1:length(web[,1]))
      sims[i,j] <- T.sim.ij(web, i, j)
  diag(sims) <- NA
  mean(apply(sims, 1, function(x) max(x[!is.na(x)])))
}

## used by Maxsim
T.sim.ij <- function(web, i, j){
  same <- sum(web[i,] & web[j,]) + sum(web[,i] & web[,j])
  total <- sum(web[i,] | web[j,]) + sum(web[,i] | web[,j])
  same / total
}

GetTL <- function(web){

    ## takes predation matrix with consumers in columns
    ## identify the columns with basal species
    tweb <- t(web)

    ## make the rows add to one
    rs <- rowSums(tweb)
    for(i in 1:length(tweb[,1]))
        tweb[i,tweb[i,]==1] = 1/rs[i]

    nb.TL <- try(solve(diag(length(tweb[,1])) - tweb), T)

    if(class(nb.TL)=="try-error")
        nbTL <- rep(NA, length(tweb[,1]))

    if(class(nb.TL)!="try-error")
        nbTL <- rowSums(nb.TL)

    nbTL

}

## takes a food web in matrix format and coverts it to list format
Matrix.to.list <- function(web.matrix, predator.first=TRUE){
  if(length(dimnames(web.matrix)[[1]])==length(web.matrix[1,]))
    species.names <- dimnames(web.matrix)[[1]]
  else
    species.names <- 1:length(web.matrix[,1])
  web.list <- matrix(0, sum(web.matrix), 2)
  counter <- 1
  for(i in 1:length(web.matrix[,1]))
    for(j in 1:length(web.matrix[,1]))
      if(web.matrix[i,j]==1){
        web.list[counter,] <- c(species.names[i],species.names[j])
        counter <- counter + 1
      }
  if(!predator.first)
    web.list <- cbind(web.list[,2], web.list[,1])
  web.list
}


                         CHAPTER  2

                   Data Structure Access

     The following functions allow one to create and manipu-
late  the  various  types of lisp data structures.  Refer to
1.2 for details of the data structures known to FRANZ LISP.

   2.1.  Lists

           The following functions exist  for  the  creation
      and  manipulating  of  lists.  Lists are composed of a
      linked list of objects  called  either  'list  cells',
      'cons cells' or 'dtpr cells'.  Lists are normally ter-
      minated with the special symbol nil.  nil  is  both  a
      symbol and a representation for the empty list ().

      2.1.1.  list creation

(cons 'g_arg1 'g_arg2)

     RETURNS: a new list cell whose car is g_arg1 and  whose
              cdr is g_arg2.

(xcons 'g_arg1 'g_arg2)

     EQUIVALENT TO: (_c_o_n_s '_g__a_r_g_2 '_g__a_r_g_1)

(ncons 'g_arg)

     EQUIVALENT TO: (_c_o_n_s '_g__a_r_g _n_i_l)

(list ['g_arg1 ... ])

     RETURNS: a list whose elements are the g_arg_i.

9

9Data Structure Access                                    2-1

Data Structure Access                                    2-2

(append 'l_arg1 'l_arg2)

     RETURNS: a list containing the elements of l_arg1  fol-
              lowed by l_arg2.

     NOTE: To generate the result, the top level list  cells
           of  l_arg1 are duplicated and the cdr of the last
           list cell is set to point to l_arg2.   Thus  this
           is  an  expensive  operation  if l_arg1 is large.
           See the  descriptions  of  _n_c_o_n_c  and  _t_c_o_n_c  for
           cheaper  ways  of  doing  the  _a_p_p_e_n_d if the list
           l_arg1 can be altered.

(append1 'l_arg1 'g_arg2)

     RETURNS: a list like l_arg1 with  g_arg2  as  the  last
              element.

     NOTE: this  is  equivalent  to  (append  'l_arg1  (list
           'g_arg2)).

    ____________________________________________________

    ; A common mistake is using append to add one element to the end of a list
    -> (_a_p_p_e_n_d '(_a _b _c _d) '_e)
    (a b c d . e)
    ; The user intended to say:
    -> (_a_p_p_e_n_d '(_a _b _c _d) '(_e))
    (_a _b _c _d _e)
    ; _b_e_t_t_e_r _i_s _a_p_p_e_n_d_1
    -> (_a_p_p_e_n_d_1 '(_a _b _c _d) '_e)
    (_a _b _c _d _e)
    ____________________________________________________

(quote! [g_qform_i] ...[! 'g_eform_i] ...  [!! 'l_form_i] ...)

     RETURNS: The list  resulting  from  the   splicing  and
              insertion process described below.

     NOTE: _q_u_o_t_e!  is the complement of the  _l_i_s_t  function.
           _l_i_s_t  forms  a list by evaluating each for in the
           argument list; evaluation is  suppressed  if  the
           form  is _q_u_o_t_eed.  In _q_u_o_t_e!, each form is impli-
           citly _q_u_o_t_eed.  To be evaluated, a form  must  be
           preceded  by one of the evaluate operations ! and
           !!. ! g_eform evaluates g_form and the  value  is
           inserted  in  the  place  of  the call; !! l_form
           evaluates l_form and the value  is  spliced  into
           the place of the call.

                                   Printed: January 31, 1984

Data Structure Access                                    2-3

           `Splicing in' means  that  the  parentheses  sur-
           rounding  the  list  are  removed  as the example
           below shows.  Use of the evaluate  operators  can
           occur at any level in a form argument.

           Another way to get the effect of the _q_u_o_t_e! func-
           tion is to use the backquote character macro (see
            8.3.3).

    ____________________________________________________

    (_q_u_o_t_e! _c_o_n_s ! (_c_o_n_s _1 _2) _3) = (_c_o_n_s (_1 . _2) _3)
    (_q_u_o_t_e! _1 !! (_l_i_s_t _2 _3 _4) _5) = (_1 _2 _3 _4 _5)
    (_s_e_t_q _q_u_o_t_e_d '_e_v_a_l_e_d)(_q_u_o_t_e! ! ((_I _a_m  ! _q_u_o_t_e_d))) = ((_I _a_m _e_v_a_l_e_d))
    (_q_u_o_t_e! _t_r_y ! '(_t_h_i_s ! _o_n_e)) = (_t_r_y (_t_h_i_s ! _o_n_e))
    ____________________________________________________

(bignum-to-list 'b_arg)

     RETURNS: A list  of  the  fixnums  which  are  used  to
              represent the bignum.

     NOTE: the inverse of this function is _l_i_s_t-_t_o-_b_i_g_n_u_m.

(list-to-bignum 'l_ints)

     WHERE:   l_ints is a list of fixnums.

     RETURNS: a bignum constructed of the given fixnums.

     NOTE: the inverse of this function is _b_i_g_n_u_m-_t_o-_l_i_s_t.

      2.1.2.  list predicates

9

9                                   Printed: January 31, 1984

Data Structure Access                                    2-4

(dtpr 'g_arg)

     RETURNS: t iff g_arg is a list cell.

     NOTE: that (dtpr '()) is nil.  The name dtpr is a  con-
           traction for ``dotted pair''.

(listp 'g_arg)

     RETURNS: t iff g_arg is a list object or nil.

(tailp 'l_x 'l_y)

     RETURNS: l_x, if a list cell _e_q  to  l_x  is  found  by
              _c_d_ring down l_y zero or more times, nil other-
              wise.

    ____________________________________________________

    -> (_s_e_t_q _x '(_a _b _c _d) _y (_c_d_d_r _x))
    (c d)
    -> (_a_n_d (_d_t_p_r _x) (_l_i_s_t_p _x))     ; x and y are dtprs and lists
    t
    -> (_d_t_p_r '())           ; () is the same as nil and is not a dtpr
    nil
    -> (_l_i_s_t_p '())          ; however it is a list
    t
    -> (_t_a_i_l_p _y _x)
    (c d)
    ____________________________________________________

(length 'l_arg)

     RETURNS: the number of elements in  the  top  level  of
              list l_arg.

      2.1.3.  list accessing

9

9                                   Printed: January 31, 1984

Data Structure Access                                    2-5

(car 'l_arg)
(cdr 'l_arg)

     RETURNS: _c_o_n_s cell.  (_c_a_r (_c_o_n_s x y)) is always x, (_c_d_r
              (_c_o_n_s  x  y)) is always y.  In FRANZ LISP, the
              cdr portion is located first in memory.   This
              is  hardly  noticeable, and we mention it pri-
              marily as a curiosity.

(c..r 'lh_arg)

     WHERE:   the .. represents any positive number  of  a's
              and d's.

     RETURNS: the result of accessing the list structure  in
              the  way determined by the function name.  The
              a's and d's are read from right to left,  a  d
              directing  the access down the cdr part of the
              list cell and an a down the car part.

     NOTE: lh_arg may also be nil, and it is guaranteed that
           the  car  and  cdr of nil is nil.  If lh_arg is a
           hunk,  then  (_c_a_r '_l_h__a_r_g)   is   the   same   as
           (_c_x_r _1 '_l_h__a_r_g) and  (_c_d_r '_l_h__a_r_g) is the same as
           (_c_x_r _0 '_l_h__a_r_g).
           It is generally hard to read and  understand  the
           context  of  functions  with large strings of a's
           and d's, but these  functions  are  supported  by
           rapid  accessing  and open-compiling (see Chapter
           12).

(nth 'x_index 'l_list)

     RETURNS: the nth element of l_list, assuming zero-based
              index.   Thus  (nth  0  l_list) is the same as
              (car l_list).  _n_t_h is both a function,  and  a
              compiler  macro,  so  that more efficient code
              might be generated than for _n_t_h_e_l_e_m (described
              below).

     NOTE: If x_arg1 is non-positive  or  greater  than  the
           length of the list, nil is returned.

9

9                                   Printed: January 31, 1984

Data Structure Access                                    2-6

(nthcdr 'x_index 'l_list)

     RETURNS: the result of  _c_d_ring  down  the  list  l_list
              x_index times.

     NOTE: If    x_index    is    less    than    0,    then
           (_c_o_n_s _n_i_l '_l__l_i_s_t) is returned.

(nthelem 'x_arg1 'l_arg2)

     RETURNS: The x_arg1'_s_t element of the list l_arg2.

     NOTE: This function comes from the PDP-11 Lisp system.

(last 'l_arg)

     RETURNS: the last list cell in the list l_arg.

     EXAMPLE: _l_a_s_t does NOT return the  last  element  of  a
              list!
              (_l_a_s_t '(_a _b)) = (b)

(ldiff 'l_x 'l_y)

     RETURNS: a  list  of all elements in l_x but not in l_y
              , i.e., the list difference of l_x and l_y.

     NOTE: l_y must be a tail of l_x, i.e., _e_q to the result
           of  applying  some  number  of _c_d_r's to l_x. Note
           that  the  value   of   _l_d_i_f_f   is   always   new
           list  structure  unless l_y is nil, in which case
           (_l_d_i_f_f _l__x _n_i_l) is l_x itself.  If l_y   is   not
           a  tail  of  l_x, _l_d_i_f_f generates an error.

     EXAMPLE: (_l_d_i_f_f '_l__x (_m_e_m_b_e_r '_g__f_o_o  '_l__x))  gives  all
              elements in l_x up to the first g_foo.

      2.1.4.  list manipulation

(rplaca 'lh_arg1 'g_arg2)

     RETURNS: the modified lh_arg1.

     SIDE EFFECT: the car of lh_arg1 is set to  g_arg2.   If
                  lh_arg1  is a hunk then the second element
                  of the hunk is set to g_arg2.

9

9                                   Printed: January 31, 1984

Data Structure Access                                    2-7

(rplacd 'lh_arg1 'g_arg2)

     RETURNS: the modified lh_arg1.

     SIDE EFFECT: the cdr of lh_arg2 is set to  g_arg2.   If
                  lh_arg1  is  a hunk then the first element
                  of the hunk is set to g_arg2.

(attach 'g_x 'l_l)

     RETURNS: l_l whose _c_a_r is now g_x, whose  _c_a_d_r  is  the
              original (_c_a_r _l__l), and whose _c_d_d_r is the ori-
              ginal (_c_d_r _l__l).

     NOTE: what happens is that g_x is added to  the  begin-
           ning  of  list l_l  yet maintaining the same list
           cell  at the beginning of the list.

(delete 'g_val 'l_list ['x_count])

     RETURNS: the result of  splicing  g_val  from  the  top
              level of l_list no more than x_count times.

     NOTE: x_count defaults to a very large number, thus  if
           x_count  is  not  given, all occurrences of g_val
           are removed from the top level of l_list.   g_val
           is compared with successive _c_a_r's of l_list using
           the function _e_q_u_a_l.

     SIDE EFFECT: l_list is modified using  rplacd,  no  new
                  list cells are used.

(delq 'g_val 'l_list ['x_count])
(dremove 'g_val 'l_list ['x_count])

     RETURNS: the result of  splicing  g_val  from  the  top
              level of l_list no more than x_count times.

     NOTE: _d_e_l_q (and _d_r_e_m_o_v_e) are the same as _d_e_l_e_t_e  except
           that _e_q is used for comparison instead of _e_q_u_a_l.

9

9                                   Printed: January 31, 1984

Data Structure Access                                    2-8

    ____________________________________________________

    ; note that you should use the value returned by _d_e_l_e_t_e or _d_e_l_q
    ; and not assume that g_val will always show the deletions.
    ; For example

    -> (_s_e_t_q _t_e_s_t '(_a _b _c _a _d _e))
    (a b c a d e)
    -> (_d_e_l_e_t_e '_a _t_e_s_t)
    (b c d e)         ; the value returned is what we would expect
    -> _t_e_s_t
    (a b c d e)       ; but test still has the first a in the list!
    ____________________________________________________

(remq 'g_x 'l_l ['x_count])
(remove 'g_x 'l_l)

     RETURNS: a _c_o_p_y of l_l  with  all  top  level  elements
              _e_q_u_a_l to g_x removed.  _r_e_m_q uses _e_q instead of
              _e_q_u_a_l for comparisons.

     NOTE: remove does not modify its arguments like _d_e_l_e_t_e,
           and _d_e_l_q do.

(insert 'g_object 'l_list 'u_comparefn 'g_nodups)

     RETURNS: a list consisting of l_list with g_object des-
              tructively  inserted  in a place determined by
              the ordering function u_comparefn.

     NOTE: (_c_o_m_p_a_r_e_f_n '_g__x  '_g__y)  should  return  something
           non-nil  if  g_x can precede g_y in sorted order,
           nil if g_y must precede g_x.  If  u_comparefn  is
           nil, alphabetical order will be used. If g_nodups
           is non-nil, an element will not be inserted if an
           equal  element  is  already  in the list.  _i_n_s_e_r_t
           does binary search to determine where  to  insert
           the new element.

9

9                                   Printed: January 31, 1984

Data Structure Access                                    2-9

(merge 'l_data1 'l_data2 'u_comparefn)

     RETURNS: the merged list of the two input sorted  lists
              l_data1  and  l_data1  using binary comparison
              function u_comparefn.

     NOTE: (_c_o_m_p_a_r_e_f_n '_g__x  '_g__y)  should  return  something
           non-nil  if  g_x can precede g_y in sorted order,
           nil if g_y must precede g_x.  If  u_comparefn  is
           nil,    alphabetical    order   will   be   used.
           u_comparefn should be thought of as "less than or
           equal".   _m_e_r_g_e  changes  both  of its data argu-
           ments.

(subst 'g_x 'g_y 'l_s)
(dsubst 'g_x 'g_y 'l_s)

     RETURNS: the result of substituting g_x for  all  _e_q_u_a_l
              occurrences of g_y  at all levels in l_s.

     NOTE: If g_y is a symbol, _e_q will be used for comparis-
           ons.   The function _s_u_b_s_t does not modify l_s but
           the function  _d_s_u_b_s_t  (destructive  substitution)
           does.

(lsubst 'l_x 'g_y 'l_s)

     RETURNS: a copy of l_s  with l_x spliced in  for  every
              occurrence  of  of g_y at all levels. Splicing
              in means that the parentheses surrounding  the
              list  l_x  are  removed  as  the example below
              shows.

    ____________________________________________________

    -> (_s_u_b_s_t '(_a _b _c) '_x '(_x _y _z (_x _y _z) (_x _y _z)))
    ((a b c) y z ((a b c) y z) ((a b c) y z))
    -> (_l_s_u_b_s_t '(_a _b _c) '_x '(_x _y _z (_x _y _z) (_x _y _z)))
    (a b c y z (a b c y z) (a b c y z))
    ____________________________________________________

9

9                                   Printed: January 31, 1984

Data Structure Access                                   2-10

(subpair 'l_old 'l_new 'l_expr)

     WHERE:   there are  the  same  number  of  elements  in
              l_old as l_new.

     RETURNS: the list l_expr  with  all  occurrences  of  a
              object  in l_old replaced by the corresponding
              one in l_new.  When a substitution is made,  a
              copy  of  the  value  to  substitute in is not
              made.

     EXAMPLE: (_s_u_b_p_a_i_r '(_a _c)' (_x _y) '(_a _b _c _d)) = (_x _b _y _d)

(nconc 'l_arg1 'l_arg2 ['l_arg3 ...])

     RETURNS: A list consisting of the  elements  of  l_arg1
              followed by the elements of l_arg2 followed by
              l_arg3 and so on.

     NOTE: The _c_d_r of  the  last  list  cell  of  l_arg_i  is
           changed to point to l_arg_i+_1.

    ____________________________________________________

    ; _n_c_o_n_c is faster than _a_p_p_e_n_d because it doesn't allocate new list cells.
    -> (_s_e_t_q _l_i_s_1 '(_a _b _c))
    (a b c)
    -> (_s_e_t_q _l_i_s_2 '(_d _e _f))
    (d e f)
    -> (_a_p_p_e_n_d _l_i_s_1 _l_i_s_2)
    (a b c d e f)
    -> _l_i_s_1
    (a b c)       ; note that lis1 has not been changed by _a_p_p_e_n_d
    -> (_n_c_o_n_c _l_i_s_1 _l_i_s_2)
    (a b c d e f) ; _n_c_o_n_c returns the same value as _a_p_p_e_n_d
    -> _l_i_s_1
    (a b c d e f) ; but in doing so alters lis1
    ____________________________________________________

9

9                                   Printed: January 31, 1984

Data Structure Access                                   2-11

(reverse 'l_arg)
(nreverse 'l_arg)

     RETURNS: the list l_arg with the elements  at  the  top
              level in reverse  order.

     NOTE: The function _n_r_e_v_e_r_s_e does the reversal in place,
           that is the list structure is modified.

(nreconc 'l_arg 'g_arg)

     EQUIVALENT TO: (_n_c_o_n_c (_n_r_e_v_e_r_s_e '_l__a_r_g) '_g__a_r_g)

   2.2.  Predicates

           The following functions test  for  properties  of
      data  objects.  When  the result of the test is either
      'false' or 'true',  then  nil  will  be  returned  for
      'false' and something other than nil (often t) will be
      returned for 'true'.

(arrayp 'g_arg)

     RETURNS: t iff g_arg is of type array.

(atom 'g_arg)

     RETURNS: t iff g_arg is not a list or hunk object.

     NOTE: (_a_t_o_m '()) returns t.

(bcdp 'g_arg)

     RETURNS: t iff g_arg is a data object of type binary.

     NOTE: This function is a throwback to the  PDP-11  Lisp
           system.   The  name stands for binary code predi-
           cate.

9

9                                   Printed: January 31, 1984

Data Structure Access                                   2-12

(bigp 'g_arg)

     RETURNS: t iff g_arg is a bignum.

(dtpr 'g_arg)

     RETURNS: t iff g_arg is a list cell.

     NOTE: that (dtpr '()) is nil.

(hunkp 'g_arg)

     RETURNS: t iff g_arg is a hunk.

(listp 'g_arg)

     RETURNS: t iff g_arg is a list object or nil.

(stringp 'g_arg)

     RETURNS: t iff g_arg is a string.

(symbolp 'g_arg)

     RETURNS: t iff g_arg is a symbol.

(valuep 'g_arg)

     RETURNS: t iff g_arg is a value cell

(vectorp 'v_vector)

     RETURNS: t iff the argument is a vector.

(vectorip 'v_vector)

     RETURNS: t iff the argument is an immediate-vector.

(type 'g_arg)
(typep 'g_arg)

     RETURNS: a symbol whose pname  describes  the  type  of
              g_arg.

9

9                                   Printed: January 31, 1984

Data Structure Access                                   2-13

(signp s_test 'g_val)

     RETURNS: t iff g_val is a number  and  the  given  test
              s_test on g_val returns true.

     NOTE: The fact that _s_i_g_n_p simply returns nil  if  g_val
           is  not  a  number is probably the most important
           reason that _s_i_g_n_p is used.  The permitted  values
           for  s_test  and what they mean are given in this
           table.

8                         ____________________
                          s_test   tested

8                         ________________________________________
                          l        g_val < 0
                          le       g_val <_ 0
                          e        g_val = 0
                          n        g_val =/ 0
                          ge       g_val >_ 0
                          g        g_val > 0
8                         ____________________
7                        |7|7|7|7|7|7|7|7|

                                            |7|7|7|7|7|7|7|7|

(eq 'g_arg1 'g_arg2)

     RETURNS: t if g_arg1 and g_arg2 are the exact same lisp
              object.

     NOTE: _E_q simply tests if g_arg1 and g_arg2 are  located
           in  the exact same place in memory.  Lisp objects
           which print the same are not necessarily _e_q.  The
           only  objects  guaranteed  to  be _e_q are interned
           symbols with the same print name.  [Unless a sym-
           bol  is  created  in  a special way (such as with
           _u_c_o_n_c_a_t or _m_a_k_n_a_m) it will be interned.]

(neq 'g_x 'g_y)

     RETURNS: t if g_x is not _e_q to g_y, otherwise nil.

(equal 'g_arg1 'g_arg2)
(eqstr 'g_arg1 'g_arg2)

     RETURNS: t iff g_arg1 and g_arg2 have the  same  struc-
              ture as described below.

     NOTE: g_arg and g_arg2 are _e_q_u_a_l if

     (1)  they are _e_q.

     (2)  they are both fixnums with the same value

9                                   Printed: January 31, 1984

Data Structure Access                                   2-14

     (3)  they are both flonums with the same value

     (4)  they are both bignums with the same value

     (5)  they are both strings and are identical.

     (6)  they are both lists and their cars  and  cdrs  are
          _e_q_u_a_l.

    ____________________________________________________

    ; _e_q is much faster than _e_q_u_a_l, especially in compiled code,
    ; however you cannot use _e_q to test for equality of numbers outside
    ; of the range -1024 to 1023.  _e_q_u_a_l will always work.
    -> (_e_q _1_0_2_3 _1_0_2_3)
    t
    -> (_e_q _1_0_2_4 _1_0_2_4)
    nil
    -> (_e_q_u_a_l _1_0_2_4 _1_0_2_4)
    t
    ____________________________________________________

(not 'g_arg)
(null 'g_arg)

     RETURNS: t iff g_arg is nil.

(member 'g_arg1 'l_arg2)
(memq 'g_arg1 'l_arg2)

     RETURNS: that part of the  l_arg2  beginning  with  the
              first  occurrence of g_arg1.  If g_arg1 is not
              in the top level of l_arg2, nil is returned.

     NOTE: _m_e_m_b_e_r tests for equality with _e_q_u_a_l, _m_e_m_q  tests
           for equality with _e_q.

   2.3.  Symbols and Strings

           In many of the following functions  the  distinc-
      tion  between symbols and strings is somewhat blurred.
      To remind ourselves of the difference, a string  is  a
      null terminated sequence of characters, stored as com-
      pactly as possible.  Strings are used as constants  in

                                   Printed: January 31, 1984

Data Structure Access                                   2-15

      FRANZ  LISP.   They  _e_v_a_l to themselves.  A symbol has
      additional structure: a value, property list, function
      binding,  as  well  as its external representation (or
      print-name).  If a symbol  is  given  to  one  of  the
      string  manipulation  functions  below, its print name
      will be used as the string.

           Another popular way to represent strings in  Lisp
      is  as  a  list of fixnums which represent characters.
      The suffix 'n' to a string manipulation function indi-
      cates that it returns a string in this form.

      2.3.1.  symbol and string creation

(concat ['stn_arg1 ... ])
(uconcat ['stn_arg1 ... ])

     RETURNS: a symbol whose print name  is  the  result  of
              concatenating  the print names, string charac-
              ters  or  numerical  representations  of   the
              sn_arg_i.

     NOTE: If no arguments are given, a symbol with  a  null
           pname  is  returned.   _c_o_n_c_a_t  places  the symbol
           created on the oblist, the function _u_c_o_n_c_a_t  does
           the  same thing but does not place the new symbol
           on the oblist.

     EXAMPLE: (_c_o_n_c_a_t '_a_b_c (_a_d_d _3 _4) "_d_e_f") = abc7def

(concatl 'l_arg)

     EQUIVALENT TO: (_a_p_p_l_y '_c_o_n_c_a_t '_l__a_r_g)

(implode 'l_arg)
(maknam 'l_arg)

     WHERE:   l_arg is a list of symbols, strings and  small
              fixnums.

     RETURNS: The symbol whose print name is the  result  of
              concatenating  the  first  characters  of  the
              print names of the symbols and strings in  the
              list.    Any  fixnums  are  converted  to  the
              equivalent ascii character.  In order to  con-
              catenate  entire  strings  or print names, use
              the function _c_o_n_c_a_t.

     NOTE: _i_m_p_l_o_d_e interns the  symbol  it  creates,  _m_a_k_n_a_m
           does not.

                                   Printed: January 31, 1984

Data Structure Access                                   2-16

(gensym ['s_leader])

     RETURNS: a new uninterned atom beginning with the first
              character  of  s_leader's  pname, or beginning
              with g if s_leader is not given.

     NOTE: The  symbol  looks  like  x0nnnnn  where   x   is
           s_leader's  first  character  and  nnnnn  is  the
           number of times you have called gensym.

(copysymbol 's_arg 'g_pred)

     RETURNS: an uninterned symbol with the same print  name
              as  s_arg.   If  g_pred  is  non nil, then the
              value, function binding and property  list  of
              the new symbol are made _e_q to those of s_arg.

(ascii 'x_charnum)

     WHERE:   x_charnum is between 0 and 255.

     RETURNS: a symbol whose print name is the single  char-
              acter    whose    fixnum   representation   is
              x_charnum.

(intern 's_arg)

     RETURNS: s_arg

     SIDE EFFECT: s_arg is put on the oblist if  it  is  not
                  already there.

(remob 's_symbol)

     RETURNS: s_symbol

     SIDE EFFECT: s_symbol is removed from the oblist.

(rematom 's_arg)

     RETURNS: t if s_arg is indeed an atom.

     SIDE EFFECT: s_arg is  put  on  the  free  atoms  list,
                  effectively reclaiming an atom cell.

     NOTE: This function does _n_o_t check to see if  s_arg  is
           on  the  oblist  or is referenced anywhere.  Thus
           calling _r_e_m_a_t_o_m on an  atom  in  the  oblist  may
           result in disaster when that atom cell is reused!

9

9                                   Printed: January 31, 1984

Data Structure Access                                   2-17

      2.3.2.  string and symbol predicates

(boundp 's_name)

     RETURNS: nil  if s_name is unbound:  that  is,  it  has
              never  been  given a value.  If x_name has the
              value g_val, then (nil . g_val)  is  returned.
              See also _m_a_k_u_n_b_o_u_n_d.

(alphalessp 'st_arg1 'st_arg2)

     RETURNS: t iff the `name' of st_arg1 is  alphabetically
              less  than the name of st_arg2. If st_arg is a
              symbol then its `name' is its print name.   If
              st_arg  is  a  string,  then its `name' is the
              string itself.

      2.3.3.  symbol and string accessing

(symeval 's_arg)

     RETURNS: the value of symbol s_arg.

     NOTE: It is illegal to ask for the value of an  unbound
           symbol.   This  function  has  the same effect as
           _e_v_a_l, but compiles into much more efficient code.

(get_pname 's_arg)

     RETURNS: the string which is the print name of s_arg.

(plist 's_arg)

     RETURNS: the property list of s_arg.

(getd 's_arg)

     RETURNS: the function definition of  s_arg  or  nil  if
              there is no function definition.

     NOTE: the function definition may turn  out  to  be  an
           array header.

9

9                                   Printed: January 31, 1984

Data Structure Access                                   2-18

(getchar 's_arg 'x_index)
(nthchar 's_arg 'x_index)
(getcharn 's_arg 'x_index)

     RETURNS: the x_index_t_h character of the print  name  of
              s_arg  or  nil  if  x_index  is less than 1 or
              greater than the length of s_arg's print name.

     NOTE: _g_e_t_c_h_a_r and _n_t_h_c_h_a_r return a symbol with a single
           character print name, _g_e_t_c_h_a_r_n returns the fixnum
           representation of the character.

(substring 'st_string 'x_index ['x_length])
(substringn 'st_string 'x_index ['x_length])

     RETURNS: a string of length at most  x_length  starting
              at x_index_t_h character in the string.

     NOTE: If x_length is not given, all of  the  characters
           for   x_index  to  the  end  of  the  string  are
           returned.  If  x_index  is  negative  the  string
           begins  at  the x_index_t_h character from the end.
           If x_index is out of bounds, nil is returned.

     NOTE: _s_u_b_s_t_r_i_n_g returns a list of  symbols,  _s_u_b_s_t_r_i_n_g_n
           returns  a  list  of  fixnums.   If _s_u_b_s_t_r_i_n_g_n is
           given a 0 x_length argument then a single  fixnum
           which is the x_index_t_h character is returned.

      2.3.4.  symbol and string manipulation

(set 's_arg1 'g_arg2)

     RETURNS: g_arg2.

     SIDE EFFECT: the value of s_arg1 is set to g_arg2.

(setq s_atm1 'g_val1 [ s_atm2 'g_val2 ... ... ])

     WHERE:   the arguments are  pairs  of  atom  names  and
              expressions.

     RETURNS: the last g_val_i.

     SIDE EFFECT: each s_atm_i  is  set  to  have  the  value
                  g_val_i.

     NOTE: _s_e_t evaluates all of its arguments, _s_e_t_q does not
           evaluate the s_atm_i.

9

9                                   Printed: January 31, 1984

Data Structure Access                                   2-19

(desetq sl_pattern1 'g_exp1 [... ...])

     RETURNS: g_expn

     SIDE EFFECT: This  acts  just  like  _s_e_t_q  if  all  the
                  sl_pattern_i  are  symbols.  If sl_pattern_i
                  is a list then it   is  a  template  which
                  should  have  the same structure as g_exp_i
                  The symbols in sl_pattern are assigned  to
                  the  corresponding  parts  of g_exp.  (See
                  also _s_e_t_f )

     EXAMPLE: (_d_e_s_e_t_q (_a _b (_c . _d)) '(_1 _2 (_3 _4 _5)))
              sets a to 1, b to 2, c to 3, and d to (4 5).

(setplist 's_atm 'l_plist)

     RETURNS: l_plist.

     SIDE EFFECT: the property  list  of  s_atm  is  set  to
                  l_plist.

(makunbound 's_arg)

     RETURNS: s_arg

     SIDE EFFECT: the value of s_arg is made `unbound'.   If
                  the interpreter attempts to evaluate s_arg
                  before it  is  again  given  a  value,  an
                  unbound variable error will occur.

(aexplode 's_arg)
(explode 'g_arg)
(aexplodec 's_arg)
(explodec 'g_arg)
(aexploden 's_arg)
(exploden 'g_arg)

     RETURNS: a list of the characters  used  to  print  out
              s_arg or g_arg.

     NOTE: The functions beginning  with  'a'  are  internal
           functions  which are limited to symbol arguments.
           The functions _a_e_x_p_l_o_d_e and _e_x_p_l_o_d_e return a  list
           of  characters which _p_r_i_n_t would use to print the
           argument. These characters include all  necessary
           escape   characters.    Functions  _a_e_x_p_l_o_d_e_c  and
           _e_x_p_l_o_d_e_c return a list of characters which  _p_a_t_o_m
           would  use  to print the argument (i.e. no escape
           characters).  Functions  _a_e_x_p_l_o_d_e_n  and  _e_x_p_l_o_d_e_n
           are similar to _a_e_x_p_l_o_d_e_c and _e_x_p_l_o_d_e_c except that
           a list of fixnum equivalents  of  characters  are

                                   Printed: January 31, 1984

Data Structure Access                                   2-20

           returned.

    ____________________________________________________

    -> (_s_e_t_q _x '|_q_u_o_t_e _t_h_i_s _\| _o_k?|)
    |quote this \| ok?|
    -> (_e_x_p_l_o_d_e _x)
    (q u o t e |\\| | | t h i s |\\| | | |\\| |\|| |\\| | | o k ?)
    ; note that |\\| just means the single character: backslash.
    ; and |\|| just means the single character: vertical bar
    ; and | | means the single character: space

    -> (_e_x_p_l_o_d_e_c _x)
    (q u o t e | | t h i s | | |\|| | | o k ?)
    -> (_e_x_p_l_o_d_e_n _x)
    (113 117 111 116 101 32 116 104 105 115 32 124 32 111 107 63)
    ____________________________________________________

   2.4.  Vectors

           See Chapter 9 for a discussion of vectors.   They
      are  less efficient that hunks but more efficient than
      arrays.

      2.4.1.  vector creation

(new-vector 'x_size ['g_fill ['g_prop]])

     RETURNS: A vector of length x_size.  Each data entry is
              initialized to g_fill, or to nil, if the argu-
              ment g_fill is not present.  The vector's pro-
              perty is set to g_prop, or to nil, by default.

(new-vectori-byte 'x_size ['g_fill ['g_prop]])
(new-vectori-word 'x_size ['g_fill ['g_prop]])
(new-vectori-long 'x_size ['g_fill ['g_prop]])

     RETURNS: A vectori with x_size  elements  in  it.   The
              actual  memory requirement is two long words +
              x_size*(n  bytes),  where  n  is  1  for  new-
              vector-byte,  2  for new-vector-word, or 4 for
              new-vectori-long.  Each data entry is initial-
              ized  to  g_fill,  or to zero, if the argument
              g_fill is not present.  The vector's  property
              is set to g_prop, or nil, by default.

                                   Printed: January 31, 1984

Data Structure Access                                   2-21

     Vectors may be created by specifying  multiple  initial
     values:

(vector ['g_val0 'g_val1 ...])

     RETURNS: a vector, with as many data elements as  there
              are arguments.  It is quite possible to have a
              vector with no data  elements.   The  vector's
              property will be a null list.

(vectori-byte ['x_val0 'x_val2 ...])
(vectori-word ['x_val0 'x_val2 ...])
(vectori-long ['x_val0 'x_val2 ...])

     RETURNS: a vectori, with as many data elements as there
              are  arguments.  The arguments are required to
              be fixnums.  Only the low order byte  or  word
              is  used  in  the  case  of  vectori-byte  and
              vectori-word.  The vector's property  will  be
              null.

      2.4.2.  vector reference

(vref 'v_vect 'x_index)
(vrefi-byte 'V_vect 'x_bindex)
(vrefi-word 'V_vect 'x_windex)
(vrefi-long 'V_vect 'x_lindex)

     RETURNS: the desired data element from a  vector.   The
              indices  must  be  fixnums.  Indexing is zero-
              based.  The vrefi functions  sign  extend  the
              data.

(vprop 'Vv_vect)

     RETURNS: The Lisp property associated with a vector.

(vget 'Vv_vect 'g_ind)

     RETURNS: The value stored under g_ind if the Lisp  pro-
              perty  associated  with 'Vv_vect is a disembo-
              died property list.

9

9                                   Printed: January 31, 1984

Data Structure Access                                   2-22

(vsize 'Vv_vect)
(vsize-byte 'V_vect)
(vsize-word 'V_vect)

     RETURNS: the number of data  elements  in  the  vector.
              For  immediate-vectors,  the  functions vsize-
              byte and vsize-word return the number of  data
              elements,  if one thinks of the binary data as
              being comprised of bytes or words.

      2.4.3.  vector modfication

(vset 'v_vect 'x_index 'g_val)
(vseti-byte 'V_vect 'x_bindex 'x_val)
(vseti-word 'V_vect 'x_windex 'x_val)
(vseti-long 'V_vect 'x_lindex 'x_val)

     RETURNS: the datum.

     SIDE EFFECT: The indexed element of the vector  is  set
                  to  the value.  As noted above, for vseti-
                  word and vseti-byte,  the  index  is  con-
                  strued  as  the number of the data element
                  within the  vector.   It  is  not  a  byte
                  address.   Also,  for those two functions,
                  the low order byte or  word  of  x_val  is
                  what is stored.

(vsetprop 'Vv_vect 'g_value)

     RETURNS: g_value.  This should be either a symbol or  a
              disembodied  property list whose _c_a_r is a sym-
              bol identifying the type of the vector.

     SIDE EFFECT: the property list of  Vv_vect  is  set  to
                  g_value.

(vputprop 'Vv_vect 'g_value 'g_ind)

     RETURNS: g_value.

     SIDE EFFECT: If the vector property  of  Vv_vect  is  a
                  disembodied  property  list, then vputprop
                  adds the value g_value under the indicator
                  g_ind.  Otherwise, the old vector property
                  is made the first element of the list.

9

9                                   Printed: January 31, 1984

Data Structure Access                                   2-23

   2.5.  Arrays

           See Chapter  9  for  a  complete  description  of
      arrays.  Some of these functions are part of a Maclisp
      array compatibility package representing only one sim-
      ple way of using the array structure of FRANZ LISP.

      2.5.1.  array creation

(marray 'g_data 's_access 'g_aux 'x_length 'x_delta)

     RETURNS: an array type with the fields set up from  the
              above  arguments  in  the  obvious  way  (see
              1.2.10).

(*array 's_name 's_type 'x_dim1 ... 'x_dim_n)
(array s_name s_type x_dim1 ... x_dim_n)

     WHERE:   s_type may be one of t, nil,  fixnum,  flonum,
              fixnum-block and flonum-block.

     RETURNS: an array of type s_type with n  dimensions  of
              extents given by the x_dim_i.

     SIDE EFFECT: If s_name is non nil, the function defini-
                  tion  of s_name is set to the array struc-
                  ture returned.

     NOTE: These  functions  create  a  Maclisp   compatible
           array.  In FRANZ LISP arrays of type t, nil, fix-
           num and flonum are equivalent and the elements of
           these  arrays  can  be  any  type of lisp object.
           Fixnum-block and  flonum-block  arrays  are  res-
           tricted  to  fixnums and flonums respectively and
           are used mainly to communicate with foreign func-
           tions (see 8.5).

     NOTE: *_a_r_r_a_y evaluates its arguments, _a_r_r_a_y does not.

      2.5.2.  array predicate

9

9                                   Printed: January 31, 1984

Data Structure Access                                   2-24

(arrayp 'g_arg)

     RETURNS: t iff g_arg is of type array.

      2.5.3.  array accessors

(getaccess 'a_array)
(getaux 'a_array)
(getdelta 'a_array)
(getdata 'a_array)
(getlength 'a_array)

     RETURNS: the field of the array object a_array given by
              the function name.

(arrayref 'a_name 'x_ind)

     RETURNS: the  x_ind_t_h  element  of  the  array   object
              a_name.  x_ind of zero accesses the first ele-
              ment.

     NOTE: _a_r_r_a_y_r_e_f uses the data, length and  delta  fields
           of a_name to determine which object to return.

(arraycall s_type 'as_array 'x_ind1 ... )

     RETURNS: the element selected by  the indices from  the
              array a_array of type s_type.

     NOTE: If as_array is a symbol then the function binding
           of this symbol should contain an array object.
           s_type is ignored by _a_r_r_a_y_c_a_l_l  but  is  included
           for compatibility with Maclisp.

(arraydims 's_name)

     RETURNS: a list of the type and  bounds  of  the  array
              s_name.

9

9                                   Printed: January 31, 1984

Data Structure Access                                   2-25

(listarray 'sa_array ['x_elements])

     RETURNS: a  list  of  all  of  the  elements  in  array
              sa_array.   If  x_elements is given, then only
              the first x_elements are returned.

    ____________________________________________________

    ; We will create a 3 by 4 array of general lisp objects
    -> (_a_r_r_a_y _e_r_n_i_e _t _3 _4)
    array[12]

    ; the array header is stored in the function definition slot of the
    ; symbol ernie
    -> (_a_r_r_a_y_p (_g_e_t_d '_e_r_n_i_e))
    t
    -> (_a_r_r_a_y_d_i_m_s (_g_e_t_d '_e_r_n_i_e))
    (t 3 4)

    ; store in ernie[2][2] the list (test list)
    -> (_s_t_o_r_e (_e_r_n_i_e _2 _2) '(_t_e_s_t _l_i_s_t))
    (test list)

    ; check to see if it is there
    -> (_e_r_n_i_e _2 _2)
    (test list)

    ; now use the low level function _a_r_r_a_y_r_e_f to find the same element
    ; arrays are 0 based and row-major (the last subscript varies the fastest)
    ; thus element [2][2] is the 10th element , (starting at 0).
    -> (_a_r_r_a_y_r_e_f (_g_e_t_d '_e_r_n_i_e) _1_0)
    (ptr to)(test list)    ; the result is a value cell (thus the (ptr to))
    ____________________________________________________

      2.5.4.  array manipulation

9

9                                   Printed: January 31, 1984

Data Structure Access                                   2-26

(putaccess 'a_array 'su_func)
(putaux 'a_array 'g_aux)
(putdata 'a_array 'g_arg)
(putdelta 'a_array 'x_delta)
(putlength 'a_array 'x_length)

     RETURNS: the second argument to the function.

     SIDE EFFECT: The field of the array object given by the
                  function  name  is  replaced by the second
                  argument to the function.

(store 'l_arexp 'g_val)

     WHERE:   l_arexp is an expression which  references  an
              array element.

     RETURNS: g_val

     SIDE EFFECT: the array location which contains the ele-
                  ment  which  l_arexp references is changed
                  to contain g_val.

(fillarray 's_array 'l_itms)

     RETURNS: s_array

     SIDE EFFECT: the array s_array is filled with  elements
                  from l_itms.  If there are not enough ele-
                  ments in l_itms to fill the entire  array,
                  then the last element of l_itms is used to
                  fill the remaining parts of the array.

   2.6.  Hunks

           Hunks are  vector-like  objects  whose  size  can
      range  from  1 to 128 elements.  Internally, hunks are
      allocated in sizes which are powers of 2.  In order to
      create  hunks  of  a  given size, a hunk with at least
      that many elements is allocated  and  a  distinguished
      symbol   EMPTY   is   placed  in  those  elements  not
      requested.  Most hunk  functions  respect  those  dis-
      tinguished  symbols,  but  there are two (*_m_a_k_h_u_n_k and
      *_r_p_l_a_c_x) which will overwrite the  distinguished  sym-
      bol.

9

9                                   Printed: January 31, 1984

Data Structure Access                                   2-27

      2.6.1.  hunk creation

(hunk 'g_val1 ['g_val2 ... 'g_val_n])

     RETURNS: a hunk of length n whose elements are initial-
              ized to the g_val_i.

     NOTE: the maximum size of a hunk is 128.

     EXAMPLE: (_h_u_n_k _4 '_s_h_a_r_p '_k_e_y_s) = {4 sharp keys}

(makhunk 'xl_arg)

     RETURNS: a hunk of length  xl_arg  initialized  to  all
              nils  if  xl_arg  is a fixnum.  If xl_arg is a
              list,  then  we  return   a   hunk   of   size
              (_l_e_n_g_t_h '_x_l__a_r_g)  initialized  to the elements
              in xl_arg.

     NOTE: (_m_a_k_h_u_n_k '(_a _b _c))     is      equivalent      to
           (_h_u_n_k '_a '_b '_c).

     EXAMPLE: (_m_a_k_h_u_n_k _4) = {_n_i_l _n_i_l _n_i_l _n_i_l}

(*makhunk 'x_arg)

     RETURNS: a hunk of size 2[x_arg] initialized to EMPTY.

     NOTE: This is only to be used by such functions as _h_u_n_k
           and _m_a_k_h_u_n_k which create and initialize hunks for
           users.

      2.6.2.  hunk accessor

(cxr 'x_ind 'h_hunk)

     RETURNS: element x_ind (starting at 0) of hunk h_hunk.

(hunk-to-list 'h_hunk)

     RETURNS: a list consisting of the elements of h_hunk.

      2.6.3.  hunk manipulators

9

9                                   Printed: January 31, 1984

Data Structure Access                                   2-28

(rplacx 'x_ind 'h_hunk 'g_val)
(*rplacx 'x_ind 'h_hunk 'g_val)

     RETURNS: h_hunk

     SIDE EFFECT: Element x_ind (starting at 0) of h_hunk is
                  set to g_val.

     NOTE: _r_p_l_a_c_x will not modify one of  the  distinguished
           (EMPTY) elements whereas *_r_p_l_a_c_x will.

(hunksize 'h_arg)

     RETURNS: the size of the hunk h_arg.

     EXAMPLE: (_h_u_n_k_s_i_z_e (_h_u_n_k _1 _2 _3)) = 3

   2.7.  Bcds

           A bcd object contains a pointer to compiled  code
      and  to  the type of function object the compiled code
      represents.

(getdisc 'y_bcd)
(getentry 'y_bcd)

     RETURNS: the field of the bcd object given by the func-
              tion name.

(putdisc 'y_func 's_discipline)

     RETURNS: s_discipline

     SIDE EFFECT: Sets the discipline  field  of  y_func  to
                  s_discipline.

   2.8.  Structures

           There are three common structures constructed out
      of  list  cells: the assoc list, the property list and
      the tconc list.  The functions below manipulate  these
      structures.

      2.8.1.  assoc list

              An `assoc list' (or alist) is  a  common  lisp
         data structure.  It has the form

                                   Printed: January 31, 1984

Data Structure Access                                   2-29

         ((key1 . value1) (key2 . value2) (key3 . value3) ... (keyn . valuen))

(assoc 'g_arg1 'l_arg2)
(assq 'g_arg1 'l_arg2)

     RETURNS: the first top level element  of  l_arg2  whose
              _c_a_r is _e_q_u_a_l (with _a_s_s_o_c) or _e_q (with _a_s_s_q) to
              g_arg1.

     NOTE: Usually l_arg2 has an _a-_l_i_s_t structure and g_arg1
           acts as key.

(sassoc 'g_arg1 'l_arg2 'sl_func)

     RETURNS: the                 result                  of
              (_c_o_n_d ((_a_s_s_o_c '_g__a_r_g '_l__a_r_g_2) (_a_p_p_l_y '_s_l__f_u_n_c _n_i_l)))

     NOTE: sassoc is written as a macro.

(sassq 'g_arg1 'l_arg2 'sl_func)

     RETURNS: the                 result                  of
              (_c_o_n_d ((_a_s_s_q '_g__a_r_g '_l__a_r_g_2) (_a_p_p_l_y '_s_l__f_u_n_c _n_i_l)))

     NOTE: sassq is written as a macro.

    ____________________________________________________

    ; _a_s_s_o_c or _a_s_s_q is given a key and an assoc list and returns
    ; the key and value item if it exists, they differ only in how they test
    ; for equality of the keys.

    -> (_s_e_t_q _a_l_i_s_t '((_a_l_p_h_a . _a) ( (_c_o_m_p_l_e_x _k_e_y) . _b) (_j_u_n_k . _x)))
    ((alpha . a) ((complex key) . b) (junk . x))

    ; we should use _a_s_s_q when the key is an atom
    -> (_a_s_s_q '_a_l_p_h_a _a_l_i_s_t)
    (alpha . a)

    ; but it may not work when the key is a list
    -> (_a_s_s_q '(_c_o_m_p_l_e_x _k_e_y) _a_l_i_s_t)
    nil

    ; however _a_s_s_o_c will always work
    -> (_a_s_s_o_c '(_c_o_m_p_l_e_x _k_e_y) _a_l_i_s_t)
    ((complex key) . b)
    ____________________________________________________

9

9                                   Printed: January 31, 1984

Data Structure Access                                   2-30

(sublis 'l_alst 'l_exp)

     WHERE:   l_alst is an _a-_l_i_s_t.

     RETURNS: the list l_exp with every occurrence  of  key_i
              replaced by val_i.

     NOTE: new list structure is returned to prevent modifi-
           cation  of l_exp.  When a substitution is made, a
           copy of the value to substitute in is not made.

      2.8.2.  property list

              A property list  consists  of  an  alternating
         sequence  of  keys and values.  Normally a property
         list is stored on a symbol. A list is  a  'disembo-
         died' property list if it contains an odd number of
         elements, the first of which is ignored.

(plist 's_name)

     RETURNS: the property list of s_name.

(setplist 's_atm 'l_plist)

     RETURNS: l_plist.

     SIDE EFFECT: the property  list  of  s_atm  is  set  to
                  l_plist.

(get 'ls_name 'g_ind)

     RETURNS: the value under indicator g_ind  in  ls_name's
              property list if ls_name is a symbol.

     NOTE: If there is no indicator g_ind in ls_name's  pro-
           perty list nil is returned.  If ls_name is a list
           of an odd number of elements then it is a  disem-
           bodied  property list. _g_e_t searches a disembodied
           property list by starting at its _c_d_r, and compar-
           ing every other element with g_ind, using _e_q.

9

9                                   Printed: January 31, 1984

Data Structure Access                                   2-31

(getl 'ls_name 'l_indicators)

     RETURNS: the property list  ls_name  beginning  at  the
              first  indicator which is a member of the list
              l_indicators, or nil if none of the indicators
              in  l_indicators  are  on  ls_name's  property
              list.

     NOTE: If ls_name is a list, then it is assumed to be  a
           disembodied property list.

(putprop 'ls_name 'g_val 'g_ind)
(defprop ls_name g_val g_ind)

     RETURNS: g_val.

     SIDE EFFECT: Adds to the property list of  ls_name  the
                  value g_val under the indicator g_ind.

     NOTE: _p_u_t_p_r_o_p evaluates it arguments, _d_e_f_p_r_o_p does not.
           ls_name  may  be a disembodied property list, see
           _g_e_t.

(remprop 'ls_name 'g_ind)

     RETURNS: the portion of  ls_name's property list begin-
              ning  with  the  property  under the indicator
              g_ind.  If there  is  no  g_ind  indicator  in
              ls_name's plist, nil is returned.

     SIDE EFFECT: the value under indicator g_ind and  g_ind
                  itself  is  removed from the property list
                  of ls_name.

     NOTE: ls_name may be a disembodied property  list,  see
           _g_e_t.

9

9                                   Printed: January 31, 1984

Data Structure Access                                   2-32

    ____________________________________________________

    -> (_p_u_t_p_r_o_p '_x_l_a_t_e '_a '_a_l_p_h_a)
    a
    -> (_p_u_t_p_r_o_p '_x_l_a_t_e '_b '_b_e_t_a)
    b
    -> (_p_l_i_s_t '_x_l_a_t_e)
    (alpha a beta b)
    -> (_g_e_t '_x_l_a_t_e '_a_l_p_h_a)
    a
    ; use of a disembodied property list:
    -> (_g_e_t '(_n_i_l _f_a_t_e_m_a_n _r_j_f _s_k_l_o_w_e_r _k_l_s _f_o_d_e_r_a_r_o _j_k_f) '_s_k_l_o_w_e_r)
    kls
    ____________________________________________________

      2.8.3.  tconc structure

              A tconc structure is a special  type  of  list
         designed to make it easy to add objects to the end.
         It consists of a list cell whose _c_a_r  points  to  a
         list  of the elements added with _t_c_o_n_c or _l_c_o_n_c and
         whose _c_d_r points to the last list cell of the  list
         pointed to by the _c_a_r.

(tconc 'l_ptr 'g_x)

     WHERE:   l_ptr is a tconc structure.

     RETURNS: l_ptr with g_x added to the end.

(lconc 'l_ptr 'l_x)

     WHERE:   l_ptr is a tconc structure.

     RETURNS: l_ptr with the list l_x spliced in at the end.

9

9                                   Printed: January 31, 1984

Data Structure Access                                   2-33

    ____________________________________________________

    ; A _t_c_o_n_c structure can be initialized in two  ways.
    ; nil can be given to _t_c_o_n_c in which case _t_c_o_n_c will generate
    ; a _t_c_o_n_c structure.

    ->(_s_e_t_q _f_o_o (_t_c_o_n_c _n_i_l _1))
    ((1) 1)

    ; Since _t_c_o_n_c destructively adds to
    ; the list, you can now add to foo without using _s_e_t_q again.

    ->(_t_c_o_n_c _f_o_o _2)
    ((1 2) 2)
    ->_f_o_o
    ((1 2) 2)

    ; Another way to create a null  _t_c_o_n_c structure
    ; is to use (_n_c_o_n_s _n_i_l).

    ->(_s_e_t_q _f_o_o (_n_c_o_n_s _n_i_l))
    (nil)
    ->(_t_c_o_n_c _f_o_o _1)
    ((1) 1)

    ; now see what _l_c_o_n_c can do
    -> (_l_c_o_n_c _f_o_o _n_i_l)
    ((1) 1)            ; no change
    -> (_l_c_o_n_c _f_o_o '(_2 _3 _4))
    ((1 2 3 4) 4)
    ____________________________________________________

      2.8.4.  fclosures

              An  fclosure  is  a  functional  object  which
         admits some data manipulations.  They are discussed
         in 8.4.  Internally, they are constructed from vec-
         tors.

9

9                                   Printed: January 31, 1984

Data Structure Access                                   2-34

(fclosure 'l_vars 'g_funobj)

     WHERE:   l_vars is a list of variables, g_funobj is any
              object that can be funcalled (including, fclo-
              sures).

     RETURNS: A vector which is the fclosure.

(fclosure-alist 'v_fclosure)

     RETURNS: An association list representing the variables
              in  the  fclosure.   This is a snapshot of the
              current state of the fclosure.  If  the  bind-
              ings  in  the fclosure are changed, any previ-
              ously  calculated  results  of  _f_c_l_o_s_u_r_e-_a_l_i_s_t
              will not change.

(fclosure-function 'v_fclosure)

     RETURNS: the functional object part of the fclosure.

(fclosurep 'v_fclosure)

     RETURNS: t iff the argument is an fclosure.

(symeval-in-fclosure 'v_fclosure 's_symbol)

     RETURNS: the current binding of a particular symbol  in
              an fclosure.

(set-in-fclosure 'v_fclosure 's_symbol 'g_newvalue)

     RETURNS: g_newvalue.

     SIDE EFFECT: The variable  s_symbol  is  bound  in  the
                  fclosure to g_newvalue.

   2.9.  Random functions

           The following functions don't fall  into  any  of
      the classifications above.

9

9                                   Printed: January 31, 1984

Data Structure Access                                   2-35

(bcdad 's_funcname)

     RETURNS: a fixnum which is the address in memory  where
              the function s_funcname begins.  If s_funcname
              is not a machine coded function (binary)  then
              _b_c_d_a_d returns nil.

(copy 'g_arg)

     RETURNS: A structure _e_q_u_a_l to g_arg but with  new  list
              cells.

(copyint* 'x_arg)

     RETURNS: a fixnum with the same value as x_arg but in a
              freshly allocated cell.

(cpy1 'xvt_arg)

     RETURNS: a new cell of the same type  as  xvt_arg  with
              the same value as xvt_arg.

(getaddress 's_entry1 's_binder1  'st_discipline1  [...  ...
...])

     RETURNS: the binary object which s_binder1's   function
              field is set to.

     NOTE: This looks in the running lisp's symbol table for
           a symbol with the same name as s_entry_i.  It then
           creates a binary object whose entry field  points
           to    s_entry_i    and    whose    discipline   is
           st_discipline_i.  This binary object is stored  in
           the    function    field    of   s_binder_i.    If
           st_discipline_i is nil, then "subroutine" is  used
           by  default.  This is especially useful for _c_f_a_s_l
           users.

(macroexpand 'g_form)

     RETURNS: g_form after all macros in it are expanded.

     NOTE: This function will only  macroexpand  expressions
           which  could  be  evaluated  and it does not know
           about the special nlambdas such as _c_o_n_d  and  _d_o,
           thus it misses many macro expansions.

9

9                                   Printed: January 31, 1984

Data Structure Access                                   2-36

(ptr 'g_arg)

     RETURNS: a value cell initialized to point to g_arg.

(quote g_arg)

     RETURNS: g_arg.

     NOTE: the reader allows you to abbreviate  (quote  foo)
           as 'foo.

(kwote 'g_arg)

     RETURNS:  (_l_i_s_t (_q_u_o_t_e _q_u_o_t_e) _g__a_r_g).

(replace 'g_arg1 'g_arg2)

     WHERE:   g_arg1 and g_arg2 must be  the  same  type  of
              lispval and not symbols or hunks.

     RETURNS: g_arg2.

     SIDE EFFECT: The effect of _r_e_p_l_a_c_e is dependent on  the
                  type  of  the  g_arg_i  although  one  will
                  notice a similarity in  the  effects.   To
                  understand what _r_e_p_l_a_c_e does to fixnum and
                  flonum arguments, you  must  first  under-
                  stand  that  such  numbers  are `boxed' in
                  FRANZ LISP.  What this means  is  that  if
                  the  symbol  x  has a value 32412, then in
                  memory the value  element  of  x's  symbol
                  structure  contains the address of another
                  word of memory (called a box)  with  32412
                  in it.

                  Thus, there are two ways of  changing  the
                  value  of  x:  the  first is to change the
                  value element of x's symbol  structure  to
                  point to a word of memory with a different
                  value.  The second way is  to  change  the
                  value  in  the box which x points to.  The
                  former method is used almost  all  of  the
                  time,  the  latter is used very rarely and
                  has the potential to  cause  great  confu-
                  sion.   The function _r_e_p_l_a_c_e allows you to
                  do the latter, i.e.,  to  actually  change
                  the value in the box.

                  You should watch out for these situations.
                  If  you  do  (_s_e_t_q _y _x), then both x and y
                  will point to the same box.   If  you  now
                  (_r_e_p_l_a_c_e _x _1_2_3_4_5),  then  y will also have
                  the value 12345.  And, in fact, there  may

                                   Printed: January 31, 1984

Data Structure Access                                   2-37

                  be many other pointers to that box.

                  Another problem with replacing fixnums  is
                  that  some  boxes are read-only.  The fix-
                  nums between -1024 and 1023 are stored  in
                  a  read-only  area and attempts to replace
                  them will result  in  an  "Illegal  memory
                  reference"  error  (see the description of
                  _c_o_p_y_i_n_t* for a way around this problem).

                  For the other valid types, the  effect  of
                  _r_e_p_l_a_c_e is easy to understand.  The fields
                  of g_val1's structure are made eq  to  the
                  corresponding  fields  of  g_val2's struc-
                  ture.  For example,  if  x   and   y  have
                  lists   as   values  then  the  effect  of
                  (_r_e_p_l_a_c_e _x _y)    is    the     same     as
                  (_r_p_l_a_c_a _x (_c_a_r _y)) and (_r_p_l_a_c_d _x (_c_d_r _y)).

(scons 'x_arg 'bs_rest)

     WHERE:   bs_rest is a bignum or nil.

     RETURNS: a bignum whose first bigit is x_arg and  whose
              higher order bigits are bs_rest.

(setf g_refexpr 'g_value)

     NOTE: _s_e_t_f is a generalization  of  setq.   Information
           may  be  stored  by  binding variables, replacing
           entries of arrays, and vectors, or being  put  on
           property  lists,  among  others.  Setf will allow
           the user to store data  into  some  location,  by
           mentioning  the  operation  used  to refer to the
           location.  Thus, the first argument may  be  par-
           tially  evaluated,  but only to the extent needed
           to calculate a reference.  _s_e_t_f returns  g_value.
           (Compare to _d_e_s_e_t_q )

    ____________________________________________________

      (setf x 3)        =  (setq x 3)
      (setf (car x) 3)  = (rplaca x 3)
      (setf (get foo 'bar) 3) = (putprop foo 3 'bar)
      (setf (vref vector index) value) = (vset vector index value)
    ____________________________________________________

9

9                                   Printed: January 31, 1984

Data Structure Access                                   2-38

(sort 'l_data 'u_comparefn)

     RETURNS: a list of the elements of  l_data  ordered  by
              the comparison function u_comparefn.

     SIDE EFFECT: the list l_data is  modified  rather  than
                  allocated in new storage.

     NOTE: (_c_o_m_p_a_r_e_f_n '_g__x  '_g__y)  should  return  something
           non-nil  if  g_x can precede g_y in sorted order;
           nil if g_y must precede g_x.  If  u_comparefn  is
           nil, alphabetical order will be used.

(sortcar 'l_list 'u_comparefn)

     RETURNS: a list of the  elements  of  l_list  with  the
              _c_a_r's    ordered    by   the   sort   function
              u_comparefn.

     SIDE EFFECT: the list l_list is  modified  rather  than
                  copied.

     NOTE: Like _s_o_r_t, if u_comparefn  is  nil,  alphabetical
           order will be used.

9

9                                   Printed: January 31, 1984