diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 0000000..4ae7f69
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,141 @@
+name: Build and Test
+
+on:
+ push:
+ branches: [ main, 'release/**' ]
+ pull_request:
+ branches: [ main ]
+
+jobs:
+ build-linux:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install dependencies
+ run: |
+ sudo apt-get update
+ sudo apt-get install -y gcc make zlib1g-dev
+
+ - name: Build nauty
+ run: |
+ curl -L -o nauty2_9_3.tar.gz https://users.cecs.anu.edu.au/~bdm/nauty/nauty2_9_3.tar.gz
+ tar xzf nauty2_9_3.tar.gz
+ cd nauty2_9_3
+ ./configure && make
+
+ - name: Build surge
+ working-directory: src
+ run: |
+ make surge \
+ NAUTY=${{ github.workspace }}/nauty2_9_3 \
+ NAUTYLIB=${{ github.workspace }}/nauty2_9_3 \
+ CCOPT="-O3"
+
+ - name: Test surge
+ working-directory: src
+ run: |
+ ./surge -help 2>&1 | head -3
+ ./surge -u C6H6
+ ./surge -u C4H10
+
+ - name: Build canonsdf
+ working-directory: src
+ run: |
+ make canonsdf \
+ NAUTY=${{ github.workspace }}/nauty2_9_3 \
+ NAUTYLIB=${{ github.workspace }}/nauty2_9_3 \
+ CCOPT="-O3"
+
+ - name: Upload Linux binary
+ uses: actions/upload-artifact@v4
+ with:
+ name: surge-linux-x86_64
+ path: src/surge
+
+ build-macos:
+ strategy:
+ matrix:
+ include:
+ - os: macos-14
+ arch: arm64
+ - os: macos-15
+ arch: arm64-m15
+ runs-on: ${{ matrix.os }}
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install nauty
+ run: brew install nauty
+
+ - name: Create nauty library symlink
+ run: |
+ NAUTY_LIB=$(brew --prefix nauty)/lib
+ ln -sf "$NAUTY_LIB/libnautyL1.a" "$NAUTY_LIB/nautyL1.a"
+
+ - name: Build surge
+ working-directory: src
+ run: |
+ make surge \
+ NAUTY=$(brew --prefix nauty)/include/nauty \
+ NAUTYLIB=$(brew --prefix nauty)/lib \
+ CCOPT="-O3"
+
+ - name: Test surge
+ working-directory: src
+ run: |
+ ./surge -help 2>&1 | head -3
+ ./surge -u C6H6
+ ./surge -u C4H10
+
+ - name: Upload macOS binary
+ uses: actions/upload-artifact@v4
+ with:
+ name: surge-macos-${{ matrix.arch }}
+ path: src/surge
+
+ build-windows:
+ runs-on: windows-latest
+ defaults:
+ run:
+ shell: msys2 {0}
+ steps:
+ - uses: actions/checkout@v4
+
+ - uses: msys2/setup-msys2@v2
+ with:
+ msystem: UCRT64
+ install: >-
+ mingw-w64-ucrt-x86_64-gcc
+ make
+ mingw-w64-ucrt-x86_64-zlib
+ curl
+ tar
+
+ - name: Build nauty
+ run: |
+ curl -L -o nauty2_9_3.tar.gz https://users.cecs.anu.edu.au/~bdm/nauty/nauty2_9_3.tar.gz
+ tar xzf nauty2_9_3.tar.gz
+ cd nauty2_9_3
+ ./configure && make
+
+ - name: Build surge
+ working-directory: src
+ run: |
+ make surge \
+ NAUTY=$(cygpath -u "$GITHUB_WORKSPACE")/nauty2_9_3 \
+ NAUTYLIB=$(cygpath -u "$GITHUB_WORKSPACE")/nauty2_9_3 \
+ CCOPT="-O3"
+
+ - name: Test surge
+ working-directory: src
+ run: |
+ ./surge.exe -help 2>&1 | head -3
+ ./surge.exe -u C6H6
+ ./surge.exe -u C4H10
+
+ - name: Upload Windows binary
+ uses: actions/upload-artifact@v4
+ with:
+ name: surge-windows-x86_64
+ path: src/surge.exe
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..3a30013
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,137 @@
+name: Release
+
+on:
+ push:
+ tags:
+ - 'v*'
+
+jobs:
+ build-release:
+ strategy:
+ matrix:
+ include:
+ - os: ubuntu-latest
+ artifact: surge-linux-x86_64
+ binary: surge
+ - os: macos-14
+ artifact: surge-macos-arm64
+ binary: surge
+ - os: windows-latest
+ artifact: surge-windows-x86_64
+ binary: surge.exe
+ runs-on: ${{ matrix.os }}
+ defaults:
+ run:
+ shell: ${{ matrix.os == 'windows-latest' && 'msys2 {0}' || 'bash' }}
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Setup MSYS2 (Windows only)
+ if: matrix.os == 'windows-latest'
+ uses: msys2/setup-msys2@v2
+ with:
+ msystem: UCRT64
+ install: >-
+ mingw-w64-ucrt-x86_64-gcc
+ make
+ mingw-w64-ucrt-x86_64-zlib
+ curl
+ tar
+
+ - name: Install dependencies (Linux)
+ if: matrix.os == 'ubuntu-latest'
+ run: |
+ sudo apt-get update
+ sudo apt-get install -y gcc make zlib1g-dev
+
+ - name: Install nauty (macOS)
+ if: startsWith(matrix.os, 'macos')
+ run: |
+ brew install nauty
+ NAUTY_LIB=$(brew --prefix nauty)/lib
+ ln -sf "$NAUTY_LIB/libnautyL1.a" "$NAUTY_LIB/nautyL1.a"
+
+ - name: Build nauty from source (Linux/Windows)
+ if: matrix.os == 'ubuntu-latest' || matrix.os == 'windows-latest'
+ run: |
+ curl -L -o nauty2_9_3.tar.gz https://users.cecs.anu.edu.au/~bdm/nauty/nauty2_9_3.tar.gz
+ tar xzf nauty2_9_3.tar.gz
+ cd nauty2_9_3
+ ./configure && make
+
+ - name: Build surge (Linux/Windows)
+ if: matrix.os == 'ubuntu-latest' || matrix.os == 'windows-latest'
+ working-directory: src
+ run: |
+ make surge \
+ NAUTY=$(cygpath -u "$GITHUB_WORKSPACE" 2>/dev/null || echo "$GITHUB_WORKSPACE")/nauty2_9_3 \
+ NAUTYLIB=$(cygpath -u "$GITHUB_WORKSPACE" 2>/dev/null || echo "$GITHUB_WORKSPACE")/nauty2_9_3 \
+ CCOPT="-O3"
+
+ - name: Build surge (macOS)
+ if: startsWith(matrix.os, 'macos')
+ working-directory: src
+ run: |
+ make surge \
+ NAUTY=$(brew --prefix nauty)/include/nauty \
+ NAUTYLIB=$(brew --prefix nauty)/lib \
+ CCOPT="-O3"
+
+ - name: Upload artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: ${{ matrix.artifact }}
+ path: src/${{ matrix.binary }}
+
+ create-release:
+ needs: build-release
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Download all artifacts
+ uses: actions/download-artifact@v4
+ with:
+ path: artifacts
+
+ - name: Package binaries
+ run: |
+ cd artifacts
+ for dir in */; do
+ name="${dir%/}"
+ cd "$name"
+ chmod +x * 2>/dev/null || true
+ tar czf "../${name}.tar.gz" *
+ cd ..
+ done
+
+ - name: Create Release
+ uses: softprops/action-gh-release@v2
+ with:
+ name: "Surge ${{ github.ref_name }}"
+ draft: true
+ generate_release_notes: false
+ body: |
+ ## Surge 2.0
+
+ A fast open-source chemical graph generator.
+
+ ### What's New in 2.0
+ - **64-bit word size**: Supports larger molecular structures
+ - **Aromaticity detection** (`-R`): Filter duplicate Kekule structures under carbon-ring aromaticity
+ - **Hexagon restrictions** (`-h#`, `-h#:#`): Control 6-membered cycle counts
+ - **Carbon 6-ring restrictions** (`-C#`, `-C#:#`): Limit carbon-only 6-membered rings
+ - **Improved summary reporting**
+
+ ### Installation
+ Download the binary for your platform below, or build from source (see README).
+
+ ### Binaries
+ - `surge-linux-x86_64.tar.gz` - Linux (x86_64)
+ - `surge-macos-arm64.tar.gz` - macOS (Apple Silicon)
+ - `surge-windows-x86_64.tar.gz` - Windows (x86_64, built with MSYS2)
+ files: |
+ artifacts/*.tar.gz
+ doc/surge2_0.pdf
diff --git a/.gitignore b/.gitignore
index be4f0d8..04726a4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -53,3 +53,8 @@ dkms.conf
# Editor backup files
*~
+
+# Surge binaries
+surge
+surge_[0-9]
+canonsdf
diff --git a/Dockerfile b/Dockerfile
index 33db01b..7b5c2fe 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,39 +1,26 @@
-FROM ubuntu:21.04
+FROM alpine:3.21 AS builder
-RUN apt-get update && \
- apt-get -y install curl gcc make zlib1g-dev && \
- apt-get -y clean && \
- rm -rf \
- /var/lib/apt/lists/* \
- /usr/share/doc \
- /usr/share/doc-base \
- /usr/share/man \
- /usr/share/locale \
- /usr/share/zoneinfo
-WORKDIR /root
-RUN curl -o nauty27r3.tar.gz http://users.cecs.anu.edu.au/~bdm/nauty/nauty27r3.tar.gz \
- && tar xzvf nauty27r3.tar.gz \
- && cd nauty27r3 \
- && ./configure && make
-ENV NAUTY_HOME=/root/nauty27r3
-COPY src/surge.c $NAUTY_HOME
-COPY src/Makefile /root
-WORKDIR $NAUTY_HOME
-RUN ln -s /root/nauty27r3 /root/nauty
-RUN make -f ../Makefile clean ; make -f ../Makefile surge
+RUN apk add --no-cache build-base curl zlib-dev
-FROM ubuntu:21.04
+WORKDIR /build
-RUN apt-get update && \
- apt-get -y install curl time gnupg zlib1g && \
- apt-get -y clean && \
- rm -rf \
- /var/lib/apt/lists/* \
- /usr/share/doc \
- /usr/share/doc-base \
- /usr/share/man \
- /usr/share/locale \
- /usr/share/zoneinfo
-RUN echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] http://packages.cloud.google.com/apt cloud-sdk main" | tee -a /etc/apt/sources.list.d/google-cloud-sdk.list && curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key --keyring /usr/share/keyrings/cloud.google.gpg add - && apt-get update -y && apt-get install google-cloud-sdk -y
+# Download and build nauty
+RUN curl -L -o nauty2_9_3.tar.gz https://users.cecs.anu.edu.au/~bdm/nauty/nauty2_9_3.tar.gz \
+ && tar xzf nauty2_9_3.tar.gz \
+ && cd nauty2_9_3 \
+ && ./configure && make -j4
-COPY --from=0 /root/nauty27r3/surge /usr/bin
+# Copy source and build surge (static linking for minimal runtime image)
+COPY src/ /build/src/
+WORKDIR /build/src
+RUN make surge \
+ NAUTY=/build/nauty2_9_3 \
+ NAUTYLIB=/build/nauty2_9_3 \
+ CCOPT="-O3 -static"
+
+# Minimal runtime image (~9MB)
+FROM alpine:3.21
+
+COPY --from=builder /build/src/surge /usr/local/bin/surge
+
+ENTRYPOINT ["surge"]
diff --git a/README.md b/README.md
index e199e4c..64dd132 100644
--- a/README.md
+++ b/README.md
@@ -3,10 +3,18 @@
# Surge: A Fast Open-Source Chemical Graph Generator
## About
Surge is a chemical structure generator based on the Nauty package and thereby on the principles of canonical augmentation.
-More precisely, Surge generates all non-isomorphic constitutional isomers of a given molecular formula. [**Surge's article**](https://jcheminf.biomedcentral.com/articles/10.1186/s13321-022-00604-9) is published in Journal of Cheminformatics and more details are given in its [**user manual**](https://github.com/StructureGenerator/SURGE/blob/main/doc/surge1_0.pdf) for more information.
+More precisely, Surge generates all non-isomorphic constitutional isomers of a given molecular formula. [**Surge's article**](https://jcheminf.biomedcentral.com/articles/10.1186/s13321-022-00604-9) is published in Journal of Cheminformatics and more details are given in its [**user manual**](https://github.com/StructureGenerator/SURGE/blob/main/doc/surge2_0.pdf).
+
+## What's New in 2.0
+- **Aromaticity filtering** (`-R`): Removes duplicate Kekule structures under carbon-ring aromaticity.
+- **6-member cycle limits** (`-h#`, `-h#:#`): Restrict the number of cycles of length 6.
+- **Chord-free 6-carbon cycle limits** (`-C#`, `-C#:#`): Restrict the number of chord-free cycles of 6 carbon atoms.
+- **SDfile output flag** (`-F`): Explicit flag for SDfile output format.
+- **64-bit word size**: Surge 2.0 is built with `WORDSIZE=64`, supporting larger molecules.
+- Updated to nauty 2.9.3.
## Usage
-Surge is a command line tool. Running `surge -u C10H16O` will generate the 452458 isomers of C10H16O in 0.1s on some vanilla flavor year-2021 PC. Running `surge -S C10H16O` outputs those structurs in SMILES format. You can either use `surge -S C10H16O > myresults.smi` to redirect the output into a result file, or use the `-o`switch to provide a filename. Further formats supported are SD Files (SDF) and a concise Surge-specific format.
+Surge is a command line tool. Running `surge -u C10H16O` will generate the 452458 isomers of C10H16O in 0.1s on some vanilla flavor year-2021 PC. Running `surge -S C10H16O` outputs those structures in SMILES format. You can either use `surge -S C10H16O > myresults.smi` to redirect the output into a result file, or use the `-o` switch to provide a filename. Further formats supported are SD Files (SDF) and a concise Surge-specific format.
For large sets of structures, the -z option for compressing the output in gzip format will come in handy.
`surge -help` will show all options:
@@ -16,47 +24,50 @@ For large sets of structures, the -z option for compressing the output in gzip f
```
-Usage: surge [-oFILE] [-z] [-u|-A|-S] [-T] [-e#|-e#:#] [-d#] [-c#] [-m#/#] formula
+Usage: surge [-oFILE] [-z] [-A|-S|-F] [-T] [-e#|-e#:#] [-R] [-d#] [-c#] [-m#/#] formula
-Make chemical graphs from a formula. Version 0.9.
-Known elements are C,B,N,P,O,S,H,Cl,F,Br,I at their lowest valences.
-Higher valences can be selected using Nx (Nitrogen/5), Sx,Sy (Sulfur 4/6), Px (Phosphorus/5).
+Make chemical graphs from a formula. Version 2.0.
+ Known elements are C,B,N,P,O,S,H,Cl,F,Br,I at their lowest valences.
+ Higher valences can be selected using Nx (Nitrogen/5), Sx,Sy (Sulfur 4/6)
+ Px (Phosphorus/5).
-formula = a formula like C8H6N2
+ formula = a formula like C8H6N2
- -E.. Define a new element (see the manual)
- -O# Output stage: 1 after geng, 2 after vcolg, 3 after multig
- Default is to write SDF format
+ -u Just count, don't write molecules (default)
-S Output in SMILES format
+ -F Output in SDfile format
-A Output in alphabetical format
- -u Just count, don't write
- -e# -e#:# Restrict to given range of distinct non-H bonds
- -t# -t#:# Limit number of rings of length 3
- -f# -f#:# Limit number of cycles of length 4
- -p# -p#:# Limit number of cycles of length 5
+ -e# -e#:# Limit the number of distinct non-H bonds
+ -t# -t#:# Limit the number of cycles of length 3
+ -f# -f#:# Limit the number of cycles of length 4
+ -p# -p#:# Limit the number of cycles of length 5
+ -h# -h#:# Limit the number of cycles of length 6
+ -C# -C#:# Limit the number of chord-free cycles 6 carbon atoms
-b Only rings of even length (same as only cycles of even length)
-T Disallow triple bonds
-P Require planarity
-d# Maximum degree not counting bond multiplicity or hydrogens (default 4)
-c# Maximum coordination number (default 4). This is the maximum number
- of distinct atoms (including H) that an atom can be bonded to
- Coordination number > 4 is only allowed if no neighbours are H
+ of distinct atoms (including H) that an atom can be bonded to
+ Coordination number > 4 is only allowed if no neighbours are H
-B#,...,# Specify sets of substructures to avoid (details in manual)
1 = no triple bonds in rings up to length 7
2 = Bredt's rule for two rings ij with one bond in
- common (33, 34, 35, 36, 44, 45)
+ common (33, 34, 35, 36, 44, 45)
3 = Bredt's rule for two rings ij with two bonds in
- common (i,j up to 56)
+ common (i,j up to 56)
4 = Bredt's rule for two rings of length 6 sharing three bonds
5 = no substructures A=A=A (in ring or not)
6 = no substructures A=A=A in rings up to length 8
- For -B5 and -B6, the central atom only has 2 non-H neighbours
+ For -B5 and -B6, the central atom only has 2 non-H neighbours
7 = no K_33 or K_24 structure
8 = none of cone of P4 or K4 with 3-ear
9 = no atom in more than one ring of length 3 or 4
+ -R Enable aromaticity detection (filters duplicate Kekule structures)
-v Write more information to stderr
-m#/# Do only a part. The two numbers are res/mod where 0<=res results.smi
```
## Misc
Surge was developed by [Brendan McKay](http://users.cecs.anu.edu.au/~bdm) with the help of [Christoph Steinbeck](https://github.com/steinbeck) and [Mehmet Aziz Yirik](https://github.com/mehmetazizyirik).
-
diff --git a/doc/surge2_0.pdf b/doc/surge2_0.pdf
new file mode 100644
index 0000000..4737dc1
Binary files /dev/null and b/doc/surge2_0.pdf differ
diff --git a/src/Makefile b/src/Makefile
index 6a95de3..d771416 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -1,65 +1,63 @@
-# Makefile for surge
-VERSION=1_0
-
-# Define CC to be the name of the C compiler, and CCOPT to be
-# switches for good optimization in your environment.
-CC=gcc
-CCOPT=-march=native -mtune=native -O3
-
-# Define NAUTY to be the directory where files like nauty.h are
-# located. Define NAUTYLIB to be the directory where files like
-# nautyW1.a are located.
-NAUTY=${HOME}/nauty
-NAUTYLIB=${NAUTY}
-
-# If you have zlib installed, define ZLIB to be "-DZLIB" and ZLIBLIB
-# to be the linker command for zlib.
-ZLIB=-DZLIB
-ZLIBLIB=-lz
-# If you don't have zlib installed, make both of them null.
-#ZLIB=
-#ZLIBLIB=
-
-SURGE=-g -I ${NAUTY} -DWORDSIZE=32 -DMAXN=WORDSIZE -DOUTPROC=surgeproc ${CCOPT} \
- -DPREPRUNE=surgepreprune ${ZLIB} -DPRUNE=surgeprune -DGENG_MAIN=geng_main
-
-all : surge surge_0 surge_1 surge_2 surge_3
-
-surge : surge.c
- ${CC} -o surge ${SURGE} \
- surge.c geng.c planarity.c ${NAUTYLIB}/nautyW1.a ${ZLIBLIB}
-
-surge_0 : surge.c plugin0.c
- ${CC} -o surge_0 ${SURGE} -DSURGEPLUGIN='"plugin0.c"' \
- surge.c geng.c planarity.c ${NAUTYLIB}/nautyW1.a ${ZLIBLIB}
-
-surge_1 : surge.c plugin1.c
- ${CC} -o surge_1 ${SURGE} -DSURGEPLUGIN='"plugin1.c"' \
- surge.c geng.c planarity.c ${NAUTYLIB}/nautyW1.a ${ZLIBLIB}
-
-surge_2 : surge.c plugin2.c
- ${CC} -o surge_2 ${SURGE} -DSURGEPLUGIN='"plugin2.c"' \
- surge.c geng.c planarity.c ${NAUTYLIB}/nautyW1.a ${ZLIBLIB}
-
-surge_3 : surge.c plugin3.c
- ${CC} -o surge_3 ${SURGE} -DSURGEPLUGIN='"plugin3.c"' \
- surge.c geng.c planarity.c ${NAUTYLIB}/nautyW1.a ${ZLIBLIB}
-
-clean :
- rm -rf surge surge_0 surge_1 surge_2 surge_3 canonsdf
-
-# This one is unoptimized for easier debugging
-surge_g :
- ${CC} -o surge_g ${SURGE} -g -O0 \
- surge.c geng.c planarity.c ${NAUTYLIB}/nautyW1.a ${ZLIBLIB}
-
-canonsdf : canonsdf.c
- ${CC} -o canonsdf -I {NAUTY} canonsdf.c ${NAUTYLIB}/nautyL1.a
-
-tarfile :
- rm -rf surge${VERSION}
- mkdir surge${VERSION}
- cp SDFformats.txt surge.c planarity.c geng.c makefile COPYRIGHT \
- canonsdf.c plugin[0-3].c surge1_0.pdf surge${VERSION}/
- touch surge${VERSION}/This_is_surge_${VERSION}.txt
- tar cvf surge${VERSION}.tar surge${VERSION}/
+# Makefile for surge
+VERSION=2_0
+
+# Define CC to be the name of the C compiler, and CCOPT to be
+# switches for good optimization in your environment.
+CC=gcc
+CCOPT=-O3 -march=native
+
+# Define NAUTY to be the directory where files like nauty.h are
+# located. Define NAUTYLIB to be the directory where files like
+# nautyL1.a are located.
+NAUTY=${HOME}/nauty
+NAUTYLIB=${NAUTY}
+
+# If you have zlib installed, define ZLIB to be "-DZLIB" and LZ
+# to be the linker command for zlib.
+ZLIB=-DZLIB
+LZ=-lz
+# If you don't have zlib installed, make both of them null.
+#ZLIB=
+#LZ=
+
+SURGE=-g -I ${NAUTY} ${CCOPT} -DWORDSIZE=64 -DMAXN=WORDSIZE \
+ -DOUTPROC=surgeproc -DPREPRUNE=surgepreprune \
+ -DPRUNE=surgeprune -DGENG_MAIN=geng_main
+
+all : surge
+
+surge : surge.c
+ ${CC} -o surge ${SURGE} ${ZLIB} \
+ surge.c geng.c planarity.c ${NAUTYLIB}/nautyL1.a ${LZ}
+
+samples : surge_0 surge_1 surge_2 surge_3
+
+surge_0 : surge.c plugin0.c
+ ${CC} -o surge_0 ${SURGE} ${ZLIB} -DSURGEPLUGIN='"plugin0.c"' \
+ surge.c geng.c planarity.c ${NAUTYLIB}/nautyL1.a ${LZ}
+
+surge_1 : surge.c plugin1.c
+ ${CC} -o surge_1 ${SURGE} ${ZLIB} -DSURGEPLUGIN='"plugin1.c"' \
+ surge.c geng.c planarity.c ${NAUTYLIB}/nautyL1.a ${LZ}
+
+surge_2 : surge.c plugin2.c
+ ${CC} -o surge_2 ${SURGE} ${ZLIB} -DSURGEPLUGIN='"plugin2.c"' \
+ surge.c geng.c planarity.c ${NAUTYLIB}/nautyL1.a ${LZ}
+
+surge_3 : surge.c plugin3.c
+ ${CC} -o surge_3 ${SURGE} ${ZLIB} -DSURGEPLUGIN='"plugin3.c"' \
+ surge.c geng.c planarity.c ${NAUTYLIB}/nautyL1.a ${LZ}
+
+canonsdf : canonsdf.c
+ ${CC} -o canonsdf -I${NAUTY} ${CCOPT} canonsdf.c ${NAUTYLIB}/nautyL1.a
+
+clean :
+ rm -rf surge surge_0 surge_1 surge_2 surge_3 canonsdf *.dSYM
+
+tarfile :
+ rm -rf surge${VERSION}
+ mkdir surge${VERSION}
+ cp surge.c planarity.c geng.c makefile LICENSE-2.0 README.txt \
+ canonsdf.c plugin[0-3].c surge${VERSION}.pdf surge${VERSION}/
+ touch surge${VERSION}/This_is_surge_${VERSION}.txt
+ tar cvf surge${VERSION}.tar surge${VERSION}/
diff --git a/src/canonsdf.c b/src/canonsdf.c
index 8e2810d..93b796b 100644
--- a/src/canonsdf.c
+++ b/src/canonsdf.c
@@ -1,451 +1,792 @@
-#define WORDSIZE 64
-#define MAXN WORDSIZE
-#include "gtools.h"
-
-#define USAGE \
- "canonsdf [-A|-w|-u] [-p#|-p#:#] [-e#|-e#:#] [-d#] [-c#] [-a] [-Hcode]"
-
-#define HELPTEXT \
-" Process molecules in SDF format.\n" \
-"\n" \
-" -a Allow more than 4 neighbours to include H\n" \
-" and N with more than 4 neighbours (default not allowed)\n" \
-" -d# Maximum degree for simple graph without H (default 4)\n" \
-" -c# Maximum coordination number without H (default 4)\n" \
-" -e# -e#:# Number of bonds between non-H atoms\n" \
-"\n" \
-" -p# -p#:# Only process one input or range of inputs (first is 1)\n" \
-" -Hcode Output is restricted to the input with this hash code.\n" \
-" The code continues to the end of the argument. If this option\n" \
-" is given, all other constraints are ignored.\n" \
-"\n" \
-" -w Write SDF to output identical to the input\n" \
-" -A Write an ASCII molecule decription\n" \
-" -u Just count\n" \
-" The default is to write a 16-character hash code\n"
-
-
-static char *atom[]
- = { "H","C","N","O","P","S","F","Cl","Br","I","B","Si" };
-static int val1[]
- = { 1, 4, 3, 2, 3, 2, 1, 1, 1, 1, 1, 4 };
-static int val2[]
- = { 0, 0, 5, 2, 5, 4, 0, 0, 0, 0, 0, 0 };
-static int val3[]
- = { 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0 };
-#define NUMATOMS (sizeof(atom)/sizeof(atom[0]))
-#define HYDROGEN 0
-#define NITROGEN 2
-
-static int fuzz1[] = {0x37541,0x61532,0x05257,0x26416};
-static int fuzz2[] = {0x06532,0x70236,0x35523,0x62437};
-
-#define FUZZ1(x) ((x) ^ fuzz1[(x)&3])
-#define FUZZ2(x) ((x) ^ fuzz2[(x)&3])
-#define MV(x) ((x) >> (WORDSIZE-n))
-
-
-typedef char card[84];
-static card line[200];
-
-static int
-atomcode(char *name)
-{
- int i;
-
- for (i = 0; i < NUMATOMS; ++i)
- if (strcmp(atom[i],name) == 0) break;
-
- if (i == NUMATOMS)
- {
- fprintf(stderr,">E unknown element %s\n",name);
- exit(1);
- }
-
- return i;
-}
-
-static int
-roundval(int index, int val)
-/* Round val up to the next legal value */
-{
- if (val1[index] >= val) return val1[index];
- if (val2[index] >= val) return val2[index];
- if (val3[index] >= val) return val3[index];
-
- fprintf(stderr,"index=%d val=%d\n",index,val);
- gt_abort(">E can't determine valence\n");
-}
-
-static int
-readmolecule()
-/* Read one SDF molecule into line[*], return number of lines (0 if none) */
-{
- int numlines;
-
- if (fgets(line[0],82,stdin) == NULL) return 0;
- numlines = 1;
- while (fgets(line[numlines],82,stdin) != NULL)
- {
- if (strcmp(line[numlines],"$$$$\n") == 0
- || strcmp(line[numlines],"$$$$\r\n") == 0
- || strcmp(line[numlines],"$$$$\r\r\n") == 0)
- {
- if (numlines < 6) gt_abort(">E too few lines\n");
- return numlines+1;
- }
- ++numlines;
- }
-
- gt_abort(">E EOF encountered\n");
-}
-
-static void
-writemolecule(int numlines)
-{
- int i;
-
- for (i = 0; i < numlines; ++i) fputs(line[i],stdout);
-}
-
-static void
-writeascii(int na, int *which, int *hyd,
- int nb, int *u, int *v, int *mult)
-/* Write molecule in ASCII */
-{
- int i;
-
- for (i = 0; i < na; ++i)
- {
- printf("%d:%s",i,atom[which[i]]);
- if (hyd[i] == 1) printf("H ");
- else if (hyd[i] > 1) printf("H%d ",hyd[i]);
- else printf(" ");
- }
-
- for (i = 0; i < nb; ++i)
- {
- printf(" %d%c%d",u[i],(mult[i]==1?'-':mult[i]==2?'=':'#'),v[i]);
- }
- printf("\n");
-}
-
-static void
-makecanoncode(int na, int *which, int *hyd,
- int nb, int *u, int *v, int *mult, char *code)
-/* Make a canonical hashcode for the molecule */
-{
- int i,j,nhyd;
- int n,nh;
- graph g[MAXN],h[MAXN];
- int lab[MAXN],ptn[MAXN],orbits[MAXN],weight[MAXN];
- static DEFAULTOPTIONS_GRAPH(options);
- statsblk stats;
- set workspace[200];
- unsigned long long code1,code2;
- char *p;
- char *glyph =
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@%";
-
- if (strlen(glyph) != 64) gt_abort(">E glyph error\n");
-
- nhyd = 0;
- for (i = 0; i < na; ++i) nhyd += hyd[i];
-
- if (2*na + nhyd > MAXN) gt_abort(">E molecule too big\n");
-
- n = 2*na + nhyd;
- for (i = 0; i < na; ++i)
- {
- weight[i] = which[i];
- weight[na+i] = which[i] + 128;
- }
- for (i = 2*na; i < n; ++i) weight[i] = 0;
- setlabptn(weight,lab,ptn,n);
-
- EMPTYSET(g,n);
-#define ADDE(ii,jj) { g[ii] |= bit[jj]; g[jj] |= bit[ii]; }
-
- nh = 2*na;
- for (i = 0; i < na; ++i)
- {
- ADDE(i,i+na);
- for (j = 0; j < hyd[i]; ++j)
- {
- ADDE(i,nh);
- ++nh;
- }
- }
-
- if (nh != n) gt_abort(">E huh?\n");
-
- for (i = 0; i < nb; ++i)
- {
- if (mult[i] == 1 || mult[i] == 3)
- ADDE(u[i],v[i]);
- if (mult[i] == 2 || mult[i] == 3)
- ADDE(na+u[i],na+v[i]);
- }
-
- options.getcanon = TRUE;
- options.defaultptn = FALSE;
- nauty(g,lab,ptn,NULL,orbits,&options,&stats,workspace,200,1,n,h);
-
- code1 = 2 + 13*(nhyd+1);
- code2 = n + 17*nb;
- for (i = 0; i < n; ++i)
- {
- code1 = 1237LLU * code1 + FUZZ1(MV(h[i]));
- code2 = 1233457LLU * code2 + FUZZ2(MV(h[i]));
- }
-
- p = code;
- for (i = 0; i < 8; ++i)
- {
- *p++ = glyph[code1%64];
- code1 >>= 8;
- }
- for (i = 0; i < 8; ++i)
- {
- *p++ = glyph[code2%64];
- code2 >>= 8;
- }
-
- *p = '\0';
-}
-
-static void
-decode(int numlines, int *n, int *which, int *hyd,
- int *nb, int *u, int *v, int *mult)
-/* which[0..*n-1] = non-H atoms
- hyd[0..*n-1] = number of hydrogens
- *nb = number of non-H bonds
- u[0..*nb-1]-v[0..*nb-1] = bonds
- mult[0..*nb-1] = multiplicities (1,2,3)
-*/
-{
- int i,j,nv,ne;
- int val[200],needval[200];
- double x,y,z;
- int j1,j2,j3,j4,j5,j6,q;
- char s[5];
-
- /* See SDFformats.txt for which formats are handled */
-
- if (sscanf(line[3],"%d%d",&nv,&ne) != 2)
- gt_abort(">E Can't read count line\n");
-
- *n = 0;
-
- for (i = 0; i < nv; ++i)
- {
- if (sscanf(line[4+i],"%lf%lf%lf%s%d%d%d%d%d%d",&x,&y,&z,
- s,&j1,&j2,&j3,&j4,&j5,&j6) != 10)
- gt_abort(">E Can't read atom line\n");
- which[i] = atomcode(s);
- if (which[i] != HYDROGEN)
- {
- if (j4 > 0) hyd[i] = j4-1;
- else hyd[i] = 0;
- ++*n;
-
- if (j6 > 0) val[i] = j6;
- else val[i] = 0;
-
- needval[i] = (j4 == 0) && (j6 == 0);
- }
- }
-
- *nb = 0;
-
- for (i = 0; i < ne; ++i)
- {
- if (sscanf(line[4+nv+i],"%d%d%d",&j1,&j2,&j3) != 3)
- gt_abort(">E Can't read bond line\n");
- if (j3 < 1 || j3 > 3)
- gt_abort(">E irregular multiplicity, maybe aromatic\n");
- if (which[j1-1] != HYDROGEN && which[j2-1] != HYDROGEN)
- {
- u[*nb] = j1-1;
- v[*nb] = j2-1;
- mult[*nb] = j3;
- ++*nb;
- }
- else if (which[j1-1] == HYDROGEN)
- ++hyd[j2-1];
- else if (which[j2-1] == HYDROGEN)
- ++hyd[j1-1];
- else
- gt_abort(">E the impossible happened\n");
- }
-
- for (i = 0; i < *n; ++i)
- {
- if (needval[i])
- {
- q = 0;
- for (j = 0; j < *nb; ++j)
- if (u[j] == i || v[j] == i) q += mult[j];
- val[i] = roundval(which[i],q+hyd[i]);
- hyd[i] = val[i] - q;
- }
- else if (val[i] > 0)
- {
- q = 0;
- for (j = 0; j < *nb; ++j)
- if (u[j] == i || v[j] == i) q += mult[j];
- hyd[i] = val[i] - q;
- }
- }
-}
-
-static boolean
-isgood(int n, int *which, int *hyd, int nb, int *u, int *v,
- int *mult, boolean aswitch, int dmax, int cmax,
- long mine, long maxe, char *neededhash)
-{
- int deg[MAXN];
- int i,coord;
- char hashcode[20];
-
- if (neededhash)
- {
- makecanoncode(n,which,hyd,nb,u,v,mult,hashcode);
- return (strcmp(neededhash,hashcode) == 0);
- }
-
- if (nb < mine || nb > maxe) return FALSE;
-
- for (i = 0; i < n; ++i) deg[i] = 0;
-
- for (i = 0; i < nb; ++i)
- {
- ++deg[u[i]];
- ++deg[v[i]];
- }
-
- for (i = 0; i < n; ++i)
- {
- coord = deg[i] + hyd[i];
- if (!aswitch)
- {
- if (coord > 4 && hyd[i] > 0) return FALSE;
- if (which[i] == NITROGEN && coord > 4) return FALSE;
- }
- if (deg[i] > dmax) return FALSE;
- if (coord > cmax) return FALSE;
- }
-
- return TRUE;
-}
-
-int
-main(int argc, char *argv[])
-{
- long long nin,nout;
- boolean badargs;
- boolean eswitch,pswitch,aswitch,dswitch,cswitch;
- boolean Hswitch,wswitch,uswitch,Aswitch;
- int numlines,maxd,maxc;
- int i,j,n,nb;
- int u[2*MAXN],v[2*MAXN],mult[2*MAXN],which[MAXN],hyd[MAXN];
- char sw,*arg,*neededhash;
- long startindex,endindex,mine,maxe;
- char hashcode[20];
-
- HELP;
-
- nauty_check(WORDSIZE,1,1,NAUTYVERSIONID);
-
- aswitch = dswitch = cswitch = wswitch = uswitch = Aswitch = FALSE;
- eswitch = pswitch = Hswitch = badargs = FALSE;
-
- for (j = 1; !badargs && j < argc; ++j)
- {
- arg = argv[j];
- if (arg[0] == '-' && arg[1] != '\0')
- {
- ++arg;
- while (*arg != '\0')
- {
- sw = *arg++;
- SWBOOLEAN('u',uswitch)
- else SWBOOLEAN('w',wswitch)
- else SWBOOLEAN('A',Aswitch)
- else SWBOOLEAN('a',aswitch)
- else SWINT('c',cswitch,maxc,"canonsdf -c")
- else SWINT('d',dswitch,maxd,"canonsdf -d")
- else SWRANGE('p',":-",pswitch,
- startindex,endindex,"canonsdf -p")
- else SWRANGE('e',":-",eswitch,mine,maxe,"canonsdf -e")
- else if (sw == 'H')
- {
- Hswitch = TRUE;
- neededhash = arg;
- break;
- }
- else badargs = TRUE;
- }
- }
- else
- badargs = TRUE;
- }
-
- if (badargs)
- {
- fprintf(stderr,">E Usage: %s\n",USAGE);
- GETHELP;
- exit(1);
- }
-
- if (!cswitch) maxc = 4;
- if (!dswitch) maxd = 4;
- if (pswitch && startindex < 0) startindex = 0;
- if (!Hswitch) neededhash = NULL;
-
- if ((uswitch!=0)+(Aswitch!=0)+(wswitch!=0) > 1)
- gt_abort(">E -v, -w and -u are incompatible\n");
-
- if (!eswitch)
- {
- mine = 0;
- maxe = NAUTY_INFINITY;
- }
-
- nin = nout = 0;
- while ((numlines = readmolecule()) != 0)
- {
- ++nin;
- if (!pswitch ||
- (nin >= startindex && nin <= endindex))
- {
- decode(numlines,&n,which,hyd,&nb,u,v,mult);
- if (isgood(n,which,hyd,nb,u,v,mult,aswitch,
- maxd,maxc,mine,maxe,neededhash))
- {
- ++nout;
- if (!uswitch)
- {
- if (wswitch) writemolecule(numlines);
- else if (Aswitch) writeascii(n,which,hyd,nb,u,v,mult);
- else
- {
- makecanoncode(n,which,hyd,nb,u,v,mult,hashcode);
- printf("%s\n",hashcode);
- }
- }
- }
- }
- }
-
- if (uswitch)
- fprintf(stderr,
- ">Z read %lld molecules; %lld removed, %lld remaining\n",
- nin,nin-nout,nout);
- else
- fprintf(stderr,
- ">Z read %lld molecules; %lld removed, %lld written\n",
- nin,nin-nout,nout);
-
- exit(0);
-}
+#define WORDSIZE 64
+#define MAXN WORDSIZE
+#include "gtools.h"
+
+#define USAGE \
+ "canonsdf [-A|-w|-u] [-p#|-p#:#] [-e#|-e#:#] [-d#] [-c#] [-a] [-Hcode]"
+
+#define HELPTEXT \
+" Process molecules in SDF format.\n" \
+"\n" \
+" -a Allow more than 4 neighbours to include H\n" \
+" and N with more than 4 neighbours (default not allowed)\n" \
+" -d# Maximum degree for simple graph without H (default 4)\n" \
+" -c# Maximum coordination number without H (default 4)\n" \
+" -e# -e#:# Number of bonds between non-H atoms\n" \
+"\n" \
+" -p# -p#:# Only process one input or range of inputs (first is 1)\n" \
+" -Hcode Output is restricted to the input with this hash code.\n" \
+" The code continues to the end of the argument. If this option\n" \
+" is given, all other constraints are ignored.\n" \
+"\n" \
+" -w Write SDF to output identical to the input\n" \
+" -A Write an ASCII molecule decription\n" \
+" -R Use aromaticity to form canonical form\n" \
+" -s Form canonical form using only simple graph include hydrogens\n" \
+" -S Form canonical form using only simple graph exclude hydrogens\n" \
+" -u Just count\n" \
+" The default is to write a 16-character hash code\n"
+
+
+static char *atom[]
+ = { "H","C","N","O","P","S","F","Cl","Br","I","B","Si" };
+static int val1[]
+ = { 1, 4, 3, 2, 3, 2, 1, 1, 1, 1, 1, 4 };
+static int val2[]
+ = { 0, 0, 5, 0, 5, 4, 0, 0, 0, 0, 0, 0 };
+static int val3[]
+ = { 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0 };
+#define NUMATOMS (sizeof(atom)/sizeof(atom[0]))
+#define HYDROGEN 0
+#define CARBON 1
+#define NITROGEN 2
+
+static int fuzz1[] = {0x37541,0x61532,0x05257,0x26416};
+static int fuzz2[] = {0x06532,0x70236,0x35523,0x62437};
+
+#define FUZZ1(x) ((x) ^ fuzz1[(x)&3])
+#define FUZZ2(x) ((x) ^ fuzz2[(x)&3])
+#define MV(x) ((x) >> (WORDSIZE-n))
+
+typedef char card[84];
+static card line[200];
+static unsigned long long nin;
+
+static int
+atomcode(char *name)
+{
+ int i;
+
+ for (i = 0; i < NUMATOMS; ++i)
+ if (strcmp(atom[i],name) == 0) break;
+
+ if (i == NUMATOMS)
+ {
+ fprintf(stderr,">E unknown element %s\n",name);
+ exit(1);
+ }
+
+ return i;
+}
+
+static int
+roundval(int index, int val)
+/* Round val up to the next legal value */
+{
+ if (val1[index] >= val) return val1[index];
+ if (val2[index] >= val) return val2[index];
+ if (val3[index] >= val) return val3[index];
+
+ fprintf(stderr,"index=%d val=%d\n",index,val);
+ gt_abort(">E can't determine valence\n");
+}
+
+static int
+readmolecule()
+/* Read one SDF molecule into line[*], return number of lines (0 if none) */
+{
+ int numlines;
+
+ if (fgets(line[0],82,stdin) == NULL) return 0;
+ numlines = 1;
+ while (fgets(line[numlines],82,stdin) != NULL)
+ {
+ if (strcmp(line[numlines],"$$$$\n") == 0
+ || strcmp(line[numlines],"$$$$\r\n") == 0
+ || strcmp(line[numlines],"$$$$\r\r\n") == 0)
+ {
+ if (numlines < 6) gt_abort(">E too few lines\n");
+ return numlines+1;
+ }
+ ++numlines;
+ }
+
+ gt_abort(">E EOF encountered\n");
+}
+
+static void
+writemolecule(int numlines)
+{
+ int i;
+
+ for (i = 0; i < numlines; ++i) fputs(line[i],stdout);
+}
+
+static void
+writeascii(int na, int *which, int *hyd,
+ int nb, int *u, int *v, int *mult)
+/* Write molecule in ASCII */
+{
+ int i;
+
+ for (i = 0; i < na; ++i)
+ {
+ printf("%d:%s",i,atom[which[i]]);
+ if (hyd[i] == 1) printf("H ");
+ else if (hyd[i] > 1) printf("H%d ",hyd[i]);
+ else printf(" ");
+ }
+
+ for (i = 0; i < nb; ++i)
+ {
+ printf(" %d%c%d",u[i],(mult[i]==1?'-':mult[i]==2?'=':'#'),v[i]);
+ }
+ printf("\n");
+}
+
+static void
+makecanoncode(int na, int *which, int *hyd,
+ int nb, int *u, int *v, int *mult, char *code)
+/* Make a canonical hashcode for the molecule */
+{
+ int i,j,nhyd;
+ int n,nh;
+ graph g[MAXN],h[MAXN];
+ int lab[MAXN],ptn[MAXN],orbits[MAXN],weight[MAXN];
+ static DEFAULTOPTIONS_GRAPH(options);
+ statsblk stats;
+ set workspace[200];
+ unsigned long long code1,code2;
+ char *p;
+ char *glyph =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@%";
+
+ if (strlen(glyph) != 64) gt_abort(">E glyph error\n");
+
+ nhyd = 0;
+ for (i = 0; i < na; ++i) nhyd += hyd[i];
+
+ if (2*na + nhyd > MAXN) gt_abort(">E molecule too big\n");
+
+ n = 2*na + nhyd;
+ for (i = 0; i < na; ++i)
+ {
+ weight[i] = which[i];
+ weight[na+i] = which[i] + 128;
+ }
+ for (i = 2*na; i < n; ++i) weight[i] = 0;
+ setlabptn(weight,lab,ptn,n);
+
+ EMPTYSET(g,n);
+#define ADDE(ii,jj) { g[ii] |= bit[jj]; g[jj] |= bit[ii]; }
+
+ nh = 2*na;
+ for (i = 0; i < na; ++i)
+ {
+ ADDE(i,i+na);
+ for (j = 0; j < hyd[i]; ++j)
+ {
+ ADDE(i,nh);
+ ++nh;
+ }
+ }
+
+ if (nh != n) gt_abort(">E huh?\n");
+
+ for (i = 0; i < nb; ++i)
+ {
+ if (mult[i] == 1 || mult[i] == 3)
+ ADDE(u[i],v[i]);
+ if (mult[i] == 2 || mult[i] == 3)
+ ADDE(na+u[i],na+v[i]);
+ }
+
+ options.getcanon = TRUE;
+ options.defaultptn = FALSE;
+ nauty(g,lab,ptn,NULL,orbits,&options,&stats,workspace,200,1,n,h);
+
+ code1 = 2 + 13*(nhyd+1);
+ code2 = n + 17*nb;
+ for (i = 0; i < n; ++i)
+ {
+ code1 = 1237LLU * code1 + FUZZ1(MV(h[i]));
+ code2 = 1233457LLU * code2 + FUZZ2(MV(h[i]));
+ }
+
+ p = code;
+ for (i = 0; i < 8; ++i)
+ {
+ *p++ = glyph[code1%64];
+ code1 >>= 8;
+ }
+ for (i = 0; i < 8; ++i)
+ {
+ *p++ = glyph[code2%64];
+ code2 >>= 8;
+ }
+
+ *p = '\0';
+}
+
+static void
+makesimplecode(int na, int *which, int *hyd, boolean Sswitch,
+ int nb, int *u, int *v, char *code)
+/* Make a canonical hashcode for the molecule ignoring bond multiplicity. */
+{
+ int i,j,nhyd;
+ int n,nh;
+ graph g[MAXN],h[MAXN];
+ int lab[MAXN],ptn[MAXN],orbits[MAXN],weight[MAXN];
+ static DEFAULTOPTIONS_GRAPH(options);
+ statsblk stats;
+ set workspace[200];
+ unsigned long long code1,code2;
+ char *p;
+ char *glyph =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@%";
+
+ if (strlen(glyph) != 64) gt_abort(">E glyph error\n");
+
+ nhyd = 0;
+
+ if (!Sswitch)
+ {
+ for (i = 0; i < na; ++i) nhyd += hyd[i];
+ if (na + nhyd > MAXN) gt_abort(">E molecule too big\n");
+ }
+
+ n = na + nhyd;
+ for (i = 0; i < na; ++i) weight[i] = which[i];
+ for (i = na; i < n; ++i) weight[i] = 0;
+ setlabptn(weight,lab,ptn,n);
+
+ EMPTYSET(g,n);
+#define ADDE(ii,jj) { g[ii] |= bit[jj]; g[jj] |= bit[ii]; }
+
+ nh = na;
+ if (!Sswitch)
+ {
+ for (i = 0; i < na; ++i)
+ {
+ for (j = 0; j < hyd[i]; ++j)
+ {
+ ADDE(i,nh);
+ ++nh;
+ }
+ }
+ }
+
+ if (nh != n) gt_abort(">E huh?\n");
+
+ for (i = 0; i < nb; ++i) ADDE(u[i],v[i]);
+
+//writeg6(stdout,g,1,n);
+
+ options.getcanon = TRUE;
+ options.defaultptn = FALSE;
+ nauty(g,lab,ptn,NULL,orbits,&options,&stats,workspace,200,1,n,h);
+
+ code1 = 2 + 13*(nhyd+1);
+ code2 = n + 17*nb;
+ for (i = 0; i < n; ++i)
+ {
+ code1 = 1237LLU * code1 + FUZZ1(MV(h[i]));
+ code2 = 1233457LLU * code2 + FUZZ2(MV(h[i]));
+ }
+
+ p = code;
+ for (i = 0; i < 8; ++i)
+ {
+ *p++ = glyph[code1%64];
+ code1 >>= 8;
+ }
+ for (i = 0; i < 8; ++i)
+ {
+ *p++ = glyph[code2%64];
+ code2 >>= 8;
+ }
+
+ *p = '\0';
+}
+
+#define MAXAROMCODES 200
+static struct { int mult[2*MAXN]; char code[20]; } aromqueue[MAXAROMCODES];
+static int Cpos,aromhead,aromtail;
+static struct twostep { int v1,v3; int e1,e2; setword v23; } twosteps[MAXN][4];
+ /* Paths of three carbon atoms of the form C-C=C. */
+static int numtwosteps[MAXN];
+
+static void
+maketwosteps(int na, int nb, int *which, int *u, int *v, int *mult)
+/* Find all 2-paths C-C=C. Could remove tree-like bits. */
+{
+ int i,j,xi,yi;
+ int x[4],ex[4],y[4],ey[4],nx,ny;
+
+ for (i = 0; i < na; ++i) numtwosteps[i] = 0;
+
+ for (i = 0; i < na; ++i) /* i is the central atom */
+ {
+ nx = ny = 0;
+ if (which[i] == Cpos)
+ {
+ for (j = 0; j < nb; ++j)
+ {
+ if (u[j] == i && which[v[j]] == Cpos)
+ {
+ if (mult[j] == 1) { ex[nx] = j; x[nx++] = v[j]; }
+ if (mult[j] == 2) { ey[ny] = j; y[ny++] = v[j]; }
+ }
+ else if (v[j] == i && which[u[j]] == Cpos)
+ {
+ if (mult[j] == 1) { ex[nx] = j; x[nx++] = u[j]; }
+ if (mult[j] == 2) { ey[ny] = j; y[ny++] = u[j]; }
+ }
+ }
+ for (xi = 0; xi < nx; ++xi)
+ for (yi = 0; yi < ny; ++yi)
+ {
+ twosteps[x[xi]][numtwosteps[x[xi]]].e1 = ex[xi];
+ twosteps[x[xi]][numtwosteps[x[xi]]].e2 = ey[yi];
+ twosteps[x[xi]][numtwosteps[x[xi]]].v1 = x[xi];
+ twosteps[x[xi]][numtwosteps[x[xi]]].v3 = y[yi];
+ twosteps[x[xi]][numtwosteps[x[xi]]].v23 = bit[i] | bit[y[yi]];
+ ++numtwosteps[x[xi]];
+ }
+ }
+ }
+}
+
+static void
+gotaromcycle(int level, struct twostep *path[],
+ int na, int *which, int *hyd, int nb, int *u, int *v, int *mult)
+{
+ int newmult[2*MAXN];
+ char code[20];
+ int i,j;
+
+ for (i = 0; i < nb; ++i) newmult[i] = mult[i];
+ for (i = 0; i < level; ++i)
+ {
+ newmult[path[i]->e1] = 2;
+ newmult[path[i]->e2] = 1;
+ }
+ makecanoncode(na,which,hyd,nb,u,v,newmult,code);
+
+ for (j = 0; j < aromtail; ++j)
+ if (strcmp(code,aromqueue[j].code) == 0) break;
+ if (j == aromtail)
+ {
+ if (aromtail == MAXAROMCODES)
+ gt_abort(">E increase MAXAROMCODES\n");
+ strcpy(aromqueue[aromtail].code,code);
+ for (i = 0; i < nb; ++i)
+ aromqueue[aromtail].mult[i] = newmult[i];
+ ++aromtail;
+ }
+}
+
+static void
+scancycles(int level, int first, struct twostep *path[], setword avail,
+ int na, int *which, int *hyd, int nb, int *u, int *v, int *mult)
+{
+ int last,i;
+
+ last = (level == 0 ? first : path[level-1]->v3);
+
+ for (i = 0; i < numtwosteps[last]; ++i)
+ {
+ if (!(twosteps[last][i].v23 & ~avail))
+ {
+ path[level] = &twosteps[last][i];
+ scancycles(level+1,first,path,
+ avail&~(twosteps[last][i].v23|bit[last]),
+ na,which,hyd,nb,u,v,mult);
+ }
+ else if (twosteps[last][i].v3 == first && level%2 == 0
+ && (twosteps[last][i].v23 & ~avail) == bit[first])
+ {
+ path[level] = &twosteps[last][i];
+ gotaromcycle(level+1,path,na,which,hyd,nb,u,v,mult);
+ }
+ }
+}
+
+static void
+makearomcode(int na, int *which, int *hyd,
+ int nb, int *u, int *v, int *mult, char *code)
+/* Make a canonical hashcode for the aromatic type of the molecule */
+{
+ int i;
+ struct twostep *path[2*MAXN]; /* Sequence of twosteps */
+
+ Cpos = atomcode("C");
+ for (i = 0; i < nb; ++i) aromqueue[0].mult[i] = mult[i];
+ makecanoncode(na,which,hyd,nb,u,v,mult,aromqueue[0].code);
+ aromhead = 0;
+ aromtail = 1;
+
+ while (aromhead < aromtail)
+ {
+ maketwosteps(na,nb,which,u,v,aromqueue[aromhead].mult);
+
+ for (i = 0; i <= na-6; ++i)
+ if (which[i] == Cpos)
+ scancycles(0,i,path,ALLBITS>>(i+1),na,which,hyd,nb,
+ u,v,aromqueue[aromhead].mult);
+ ++aromhead;
+ }
+
+ strcpy(code,aromqueue[0].code);
+ for (i = 1; i < aromtail; ++i)
+ if (strcmp(code,aromqueue[i].code) > 0)
+ strcpy(code,aromqueue[i].code);
+}
+
+static void
+decode(int numlines, int *n, int *which, int *hyd,
+ int *nb, int *u, int *v, int *mult)
+/* which[0..*n-1] = non-H atoms
+ hyd[0..*n-1] = number of hydrogens attached to each
+ *nb = number of non-H bonds
+ u[0..*nb-1]-v[0..*nb-1] = bonds
+ mult[0..*nb-1] = multiplicities (1,2,3)
+*/
+{
+ int i,j,nv,ne;
+ int val[200],needval[200];
+ double x,y,z;
+ int j1,j2,j3,j4,j5,j6;
+ char s[5];
+
+ /* See SDFformats.txt for which formats are handled */
+
+ if (sscanf(line[3],"%d%d",&nv,&ne) != 2)
+ gt_abort(">E Can't read count line\n");
+
+ *n = 0;
+
+ for (i = 0; i < nv; ++i)
+ {
+ if (sscanf(line[4+i],"%lf%lf%lf%s%d%d%d%d%d%d",&x,&y,&z,
+ s,&j1,&j2,&j3,&j4,&j5,&j6) != 10)
+ gt_abort(">E Can't read atom line\n");
+ which[i] = atomcode(s);
+ if (which[i] != HYDROGEN)
+ {
+ if (j4 > 0) hyd[i] = j4-1;
+ else hyd[i] = 0;
+ ++*n;
+
+ if (j6 > 0) val[i] = j6;
+ else val[i] = 0;
+
+ needval[i] = (j4 == 0) && (j6 == 0);
+ }
+ }
+
+ *nb = 0;
+
+ for (i = 0; i < ne; ++i)
+ {
+ if (sscanf(line[4+nv+i],"%d%d%d",&j1,&j2,&j3) != 3)
+ gt_abort(">E Can't read bond line\n");
+ if (j3 < 1 || j3 > 4)
+ gt_abort_2(">E %llu: irregular multiplicity\n%s\n",nin,line[4+nv+i]);
+ if (which[j1-1] != HYDROGEN && which[j2-1] != HYDROGEN)
+ {
+ u[*nb] = j1-1;
+ v[*nb] = j2-1;
+ mult[*nb] = j3;
+ ++*nb;
+ }
+ else if (which[j1-1] == HYDROGEN)
+ ++hyd[j2-1];
+ else if (which[j2-1] == HYDROGEN)
+ ++hyd[j1-1];
+ else
+ gt_abort(">E the impossible happened\n");
+ }
+
+#if 0
+{ int ii;
+printf("----\nn=%d nb=%d nv=%d ne=%d\n",*n,*nb,nv,ne);
+printf("atom="); for(ii=0;ii<*n;++ii)printf(" %d",which[ii]);printf("\n");
+printf(" hyd="); for(ii=0;ii<*n;++ii)printf(" %d",hyd[ii]);printf("\n");
+printf("need="); for(ii=0;ii<*n;++ii)printf(" %d",needval[ii]);printf("\n");
+printf(" val="); for(ii=0;ii<*n;++ii)printf(" %d",val[ii]);printf("\n");
+printf("edges="); for(ii=0;ii<*nb;++ii)printf(" %d%c%d",
+ u[ii],(mult[ii]==1?'-':mult[ii]==2?'=':mult[ii]==3?'#':'*'),v[ii]);
+ printf("\n");
+}
+#endif
+
+ for (i = 0; i < *n; ++i)
+ {
+ boolean saw4;
+ int q;
+ /* If aromatic edges are incident, we take one of them
+ to be double and the rest of them to be single. */
+
+ q = 0;
+ saw4 = FALSE;
+ for (j = 0; j < *nb; ++j)
+ if (u[j] == i || v[j] == i)
+ {
+ if (mult[j] == 4)
+ {
+ if (!saw4)
+ {
+ q += 2;
+ saw4 = TRUE;
+ }
+ else
+ ++q;
+ }
+ else
+ q += mult[j];
+ }
+
+ if (needval[i])
+ {
+ val[i] = roundval(which[i],q+hyd[i]);
+ hyd[i] = val[i] - q;
+ }
+ else if (val[i] > 0)
+ hyd[i] = val[i] - q;
+ else
+ val[i] = roundval(which[i],q+hyd[i]);
+ }
+
+#if 0
+{ int ii;
+printf("----\nn=%d nb=%d nv=%d ne=%d\n",*n,*nb,nv,ne);
+printf("atom="); for(ii=0;ii<*n;++ii)printf(" %d",which[ii]);printf("\n");
+printf(" hyd="); for(ii=0;ii<*n;++ii)printf(" %d",hyd[ii]);printf("\n");
+printf("need="); for(ii=0;ii<*n;++ii)printf(" %d",needval[ii]);printf("\n");
+printf(" val="); for(ii=0;ii<*n;++ii)printf(" %d",val[ii]);printf("\n");
+printf("edges="); for(ii=0;ii<*nb;++ii)printf(" %d%c%d",
+ u[ii],(mult[ii]==1?'-':mult[ii]==2?'=':mult[ii]==3?'#':'*'),v[ii]);
+ printf("\n");
+}
+#endif
+
+ /* Try to find a plausible Kekule structure for aromatic edges.
+ * This is a real kludge and frequently doesn't work. */
+
+ { int k,laste,queue[2*MAXN],head,tail,ul,vl,ui,vi,du,dv;
+
+ for (k = 0; k < *nb; ++k)
+ {
+ for (i = 0; i < *nb; ++i) if (mult[i] == 4) break;
+ if (i == *nb) break;
+
+ queue[0] = i;
+ mult[i] = 1;
+ head = 0;
+ tail = 1;
+ while (head < tail)
+ {
+ laste = queue[head++];
+ ul = u[laste]; vl = v[laste];
+ for (i = 0; i < *nb; ++i)
+ {
+ ui = u[i]; vi = v[i];
+ if (mult[i] == 4 && (ui == ul || ui == vl ||
+ vi == ul || vi == vl))
+ {
+ du = val[ui] - hyd[ui];
+ dv = val[vi] - hyd[vi];
+ for (j = 0; j < *nb; ++j)
+ {
+ if (j != i && (u[j] == ui || v[j] == ui))
+ du -= (mult[j] == 4 ? 1 : mult[j]);
+ if (j != i && (u[j] == vi || v[j] == vi))
+ dv -= (mult[j] == 4 ? 1 : mult[j]);
+ }
+ if (du <= 1 || dv <= 1) mult[i] = 1;
+ else mult[i] = 3 - mult[laste];
+ queue[tail++] = i;
+ }
+ }
+ }
+ }
+ }
+
+#if 0
+{ int ii;
+printf("----\nn=%d nb=%d nv=%d ne=%d\n",*n,*nb,nv,ne);
+printf("atom="); for(ii=0;ii<*n;++ii)printf(" %d",which[ii]);printf("\n");
+printf(" hyd="); for(ii=0;ii<*n;++ii)printf(" %d",hyd[ii]);printf("\n");
+printf("need="); for(ii=0;ii<*n;++ii)printf(" %d",needval[ii]);printf("\n");
+printf(" val="); for(ii=0;ii<*n;++ii)printf(" %d",val[ii]);printf("\n");
+printf("edges="); for(ii=0;ii<*nb;++ii)printf(" %d%c%d",
+ u[ii],(mult[ii]==1?'-':mult[ii]==2?'=':mult[ii]==3?'#':'*'),v[ii]);
+ printf("\n");
+}
+#endif
+
+}
+
+static boolean
+isgood(int n, int *which, int *hyd, int nb, int *u, int *v, int *mult,
+ boolean aswitch, boolean Rswitch, boolean sswitch, boolean Sswitch,
+ int dmax, int cmax, long mine, long maxe, char *neededhash)
+{
+ int deg[MAXN];
+ int i,coord;
+ char hashcode[20];
+
+ if (neededhash)
+ {
+ if (Rswitch)
+ makearomcode(n,which,hyd,nb,u,v,mult,hashcode);
+ else if (sswitch)
+ makesimplecode(n,which,hyd,Sswitch,nb,u,v,hashcode);
+ else
+ makecanoncode(n,which,hyd,nb,u,v,mult,hashcode);
+ return (strcmp(neededhash,hashcode) == 0);
+ }
+
+ if (nb < mine || nb > maxe) return FALSE;
+
+ for (i = 0; i < n; ++i) deg[i] = 0;
+
+ for (i = 0; i < nb; ++i)
+ {
+ ++deg[u[i]];
+ ++deg[v[i]];
+ }
+
+ for (i = 0; i < n; ++i)
+ {
+ coord = deg[i] + hyd[i];
+ if (!aswitch)
+ {
+ if (coord > 4 && hyd[i] > 0) return FALSE;
+ if (which[i] == NITROGEN && coord > 4) return FALSE;
+ }
+ if (deg[i] > dmax) return FALSE;
+ if (coord > cmax) return FALSE;
+ }
+
+ return TRUE;
+}
+
+int
+main(int argc, char *argv[])
+{
+ unsigned long long nout;
+ boolean badargs;
+ boolean eswitch,pswitch,aswitch,dswitch,cswitch,Sswitch;
+ boolean Hswitch,wswitch,uswitch,Aswitch,Rswitch,sswitch;
+ int numlines,maxd,maxc;
+ int j,n,nb;
+ int u[2*MAXN],v[2*MAXN],mult[2*MAXN],which[MAXN],hyd[MAXN];
+ char sw,*arg,*neededhash;
+ long startindex,endindex,mine,maxe;
+ char hashcode[20];
+
+ HELP;
+
+ nauty_check(WORDSIZE,1,1,NAUTYVERSIONID);
+
+ aswitch = dswitch = cswitch = wswitch = uswitch = Aswitch = FALSE;
+ Rswitch = eswitch = pswitch = Hswitch = sswitch = badargs = FALSE;
+ Sswitch = FALSE;
+
+ for (j = 1; !badargs && j < argc; ++j)
+ {
+ arg = argv[j];
+ if (arg[0] == '-' && arg[1] != '\0')
+ {
+ ++arg;
+ while (*arg != '\0')
+ {
+ sw = *arg++;
+ SWBOOLEAN('u',uswitch)
+ else SWBOOLEAN('w',wswitch)
+ else SWBOOLEAN('A',Aswitch)
+ else SWBOOLEAN('a',aswitch)
+ else SWBOOLEAN('R',Rswitch)
+ else SWBOOLEAN('s',sswitch)
+ else SWBOOLEAN('S',Sswitch)
+ else SWINT('c',cswitch,maxc,"canonsdf -c")
+ else SWINT('d',dswitch,maxd,"canonsdf -d")
+ else SWRANGE('p',":-",pswitch,
+ startindex,endindex,"canonsdf -p")
+ else SWRANGE('e',":-",eswitch,mine,maxe,"canonsdf -e")
+ else if (sw == 'H')
+ {
+ Hswitch = TRUE;
+ neededhash = arg;
+ break;
+ }
+ else badargs = TRUE;
+ }
+ }
+ else
+ badargs = TRUE;
+ }
+
+ if (badargs)
+ {
+ fprintf(stderr,">E Usage: %s\n",USAGE);
+ GETHELP;
+ exit(1);
+ }
+
+ if (!cswitch) maxc = 4;
+ if (!dswitch) maxd = 4;
+ if (pswitch && startindex < 0) startindex = 0;
+ if (!Hswitch) neededhash = NULL;
+
+ if ((uswitch!=0)+(Aswitch!=0)+(wswitch!=0) > 1)
+ gt_abort(">E -v, -w and -u are incompatible\n");
+ if ((Rswitch!=0)+(sswitch!=0)+(Sswitch!=0) > 1)
+ gt_abort(">E -s, -S and -R are incompatible\n");
+ if (Sswitch) sswitch = TRUE;
+
+ if (!eswitch)
+ {
+ mine = 0;
+ maxe = NAUTY_INFINITY;
+ }
+
+ nin = nout = 0;
+ while ((numlines = readmolecule()) != 0)
+ {
+ ++nin;
+ if (!pswitch ||
+ (nin >= startindex && nin <= endindex))
+ {
+ decode(numlines,&n,which,hyd,&nb,u,v,mult);
+ if (isgood(n,which,hyd,nb,u,v,mult,aswitch,Rswitch,sswitch,
+ Sswitch,maxd,maxc,mine,maxe,neededhash))
+ {
+ ++nout;
+ if (!uswitch)
+ {
+ if (wswitch) writemolecule(numlines);
+ else if (Aswitch) writeascii(n,which,hyd,nb,u,v,mult);
+ else if (Rswitch)
+ {
+ makearomcode(n,which,hyd,nb,u,v,mult,hashcode);
+ printf("%s\n",hashcode);
+ }
+ else if (sswitch)
+ {
+ makesimplecode(n,which,hyd,Sswitch,nb,u,v,hashcode);
+ printf("%s\n",hashcode);
+ }
+ else
+ {
+ makecanoncode(n,which,hyd,nb,u,v,mult,hashcode);
+ printf("%s\n",hashcode);
+ }
+ }
+ }
+ }
+ }
+
+ if (uswitch)
+ fprintf(stderr,
+ ">Z read %lld molecules; %llu removed, %llu remaining\n",
+ nin,nin-nout,nout);
+ else
+ fprintf(stderr,
+ ">Z read %lld molecules; %llu removed, %llu written\n",
+ nin,nin-nout,nout);
+
+ exit(0);
+}
diff --git a/src/geng.c b/src/geng.c
index c66c825..e83f5fa 100644
--- a/src/geng.c
+++ b/src/geng.c
@@ -1,2507 +1,2507 @@
-/* TODO:
- * add chordal graphs
- * add perfect graphs
- * add complements for ordinary graphs
- * add 5-cycle rejection
- * improve output by compiling g6 from level n-1 */
-
-/* geng.c version 3.3; B D McKay, June 2021. */
-
-#define USAGE \
-"geng [-cCmtfbd#D#] [-uygsnh] [-lvq] \n\
- [-x#X#] n [mine[:maxe]] [res/mod] [file]"
-
-#define HELPTEXT \
-" Generate all graphs of a specified class.\n\
-\n\
- n : the number of vertices\n\
- mine:maxe : a range for the number of edges\n\
- #:0 means '# or more' except in the case 0:0\n\
- res/mod : only generate subset res out of subsets 0..mod-1\n\
-\n\
- -c : only write connected graphs\n\
- -C : only write biconnected graphs\n\
- -t : only generate triangle-free graphs\n\
- -f : only generate 4-cycle-free graphs\n\
- -b : only generate bipartite graphs\n\
- (-t, -f and -b can be used in any combination)\n\
- -m : save memory at the expense of time (only makes a\n\
- difference in the absence of -b, -t, -f and n <= 28).\n\
- -d# : a lower bound for the minimum degree\n\
- -D# : an upper bound for the maximum degree\n\
- -v : display counts by number of edges\n\
- -l : canonically label output graphs\n\
-\n\
- -u : do not output any graphs, just generate and count them\n\
- -g : use graph6 output (default)\n\
- -s : use sparse6 output\n\
- -h : for graph6 or sparse6 format, write a header too\n\
-\n\
- -q : suppress auxiliary output (except from -v)\n\
-\n\
- See program text for much more information.\n"
-
-
-/* Parameters:
-
- n = the number of vertices (1..MAXN)
- Note that MAXN is limited to WORDSIZE
- mine = the minimum number of edges (no bounds if missing)
- maxe = the maximum number of edges (same as mine if missing)
- 0 means "infinity" except in the case "0-0"
- mod, res = a way to restrict the output to a subset.
- All the graphs in G(n,mine..maxe) are divided into
- disjoint classes C(0,mod),C(1,mod),...,C(mod-1,mod),
- of very approximately equal size.
- Only the class C(res,mod) is written.
-
- If the -x or -X switch is used, they must have the
- same value for different values of res; otherwise
- the partitioning may not be valid. In this case
- (-x,-X with constant value), the usual relationships
- between modulo classes are obeyed; for example
- C(3,4) = C(3,8) union C(7,8). This is not true
- if 3/8 and 7/8 are done with -x or -X values
- different from those used for 3/4.
-
- file = a name for the output file (stdout if missing or "-")
-
- All switches can be concatenated or separate. However, the
- value of -d must be attached to the "d", and similarly for "x".
-
- -c : only write connected graphs
- -C : only write biconnected graphs
- -t : only generate triangle-free graphs
- -f : only generate 4-cycle-free graphs
- -b : only generate bipartite graphs
- (-t, -f and -b can be used in any combination)
- -m : save memory at expense of time (only makes a
- difference in the absence of -b, -t, -f and n <= 30).
- -D : specify an upper bound for the maximum degree.
- The value of the upper bound must be adjacent to
- the "D". Example: -D6
- -d : specify a lower bound for the minimum degree.
- The value of the upper bound must be adjacent to
- the "d". Example: -d6
- -v : display counts by number of edges
- -l : canonically label output graphs
-
- -u : do not output any graphs, just generate and count them
- -g : use graph6 output (default)
- -s : use sparse6 output
- -n : use nauty format instead of graph6 format for output
- -y : use the obsolete y-format for output
- -h : for graph6 or sparse6 format, write a header too
-
- -q : suppress auxiliary output (except from -v)
-
- -x : specify a parameter that determines how evenly
- the res/mod facility splits the graphs into subsets.
- High values mean more even splitting at slight cost
- to the total time. The default is 20*mod, and the
- the legal minimum is 3*mod. More information is given
- under "res/mod" above.
- -X : move the initial splitting level higher by ,
- in order to force more even splitting at the cost
- of speed. Default is -X0. More information is given
- under "res/mod" above.
-
-Output formats.
-
- The output format is determined by the mutually exclusive switches
- -u, -n, -y, -g and -s. The default is -g.
-
- -u suppresses output of graphs completely.
-
- -s and -g specify sparse6 and graph6 format, defined elsewhere.
- In this case a header is also written if -h is present.
-
- If -y is present, graphs will be written in y-format.
- y-format is obsolete and only provided for backwards compatibility.
-
- Each graph occupies one line with a terminating newline.
- Except for the newline, each byte has the format 01xxxxxx, where
- each "x" represents one bit of data.
- First byte: xxxxxx is the number of vertices n
- Other ceiling(n(n-1)/12) bytes: These contain the upper triangle of
- the adjacency matrix in column major order. That is, the entries
- appear in the order (0,1),(0,2),(1,2),(0,3),(1,3),(2,3),(0,4),... .
- The bits are used in left to right order within each byte.
- Any unused bits on the end are set to zero.
-
- If -n is present, any output graphs are written in nauty format.
-
- For a graph of n vertices, the output consists of one int giving
- the number of vertices, and n setwords containing the adjacency
- matrix. Note that this is system dependent (i.e. don't use it).
- It will not work properly if the output is to stdout and your
- system distinguishes binary and text files.
-
-OUTPROC feature.
-
- By defining the C preprocessor variable OUTPROC at compile time
- (for Unix the syntax is -DOUTPROC=procname on the cc command),
- geng can be made to call a procedure of your manufacture with
- each output graph instead of writing anything. Your procedure
- needs to have type void and the argument list (FILE *f, graph
- *g, int n). f is a stream open for writing, g is the graph in
- nauty format, and n is the number of vertices. Your procedure
- can be in a separate file so long as it is linked with geng. The
- global variables sparse6, graph6, quiet, nooutput, nautyformat,
- yformat and canonise (all type boolean) can be used to test
- for the presence of the flags -s, -g, -q, -u, -n, -y and -l,
- respectively. If -l is present, the group size and similar
- details can be found in the global variable nauty_stats.
-
-PRUNE feature.
-
- By defining the C preprocessor variable PRUNE at compile time, geng
- can be made to call
- int PRUNE(graph *g,int n,int maxn)
- for each intermediate (and final) graph, and reject it if
- the value returned is nonzero. The arguments are:
-
- g = the graph in nauty format (m=1)
- n = the number of vertices in g
- maxn = the number of vertices for output
- (the value you gave on the command line to geng)
-
- geng constructs the graph starting with vertex 0, then adding
- vertices 1,2,3,... in that order. Each graph in the sequence is
- an induced subgraph of all later graphs in the sequence.
-
- A call is made for all orders from 1 to maxn. In testing for
- a uniform property (such as a forbidden subgraph or forbidden
- induced subgraph) it might save time to notice that a call to
- PRUNE for n implies that the call for n-1 already passed.
-
- For very fast tests, it might be worthwhile using PREPRUNE as
- well or instead. It has the same meaning but is applied earlier
- and more often.
-
- If -c or -C is given, the connectivity test is done before
- PRUNE but not necessarily before PREPRUNE.
-
- Some parameters are available in global variables:
- geng_mindeg, geng_maxdeg, geng_mine, geng_maxe, geng_connec;
-
-SUMMARY
-
- If the C preprocessor variable SUMMARY is defined at compile time, the
- procedure SUMMARY(nauty_counter nout, double cpu) is called just before
- the program exits. The purpose is to allow reporting of statistics
- collected by PRUNE or OUTPROC. The values nout and cpu are the output
- count and cpu time reported on the >Z line.
- Output should be written to stderr.
-
-INSTRUMENT feature.
-
- If the C preprocessor variable INSTRUMENT is defined at compile time,
- extra code is inserted to collect statistics during execution, and
- more information is written to stderr at termination.
-
-CALLING FROM A PROGRAM
-
- It is possible to call geng from another program instead of using it
- as a stand-alone program. The main requirement is to change the name
- of the main program to be other than "main". This is done by defining
- the preprocessor variable GENG_MAIN. You might also like to define
- OUTPROC to be the name of a procedure to receive the graphs. To call
- the program you need to define an argument list argv[] consistent with
- the usual one; don't forget that argv[0] is the command name and not
- the first argument. The value of argc is the number of strings in
- argv[]; that is, one more than the number of arguments. See the
- sample program callgeng.c.
-
-**************************************************************************
-
-Counts and sample performance statistics.
-
- Here we give some graph counts and approximate execution times
- on a Linux computer with Intel Core i7-4790 nominally 3.6GHz,
- compiled with gcc 6.2.0.
- Times are with the -u option (generate but don't write); add
- 0.2-0.3 microseconds per graph for output to a file.
-
-
- General Graphs C3-free Graphs (-t)
-
- 1 1 1 1
- 2 2 2 2
- 3 4 3 3
- 4 11 4 7
- 5 34 5 14
- 6 156 6 38
- 7 1044 7 107
- 8 12346 8 410
- 9 274668 0.08 sec 9 1897
- 10 12005168 2.7 sec 10 12172
- 11 1018997864 207 sec 11 105071 0.09 sec
- 12 165091172592 9 hr 12 1262180 0.8 sec
- 13 50502031367952 108 days 13 20797002 11 sec
- These can be done in about half 14 467871369 220 sec
- the time by setting the edge limit 15 14232552452 1.7 hr
- half way then adding complements. 16 581460254001 65 hr
- 17 31720840164950 145 days
-
-
- C4-free Graphs (-f) (C3,C4)-free Graphs (-tf)
-
- 1 1 1 1
- 2 2 2 2
- 3 4 3 3
- 4 8 4 6
- 5 18 5 11
- 6 44 6 23
- 7 117 7 48
- 8 351 8 114
- 9 1230 9 293
- 10 5069 10 869
- 11 25181 0.04 sec 11 2963
- 12 152045 0.17 sec 12 12066 0.03 sec
- 13 1116403 1.0 sec 13 58933 0.10 sec
- 14 9899865 7.5 sec 14 347498 0.5 sec
- 15 104980369 71 sec 15 2455693 2.7 sec
- 16 1318017549 14 min 16 20592932 19 sec
- 17 19427531763 3.4 hr 17 202724920 170 sec
- 18 333964672216 56 hr 18 2322206466 32 min
- 19 6660282066936 45 days 19 30743624324 7 hr
- 20 468026657815 4 days
- 21 8161170076257 106 days
-
- Old value was wrong: 18 2142368552 (The program was
- ok, but somehow we tabulated the answer incorrectly.)
-
-
- Bipartite Graphs (-b) C4-free Bipartite Graphs (-bf)
-
- 1 1 1 1
- 2 2 2 2
- 3 3 3 3
- 4 7 4 6
- 5 13 5 10
- 6 35 6 21
- 7 88 7 39
- 8 303 8 86
- 9 1119 9 182
- 10 5479 10 440
- 11 32303 0.03 sec 11 1074
- 12 251135 2.3 sec 12 2941
- 13 2527712 1.7 sec 13 8424 0.04 sec
- 14 33985853 19 sec 14 26720 0.11 sec
- 15 611846940 4.9 min 15 90883 0.33 sec
- 16 14864650924 1.8 hr 16 340253 1.1 sec
- 17 488222721992 2.4 days 17 1384567 3.7 sec
- 18 21712049275198 105 days 18 6186907 14 sec
- 19 30219769 59 sec
- 20 161763233 280 sec
- 21 946742190 24 min
- 22 6054606722 2.5 hr
- 23 42229136988 17 hr
- 24 320741332093 125 hr
- 25 2648348712904 58 days
-
-If you know any more of these counts, please tell me.
-
-**************************************************************************
-
-Hints:
-
-To make all the graphs of order n, without restriction on type,
-it is fastest to make them up to binomial(n,2)/2 edges and append
-the complement of those with strictly less than binomial(n,2)/2 edges.
-
-If it is necessary to split the computation into pieces, it is more
-efficient to use the res/mod feature than to split by numbers of edges.
-
-**************************************************************************
-
- Author: B. D. McKay, Sep 1991 and many later dates.
- Copyright B. McKay (1991-2018). All rights reserved.
- This software is subject to the conditions and waivers
- detailed in the file nauty.h.
-
- Changes: Nov 18, 1991 : added -d switch
- fixed operation for n=16
- Nov 26, 1991 : added OUTPROC feature
- Nov 29, 1991 : -c implies mine >= n-1
- Jan 8, 1992 : make writeny() not static
- Jan 10, 1992 : added -n switch
- Feb 9, 1992 : fixed case of n=1
- Feb 16, 1992 : changed mine,maxe,maxdeg testing
- Feb 19, 1992 : added -b, -t and -u options
- documented OUTPROC and added external
- declaration for it.
- Feb 20, 1992 : added -v option
- Feb 22, 1992 : added INSTRUMENT compile-time option
- Feb 23, 1992 : added xbnds() for more effective pruning
- Feb 24, 1992 : added -l option
- Feb 25, 1992 : changed writenauty() to use fwrite()
- Mar 11, 1992 : completely revised many parts, incl
- new refinement procedure for fast rejection,
- distance invariant for regular graphs
- May 19, 1992 : modified userautomproc slightly. xorb[]
- is no longer idempotent but it doesn't matter.
- Speed-up of 2-5% achieved.
- June 5, 1993 : removed ";" after "CPUDEFS" to avoid illegal
- empty declaration.
- Nov 24, 1994 : tested for 0 <= res < mod
-
- Apr 13, 1996 : Major overhaul. Renamed "geng".
- Changed argument syntax.
- Removed 16-vertex limit.
- Added -s, -m, -x. Allowed combinations.
- Replaced code for non-general graphs.
- Very many small changes.
- Jul 12, 1996 : Changed semantics of -x and res/mod.
- Changed >A line and added fflush()/
- All switches can be concatenated or not.
- Aug 16, 1996 : Added -X switch and PRUNE() feature.
- Fixed case of argument 0-0.
- Sep 22, 1996 : Improved 1-2% by tweaking refinex().
- Jan 21, 1997 : Renamed to geng.
- Changed -s to -f, and added -sghq.
- Sep 7, 1997 : Fixed WORDSIZE=16 problems.
- Sep 22, 1997 : Use "wb" open for nautyformat.
- Jan 26, 1998 : Added SUMMARY feature.
- Mar 4, 1998 : Added -C.
- Mar 12, 1998 : Moved stats to nauty_stats.
- Jan 1, 2000 : Changed -d to -D and added -d.
- Feb 24, 2000 : Raised limit to 32 vertices.
- Mar 3, 2000 : Made some counts into unsigned long.
- (Includes first arg to SUMMARY.)
- Mar 12, 2000 : Used bigint for counts that may exceed 2^32.
- Now all counts from very long runs are ok.
- Oct 12, 2000 : Changed maxef[32] to 92 after confirmation
- from Yang Yuansheng. The old value of 93 was
- valid but 92 is slightly more efficient.
- Nov 16, 2000 : Used fuction prototypes.
- Jul 31, 2001 : Added PREPRUNE
- May 7, 2004 : Complete all function prototypes
- Nov 24, 2004 : Force -m for very large sizes
- Add -bf automatically if generating trees
- Apr 1, 2007 : Write >A in one fputs() to try to reduce
- mixing of outputs in multi-process pipes.
- Sep 19, 2007 : Force -m for n > 28 regardless of word size.
- Nov 29, 2008 : Slightly improved connectivity testing.
- Mar 3, 2015 : Improve maxdeg tweaking.
- Jan 18, 2016 : Replace bigint by nauty_counter.
- Mar 8, 2018 : Can now compile for MAXN up to WORDSIZE.
- Use setword instead of unsigned for xword.
- Revised splitting level.
- Updated sample execution times.
- Mar 10, 2018 : Fix overflow at impossibly large n, maxdeg.
- Jan 14, 2019 : Define geng_mindeg, geng_maxdeg, geng_mine, geng_maxe.
- Jun 1, 2021 : Define geng_connec.
- Jun 4, 2021 : Improve performance for -c and -C with small edge count.
- Jun 21, 2021 : K1 is not 2-connected.
-
-**************************************************************************/
-
-#define NAUTY_PGM 1 /* 1 = geng, 2 = genbg, 3 = gentourng */
-
-#ifndef MAXN
-#define MAXN WORDSIZE /* not more than WORDSIZE */
-#endif
-
-#if MAXN > WORDSIZE
- #error "Can't have MAXN greater than WORDSIZE"
-#endif
-
-#define ONE_WORD_SETS
-#include "gtools.h" /* which includes nauty.h and stdio.h */
-
-typedef setword xword;
-
-static void (*outproc)(FILE*,graph*,int);
-
-static FILE *outfile; /* file for output graphs */
-static int connec; /* 1 for -c, 2 for -C, 0 for neither */
-static boolean bipartite; /* presence of -b */
-static boolean trianglefree; /* presence of -t */
-static boolean squarefree; /* presence of -f */
-static boolean savemem; /* presence of -m */
-static boolean verbose; /* presence of -v */
-boolean nautyformat; /* presence of -n */
-boolean yformat; /* presence of -y */
-boolean graph6; /* presence of -g */
-boolean sparse6; /* presence of -s */
-boolean nooutput; /* presence of -u */
-boolean canonise; /* presence of -l */
-boolean quiet; /* presence of -q */
-boolean header; /* presence of -h */
-statsblk nauty_stats;
-static int mindeg,maxdeg,maxn,mine,maxe,mod,res;
-#define PRUNEMULT 50 /* bigger -> more even split at greater cost */
-static int min_splitlevel,odometer,splitlevel,multiplicity;
-static graph gcan[MAXN];
-
-#define XBIT(i) ((xword)1 << (i))
-#define XPOPCOUNT(x) POPCOUNT(x)
-#define XNEXTBIT(x) (WORDSIZE-1-FIRSTBITNZ(x)) /* Assumes non-zero */
-
-typedef struct
-{
- int ne,dmax; /* values used for xlb,xub calculation */
- int xlb,xub; /* saved bounds on extension degree */
- xword lo,hi; /* work purposes for orbit calculation */
- xword xstart[MAXN+1]; /* index into xset[] for each cardinality */
- xword *xset; /* array of all x-sets in card order */
- xword *xcard; /* cardinalities of all x-sets */
- xword *xinv; /* map from x-set to index in xset */
- xword *xorb; /* min orbit representative */
- xword *xx; /* (-b, -t, -s, -m) candidate x-sets */
- /* note: can be the same as xcard */
- xword xlim; /* number of x-sets in xx[] */
-} leveldata;
-
-static leveldata data[MAXN]; /* data[n] is data for n -> n+1 */
-static nauty_counter ecount[1+MAXN*(MAXN-1)/2]; /* counts by number of edges */
-static nauty_counter nodes[MAXN]; /* nodes at each level */
-
-#ifdef INSTRUMENT
-static nauty_counter rigidnodes[MAXN],fertilenodes[MAXN];
-static nauty_counter a1calls,a1nauty,a1succs;
-static nauty_counter a2calls,a2nauty,a2uniq,a2succs;
-#endif
-
-/* The numbers below are actual maximum edge counts.
- geng works correctly with any upper bounds.
- To extend known upper bounds upwards:
- (n-1, E) -> (n, E + floor(2*E/(n-2))),
- which is done by the procedure findmaxe().
-*/
-
-static int maxeb[65] = /* max edges for -b */
- {0,0,1,2,4, -1};
-static int maxet[65] = /* max edges for -t */
- {0,0,1,2,4, -1};
-static int maxef[65] = /* max edges for -f */
- {0,0,1,3,4, 6,7,9,11,13,
- 16,18,21,24,27, 30,33,36,39,42,
- 46,50,52,56,59, 63,67,71,76,80,
- 85,90,92,96,102, 106,110,113,117,122, -1};
-static int maxeft[65] = /* max edges for -ft */
- {0,0,1,2,3, 5,6,8,10,12,
- 15,16,18,21,23, 26,28,31,34,38,
- 41,44,47,50,54, 57,61,65,68,72,
- 76,80,85,87,90, 95,99,104,109,114,
- 120,124,129,134,139, 145,150,156,162,168,
- 175,176,178, -1};
-static int maxebf[65] = /* max edges for -bf */
- {0,0,1,2,3, 4,6,7,9,10,
- 12,14,16,18,21, 22,24,26,29,31,
- 34,36,39,42,45, 48,52,53,56,58,
- 61,64,67,70,74, 77,81,84,88,92,
- 96,100,105,106,108, 110,115,118,122,126,
- 130,134,138,142,147, 151,156,160,165,170,
- 175,180,186,187, -1};
-
-#ifdef PLUGIN
-#include PLUGIN
-#endif
-
-#ifdef OUTPROC
-extern void OUTPROC(FILE*,graph*,int);
-#endif
-#ifdef PRUNE
-extern int PRUNE(graph*,int,int);
-#endif
-#ifdef PREPRUNE
-extern int PREPRUNE(graph*,int,int);
-#endif
-#ifdef SUMMARY
-extern void SUMMARY(nauty_counter,double);
-#endif
-
-#if defined(PRUNE) || defined(PREPRUNE)
-int geng_mindeg, geng_maxdeg, geng_mine, geng_maxe, geng_connec;
-#endif
-
-/************************************************************************/
-
-#define EXTEND(table,n) ((n) <= 1 ? 0 : (n) == 2 ? 1 : \
- table[(n)-1] + (2*table[(n)-1]/((n)-2)))
-
-static int
-findmaxe(int *table, int n)
-/* Return the n-th entry of a maxe table, extending existing values
- if necessary. */
-{
- int i;
-
- for (i = 0; i <= n && table[i] >= 0; ++i) {}
- for ( ; i <= n; ++i) table[i] = EXTEND(table,i);
-
- return table[n];
-}
-
-/************************************************************************/
-
-void
-writeny(FILE *f, graph *g, int n)
-/* write graph g (n vertices) to file f in y format */
-{
- static char ybit[] = {32,16,8,4,2,1};
- char s[(MAXN*(MAXN-1)/2 + 5)/6 + 4];
- int i,j,k;
- char y,*sp;
-
- sp = s;
- *(sp++) = 0x40 | n;
- y = 0x40;
-
- k = -1;
- for (j = 1; j < n; ++j)
- for (i = 0; i < j; ++i)
- {
- if (++k == 6)
- {
- *(sp++) = y;
- y = 0x40;
- k = 0;
- }
- if (g[i] & bit[j]) y |= ybit[k];
- }
- if (n >= 2) *(sp++) = y;
- *(sp++) = '\n';
- *sp = '\0';
-
- if (fputs(s,f) == EOF || ferror(f))
- {
- fprintf(stderr,">E writeny : error on writing file\n");
- exit(2);
- }
-}
-
-/************************************************************************/
-
-void
-writeg6x(FILE *f, graph *g, int n)
-/* write graph g (n vertices) to file f in graph6 format */
-{
- writeg6(f,g,1,n);
-}
-
-/************************************************************************/
-
-void
-writes6x(FILE *f, graph *g, int n)
-/* write graph g (n vertices) to file f in sparse6 format */
-{
- writes6(f,g,1,n);
-}
-
-/***********************************************************************/
-
-static void
-nullwrite(FILE *f, graph *g, int n)
-/* don't write graph g (n vertices) to file f */
-{
-}
-
-/***********************************************************************/
-
-void
-writenauty(FILE *f, graph *g, int n)
-/* write graph g (n vertices) to file f in nauty format */
-{
- int nn;
-
- nn = n;
-
- if (fwrite((char *)&nn,sizeof(int),(size_t)1,f) != 1 ||
- fwrite((char*)g,sizeof(setword),(size_t)n,f) != n)
- {
- fprintf(stderr,">E writenauty : error on writing file\n");
- exit(2);
- }
-}
-
-/*********************************************************************/
-
-static boolean
-isconnected(graph *g, int n)
-/* test if g is connected */
-{
- setword seen,expanded,toexpand,allbits;
- int i;
-
- allbits = ALLMASK(n);
-
- expanded = bit[n-1];
- seen = expanded | g[n-1];
-
- while (seen != allbits && (toexpand = (seen & ~expanded))) /* not == */
- {
- i = FIRSTBITNZ(toexpand);
- expanded |= bit[i];
- seen |= g[i];
- }
-
- return seen == allbits;
-}
-
-static boolean
-connpreprune(graph *g, int n, int maxn)
-/* This function speeds up the generation of connected graphs
- with not many edges. */
-{
- setword notvisited,queue;
- int ne,nc,i;
-
- if (n == maxn || maxe - maxn >= 5) return 0;
-
- ne = 0;
- for (i = 0; i < n; ++i) ne += POPCOUNT(g[i]);
- ne /= 2;
-
- nc = 0;
- notvisited = ALLMASK(n);
-
- while (notvisited)
- {
- ++nc;
- queue = SWHIBIT(notvisited);
- notvisited &= ~queue;
- while (queue)
- {
- TAKEBIT(i,queue);
- notvisited &= ~bit[i];
- queue |= g[i] & notvisited;
- }
- }
-
- if (ne - n + nc > maxe - maxn + 1) return TRUE;
-
- return FALSE;
-}
-
-/**********************************************************************/
-
-static boolean
-isbiconnected(graph *g, int n)
-/* test if g is biconnected */
-{
- int sp,v,w;
- setword sw;
- setword visited;
- int numvis,num[MAXN],lp[MAXN],stack[MAXN];
-
- if (n <= 2) return FALSE;
-
- visited = bit[0];
- stack[0] = 0;
- num[0] = 0;
- lp[0] = 0;
- numvis = 1;
- sp = 0;
- v = 0;
-
- for (;;)
- {
- if ((sw = g[v] & ~visited)) /* not "==" */
- {
- w = v;
- v = FIRSTBITNZ(sw); /* visit next child */
- stack[++sp] = v;
- visited |= bit[v];
- lp[v] = num[v] = numvis++;
- sw = g[v] & visited & ~bit[w];
- while (sw)
- {
- w = FIRSTBITNZ(sw);
- sw &= ~bit[w];
- if (num[w] < lp[v]) lp[v] = num[w];
- }
- }
- else
- {
- w = v; /* back up to parent */
- if (sp <= 1) return numvis == n;
- v = stack[--sp];
- if (lp[w] >= num[v]) return FALSE;
- if (lp[w] < lp[v]) lp[v] = lp[w];
- }
- }
-}
-
-/**********************************************************************/
-
-static void
-gcomplement(graph *g, graph *gc, int n)
-/* Take the complement of g and put it in gc */
-{
- int i;
- setword all;
-
- all = ~(setword)BITMASK(n-1);
- for (i = 0; i < n; ++i)
- gc[i] = g[i] ^ all ^ bit[i];
-}
-
-/**********************************************************************/
-
-static boolean
-distinvar(graph *g, int *invar, int n)
-/* make distance invariant
- return FALSE if n-1 not maximal else return TRUE */
-{
- int w;
- setword workset,frontier;
- setword sofar;
- int inv,d,v;
-
- for (v = n-1; v >= 0; --v)
- {
- inv = 0;
- sofar = frontier = bit[v];
- for (d = 1; frontier != 0; ++d)
- {
- workset = 0;
- inv += POPCOUNT(frontier) ^ (0x57 + d);
- while (frontier)
- {
- w = FIRSTBITNZ(frontier);
- frontier ^= bit[w];
- workset |= g[w];
- }
- frontier = workset & ~sofar;
- sofar |= frontier;
- }
- invar[v] = inv;
- if (v < n-1 && inv > invar[n-1]) return FALSE;
- }
- return TRUE;
-}
-
-/**************************************************************************/
-
-static void
-makexgraph(graph *g, xword *h, int n)
-/* make x-format graph from nauty format graph */
-{
- setword gi;
- int i,j;
- xword hi;
-
- for (i = 0; i < n; ++i)
- {
- hi = 0;
- gi = g[i];
- while (gi)
- {
- j = FIRSTBITNZ(gi);
- gi ^= bit[j];
- hi |= XBIT(j);
- }
- h[i] = hi;
- }
-}
-
-/**************************************************************************/
-
-static void
-make0graph(graph *g, xword *h, int n)
-/* make x-format graph without edges */
-{
- int i;
-
- for (i = 0; i < n; ++i) h[i] = 0;
-}
-
-/**************************************************************************/
-
-static void
-makebgraph(graph *g, xword *h, int n)
-/* make x-format graph of different colour graph */
-{
- setword seen1,seen2,expanded,w;
- setword restv;
- xword xseen1,xseen2;
- int i;
-
- restv = 0;
- for (i = 0; i < n; ++i) restv |= bit[i];
-
- seen1 = seen2 = 0;
- expanded = 0;
-
- while (TRUE)
- {
- if ((w = ((seen1 | seen2) & ~expanded)) == 0)
- {
- xseen1 = 0;
- w = seen1;
- while (w)
- {
- i = FIRSTBITNZ(w);
- w ^= bit[i];
- xseen1 |= XBIT(i);
- }
- xseen2 = 0;
- w = seen2;
- while (w)
- {
- i = FIRSTBITNZ(w);
- w ^= bit[i];
- xseen2 |= XBIT(i);
- }
-
- w = seen1;
- while (w)
- {
- i = FIRSTBITNZ(w);
- w ^= bit[i];
- h[i] = xseen2;
- }
- w = seen2;
- while (w)
- {
- i = FIRSTBITNZ(w);
- w ^= bit[i];
- h[i] = xseen1;
- }
-
- restv &= ~(seen1 | seen2);
- if (restv == 0) return;
- i = FIRSTBITNZ(restv);
- seen1 = bit[i];
- seen2 = 0;
- }
- else
- i = FIRSTBITNZ(w);
-
- expanded |= bit[i];
- if (bit[i] & seen1) seen2 |= g[i];
- else seen1 |= g[i];
- }
-}
-
-/**************************************************************************/
-
-static void
-makeb6graph(graph *g, xword *h, int n)
-/* make x-format bipartite girth 6 graph */
-{
- setword w,x;
- xword hi;
- int i,j;
-
- makebgraph(g,h,n);
-
- for (i = 0; i < n; ++i)
- {
- w = g[i];
- x = 0;
- while (w)
- {
- j = FIRSTBITNZ(w);
- w ^= bit[j];
- x |= g[j];
- }
- x &= ~bit[i];
- hi = h[i];
- while (x)
- {
- j = FIRSTBITNZ(x);
- x ^= bit[j];
- hi |= XBIT(j);
- }
- h[i] = hi;
- }
-}
-
-/**************************************************************************/
-
-static void
-makesgraph(graph *g, xword *h, int n)
-/* make x-format square graph */
-{
- setword w,x;
- xword hi;
- int i,j;
-
- for (i = 0; i < n; ++i)
- {
- w = g[i];
- x = 0;
- while (w)
- {
- j = FIRSTBITNZ(w);
- w ^= bit[j];
- x |= g[j];
- }
- x &= ~bit[i];
- hi = 0;
- while (x)
- {
- j = FIRSTBITNZ(x);
- x ^= bit[j];
- hi |= XBIT(j);
- }
- h[i] = hi;
- }
-}
-
-/**************************************************************************/
-
-static void
-makeg5graph(graph *g, xword *h, int n)
-/* make x-format girth-5 graph */
-{
- setword w,x;
- xword hi;
- int i,j;
-
- for (i = 0; i < n; ++i)
- {
- w = g[i];
- x = g[i];
- while (w)
- {
- j = FIRSTBITNZ(w);
- w ^= bit[j];
- x |= g[j];
- }
- x &= ~bit[i];
- hi = 0;
- while (x)
- {
- j = FIRSTBITNZ(x);
- x ^= bit[j];
- hi |= XBIT(j);
- }
- h[i] = hi;
- }
-}
-
-/**************************************************************************/
-
-static xword
-arith(xword a, xword b, xword c)
-/* Calculate a*b/c, assuming a*b/c and (c-1)*b are representable integers */
-{
- return (a/c)*b + ((a%c)*b)/c;
-}
-
-/**************************************************************************/
-
-static void
-makeleveldata(boolean restricted)
-/* make the level data for each level */
-{
- long h;
- int n,nn;
- xword ncj;
- leveldata *d;
- xword *xcard,*xinv;
- xword *xset,xw,nxsets;
- xword cw;
- xword i,ilast,j;
- size_t tttn;
-
- for (n = 1; n < maxn; ++n)
- {
- nn = maxdeg <= n ? maxdeg : n;
- ncj = nxsets = 1;
- for (j = 1; j <= nn; ++j)
- {
- ncj = arith(ncj,n-j+1,j);
- nxsets += ncj;
- }
-
- d = &data[n];
- d->ne = d->dmax = d->xlb = d->xub = -1;
-
- if (restricted)
- {
- d->xorb = (xword*) calloc(nxsets,sizeof(xword));
- d->xx = (xword*) calloc(nxsets,sizeof(xword));
- if (d->xorb == NULL || d->xx == NULL)
- {
- fprintf(stderr,
- ">E geng: calloc failed in makeleveldata()\n");
- exit(2);
- }
- continue; /* <--- NOTE THIS! */
- }
-
- tttn = (size_t)1 << n;
- d->xset = xset = (xword*) calloc(nxsets,sizeof(xword));
- d->xcard = xcard = (xword*) calloc(nxsets,sizeof(xword));
- d->xinv = xinv = (xword*) calloc(tttn,sizeof(xword));
- d->xorb = (xword*) calloc(nxsets,sizeof(xword));
- d->xx = d->xcard;
-
- if (xset==NULL || xcard==NULL || xinv==NULL || d->xorb==NULL)
- {
- fprintf(stderr,">E geng: calloc failed in makeleveldata()\n");
- exit(2);
- }
-
- j = 0;
-
- ilast = (n == WORDSIZE ? ~(setword)0 : XBIT(n)-1);
- for (i = 0;; ++i)
- {
- if ((h = XPOPCOUNT(i)) <= maxdeg)
- {
- xset[j] = i;
- xcard[j] = h;
- ++j;
- }
- if (i == ilast) break;
- }
-
- if (j != nxsets)
- {
- fprintf(stderr,">E geng: j=" SETWORD_DEC_FORMAT
- " nxsets=" SETWORD_DEC_FORMAT "\n",
- j,nxsets);
- exit(2);
- }
-
- h = 1;
- do
- h = 3 * h + 1;
- while (h < nxsets);
-
- do /* Shell sort, consider replacing */
- {
- for (i = h; i < nxsets; ++i)
- {
- xw = xset[i];
- cw = xcard[i];
- for (j = i; xcard[j-h] > cw ||
- (xcard[j-h] == cw && xset[j-h] > xw); )
- {
- xset[j] = xset[j-h];
- xcard[j] = xcard[j-h];
- if ((j -= h) < h) break;
- }
- xset[j] = xw;
- xcard[j] = cw;
- }
- h /= 3;
- }
- while (h > 0);
-
- for (i = 0; i < nxsets; ++i) xinv[xset[i]] = i;
-
- d->xstart[0] = 0;
- for (i = 1; i < nxsets; ++i)
- if (xcard[i] > xcard[i-1]) d->xstart[xcard[i]] = i;
- d->xstart[xcard[nxsets-1]+1] = nxsets;
- }
-}
-
-/**************************************************************************/
-
-static void
-userautomproc(int count, int *p, int *orbits,
- int numorbits, int stabvertex, int n)
-/* form orbits on powerset of VG
- called by nauty; operates on data[n] */
-{
- xword i,j1,j2,moved,pi,pxi;
- xword lo,hi;
- xword *xorb,*xinv,*xset,w;
-
- xorb = data[n].xorb;
- xset = data[n].xset;
- xinv = data[n].xinv;
- lo = data[n].lo;
- hi = data[n].hi;
-
- if (count == 1) /* first automorphism */
- for (i = lo; i < hi; ++i) xorb[i] = i;
-
- moved = 0;
- for (i = 0; i < n; ++i)
- if (p[i] != i) moved |= XBIT(i);
-
- for (i = lo; i < hi; ++i)
- {
- if ((w = xset[i] & moved) == 0) continue;
- pxi = xset[i] & ~moved;
- while (w)
- {
- j1 = XNEXTBIT(w);
- w ^= XBIT(j1);
- pxi |= XBIT(p[j1]);
- }
- pi = xinv[pxi];
-
- j1 = xorb[i];
- while (xorb[j1] != j1) j1 = xorb[j1];
- j2 = xorb[pi];
- while (xorb[j2] != j2) j2 = xorb[j2];
-
- if (j1 < j2) xorb[j2] = xorb[i] = xorb[pi] = j1;
- else if (j1 > j2) xorb[j1] = xorb[i] = xorb[pi] = j2;
- }
-}
-
-/**************************************************************************/
-
-static void
-userautomprocb(int count, int *p, int *orbits,
- int numorbits, int stabvertex, int n)
-/* form orbits on powerset of VG
- called by nauty; operates on data[n] */
-{
- xword j1,j2,moved,pi,pxi,lo,hi,x;
- xword i,*xorb,*xx,w,xlim,xlb;
-
- xorb = data[n].xorb;
- xx = data[n].xx;
- xlim = data[n].xlim;
-
- if (count == 1) /* first automorphism */
- {
- j1 = 0;
- xlb = data[n].xlb;
-
- for (i = 0; i < xlim; ++i)
- {
- x = xx[i];
- if (XPOPCOUNT(x) >= xlb)
- {
- xx[j1] = x;
- xorb[j1] = j1;
- ++j1;
- }
- }
- data[n].xlim = xlim = j1;
- }
-
- moved = 0;
- for (i = 0; i < n; ++i)
- if (p[i] != i) moved |= XBIT(i);
-
- for (i = 0; i < xlim; ++i)
- {
- if ((w = xx[i] & moved) == 0) continue;
- pxi = xx[i] & ~moved;
- while (w)
- {
- j1 = XNEXTBIT(w);
- w ^= XBIT(j1);
- pxi |= XBIT(p[j1]);
- }
- /* pi = position of pxi */
-
- lo = 0;
- hi = xlim - 1;
-
- for (;;)
- {
- pi = (lo + hi) >> 1;
- if (xx[pi] == pxi) break;
- else if (xx[pi] < pxi) lo = pi + 1;
- else hi = pi - 1;
- }
-
- j1 = xorb[i];
- while (xorb[j1] != j1) j1 = xorb[j1];
- j2 = xorb[pi];
- while (xorb[j2] != j2) j2 = xorb[j2];
-
- if (j1 < j2) xorb[j2] = xorb[i] = xorb[pi] = j1;
- else if (j1 > j2) xorb[j1] = xorb[i] = xorb[pi] = j2;
- }
-}
-
-/*****************************************************************************
-* *
-* refinex(g,lab,ptn,level,numcells,count,active,goodret,code,m,n) is a *
-* custom version of refine() which can exit quickly if required. *
-* *
-* Only use at level==0. *
-* goodret : whether to do an early return for code 1 *
-* code := -1 for n-1 not max, 0 for maybe, 1 for definite *
-* *
-*****************************************************************************/
-
-static void
-refinex(graph *g, int *lab, int *ptn, int level, int *numcells,
- int *count, set *active, boolean goodret, int *code, int m, int n)
-{
- int i,c1,c2,labc1;
- setword x,lact;
- int split1,split2,cell1,cell2;
- int cnt,bmin,bmax;
- set *gptr;
- setword workset;
- int workperm[MAXN];
- int bucket[MAXN+2];
-
- if (n == 1)
- {
- *code = 1;
- return;
- }
-
- *code = 0;
- lact = *active;
-
- while (*numcells < n && lact)
- {
- TAKEBIT(split1,lact);
-
- for (split2 = split1; ptn[split2] > 0; ++split2) {}
- if (split1 == split2) /* trivial splitting cell */
- {
- gptr = GRAPHROW(g,lab[split1],1);
- for (cell1 = 0; cell1 < n; cell1 = cell2 + 1)
- {
- for (cell2 = cell1; ptn[cell2] > 0; ++cell2) {}
- if (cell1 == cell2) continue;
-
- c1 = cell1;
- c2 = cell2;
- while (c1 <= c2)
- {
- labc1 = lab[c1];
- if (ISELEMENT1(gptr,labc1))
- ++c1;
- else
- {
- lab[c1] = lab[c2];
- lab[c2] = labc1;
- --c2;
- }
- }
- if (c2 >= cell1 && c1 <= cell2)
- {
- ptn[c2] = 0;
- ++*numcells;
- lact |= bit[c1];
- }
- }
- }
-
- else /* nontrivial splitting cell */
- {
- workset = 0;
- for (i = split1; i <= split2; ++i) workset |= bit[lab[i]];
-
- for (cell1 = 0; cell1 < n; cell1 = cell2 + 1)
- {
- for (cell2 = cell1; ptn[cell2] > 0; ++cell2) {}
- if (cell1 == cell2) continue;
- i = cell1;
- if ((x = workset & g[lab[i]]) != 0) cnt = POPCOUNT(x);
- else cnt = 0;
- count[i] = bmin = bmax = cnt;
- bucket[cnt] = 1;
- while (++i <= cell2)
- {
- if ((x = workset & g[lab[i]]) != 0)
- cnt = POPCOUNT(x);
- else
- cnt = 0;
-
- while (bmin > cnt) bucket[--bmin] = 0;
- while (bmax < cnt) bucket[++bmax] = 0;
- ++bucket[cnt];
- count[i] = cnt;
- }
- if (bmin == bmax) continue;
- c1 = cell1;
- for (i = bmin; i <= bmax; ++i)
- if (bucket[i])
- {
- c2 = c1 + bucket[i];
- bucket[i] = c1;
- if (c1 != cell1)
- {
- lact |= bit[c1];
- ++*numcells;
- }
- if (c2 <= cell2) ptn[c2-1] = 0;
- c1 = c2;
- }
- for (i = cell1; i <= cell2; ++i)
- workperm[bucket[count[i]]++] = lab[i];
- for (i = cell1; i <= cell2; ++i) lab[i] = workperm[i];
- }
- }
-
- if (ptn[n-2] == 0)
- {
- if (lab[n-1] == n-1)
- {
- *code = 1;
- if (goodret) return;
- }
- else
- {
- *code = -1;
- return;
- }
- }
- else
- {
- i = n - 1;
- while (TRUE)
- {
- if (lab[i] == n-1) break;
- --i;
- if (ptn[i] == 0)
- {
- *code = -1;
- return;
- }
- }
- }
- }
-}
-
-/**************************************************************************/
-
-static void
-makecanon(graph *g, graph *gcan, int n)
-/* gcan := canonise(g) */
-{
- int lab[MAXN],ptn[MAXN],orbits[MAXN];
- static DEFAULTOPTIONS_GRAPH(options);
- setword workspace[50];
-
- options.getcanon = TRUE;
-
- nauty(g,lab,ptn,NULL,orbits,&options,&nauty_stats,
- workspace,50,1,n,gcan);
-}
-
-/**************************************************************************/
-
-static boolean
-accept1(graph *g, int n, xword x, graph *gx, int *deg, boolean *rigid)
-/* decide if n in theta(g+x) - version for n+1 < maxn */
-{
- int i;
- int lab[MAXN],ptn[MAXN],orbits[MAXN];
- int count[MAXN];
- graph h[MAXN];
- xword xw;
- int nx,numcells,code;
- int i0,i1,degn;
- set active[MAXM];
- statsblk stats;
- static DEFAULTOPTIONS_GRAPH(options);
- setword workspace[50];
-
-#ifdef INSTRUMENT
- ++a1calls;
-#endif
-
- nx = n + 1;
- for (i = 0; i < n; ++i) gx[i] = g[i];
- gx[n] = 0;
- deg[n] = degn = XPOPCOUNT(x);
-
- xw = x;
- while (xw)
- {
- i = XNEXTBIT(xw);
- xw ^= XBIT(i);
- gx[i] |= bit[n];
- gx[n] |= bit[i];
- ++deg[i];
- }
-
-#ifdef PREPRUNE
- if (PREPRUNE(gx,n+1,maxn)) return FALSE;
-#endif
- if (connec == 2 && n+2 == maxn && !isconnected(gx,n+1)) return FALSE;
- if (((connec ==2 && n+2 < maxn) || (connec == 1 && n+2 <= maxn))
- && connpreprune(gx,n+1,maxn)) return FALSE;
-
- i0 = 0;
- i1 = n;
- for (i = 0; i < nx; ++i)
- {
- if (deg[i] == degn) lab[i1--] = i;
- else lab[i0++] = i;
- ptn[i] = 1;
- }
- ptn[n] = 0;
- if (i0 == 0)
- {
- numcells = 1;
- active[0] = bit[0];
- }
- else
- {
- numcells = 2;
- active[0] = bit[0] | bit[i1+1];
- ptn[i1] = 0;
- }
- refinex(gx,lab,ptn,0,&numcells,count,active,FALSE,&code,1,nx);
-
- if (code < 0) return FALSE;
-
- if (numcells == nx)
- {
- *rigid = TRUE;
-#ifdef INSTRUMENT
- ++a1succs;
-#endif
- return TRUE;
- }
-
- options.getcanon = TRUE;
- options.defaultptn = FALSE;
- options.userautomproc = userautomproc;
-
- active[0] = 0;
-#ifdef INSTRUMENT
- ++a1nauty;
-#endif
- nauty(gx,lab,ptn,active,orbits,&options,&stats,workspace,50,1,nx,h);
-
- if (orbits[lab[n]] == orbits[n])
- {
- *rigid = stats.numorbits == nx;
-#ifdef INSTRUMENT
- ++a1succs;
-#endif
- return TRUE;
- }
- else
- return FALSE;
-}
-
-/**************************************************************************/
-
-static boolean
-accept1b(graph *g, int n, xword x, graph *gx, int *deg, boolean *rigid,
- void (*makeh)(graph*,xword*,int))
-/* decide if n in theta(g+x) -- version for n+1 < maxn */
-{
- int i,v;
- xword z,hv,bitv,ixx;
- int lab[MAXN],ptn[MAXN],orbits[MAXN];
- int count[MAXN];
- graph gc[MAXN];
- xword h[MAXN],xw,jxx,kxx,*xx;
- int nx,numcells,code;
- int i0,i1,degn,xubx;
- set active[MAXM];
- statsblk stats;
- static DEFAULTOPTIONS_GRAPH(options);
- setword workspace[50];
-
-#ifdef INSTRUMENT
- ++a1calls;
-#endif
-
- nx = n + 1;
- for (i = 0; i < n; ++i) gx[i] = g[i];
- gx[n] = 0;
- deg[n] = degn = XPOPCOUNT(x);
-
- xw = x;
- while (xw)
- {
- i = XNEXTBIT(xw);
- xw ^= XBIT(i);
- gx[i] |= bit[n];
- gx[n] |= bit[i];
- ++deg[i];
- }
-
-#ifdef PREPRUNE
- if (PREPRUNE(gx,n+1,maxn)) return FALSE;
-#endif
- if (connec == 2 && n+2 == maxn && !isconnected(gx,n+1)) return FALSE;
- if (((connec ==2 && n+2 < maxn) || (connec == 1 && n+2 <= maxe))
- && connpreprune(gx,n+1,maxn)) return FALSE;
-
- i0 = 0;
- i1 = n;
- for (i = 0; i < nx; ++i)
- {
- if (deg[i] == degn) lab[i1--] = i;
- else lab[i0++] = i;
- ptn[i] = 1;
- }
- ptn[n] = 0;
- if (i0 == 0)
- {
- numcells = 1;
- active[0] = bit[0];
- }
- else
- {
- numcells = 2;
- active[0] = bit[0] | bit[i1+1];
- ptn[i1] = 0;
- }
- refinex(gx,lab,ptn,0,&numcells,count,active,FALSE,&code,1,nx);
-
- if (code < 0) return FALSE;
-
- (*makeh)(gx,h,nx);
- xx = data[nx].xx;
- xubx = data[nx].xub;
-
- xx[0] = 0;
- kxx = 1;
- for (v = 0; v < nx; ++v)
- {
- bitv = XBIT(v);
- hv = h[v];
- jxx = kxx;
- for (ixx = 0; ixx < jxx; ++ixx)
- if ((hv & xx[ixx]) == 0)
- {
- z = xx[ixx] | bitv;
- if (XPOPCOUNT(z) <= xubx) xx[kxx++] = z;
- }
- }
- data[nx].xlim = kxx;
-
- if (numcells == nx)
- {
- *rigid = TRUE;
-#ifdef INSTRUMENT
- ++a1succs;
-#endif
- return TRUE;
- }
-
- options.getcanon = TRUE;
- options.defaultptn = FALSE;
- options.userautomproc = userautomprocb;
-
- active[0] = 0;
-#ifdef INSTRUMENT
- ++a1nauty;
-#endif
- nauty(gx,lab,ptn,active,orbits,&options,&stats,workspace,50,1,nx,gc);
-
- if (orbits[lab[n]] == orbits[n])
- {
- *rigid = stats.numorbits == nx;
-#ifdef INSTRUMENT
- ++a1succs;
-#endif
- return TRUE;
- }
- else
- return FALSE;
-}
-
-/**************************************************************************/
-
-static boolean
-accept2(graph *g, int n, xword x, graph *gx, int *deg, boolean nuniq)
-/* decide if n in theta(g+x) -- version for n+1 == maxn */
-{
- int i;
- int lab[MAXN],ptn[MAXN],orbits[MAXN];
- int degx[MAXN],invar[MAXN];
- setword vmax,gv;
- int qn,qv;
- int count[MAXN];
- xword xw;
- int nx,numcells,code;
- int degn,i0,i1,j,j0,j1;
- set active[MAXM];
- statsblk stats;
- static DEFAULTOPTIONS_GRAPH(options);
- setword workspace[50];
- boolean cheapacc;
-
-#ifdef INSTRUMENT
- ++a2calls;
- if (nuniq) ++a2uniq;
-#endif
- nx = n + 1;
- for (i = 0; i < n; ++i)
- {
- gx[i] = g[i];
- degx[i] = deg[i];
- }
- gx[n] = 0;
- degx[n] = degn = XPOPCOUNT(x);
-
- xw = x;
- while (xw)
- {
- i = XNEXTBIT(xw);
- xw ^= XBIT(i);
- gx[i] |= bit[n];
- gx[n] |= bit[i];
- ++degx[i];
- }
-
-#ifdef PREPRUNE
- if (PREPRUNE(gx,n+1,maxn)) return FALSE;
-#endif
- if (connec == 2 && n+2 == maxn && !isconnected(gx,n+1)) return FALSE;
- if (((connec ==2 && n+2 < maxn) || (connec == 1 && n+2 <= maxe))
- && connpreprune(gx,n+1,maxn)) return FALSE;
-
- if (nuniq)
- {
-#ifdef INSTRUMENT
- ++a2succs;
-#endif
- if (canonise) makecanon(gx,gcan,nx);
- return TRUE;
- }
-
- i0 = 0;
- i1 = n;
- for (i = 0; i < nx; ++i)
- {
- if (degx[i] == degn) lab[i1--] = i;
- else lab[i0++] = i;
- ptn[i] = 1;
- }
- ptn[n] = 0;
- if (i0 == 0)
- {
- numcells = 1;
- active[0] = bit[0];
-
- if (!distinvar(gx,invar,nx)) return FALSE;
- qn = invar[n];
- j0 = 0;
- j1 = n;
- while (j0 <= j1)
- {
- j = lab[j0];
- qv = invar[j];
- if (qv < qn)
- ++j0;
- else
- {
- lab[j0] = lab[j1];
- lab[j1] = j;
- --j1;
- }
- }
- if (j0 > 0)
- {
- if (j0 == n)
- {
-#ifdef INSTRUMENT
- ++a2succs;
-#endif
- if (canonise) makecanon(gx,gcan,nx);
- return TRUE;
- }
- ptn[j1] = 0;
- ++numcells;
- active[0] |= bit[j0];
- }
- }
- else
- {
- numcells = 2;
- ptn[i1] = 0;
- active[0] = bit[0] | bit[i1+1];
-
- vmax = 0;
- for (i = i1+1; i < nx; ++i) vmax |= bit[lab[i]];
-
- gv = gx[n] & vmax;
- qn = POPCOUNT(gv);
-
- j0 = i1+1;
- j1 = n;
- while (j0 <= j1)
- {
- j = lab[j0];
- gv = gx[j] & vmax;
- qv = POPCOUNT(gv);
- if (qv > qn)
- return FALSE;
- else if (qv < qn)
- ++j0;
- else
- {
- lab[j0] = lab[j1];
- lab[j1] = j;
- --j1;
- }
- }
- if (j0 > i1+1)
- {
- if (j0 == n)
- {
-#ifdef INSTRUMENT
- ++a2succs;
-#endif
- if (canonise) makecanon(gx,gcan,nx);
- return TRUE;
- }
- ptn[j1] = 0;
- ++numcells;
- active[0] |= bit[j0];
- }
- }
-
- refinex(gx,lab,ptn,0,&numcells,count,active,TRUE,&code,1,nx);
-
- if (code < 0) return FALSE;
-
- cheapacc = FALSE;
- if (code > 0 || numcells >= nx-4)
- cheapacc = TRUE;
- else if (numcells == nx-5)
- {
- for (j1 = nx-2; j1 >= 0 && ptn[j1] > 0; --j1) {}
- if (nx - j1 != 5) cheapacc = TRUE;
- }
- else
- {
- j1 = nx;
- j0 = 0;
- for (i1 = 0; i1 < nx; ++i1)
- {
- --j1;
- if (ptn[i1] > 0)
- {
- ++j0;
- while (ptn[++i1] > 0) {}
- }
- }
- if (j1 <= j0 + 1) cheapacc = TRUE;
- }
-
- if (cheapacc)
- {
-#ifdef INSTRUMENT
- ++a2succs;
-#endif
- if (canonise) makecanon(gx,gcan,nx);
- return TRUE;
- }
-
- options.getcanon = TRUE;
- options.defaultptn = FALSE;
-
- active[0] = 0;
-#ifdef INSTRUMENT
- ++a2nauty;
-#endif
- nauty(gx,lab,ptn,active,orbits,&options,&stats,workspace,50,1,nx,gcan);
-
- if (orbits[lab[n]] == orbits[n])
- {
-#ifdef INSTRUMENT
- ++a2succs;
-#endif
- if (canonise) makecanon(gx,gcan,nx);
- return TRUE;
- }
- else
- return FALSE;
-}
-
-/**************************************************************************/
-
-static void
-xbnds(int n, int ne, int dmax)
-/* find bounds on extension degree; store answer in data[*].* */
-{
- int xlb,xub,d,nn,m,xc;
-
- xlb = n == 1 ? 0 : (dmax > (2*ne + n - 2)/(n - 1) ?
- dmax : (2*ne + n - 2)/(n - 1));
- xub = n < maxdeg ? n : maxdeg;
-
- for (xc = xub; xc >= xlb; --xc)
- {
- d = xc;
- m = ne + d;
- for (nn = n+1; nn < maxn; ++nn)
- {
- if (d < (2*m + nn - 2)/(nn - 1)) d = (2*m + nn - 2)/(nn - 1);
- m += d;
- }
- if (d > maxdeg || m > maxe) xub = xc - 1;
- else break;
- }
-
- if (ne + xlb < mine)
- for (xc = xlb; xc <= xub; ++xc)
- {
- m = ne + xc;
- for (nn = n + 1; nn < maxn; ++nn)
- m += maxdeg < nn ? maxdeg : nn;
- if (m < mine) xlb = xc + 1;
- else break;
- }
-
- data[n].ne = ne;
- data[n].dmax = dmax;
- data[n].xlb = xlb;
- data[n].xub = xub;
-}
-
-/**************************************************************************/
-
-static void
-spaextend(graph *g, int n, int *deg, int ne, boolean rigid,
- int xlb, int xub, void (*makeh)(graph*,xword*,int))
-/* extend from n to n+1 -- version for restricted graphs */
-{
- xword x,d,dlow;
- xword xlim,*xorb;
- int xc,nx,i,j,dmax,dcrit,xlbx,xubx;
- graph gx[MAXN];
- xword *xx,ixx;
- int degx[MAXN];
- boolean rigidx;
-
-#ifdef INSTRUMENT
- boolean haschild;
-
- haschild = FALSE;
- if (rigid) ++rigidnodes[n];
-#endif
- ++nodes[n];
-
- nx = n + 1;
- dmax = deg[n-1];
- dcrit = mindeg - maxn + n;
- d = dlow = 0;
- for (i = 0; i < n; ++i)
- {
- if (deg[i] == dmax) d |= XBIT(i);
- if (deg[i] == dcrit) dlow |= XBIT(i);
- }
-
- if (xlb == dmax && XPOPCOUNT(d) + dmax > n) ++xlb;
- if (nx == maxn && xlb < mindeg) xlb = mindeg;
- if (xlb > xub) return;
-
-#ifdef PRUNE
- if (PRUNE(g,n,maxn)) return;
-#endif
-
- xorb = data[n].xorb;
- xx = data[n].xx;
- xlim = data[n].xlim;
-
- if (nx == maxn)
- {
- for (ixx = 0; ixx < xlim; ++ixx)
- {
- x = xx[ixx];
- xc = XPOPCOUNT(x);
- if (xc < xlb || xc > xub) continue;
- if ((rigid || xorb[ixx] == ixx)
- && (xc > dmax || (xc == dmax && (x & d) == 0))
- && (dlow & ~x) == 0)
- {
- if (accept2(g,n,x,gx,deg,
- xc > dmax+1 || (xc == dmax+1 && (x & d) == 0))
- && (!connec ||
- (connec==1 && isconnected(gx,nx)) ||
- (connec>1 && isbiconnected(gx,nx))))
- {
-#ifdef PRUNE
- if (!PRUNE(gx,nx,maxn))
-#endif
- {
-#ifdef INSTRUMENT
- haschild = TRUE;
-#endif
- ++ecount[ne+xc];
- (*outproc)(outfile,canonise ? gcan : gx,nx);
- }
- }
- }
- }
- }
- else
- {
- for (ixx = 0; ixx < xlim; ++ixx)
- {
- if (nx == splitlevel)
- {
- if (odometer-- != 0) continue;
- odometer = mod - 1;
- }
- x = xx[ixx];
- xc = XPOPCOUNT(x);
- if (xc < xlb || xc > xub) continue;
- if ((rigid || xorb[ixx] == ixx)
- && (xc > dmax || (xc == dmax && (x & d) == 0))
- && (dlow & ~x) == 0)
- {
- for (j = 0; j < n; ++j) degx[j] = deg[j];
- if (data[nx].ne != ne+xc || data[nx].dmax != xc)
- xbnds(nx,ne+xc,xc);
-
- xlbx = data[nx].xlb;
- xubx = data[nx].xub;
- if (xlbx <= xubx
- && accept1b(g,n,x,gx,degx,&rigidx,makeh))
- {
-#ifdef INSTRUMENT
- haschild = TRUE;
-#endif
- spaextend(gx,nx,degx,ne+xc,rigidx,xlbx,xubx,makeh);
- }
- }
- }
- if (n == splitlevel - 1 && n >= min_splitlevel
- && nodes[n] >= multiplicity)
- --splitlevel;
- }
-#ifdef INSTRUMENT
- if (haschild) ++fertilenodes[n];
-#endif
-}
-
-/**************************************************************************/
-
-static void
-genextend(graph *g, int n, int *deg, int ne, boolean rigid, int xlb, int xub)
-/* extend from n to n+1 -- version for general graphs */
-{
- xword x,d,dlow;
- xword *xset,*xcard,*xorb;
- xword i,imin,imax;
- int nx,xc,j,dmax,dcrit;
- int xlbx,xubx;
- graph gx[MAXN];
- int degx[MAXN];
- boolean rigidx;
-
-#ifdef INSTRUMENT
- boolean haschild;
-
- haschild = FALSE;
- if (rigid) ++rigidnodes[n];
-#endif
- ++nodes[n];
-
- nx = n + 1;
- dmax = deg[n-1];
- dcrit = mindeg - maxn + n;
- d = dlow = 0;
- for (i = 0; i < n; ++i)
- {
- if (deg[i] == dmax) d |= XBIT(i);
- if (deg[i] == dcrit) dlow |= XBIT(i);
- }
-
- if (xlb == dmax && XPOPCOUNT(d) + dmax > n) ++xlb;
- if (nx == maxn && xlb < mindeg) xlb = mindeg;
- if (xlb > xub) return;
-
-#ifdef PRUNE
- if (PRUNE(g,n,maxn)) return;
-#endif
-
- imin = data[n].xstart[xlb];
- imax = data[n].xstart[xub+1];
- xset = data[n].xset;
- xcard = data[n].xcard;
- xorb = data[n].xorb;
-
- if (nx == maxn)
- for (i = imin; i < imax; ++i)
- {
- if (!rigid && xorb[i] != i) continue;
- x = xset[i];
- xc = (int)xcard[i];
- if (xc == dmax && (x & d) != 0) continue;
- if ((dlow & ~x) != 0) continue;
-
- if (accept2(g,n,x,gx,deg,
- xc > dmax+1 || (xc == dmax+1 && (x & d) == 0)))
- if (!connec || (connec==1 && isconnected(gx,nx))
- || (connec>1 && isbiconnected(gx,nx)))
- {
-#ifdef PRUNE
- if (!PRUNE(gx,nx,maxn))
-#endif
- {
-#ifdef INSTRUMENT
- haschild = TRUE;
-#endif
- ++ecount[ne+xc];
- (*outproc)(outfile,canonise ? gcan : gx,nx);
- }
- }
- }
- else
- for (i = imin; i < imax; ++i)
- {
- if (!rigid && xorb[i] != i) continue;
- x = xset[i];
- xc = (int)xcard[i];
- if (xc == dmax && (x & d) != 0) continue;
- if ((dlow & ~x) != 0) continue;
- if (nx == splitlevel)
- {
- if (odometer-- != 0) continue;
- odometer = mod - 1;
- }
-
- for (j = 0; j < n; ++j) degx[j] = deg[j];
- if (data[nx].ne != ne+xc || data[nx].dmax != xc)
- xbnds(nx,ne+xc,xc);
- xlbx = data[nx].xlb;
- xubx = data[nx].xub;
- if (xlbx > xubx) continue;
-
- data[nx].lo = data[nx].xstart[xlbx];
- data[nx].hi = data[nx].xstart[xubx+1];
- if (accept1(g,n,x,gx,degx,&rigidx))
- {
-#ifdef INSTRUMENT
- haschild = TRUE;
-#endif
- genextend(gx,nx,degx,ne+xc,rigidx,xlbx,xubx);
- }
- }
-
- if (n == splitlevel-1 && n >= min_splitlevel
- && nodes[n] >= multiplicity)
- --splitlevel;
-#ifdef INSTRUMENT
- if (haschild) ++fertilenodes[n];
-#endif
-}
-
-/**************************************************************************/
-/**************************************************************************/
-
-#ifdef GENG_MAIN
-int
-GENG_MAIN(int argc, char *argv[])
-#else
-int
-main(int argc, char *argv[])
-#endif
-{
- char *arg;
- boolean badargs,gote,gotmr,gotf,gotd,gotD,gotx,gotX;
- boolean secret,connec1,connec2,safe,sparse;
- char *outfilename,sw;
- int i,j,argnum;
- graph g[1];
- int tmaxe,deg[1];
- nauty_counter nout;
- int splitlevinc;
- double t1,t2;
- char msg[201];
-
- HELP; PUTVERSION;
- nauty_check(WORDSIZE,1,MAXN,NAUTYVERSIONID);
-
- badargs = FALSE;
- trianglefree = FALSE;
- bipartite = FALSE;
- squarefree = FALSE;
- verbose = FALSE;
- nautyformat = FALSE;
- yformat = FALSE;
- graph6 = FALSE;
- sparse6 = FALSE;
- savemem = FALSE;
- nooutput = FALSE;
- canonise = FALSE;
- header = FALSE;
- outfilename = NULL;
- secret = FALSE;
- safe = FALSE;
- connec1 = connec2 = FALSE;
-
- maxdeg = MAXN;
- mindeg = 0;
-
- gotX = gotx = gotd = gotD = gote = gotmr = gotf = FALSE;
-
- argnum = 0;
- for (j = 1; !badargs && j < argc; ++j)
- {
- arg = argv[j];
- if (arg[0] == '-' && arg[1] != '\0')
- {
- ++arg;
- while (*arg != '\0')
- {
- sw = *arg++;
- SWBOOLEAN('n',nautyformat)
- else SWBOOLEAN('u',nooutput)
- else SWBOOLEAN('g',graph6)
- else SWBOOLEAN('s',sparse6)
- else SWBOOLEAN('t',trianglefree)
- else SWBOOLEAN('f',squarefree)
- else SWBOOLEAN('b',bipartite)
- else SWBOOLEAN('v',verbose)
- else SWBOOLEAN('l',canonise)
- else SWBOOLEAN('y',yformat)
- else SWBOOLEAN('h',header)
- else SWBOOLEAN('m',savemem)
- else SWBOOLEAN('c',connec1)
- else SWBOOLEAN('C',connec2)
- else SWBOOLEAN('q',quiet)
- else SWBOOLEAN('$',secret)
- else SWBOOLEAN('S',safe)
- else SWINT('d',gotd,mindeg,"geng -d")
- else SWINT('D',gotD,maxdeg,"geng -D")
- else SWINT('x',gotx,multiplicity,"geng -x")
- else SWINT('X',gotX,splitlevinc,"geng -X")
-#ifdef PLUGIN_SWITCHES
-PLUGIN_SWITCHES
-#endif
- else badargs = TRUE;
- }
- }
- else if (arg[0] == '-' && arg[1] == '\0')
- gotf = TRUE;
- else
- {
- if (argnum == 0)
- {
- if (sscanf(arg,"%d",&maxn) != 1) badargs = TRUE;
- ++argnum;
- }
- else if (gotf)
- badargs = TRUE;
- else
- {
- if (!gotmr)
- {
- if (sscanf(arg,"%d/%d",&res,&mod) == 2)
- {
- gotmr = TRUE;
- continue;
- }
- }
- if (!gote)
- {
- if (sscanf(arg,"%d:%d",&mine,&maxe) == 2
- || sscanf(arg,"%d-%d",&mine,&maxe) == 2)
- {
- gote = TRUE;
- if (maxe == 0 && mine > 0) maxe = MAXN*(MAXN-1)/2;
- continue;
- }
- else if (sscanf(arg,"%d",&mine) == 1)
- {
- gote = TRUE;
- maxe = mine;
- continue;
- }
- }
- if (!gotf)
- {
- outfilename = arg;
- gotf = TRUE;
- continue;
- }
- }
- }
- }
-
- if (argnum == 0)
- badargs = TRUE;
- else if (maxn < 1 || maxn > MAXN)
- {
- fprintf(stderr,">E geng: n must be in the range 1..%d\n",MAXN);
- badargs = TRUE;
- }
-
- if (!gotmr)
- {
- mod = 1;
- res = 0;
- }
-
- if (!gote)
- {
- mine = 0;
- maxe = (maxn*maxn - maxn) / 2;
- }
-
- if (connec1 && mindeg < 1 && maxn > 1) mindeg = 1;
- if (connec2 && mindeg < 2 && maxn > 2) mindeg = 2;
- if (maxdeg >= maxn) maxdeg = maxn - 1;
- if (maxe > maxn*maxdeg / 2) maxe = maxn*maxdeg / 2;
- if (maxdeg > maxe) maxdeg = maxe;
- if (mindeg < 0) mindeg = 0;
- if (mine < (maxn*mindeg+1) / 2) mine = (maxn*mindeg+1) / 2;
- if (maxdeg > 2*maxe - mindeg*(maxn-1)) maxdeg = 2*maxe - mindeg*(maxn-1);
-
- if (connec2) connec = 2;
- else if (connec1) connec = 1;
- else connec = 0;
- if (connec && mine < maxn-1) mine = maxn - 2 + connec;
-
-#if defined(PRUNE) || defined(PREPRUNE)
- geng_mindeg = mindeg;
- geng_maxdeg = maxdeg;
- geng_mine = mine;
- geng_maxe = maxe;
- geng_connec = connec;
-#endif
-
- if (!badargs && (mine > maxe || maxe < 0 || maxdeg < 0))
- {
- fprintf(stderr,
- ">E geng: impossible mine,maxe,mindeg,maxdeg values\n");
- badargs = TRUE;
- }
-
- if (!badargs && (res < 0 || res >= mod))
- {
- fprintf(stderr,">E geng: must have 0 <= res < mod\n");
- badargs = TRUE;
- }
-
- if (badargs)
- {
- fprintf(stderr,">E Usage: %s\n",USAGE);
- GETHELP;
- exit(1);
- }
-
- if ((nautyformat!=0) + (yformat!=0) + (graph6!=0)
- + (sparse6!=0) + (nooutput!=0) > 1)
- gt_abort(">E geng: -uyngs are incompatible\n");
-
-#ifdef OUTPROC
- outproc = OUTPROC;
-#else
- if (nautyformat) outproc = writenauty;
- else if (yformat) outproc = writeny;
- else if (nooutput) outproc = nullwrite;
- else if (sparse6) outproc = writes6x;
- else outproc = writeg6x;
-#endif
-
-#ifdef PLUGIN_INIT
-PLUGIN_INIT
-#endif
-
- for (i = 0; i <= maxe; ++i) ecount[i] = 0;
- for (i = 0; i < maxn; ++i) nodes[i] = 0;
-
- if (nooutput)
- outfile = stdout;
- else if (!gotf || outfilename == NULL)
- {
- outfilename = "stdout";
- outfile = stdout;
- }
- else if ((outfile = fopen(outfilename,
- nautyformat ? "wb" : "w")) == NULL)
- {
- fprintf(stderr,
- ">E geng: can't open %s for writing\n",outfilename);
- gt_abort(NULL);
- }
-
- if (bipartite)
- if (squarefree) tmaxe = findmaxe(maxebf,maxn);
- else tmaxe = findmaxe(maxeb,maxn);
- else if (trianglefree)
- if (squarefree) tmaxe = findmaxe(maxeft,maxn);
- else tmaxe = findmaxe(maxet,maxn);
- else if (squarefree) tmaxe = findmaxe(maxef,maxn);
- else tmaxe = (maxn*maxn - maxn) / 2;
-
- if (safe) ++tmaxe;
-
- if (maxe > tmaxe) maxe = tmaxe;
-
- if (gotx)
- {
- if (multiplicity < 3 * mod || multiplicity > 999999999)
- gt_abort(">E geng: -x value must be in [3*mod,10^9-1]\n");
- }
- else
- {
- multiplicity = PRUNEMULT * mod;
- if (multiplicity / PRUNEMULT != mod)
- gt_abort(">E geng: mod value is too large\n");
- }
-
- if (!gotX) splitlevinc = 0;
-
- if (!quiet)
- {
- msg[0] = '\0';
- if (strlen(argv[0]) > 75)
- fprintf(stderr,">A %s",argv[0]);
- else
- CATMSG1(">A %s",argv[0]);
-
- CATMSG6(" -%s%s%s%s%s%s",
- connec2 ? "C" : connec1 ? "c" : "",
- trianglefree ? "t" : "",
- squarefree ? "f" : "",
- bipartite ? "b" : "",
- canonise ? "l" : "",
- savemem ? "m" : "");
- if (mod > 1)
- CATMSG2("X%dx%d",splitlevinc,multiplicity);
- CATMSG4("d%dD%d n=%d e=%d",mindeg,maxdeg,maxn,mine);
- if (maxe > mine) CATMSG1("-%d",maxe);
- if (mod > 1) CATMSG2(" class=%d/%d",res,mod);
- CATMSG0("\n");
- fputs(msg,stderr);
- fflush(stderr);
- }
-
- g[0] = 0;
- deg[0] = 0;
-
- sparse = bipartite || squarefree || trianglefree || savemem;
-
- t1 = CPUTIME;
-
- if (header)
- {
- if (sparse6)
- {
- writeline(outfile,SPARSE6_HEADER);
- fflush(outfile);
- }
- else if (!yformat && !nautyformat && !nooutput)
- {
- writeline(outfile,GRAPH6_HEADER);
- fflush(outfile);
- }
- }
-
- if (maxn == 1)
- {
- if (res == 0 && connec < 2)
- {
- ++ecount[0];
- (*outproc)(outfile,g,1);
- }
- }
- else
- {
- if (maxn > 28 || maxn+4 > 8*sizeof(xword))
- savemem = sparse = TRUE;
- if (maxn == maxe+1 && connec)
- bipartite = squarefree = sparse = TRUE; /* trees */
-
- makeleveldata(sparse);
-
- if (maxn >= 14 && mod > 1) splitlevel = maxn - 4;
- else if (maxn >= 6 && mod > 1) splitlevel = maxn - 3;
- else splitlevel = -1;
-
- if (splitlevel > 0) splitlevel += splitlevinc;
- if (splitlevel > maxn - 1) splitlevel = maxn - 1;
- if (splitlevel < 3) splitlevel = -1;
-
- min_splitlevel = 6;
- odometer = secret ? -1 : res;
-
- if (maxe >= mine &&
- (mod <= 1 || (mod > 1 && (splitlevel > 2 || res == 0))))
- {
- xbnds(1,0,0);
- if (sparse)
- {
- data[1].xx[0] = 0;
- if (maxdeg > 0) data[1].xx[1] = XBIT(0);
- data[1].xlim = data[1].xub + 1;
- }
-
- if (bipartite)
- if (squarefree)
- spaextend(g,1,deg,0,TRUE,
- data[1].xlb,data[1].xub,makeb6graph);
- else
- spaextend(g,1,deg,0,TRUE,
- data[1].xlb,data[1].xub,makebgraph);
- else if (trianglefree)
- if (squarefree)
- spaextend(g,1,deg,0,TRUE,
- data[1].xlb,data[1].xub,makeg5graph);
- else
- spaextend(g,1,deg,0,TRUE,
- data[1].xlb,data[1].xub,makexgraph);
- else if (squarefree)
- spaextend(g,1,deg,0,TRUE,
- data[1].xlb,data[1].xub,makesgraph);
- else if (savemem)
- spaextend(g,1,deg,0,TRUE,
- data[1].xlb,data[1].xub,make0graph);
- else
- genextend(g,1,deg,0,TRUE,data[1].xlb,data[1].xub);
- }
- }
- t2 = CPUTIME;
-
- nout = 0;
- for (i = 0; i <= maxe; ++i) nout += ecount[i];
-
- if (verbose)
- {
- for (i = 0; i <= maxe; ++i)
- if (ecount[i] != 0)
- {
- fprintf(stderr,">C " COUNTER_FMT " graphs with %d edges\n",
- ecount[i],i);
- }
- }
-
-#ifdef INSTRUMENT
- fprintf(stderr,"\n>N node counts\n");
- for (i = 1; i < maxn; ++i)
- {
- fprintf(stderr," level %2d: ",i);
- fprintf(stderr,COUNTER_FMT " (" COUNTER_FMT
- " rigid, " COUNTER_FMT " fertile)\n",
- nodes[i],rigidnodes[i],fertilenodes[i]);
- }
- fprintf(stderr,">A1 " COUNTER_FMT " calls to accept1, "
- COUNTER_FMT " nauty, " COUNTER_FMT " succeeded\n",
- a1calls,a1nauty,a1succs);
- fprintf(stderr,">A2 " COUNTER_FMT " calls to accept2, " COUNTER_FMT
- " nuniq, "COUNTER_FMT " nauty, " COUNTER_FMT " succeeded\n",
- a2calls,a2uniq,a2nauty,a2succs);
- fprintf(stderr,"\n");
-#endif
-
-#ifdef SUMMARY
- SUMMARY(nout,t2-t1);
-#endif
-
- if (!quiet)
- {
- fprintf(stderr,">Z " COUNTER_FMT " graphs generated in %3.2f sec\n",
- nout,t2-t1);
- }
-
-#ifdef GENG_MAIN
- for (i = 1; i < maxn; ++i)
- if (sparse)
- {
- free(data[i].xorb);
- free(data[i].xx);
- }
- else
- {
- free(data[i].xorb);
- free(data[i].xset);
- free(data[i].xinv);
- free(data[i].xcard);
- }
- return 0;
-#else
- exit(0);
-#endif
-}
+/* TODO:
+ * add chordal graphs
+ * add perfect graphs
+ * add complements for ordinary graphs
+ * add 5-cycle rejection
+ * improve output by compiling g6 from level n-1 */
+
+/* geng.c version 3.3; B D McKay, June 2021. */
+
+#define USAGE \
+"geng [-cCmtfbd#D#] [-uygsnh] [-lvq] \n\
+ [-x#X#] n [mine[:maxe]] [res/mod] [file]"
+
+#define HELPTEXT \
+" Generate all graphs of a specified class.\n\
+\n\
+ n : the number of vertices\n\
+ mine:maxe : a range for the number of edges\n\
+ #:0 means '# or more' except in the case 0:0\n\
+ res/mod : only generate subset res out of subsets 0..mod-1\n\
+\n\
+ -c : only write connected graphs\n\
+ -C : only write biconnected graphs\n\
+ -t : only generate triangle-free graphs\n\
+ -f : only generate 4-cycle-free graphs\n\
+ -b : only generate bipartite graphs\n\
+ (-t, -f and -b can be used in any combination)\n\
+ -m : save memory at the expense of time (only makes a\n\
+ difference in the absence of -b, -t, -f and n <= 28).\n\
+ -d# : a lower bound for the minimum degree\n\
+ -D# : an upper bound for the maximum degree\n\
+ -v : display counts by number of edges\n\
+ -l : canonically label output graphs\n\
+\n\
+ -u : do not output any graphs, just generate and count them\n\
+ -g : use graph6 output (default)\n\
+ -s : use sparse6 output\n\
+ -h : for graph6 or sparse6 format, write a header too\n\
+\n\
+ -q : suppress auxiliary output (except from -v)\n\
+\n\
+ See program text for much more information.\n"
+
+
+/* Parameters:
+
+ n = the number of vertices (1..MAXN)
+ Note that MAXN is limited to WORDSIZE
+ mine = the minimum number of edges (no bounds if missing)
+ maxe = the maximum number of edges (same as mine if missing)
+ 0 means "infinity" except in the case "0-0"
+ mod, res = a way to restrict the output to a subset.
+ All the graphs in G(n,mine..maxe) are divided into
+ disjoint classes C(0,mod),C(1,mod),...,C(mod-1,mod),
+ of very approximately equal size.
+ Only the class C(res,mod) is written.
+
+ If the -x or -X switch is used, they must have the
+ same value for different values of res; otherwise
+ the partitioning may not be valid. In this case
+ (-x,-X with constant value), the usual relationships
+ between modulo classes are obeyed; for example
+ C(3,4) = C(3,8) union C(7,8). This is not true
+ if 3/8 and 7/8 are done with -x or -X values
+ different from those used for 3/4.
+
+ file = a name for the output file (stdout if missing or "-")
+
+ All switches can be concatenated or separate. However, the
+ value of -d must be attached to the "d", and similarly for "x".
+
+ -c : only write connected graphs
+ -C : only write biconnected graphs
+ -t : only generate triangle-free graphs
+ -f : only generate 4-cycle-free graphs
+ -b : only generate bipartite graphs
+ (-t, -f and -b can be used in any combination)
+ -m : save memory at expense of time (only makes a
+ difference in the absence of -b, -t, -f and n <= 30).
+ -D : specify an upper bound for the maximum degree.
+ The value of the upper bound must be adjacent to
+ the "D". Example: -D6
+ -d : specify a lower bound for the minimum degree.
+ The value of the upper bound must be adjacent to
+ the "d". Example: -d6
+ -v : display counts by number of edges
+ -l : canonically label output graphs
+
+ -u : do not output any graphs, just generate and count them
+ -g : use graph6 output (default)
+ -s : use sparse6 output
+ -n : use nauty format instead of graph6 format for output
+ -y : use the obsolete y-format for output
+ -h : for graph6 or sparse6 format, write a header too
+
+ -q : suppress auxiliary output (except from -v)
+
+ -x : specify a parameter that determines how evenly
+ the res/mod facility splits the graphs into subsets.
+ High values mean more even splitting at slight cost
+ to the total time. The default is 20*mod, and the
+ the legal minimum is 3*mod. More information is given
+ under "res/mod" above.
+ -X : move the initial splitting level higher by ,
+ in order to force more even splitting at the cost
+ of speed. Default is -X0. More information is given
+ under "res/mod" above.
+
+Output formats.
+
+ The output format is determined by the mutually exclusive switches
+ -u, -n, -y, -g and -s. The default is -g.
+
+ -u suppresses output of graphs completely.
+
+ -s and -g specify sparse6 and graph6 format, defined elsewhere.
+ In this case a header is also written if -h is present.
+
+ If -y is present, graphs will be written in y-format.
+ y-format is obsolete and only provided for backwards compatibility.
+
+ Each graph occupies one line with a terminating newline.
+ Except for the newline, each byte has the format 01xxxxxx, where
+ each "x" represents one bit of data.
+ First byte: xxxxxx is the number of vertices n
+ Other ceiling(n(n-1)/12) bytes: These contain the upper triangle of
+ the adjacency matrix in column major order. That is, the entries
+ appear in the order (0,1),(0,2),(1,2),(0,3),(1,3),(2,3),(0,4),... .
+ The bits are used in left to right order within each byte.
+ Any unused bits on the end are set to zero.
+
+ If -n is present, any output graphs are written in nauty format.
+
+ For a graph of n vertices, the output consists of one int giving
+ the number of vertices, and n setwords containing the adjacency
+ matrix. Note that this is system dependent (i.e. don't use it).
+ It will not work properly if the output is to stdout and your
+ system distinguishes binary and text files.
+
+OUTPROC feature.
+
+ By defining the C preprocessor variable OUTPROC at compile time
+ (for Unix the syntax is -DOUTPROC=procname on the cc command),
+ geng can be made to call a procedure of your manufacture with
+ each output graph instead of writing anything. Your procedure
+ needs to have type void and the argument list (FILE *f, graph
+ *g, int n). f is a stream open for writing, g is the graph in
+ nauty format, and n is the number of vertices. Your procedure
+ can be in a separate file so long as it is linked with geng. The
+ global variables sparse6, graph6, quiet, nooutput, nautyformat,
+ yformat and canonise (all type boolean) can be used to test
+ for the presence of the flags -s, -g, -q, -u, -n, -y and -l,
+ respectively. If -l is present, the group size and similar
+ details can be found in the global variable nauty_stats.
+
+PRUNE feature.
+
+ By defining the C preprocessor variable PRUNE at compile time, geng
+ can be made to call
+ int PRUNE(graph *g,int n,int maxn)
+ for each intermediate (and final) graph, and reject it if
+ the value returned is nonzero. The arguments are:
+
+ g = the graph in nauty format (m=1)
+ n = the number of vertices in g
+ maxn = the number of vertices for output
+ (the value you gave on the command line to geng)
+
+ geng constructs the graph starting with vertex 0, then adding
+ vertices 1,2,3,... in that order. Each graph in the sequence is
+ an induced subgraph of all later graphs in the sequence.
+
+ A call is made for all orders from 1 to maxn. In testing for
+ a uniform property (such as a forbidden subgraph or forbidden
+ induced subgraph) it might save time to notice that a call to
+ PRUNE for n implies that the call for n-1 already passed.
+
+ For very fast tests, it might be worthwhile using PREPRUNE as
+ well or instead. It has the same meaning but is applied earlier
+ and more often.
+
+ If -c or -C is given, the connectivity test is done before
+ PRUNE but not necessarily before PREPRUNE.
+
+ Some parameters are available in global variables:
+ geng_mindeg, geng_maxdeg, geng_mine, geng_maxe, geng_connec;
+
+SUMMARY
+
+ If the C preprocessor variable SUMMARY is defined at compile time, the
+ procedure SUMMARY(nauty_counter nout, double cpu) is called just before
+ the program exits. The purpose is to allow reporting of statistics
+ collected by PRUNE or OUTPROC. The values nout and cpu are the output
+ count and cpu time reported on the >Z line.
+ Output should be written to stderr.
+
+INSTRUMENT feature.
+
+ If the C preprocessor variable INSTRUMENT is defined at compile time,
+ extra code is inserted to collect statistics during execution, and
+ more information is written to stderr at termination.
+
+CALLING FROM A PROGRAM
+
+ It is possible to call geng from another program instead of using it
+ as a stand-alone program. The main requirement is to change the name
+ of the main program to be other than "main". This is done by defining
+ the preprocessor variable GENG_MAIN. You might also like to define
+ OUTPROC to be the name of a procedure to receive the graphs. To call
+ the program you need to define an argument list argv[] consistent with
+ the usual one; don't forget that argv[0] is the command name and not
+ the first argument. The value of argc is the number of strings in
+ argv[]; that is, one more than the number of arguments. See the
+ sample program callgeng.c.
+
+**************************************************************************
+
+Counts and sample performance statistics.
+
+ Here we give some graph counts and approximate execution times
+ on a Linux computer with Intel Core i7-4790 nominally 3.6GHz,
+ compiled with gcc 6.2.0.
+ Times are with the -u option (generate but don't write); add
+ 0.2-0.3 microseconds per graph for output to a file.
+
+
+ General Graphs C3-free Graphs (-t)
+
+ 1 1 1 1
+ 2 2 2 2
+ 3 4 3 3
+ 4 11 4 7
+ 5 34 5 14
+ 6 156 6 38
+ 7 1044 7 107
+ 8 12346 8 410
+ 9 274668 0.08 sec 9 1897
+ 10 12005168 2.7 sec 10 12172
+ 11 1018997864 207 sec 11 105071 0.09 sec
+ 12 165091172592 9 hr 12 1262180 0.8 sec
+ 13 50502031367952 108 days 13 20797002 11 sec
+ These can be done in about half 14 467871369 220 sec
+ the time by setting the edge limit 15 14232552452 1.7 hr
+ half way then adding complements. 16 581460254001 65 hr
+ 17 31720840164950 145 days
+
+
+ C4-free Graphs (-f) (C3,C4)-free Graphs (-tf)
+
+ 1 1 1 1
+ 2 2 2 2
+ 3 4 3 3
+ 4 8 4 6
+ 5 18 5 11
+ 6 44 6 23
+ 7 117 7 48
+ 8 351 8 114
+ 9 1230 9 293
+ 10 5069 10 869
+ 11 25181 0.04 sec 11 2963
+ 12 152045 0.17 sec 12 12066 0.03 sec
+ 13 1116403 1.0 sec 13 58933 0.10 sec
+ 14 9899865 7.5 sec 14 347498 0.5 sec
+ 15 104980369 71 sec 15 2455693 2.7 sec
+ 16 1318017549 14 min 16 20592932 19 sec
+ 17 19427531763 3.4 hr 17 202724920 170 sec
+ 18 333964672216 56 hr 18 2322206466 32 min
+ 19 6660282066936 45 days 19 30743624324 7 hr
+ 20 468026657815 4 days
+ 21 8161170076257 106 days
+
+ Old value was wrong: 18 2142368552 (The program was
+ ok, but somehow we tabulated the answer incorrectly.)
+
+
+ Bipartite Graphs (-b) C4-free Bipartite Graphs (-bf)
+
+ 1 1 1 1
+ 2 2 2 2
+ 3 3 3 3
+ 4 7 4 6
+ 5 13 5 10
+ 6 35 6 21
+ 7 88 7 39
+ 8 303 8 86
+ 9 1119 9 182
+ 10 5479 10 440
+ 11 32303 0.03 sec 11 1074
+ 12 251135 2.3 sec 12 2941
+ 13 2527712 1.7 sec 13 8424 0.04 sec
+ 14 33985853 19 sec 14 26720 0.11 sec
+ 15 611846940 4.9 min 15 90883 0.33 sec
+ 16 14864650924 1.8 hr 16 340253 1.1 sec
+ 17 488222721992 2.4 days 17 1384567 3.7 sec
+ 18 21712049275198 105 days 18 6186907 14 sec
+ 19 30219769 59 sec
+ 20 161763233 280 sec
+ 21 946742190 24 min
+ 22 6054606722 2.5 hr
+ 23 42229136988 17 hr
+ 24 320741332093 125 hr
+ 25 2648348712904 58 days
+
+If you know any more of these counts, please tell me.
+
+**************************************************************************
+
+Hints:
+
+To make all the graphs of order n, without restriction on type,
+it is fastest to make them up to binomial(n,2)/2 edges and append
+the complement of those with strictly less than binomial(n,2)/2 edges.
+
+If it is necessary to split the computation into pieces, it is more
+efficient to use the res/mod feature than to split by numbers of edges.
+
+**************************************************************************
+
+ Author: B. D. McKay, Sep 1991 and many later dates.
+ Copyright B. McKay (1991-2018). All rights reserved.
+ This software is subject to the conditions and waivers
+ detailed in the file nauty.h.
+
+ Changes: Nov 18, 1991 : added -d switch
+ fixed operation for n=16
+ Nov 26, 1991 : added OUTPROC feature
+ Nov 29, 1991 : -c implies mine >= n-1
+ Jan 8, 1992 : make writeny() not static
+ Jan 10, 1992 : added -n switch
+ Feb 9, 1992 : fixed case of n=1
+ Feb 16, 1992 : changed mine,maxe,maxdeg testing
+ Feb 19, 1992 : added -b, -t and -u options
+ documented OUTPROC and added external
+ declaration for it.
+ Feb 20, 1992 : added -v option
+ Feb 22, 1992 : added INSTRUMENT compile-time option
+ Feb 23, 1992 : added xbnds() for more effective pruning
+ Feb 24, 1992 : added -l option
+ Feb 25, 1992 : changed writenauty() to use fwrite()
+ Mar 11, 1992 : completely revised many parts, incl
+ new refinement procedure for fast rejection,
+ distance invariant for regular graphs
+ May 19, 1992 : modified userautomproc slightly. xorb[]
+ is no longer idempotent but it doesn't matter.
+ Speed-up of 2-5% achieved.
+ June 5, 1993 : removed ";" after "CPUDEFS" to avoid illegal
+ empty declaration.
+ Nov 24, 1994 : tested for 0 <= res < mod
+
+ Apr 13, 1996 : Major overhaul. Renamed "geng".
+ Changed argument syntax.
+ Removed 16-vertex limit.
+ Added -s, -m, -x. Allowed combinations.
+ Replaced code for non-general graphs.
+ Very many small changes.
+ Jul 12, 1996 : Changed semantics of -x and res/mod.
+ Changed >A line and added fflush()/
+ All switches can be concatenated or not.
+ Aug 16, 1996 : Added -X switch and PRUNE() feature.
+ Fixed case of argument 0-0.
+ Sep 22, 1996 : Improved 1-2% by tweaking refinex().
+ Jan 21, 1997 : Renamed to geng.
+ Changed -s to -f, and added -sghq.
+ Sep 7, 1997 : Fixed WORDSIZE=16 problems.
+ Sep 22, 1997 : Use "wb" open for nautyformat.
+ Jan 26, 1998 : Added SUMMARY feature.
+ Mar 4, 1998 : Added -C.
+ Mar 12, 1998 : Moved stats to nauty_stats.
+ Jan 1, 2000 : Changed -d to -D and added -d.
+ Feb 24, 2000 : Raised limit to 32 vertices.
+ Mar 3, 2000 : Made some counts into unsigned long.
+ (Includes first arg to SUMMARY.)
+ Mar 12, 2000 : Used bigint for counts that may exceed 2^32.
+ Now all counts from very long runs are ok.
+ Oct 12, 2000 : Changed maxef[32] to 92 after confirmation
+ from Yang Yuansheng. The old value of 93 was
+ valid but 92 is slightly more efficient.
+ Nov 16, 2000 : Used fuction prototypes.
+ Jul 31, 2001 : Added PREPRUNE
+ May 7, 2004 : Complete all function prototypes
+ Nov 24, 2004 : Force -m for very large sizes
+ Add -bf automatically if generating trees
+ Apr 1, 2007 : Write >A in one fputs() to try to reduce
+ mixing of outputs in multi-process pipes.
+ Sep 19, 2007 : Force -m for n > 28 regardless of word size.
+ Nov 29, 2008 : Slightly improved connectivity testing.
+ Mar 3, 2015 : Improve maxdeg tweaking.
+ Jan 18, 2016 : Replace bigint by nauty_counter.
+ Mar 8, 2018 : Can now compile for MAXN up to WORDSIZE.
+ Use setword instead of unsigned for xword.
+ Revised splitting level.
+ Updated sample execution times.
+ Mar 10, 2018 : Fix overflow at impossibly large n, maxdeg.
+ Jan 14, 2019 : Define geng_mindeg, geng_maxdeg, geng_mine, geng_maxe.
+ Jun 1, 2021 : Define geng_connec.
+ Jun 4, 2021 : Improve performance for -c and -C with small edge count.
+ Jun 21, 2021 : K1 is not 2-connected.
+
+**************************************************************************/
+
+#define NAUTY_PGM 1 /* 1 = geng, 2 = genbg, 3 = gentourng */
+
+#ifndef MAXN
+#define MAXN WORDSIZE /* not more than WORDSIZE */
+#endif
+
+#if MAXN > WORDSIZE
+ #error "Can't have MAXN greater than WORDSIZE"
+#endif
+
+#define ONE_WORD_SETS
+#include "gtools.h" /* which includes nauty.h and stdio.h */
+
+typedef setword xword;
+
+static void (*outproc)(FILE*,graph*,int);
+
+static FILE *outfile; /* file for output graphs */
+static int connec; /* 1 for -c, 2 for -C, 0 for neither */
+static boolean bipartite; /* presence of -b */
+static boolean trianglefree; /* presence of -t */
+static boolean squarefree; /* presence of -f */
+static boolean savemem; /* presence of -m */
+static boolean verbose; /* presence of -v */
+boolean nautyformat; /* presence of -n */
+boolean yformat; /* presence of -y */
+boolean graph6; /* presence of -g */
+boolean sparse6; /* presence of -s */
+boolean nooutput; /* presence of -u */
+boolean canonise; /* presence of -l */
+boolean quiet; /* presence of -q */
+boolean header; /* presence of -h */
+statsblk nauty_stats;
+static int mindeg,maxdeg,maxn,mine,maxe,mod,res;
+#define PRUNEMULT 50 /* bigger -> more even split at greater cost */
+static int min_splitlevel,odometer,splitlevel,multiplicity;
+static graph gcan[MAXN];
+
+#define XBIT(i) ((xword)1 << (i))
+#define XPOPCOUNT(x) POPCOUNT(x)
+#define XNEXTBIT(x) (WORDSIZE-1-FIRSTBITNZ(x)) /* Assumes non-zero */
+
+typedef struct
+{
+ int ne,dmax; /* values used for xlb,xub calculation */
+ int xlb,xub; /* saved bounds on extension degree */
+ xword lo,hi; /* work purposes for orbit calculation */
+ xword xstart[MAXN+1]; /* index into xset[] for each cardinality */
+ xword *xset; /* array of all x-sets in card order */
+ xword *xcard; /* cardinalities of all x-sets */
+ xword *xinv; /* map from x-set to index in xset */
+ xword *xorb; /* min orbit representative */
+ xword *xx; /* (-b, -t, -s, -m) candidate x-sets */
+ /* note: can be the same as xcard */
+ xword xlim; /* number of x-sets in xx[] */
+} leveldata;
+
+static leveldata data[MAXN]; /* data[n] is data for n -> n+1 */
+static nauty_counter ecount[1+MAXN*(MAXN-1)/2]; /* counts by number of edges */
+static nauty_counter nodes[MAXN]; /* nodes at each level */
+
+#ifdef INSTRUMENT
+static nauty_counter rigidnodes[MAXN],fertilenodes[MAXN];
+static nauty_counter a1calls,a1nauty,a1succs;
+static nauty_counter a2calls,a2nauty,a2uniq,a2succs;
+#endif
+
+/* The numbers below are actual maximum edge counts.
+ geng works correctly with any upper bounds.
+ To extend known upper bounds upwards:
+ (n-1, E) -> (n, E + floor(2*E/(n-2))),
+ which is done by the procedure findmaxe().
+*/
+
+static int maxeb[65] = /* max edges for -b */
+ {0,0,1,2,4, -1};
+static int maxet[65] = /* max edges for -t */
+ {0,0,1,2,4, -1};
+static int maxef[65] = /* max edges for -f */
+ {0,0,1,3,4, 6,7,9,11,13,
+ 16,18,21,24,27, 30,33,36,39,42,
+ 46,50,52,56,59, 63,67,71,76,80,
+ 85,90,92,96,102, 106,110,113,117,122, -1};
+static int maxeft[65] = /* max edges for -ft */
+ {0,0,1,2,3, 5,6,8,10,12,
+ 15,16,18,21,23, 26,28,31,34,38,
+ 41,44,47,50,54, 57,61,65,68,72,
+ 76,80,85,87,90, 95,99,104,109,114,
+ 120,124,129,134,139, 145,150,156,162,168,
+ 175,176,178, -1};
+static int maxebf[65] = /* max edges for -bf */
+ {0,0,1,2,3, 4,6,7,9,10,
+ 12,14,16,18,21, 22,24,26,29,31,
+ 34,36,39,42,45, 48,52,53,56,58,
+ 61,64,67,70,74, 77,81,84,88,92,
+ 96,100,105,106,108, 110,115,118,122,126,
+ 130,134,138,142,147, 151,156,160,165,170,
+ 175,180,186,187, -1};
+
+#ifdef PLUGIN
+#include PLUGIN
+#endif
+
+#ifdef OUTPROC
+extern void OUTPROC(FILE*,graph*,int);
+#endif
+#ifdef PRUNE
+extern int PRUNE(graph*,int,int);
+#endif
+#ifdef PREPRUNE
+extern int PREPRUNE(graph*,int,int);
+#endif
+#ifdef SUMMARY
+extern void SUMMARY(nauty_counter,double);
+#endif
+
+#if defined(PRUNE) || defined(PREPRUNE)
+int geng_mindeg, geng_maxdeg, geng_mine, geng_maxe, geng_connec;
+#endif
+
+/************************************************************************/
+
+#define EXTEND(table,n) ((n) <= 1 ? 0 : (n) == 2 ? 1 : \
+ table[(n)-1] + (2*table[(n)-1]/((n)-2)))
+
+static int
+findmaxe(int *table, int n)
+/* Return the n-th entry of a maxe table, extending existing values
+ if necessary. */
+{
+ int i;
+
+ for (i = 0; i <= n && table[i] >= 0; ++i) {}
+ for ( ; i <= n; ++i) table[i] = EXTEND(table,i);
+
+ return table[n];
+}
+
+/************************************************************************/
+
+void
+writeny(FILE *f, graph *g, int n)
+/* write graph g (n vertices) to file f in y format */
+{
+ static char ybit[] = {32,16,8,4,2,1};
+ char s[(MAXN*(MAXN-1)/2 + 5)/6 + 4];
+ int i,j,k;
+ char y,*sp;
+
+ sp = s;
+ *(sp++) = 0x40 | n;
+ y = 0x40;
+
+ k = -1;
+ for (j = 1; j < n; ++j)
+ for (i = 0; i < j; ++i)
+ {
+ if (++k == 6)
+ {
+ *(sp++) = y;
+ y = 0x40;
+ k = 0;
+ }
+ if (g[i] & bit[j]) y |= ybit[k];
+ }
+ if (n >= 2) *(sp++) = y;
+ *(sp++) = '\n';
+ *sp = '\0';
+
+ if (fputs(s,f) == EOF || ferror(f))
+ {
+ fprintf(stderr,">E writeny : error on writing file\n");
+ exit(2);
+ }
+}
+
+/************************************************************************/
+
+void
+writeg6x(FILE *f, graph *g, int n)
+/* write graph g (n vertices) to file f in graph6 format */
+{
+ writeg6(f,g,1,n);
+}
+
+/************************************************************************/
+
+void
+writes6x(FILE *f, graph *g, int n)
+/* write graph g (n vertices) to file f in sparse6 format */
+{
+ writes6(f,g,1,n);
+}
+
+/***********************************************************************/
+
+static void
+nullwrite(FILE *f, graph *g, int n)
+/* don't write graph g (n vertices) to file f */
+{
+}
+
+/***********************************************************************/
+
+void
+writenauty(FILE *f, graph *g, int n)
+/* write graph g (n vertices) to file f in nauty format */
+{
+ int nn;
+
+ nn = n;
+
+ if (fwrite((char *)&nn,sizeof(int),(size_t)1,f) != 1 ||
+ fwrite((char*)g,sizeof(setword),(size_t)n,f) != n)
+ {
+ fprintf(stderr,">E writenauty : error on writing file\n");
+ exit(2);
+ }
+}
+
+/*********************************************************************/
+
+static boolean
+isconnected(graph *g, int n)
+/* test if g is connected */
+{
+ setword seen,expanded,toexpand,allbits;
+ int i;
+
+ allbits = ALLMASK(n);
+
+ expanded = bit[n-1];
+ seen = expanded | g[n-1];
+
+ while (seen != allbits && (toexpand = (seen & ~expanded))) /* not == */
+ {
+ i = FIRSTBITNZ(toexpand);
+ expanded |= bit[i];
+ seen |= g[i];
+ }
+
+ return seen == allbits;
+}
+
+static boolean
+connpreprune(graph *g, int n, int maxn)
+/* This function speeds up the generation of connected graphs
+ with not many edges. */
+{
+ setword notvisited,queue;
+ int ne,nc,i;
+
+ if (n == maxn || maxe - maxn >= 5) return 0;
+
+ ne = 0;
+ for (i = 0; i < n; ++i) ne += POPCOUNT(g[i]);
+ ne /= 2;
+
+ nc = 0;
+ notvisited = ALLMASK(n);
+
+ while (notvisited)
+ {
+ ++nc;
+ queue = SWHIBIT(notvisited);
+ notvisited &= ~queue;
+ while (queue)
+ {
+ TAKEBIT(i,queue);
+ notvisited &= ~bit[i];
+ queue |= g[i] & notvisited;
+ }
+ }
+
+ if (ne - n + nc > maxe - maxn + 1) return TRUE;
+
+ return FALSE;
+}
+
+/**********************************************************************/
+
+static boolean
+isbiconnected(graph *g, int n)
+/* test if g is biconnected */
+{
+ int sp,v,w;
+ setword sw;
+ setword visited;
+ int numvis,num[MAXN],lp[MAXN],stack[MAXN];
+
+ if (n <= 2) return FALSE;
+
+ visited = bit[0];
+ stack[0] = 0;
+ num[0] = 0;
+ lp[0] = 0;
+ numvis = 1;
+ sp = 0;
+ v = 0;
+
+ for (;;)
+ {
+ if ((sw = g[v] & ~visited)) /* not "==" */
+ {
+ w = v;
+ v = FIRSTBITNZ(sw); /* visit next child */
+ stack[++sp] = v;
+ visited |= bit[v];
+ lp[v] = num[v] = numvis++;
+ sw = g[v] & visited & ~bit[w];
+ while (sw)
+ {
+ w = FIRSTBITNZ(sw);
+ sw &= ~bit[w];
+ if (num[w] < lp[v]) lp[v] = num[w];
+ }
+ }
+ else
+ {
+ w = v; /* back up to parent */
+ if (sp <= 1) return numvis == n;
+ v = stack[--sp];
+ if (lp[w] >= num[v]) return FALSE;
+ if (lp[w] < lp[v]) lp[v] = lp[w];
+ }
+ }
+}
+
+/**********************************************************************/
+
+static void
+gcomplement(graph *g, graph *gc, int n)
+/* Take the complement of g and put it in gc */
+{
+ int i;
+ setword all;
+
+ all = ~(setword)BITMASK(n-1);
+ for (i = 0; i < n; ++i)
+ gc[i] = g[i] ^ all ^ bit[i];
+}
+
+/**********************************************************************/
+
+static boolean
+distinvar(graph *g, int *invar, int n)
+/* make distance invariant
+ return FALSE if n-1 not maximal else return TRUE */
+{
+ int w;
+ setword workset,frontier;
+ setword sofar;
+ int inv,d,v;
+
+ for (v = n-1; v >= 0; --v)
+ {
+ inv = 0;
+ sofar = frontier = bit[v];
+ for (d = 1; frontier != 0; ++d)
+ {
+ workset = 0;
+ inv += POPCOUNT(frontier) ^ (0x57 + d);
+ while (frontier)
+ {
+ w = FIRSTBITNZ(frontier);
+ frontier ^= bit[w];
+ workset |= g[w];
+ }
+ frontier = workset & ~sofar;
+ sofar |= frontier;
+ }
+ invar[v] = inv;
+ if (v < n-1 && inv > invar[n-1]) return FALSE;
+ }
+ return TRUE;
+}
+
+/**************************************************************************/
+
+static void
+makexgraph(graph *g, xword *h, int n)
+/* make x-format graph from nauty format graph */
+{
+ setword gi;
+ int i,j;
+ xword hi;
+
+ for (i = 0; i < n; ++i)
+ {
+ hi = 0;
+ gi = g[i];
+ while (gi)
+ {
+ j = FIRSTBITNZ(gi);
+ gi ^= bit[j];
+ hi |= XBIT(j);
+ }
+ h[i] = hi;
+ }
+}
+
+/**************************************************************************/
+
+static void
+make0graph(graph *g, xword *h, int n)
+/* make x-format graph without edges */
+{
+ int i;
+
+ for (i = 0; i < n; ++i) h[i] = 0;
+}
+
+/**************************************************************************/
+
+static void
+makebgraph(graph *g, xword *h, int n)
+/* make x-format graph of different colour graph */
+{
+ setword seen1,seen2,expanded,w;
+ setword restv;
+ xword xseen1,xseen2;
+ int i;
+
+ restv = 0;
+ for (i = 0; i < n; ++i) restv |= bit[i];
+
+ seen1 = seen2 = 0;
+ expanded = 0;
+
+ while (TRUE)
+ {
+ if ((w = ((seen1 | seen2) & ~expanded)) == 0)
+ {
+ xseen1 = 0;
+ w = seen1;
+ while (w)
+ {
+ i = FIRSTBITNZ(w);
+ w ^= bit[i];
+ xseen1 |= XBIT(i);
+ }
+ xseen2 = 0;
+ w = seen2;
+ while (w)
+ {
+ i = FIRSTBITNZ(w);
+ w ^= bit[i];
+ xseen2 |= XBIT(i);
+ }
+
+ w = seen1;
+ while (w)
+ {
+ i = FIRSTBITNZ(w);
+ w ^= bit[i];
+ h[i] = xseen2;
+ }
+ w = seen2;
+ while (w)
+ {
+ i = FIRSTBITNZ(w);
+ w ^= bit[i];
+ h[i] = xseen1;
+ }
+
+ restv &= ~(seen1 | seen2);
+ if (restv == 0) return;
+ i = FIRSTBITNZ(restv);
+ seen1 = bit[i];
+ seen2 = 0;
+ }
+ else
+ i = FIRSTBITNZ(w);
+
+ expanded |= bit[i];
+ if (bit[i] & seen1) seen2 |= g[i];
+ else seen1 |= g[i];
+ }
+}
+
+/**************************************************************************/
+
+static void
+makeb6graph(graph *g, xword *h, int n)
+/* make x-format bipartite girth 6 graph */
+{
+ setword w,x;
+ xword hi;
+ int i,j;
+
+ makebgraph(g,h,n);
+
+ for (i = 0; i < n; ++i)
+ {
+ w = g[i];
+ x = 0;
+ while (w)
+ {
+ j = FIRSTBITNZ(w);
+ w ^= bit[j];
+ x |= g[j];
+ }
+ x &= ~bit[i];
+ hi = h[i];
+ while (x)
+ {
+ j = FIRSTBITNZ(x);
+ x ^= bit[j];
+ hi |= XBIT(j);
+ }
+ h[i] = hi;
+ }
+}
+
+/**************************************************************************/
+
+static void
+makesgraph(graph *g, xword *h, int n)
+/* make x-format square graph */
+{
+ setword w,x;
+ xword hi;
+ int i,j;
+
+ for (i = 0; i < n; ++i)
+ {
+ w = g[i];
+ x = 0;
+ while (w)
+ {
+ j = FIRSTBITNZ(w);
+ w ^= bit[j];
+ x |= g[j];
+ }
+ x &= ~bit[i];
+ hi = 0;
+ while (x)
+ {
+ j = FIRSTBITNZ(x);
+ x ^= bit[j];
+ hi |= XBIT(j);
+ }
+ h[i] = hi;
+ }
+}
+
+/**************************************************************************/
+
+static void
+makeg5graph(graph *g, xword *h, int n)
+/* make x-format girth-5 graph */
+{
+ setword w,x;
+ xword hi;
+ int i,j;
+
+ for (i = 0; i < n; ++i)
+ {
+ w = g[i];
+ x = g[i];
+ while (w)
+ {
+ j = FIRSTBITNZ(w);
+ w ^= bit[j];
+ x |= g[j];
+ }
+ x &= ~bit[i];
+ hi = 0;
+ while (x)
+ {
+ j = FIRSTBITNZ(x);
+ x ^= bit[j];
+ hi |= XBIT(j);
+ }
+ h[i] = hi;
+ }
+}
+
+/**************************************************************************/
+
+static xword
+arith(xword a, xword b, xword c)
+/* Calculate a*b/c, assuming a*b/c and (c-1)*b are representable integers */
+{
+ return (a/c)*b + ((a%c)*b)/c;
+}
+
+/**************************************************************************/
+
+static void
+makeleveldata(boolean restricted)
+/* make the level data for each level */
+{
+ long h;
+ int n,nn;
+ xword ncj;
+ leveldata *d;
+ xword *xcard,*xinv;
+ xword *xset,xw,nxsets;
+ xword cw;
+ xword i,ilast,j;
+ size_t tttn;
+
+ for (n = 1; n < maxn; ++n)
+ {
+ nn = maxdeg <= n ? maxdeg : n;
+ ncj = nxsets = 1;
+ for (j = 1; j <= nn; ++j)
+ {
+ ncj = arith(ncj,n-j+1,j);
+ nxsets += ncj;
+ }
+
+ d = &data[n];
+ d->ne = d->dmax = d->xlb = d->xub = -1;
+
+ if (restricted)
+ {
+ d->xorb = (xword*) calloc(nxsets,sizeof(xword));
+ d->xx = (xword*) calloc(nxsets,sizeof(xword));
+ if (d->xorb == NULL || d->xx == NULL)
+ {
+ fprintf(stderr,
+ ">E geng: calloc failed in makeleveldata()\n");
+ exit(2);
+ }
+ continue; /* <--- NOTE THIS! */
+ }
+
+ tttn = (size_t)1 << n;
+ d->xset = xset = (xword*) calloc(nxsets,sizeof(xword));
+ d->xcard = xcard = (xword*) calloc(nxsets,sizeof(xword));
+ d->xinv = xinv = (xword*) calloc(tttn,sizeof(xword));
+ d->xorb = (xword*) calloc(nxsets,sizeof(xword));
+ d->xx = d->xcard;
+
+ if (xset==NULL || xcard==NULL || xinv==NULL || d->xorb==NULL)
+ {
+ fprintf(stderr,">E geng: calloc failed in makeleveldata()\n");
+ exit(2);
+ }
+
+ j = 0;
+
+ ilast = (n == WORDSIZE ? ~(setword)0 : XBIT(n)-1);
+ for (i = 0;; ++i)
+ {
+ if ((h = XPOPCOUNT(i)) <= maxdeg)
+ {
+ xset[j] = i;
+ xcard[j] = h;
+ ++j;
+ }
+ if (i == ilast) break;
+ }
+
+ if (j != nxsets)
+ {
+ fprintf(stderr,">E geng: j=" SETWORD_DEC_FORMAT
+ " nxsets=" SETWORD_DEC_FORMAT "\n",
+ j,nxsets);
+ exit(2);
+ }
+
+ h = 1;
+ do
+ h = 3 * h + 1;
+ while (h < nxsets);
+
+ do /* Shell sort, consider replacing */
+ {
+ for (i = h; i < nxsets; ++i)
+ {
+ xw = xset[i];
+ cw = xcard[i];
+ for (j = i; xcard[j-h] > cw ||
+ (xcard[j-h] == cw && xset[j-h] > xw); )
+ {
+ xset[j] = xset[j-h];
+ xcard[j] = xcard[j-h];
+ if ((j -= h) < h) break;
+ }
+ xset[j] = xw;
+ xcard[j] = cw;
+ }
+ h /= 3;
+ }
+ while (h > 0);
+
+ for (i = 0; i < nxsets; ++i) xinv[xset[i]] = i;
+
+ d->xstart[0] = 0;
+ for (i = 1; i < nxsets; ++i)
+ if (xcard[i] > xcard[i-1]) d->xstart[xcard[i]] = i;
+ d->xstart[xcard[nxsets-1]+1] = nxsets;
+ }
+}
+
+/**************************************************************************/
+
+static void
+userautomproc(int count, int *p, int *orbits,
+ int numorbits, int stabvertex, int n)
+/* form orbits on powerset of VG
+ called by nauty; operates on data[n] */
+{
+ xword i,j1,j2,moved,pi,pxi;
+ xword lo,hi;
+ xword *xorb,*xinv,*xset,w;
+
+ xorb = data[n].xorb;
+ xset = data[n].xset;
+ xinv = data[n].xinv;
+ lo = data[n].lo;
+ hi = data[n].hi;
+
+ if (count == 1) /* first automorphism */
+ for (i = lo; i < hi; ++i) xorb[i] = i;
+
+ moved = 0;
+ for (i = 0; i < n; ++i)
+ if (p[i] != i) moved |= XBIT(i);
+
+ for (i = lo; i < hi; ++i)
+ {
+ if ((w = xset[i] & moved) == 0) continue;
+ pxi = xset[i] & ~moved;
+ while (w)
+ {
+ j1 = XNEXTBIT(w);
+ w ^= XBIT(j1);
+ pxi |= XBIT(p[j1]);
+ }
+ pi = xinv[pxi];
+
+ j1 = xorb[i];
+ while (xorb[j1] != j1) j1 = xorb[j1];
+ j2 = xorb[pi];
+ while (xorb[j2] != j2) j2 = xorb[j2];
+
+ if (j1 < j2) xorb[j2] = xorb[i] = xorb[pi] = j1;
+ else if (j1 > j2) xorb[j1] = xorb[i] = xorb[pi] = j2;
+ }
+}
+
+/**************************************************************************/
+
+static void
+userautomprocb(int count, int *p, int *orbits,
+ int numorbits, int stabvertex, int n)
+/* form orbits on powerset of VG
+ called by nauty; operates on data[n] */
+{
+ xword j1,j2,moved,pi,pxi,lo,hi,x;
+ xword i,*xorb,*xx,w,xlim,xlb;
+
+ xorb = data[n].xorb;
+ xx = data[n].xx;
+ xlim = data[n].xlim;
+
+ if (count == 1) /* first automorphism */
+ {
+ j1 = 0;
+ xlb = data[n].xlb;
+
+ for (i = 0; i < xlim; ++i)
+ {
+ x = xx[i];
+ if (XPOPCOUNT(x) >= xlb)
+ {
+ xx[j1] = x;
+ xorb[j1] = j1;
+ ++j1;
+ }
+ }
+ data[n].xlim = xlim = j1;
+ }
+
+ moved = 0;
+ for (i = 0; i < n; ++i)
+ if (p[i] != i) moved |= XBIT(i);
+
+ for (i = 0; i < xlim; ++i)
+ {
+ if ((w = xx[i] & moved) == 0) continue;
+ pxi = xx[i] & ~moved;
+ while (w)
+ {
+ j1 = XNEXTBIT(w);
+ w ^= XBIT(j1);
+ pxi |= XBIT(p[j1]);
+ }
+ /* pi = position of pxi */
+
+ lo = 0;
+ hi = xlim - 1;
+
+ for (;;)
+ {
+ pi = (lo + hi) >> 1;
+ if (xx[pi] == pxi) break;
+ else if (xx[pi] < pxi) lo = pi + 1;
+ else hi = pi - 1;
+ }
+
+ j1 = xorb[i];
+ while (xorb[j1] != j1) j1 = xorb[j1];
+ j2 = xorb[pi];
+ while (xorb[j2] != j2) j2 = xorb[j2];
+
+ if (j1 < j2) xorb[j2] = xorb[i] = xorb[pi] = j1;
+ else if (j1 > j2) xorb[j1] = xorb[i] = xorb[pi] = j2;
+ }
+}
+
+/*****************************************************************************
+* *
+* refinex(g,lab,ptn,level,numcells,count,active,goodret,code,m,n) is a *
+* custom version of refine() which can exit quickly if required. *
+* *
+* Only use at level==0. *
+* goodret : whether to do an early return for code 1 *
+* code := -1 for n-1 not max, 0 for maybe, 1 for definite *
+* *
+*****************************************************************************/
+
+static void
+refinex(graph *g, int *lab, int *ptn, int level, int *numcells,
+ int *count, set *active, boolean goodret, int *code, int m, int n)
+{
+ int i,c1,c2,labc1;
+ setword x,lact;
+ int split1,split2,cell1,cell2;
+ int cnt,bmin,bmax;
+ set *gptr;
+ setword workset;
+ int workperm[MAXN];
+ int bucket[MAXN+2];
+
+ if (n == 1)
+ {
+ *code = 1;
+ return;
+ }
+
+ *code = 0;
+ lact = *active;
+
+ while (*numcells < n && lact)
+ {
+ TAKEBIT(split1,lact);
+
+ for (split2 = split1; ptn[split2] > 0; ++split2) {}
+ if (split1 == split2) /* trivial splitting cell */
+ {
+ gptr = GRAPHROW(g,lab[split1],1);
+ for (cell1 = 0; cell1 < n; cell1 = cell2 + 1)
+ {
+ for (cell2 = cell1; ptn[cell2] > 0; ++cell2) {}
+ if (cell1 == cell2) continue;
+
+ c1 = cell1;
+ c2 = cell2;
+ while (c1 <= c2)
+ {
+ labc1 = lab[c1];
+ if (ISELEMENT1(gptr,labc1))
+ ++c1;
+ else
+ {
+ lab[c1] = lab[c2];
+ lab[c2] = labc1;
+ --c2;
+ }
+ }
+ if (c2 >= cell1 && c1 <= cell2)
+ {
+ ptn[c2] = 0;
+ ++*numcells;
+ lact |= bit[c1];
+ }
+ }
+ }
+
+ else /* nontrivial splitting cell */
+ {
+ workset = 0;
+ for (i = split1; i <= split2; ++i) workset |= bit[lab[i]];
+
+ for (cell1 = 0; cell1 < n; cell1 = cell2 + 1)
+ {
+ for (cell2 = cell1; ptn[cell2] > 0; ++cell2) {}
+ if (cell1 == cell2) continue;
+ i = cell1;
+ if ((x = workset & g[lab[i]]) != 0) cnt = POPCOUNT(x);
+ else cnt = 0;
+ count[i] = bmin = bmax = cnt;
+ bucket[cnt] = 1;
+ while (++i <= cell2)
+ {
+ if ((x = workset & g[lab[i]]) != 0)
+ cnt = POPCOUNT(x);
+ else
+ cnt = 0;
+
+ while (bmin > cnt) bucket[--bmin] = 0;
+ while (bmax < cnt) bucket[++bmax] = 0;
+ ++bucket[cnt];
+ count[i] = cnt;
+ }
+ if (bmin == bmax) continue;
+ c1 = cell1;
+ for (i = bmin; i <= bmax; ++i)
+ if (bucket[i])
+ {
+ c2 = c1 + bucket[i];
+ bucket[i] = c1;
+ if (c1 != cell1)
+ {
+ lact |= bit[c1];
+ ++*numcells;
+ }
+ if (c2 <= cell2) ptn[c2-1] = 0;
+ c1 = c2;
+ }
+ for (i = cell1; i <= cell2; ++i)
+ workperm[bucket[count[i]]++] = lab[i];
+ for (i = cell1; i <= cell2; ++i) lab[i] = workperm[i];
+ }
+ }
+
+ if (ptn[n-2] == 0)
+ {
+ if (lab[n-1] == n-1)
+ {
+ *code = 1;
+ if (goodret) return;
+ }
+ else
+ {
+ *code = -1;
+ return;
+ }
+ }
+ else
+ {
+ i = n - 1;
+ while (TRUE)
+ {
+ if (lab[i] == n-1) break;
+ --i;
+ if (ptn[i] == 0)
+ {
+ *code = -1;
+ return;
+ }
+ }
+ }
+ }
+}
+
+/**************************************************************************/
+
+static void
+makecanon(graph *g, graph *gcan, int n)
+/* gcan := canonise(g) */
+{
+ int lab[MAXN],ptn[MAXN],orbits[MAXN];
+ static DEFAULTOPTIONS_GRAPH(options);
+ setword workspace[50];
+
+ options.getcanon = TRUE;
+
+ nauty(g,lab,ptn,NULL,orbits,&options,&nauty_stats,
+ workspace,50,1,n,gcan);
+}
+
+/**************************************************************************/
+
+static boolean
+accept1(graph *g, int n, xword x, graph *gx, int *deg, boolean *rigid)
+/* decide if n in theta(g+x) - version for n+1 < maxn */
+{
+ int i;
+ int lab[MAXN],ptn[MAXN],orbits[MAXN];
+ int count[MAXN];
+ graph h[MAXN];
+ xword xw;
+ int nx,numcells,code;
+ int i0,i1,degn;
+ set active[MAXM];
+ statsblk stats;
+ static DEFAULTOPTIONS_GRAPH(options);
+ setword workspace[50];
+
+#ifdef INSTRUMENT
+ ++a1calls;
+#endif
+
+ nx = n + 1;
+ for (i = 0; i < n; ++i) gx[i] = g[i];
+ gx[n] = 0;
+ deg[n] = degn = XPOPCOUNT(x);
+
+ xw = x;
+ while (xw)
+ {
+ i = XNEXTBIT(xw);
+ xw ^= XBIT(i);
+ gx[i] |= bit[n];
+ gx[n] |= bit[i];
+ ++deg[i];
+ }
+
+#ifdef PREPRUNE
+ if (PREPRUNE(gx,n+1,maxn)) return FALSE;
+#endif
+ if (connec == 2 && n+2 == maxn && !isconnected(gx,n+1)) return FALSE;
+ if (((connec ==2 && n+2 < maxn) || (connec == 1 && n+2 <= maxn))
+ && connpreprune(gx,n+1,maxn)) return FALSE;
+
+ i0 = 0;
+ i1 = n;
+ for (i = 0; i < nx; ++i)
+ {
+ if (deg[i] == degn) lab[i1--] = i;
+ else lab[i0++] = i;
+ ptn[i] = 1;
+ }
+ ptn[n] = 0;
+ if (i0 == 0)
+ {
+ numcells = 1;
+ active[0] = bit[0];
+ }
+ else
+ {
+ numcells = 2;
+ active[0] = bit[0] | bit[i1+1];
+ ptn[i1] = 0;
+ }
+ refinex(gx,lab,ptn,0,&numcells,count,active,FALSE,&code,1,nx);
+
+ if (code < 0) return FALSE;
+
+ if (numcells == nx)
+ {
+ *rigid = TRUE;
+#ifdef INSTRUMENT
+ ++a1succs;
+#endif
+ return TRUE;
+ }
+
+ options.getcanon = TRUE;
+ options.defaultptn = FALSE;
+ options.userautomproc = userautomproc;
+
+ active[0] = 0;
+#ifdef INSTRUMENT
+ ++a1nauty;
+#endif
+ nauty(gx,lab,ptn,active,orbits,&options,&stats,workspace,50,1,nx,h);
+
+ if (orbits[lab[n]] == orbits[n])
+ {
+ *rigid = stats.numorbits == nx;
+#ifdef INSTRUMENT
+ ++a1succs;
+#endif
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+/**************************************************************************/
+
+static boolean
+accept1b(graph *g, int n, xword x, graph *gx, int *deg, boolean *rigid,
+ void (*makeh)(graph*,xword*,int))
+/* decide if n in theta(g+x) -- version for n+1 < maxn */
+{
+ int i,v;
+ xword z,hv,bitv,ixx;
+ int lab[MAXN],ptn[MAXN],orbits[MAXN];
+ int count[MAXN];
+ graph gc[MAXN];
+ xword h[MAXN],xw,jxx,kxx,*xx;
+ int nx,numcells,code;
+ int i0,i1,degn,xubx;
+ set active[MAXM];
+ statsblk stats;
+ static DEFAULTOPTIONS_GRAPH(options);
+ setword workspace[50];
+
+#ifdef INSTRUMENT
+ ++a1calls;
+#endif
+
+ nx = n + 1;
+ for (i = 0; i < n; ++i) gx[i] = g[i];
+ gx[n] = 0;
+ deg[n] = degn = XPOPCOUNT(x);
+
+ xw = x;
+ while (xw)
+ {
+ i = XNEXTBIT(xw);
+ xw ^= XBIT(i);
+ gx[i] |= bit[n];
+ gx[n] |= bit[i];
+ ++deg[i];
+ }
+
+#ifdef PREPRUNE
+ if (PREPRUNE(gx,n+1,maxn)) return FALSE;
+#endif
+ if (connec == 2 && n+2 == maxn && !isconnected(gx,n+1)) return FALSE;
+ if (((connec ==2 && n+2 < maxn) || (connec == 1 && n+2 <= maxe))
+ && connpreprune(gx,n+1,maxn)) return FALSE;
+
+ i0 = 0;
+ i1 = n;
+ for (i = 0; i < nx; ++i)
+ {
+ if (deg[i] == degn) lab[i1--] = i;
+ else lab[i0++] = i;
+ ptn[i] = 1;
+ }
+ ptn[n] = 0;
+ if (i0 == 0)
+ {
+ numcells = 1;
+ active[0] = bit[0];
+ }
+ else
+ {
+ numcells = 2;
+ active[0] = bit[0] | bit[i1+1];
+ ptn[i1] = 0;
+ }
+ refinex(gx,lab,ptn,0,&numcells,count,active,FALSE,&code,1,nx);
+
+ if (code < 0) return FALSE;
+
+ (*makeh)(gx,h,nx);
+ xx = data[nx].xx;
+ xubx = data[nx].xub;
+
+ xx[0] = 0;
+ kxx = 1;
+ for (v = 0; v < nx; ++v)
+ {
+ bitv = XBIT(v);
+ hv = h[v];
+ jxx = kxx;
+ for (ixx = 0; ixx < jxx; ++ixx)
+ if ((hv & xx[ixx]) == 0)
+ {
+ z = xx[ixx] | bitv;
+ if (XPOPCOUNT(z) <= xubx) xx[kxx++] = z;
+ }
+ }
+ data[nx].xlim = kxx;
+
+ if (numcells == nx)
+ {
+ *rigid = TRUE;
+#ifdef INSTRUMENT
+ ++a1succs;
+#endif
+ return TRUE;
+ }
+
+ options.getcanon = TRUE;
+ options.defaultptn = FALSE;
+ options.userautomproc = userautomprocb;
+
+ active[0] = 0;
+#ifdef INSTRUMENT
+ ++a1nauty;
+#endif
+ nauty(gx,lab,ptn,active,orbits,&options,&stats,workspace,50,1,nx,gc);
+
+ if (orbits[lab[n]] == orbits[n])
+ {
+ *rigid = stats.numorbits == nx;
+#ifdef INSTRUMENT
+ ++a1succs;
+#endif
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+/**************************************************************************/
+
+static boolean
+accept2(graph *g, int n, xword x, graph *gx, int *deg, boolean nuniq)
+/* decide if n in theta(g+x) -- version for n+1 == maxn */
+{
+ int i;
+ int lab[MAXN],ptn[MAXN],orbits[MAXN];
+ int degx[MAXN],invar[MAXN];
+ setword vmax,gv;
+ int qn,qv;
+ int count[MAXN];
+ xword xw;
+ int nx,numcells,code;
+ int degn,i0,i1,j,j0,j1;
+ set active[MAXM];
+ statsblk stats;
+ static DEFAULTOPTIONS_GRAPH(options);
+ setword workspace[50];
+ boolean cheapacc;
+
+#ifdef INSTRUMENT
+ ++a2calls;
+ if (nuniq) ++a2uniq;
+#endif
+ nx = n + 1;
+ for (i = 0; i < n; ++i)
+ {
+ gx[i] = g[i];
+ degx[i] = deg[i];
+ }
+ gx[n] = 0;
+ degx[n] = degn = XPOPCOUNT(x);
+
+ xw = x;
+ while (xw)
+ {
+ i = XNEXTBIT(xw);
+ xw ^= XBIT(i);
+ gx[i] |= bit[n];
+ gx[n] |= bit[i];
+ ++degx[i];
+ }
+
+#ifdef PREPRUNE
+ if (PREPRUNE(gx,n+1,maxn)) return FALSE;
+#endif
+ if (connec == 2 && n+2 == maxn && !isconnected(gx,n+1)) return FALSE;
+ if (((connec ==2 && n+2 < maxn) || (connec == 1 && n+2 <= maxe))
+ && connpreprune(gx,n+1,maxn)) return FALSE;
+
+ if (nuniq)
+ {
+#ifdef INSTRUMENT
+ ++a2succs;
+#endif
+ if (canonise) makecanon(gx,gcan,nx);
+ return TRUE;
+ }
+
+ i0 = 0;
+ i1 = n;
+ for (i = 0; i < nx; ++i)
+ {
+ if (degx[i] == degn) lab[i1--] = i;
+ else lab[i0++] = i;
+ ptn[i] = 1;
+ }
+ ptn[n] = 0;
+ if (i0 == 0)
+ {
+ numcells = 1;
+ active[0] = bit[0];
+
+ if (!distinvar(gx,invar,nx)) return FALSE;
+ qn = invar[n];
+ j0 = 0;
+ j1 = n;
+ while (j0 <= j1)
+ {
+ j = lab[j0];
+ qv = invar[j];
+ if (qv < qn)
+ ++j0;
+ else
+ {
+ lab[j0] = lab[j1];
+ lab[j1] = j;
+ --j1;
+ }
+ }
+ if (j0 > 0)
+ {
+ if (j0 == n)
+ {
+#ifdef INSTRUMENT
+ ++a2succs;
+#endif
+ if (canonise) makecanon(gx,gcan,nx);
+ return TRUE;
+ }
+ ptn[j1] = 0;
+ ++numcells;
+ active[0] |= bit[j0];
+ }
+ }
+ else
+ {
+ numcells = 2;
+ ptn[i1] = 0;
+ active[0] = bit[0] | bit[i1+1];
+
+ vmax = 0;
+ for (i = i1+1; i < nx; ++i) vmax |= bit[lab[i]];
+
+ gv = gx[n] & vmax;
+ qn = POPCOUNT(gv);
+
+ j0 = i1+1;
+ j1 = n;
+ while (j0 <= j1)
+ {
+ j = lab[j0];
+ gv = gx[j] & vmax;
+ qv = POPCOUNT(gv);
+ if (qv > qn)
+ return FALSE;
+ else if (qv < qn)
+ ++j0;
+ else
+ {
+ lab[j0] = lab[j1];
+ lab[j1] = j;
+ --j1;
+ }
+ }
+ if (j0 > i1+1)
+ {
+ if (j0 == n)
+ {
+#ifdef INSTRUMENT
+ ++a2succs;
+#endif
+ if (canonise) makecanon(gx,gcan,nx);
+ return TRUE;
+ }
+ ptn[j1] = 0;
+ ++numcells;
+ active[0] |= bit[j0];
+ }
+ }
+
+ refinex(gx,lab,ptn,0,&numcells,count,active,TRUE,&code,1,nx);
+
+ if (code < 0) return FALSE;
+
+ cheapacc = FALSE;
+ if (code > 0 || numcells >= nx-4)
+ cheapacc = TRUE;
+ else if (numcells == nx-5)
+ {
+ for (j1 = nx-2; j1 >= 0 && ptn[j1] > 0; --j1) {}
+ if (nx - j1 != 5) cheapacc = TRUE;
+ }
+ else
+ {
+ j1 = nx;
+ j0 = 0;
+ for (i1 = 0; i1 < nx; ++i1)
+ {
+ --j1;
+ if (ptn[i1] > 0)
+ {
+ ++j0;
+ while (ptn[++i1] > 0) {}
+ }
+ }
+ if (j1 <= j0 + 1) cheapacc = TRUE;
+ }
+
+ if (cheapacc)
+ {
+#ifdef INSTRUMENT
+ ++a2succs;
+#endif
+ if (canonise) makecanon(gx,gcan,nx);
+ return TRUE;
+ }
+
+ options.getcanon = TRUE;
+ options.defaultptn = FALSE;
+
+ active[0] = 0;
+#ifdef INSTRUMENT
+ ++a2nauty;
+#endif
+ nauty(gx,lab,ptn,active,orbits,&options,&stats,workspace,50,1,nx,gcan);
+
+ if (orbits[lab[n]] == orbits[n])
+ {
+#ifdef INSTRUMENT
+ ++a2succs;
+#endif
+ if (canonise) makecanon(gx,gcan,nx);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+/**************************************************************************/
+
+static void
+xbnds(int n, int ne, int dmax)
+/* find bounds on extension degree; store answer in data[*].* */
+{
+ int xlb,xub,d,nn,m,xc;
+
+ xlb = n == 1 ? 0 : (dmax > (2*ne + n - 2)/(n - 1) ?
+ dmax : (2*ne + n - 2)/(n - 1));
+ xub = n < maxdeg ? n : maxdeg;
+
+ for (xc = xub; xc >= xlb; --xc)
+ {
+ d = xc;
+ m = ne + d;
+ for (nn = n+1; nn < maxn; ++nn)
+ {
+ if (d < (2*m + nn - 2)/(nn - 1)) d = (2*m + nn - 2)/(nn - 1);
+ m += d;
+ }
+ if (d > maxdeg || m > maxe) xub = xc - 1;
+ else break;
+ }
+
+ if (ne + xlb < mine)
+ for (xc = xlb; xc <= xub; ++xc)
+ {
+ m = ne + xc;
+ for (nn = n + 1; nn < maxn; ++nn)
+ m += maxdeg < nn ? maxdeg : nn;
+ if (m < mine) xlb = xc + 1;
+ else break;
+ }
+
+ data[n].ne = ne;
+ data[n].dmax = dmax;
+ data[n].xlb = xlb;
+ data[n].xub = xub;
+}
+
+/**************************************************************************/
+
+static void
+spaextend(graph *g, int n, int *deg, int ne, boolean rigid,
+ int xlb, int xub, void (*makeh)(graph*,xword*,int))
+/* extend from n to n+1 -- version for restricted graphs */
+{
+ xword x,d,dlow;
+ xword xlim,*xorb;
+ int xc,nx,i,j,dmax,dcrit,xlbx,xubx;
+ graph gx[MAXN];
+ xword *xx,ixx;
+ int degx[MAXN];
+ boolean rigidx;
+
+#ifdef INSTRUMENT
+ boolean haschild;
+
+ haschild = FALSE;
+ if (rigid) ++rigidnodes[n];
+#endif
+ ++nodes[n];
+
+ nx = n + 1;
+ dmax = deg[n-1];
+ dcrit = mindeg - maxn + n;
+ d = dlow = 0;
+ for (i = 0; i < n; ++i)
+ {
+ if (deg[i] == dmax) d |= XBIT(i);
+ if (deg[i] == dcrit) dlow |= XBIT(i);
+ }
+
+ if (xlb == dmax && XPOPCOUNT(d) + dmax > n) ++xlb;
+ if (nx == maxn && xlb < mindeg) xlb = mindeg;
+ if (xlb > xub) return;
+
+#ifdef PRUNE
+ if (PRUNE(g,n,maxn)) return;
+#endif
+
+ xorb = data[n].xorb;
+ xx = data[n].xx;
+ xlim = data[n].xlim;
+
+ if (nx == maxn)
+ {
+ for (ixx = 0; ixx < xlim; ++ixx)
+ {
+ x = xx[ixx];
+ xc = XPOPCOUNT(x);
+ if (xc < xlb || xc > xub) continue;
+ if ((rigid || xorb[ixx] == ixx)
+ && (xc > dmax || (xc == dmax && (x & d) == 0))
+ && (dlow & ~x) == 0)
+ {
+ if (accept2(g,n,x,gx,deg,
+ xc > dmax+1 || (xc == dmax+1 && (x & d) == 0))
+ && (!connec ||
+ (connec==1 && isconnected(gx,nx)) ||
+ (connec>1 && isbiconnected(gx,nx))))
+ {
+#ifdef PRUNE
+ if (!PRUNE(gx,nx,maxn))
+#endif
+ {
+#ifdef INSTRUMENT
+ haschild = TRUE;
+#endif
+ ++ecount[ne+xc];
+ (*outproc)(outfile,canonise ? gcan : gx,nx);
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ for (ixx = 0; ixx < xlim; ++ixx)
+ {
+ if (nx == splitlevel)
+ {
+ if (odometer-- != 0) continue;
+ odometer = mod - 1;
+ }
+ x = xx[ixx];
+ xc = XPOPCOUNT(x);
+ if (xc < xlb || xc > xub) continue;
+ if ((rigid || xorb[ixx] == ixx)
+ && (xc > dmax || (xc == dmax && (x & d) == 0))
+ && (dlow & ~x) == 0)
+ {
+ for (j = 0; j < n; ++j) degx[j] = deg[j];
+ if (data[nx].ne != ne+xc || data[nx].dmax != xc)
+ xbnds(nx,ne+xc,xc);
+
+ xlbx = data[nx].xlb;
+ xubx = data[nx].xub;
+ if (xlbx <= xubx
+ && accept1b(g,n,x,gx,degx,&rigidx,makeh))
+ {
+#ifdef INSTRUMENT
+ haschild = TRUE;
+#endif
+ spaextend(gx,nx,degx,ne+xc,rigidx,xlbx,xubx,makeh);
+ }
+ }
+ }
+ if (n == splitlevel - 1 && n >= min_splitlevel
+ && nodes[n] >= multiplicity)
+ --splitlevel;
+ }
+#ifdef INSTRUMENT
+ if (haschild) ++fertilenodes[n];
+#endif
+}
+
+/**************************************************************************/
+
+static void
+genextend(graph *g, int n, int *deg, int ne, boolean rigid, int xlb, int xub)
+/* extend from n to n+1 -- version for general graphs */
+{
+ xword x,d,dlow;
+ xword *xset,*xcard,*xorb;
+ xword i,imin,imax;
+ int nx,xc,j,dmax,dcrit;
+ int xlbx,xubx;
+ graph gx[MAXN];
+ int degx[MAXN];
+ boolean rigidx;
+
+#ifdef INSTRUMENT
+ boolean haschild;
+
+ haschild = FALSE;
+ if (rigid) ++rigidnodes[n];
+#endif
+ ++nodes[n];
+
+ nx = n + 1;
+ dmax = deg[n-1];
+ dcrit = mindeg - maxn + n;
+ d = dlow = 0;
+ for (i = 0; i < n; ++i)
+ {
+ if (deg[i] == dmax) d |= XBIT(i);
+ if (deg[i] == dcrit) dlow |= XBIT(i);
+ }
+
+ if (xlb == dmax && XPOPCOUNT(d) + dmax > n) ++xlb;
+ if (nx == maxn && xlb < mindeg) xlb = mindeg;
+ if (xlb > xub) return;
+
+#ifdef PRUNE
+ if (PRUNE(g,n,maxn)) return;
+#endif
+
+ imin = data[n].xstart[xlb];
+ imax = data[n].xstart[xub+1];
+ xset = data[n].xset;
+ xcard = data[n].xcard;
+ xorb = data[n].xorb;
+
+ if (nx == maxn)
+ for (i = imin; i < imax; ++i)
+ {
+ if (!rigid && xorb[i] != i) continue;
+ x = xset[i];
+ xc = (int)xcard[i];
+ if (xc == dmax && (x & d) != 0) continue;
+ if ((dlow & ~x) != 0) continue;
+
+ if (accept2(g,n,x,gx,deg,
+ xc > dmax+1 || (xc == dmax+1 && (x & d) == 0)))
+ if (!connec || (connec==1 && isconnected(gx,nx))
+ || (connec>1 && isbiconnected(gx,nx)))
+ {
+#ifdef PRUNE
+ if (!PRUNE(gx,nx,maxn))
+#endif
+ {
+#ifdef INSTRUMENT
+ haschild = TRUE;
+#endif
+ ++ecount[ne+xc];
+ (*outproc)(outfile,canonise ? gcan : gx,nx);
+ }
+ }
+ }
+ else
+ for (i = imin; i < imax; ++i)
+ {
+ if (!rigid && xorb[i] != i) continue;
+ x = xset[i];
+ xc = (int)xcard[i];
+ if (xc == dmax && (x & d) != 0) continue;
+ if ((dlow & ~x) != 0) continue;
+ if (nx == splitlevel)
+ {
+ if (odometer-- != 0) continue;
+ odometer = mod - 1;
+ }
+
+ for (j = 0; j < n; ++j) degx[j] = deg[j];
+ if (data[nx].ne != ne+xc || data[nx].dmax != xc)
+ xbnds(nx,ne+xc,xc);
+ xlbx = data[nx].xlb;
+ xubx = data[nx].xub;
+ if (xlbx > xubx) continue;
+
+ data[nx].lo = data[nx].xstart[xlbx];
+ data[nx].hi = data[nx].xstart[xubx+1];
+ if (accept1(g,n,x,gx,degx,&rigidx))
+ {
+#ifdef INSTRUMENT
+ haschild = TRUE;
+#endif
+ genextend(gx,nx,degx,ne+xc,rigidx,xlbx,xubx);
+ }
+ }
+
+ if (n == splitlevel-1 && n >= min_splitlevel
+ && nodes[n] >= multiplicity)
+ --splitlevel;
+#ifdef INSTRUMENT
+ if (haschild) ++fertilenodes[n];
+#endif
+}
+
+/**************************************************************************/
+/**************************************************************************/
+
+#ifdef GENG_MAIN
+int
+GENG_MAIN(int argc, char *argv[])
+#else
+int
+main(int argc, char *argv[])
+#endif
+{
+ char *arg;
+ boolean badargs,gote,gotmr,gotf,gotd,gotD,gotx,gotX;
+ boolean secret,connec1,connec2,safe,sparse;
+ char *outfilename,sw;
+ int i,j,argnum;
+ graph g[1];
+ int tmaxe,deg[1];
+ nauty_counter nout;
+ int splitlevinc;
+ double t1,t2;
+ char msg[201];
+
+ HELP; PUTVERSION;
+ nauty_check(WORDSIZE,1,MAXN,NAUTYVERSIONID);
+
+ badargs = FALSE;
+ trianglefree = FALSE;
+ bipartite = FALSE;
+ squarefree = FALSE;
+ verbose = FALSE;
+ nautyformat = FALSE;
+ yformat = FALSE;
+ graph6 = FALSE;
+ sparse6 = FALSE;
+ savemem = FALSE;
+ nooutput = FALSE;
+ canonise = FALSE;
+ header = FALSE;
+ outfilename = NULL;
+ secret = FALSE;
+ safe = FALSE;
+ connec1 = connec2 = FALSE;
+
+ maxdeg = MAXN;
+ mindeg = 0;
+
+ gotX = gotx = gotd = gotD = gote = gotmr = gotf = FALSE;
+
+ argnum = 0;
+ for (j = 1; !badargs && j < argc; ++j)
+ {
+ arg = argv[j];
+ if (arg[0] == '-' && arg[1] != '\0')
+ {
+ ++arg;
+ while (*arg != '\0')
+ {
+ sw = *arg++;
+ SWBOOLEAN('n',nautyformat)
+ else SWBOOLEAN('u',nooutput)
+ else SWBOOLEAN('g',graph6)
+ else SWBOOLEAN('s',sparse6)
+ else SWBOOLEAN('t',trianglefree)
+ else SWBOOLEAN('f',squarefree)
+ else SWBOOLEAN('b',bipartite)
+ else SWBOOLEAN('v',verbose)
+ else SWBOOLEAN('l',canonise)
+ else SWBOOLEAN('y',yformat)
+ else SWBOOLEAN('h',header)
+ else SWBOOLEAN('m',savemem)
+ else SWBOOLEAN('c',connec1)
+ else SWBOOLEAN('C',connec2)
+ else SWBOOLEAN('q',quiet)
+ else SWBOOLEAN('$',secret)
+ else SWBOOLEAN('S',safe)
+ else SWINT('d',gotd,mindeg,"geng -d")
+ else SWINT('D',gotD,maxdeg,"geng -D")
+ else SWINT('x',gotx,multiplicity,"geng -x")
+ else SWINT('X',gotX,splitlevinc,"geng -X")
+#ifdef PLUGIN_SWITCHES
+PLUGIN_SWITCHES
+#endif
+ else badargs = TRUE;
+ }
+ }
+ else if (arg[0] == '-' && arg[1] == '\0')
+ gotf = TRUE;
+ else
+ {
+ if (argnum == 0)
+ {
+ if (sscanf(arg,"%d",&maxn) != 1) badargs = TRUE;
+ ++argnum;
+ }
+ else if (gotf)
+ badargs = TRUE;
+ else
+ {
+ if (!gotmr)
+ {
+ if (sscanf(arg,"%d/%d",&res,&mod) == 2)
+ {
+ gotmr = TRUE;
+ continue;
+ }
+ }
+ if (!gote)
+ {
+ if (sscanf(arg,"%d:%d",&mine,&maxe) == 2
+ || sscanf(arg,"%d-%d",&mine,&maxe) == 2)
+ {
+ gote = TRUE;
+ if (maxe == 0 && mine > 0) maxe = MAXN*(MAXN-1)/2;
+ continue;
+ }
+ else if (sscanf(arg,"%d",&mine) == 1)
+ {
+ gote = TRUE;
+ maxe = mine;
+ continue;
+ }
+ }
+ if (!gotf)
+ {
+ outfilename = arg;
+ gotf = TRUE;
+ continue;
+ }
+ }
+ }
+ }
+
+ if (argnum == 0)
+ badargs = TRUE;
+ else if (maxn < 1 || maxn > MAXN)
+ {
+ fprintf(stderr,">E geng: n must be in the range 1..%d\n",MAXN);
+ badargs = TRUE;
+ }
+
+ if (!gotmr)
+ {
+ mod = 1;
+ res = 0;
+ }
+
+ if (!gote)
+ {
+ mine = 0;
+ maxe = (maxn*maxn - maxn) / 2;
+ }
+
+ if (connec1 && mindeg < 1 && maxn > 1) mindeg = 1;
+ if (connec2 && mindeg < 2 && maxn > 2) mindeg = 2;
+ if (maxdeg >= maxn) maxdeg = maxn - 1;
+ if (maxe > maxn*maxdeg / 2) maxe = maxn*maxdeg / 2;
+ if (maxdeg > maxe) maxdeg = maxe;
+ if (mindeg < 0) mindeg = 0;
+ if (mine < (maxn*mindeg+1) / 2) mine = (maxn*mindeg+1) / 2;
+ if (maxdeg > 2*maxe - mindeg*(maxn-1)) maxdeg = 2*maxe - mindeg*(maxn-1);
+
+ if (connec2) connec = 2;
+ else if (connec1) connec = 1;
+ else connec = 0;
+ if (connec && mine < maxn-1) mine = maxn - 2 + connec;
+
+#if defined(PRUNE) || defined(PREPRUNE)
+ geng_mindeg = mindeg;
+ geng_maxdeg = maxdeg;
+ geng_mine = mine;
+ geng_maxe = maxe;
+ geng_connec = connec;
+#endif
+
+ if (!badargs && (mine > maxe || maxe < 0 || maxdeg < 0))
+ {
+ fprintf(stderr,
+ ">E geng: impossible mine,maxe,mindeg,maxdeg values\n");
+ badargs = TRUE;
+ }
+
+ if (!badargs && (res < 0 || res >= mod))
+ {
+ fprintf(stderr,">E geng: must have 0 <= res < mod\n");
+ badargs = TRUE;
+ }
+
+ if (badargs)
+ {
+ fprintf(stderr,">E Usage: %s\n",USAGE);
+ GETHELP;
+ exit(1);
+ }
+
+ if ((nautyformat!=0) + (yformat!=0) + (graph6!=0)
+ + (sparse6!=0) + (nooutput!=0) > 1)
+ gt_abort(">E geng: -uyngs are incompatible\n");
+
+#ifdef OUTPROC
+ outproc = OUTPROC;
+#else
+ if (nautyformat) outproc = writenauty;
+ else if (yformat) outproc = writeny;
+ else if (nooutput) outproc = nullwrite;
+ else if (sparse6) outproc = writes6x;
+ else outproc = writeg6x;
+#endif
+
+#ifdef PLUGIN_INIT
+PLUGIN_INIT
+#endif
+
+ for (i = 0; i <= maxe; ++i) ecount[i] = 0;
+ for (i = 0; i < maxn; ++i) nodes[i] = 0;
+
+ if (nooutput)
+ outfile = stdout;
+ else if (!gotf || outfilename == NULL)
+ {
+ outfilename = "stdout";
+ outfile = stdout;
+ }
+ else if ((outfile = fopen(outfilename,
+ nautyformat ? "wb" : "w")) == NULL)
+ {
+ fprintf(stderr,
+ ">E geng: can't open %s for writing\n",outfilename);
+ gt_abort(NULL);
+ }
+
+ if (bipartite)
+ if (squarefree) tmaxe = findmaxe(maxebf,maxn);
+ else tmaxe = findmaxe(maxeb,maxn);
+ else if (trianglefree)
+ if (squarefree) tmaxe = findmaxe(maxeft,maxn);
+ else tmaxe = findmaxe(maxet,maxn);
+ else if (squarefree) tmaxe = findmaxe(maxef,maxn);
+ else tmaxe = (maxn*maxn - maxn) / 2;
+
+ if (safe) ++tmaxe;
+
+ if (maxe > tmaxe) maxe = tmaxe;
+
+ if (gotx)
+ {
+ if (multiplicity < 3 * mod || multiplicity > 999999999)
+ gt_abort(">E geng: -x value must be in [3*mod,10^9-1]\n");
+ }
+ else
+ {
+ multiplicity = PRUNEMULT * mod;
+ if (multiplicity / PRUNEMULT != mod)
+ gt_abort(">E geng: mod value is too large\n");
+ }
+
+ if (!gotX) splitlevinc = 0;
+
+ if (!quiet)
+ {
+ msg[0] = '\0';
+ if (strlen(argv[0]) > 75)
+ fprintf(stderr,">A %s",argv[0]);
+ else
+ CATMSG1(">A %s",argv[0]);
+
+ CATMSG6(" -%s%s%s%s%s%s",
+ connec2 ? "C" : connec1 ? "c" : "",
+ trianglefree ? "t" : "",
+ squarefree ? "f" : "",
+ bipartite ? "b" : "",
+ canonise ? "l" : "",
+ savemem ? "m" : "");
+ if (mod > 1)
+ CATMSG2("X%dx%d",splitlevinc,multiplicity);
+ CATMSG4("d%dD%d n=%d e=%d",mindeg,maxdeg,maxn,mine);
+ if (maxe > mine) CATMSG1("-%d",maxe);
+ if (mod > 1) CATMSG2(" class=%d/%d",res,mod);
+ CATMSG0("\n");
+ fputs(msg,stderr);
+ fflush(stderr);
+ }
+
+ g[0] = 0;
+ deg[0] = 0;
+
+ sparse = bipartite || squarefree || trianglefree || savemem;
+
+ t1 = CPUTIME;
+
+ if (header)
+ {
+ if (sparse6)
+ {
+ writeline(outfile,SPARSE6_HEADER);
+ fflush(outfile);
+ }
+ else if (!yformat && !nautyformat && !nooutput)
+ {
+ writeline(outfile,GRAPH6_HEADER);
+ fflush(outfile);
+ }
+ }
+
+ if (maxn == 1)
+ {
+ if (res == 0 && connec < 2)
+ {
+ ++ecount[0];
+ (*outproc)(outfile,g,1);
+ }
+ }
+ else
+ {
+ if (maxn > 28 || maxn+4 > 8*sizeof(xword))
+ savemem = sparse = TRUE;
+ if (maxn == maxe+1 && connec)
+ bipartite = squarefree = sparse = TRUE; /* trees */
+
+ makeleveldata(sparse);
+
+ if (maxn >= 14 && mod > 1) splitlevel = maxn - 4;
+ else if (maxn >= 6 && mod > 1) splitlevel = maxn - 3;
+ else splitlevel = -1;
+
+ if (splitlevel > 0) splitlevel += splitlevinc;
+ if (splitlevel > maxn - 1) splitlevel = maxn - 1;
+ if (splitlevel < 3) splitlevel = -1;
+
+ min_splitlevel = 6;
+ odometer = secret ? -1 : res;
+
+ if (maxe >= mine &&
+ (mod <= 1 || (mod > 1 && (splitlevel > 2 || res == 0))))
+ {
+ xbnds(1,0,0);
+ if (sparse)
+ {
+ data[1].xx[0] = 0;
+ if (maxdeg > 0) data[1].xx[1] = XBIT(0);
+ data[1].xlim = data[1].xub + 1;
+ }
+
+ if (bipartite)
+ if (squarefree)
+ spaextend(g,1,deg,0,TRUE,
+ data[1].xlb,data[1].xub,makeb6graph);
+ else
+ spaextend(g,1,deg,0,TRUE,
+ data[1].xlb,data[1].xub,makebgraph);
+ else if (trianglefree)
+ if (squarefree)
+ spaextend(g,1,deg,0,TRUE,
+ data[1].xlb,data[1].xub,makeg5graph);
+ else
+ spaextend(g,1,deg,0,TRUE,
+ data[1].xlb,data[1].xub,makexgraph);
+ else if (squarefree)
+ spaextend(g,1,deg,0,TRUE,
+ data[1].xlb,data[1].xub,makesgraph);
+ else if (savemem)
+ spaextend(g,1,deg,0,TRUE,
+ data[1].xlb,data[1].xub,make0graph);
+ else
+ genextend(g,1,deg,0,TRUE,data[1].xlb,data[1].xub);
+ }
+ }
+ t2 = CPUTIME;
+
+ nout = 0;
+ for (i = 0; i <= maxe; ++i) nout += ecount[i];
+
+ if (verbose)
+ {
+ for (i = 0; i <= maxe; ++i)
+ if (ecount[i] != 0)
+ {
+ fprintf(stderr,">C " COUNTER_FMT " graphs with %d edges\n",
+ ecount[i],i);
+ }
+ }
+
+#ifdef INSTRUMENT
+ fprintf(stderr,"\n>N node counts\n");
+ for (i = 1; i < maxn; ++i)
+ {
+ fprintf(stderr," level %2d: ",i);
+ fprintf(stderr,COUNTER_FMT " (" COUNTER_FMT
+ " rigid, " COUNTER_FMT " fertile)\n",
+ nodes[i],rigidnodes[i],fertilenodes[i]);
+ }
+ fprintf(stderr,">A1 " COUNTER_FMT " calls to accept1, "
+ COUNTER_FMT " nauty, " COUNTER_FMT " succeeded\n",
+ a1calls,a1nauty,a1succs);
+ fprintf(stderr,">A2 " COUNTER_FMT " calls to accept2, " COUNTER_FMT
+ " nuniq, "COUNTER_FMT " nauty, " COUNTER_FMT " succeeded\n",
+ a2calls,a2uniq,a2nauty,a2succs);
+ fprintf(stderr,"\n");
+#endif
+
+#ifdef SUMMARY
+ SUMMARY(nout,t2-t1);
+#endif
+
+ if (!quiet)
+ {
+ fprintf(stderr,">Z " COUNTER_FMT " graphs generated in %3.2f sec\n",
+ nout,t2-t1);
+ }
+
+#ifdef GENG_MAIN
+ for (i = 1; i < maxn; ++i)
+ if (sparse)
+ {
+ free(data[i].xorb);
+ free(data[i].xx);
+ }
+ else
+ {
+ free(data[i].xorb);
+ free(data[i].xset);
+ free(data[i].xinv);
+ free(data[i].xcard);
+ }
+ return 0;
+#else
+ exit(0);
+#endif
+}
diff --git a/src/planarity.c b/src/planarity.c
index 9047f67..3cd5cbd 100644
--- a/src/planarity.c
+++ b/src/planarity.c
@@ -1,10386 +1,10386 @@
-/* planarity.c - code for planarity testing of undirected graphs.
- * Method of Boyer and Myrvold, programmed by Paulette Lieby.
- * The copyright of this program is owned by the Magma project.
- * Distributed with nauty by permission.
- ***************************************************************/
-
-/*
- * sparseg_adjl.c
- */
-
-/*
- What:
- *****
-
- Implementing:
-
- Some high-level functions on the sparse graph as
- an adjacency list.
- In particular, testing if it is planar.
-
-
-
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++
- authors:
- ********
-
- Paulette Lieby (Magma), Brendan McKay (ANU)
-
- Started October 2001
-*/
-
-#include "planarity.h"
-
-#define IF_DEB(x) {}
-#define IF_VERB(x) {}
-
-
-
-/* aproto: header embed_graph_protos.h */
-
-
-#ifndef PLANAR_IN_MAGMA
-#endif
-
-
-boolean
-sparseg_adjl_plan_and_iso (t_ver_sparse_rep *V, int n, t_adjl_sparse_rep *A,
- int e, int *c, t_ver_sparse_rep **VR, t_adjl_sparse_rep **AR,
- t_embed_sparse_rep **ER, int *nbr_e_obs)
- /*
- the input graph is given as an adjacency list:
- V: array of vertices
- n: size of graph
- A: adjacency list
- e: number of edges
-
- if the graph is planar the embedding is stored in VR and ER;
- the embedding contains e edges
- (nbr_e_obs not used)
-
- if the graph is non planar the obstruction is returned in
- VR and AR together with the number of edges in nbr_e_obs
-
- in all cases is also returned the number of components (in c)
- */
-{
- t_dlcl **dfs_tree, **back_edges, **mult_edges;
- int edge_pos, v, w;
- boolean ans;
- t_ver_edge *embed_graph;
-
- ans = sparseg_adjl_is_planar(V, n, A, c,
- &dfs_tree, &back_edges, &mult_edges,
- &embed_graph, &edge_pos, &v, &w);
-
- if (!ans)
- {
- embedg_obstruction(V, A, dfs_tree, back_edges,
- embed_graph, n, &edge_pos,
- v, w, VR, AR, nbr_e_obs);
- }
- else
- {
- embedg_embedding(V, A, embed_graph, n, e, *c, edge_pos, mult_edges,
- VR, ER);
- }
-
- sparseg_dlcl_delete(dfs_tree, n);
- sparseg_dlcl_delete(back_edges, n);
- sparseg_dlcl_delete(mult_edges, n);
- embedg_VES_delete(embed_graph, n);
-
- return ans;
-}
-
-
-
-int *
-sparseg_adjl_footprint (t_ver_sparse_rep *V, int n,
- t_adjl_sparse_rep *A, int v)
- /*
- return v's footprint:
- an array fp of size n where fp[i] = index of (directed)
- edge [v, i] in A
- */
-{
- /*
- note that we won't initialise the array:
- its subsequent usage doesn't require it
- */
- int *fp, e;
-
- fp = (int *) mem_malloc(sizeof(int) * n);
-
- if (V[v].first_edge == NIL)
- /*
- do nothing
- */
- return fp;
-
- e = V[v].first_edge;
- while (e != NIL)
- {
- fp[A[e].end_vertex] = e;
- e = A[e].next;
- }
-
- return fp;
-}
-
-
-void
-sparseg_adjl_print (t_ver_sparse_rep *V, int n,
- t_adjl_sparse_rep *A, boolean user_level)
-{
- int v;
-
- for (v = 0; v < n; v++)
- {
- int next;
-
- if (user_level)
- fprintf(stdout, "%d:\t", v + 1);
- else
- fprintf(stdout, "%d:\t", v);
-
- next = V[v].first_edge;
- while (next != NIL)
- {
- if (user_level)
- fprintf(stdout, "%d ", A[next].end_vertex + 1);
- else
- fprintf(stdout, "%d ", A[next].end_vertex);
-
- next = A[next].next;
- }
- fprintf(stdout, "\n");
- }
-}
-
-
-
-
-void
-sparseg_adjl_embed_print (t_ver_sparse_rep *V_e, int n,
- t_adjl_sparse_rep *A, t_embed_sparse_rep *E, boolean user_level)
- /*
- print the embedding given by E,
- edges are referred to by their index in A
-
- and V_e[v].first_edge is the index in E of the first edge
- (in the embedding's order) incident from v
-
- note that E is NOT indexed by the same vertices' array
- that indexes A (at the creation of the sparse graph)
- */
-{
- int v;
-
- for (v = 0; v < n; v++)
- {
- int start, next;
-
- if (user_level)
- fprintf(stdout, "%d:\t", v + 1);
- else
- fprintf(stdout, "%d:\t", v);
-
- if (V_e[v].first_edge == NIL)
- {
- fprintf(stdout, "\n");
- continue;
- }
- start = next = V_e[v].first_edge;
-
- if (user_level)
- fprintf(stdout, "%d ", A[ E[next].in_adjl ].end_vertex + 1);
- else
- fprintf(stdout, "%d ", A[ E[next].in_adjl ].end_vertex);
-
- next = E[next].next;
-
- while (next != start)
- /*
- recall that in E edges are linked into a circular list
- */
- {
- if (user_level)
- fprintf(stdout, "%d ", A[ E[next].in_adjl ].end_vertex + 1);
- else
- fprintf(stdout, "%d ", A[ E[next].in_adjl ].end_vertex);
-
- next = E[next].next;
- }
- fprintf(stdout, "\n");
- }
-}
-
-graph *
-sparseg_adjl_to_nauty_graph (t_ver_sparse_rep *V, int n, t_adjl_sparse_rep *A)
- /*
- write the sparse graph as a nauty graph
- */
-{
- int m, v, e, i;
- graph *g;
-
- m = (n + WORDSIZE - 1) / WORDSIZE;
- g = (graph *) mem_malloc(n * m * sizeof(graph));
- for (i = (long) m * n; --i >= 0;)
- g[i] = 0;
-
- /*
- we first copy V and A's information into g
- */
- for (v = 0; v < n; v++)
- {
- e = V[v].first_edge;
- while (e != NIL)
- /*
- A[e].end_vertex is the next neighbour in the list,
- A[e].next points to the next edge in the list
- */
- {
- if (A[e].end_vertex != v) /* no loops */
- {
- ADDELEMENT(GRAPHROW(g, v, m), A[e].end_vertex);
- }
- e = A[e].next;
- }
- }
-
- return g;
-}
-
-
-
-#if 0
-t_edge_sparse_rep *
-sparseg_adjl_edges (t_ver_sparse_rep *V, int n,
- t_adjl_sparse_rep *A, int e, boolean digraph)
- /*
- e is the number of edges
- */
-{
- t_edge_sparse_rep *edges;
- int m, u, v, pos_e;
- graph *g;
-
- edges = (t_edge_sparse_rep *) mem_malloc(sizeof(t_edge_sparse_rep) * e);
-
- m = (n + WORDSIZE - 1) / WORDSIZE;
- g = sparseg_adjl_to_nauty_graph(V, n, A);
-
- pos_e = 0;
- for (u = 0; u < n; u++)
- {
- v = digraph == TRUE ? 0 : u + 1;
- for (; v < n; v++)
- {
- if (ISELEMENT(GRAPHROW(g, u, m), v))
- {
- t_edge_sparse_rep edge;
-
- edge.ends[0] = u;
- edge.ends[1] = v;
- edges[pos_e++] = edge;
- }
- }
- }
- ASSERT(pos_e == e);
- mem_free(g);
-
- return edges;
-}
-#endif
-
-
-
-t_edge_sparse_rep *
-sparseg_adjl_edges (t_ver_sparse_rep *V, int n, t_adjl_sparse_rep *A,
- int e, boolean digraph)
- /*
- e is the number of edges
- */
-{
-#if 0
- t_edge_sparse_rep *edges;
- int u, v, pos_e, *loops, *foot_print;
- graph *g;
-
- loops = (int *) mem_malloc(sizeof(int) * n);
- for (v = 0; v < n; v++)
- {
- loops[v] = 0;
- }
-
- edges = (t_edge_sparse_rep *) mem_malloc(sizeof(t_edge_sparse_rep) * e);
- pos_e = 0;
-
- foot_print = (int *) mem_malloc(sizeof(int) * n);
- for (u = 0; u < n; u++)
- foot_print[u] = NIL;
-
- for (v = 0; v < n; v++)
- {
- int ne;
- t_edge_sparse_rep edge;
-
- ne = V[v].first_edge;
- while (ne != NIL)
- {
- u = A[ne].end_vertex;
- if (digraph
- || (!digraph && u > v))
- {
- foot_print[u] = v;
- }
- else if (!digraph && u == v)
- {
- if (loops[v] == 0)
- {
- foot_print[u] = v;
- }
-
- loops[v] ^= 1;
- }
-
- ne = A[ne].next;
- }
-
- for (u = 0; u < n; u++)
- if (foot_print[u] == v)
- {
- edge.ends[0] = v;
- edge.ends[1] = u;
- edges[pos_e++] = edge;
- }
- }
- ASSERT(pos_e == e);
- mem_free(loops);
- mem_free(foot_print);
-
- return edges;
-
-#endif
- /*
- there must be a simpler way
- */
-#if 0
- typedef struct edge_list {
- int size;
- t_edge_sparse_rep *edges;
- } t_edge_list;
-
- t_edge_list *edge_table;
- t_edge_sparse_rep *edges;
- int u, v, nbr_e, pos_e, *loops;
- graph *g;
-
- loops = (int *) mem_malloc(sizeof(int) * n);
- for (v = 0; v < n; v++)
- {
- loops[v] = 0;
- }
-
- /*
- now create an edge table as follows:
- - there are n lists in total
- - their respective size is given by size
- - their contents by *edges:
-
- edge_table[i] will contain all the edges whose end-point is i:
- these edges, by construction, will be sorted according to their
- starting point
-
- what for? to finish off each start-vertex processing
- with a bucket sort so that
- the edges are sorted wrt start- & end-point
-
- bucket sort is linear, hence why...
- */
- edge_table = (t_edge_list *) mem_malloc(sizeof(t_edge_list) * n);
- for (v = 0; v < n; v++)
- {
- edge_table[v].size = 0;
- edge_table[v].edges = NP;
- }
-
- edges = (t_edge_sparse_rep *) mem_malloc(sizeof(t_edge_sparse_rep) * e);
-
- nbr_e = 0;
- pos_e = 0;
- for (v = 0; v < n; v++)
- {
- int ne, w, u;
-
- ne = V[v].first_edge;
- while (ne != NIL)
- {
- u = A[ne].end_vertex;
- if (digraph
- || (!digraph && u > v))
- {
- t_edge_sparse_rep edge;
-
- edge.ends[0] = v;
- edge.ends[1] = u;
-
- /*
- now stick this edge into the table: one may ponder
- as to the cost of constantly reallocating memory...
- some cursory tests in another context tell me that
- this is pretty much ok
- (and certainly better than allocating n^2 storage space)
- */
- if (edge_table[u].size == 0)
- {
- edge_table[u].edges = (t_edge_sparse_rep *)
- mem_malloc(sizeof(t_edge_sparse_rep));
- }
- else
- {
- edge_table[u].edges = (t_edge_sparse_rep *)
- mem_realloc(edge_table[u].edges,
- sizeof(t_edge_sparse_rep)
- * (edge_table[u].size + 1));
- }
-
- (edge_table[u].edges)[edge_table[u].size] = edge;
- edge_table[u].size += 1;
- nbr_e++;
- }
- else if (!digraph && u == v)
- {
- if (loops[v] == 0)
- {
- t_edge_sparse_rep edge;
-
- edge.ends[0] = v;
- edge.ends[1] = u;
-
- if (edge_table[u].size == 0)
- {
- edge_table[u].edges = (t_edge_sparse_rep *)
- mem_malloc(sizeof(t_edge_sparse_rep));
- }
- else
- {
- edge_table[u].edges = (t_edge_sparse_rep *)
- mem_realloc(edge_table[u].edges,
- sizeof(t_edge_sparse_rep)
- * (edge_table[u].size + 1));
- }
-
- (edge_table[u].edges)[edge_table[u].size] = edge;
- edge_table[u].size += 1;
- nbr_e++;
- }
-
- loops[v] ^= 1;
- }
-
- ne = A[ne].next;
- }
-
- /*
- bucket sort must take place here:
- of course the whole lot is not exactly linear!
- since we perform the sort n times; but we can hope for
- a "good" ?? average behaviour:
-
- in any case this must be better that checking adjacencies
- n^2 times in a sparse rep. (see edge_set_iset_assure)
- */
- for (w = 0; w < n; w++)
- {
- if (edge_table[w].size > 0)
- {
- for (u = 0; u < edge_table[w].size; u++)
- {
- ASSERT((edge_table[w].edges)[u].ends[0] == v);
- edges[pos_e++] = (edge_table[w].edges)[u];
- }
- mem_free(edge_table[w].edges);
- edge_table[w].size = 0;
- edge_table[w].edges = NP;
- }
- }
- }
- ASSERT(nbr_e == e);
- ASSERT(pos_e == e);
- mem_free(loops);
- mem_free(edge_table);
-
- return edges;
-#endif
-
- t_edge_sparse_rep *edges;
- int v, pos_e, *loops;
-
- edges = (t_edge_sparse_rep *) mem_malloc(sizeof(t_edge_sparse_rep) * e);
- loops = (int *) mem_malloc(sizeof(int) * n);
- for (v = 0; v < n; v++)
- {
- loops[v] = 0;
- }
-
- pos_e = 0;
- for (v = 0; v < n; v++)
- {
- int ne;
-
- ne = V[v].first_edge;
- while (ne != NIL)
- {
- int u;
-
- u = A[ne].end_vertex;
- if (digraph
- || (!digraph && u > v))
- {
- t_edge_sparse_rep edge;
-
- edge.ends[0] = v;
- edge.ends[1] = u;
- edges[pos_e++] = edge;
- }
- else if (!digraph && u == v)
- {
- if (loops[v] == 0)
- {
- t_edge_sparse_rep edge;
-
- edge.ends[0] = v;
- edge.ends[1] = u;
- edges[pos_e++] = edge;
- }
-
- loops[v] ^= 1;
- }
- ne = A[ne].next;
- }
- }
- ASSERT(pos_e == e);
- mem_free(loops);
-
- return edges;
-
-}
-
-/*
- * sparseg_adjl_modify.c
- */
-
-/*
- What:
- *****
-
- Implementing:
-
- Some high-level functions on the sparse graph as
- an adjacency list.
- In particular, adding/removing vertices/edges.
-
-
- NOTE: Most of the functions implicitely assume that the
- graph is undirected;
- this must be slightly rewritten for the general case
- -- just haven't got the time right now...
-
-
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++
- authors:
- ********
-
- Paulette Lieby (Magma), Brendan McKay (ANU)
-
- Started October 2001
-*/
-
-#include "planarity.h"
-
-#define IF_DEB(x) {}
-#define IF_VERB(x) {}
-
-
-/* aproto: header embed_graph_protos.h */
-
-
-
-#ifndef PLANAR_IN_MAGMA
-#endif
-
-
-
-boolean
-sparseg_adjl_add_edge (t_ver_sparse_rep *V, int n, t_adjl_sparse_rep **A,
- int *size_A, int *pos, int u, int v, boolean CHECK)
- /*
- add the UNDIRECTED edge to the sparse graph (V, n, A)
- - pos records where to add the next edge in A
- - if pos + 1 == size_A, we must extend A
-
- we check if the edge is already in the graph iff CHECK true
-
- also we assume that the graph (V, n, A) is undirected
- */
-{
- boolean edge_exists;
-
- edge_exists = FALSE;
- if (CHECK)
- {
- edge_exists = sparseg_adjl_dir_edge_exists(V, n, *A, u, v);
-
- if (edge_exists)
- return FALSE;
- }
-
- if (*pos == *size_A)
- {
- IF_DEB(
- fprintf(stdout, "realloc \n");
- )
-
- *size_A += 2; /* add two directed edges */
- *A = (t_adjl_sparse_rep *)
- mem_realloc(*A, sizeof(t_adjl_sparse_rep) * *size_A);
- }
- else if (*pos + 1 == *size_A)
- {
- IF_DEB(
- fprintf(stdout, "realloc \n");
- )
-
- *size_A += 1; /* add two directed edges */
- *A = (t_adjl_sparse_rep *)
- mem_realloc(*A, sizeof(t_adjl_sparse_rep) * *size_A);
- }
- ASSERT(*pos + 1 < *size_A);
-
- sparseg_adjl_add_dir_edge(V, n, A, size_A, pos, u, v, FALSE);
- sparseg_adjl_add_dir_edge(V, n, A, size_A, pos, v, u, FALSE);
-
- return TRUE;
-}
-
-boolean
-sparseg_adjl_add_edge_no_extend (t_ver_sparse_rep *V, int n,
- t_adjl_sparse_rep *A, int size_A, int *pos, int u, int v, boolean CHECK)
- /*
- like sparseg_adjl_add_edge but here we are guaranteed
- that pos + 1 < size_A
- (unless that for some reason we attempt to add
- an edge which is already there)
-
- this feature is required when A is part of a Magma block:
- we do not want to reallocate A here
- (would be done at a higher level)
-
- we check if the edge is already in the graph iff CHECK true
-
- also, we assume that we use this procedur only when dealing
- with an undirected graph
- */
-{
- boolean edge_added;
-
- edge_added =
- sparseg_adjl_add_dir_edge_no_extend(V, n, A, size_A, pos, u, v,
- CHECK);
-
- if (edge_added)
- sparseg_adjl_add_dir_edge_no_extend(V, n, A, size_A, pos, v, u,
- FALSE);
-
- return edge_added;
-}
-
-
-boolean
-sparseg_adjl_add_dir_edge (t_ver_sparse_rep *V, int n,
- t_adjl_sparse_rep **A, int *size_A, int *pos, int u, int v,
- boolean CHECK)
- /*
- add the DIRECTED edge to the sparse graph (V, n, A)
- - pos records where to add the next edge in A
- - if pos >= size_A, we must extend A
-
- we check if the edge is already in the graph iff CHECK true
- */
-{
- boolean edge_exists;
-
- edge_exists = FALSE;
- if (CHECK)
- {
- edge_exists = sparseg_adjl_dir_edge_exists(V, n, *A, u, v);
-
- if (edge_exists)
- return FALSE;
- }
-
- if (*pos == *size_A)
- {
- *size_A += 1; /* add one directed edge */
- *A = (t_adjl_sparse_rep *)
- mem_realloc(*A, sizeof(t_adjl_sparse_rep) * *size_A);
- }
- ASSERT(*pos < *size_A);
-
- sparseg_adjl_add_dir_edge_no_extend(V, n, *A, *size_A, pos, u, v,
- FALSE);
-
- return TRUE;
-}
-
-boolean
-sparseg_adjl_add_dir_edge_no_extend (t_ver_sparse_rep *V, int n,
- t_adjl_sparse_rep *A, int size_A, int *pos, int u, int v, boolean CHECK)
- /*
- add an edge where A is guaranteed to be be big enough
- (unless that for some reason we attempt to add
- an edge which is already there)
-
- this feature is required when A is part of a Magma block:
- we do not want to reallocate A here
- (would be done at a higher level)
-
- we check if the edge is already in the graph iff CHECK true
- */
-{
- /*
- given the way V and A represent the graph, it is simplest
- to add the new edge at the beginning of i's adj. list
- */
- int i_v;
- t_adjl_sparse_rep a;
-
- if (CHECK && sparseg_adjl_dir_edge_exists(V, n, A, u, v))
- return FALSE;
-
- if (*pos >= size_A)
- DIE();
-
- /*
- otherwise always add the edge
- */
- i_v = *pos;
- a.end_vertex = v;
- a.next = V[u].first_edge;
- A[(*pos)++] = a;
- V[u].first_edge = i_v;
-
- return TRUE;
-}
-
-
-
-boolean
-sparseg_adjl_remove_edge_no_red (t_ver_sparse_rep *V, t_adjl_sparse_rep *A,
- int u, int v)
- /*
- remove the UNDIRECTED edge from sparse graph (V, A)
- if (u, v) is not an edge then nothing changes (and return FALSE)
-
- A will be left with "holes"
- */
-{
- sparseg_adjl_remove_dir_edge_no_red(V, A, u, v);
- return sparseg_adjl_remove_dir_edge_no_red(V, A, v, u);
-}
-
-
-boolean
-sparseg_adjl_remove_dir_edge_no_red (t_ver_sparse_rep *V,
- t_adjl_sparse_rep *A, int u, int v)
- /*
- remove the DIRECTED edge from the sparse graph (V, n, A)
- if (u, v) is not an edge then nothing changes (and return FALSE)
-
- A will be left with "holes"
- */
-{
- int cur_e, prev_e;
-
- cur_e = V[u].first_edge;
- if (cur_e == NIL)
- /*
- (u, v) is not an edge
- */
- return FALSE;
-
- if (A[cur_e].end_vertex == v)
- {
- V[u].first_edge = A[cur_e].next;
- return TRUE; /* done */
- }
-
- while (A[cur_e].end_vertex != v)
- /*
- if (u, v) is an edge then this loop will terminate
- */
- {
- prev_e = cur_e;
- cur_e = A[cur_e].next;
- if (cur_e == NIL)
- /*
- (u, v) is not an edge
- */
- return FALSE;
- }
- ASSERT(A[cur_e].end_vertex == v);
-
- A[prev_e].next = A[cur_e].next;
- return TRUE;
-}
-
-int
-sparseg_adjl_remove_all_dir_edge_no_red (t_ver_sparse_rep *V,
- t_adjl_sparse_rep *A, int u, int v)
- /*
- remove all DIRECTED edges [u, v] from the non-simple
- sparse graph (V, n, A)
- if (u, v) is not an edge then nothing changes;
- we return the number of edges removed
-
- A will be left with "holes"
- */
-{
- int cur_e, prev_e, e_removed;
-
- if (V[u].first_edge == NIL)
- /*
- (u, v) is not an edge
- */
- return 0;
-
- e_removed = 0;
- while (A[V[u].first_edge].end_vertex == v)
- {
- V[u].first_edge = A[V[u].first_edge].next;
- e_removed++;
-
- if (V[u].first_edge == NIL)
- return e_removed;
- }
- ASSERT(A[V[u].first_edge].end_vertex != v);
-
- prev_e = V[u].first_edge;
- cur_e = A[prev_e].next;
- while (cur_e != NIL)
- {
- if (A[cur_e].end_vertex == v)
- {
- A[prev_e].next = A[cur_e].next;
- e_removed++;
- cur_e = A[cur_e].next;
- }
- else
- {
- prev_e = cur_e;
- cur_e = A[cur_e].next;
- }
- }
-
- return e_removed;
-}
-
-
-
-void
-sparseg_adjl_add_vertices (t_ver_sparse_rep **V, int n, int nmore)
- /*
- add nmore vertices
- V is assumed to have length n
- */
-{
- *V = (t_ver_sparse_rep *)
- mem_realloc(*V, sizeof(t_ver_sparse_rep) * (n + nmore));
-
- sparseg_adjl_add_vertices_no_extend(*V, n, nmore);
-}
-
-void
-sparseg_adjl_add_vertices_no_extend (t_ver_sparse_rep *V, int n, int nmore)
- /*
- add nmore vertices,
- here V is assumed to have length n + nmore (ie V has already
- been made bigger)
- */
-{
- int v;
-
- for (v = n; v < n + nmore; v++)
- {
- V[v].first_edge = NIL;
- }
-}
-
-void
-sparseg_adjl_remove_vertex (t_ver_sparse_rep **V, int n,
- t_adjl_sparse_rep *A, int pos_A, int w, int *e)
- /*
- V is assumed to have length n: we will reallocate
- V so that V will have length n-1
-
- A is occupied from [0..pos-1], A will be left with holes
-
- we also assume that the graph can have loops and multiple edges;
- further, we the edge counting implicitely assumes that graph
- is undirected!!!
-
- this must be eventually fixed
- */
-{
- int v, nv, edge, loops;
- t_ver_sparse_rep *new_V;
-
- /*
- we first count the loops if any
- */
- loops = 0;
- edge = (*V)[w].first_edge;
- while (edge != NIL)
- {
- loops = A[edge].end_vertex == w ? loops + 1 : loops;
- edge = A[edge].next;
- }
- ASSERT(loops % 2 == 0);
- loops /= 2;
-
- /*
- we recreate the vertices array
- */
- new_V = (t_ver_sparse_rep *)
- mem_malloc(sizeof(t_ver_sparse_rep) * (n - 1));
-
- for (v = 0, nv = 0; v < n; v++, nv++)
- {
- if (v == w)
- {
- nv--;
- }
- else
- {
- new_V[nv].first_edge = (*V)[v].first_edge;
- }
- }
- mem_free(*V);
- *V = new_V;
-
- *e -= loops;
- sparseg_adjl_remove_vertex_no_red(*V, n, A, w, e);
-
- /*
- oops! not relabelling vertices can wreck havock!
- */
- sparseg_adjl_relabel_vertex(A, pos_A, w);
-}
-
-void
-sparseg_adjl_remove_vertex_no_red (t_ver_sparse_rep *V, int n,
- t_adjl_sparse_rep *A, int w, int *e)
- /*
- here V has already size n - 1 and has been initialised,
- all what remains to do is to remove the edges incident
- from w in A
-
- A will be left with holes
- */
-{
- int v, nbr_e_removed;
-
- nbr_e_removed = 0;
- for (v = 0; v < n - 1; v++)
- {
- nbr_e_removed += sparseg_adjl_remove_all_dir_edge_no_red(V, A, v, w);
- }
-
- *e= *e - nbr_e_removed;
-}
-
-void
-sparseg_adjl_relabel_vertex (t_adjl_sparse_rep *A, int pos, int u)
- /*
- relabel all vertices v > u as v-1
- (required when removing a vertex)
- */
-{
- int i;
-
- for (i = 0; i < pos; i++)
- {
- A[i].end_vertex = A[i].end_vertex > u ?
- A[i].end_vertex - 1 : A[i].end_vertex;
- }
-}
-
-/*
- * sparseg_adjl_pred.c
- */
-
-/*
- What:
- *****
-
- Implementing:
-
- Some high-level functions on the sparse graph as
- an adjacency list: predicates.
-
-
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++
- authors:
- ********
-
- Paulette Lieby (Magma), Brendan McKay (ANU)
-
- Started October 2001
-*/
-
-#include "planarity.h"
-
-#define IF_DEB(x) {}
-#define IF_VERB(x) {}
-
-
-
-/* aproto: header embed_graph_protos.h */
-
-
-#ifndef PLANAR_IN_MAGMA
-#endif
-
-boolean
-sparseg_adjl_dir_edge_exists (t_ver_sparse_rep *V, int n,
- t_adjl_sparse_rep *A, int u, int v)
- /*
- does the directed edge [u, v] already exist in the graph
- */
-{
- int cur_e, prev_e;
-
- cur_e = V[u].first_edge;
- if (cur_e == NIL)
- return FALSE;
-
- if (A[cur_e].end_vertex == v)
- {
- return TRUE;
- }
-
- while (A[cur_e].end_vertex != v)
- {
- prev_e = cur_e;
- cur_e = A[cur_e].next;
- if (cur_e == NIL)
- /*
- (u, v) is not an edge
- */
- return FALSE;
- }
- ASSERT(A[cur_e].end_vertex == v);
- return TRUE;
-}
-
-
-
-boolean
-sparseg_adjl_u_adj_v (t_ver_sparse_rep *V, int n, t_adjl_sparse_rep *A,
- int u, int v)
- /*
- is u adj. to v
- */
-{
- return sparseg_adjl_dir_edge_exists(V, n, A, u, v);
-}
-
-
-boolean
-sparseg_adjl_sub (t_ver_sparse_rep *V1, int n1, t_adjl_sparse_rep *A1,
- t_ver_sparse_rep *V2, int n2, t_adjl_sparse_rep *A2)
- /*
- test if the (V1, n1, A1) sparse graph is a subgraph of
- the (V2, n2, A2) graph
- */
-{
- int v, *fp, n, bign, i;
-
- n = n1 > n2 ? n2 : n1;
- bign = n1 > n2 ? n1 : 0;
- fp = (int *) mem_malloc(sizeof(int) * n);
- for (i = 0; i < n; i++)
- fp[i] = NIL;
-
- for (v = 0; v < n; v++)
- {
- int ne1, ne2;
-
- ne1 = V1[v].first_edge;
- ne2 = V2[v].first_edge;
- if (ne1 == NIL)
- {
- continue;
- }
- else if (ne2 == NIL)
- {
- mem_free(fp);
- return FALSE;
- }
-
- while (ne2 != NIL)
- {
- int u2;
-
- u2 = A2[ne2].end_vertex;
- fp[u2] = v;
- ne2 = A2[ne2].next;
- }
-
- while (ne1 != NIL)
- {
- int u1;
-
- u1 = A1[ne1].end_vertex;
- if (fp[u1] != v)
- {
- mem_free(fp);
- return FALSE;
- }
- ne1 = A1[ne1].next;
- }
- }
- mem_free(fp);
-
- for (v = n; v < bign; v++)
- /*
- those vertices must not be end points of edges:
- this chcek is only necessary in the digraph case
- */
- {
- if (V1[v].first_edge != NIL)
- return FALSE;
- }
-
- return TRUE;
-}
-
-
-
-boolean
-sparseg_adjl_eq (t_ver_sparse_rep *V1, int n1, t_adjl_sparse_rep *A1,
- t_ver_sparse_rep *V2, int n2, t_adjl_sparse_rep *A2)
- /*
- compare the two sparse graphs (V1, n1, A1) & (V2, n2, A2)
- we don't know their number of edges
- */
-{
- if (n1 != n2)
- return FALSE;
-
- return sparseg_adjl_sub(V1, n1, A1, V2, n2, A2)
- && sparseg_adjl_sub(V2, n2, A2, V1, n1, A1);
-}
-
-
-
-/*
- * sparseg_dlcl_misc.c
- */
-
-/*
- What:
- *****
-
- Implementing:
-
- Housekeeping for an internal sparse graph representation
- internal to the planarity tester and obstruction isolator.
-
- This sparse graph consists of an array of doubly linked circular lists
- (the neighbour lists for each vertex).
-
-
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++
- authors:
- ********
-
- Paulette Lieby (Magma), Brendan McKay (ANU)
-
- Started October 2001
-*/
-
-#include "planarity.h"
-
-#define IF_DEB(x) {}
-#define IF_VERB(x) {}
-
-
-/* aproto: header embed_graph_protos.h */
-
-/* aproto: beginstatic -- don't touch this!! */
-static boolean sparseg_dlcl_is_present (t_dlcl *, int, t_dlcl **);
-/* aproto: endstatic -- don't touch this!! */
-
-
-#ifndef PLANAR_IN_MAGMA
-#endif
-
-
-void
-sparseg_dlcl_delete (t_dlcl **g, int n)
-{
- int i;
-
- for (i = 0; i < n; i++)
- {
- embedg_dlcl_delete(g[i]);
- }
- mem_free(g);
-}
-
-void
-sparseg_dlcl_print (t_dlcl **g, int n)
-{
- int i;
-
- for (i = 0; i < n; i++)
- {
- fprintf(stdout,"%d:\t", i);
- embedg_dlcl_print(g[i]);
- }
-}
-
-
-static boolean
-sparseg_dlcl_is_present (t_dlcl *l, int label, t_dlcl **p)
-{
- *p = embedg_dlcl_find(l, label);
- return *p == NP ? FALSE : TRUE;
-}
-
-
-boolean
-sparseg_dlcl_is_adjacent (t_dlcl **g, int n, int v, int u, t_dlcl **p)
- /*
- is u adjacent to v
- */
-{
- ASSERT(v >= 0 && v < n && u >= 0 && u < n);
- return sparseg_dlcl_is_present(g[v], u, p);
-}
-
-void
-sparseg_dlcl_append_to_neigh_list (t_dlcl **g, int n, int v, int u, int in_adjl)
- /*
- append u to the neighbour list of v
- */
-{
- t_dlcl *u_rec;
-
- u_rec = embedg_dlcl_rec_new(u);
- u_rec->in_adjl = in_adjl;
- g[v] = embedg_dlcl_rec_append(g[v], u_rec);
-}
-
-
-
-
-void
-sparseg_dlcl_to_sparseg (t_dlcl **g, int n, int e,
- t_ver_sparse_rep **V, t_adjl_sparse_rep **A)
- /*
- e is the number of undirected edges of g
-
- convert a dlcl into the standard sparseg rep. as an
- adjacency list
- */
-{
- int i_e, v;
-
- *V = (t_ver_sparse_rep *) mem_malloc(sizeof(t_ver_sparse_rep) * n);
- *A = (t_adjl_sparse_rep *) mem_malloc(sizeof(t_adjl_sparse_rep) * 2 * e);
-
- for (v = 0; v < n; v++)
- (*V)[v].first_edge = NIL;
-
- i_e = 0;
- for (v = 0; v < n; v++)
- {
- t_dlcl *l, *p;
-
- l = p = g[v];
- if (!embedg_dlcl_is_empty(p))
- {
- t_adjl_sparse_rep a;
-
- ASSERT((*V)[v].first_edge == NIL);
- (*V)[v].first_edge = i_e;
- a.end_vertex = p->info;
- a.next = i_e + 1;
- (*A)[i_e++] = a;
-
- p = embedg_dlcl_list_next(p);
- while (p != l)
- {
- a.end_vertex = p->info;
- a.next = i_e + 1;
- (*A)[i_e++] = a;
-
- p = embedg_dlcl_list_next(p);
- }
-
- /*
- end of list for v
- */
- (*A)[i_e - 1].next = NIL;
- }
- }
- ASSERT(i_e == 2 * e);
-}
-
-boolean
-sparseg_dlcl_sub (t_dlcl **g1, int n1, t_dlcl **g2, int n2)
- /*
- is g2 a subgraph of g1
-
- I request that both graphs have same order
-
- This is not used anywhere... do we need it???
- */
-{
- int n, v, *fp;
-
- if (n1 != n2)
- return FALSE;
-
- n = n1;
- fp = (int *) mem_malloc(sizeof(int) * n);
- for (v = 0; v < n; v++)
- fp[v] = NIL;
-
- for (v = 0; v < n; v++)
- {
- t_dlcl *l1, *p1, *l2, *p2;
-
- l1 = p1 = g1[v];
- l2 = p2 = g2[v];
- if (embedg_dlcl_is_empty(p1) && !embedg_dlcl_is_empty(p2))
- {
- mem_free(fp);
- return FALSE;
- }
- if (embedg_dlcl_is_empty(p2))
- {
- continue;
- }
-
- fp[p1->info] = v;
- p1 = embedg_dlcl_list_next(p1);
- while (p1 != l1)
- {
- fp[p1->info] = v;
- p1 = embedg_dlcl_list_next(p1);
- }
-
- if (fp[p2->info] != v)
- {
- mem_free(fp);
- return FALSE;
- }
- p2 = embedg_dlcl_list_next(p2);
- while (p2 != l2)
- {
- if (fp[p2->info] != v)
- {
- mem_free(fp);
- return FALSE;
- }
- }
- }
- mem_free(fp);
-
- return TRUE;
-}
-/*
- * VES_misc.c
- */
-
-/*
- What:
- *****
-
- Implementing:
-
- All low-level routines for the VES structure:
-
- - the VES structure is solely used within the planarity tester
- and obstruction isolator
-
- - it stores vertices, virtual vertices and edges
- --more on this later--
-
- - it allows for circular doubly linked lists, hence
- enabling us -among other things- to store the
- graph embedding if the tester is successful
-
- - basic features:
- + the VES has exactly size 2n + 2(3n-5) :
- we add at most one more edge than the max for a planar graph
- (need to x by 2: we store directed edges)
- + a vertex and the edges incident FROM it are linked in a doubly
- linked circular list
- + where a vertex is inserted between two of its outcoming edges
- determines an external face walk for a bicomponent
- + the twin edge is more commonly known as the inverse edge
- + we have tree and back edges (from the DFS), and short-cut edges
- which are added by the tester
- -but short-cut edges are added in such a way as to maintain
- planarity (in a local sense)
- + vertices and edges can be marked (visited for example)
- + they have an orientation which must be eventuall recovered
- and which is set in the merge_bicomp routine
- + vertices are essentially known via their DFI or DFS index
- (though their label is stored too)
-
- blah, blah.... later then.
- Have a look at embedg_planar_alg_init which initialises the VES
- structure
-
-
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++
- from
-
- Simplified O(n) Planarity Algorithms (draft)
- ************************************
-
- John Boyer JBoyer@PureEdge.com, jboyer@acm.org
- Wendy Myrvold wendym@csr.uvic.ca
-
-
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++
- authors:
- ********
-
- Paulette Lieby (Magma), Brendan McKay (ANU)
-
- Started October 2001
-*/
-
-
-#include "planarity.h"
-
-#define IF_DEB(x) {}
-#define IF_DEB_SCE(x) {}
-#define IF_DEB_PROPER_FACE(x) {}
-#define IF_VERB(x) {}
-
-
-/* aproto: header embed_graph_protos.h */
-
-boolean
-embedg_VES_is_vertex (int n, int i)
- /*
- is this a vertex
- (relative to the "big" array of size 2n + 2(3n-5))
- */
-{
- return i < n ? TRUE : FALSE;
-}
-
-boolean
-embedg_VES_is_virtual_vertex (int n, int i)
- /*
- is this a virtual vertex
- (relative to the "big" array of size 2n + 2(3n-5))
-
- a virtual vertex is a vertex v^c which denotes the
- DFS parent of the child c
-
- see embedg_planar_alg_init for more
- */
-{
- return i >= n && i < 2*n ? TRUE : FALSE;
-}
-
-boolean
-embedg_VES_is_edge (int n, int i)
- /*
- is this an edge
- (relative to the "big" array of size 2n + 2(3n-5))
- */
-{
- return i >= 2*n ? TRUE : FALSE;
-}
-
-boolean
-embedg_VES_is_tree_edge (t_ver_edge *embed_graph, int n, int i)
- /*
- is this s tree edge
- */
-{
- return embedg_VES_is_edge(n, i)
- && embed_graph[i].type == TE;
-}
-
-boolean
-embedg_VES_is_back_edge (t_ver_edge *embed_graph, int n, int i)
- /*
- is this a back edge
- */
-{
- return embedg_VES_is_edge(n, i)
- && embed_graph[i].type == BE;
-}
-
-boolean
-embedg_VES_is_short_cut_edge (t_ver_edge *embed_graph, int n, int i)
- /*
- as the name indicates...
- */
-{
- return embedg_VES_is_edge(n, i)
- && embed_graph[i].type == SCE;
-}
-
-void
-embedg_VES_print_vertex (int n, int v)
-{
- ASSERT(embedg_VES_is_vertex(n, v));
- fprintf(stdout, "%d ", v);
-}
-
-void
-embedg_VES_print_virtual_vertex (t_ver_edge *embed_graph, int n, int v)
-{
- int c;
-
- ASSERT(embedg_VES_is_virtual_vertex(n, v));
- c = v - n;
- fprintf(stdout, "%d^%d ", embed_graph[c].DFS_parent, c);
-}
-
-void
-embedg_VES_print_any_vertex (t_ver_edge *embed_graph, int n, int v)
-{
- if (embedg_VES_is_vertex(n, v))
- {
- embedg_VES_print_vertex(n, v);
- }
- else
- {
- embedg_VES_print_virtual_vertex(embed_graph, n, v);
- }
-}
-
-void
-embedg_VES_print_any_rec (t_ver_edge *embed_graph, int n, int r)
-{
- if (embedg_VES_is_edge(n, r))
- {
- embedg_VES_print_edge(embed_graph, n, r);
- }
- else
- {
- embedg_VES_print_any_vertex(embed_graph, n, r);
- }
-}
-
-void
-embedg_VES_print_edge (t_ver_edge *embed_graph, int n, int e)
-{
- int v, prev, cur;
-
- ASSERT(embedg_VES_is_edge(n, e));
-
- /*
- must find the vertex in the doubly linked circular list
- of vertices/edges
- */
-
- prev = e;
- cur = v = embed_graph[e].link[0];
- if (embedg_VES_is_vertex(n, v)
- || embedg_VES_is_virtual_vertex(n, v))
- {
- embedg_VES_print_any_vertex(embed_graph, n, v);
- fprintf(stdout, ", ");
- embedg_VES_print_any_vertex(embed_graph, n,
- embed_graph[e].neighbour);
- fprintf(stdout, "):0\n");
- }
- else while (!embedg_VES_is_vertex(n, v)
- && !embedg_VES_is_virtual_vertex(n, v))
- {
- v = embedg_VES_get_next_in_dlcl(embed_graph, n,
- cur, prev);
-
- if (embedg_VES_is_vertex(n, v)
- || embedg_VES_is_virtual_vertex(n, v))
- {
- embedg_VES_print_any_vertex(embed_graph, n, v);
- fprintf(stdout, ", ");
- embedg_VES_print_any_vertex(embed_graph, n,
- embed_graph[e].neighbour);
- fprintf(stdout, "):0\n");
- }
- else
- {
- prev = cur;
- cur = v;
- }
- }
-}
-
-void
-embedg_VES_print_flipped_edges (t_ver_edge *embed_graph, int n, int edge_pos)
- /*
- print those edges in the structure whose sign is CLOCKW,
- ie which have been flipped at some stage
- */
-{
- int e;
-
- for (e = 2*n; e <= edge_pos; e++)
- {
- if (!embedg_VES_is_short_cut_edge(embed_graph, n, e))
- /*
- we don't care about the short-cut edges
- */
- {
- if (embed_graph[e].sign != CCLOCKW)
- {
- embedg_VES_print_edge(embed_graph, n, e);
- }
- }
- }
-}
-
-#if 0
-int
-embedg_VES_get_edge_from_ver (t_ver_edge *embed_graph, int n, int v)
- /*
- not used anywhere; why is this here???
- */
-{
- int in, e;
-
- ASSERT(embedg_VES_is_vertex(n, v)
- || embedg_VES_is_virtual_vertex(n, v));
-
- in = embedg_VES_is_edge(n, embed_graph[v].link[0]) ? 0 : 1;
- e = embed_graph[v].link[in];
- ASSERT(embedg_VES_is_edge(n, e));
-
- return e;
-}
-
-int
-embedg_VES_get_ver_from_edge (t_ver_edge *embed_graph, int n, int e)
-{
- int in, v;
-
- ASSERT(embedg_VES_is_edge(n, e));
-
- in = embedg_VES_is_vertex(n, embed_graph[e].link[0])
- || embedg_VES_is_virtual_vertex(n, embed_graph[e].link[0])
- ?
- 0 : 1;
-
- v = embed_graph[e].link[in];
- ASSERT(embedg_VES_is_vertex(n, v)
- || embedg_VES_is_virtual_vertex(n, v));
-
- return v;
-}
-#endif
-
-int
-embedg_VES_get_twin_edge (t_ver_edge *embed_graph, int n, int e)
- /*
- the twin edge is understood as being the inverse edge
- */
-{
- int twin;
-
- ASSERT(embedg_VES_is_edge(n, e));
-
- twin = e % 2 == 0 ? e + 1 : e - 1;
- ASSERT(embedg_VES_is_edge(n, twin));
-
- return twin;
-}
-
-int
-embedg_VES_get_ver_from_virtual (t_ver_edge *embed_graph, int n, int vv)
- /*
- get v from the virtual vertex v^c
- */
-{
- int v;
-
- ASSERT(embedg_VES_is_virtual_vertex(n, vv));
- v = embed_graph[vv - n].DFS_parent;
-
- return v;
-}
-
-int
-embedg_VES_get_ver (t_ver_edge *embed_graph, int n, int v)
-{
- if (embedg_VES_is_virtual_vertex(n, v))
- return embedg_VES_get_ver_from_virtual(embed_graph, n, v);
-
- return v;
-}
-
-
-int
-embedg_VES_get_next_in_dlcl (t_ver_edge *embed_graph, int n, int r, int prev)
- /*
- r is a (virtual) vertex or edge record in embed_graph:
- get the next in the list (formed by the .link[] fields)
- in the doubly linked circular list
-
- so that prev != next
- -- NOTE: a priori these lists always contain 2 elts at least
- so that there shouldn't be any problem...
- --> huh? is that true?
- */
-{
- return embed_graph[r].link[0] == prev ?
- embed_graph[r].link[1] : embed_graph[r].link[0];
-}
-
-
-void
-embedg_VES_walk_bicomp (t_ver_edge *embed_graph, int n, int v, int vin)
- /*
- walk the external face of the bicomp starting
- at VIRTUAL vertex v entered via vin
-
- this of course assumes that the "thing" rooted at
- v is a bicomponent -- depending where we are at in the
- tester this is not necessarily the case
- -- I comment upon this in merge_bicomps.c:
- embedg_VES_merge_pertinent_bicomps
- */
-{
- int start, startin, s, sin;
-
- ASSERT(embedg_VES_is_virtual_vertex(n, v));
-
- embedg_VES_print_virtual_vertex(embed_graph, n, v);
-
- s = NIL;
- start = v;
- startin = vin;
- while (s != v)
- {
- embedg_VES_get_succ_on_ext_face(embed_graph, n, start, startin,
- FALSE, 0, &s, &sin);
- if (embedg_VES_is_virtual_vertex(n, s))
- {
- embedg_VES_print_virtual_vertex(embed_graph, n, s);
- }
- else
- {
- embedg_VES_print_vertex(n, s);
- }
- start = s;
- startin = sin;
- }
- fprintf(stdout, "\n");
-}
-
-void
-embedg_VES_print_adj_list (t_ver_edge *embed_graph, int n, int r,
- boolean consistent)
- /*
- print r's adjacency list - r can be a vertex or edge
-
- the boolean if true assumes that
- the list is consistent (will determine the way we traverse the list)
-
- a priori we should get the same result either way
- */
-{
- if (consistent)
- {
- int next;
-
- embedg_VES_print_any_rec(embed_graph, n, r);
-
- next = embed_graph[r].link[0];
- while (next != r)
- {
- embedg_VES_print_any_rec(embed_graph, n, next);
- next = embed_graph[next].link[0];
- }
- }
- else
- {
- int prev, cur, next;
-
- embedg_VES_print_any_rec(embed_graph, n, r);
-
- prev = r;
- cur = embed_graph[r].link[0];
-
- while (cur != r)
- {
- embedg_VES_print_any_rec(embed_graph, n, cur);
- next = embedg_VES_get_next_in_dlcl(embed_graph, n,
- cur, prev);
- prev = cur;
- cur = next;
- }
- }
-}
-
-boolean
-embedg_VES_is_adj_list_consistent (t_ver_edge *embed_graph, int n, int r)
- /*
- checks that r's adjacency list is consistent:
- ie, that either traversing it using link[0] always
- or traversing it using embedg_VES_get_next_in_dlcl
- gives the SAME result
- */
-{
- int *list_link, *list_n_dldl, il, id, i;
-
- list_link = (int *) mem_malloc(sizeof(int) * 2 * n);
- list_n_dldl = (int *) mem_malloc(sizeof(int) * 2 * n);
- /*
- must allocate 2*n space: I could have TE and SCE with same neighbour
- (or BE and SCE as well)
- */
- il = id = -1;
-
- /*
- traversing the list via link[0]
- */
- {
- int next;
-
- list_link[++il] = r;
-
- next = embed_graph[r].link[0];
- while (next != r)
- {
- list_link[++il] = next;
- next = embed_graph[next].link[0];
- }
- }
-
- /*
- traversing the list using embedg_VES_get_next_in_dlcl
- */
- {
- int prev, cur, next;
-
- list_n_dldl[++id] = r;
- prev = r;
- cur = embed_graph[r].link[0];
-
- while (cur != r)
- {
- list_n_dldl[++id] = cur;
- next = embedg_VES_get_next_in_dlcl(embed_graph, n,
- cur, prev);
- prev = cur;
- cur = next;
- }
- }
-
- if (il != id)
- {
- mem_free(list_link);
- mem_free(list_n_dldl);
- return FALSE;
- }
-
- for (i = 0; i <= il; i++)
- {
- if (list_link[i] != list_n_dldl[i])
- {
- mem_free(list_link);
- mem_free(list_n_dldl);
- return FALSE;
- }
- }
-
- mem_free(list_link);
- mem_free(list_n_dldl);
- return TRUE;
-}
-
-
-boolean
-embedg_VES_are_adj_lists_consistent (t_ver_edge *embed_graph, int n)
- /*
- checks that the adjacency list of each vertex is consistent
- in the manner of embedg_VES_is_adj_list_consistent
- */
-{
- int i;
-
- /*
- it is enough to visit the vertices and virtual vertices only
- (I don't think it is enough to do the vertices only --??)
- */
- for (i = 0; i < 2*n; i++)
- if (!embedg_VES_is_adj_list_consistent(embed_graph, n, i))
- return FALSE;
-
- return TRUE;
-}
-
-
-
-void
-embedg_VES_remove_edge (t_ver_edge *embed_graph, int n, int e)
- /*
- remove edge e from the embedding
- */
-{
- int r1, r2, r1out, r2in, twin;
-
- ASSERT(embedg_VES_is_edge(n, e));
-
- IF_DEB_SCE(
- fprintf(stdout, "removing an SCE, enter\n");
- embedg_VES_print_edge(embed_graph, n, e);
- )
-
- r1 = embed_graph[e].link[0];
- r2 = embed_graph[e].link[1];
-
- /*
- disable e and link r1 and r2 together:
- we had r1 -> e -> r2
- */
- embed_graph[e].link[0] = embed_graph[e].link[1] = e;
-
- r1out = embed_graph[r1].link[0] == e ? 0 : 1;
- r2in = embed_graph[r2].link[0] == e ? 0 : 1;
-
- if (r1 == r2)
- /*
- this I think should never happen, but one never knows...
- */
- {
- embed_graph[r1].link[0] = embed_graph[r1].link[1] = r1;
- }
- else
- {
- embed_graph[r1].link[r1out] = r2;
- embed_graph[r2].link[r2in] = r1;
- }
-
- ASSERT(embedg_VES_is_adj_list_consistent(embed_graph, n, r1));
-
- /*
- now we must do a similar thing for the twin
- (which must get reomved as well)
- */
- twin = embedg_VES_get_twin_edge(embed_graph, n, e);
-
- IF_DEB_SCE(
- fprintf(stdout, "removing an SCE, the twin\n");
- embedg_VES_print_edge(embed_graph, n, twin);
- )
-
- r1 = embed_graph[twin].link[0];
- r2 = embed_graph[twin].link[1];
-
- embed_graph[twin].link[0] = embed_graph[twin].link[1] = twin;
-
- r1out = embed_graph[r1].link[0] == twin ? 0 : 1;
- r2in = embed_graph[r2].link[0] == twin ? 0 : 1;
-
- if (r1 == r2)
- {
- embed_graph[r1].link[0] = embed_graph[r1].link[1] = r1;
- }
- else
- {
- embed_graph[r1].link[r1out] = r2;
- embed_graph[r2].link[r2in] = r1;
- }
-
- ASSERT(embedg_VES_is_adj_list_consistent(embed_graph, n, r1));
-}
-
-
-void
-embedg_VES_set_orientation (t_ver_edge *embed_graph, int n, int *ver_orient)
- /*
- using the vertices' orientation as given in ver_orient
- we set the orientation for each edge in the adjacency list
- for each vertex
-
- to do this we use the field sign which is NOT needed
- anymore by the tester since by the time we call this
- function we would have finished with that bit (the tester)
-
- sign is only set when merging bicomps
- - even though we'll perform another walkdown when
- recovering an obstruction (if any) no bicomp merging will occur,
- so we are safe
- */
-{
- int v;
-
- for (v = 0; v < n; v++)
- {
- int o, e;
-
- o = ver_orient[v];
- embed_graph[v].sign = o;
-
- e = embed_graph[v].link[0];
-
- while (e != v)
- /*
- just as a note: note the way I get the next in the list
- here (as opposed to using
- embedg_VES_get_next_in_dlcl):
- this is because I implicitely assume that
- the adjacency lists are consistent
-
- Also note that edges can be SCE, it doesn't really matter
- anyway (they may not have been removed yet
- -- see the way we recover the obstruction:
- embedg_mark_obstruction)
- */
- {
- embed_graph[e].sign = o;
- e = embed_graph[e].link[0];
- }
- }
-}
-
-
-/*
- * dlcl_misc.c
- */
-
-/*
- What:
- *****
-
- Implementing:
-
- Housekeeping for a simple doubly linked circular list:
- this is a data structure ONLY used WITHIN
- the planarity tester and obstruction isolator and is not to be
- confused with the VES structure mentionned elsewhere.
-
- The VES structure is an array, while the dlcl one is a list of
- pointers.
-
- The dlcl is especially useful as it allows for the storage
- of an ordered list.
-
-
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++
- from
-
- Simplified O(n) Planarity Algorithms (draft)
- ************************************
-
- John Boyer JBoyer@PureEdge.com, jboyer@acm.org
- Wendy Myrvold wendym@csr.uvic.ca
-
-
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++
- authors:
- ********
-
- Paulette Lieby (Magma), Brendan McKay (ANU)
-
- Started October 2001
-*/
-
-
-#include "planarity.h"
-
-#define IF_DEB(x) {}
-#define IF_VERB(x) {}
-
-
-/* aproto: header embed_graph_protos.h */
-
-/* aproto: beginstatic -- don't touch this!! */
-static void embedg_dlcl_rec_free (t_dlcl *);
-static void embedg_dlcl_rec_insert_right (t_dlcl *, t_dlcl *);
-static void embedg_dlcl_rec_insert_left (t_dlcl *, t_dlcl *);
-static void embedg_dlcl_rec_retrieve (t_dlcl *);
-static void embedg_dlcl_rec_delete (t_dlcl *);
-static boolean embedg_dlcl_is_singleton (t_dlcl *);
-/* aproto: endstatic -- don't touch this!! */
-
-
-#ifndef PLANAR_IN_MAGMA
-#endif
-
-
-t_dlcl *
-embedg_dlcl_rec_new (int info)
- /*
- create a new record with info in the global array
- to insert in the list
- */
-{
- t_dlcl *r;
-
- r = (t_dlcl *) mem_malloc(sizeof(t_dlcl));
- r->info = info;
- r->in_adjl = r->twin_in_adjl = NIL;
- r->mult = 1;
- r->right = r;
- r->left = r;
- return r;
-}
-
-static void
-embedg_dlcl_rec_free (t_dlcl *r)
- /*
- free
- */
-{
- mem_free(r);
-}
-
-void
-embedg_dlcl_rec_print (t_dlcl *r)
-{
- fprintf(stdout,"%d ", r->info);
-}
-
-void
-embedg_dlcl_print (t_dlcl *l)
-{
- t_dlcl *p = l;
-
- if (!embedg_dlcl_is_empty(p))
- {
- embedg_dlcl_rec_print(p);
- p = embedg_dlcl_list_next(p);
- while (p != l)
- {
- embedg_dlcl_rec_print(p);
- p = embedg_dlcl_list_next(p);
- }
- }
- fprintf(stdout,"\n");
-}
-
-
-static void
-embedg_dlcl_rec_insert_right (t_dlcl *l, t_dlcl *r)
-{
- t_dlcl *tmp_r, *tmp_l;
-
- tmp_r = l->right;
- tmp_l = tmp_r->left;
-
- l->right = r;
- r->right = tmp_r;
-
- r->left = tmp_l;
- tmp_r->left = r;
-}
-
-
-static void
-embedg_dlcl_rec_insert_left (t_dlcl *l, t_dlcl *r)
-{
- t_dlcl *tmp_r, *tmp_l;
-
- tmp_l = l->left;
- tmp_r = tmp_l->right;
-
- l->left = r;
- r->left = tmp_l;
-
- r->right = tmp_r;
- tmp_l->right = r;
-}
-
-t_dlcl *
-embedg_dlcl_rec_append (t_dlcl *l, t_dlcl *r)
-{
- if (embedg_dlcl_is_empty(l))
- return r;
-
- embedg_dlcl_rec_insert_left(l, r);
- return l;
-}
-
-t_dlcl *
-embedg_dlcl_rec_prepend (t_dlcl *l, t_dlcl *r)
-{
- if (embedg_dlcl_is_empty(l))
- return r;
-
- embedg_dlcl_rec_insert_left(l, r);
- return r;
-}
-
-t_dlcl *
-embedg_dlcl_cat (t_dlcl *l, t_dlcl *m)
- /*
- concatenate m to the RIGHT of the end of l
- WITHOUT copying m
- */
-{
- t_dlcl *h1, *h2, *e1, *e2;
-
- if (embedg_dlcl_is_empty(l))
- return m;
- if (embedg_dlcl_is_empty(m))
- return l;
-
- h1 = l;
- e1 = l->left;
- h2 = m;
- e2 = m->left;
-
- e1->right = h2;
- h2->left = e1;
- e2->right = h1;
- h1->left = e2;
-
- return l;
-}
-
-t_dlcl *
-embedg_dlcl_find (t_dlcl *l, int info)
-{
- t_dlcl *p = l;
-
- if (!embedg_dlcl_is_empty(p))
- {
- if (p->info == info)
- {
- return p;
- }
- p = embedg_dlcl_list_next(p);
- while (p != l)
- {
- if (p->info == info)
- {
- return p;
- }
- p = embedg_dlcl_list_next(p);
- }
- }
- return NP;
-}
-
-t_dlcl *
-embedg_dlcl_find_with_NIL_twin_in_adjl (t_dlcl *l, int info)
-{
- t_dlcl *p = l;
-
- if (!embedg_dlcl_is_empty(p))
- {
- if (p->info == info && p->twin_in_adjl == NIL)
- {
- return p;
- }
- p = embedg_dlcl_list_next(p);
- while (p != l)
- {
- if (p->info == info && p->twin_in_adjl == NIL)
- {
- return p;
- }
- p = embedg_dlcl_list_next(p);
- }
- }
- return NP;
-}
-
-
-
-static void
-embedg_dlcl_rec_retrieve (t_dlcl *r)
-{
- t_dlcl *right, *left;
-
- right = r->right;
- left = r->left;
-
- left->right = right;
- right->left = left;
-
- r->right = r;
- r->left = r;
-}
-
-static void
-embedg_dlcl_rec_delete (t_dlcl *r)
-{
- embedg_dlcl_rec_retrieve(r);
- embedg_dlcl_rec_free(r);
-}
-
-
-t_dlcl *
-embedg_dlcl_delete_first (t_dlcl *l)
- /*
- prune the list from the head:
- - set new head to right of old head
- - delete old head
- */
-{
- t_dlcl *new_head;
-
- ASSERT(!embedg_dlcl_is_empty(l));
- if (embedg_dlcl_is_singleton(l))
- {
- new_head = NP;
- }
- else
- {
- new_head = l->right;
- }
- embedg_dlcl_rec_delete(l);
- return new_head;
-}
-
-
-t_dlcl *
-embedg_dlcl_delete_rec (t_dlcl *l, t_dlcl *r)
- /*
- delete r from l;
- if r == l, set new head to right of old head
- */
-{
- if (r == l)
- {
- return embedg_dlcl_delete_first(l);
- }
- embedg_dlcl_rec_delete(r);
- return l;
-}
-
-
-boolean
-embedg_dlcl_is_empty (t_dlcl *l)
-{
- return (l == NP) ? TRUE : FALSE;
-}
-
-
-static boolean
-embedg_dlcl_is_singleton (t_dlcl *l)
-{
- return (l->right == l) ? TRUE : FALSE;
- /*
- same as l->left == l
- */
-}
-
-t_dlcl *
-embedg_dlcl_list_next (t_dlcl *l)
- /*
- this assumes no choice in the direction of the walking
- (always to the right)
- -- good enough when deleting for example or when
- the direction of the walking does not matter
- */
-{
- return l->right;
-}
-
-
-t_dlcl *
-embedg_dlcl_list_prev (t_dlcl *l)
- /*
- this assumes no choice in the direction of the walking
- (always to the right)
- */
-{
- return l->left;
-}
-
-t_dlcl *
-embedg_dlcl_list_last (t_dlcl *l)
-{
- return embedg_dlcl_list_prev(l);
-}
-
-
-
-void
-embedg_dlcl_delete (t_dlcl *l)
-{
- if (!embedg_dlcl_is_empty(l))
- {
- while (!embedg_dlcl_is_singleton(l))
- {
- t_dlcl *next;
-
- next = embedg_dlcl_list_next(l);
- embedg_dlcl_rec_delete(next);
- }
- embedg_dlcl_rec_delete(l);
- }
-}
-
-t_dlcl *
-embedg_dlcl_copy (t_dlcl *l)
-{
- t_dlcl *p, *c;
-
- if (embedg_dlcl_is_empty(l))
- return NP;
-
- c = embedg_dlcl_rec_new(l->info);
-
- p = embedg_dlcl_list_next(l);
- while (p != l)
- {
- t_dlcl *temp;
-
- temp = embedg_dlcl_rec_new(p->info);
- temp->in_adjl = p->in_adjl;
- temp->twin_in_adjl = p->twin_in_adjl;
- temp->mult = p->mult;
- c = embedg_dlcl_rec_append(c, temp);
- p = embedg_dlcl_list_next(p);
- }
- return c;
-}
-
-
-int
-embedg_dlcl_length (t_dlcl *l)
-{
- t_dlcl *p;
- int n;
-
- if (embedg_dlcl_is_empty(l))
- return 0;
-
- p = embedg_dlcl_list_next(l);
- n = 1;
- while (p != l)
- {
- n++;
- p = embedg_dlcl_list_next(p);
- }
- return n;
-}
-/*
- * planar_by_edge_addition.c
- */
-
-/*
- What:
- *****
-
- Implementing:
-
- The top level for the planarity tester.
-
-
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++
- from
-
- Simplified O(n) Planarity Algorithms (draft)
- ************************************
-
- John Boyer JBoyer@PureEdge.com, jboyer@acm.org
- Wendy Myrvold wendym@csr.uvic.ca
-
-
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++
- authors:
- ********
-
- Paulette Lieby (Magma), Brendan McKay (ANU)
-
- Started October 2001
-*/
-
-
-#include "planarity.h"
-
-#define IF_DEB(x) {}
-#define IF_VERB(x) {}
-#define IF_DEB_TREE(x) {}
-#define IF_DEB_EDGES(x) {}
-#define IF_CPU(x) {}
-
-
-/* aproto: header embed_graph_protos.h */
-
-
-#ifndef PLANAR_IN_MAGMA
-#endif
-
-
-boolean
-sparseg_adjl_is_planar (
- t_ver_sparse_rep *V,
- int n,
- t_adjl_sparse_rep *A, /* input sparse graph */
- int *nbr_c, /* size of the graph, #components
- */
- t_dlcl ***dfs_tree, /* a sparse graph rep. for the dfs tree
- -- vertices are as DFIs
- -- and children are ordered wrt
- lowpoint value
- */
- t_dlcl ***back_edges, /* for each vertex v, a dlcl
- of the back edges [v, x] incident to v
- where x is a DESCENDANT of v
- (vertices are given as DFIs)
- */
- t_dlcl ***mult_edges, /* for each vertex v, a dlcl
- of the back edges [v, x] incident to v
- where x is a DESCENDANT of v
- (vertices are given as DFIs)
- */
- t_ver_edge **embed_graph, /* output graph embedding -- more on that
- later
- */
- int *edge_pos, /* pos. in embed_graph for addition
- of the next edge */
- int *vr,
- int *wr /* if graph is non planar, return
- the unembedded edge
- (where wr descendant of vr)
- */
-)
- /*
- as the name indicates: is the graph planar?
- */
-{
- int v;
-
- IF_CPU(
- float sttime; float time_to_now;
- )
-
-
- *embed_graph =
- embedg_planar_alg_init(V, n, A, nbr_c,
- edge_pos, dfs_tree, back_edges, mult_edges);
- IF_CPU(
- sttime = time_current_user();
- )
-
- for (v = n - 1; v >= 0; v--)
- /*
- visit all vertices in descending DFI order
- */
- {
- t_dlcl *be_l, *te_l, *p;
-
- IF_DEB(
- fprintf(stdout, "top level, vertex %d\n", v);
- )
-
- /*
- find all the back edges [w, v] where w is a descendant of v
- and perform a walkup from w to v
- (ie determine which bicomps are pertinent)
- */
- be_l = (*back_edges)[v];
- p = be_l;
-
- if (!embedg_dlcl_is_empty(p))
- {
- int w;
-
- w = p->info;
- IF_DEB(
- fprintf(stdout, "top level, before walkup for w %d\n", w);
- )
- embedg_walkup(*embed_graph, n, v, p);
-
- p = embedg_dlcl_list_next(p);
- while (p != be_l)
- {
- w = p->info;
- IF_DEB(
- fprintf(stdout, "top level, before walkup for w %d\n", w);
- )
- embedg_walkup(*embed_graph, n, v, p);
-
- p = embedg_dlcl_list_next(p);
- }
- }
-
- /*
- perform a walkdown for each tree edge [v, c], c a descendant of v
- (ie attempt to embed all back edges on the pertinent bicomps)
- */
- te_l = (*dfs_tree)[v];
- p = te_l;
-
- if (!embedg_dlcl_is_empty(p))
- {
- int c, vv;
- t_merge_queue q;
-
- c = p->info;
- vv = c + n;
- IF_DEB(
- fprintf(stdout, "top level, before walkdown for c %d\n", c);
- )
- q = embedg_walkdown(*embed_graph, n, edge_pos, vv);
-
- IF_DEB(
- fprintf(stdout, "top level, after walkdown for c %d, state of edges'sign\n", c);
- embedg_VES_print_flipped_edges(*embed_graph,
- n, *edge_pos);
- )
-
- /*
- temp only
- */
- embedg_merge_queue_delete(q);
- p = embedg_dlcl_list_next(p);
- while (p != te_l)
- {
- c = p->info;
- vv = c + n;
- IF_DEB(
- fprintf(stdout, "top level, before walkdown for c %d\n", c);
- )
- q = embedg_walkdown(*embed_graph, n, edge_pos, vv);
-
- IF_DEB(
- fprintf(stdout, "top level, after walkdown for c %d, state of edges'sign\n", c);
- embedg_VES_print_flipped_edges(*embed_graph,
- n, *edge_pos);
- )
-
- /*
- temp only
- */
- embedg_merge_queue_delete(q);
-
- p = embedg_dlcl_list_next(p);
- }
- }
-
-
- /*
- check that each back edge [w, v], w a descendant of v,
- has been embedded
- */
- be_l = (*back_edges)[v];
- p = be_l;
-
- if (!embedg_dlcl_is_empty(p))
- {
- int w;
-
- w = p->info;
- IF_DEB(
- fprintf(stdout, "top level, before checking embedding for w %d\n",
- w);
- )
- if ((*embed_graph)[w].adjacent_to == v)
- /*
- this edge hasn't been embedded:
- the graph is non-planar
- */
- {
- /*
- before returning we really want to ensure that
- the vertices' adjacency lists are consistent
- */
- ASSERT(embedg_VES_are_adj_lists_consistent(
- *embed_graph, n));
-
- IF_CPU(
- fprintf(stdout, "CPU for tester only %f\n",
- (time_current_user() - sttime));
- )
-
- *vr = v;
- *wr = w;
- return FALSE;
- }
-
- p = embedg_dlcl_list_next(p);
- while (p != be_l)
- {
- w = p->info;
- IF_DEB(
- fprintf(stdout, "top level, before checking embedding for w %d\n",
- w);
- )
- if ((*embed_graph)[w].adjacent_to == v)
- {
- /*
- before returning we really want to ensure that
- the vertices' adjacency lists are consistent
- */
- ASSERT(embedg_VES_are_adj_lists_consistent(
- *embed_graph, n));
-
- IF_CPU(
- fprintf(stdout, "CPU for tester only %f\n",
- (time_current_user() - sttime));
- )
-
- *vr = v;
- *wr = w;
- return FALSE;
- }
-
- p = embedg_dlcl_list_next(p);
- }
- }
- }
- IF_DEB_EDGES(
- fprintf(stdout, "top level, total number of edges in embedding %d\n",
- *edge_pos - 2 * n + 1);
- )
-
-
- /*
- before returning we really want to ensure that
- the vertices' adjacency lists are consistent
- */
- ASSERT(embedg_VES_are_adj_lists_consistent(*embed_graph, n));
-
- IF_CPU(
- fprintf(stdout, "CPU for tester only %f\n",
- (time_current_user() - sttime));
- )
-
- return TRUE;
-}
-
-
-
-/*
- * walkup.c
- */
-
-/*
- What:
- *****
-
- Implementing:
-
- The walkup routine within the VES structure:
-
- Walking up from w where [w, v^c] is a (directed)
- back edge to be embeeding later.
- Along the way collect all the pertinent bicomps that
- will need to be merged before embedding the back edges
- to v^c.
-
-
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++
- from
-
- Simplified O(n) Planarity Algorithms (draft)
- ************************************
-
- John Boyer JBoyer@PureEdge.com, jboyer@acm.org
- Wendy Myrvold wendym@csr.uvic.ca
-
-
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++
- authors:
- ********
-
- Paulette Lieby (Magma), Brendan McKay (ANU)
-
- Started October 2001
-*/
-
-
-#include "planarity.h"
-
-#define IF_DEB(x) {}
-#define IF_VERB(x) {}
-
-
-
-/* aproto: header embed_graph_protos.h */
-
-
-#ifndef PLANAR_IN_MAGMA
-#endif
-
-
-void
-embedg_walkup (t_ver_edge *embed_graph, int n, int v, t_dlcl *p)
- /*
- walkup from w = p->info to v: [w, v] is a back edge where w is a DFS
- descendant of v
- */
-{
- int w, x, xin, y, yin;
-
- w = p->info;
-
- IF_DEB(
- fprintf(stdout, "walkup from %d to %d, enter\n", w, v);
- )
-
- embed_graph[w].adjacent_to = v;
- /*
- dirty trick to record some information about the BE [w, v]
- which will be useful at the time of creation and insertion of
- this BE: this happens in the walkdown procedure
-
- note that what I am doing here is safe: [w].in_adjl,
- [w].twin_in_adjl, [w].mult had no use so far since w is a vertex
- (and not an edge...)
- */
- embed_graph[w].in_adjl = p->in_adjl;
- embed_graph[w].twin_in_adjl = p->twin_in_adjl;
- embed_graph[w].mult = p->mult;
-
- /*
- set up the traversal contexts for w: one in each direction
- */
- x = w;
- xin = 1;
- y = w;
- yin = 0;
-
- while (x != v)
- {
- int vz, z, c;
-
- IF_DEB(
- fprintf(stdout, "walkup, x %d and y %d\n", x, y);
- )
-
- if (embed_graph[x].visited == v
- || embed_graph[y].visited == v)
- {
- IF_DEB(
- if (embed_graph[x].visited == v)
- fprintf(stdout, "walkup, x visited\n");
- else
- fprintf(stdout, "walkup, y visited\n");
- )
- break;
- }
-
- /*
- set x and y as visited!
- */
- embed_graph[x].visited = embed_graph[y].visited = v;
-
- vz = embedg_VES_is_virtual_vertex(n, x) ? x : NIL;
- vz = embedg_VES_is_virtual_vertex(n, y) ? y : vz;
-
- if (vz != NIL)
- /*
- that is, x (or y) is a virtual vertex
- -- in other words, we are set to find the root of the bicomp
- containing w, or of the bicomp r^c such that w is in the tree
- rooted by c
-
- consequently, by definition, vz is PERTINENT
- */
- {
- c = vz - n;
- z = embed_graph[c].DFS_parent;
-
- IF_DEB(
- fprintf(stdout, "walkup, vz is virtual, %d^%d\n",
- z, c);
- )
-
- if (z != v)
- /*
- determine if vz externally or internally active
- */
- {
- if (embed_graph[c].lowpoint < v)
- /*
- vz is externally active: APPEND to the list
- of pertinent bicomps
- */
- {
- IF_DEB(
- fprintf(stdout, "walkup, vz is ext. active\n");
- )
-
- embed_graph[z].pertinent_bicomp_list =
- embedg_dlcl_rec_append(
- embed_graph[z].pertinent_bicomp_list,
- embedg_dlcl_rec_new(vz));
- }
- else
- /*
- vz is internally active: PREPEND to the list
- of pertinent bicomps
- */
- {
- IF_DEB(
- fprintf(stdout, "walkup, vz is pertinent\n");
- )
-
- embed_graph[z].pertinent_bicomp_list =
- embedg_dlcl_rec_prepend(
- embed_graph[z].pertinent_bicomp_list,
- embedg_dlcl_rec_new(vz));
- }
- }
-
- /*
- continue the walkup, look if there are any other
- pertinent bicomps
- -- here "jump" to the next bicomp "up"
- */
- x = z;
- xin = 1;
- y = z;
- yin = 0;
- }
- else
- /*
- continue the traversal of the bicomp until one finds
- its (virtual) root
- */
- {
- embedg_VES_get_succ_on_ext_face(embed_graph, n,
- x, xin, FALSE, 0, &x, &xin);
- embedg_VES_get_succ_on_ext_face(embed_graph, n,
- y, yin, FALSE, 0, &y, &yin);
- }
- }
-}
-
-/*
- * walkdown.c
- */
-
-/*
- What:
- *****
-
- Implementing:
-
- The walkdown routine within the VES structure:
-
- walking down a bicomp rooted by a virtual vertex v^c
- and attempting to embed the back edges.
- This cannot be done if the walk has to stop due to the
- presence of externally active vertices on both
- the clockwise and the anticlockwise side of the bicomp.
-
-
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++
- from
-
- Simplified O(n) Planarity Algorithms (draft)
- ************************************
-
- John Boyer JBoyer@PureEdge.com, jboyer@acm.org
- Wendy Myrvold wendym@csr.uvic.ca
-
-
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++
- authors:
- ********
-
- Paulette Lieby (Magma), Brendan McKay (ANU)
-
- Started October 2001
-*/
-
-
-#include "planarity.h"
-
-#define IF_DEB(x) {}
-#define IF_DEB_EMBED(x) {}
-#define IF_DEB_BE(x) {}
-#define IF_DEB_SCE(x) {}
-#define IF_VERB(x) {}
-
-
-
-/* aproto: header embed_graph_protos.h */
-
-
-#ifndef PLANAR_IN_MAGMA
-#endif
-
-
-
-t_merge_queue
-embedg_walkdown (t_ver_edge *embed_graph, int n, int *edge_pos, int vv)
- /*
- walkdown from the virtual vertex:
- embed any back edges incident to vv if any
- and merge the encountered bicomps while walking down
- (very informative isn't it? :))
-
- ... and return the merge queue: will be useful when
- isolating the Kuratowski subgraphs
- */
-{
- t_merge_queue q;
- int v, c, vvout;
-
- ASSERT(embedg_VES_is_virtual_vertex(n, vv));
-
- /*
- find v and c such that v^c = vv
- */
- c = vv - n;
- v = embed_graph[c].DFS_parent;
-
- IF_DEB(
- fprintf(stdout, "walkdown from %d^%d, enter\n", v, c);
- )
-
- IF_DEB_EMBED(
- fprintf(stdout, "walkdown, embedding at start\n");
- embedg_VES_print_bigcomps(embed_graph, n);
- )
-
- /*
- create an empty merge queue
- */
- q = embedg_merge_queue_new(n);
-
- for (vvout = 0; vvout <= 1; vvout++)
- /*
- chose a direction for the walk, but walk in both
- directions unless a stopping vertex is encountered
- and other conditions are satisfied (see below)
- */
- {
- int w, win;
-
- embedg_VES_get_succ_on_ext_face(embed_graph, n, vv, vvout ^ 1,
- FALSE, 0, &w, &win);
-
- IF_DEB(
- fprintf(stdout, "walkdown, successor (outside while loop) from %d^%d:%d is %d:%d\n",
- embed_graph[vv-n].DFS_parent, vv-n, vvout ^ 1,
- w, win);
- )
-
- while (w != vv)
- /*
- is there no danger we walk the whole way back to vv
- and that all the vertices along the walk are inactive?
-
- answer: no, because of the short-cut edges.
-
- Short-cut edges are precisely inserted to remove the inactive
- vertices from the external face (ie they are "pushed"
- to the internal face of the bicomp)
- */
- {
- if (embed_graph[w].adjacent_to == v)
- /*
- ie there is a (directed) back edge [w, v]
- (would have been set in the previous walkup routine):
- embed this edge, but before that, merge all the bicomps
- previouslsy collected
- */
- {
- IF_DEB(
- fprintf(stdout, "walkdown, embed BE (%d^%d:%d, %d:%d)\n",
- embed_graph[vv-n].DFS_parent, vv - n, vvout,
- w, win);
- fprintf(stdout, "walkdown, queue before pulling elts\n");
- embedg_merge_queue_print(q);
- )
-
- while (!embedg_merge_queue_empty(q))
- {
- int u, uin, vu, vuout;
-
- embedg_merge_queue_get(&q, &u, &uin, &vu, &vuout);
-
- IF_DEB(
- fprintf(stdout, "walkdown, pull from queue (%d:%d, %d^%d:%d)\n",
- u, uin,
- embed_graph[vu-n].DFS_parent, vu-n,
- vuout);
- )
-
- embedg_VES_merge_pertinent_bicomps(
- embed_graph, n,
- vu, vuout, u, uin);
- }
- IF_DEB_BE(
- fprintf(stdout, "walkdown, before embed BE [%d^%d:%d, %d:%d]\n",
- embed_graph[vv-n].DFS_parent, vv - n,
- vvout, w, win);
- embedg_VES_print_adj_list(
- embed_graph, n, vv,
- TRUE);
- fprintf(stdout, "\n");
- embedg_VES_print_adj_list(
- embed_graph, n, vv,
- FALSE);
- )
-
- embedg_VES_embed_edge(embed_graph, n, edge_pos,
- BE, vv, vvout, w, win);
-
- IF_DEB_BE(
- fprintf(stdout, "walkdown, after embed BE [%d^%d:%d, %d:%d]\n",
- embed_graph[vv-n].DFS_parent, vv - n,
- vvout, w, win);
- embedg_VES_print_adj_list(
- embed_graph, n, vv,
- TRUE);
- fprintf(stdout, "\n");
- embedg_VES_print_adj_list(
- embed_graph, n, vv,
- FALSE);
- )
- IF_DEB_EMBED(
- fprintf(stdout, "walkdown, embedding after bicomp merge & back edge embedding\n");
- embedg_VES_print_bigcomps(embed_graph, n);
- )
-
- /*
- clear the adjacent_to flag
- */
- embed_graph[w].adjacent_to = n; /* "invalid" value */
- }
-
- if (!embedg_dlcl_is_empty(embed_graph[w].pertinent_bicomp_list))
- /*
- each pertinent child bicomp of w
- (pertinent: contains active (ie more back edges to embed)
- elts)
- must be traversed
- and pushed onto the queue for later bicomp merging
- */
- {
- int vw, vwout, x, xin, y, yin, s, sin;
-
- IF_DEB(
- fprintf(stdout, "walkdown, pertinent list for %d\n",
- w);
- embedg_dlcl_print(embed_graph[w].pertinent_bicomp_list);
- )
-
- /*
- get the first child in the pertinent list
- (see how the list is built in embedg_walkup)
-
- the child will eventually be removed from that list
- when merging the bicomps, and surely
- this bicomp (rooted at vw) will be merged (later)
- because it is active and hence pushed on
- the merge queue
- */
-
- /*
- we can start by pushing the vertex (w, win) on
- the merge queue
- */
- embedg_merge_queue_append_vertex(&q, embed_graph, n, w, win);
-
- IF_DEB(
- fprintf(stdout, "walkdown, push 1rst 2-tuple on queue\n");
- embedg_merge_queue_print(q);
- )
-
- /*
- get the first child in the pertinent list
- */
- vw = (embed_graph[w].pertinent_bicomp_list)->info;
-
- IF_DEB(
- fprintf(stdout, "walkdown, get pertinent %d^%d\n",
- embed_graph[vw - n].DFS_parent, vw - n);
- )
-
- /*
- start two walks starting at vw
- */
- embedg_VES_get_succ_active_on_ext_face(embed_graph, n,
- v , vw, 1,
- FALSE, 0, &x, &xin);
- embedg_VES_get_succ_active_on_ext_face(embed_graph, n,
- v, vw, 0,
- FALSE, 0, &y, &yin);
-
- /*
- because of the trick of inserting short-cut edges
- at previous stages, neighbours of vw are guaranteed
- to be active
-
- (however I'll use the more general
- embedg_VES_get_succ_active_on_ext_face
- instead of the restrictive
- embedg_VES_get_succ_on_ext_face
- because the walkdown may be used later to isolate
- Kuratowski minors, in a situation where SCEs could have
- been removed and thus where the successor on the
- external face will no longer be guaranteed to be active)
- (* actually I have decided to remove the SCE at the
- very last moment hence the above pb
- does not occur in the present implementation)
-
-
- it only remains to chose the next vertex where from
- to continue the walk; the choice is made in that order:
- - an internally active vertex
- (incident to v via a backedge but whose lowpoint
- is NO less than v)
- - a (externally active) pertinent vertex
- (incident to v via a backedge but whose lowpoint
- is less than v: ie which is also externally active)
- - as a last resort, a non-pertinent externally vertex,
- which is then a stopping vertex
- */
- IF_DEB(
- fprintf(stdout, "walkdown, x and y: %d, %d\n", x, y);
- )
-
- if (embedg_VES_is_ver_int_active(embed_graph, n,
- v, x))
- /*
- x is internally active
- */
- {
- IF_DEB(
- fprintf(stdout, "walkdown, x is int. active\n");
- )
-
- s = x;
- sin = xin;
- }
- else if (embedg_VES_is_ver_int_active(
- embed_graph, n,
- v, y))
- /*
- y is internally active
- */
- {
- IF_DEB(
- fprintf(stdout, "walkdown, y is int. active\n");
- )
-
- s = y;
- sin = yin;
- }
- else if (embedg_VES_is_ver_pertinent(
- embed_graph, n,
- v, x))
- /*
- x is pertinent
- */
- {
- IF_DEB(
- fprintf(stdout, "walkdown, x is pertinent\n");
- )
-
- s = x;
- sin = xin;
- }
- else
- /*
- tough luck: y may be externally active
- */
- {
- IF_DEB(
- fprintf(stdout, "walkdown, tough luck\n");
- )
-
- s = y;
- sin = yin;
- }
-
- IF_DEB(
- fprintf(stdout, "walkdown, succ. on pertinent bicomp is %d:%d\n", s, sin);
- )
-
- /*
- set vwout to respect consistency of traversal
- */
- vwout = s == x ? 0 : 1;
-
- /*
- now that we know vwout we can push (vw, vwout)
- on the merge queue, thus completing the 4-tuple
- (w, win, vw, vwout) describing a bicomp merge
- to occur at a later stage
- */
- embedg_merge_queue_append_virtual_vertex(&q, embed_graph, n,
- vw, vwout);
-
- IF_DEB(
- fprintf(stdout, "walkdown, push on queue (%d:%d, %d^%d:%d)\n",
- w, win, embed_graph[vw-n].DFS_parent, vw - n,
- vwout);
- embedg_merge_queue_print(q);
- )
-
- /*
- we continue the walk
- */
- w = s;
- win = sin;
- }
- /*
- at this point, w is either inactive or externally active
- (w can't be pertinent: its pertinent bicomp list is empty,
- and the back edge [w, v], if any, has already been embedded)
- */
- else if (embedg_VES_is_ver_inactive(embed_graph, n,
- v, w))
- /*
- w is inactive: continue with the walk on the external face
- and, insert a short cut edge so that w is removed
- from the external face
- */
- {
- int s, sin;
-
- IF_DEB(
- fprintf(stdout, "walkdown, %d has no pertinent bicomps and is inactive\n", w);
- )
-
- embedg_VES_get_succ_on_ext_face(embed_graph, n,
- w, win,
- FALSE, 0, &s, &sin);
-
- IF_DEB(
- fprintf(stdout, "walkdown, successor from %d:%d is %d:%d\n",
- w, win, s, sin);
- )
-
- /*
- s is the successor of w: we embed a short circuit edge
- [vv, s] if
- - the bicomp is externally active (to ensure that
- at a later stage this new face gets bisected:
- so that we don't end up with a face of degree 2
- (parallel edges))
- - if [s, vv] is not a back edge
-
- CONSEQUENTLY, adding SCE edges
- + does not destroy the planarity of the graph
- + ensures that each face has degree > 2 so that
- |E| <= 3 * |V| - 6 remains valid at all times
- + that the space allocated to the edges in embed_graph
- (via MAXDE(n)) is sufficient
-
- NOTE:
- the above still allows to embed a short-cut edge
- as an edge parallel to a tree edge OR a back edge
- (which then has been embedded previously
- so that [w].adjacent has been cleared)
-
- but again, since the degree of the face will be
- > 2, that's ok
-
- recall that c = vv - n
- */
- if (embed_graph[c].lowpoint < v
- /*
- bicomp rooted at vv is externally active
- */
- && embed_graph[s].adjacent_to != v)
- /*
- [s, vv] is not a back edge
- */
- {
- IF_DEB_SCE(
- fprintf(stdout, "walkdown, before embed SCE [%d^%d:%d, %d:%d]\n",
- embed_graph[vv-n].DFS_parent, vv - n,
- vvout, s, sin);
- embedg_VES_print_adj_list(
- embed_graph, n, vv,
- TRUE);
- fprintf(stdout, "\n");
- embedg_VES_print_adj_list(
- embed_graph, n, vv,
- FALSE);
-
- )
-
- embedg_VES_embed_edge(embed_graph,
- n, edge_pos,
- SCE, vv, vvout, s, sin);
- /*
- note also that the addition of short cut edges
- does not change the fact that the graph is planar
- (when it is, so we never run into the problem
- of creating/adding too many edges to embed-graph)
- */
- IF_DEB_SCE(
- fprintf(stdout, "walkdown, after embed SCE [%d^%d:%d, %d:%d]\n",
- embed_graph[vv-n].DFS_parent, vv - n,
- vvout, s, sin);
- embedg_VES_print_adj_list(
- embed_graph, n, vv,
- TRUE);
- fprintf(stdout, "\n");
- embedg_VES_print_adj_list(
- embed_graph, n, vv,
- FALSE);
-
- )
- IF_DEB(
- fprintf(stdout, "walkdown, embed SCE [%d^%d:%d, %d:%d]\n",
- embed_graph[vv-n].DFS_parent, vv - n,
- vvout, s, sin);
- )
-
- }
- /*
- continue the walk
- */
- w = s;
- win = sin;
- }
- else
- /*
- w is non-pertinent and externally active:
- it is a stopping vertex:
- we stop here and see if we can walk in the other direction
- */
- {
- IF_DEB(
- fprintf(stdout, "walkdown, %d is externally active\n", w);
- )
- break;
- }
- }
- if (!embedg_merge_queue_empty(q))
- /*
- mumm.... don't understand this one... let's see:
- the queue constains pertinent bicomps collected during one of
- the traversal of the external face, so that once
- a stopping vertex has been encountered and the queue
- is not empty, this means that we will be unable
- to embed any remaining back edges:
-
- it is important to remember that when w is a stopping vertex
- there is no choice left, since we walk the pertinent
- bicomp in both directions at once, and always choose
- the "best" possible vertex
- (see the choice strategy: (a) internally active, (b) pertinent,
- (c) the rest)
- */
- {
- IF_DEB(
- fprintf(stdout, "walkdown, merge queue is not empty\n");
- )
- break;
- }
- }
-
- /*
- and return the merge queue
- */
- return q;
-}
-
-
-
-
-/*
- * merge_queue_misc.c
- */
-
-/*
- What:
- *****
-
- Implementing:
-
- The merge queue stores the pertinent bicomps waiting to
- be merged before a subsequent back edge embedding.
- See walkdown.c
-
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++
- from
-
- Simplified O(n) Planarity Algorithms (draft)
- ************************************
-
- John Boyer JBoyer@PureEdge.com, jboyer@acm.org
- Wendy Myrvold wendym@csr.uvic.ca
-
-
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++
- authors:
- ********
-
- Paulette Lieby (Magma), Brendan McKay (ANU)
-
- Started October 2001
-*/
-
-
-#include "planarity.h"
-
-#define IF_DEB(x) {}
-#define IF_VERB(x) {}
-
-
-
-/* aproto: header embed_graph_protos.h */
-
-
-#ifndef PLANAR_IN_MAGMA
-#endif
-
-t_merge_queue
-embedg_merge_queue_new (int n)
- /*
- create a merge queue of 4 * (n-1) elts:
- we can only have at most n-1 virtual vertices,
- and for each of those we need to store 4 bits of info
- */
-{
- t_merge_queue q;
-
- q.start = q.end = 0;
- q.b = (int *) mem_malloc(sizeof(int) * 4 * (n - 1));
-
- return q;
-}
-
-void
-embedg_merge_queue_delete (t_merge_queue q)
-{
- mem_free(q.b);
-}
-
-
-boolean
-embedg_merge_queue_empty (t_merge_queue q)
-{
- return q.start == q.end ? TRUE : FALSE;
-}
-
-void
-embedg_merge_queue_print (t_merge_queue q)
-{
- int i;
-
- for (i = q.start; i < q.end; i++)
- {
- fprintf(stdout, "%d:%d ", q.b[i], q.b[i+1]);
- ++i;
- }
- fprintf(stdout, "\n");
-}
-
-void
-embedg_merge_queue_append (t_merge_queue *q, t_ver_edge *embed_graph,
- int n, int v, int vin, int vv, int vvout)
- /*
- append the 4-tuple (v, vin, vv, vvout)
- where v is a vertex and vv is its virtual counterpart
-
- we don't do much here, most of the work is done
- when pulling a bicomp/4-tuple from the queue
- */
-{
- /*
- is this really necessary? - YES!!!
- */
- ASSERT((*q).end < 4 * (n - 2));
- ASSERT(embedg_VES_is_vertex(n, v));
- ASSERT(embedg_VES_is_virtual_vertex(n, vv));
- ASSERT(embed_graph[vv - n].DFS_parent == v);
-
- (*q).b[(*q).end++] = v;
- (*q).b[(*q).end++] = vin;
- (*q).b[(*q).end++] = vv;
- (*q).b[(*q).end++] = vvout;
-}
-
-void
-embedg_merge_queue_append_vertex (t_merge_queue *q, t_ver_edge *embed_graph,
- int n, int v, int vin)
- /*
- same as above but were we only append the 2-tuple (v, vin),
- appending the 2-tuple (vv, vvout) at a later stage
- (see embedg_merge_queue_append_virtual_vertex)
- */
-{
- ASSERT((*q).end < 4 * (n - 2));
- ASSERT(embedg_VES_is_vertex(n, v));
-
- (*q).b[(*q).end++] = v;
- (*q).b[(*q).end++] = vin;
-
- IF_DEB(
- fprintf(stdout, "merge_queue_append_vertex, after, end is %d\n",
- (*q).end);
- )
-}
-
-void
-embedg_merge_queue_append_virtual_vertex (t_merge_queue *q,
- t_ver_edge *embed_graph, int n, int vv, int vvout)
- /*
- counterpart to embedg_merge_queue_append_vertex:
- here we append the 2-tuple (vv, vvout), vv = v^c,
- where the 2-tuple (v, vin) is already in the queue
- (see embedg_merge_queue_append_vertex)
- */
-{
- ASSERT(!embedg_merge_queue_empty(*q));
- ASSERT(embedg_VES_is_virtual_vertex(n, vv));
- ASSERT(embed_graph[vv - n].DFS_parent == (*q).b[(*q).end - 2]);
-
- (*q).b[(*q).end++] = vv;
- (*q).b[(*q).end++] = vvout;
-
- IF_DEB(
- fprintf(stdout, "merge_queue_append_virtual_vertex, after, end is %d\n",
- (*q).end);
- )
-}
-
-void
-embedg_merge_queue_get (t_merge_queue *q, int *v, int *vin, int *vv, int *vvout)
- /*
- pulling out a 4-tuple from the beginning of the FIFO queue
- */
-{
- ASSERT(!embedg_merge_queue_empty((*q)));
-
- *v = (*q).b[(*q).start++];
- *vin = (*q).b[(*q).start++];
- *vv = (*q).b[(*q).start++];
- *vvout = (*q).b[(*q).start++];
-}
-
-void
-embedg_merge_queue_prune (t_merge_queue *q, int *v,
- int *vin, int *vv, int *vvout)
- /*
- pulling out a 4-tuple from the end of the FIFO queue
- */
-{
- ASSERT(!embedg_merge_queue_empty((*q)));
-
- *vvout = (*q).b[--((*q).end)];
- *vv = (*q).b[--((*q).end)];
- *vin = (*q).b[--((*q).end)];
- *v = (*q).b[--((*q).end)];
-}
-
-/*
- * vertex_activity.c
- */
-
-/*
- What:
- *****
-
- Implementing:
-
- Determining a vertex's activity. This takes place within
- the VES structure.
-
-
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++
- from
-
- Simplified O(n) Planarity Algorithms (draft)
- ************************************
-
- John Boyer JBoyer@PureEdge.com, jboyer@acm.org
- Wendy Myrvold wendym@csr.uvic.ca
-
-
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++
- authors:
- ********
-
- Paulette Lieby (Magma), Brendan McKay (ANU)
-
- Started October 2001
-*/
-
-#include "planarity.h"
-
-#define IF_DEB(x) {}
-#define IF_VERB(x) {}
-
-
-
-/* aproto: header embed_graph_protos.h */
-
-
-#ifndef PLANAR_IN_MAGMA
-#endif
-
-
-
-
-boolean
-embedg_VES_is_ver_pertinent (t_ver_edge *embed_graph, int n, int v, int w)
- /*
- is w pertinent (wrt v)
- - the field adjacent_to = v: means there is a back edge [w, v]
- - or w has a non empty pertinent_bicomp_list
- */
-{
- boolean ans;
-
- ans = embed_graph[w].adjacent_to == v ? TRUE : FALSE;
-
- if (ans)
- return TRUE;
- else
- return embedg_dlcl_is_empty(embed_graph[w].pertinent_bicomp_list) ?
- FALSE : TRUE;
-}
-
-boolean
-embedg_VES_is_ver_ext_active (t_ver_edge *embed_graph, int n, int v, int w)
- /*
- is w externally active (wrt v)
- this is the case when either w's least_ancestor < v
- or the first member of w's separated_DFS_child_list has lowpoint < v
- (the vertices in separated_DFS_child_list are ordered by lowpoint)
-
- why? because w's separated_DFS_child_list may be empty
- (due to prior bicomp merging say) and so its children are in effect
- inactive
- */
-{
- boolean ans;
-
- ans = embed_graph[w].least_ancestor < v ? TRUE : FALSE;
-
- if (ans)
- return TRUE;
- else
- {
- if (embedg_dlcl_is_empty(embed_graph[w].separated_DFS_child_list))
- {
- return FALSE;
- }
- else
- {
- int c;
-
- c = (embed_graph[w].separated_DFS_child_list)->info;
- return embed_graph[c].lowpoint < v ? TRUE : FALSE;
- }
- }
-}
-
-
-boolean
-embedg_VES_is_ver_int_active (t_ver_edge *embed_graph, int n, int v, int w)
- /*
- is w internally active (wrt v):
- this happens when w is pertinent but NOT externally active
- */
-{
- return embedg_VES_is_ver_pertinent(embed_graph, n, v, w)
- && !embedg_VES_is_ver_ext_active(embed_graph, n, v, w);
-}
-
-boolean
-embedg_VES_is_ver_inactive (t_ver_edge *embed_graph, int n, int v, int w)
- /*
- is w inactive (wrt v), that is w nor pertinent nor externally activ
- */
-{
- return !embedg_VES_is_ver_pertinent(embed_graph, n, v, w)
- && !embedg_VES_is_ver_ext_active(embed_graph, n, v, w);
-}
-
-/*
- * merge_bicomps.c
- */
-
-/*
- What:
- *****
-
- Implementing:
-
- In the VES structure, merging two bicomponents.
- That is, merging the virtual vertex v^c with the
- actual vertex v while merging their respective
- adjacency lists.
- This must be done in a very specific manner so as to able to
- determine the subsequent internal/external faces.
- Also, great care must be taken so that the resulting
- adj. list for v is consistent (wrt to the direction
- of traversal).
-
-
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++
- from
-
- Simplified O(n) Planarity Algorithms (draft)
- ************************************
-
- John Boyer JBoyer@PureEdge.com, jboyer@acm.org
- Wendy Myrvold wendym@csr.uvic.ca
-
-
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++
- authors:
- ********
-
- Paulette Lieby (Magma), Brendan McKay (ANU)
-
- Started October 2001
-*/
-
-#include "planarity.h"
-
-#define IF_DEB(x) {}
-#define IF_DEB_ADJL(x) {}
-#define IF_VERB(x) {}
-
-
-
-/* aproto: header embed_graph_protos.h */
-
-
-void
-embedg_VES_merge_simple_bicomps (t_ver_edge *embed_graph, int n, int vv,
- int vvout, int v, int vin)
- /*
- merge the bicomp rooted at vv (vv a virtual vertex) with
- its counterpart v so that the resulting adjacency list for v
- is consistent and is the union of the adjacency lists for vv and v
-
- we treat the case that the bicomp may be flipped (vvout == vin)
- here
- */
-{
- int c, edge, twin, root_edge, cur, prev;
- int vout, vvin, e1, e2, e3, e4, e1out, e3out, e4in;
-
- /*
- find c such that [v^c, c] is the root edge of the bicomp
- rooted at vv = v^c
- */
- c = vv - n;
- ASSERT(embed_graph[c].DFS_parent == v);
-
- IF_DEB(
- fprintf(stdout, "merge_simple_bicomp, start: merge\n");
- embedg_VES_print_virtual_vertex(embed_graph, n, vv);
- fprintf(stdout, ":%d & ", vvout);
- embedg_VES_print_vertex(n, v);
- fprintf(stdout, ":%d\n", vin);
- )
-
- IF_DEB_ADJL(
- fprintf(stdout, "merge_simple_bicomp, adj. list for %d (before)\n", vv);
- embedg_VES_print_adj_list(embed_graph, n, vv,
- TRUE);
- fprintf(stdout, "\n");
- embedg_VES_print_adj_list(embed_graph, n, vv,
- FALSE);
- fprintf(stdout, "\n");
-
- fprintf(stdout, "merge_simple_bicomp, adj. list for %d (before)\n", v);
- embedg_VES_print_adj_list(embed_graph, n, v,
- TRUE);
- fprintf(stdout, "\n");
- embedg_VES_print_adj_list(embed_graph, n, v,
- FALSE);
- )
- /*
- find all edges incident to vv and (re)set all references
- to incidence to vv to incidence to v
-
- by the same token, find the root_edge [v^c, c]
-
- MOREVOVER, when vin == vvout, the bicomp (rooted by v^v = vv)
- will be flipped:
- we must invert the links of all the edges incident
- to vv so that their further union with v's adjacency list
- results in a consistent adjacency list for v!
-
- we do everything in one go
- */
-
- /*
- very careful here: a root edge must ALSO be a TE
- (because the same edge could have been added as a SCE)
- */
-
- root_edge = NIL;
- edge = embed_graph[vv].link[vvout];
- ASSERT(embedg_VES_is_edge(n, edge));
- if (embed_graph[edge].neighbour == c
- && embedg_VES_is_tree_edge(embed_graph, n, edge))
- {
- root_edge = edge;
- }
-
- if (vin == vvout)
- /*
- invert the links
- */
- {
- int in, out;
-
- in = embed_graph[edge].link[0];
- out = embed_graph[edge].link[1];
- embed_graph[edge].link[0] = out;
- embed_graph[edge].link[1] = in;
- }
- /*
- get the twin and set the neighbour there to v (was vv originally)
- */
- twin = embedg_VES_get_twin_edge(embed_graph, n, edge);
- ASSERT(embed_graph[twin].neighbour == vv);
- embed_graph[twin].neighbour = v;
-
- prev = vv;
- cur = edge;
- while (edge != vv)
- {
- edge =
- embedg_VES_get_next_in_dlcl(embed_graph, n,
- cur, prev);
-
- if (embedg_VES_is_edge(n, edge))
- /*
- get the twin again (and invert the links if need be)
- */
- {
- if (embed_graph[edge].neighbour == c
- && embedg_VES_is_tree_edge(embed_graph, n, edge))
- {
- root_edge = edge;
- }
-
- if (vin == vvout)
- {
- int in, out;
-
- in = embed_graph[edge].link[0];
- out = embed_graph[edge].link[1];
- embed_graph[edge].link[0] = out;
- embed_graph[edge].link[1] = in;
- }
-
- twin =
- embedg_VES_get_twin_edge(embed_graph, n, edge);
- ASSERT(embed_graph[twin].neighbour == vv);
- embed_graph[twin].neighbour = v;
-
- prev = cur;
- cur = edge;
- }
- else
- {
- ASSERT(edge == vv);
- /*
- only one vertex in the whole circular list
- */
- }
- }
- ASSERT(root_edge != NIL);
-
- /*
- and now union the adjacency lists of v and vv:
-
- let e1 be the edge record used to enter v
- e2 exit v
- e3 enter vv
- e4 exit vv :
-
- e1 -> v -> e2
- e3 -> vv -> e4
-
- the union of the list is done in such a way that
- - e1 and e4 are consecutive in v's adjacency list:
- they are now in the internal face
- - e3 is now the edge record used to enter v:
- it is on the external face (along with e2) :
-
- e1 -> e4
- e3 -> v -> e2
-
- (note that this does not assume that e1 & e2 are distinct
- or that e3 & e4 are distinct)
- */
- /*
- I must not forget the case where v is a lone vertex:
- this is the case where v has no DFS ancestor, ie when
- v is the root of a tree in the DFS forest
- */
-
- e1 = embed_graph[v].link[vin];
- vout = 1 ^ vin;
- e2 = embed_graph[v].link[vout];
-
- if (e1 != v)
- {
- ASSERT(e2 != v);
- ASSERT(embedg_VES_is_edge(n, e1));
- ASSERT(embedg_VES_is_edge(n, e2));
- }
-
- e4 = embed_graph[vv].link[vvout];
- ASSERT(embedg_VES_is_edge(n, e4));
-
- vvin = 1 ^ vvout;
- e3 = embed_graph[vv].link[vvin];
- ASSERT(embedg_VES_is_edge(n, e3));
-
- /*
- must take care of the adjacency list's consistency of traversal
- (will be important only when recovering the embedding)
- */
- if (e1 == e2)
- {
- ASSERT(embed_graph[e1].link[0] == embed_graph[e1].link[1]);
- if (vin == vvout)
- /*
- the bicomp will be flipped:
- must take 1 ^ vvout - difficult to explain -- later...
- */
- {
- e1out = 1 ^ vvout;
- }
- else
- {
- e1out = vvout;
- }
- }
- else
- {
- e1out = embed_graph[e1].link[0] == v ? 0 : 1;
- }
- if (e3 == e4)
- {
- ASSERT(embed_graph[e3].link[0] == embed_graph[e3].link[1]);
- e3out = 1 ^ vin;
- e4in = vin;
- }
- else
- {
- e4in = embed_graph[e4].link[0] == vv ? 0 : 1;
- e3out = embed_graph[e3].link[0] == vv ? 0 : 1;
- }
-
- IF_DEB(
- fprintf(stdout, "merge_simple_bicomp, before union of lists, e1\n");
- embedg_VES_print_edge(embed_graph, n, e1);
- fprintf(stdout, "merge_simple_bicomp, e3\n");
- embedg_VES_print_edge(embed_graph, n, e3);
- fprintf(stdout, "merge_simple_bicomp, e4\n");
- embedg_VES_print_edge(embed_graph, n, e4);
- )
-
- /*
- make e1 and e4 consecutive in the adjacency list
- */
- embed_graph[e1].link[e1out] = e4;
- embed_graph[e4].link[e4in] = e1;
- embed_graph[e3].link[e3out] = v;
- embed_graph[v].link[vin] = e3;
-
- IF_DEB(
- fprintf(stdout, "merge_simple_bicomp, after union of lists, e1\n");
- embedg_VES_print_edge(embed_graph, n, e1);
- fprintf(stdout, "merge_simple_bicomp, e3\n");
- embedg_VES_print_edge(embed_graph, n, e3);
- fprintf(stdout, "merge_simple_bicomp, e4\n");
- embedg_VES_print_edge(embed_graph, n, e4);
- )
-
- /*
- also, want to "disable" vv links, meaning then that
- vv is no longer a root of a bicomp
- */
- embed_graph[vv].link[0] = embed_graph[vv].link[1] = vv;
-
- IF_DEB_ADJL(
- fprintf(stdout, "merge_simple_bicomp, adj. list for %d (after)\n", vv);
- embedg_VES_print_adj_list(embed_graph, n, vv,
- TRUE);
- fprintf(stdout, "\n");
- embedg_VES_print_adj_list(embed_graph, n, vv,
- FALSE);
- fprintf(stdout, "\n");
-
- fprintf(stdout, "merge_simple_bicomp, adj. list for %d (after)\n", v);
- embedg_VES_print_adj_list(embed_graph, n, v,
- TRUE);
- fprintf(stdout, "\n");
- embedg_VES_print_adj_list(embed_graph, n, v,
- FALSE);
- )
-
- ASSERT(embedg_VES_is_adj_list_consistent(embed_graph, n, v));
-
- /*
- finally, give an orientation to the (formerly) root edge [vv, c]
- to keep traversal consistent (when recovering embedding)
- */
- if (vin == vvout)
- /*
- flip: set the sign of the root edge to clockwise
-
- note: a bicomp is merged only once, so there is no need to
- "flip" the root_edge's sign: it is set once at initialisation
- and then changed here if need be.
- */
- {
- embed_graph[root_edge].sign = CLOCKW;
-
- IF_VERB(
- fprintf(stdout, "merge_simple_bicomp, flip for %d, sign is now %d for %d of type %d\n",
- c, embed_graph[root_edge].sign, root_edge, embed_graph[root_edge].type);
- embedg_VES_print_edge(embed_graph, n, root_edge);
- )
- }
-}
-
-
-
-void
-embedg_VES_merge_pertinent_bicomps (t_ver_edge *embed_graph, int n,
- int vv, int vvout, int v, int vin)
- /*
- the bicomps to be merged are pertinent: on top (and before)
- performing a simple merge, there are several things to do
- related to the merging to pertinent bicomps
- */
-{
- /*
- a note of caution:
- it is (very) likely that after a bicomp merge the resulting
- bicomp is not biconnected (and hence traversal of the external face
- of the bicomp via embedg_VES_get_succ_on_ext_face is non-sensical)
-
- remembering that a PERTINENT bicomp merge is ALWAYS followed
- by a back edge embedding we see that the end result is then a bicomp
- where again traversal of the external face
- via embedg_VES_get_succ_on_ext_face will make sense
- */
- t_dlcl *pertinent_list, *head, *rep_in_parent_list, *parent_list;
- int c;
-
- /*
- find c such that [v^c, c] is the root edge of the bicomp
- rooted at vv = v^c
- */
- c = vv - n;
- ASSERT(embed_graph[c].DFS_parent == v);
-
- /*
- two things to do first:
- - remove vv from head of pertinent_bicomp_list of v
- - remove c from separated_DFS_child_list of v
-
- one may ask the point of this since the separated_DFS_child_list
- seems to mirror pertinent_bicomp_list: but this is not exactly so:
- + pertinent_bicomp_list is ordered according to the activity
- of the (virtual) vertices
- + separated_DFS_child_list is ordered according to the vertices'
- lowpoint values
- in effect, it could (almost?*) be said that these two lists
- are in reverse order (the *almost bit would warrant some thinking here)
- */
-
- /*
- remove vv from head of pertinent_bicomp_list of v
- */
- pertinent_list = head = embed_graph[v].pertinent_bicomp_list;
- ASSERT(!embedg_dlcl_is_empty(pertinent_list));
- ASSERT(head->info == vv);
-
- IF_DEB(
- fprintf(stdout, "merge_pertinent_bicomp, start: merge\n");
- embedg_VES_print_virtual_vertex(embed_graph, n, vv);
- fprintf(stdout, ":%d & ", vvout);
- embedg_VES_print_vertex(n, v);
- fprintf(stdout, ":%d\n", vin);
- )
-
- IF_DEB(
- fprintf(stdout, "merge_pertinent_bicomp, pertinent bicomp_list of %d (before)\n", v);
- embedg_dlcl_print(embed_graph[v].pertinent_bicomp_list);
- )
-
-
- embed_graph[v].pertinent_bicomp_list =
- embedg_dlcl_delete_first(pertinent_list);
-
- IF_DEB(
- fprintf(stdout, "merge_pertinent_bicomp, pertinent bicomp_list of %d (after)\n", v);
- embedg_dlcl_print(embed_graph[v].pertinent_bicomp_list);
- )
-
- /*
- vv = v^c: remove c from separated_DFS_child_list of v
- */
- rep_in_parent_list = embed_graph[c].rep_in_parent_list;
- ASSERT(!embedg_dlcl_is_empty(rep_in_parent_list));
-
- parent_list = embed_graph[v].separated_DFS_child_list;
- ASSERT(!embedg_dlcl_is_empty(parent_list));
- embed_graph[v].separated_DFS_child_list =
- embedg_dlcl_delete_rec(parent_list, rep_in_parent_list);
-
- /*
- that's it, it remains to merge, ie. union the adjacency list,
- and flipping the bicomp if necessary
- */
- embedg_VES_merge_simple_bicomps(embed_graph, n,
- vv, vvout, v, vin);
-}
-
-
-
-
-
-
-/*
- * embed_edge.c
- */
-
-/*
- What:
- *****
-
- Implementing:
-
- Embedding an edge so that it lies on the external face of a bicomp.
- We work here with the VES structure.
-
-
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++
- from
-
- Simplified O(n) Planarity Algorithms (draft)
- ************************************
-
- John Boyer JBoyer@PureEdge.com, jboyer@acm.org
- Wendy Myrvold wendym@csr.uvic.ca
-
-
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++
- authors:
- ********
-
- Paulette Lieby (Magma), Brendan McKay (ANU)
-
- Started October 2001
-*/
-
-
-#include "planarity.h"
-
-#define IF_DEB(x) {}
-#define IF_DEB_EMBED(x) {}
-#define IF_VERB(x) {}
-
-
-/* aproto: header embed_graph_protos.h */
-
-
-void
-embedg_VES_embed_edge (t_ver_edge *embed_graph, int n, int *edge_pos,
- int edge_type, int vv, int vvout, int w, int win)
- /*
- embed the edge (vv, w) (vv a virtual vertex, w a vertex) between
- vv and the edge vvout
- and the edge win and w
-
- so that after the embedding, one exits vv via (vv, w) and
- enters w via the twin (w, vv)
- */
-{
- int temp, tempin, tempout;
-
- ASSERT(edge_type == BE || edge_type == SCE);
- ASSERT(embedg_VES_is_virtual_vertex(n, vv));
- ASSERT(embedg_VES_is_vertex(n, w));
-
- IF_DEB(
- fprintf(stdout, "embed_edge, (%d:%d)\n", vv, w);
- )
-
- /*
- first, set the edge [vv, w] with the appropriate info
-
- when [vv, w] is a back edge there is some more work to do
- (see the walkup procedure for the extra information we need
- to copy here
- */
- (*edge_pos)++;
- ASSERT(*edge_pos < 2*n + 2 * MAXE(n));
- embed_graph[*edge_pos].neighbour = w;
- embed_graph[*edge_pos].type = edge_type;
- embed_graph[*edge_pos].sign = CCLOCKW;
- if (edge_type == BE)
- {
- ASSERT(embed_graph[w].adjacent_to ==
- embed_graph[vv - n].DFS_parent);
-
- /*
- PLUS: originally when the back edge [w, vv] was
- created (in the dfs preprocessing stage), it carried in
- .in_adjl the index of this directed edge in the
- adjacency list
-
- but now, note that we are actually inserting the
- directed edge [vv, w] in vv's adjacency list,
- meaning that in_adjl and twin_in_adjl
- must be exchanged!
- */
- embed_graph[*edge_pos].in_adjl = embed_graph[w].twin_in_adjl;
- embed_graph[*edge_pos].twin_in_adjl = embed_graph[w].in_adjl;
-
- ASSERT(embed_graph[w].mult % 2 == 0);
- /*
- the original graph is always undirected:
- we store its number of undirected edges
- */
- embed_graph[*edge_pos].mult = embed_graph[w].mult / 2;
- }
-
- /*
- insert this edge between vertex record for vv
- and edge record vv.link[vvout]
- */
- temp = embed_graph[vv].link[vvout];
-
- if (embed_graph[temp].link[0] == embed_graph[temp].link[1])
- /*
- this needs special treatment to ensure consistency of
- orientation
- */
- {
- ASSERT(embed_graph[temp].link[0] == vv);
- tempin = 1 ^ vvout;
- }
- else
- {
- tempin = embed_graph[temp].link[0] == vv ? 0 : 1;
- }
-
- IF_DEB(
- fprintf(stdout, "embed_edge, edge out of vv\n");
- embedg_VES_print_edge(embed_graph, n, temp);
- )
-
- embed_graph[vv].link[vvout] = *edge_pos;
- embed_graph[temp].link[tempin] = *edge_pos;
- /*
- the links for *edge_pos must also be "consistent"
- */
- embed_graph[*edge_pos].link[vvout] = temp;
- embed_graph[*edge_pos].link[vvout ^ 1] = vv;
-
- /*
- now create/set the twin edge, the directed edge [w, vv]
- */
- (*edge_pos)++;
- ASSERT(*edge_pos < 2*n + 2 * MAXE(n));
- embed_graph[*edge_pos].neighbour = vv;
- embed_graph[*edge_pos].type = edge_type;
- embed_graph[*edge_pos].sign = CCLOCKW;
- if (edge_type == BE)
- {
- embed_graph[*edge_pos].in_adjl = embed_graph[w].in_adjl;
- embed_graph[*edge_pos].twin_in_adjl = embed_graph[w].twin_in_adjl;
- embed_graph[*edge_pos].mult = embed_graph[w].mult / 2;
- }
-
- /*
- and insert the twin edge between edge record w.link[win]
- and vertex record for w
- */
- temp = embed_graph[w].link[win];
-
- if (embed_graph[temp].link[0] == embed_graph[temp].link[1])
- /*
- again, special treatment to ensure consistency of orientation
- */
- {
- ASSERT(embed_graph[temp].link[0] == w);
- tempout = 1 ^ win;
- }
- else
- {
- tempout = embed_graph[temp].link[0] == w ? 0 : 1;
- }
-
- IF_DEB(
- fprintf(stdout, "embed_edge, edge in of w\n");
- embedg_VES_print_edge(embed_graph, n, temp);
- )
-
- embed_graph[w].link[win] = *edge_pos;
- embed_graph[temp].link[tempout] = *edge_pos;
- /*
- and consistent orientation
- */
- embed_graph[*edge_pos].link[win] = temp;
- embed_graph[*edge_pos].link[win ^ 1] = w;
-}
-
-
-
-void
-embedg_VES_add_edge (t_ver_edge *embed_graph, int n, int *edge_pos,
- int v, int w, boolean MARK, int mark)
- /*
- add the edge (v, w): this is DIFFERENT from
- embedg_VES_embed_edge in the sense
- that the present function will only be used
- when building the Kuratowski homeomorphs:
-
- that is, we are in a situation where the graph is NON planar
-
- consequently it doesn't matter much where in the adjacency
- lists of v & w the edge is added:
- let's say that we always add it at the beginning
-
- for our sanity's sake, we'll ensure that the resulting
- adjacency lists remain consistent!
-
- and we add the edge as a BE!
- PLUS we mark it with mark in MARK true
- */
-{
- int temp;
-
- ASSERT(embedg_VES_is_vertex(n, v) ||
- embedg_VES_is_virtual_vertex(n, v));
- ASSERT(embedg_VES_is_vertex(n, w) ||
- embedg_VES_is_virtual_vertex(n, w));
-
- IF_DEB(
- fprintf(stdout, "add_edge, (%d:%d)\n", v, w);
- )
-
- /*
- not sure this is the best place to do this: mark the endpoints
- */
- if (MARK)
- {
- embed_graph[v].visited = mark;
- embed_graph[w].visited = mark;
- }
-
- /*
- first, set the edge [v, w] with the appropriate info
- */
- (*edge_pos)++;
- ASSERT(*edge_pos < 2*n + 2 * MAXE(n));
- embed_graph[*edge_pos].neighbour = w;
- embed_graph[*edge_pos].type = BE;
- /*
- the edge's orientation will be the same as the vertex
- */
- embed_graph[*edge_pos].sign = embed_graph[v].sign;
- /*
- and mark the edge
- */
- if (MARK)
- {
- embed_graph[*edge_pos].visited = mark;
- }
-
- /*
- insert this edge between vertex record for v
- and edge record v.link[1]
- */
- temp = embed_graph[v].link[1];
-
- IF_DEB(
- fprintf(stdout, "add_edge, edge out of v\n");
- embedg_VES_print_edge(embed_graph, n, temp);
- )
-
- embed_graph[v].link[1] = *edge_pos;
- embed_graph[temp].link[0] = *edge_pos;
- /*
- the links for *edge_pos must also be "consistent"
- */
- embed_graph[*edge_pos].link[1] = temp;
- embed_graph[*edge_pos].link[0] = v;
-
- /*
- now create/set the twin edge, the directed edge [w, v]
- */
- (*edge_pos)++;
- ASSERT(*edge_pos < 2*n + 2 * MAXE(n));
- embed_graph[*edge_pos].neighbour = v;
- embed_graph[*edge_pos].type = BE;
- embed_graph[*edge_pos].sign = embed_graph[w].sign;
- if (MARK)
- {
- embed_graph[*edge_pos].visited = mark;
- }
-
- /*
- insert this edge between vertex record for w
- and edge record w.link[1]
- */
- temp = embed_graph[w].link[1];
-
- IF_DEB(
- fprintf(stdout, "add_edge, edge out of w\n");
- embedg_VES_print_edge(embed_graph, n, temp);
- )
-
- embed_graph[w].link[1] = *edge_pos;
- embed_graph[temp].link[0] = *edge_pos;
- /*
- and consistent orientation
- */
- embed_graph[*edge_pos].link[1] = temp;
- embed_graph[*edge_pos].link[0] = w;
-}
-
-
-/*
- * recover.c
- */
-
-/*
- What:
- *****
-
- Implementing:
-
- From the VES data structure recover either the embedding ot
- the obstruction into the
-
- t_sparseg_ver_struct,
- t_sparseg_adjl_struct,
- t_sparseg_embed_struct
-
- data types.
-
-
- (This is no even quite true: for some obscure reason
- I recover the obstruction as a dlcl[] structure to be
- converted later.
- The obvious reason being that it is easier to check as such.
- Maybe I leave it as it is...)
-
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++
- from
-
- Simplified O(n) Planarity Algorithms (draft)
- ************************************
-
- John Boyer JBoyer@PureEdge.com, jboyer@acm.org
- Wendy Myrvold wendym@csr.uvic.ca
-
-
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++
- authors:
- ********
-
- Paulette Lieby (Magma), Brendan McKay (ANU)
-
- Started October 2001
-*/
-
-
-#include "planarity.h"
-
-#define IF_DEB(x) {}
-#define IF_DEB_EMBED_MULT(x) {}
-#define IF_DEB_EMBED_LOOPS(x) {}
-#define IF_DEB_EMBED(x) {}
-#define IF_DEB_CHECK_EMBED(x) {}
-#define IF_DEB_FACES(x) {}
-#define IF_VERB(x) {}
-#define IF_DEB_SCE(x) {}
-#define IF_DEB_OBS(x) {}
-#define IF_DEB_CHECK_OBS(x) {}
-#define IF_CPU(x) {}
-
-
-
-/* aproto: header embed_graph_protos.h */
-
-/* aproto: beginstatic -- don't touch this!! */
-static void embedg_recover_embedding_embed_mult
- (t_dlcl **, t_embed_sparse_rep *, int, int, int, int, int *, boolean *, int *);
-static void embedg_recover_embedding_embed_loops
- (t_dlcl **, t_embed_sparse_rep *, int, int, int *, boolean *);
-static t_dlcl **embedg_get_reduced_obs (t_dlcl **, int);
-static boolean embedg_is_red_obs_K33 (t_dlcl **, int);
-static boolean embedg_is_red_obs_K5 (t_dlcl **, int);
-/* aproto: endstatic -- don't touch this!! */
-
-
-#ifndef PLANAR_IN_MAGMA
-#endif
-
-void
-embedg_recover_embedding (
- t_ver_sparse_rep *V,
- t_adjl_sparse_rep *A, /* input (original sparse graph) */
- t_ver_edge *embed_graph,
- int n,
- int nbr_e,
- t_dlcl **mult_edges,
- t_ver_sparse_rep **vertices,
- t_embed_sparse_rep **embedding
-)
- /*
- recover the embedding
- to prepare for the final Magma type for sparse & embedded graph
-
- we assume that all vertices/edges have been given their
- orientation
-
- at this stage we also embed the multiple edges and loops
- which were set aside in mult_edges by
- sparseg_adjl_dfs_preprocessing:
-
- as it turns out the last bit is pretty hairy!
- */
-{
- /*
- the idea is to return an array of vertices and an array
- representing the embedding
- (careful: need to weedout the SCE)
-
- vertices: (*vertices)[i].first_edge contains index
- to first edge in embedding
-
- embedding: a doubly linked circular list of edges,
- for each record/edge e = (*embedding)[i]:
- e.in_adjl: index in A of e
- e.next: next edge in CLOCKW
- (as an index in the embedding)
- e.prev: previous edge in CLOCKW
- (as an index in embedding)
- e.inv: inverse edge (as an index in embedding)
- e.mark: a mark for this edge
-
- let's say that this new array is a slimmed down version of embed_graph
-
- one issue to address:
- - for edge e, find its index in A: this should be found
- in either the embed_graph[v] record of the mult_edges[v] record
- */
- int index_embed, v, mult, w, v_w_in_embed, new_first_edge;
- boolean set_next;
-
- IF_DEB(
- fprintf(stdout, "in recover emb.\n");
- sparseg_dlcl_print(mult_edges, n);
- );
-
- *vertices = (t_ver_sparse_rep *)
- mem_malloc(sizeof(t_ver_sparse_rep) * n);
- *embedding = (t_embed_sparse_rep *)
- mem_malloc(sizeof(t_embed_sparse_rep) * 2 * nbr_e);
-
- index_embed = 0;
- set_next = TRUE;
- for (v = 0; v < n; v++)
- {
- int v_l, orient, in, out, e, cur_e, next_e;
-
- /*
- we take v's label
- */
- v_l = embed_graph[v].label;
-
- /*
- first let's deal with the isolated vertex case: those
- that refer to self
- */
- if (embed_graph[v].link[0] == v)
- {
- int temp_index_embed;
-
- ASSERT(embed_graph[v].link[1] == v);
-
- /*
- there may be [v, v] loops for this vertex, must check this
- */
- temp_index_embed = index_embed - 1;
- /*
- temp_index_embed is pre-increased below
- */
- embedg_recover_embedding_embed_loops(mult_edges, *embedding,
- nbr_e, v,
- &temp_index_embed,
- &set_next);
-
- if (temp_index_embed > index_embed - 1)
- /*
- must fix beginning and end of adjacency list:
- */
- {
- (*vertices)[v_l].first_edge = index_embed;
- (*embedding)[temp_index_embed].next =
- (*vertices)[v_l].first_edge;
- (*embedding)[(*vertices)[v_l].first_edge].prev =
- temp_index_embed;
-
- index_embed = temp_index_embed;
- index_embed += 1;
- }
- else
- {
- (*vertices)[v_l].first_edge = NIL;
- }
- continue;
- }
-
- /*
- get v's orientation, and from this decide the way in which
- v's adjacency list will be traversed
- (recall that the list is supposed to be consistent, so no bad
- surprises)
- */
- orient = embed_graph[v].sign;
- in = orient == CCLOCKW ? 0 : 1;
- out = 1 ^ in;
-
- e = embed_graph[v].link[out];
- while (embedg_VES_is_short_cut_edge(embed_graph, n, e))
- {
- e = embed_graph[e].link[out];
- }
- ASSERT(embedg_VES_is_edge(n, e)
- && !embedg_VES_is_short_cut_edge(embed_graph, n, e));
- /*
- strictly speaking there should be no SCEs left at this stage...
-
- if there are SCEs in v's list, it must be the case that
- the list also contains tree or back edges...
- */
-
- (*vertices)[v_l].first_edge = index_embed;
-
- IF_DEB_EMBED(
- fprintf(stdout, "recov. embed. DFI %d vertex %d at %d (edges) and %d (embedding)\n",
- v, v_l, index_e, (*vertices)[v_l].first_edge);
- )
-
- cur_e = e;
- while (TRUE)
- {
- next_e = embed_graph[cur_e].link[out];
- while (embedg_VES_is_short_cut_edge(embed_graph, n, next_e))
- {
- next_e = embed_graph[next_e].link[out];
- }
- ASSERT(!embedg_VES_is_short_cut_edge(embed_graph, n, next_e));
-
- if (next_e == v)
- /*
- end of adjacency list
- */
- {
- break;
- }
-
- ASSERT(embedg_VES_is_edge(n, next_e));
-
- (*embedding)[index_embed].in_adjl = embed_graph[cur_e].in_adjl;
- (*embedding)[index_embed].next = index_embed + 1; /* next in adj.
- list */
- (*embedding)[index_embed].mark = NIL; /* mark */
-
- /*
- cur_e's twin is trickier:
- we'll use twin's label field to store cur_e's index in
- the embedding
-
- if cur_e's label != NIL this means that cur_e's twin
- is already stored in edges/embedding and consequently
- that cur_e.label = index of its twin (in the embedding)
-
- note that it is safe to do so since an edge's label
- has no meaning
- */
- if (embed_graph[cur_e].label != NIL)
- {
- (*embedding)[index_embed].inv = embed_graph[cur_e].label;
-
- /*
- but fix the twin by the same token
- */
- (*embedding)[embed_graph[cur_e].label].inv = index_embed;
- ASSERT((*embedding)[embed_graph[cur_e].label].in_adjl ==
- embed_graph[cur_e].twin_in_adjl);
- }
- else
- /*
- we store cur_e's index in the embedding in twin's label
- */
- {
- int twin;
-
- twin = embedg_VES_get_twin_edge(embed_graph, n, cur_e);
- embed_graph[twin].label = index_embed;
- }
-
- /*
- so the only thing we couldn't update yet is
- (*embedding)[index_embed].prev, cur_e previous edge in the list
-
- but we can do this for next_e
- */
- (*embedding)[index_embed + 1].prev = index_embed;
-
- /*
- we check if there are any multiple edges or loops
- to embed
- */
- w = embed_graph[cur_e].neighbour;
- mult = embed_graph[cur_e].mult - 1;
- /*
- one was for the TE or BE edge
- */
-
- if (index_embed == (*vertices)[v_l].first_edge)
- /*
- when looking for multiple edges/loops
- we must temporarily "close" this ordered
- list of vertices when in presence of the first
- edge in the list:
-
- not doing this would mean that
- (*embedding)[(*vertices)[v_l].first_edge].prev
- contains some irrelevant value which may cause
- (major) trouble when embedding inverses of
- multiple edges...
- */
- {
- (*embedding)[(*vertices)[v_l].first_edge].prev = index_embed;
- }
-
- embedg_recover_embedding_embed_mult(mult_edges, *embedding,
- nbr_e, v, w, mult,
- &index_embed, &set_next,
- &new_first_edge);
- embedg_recover_embedding_embed_loops(mult_edges, *embedding,
- nbr_e, v, &index_embed,
- &set_next);
- set_next = TRUE;
-
- /*
- yes, it may be the case that (*vertices)[v_l].first_edge
- change while in embedg_recover_embedding_embed_mult
- -- see that function for more
- */
- (*vertices)[v_l].first_edge = new_first_edge == NIL ?
- (*vertices)[v_l].first_edge : new_first_edge;
-
- /*
- that's all, we proceed to read a new edge in the list
- */
- index_embed += 1;
- cur_e = next_e;
- }
-
- /*
- now next_e = v so that cur_e is the last edge in v's adjacency list
- we must deal with this case separately
- */
-
- /*
- fix cur_e in embedding (and its twin)
- */
- (*embedding)[index_embed].in_adjl = embed_graph[cur_e].in_adjl;
-
- /*
- we temporarily set next of cur_e in to index_embed + 1
- */
- (*embedding)[index_embed].next = index_embed + 1;
- (*embedding)[index_embed].mark = NIL; /* mark */
-
- /*
- fix cur_e's twin
- */
- if (embed_graph[cur_e].label != NIL)
- {
- (*embedding)[index_embed].inv = embed_graph[cur_e].label;
- (*embedding)[embed_graph[cur_e].label].inv = index_embed;
- ASSERT((*embedding)[embed_graph[cur_e].label].in_adjl ==
- embed_graph[cur_e].twin_in_adjl);
- }
- else
- {
- int twin;
-
- twin = embedg_VES_get_twin_edge(embed_graph, n, cur_e);
- embed_graph[twin].label = index_embed;
- }
-
- /*
- we temporarily set the next record's prev field:
- but we can do that only if we haven't processed
- all the edges yet
- */
- if (index_embed < 2 * nbr_e - 1)
- {
- (*embedding)[index_embed + 1].prev = index_embed;
-
- /*
- again, check if there are any multiple edges/loops
- to embed
- */
- w = embed_graph[cur_e].neighbour;
- mult = embed_graph[cur_e].mult - 1;
- /*
- one was for the TE or BE edge
- */
- v_w_in_embed = index_embed;
-
- if (index_embed == (*vertices)[v_l].first_edge)
- /*
- same comment as above
- */
- {
- (*embedding)[(*vertices)[v_l].first_edge].prev = index_embed;
- }
-
- embedg_recover_embedding_embed_mult(mult_edges, *embedding,
- nbr_e, v, w, mult,
- &index_embed, &set_next,
- &new_first_edge);
- embedg_recover_embedding_embed_loops(mult_edges, *embedding,
- nbr_e, v, &index_embed,
- &set_next);
-
- /*
- same comment as above
- */
- (*vertices)[v_l].first_edge = new_first_edge == NIL ?
- (*vertices)[v_l].first_edge : new_first_edge;
- }
-
- /*
- to finish off, we must set:
-
- cur_e's next field:
- next of cur_e in the list is ... vertices[v_l].first_edge
-
- cur_e's next's previous field...
- */
- if (set_next)
- /*
- set_next (poorly named) is used to indicate which
- edges must be updated to "close off" the list:
-
- if set_next is TRUE, we are in the standard case
- where the last edge in the ordered adj. list
- is at index_embed
-
- if set_next is FALSE, the last edge in the ordered adj. list
- is at v_w_in_embed: because it could have happened
- (in embedg_recover_embedding_embed_mult only)
- that the edges have been "wedged" between
- v_w_in_embed.prev and v_w_in_embed,
- leaving v_w_in_embed the last in the list
- */
- {
- (*embedding)[index_embed].next = (*vertices)[v_l].first_edge;
- (*embedding)[(*vertices)[v_l].first_edge].prev = index_embed;
- }
- else
- {
- (*embedding)[v_w_in_embed].next = (*vertices)[v_l].first_edge;
- (*embedding)[(*vertices)[v_l].first_edge].prev = v_w_in_embed;
- }
- set_next = TRUE;
-
- /*
- a simple check
- */
- ASSERT(embedg_dlcl_is_empty(mult_edges[v]));
-
- /*
- we can process another vertex
- */
- index_embed += 1;
- }
- /*
- when this is done there are a few things that must hold
- */
- ASSERT(index_embed == 2 * nbr_e);
-}
-
-
-static void
-embedg_recover_embedding_embed_mult (t_dlcl **mult_edges,
- t_embed_sparse_rep *embedding, int nbr_e, int v, int w,
- int mult, int *index_embed, boolean *set_next, int *first_edge)
- /*
- see if the directed edge [v, w] is multiple: if so embed it
- in embedding
-
- moreover if there are any [v, v] loops do that too
- */
-{
- /*
- we take care of multiple edges: for tree edges and back
- edges their multiplicity is indicated by the
- embed_graph[cur_e].mult field (which records the number
- of undirected edges)
-
- for loops hovewer this information is stored in the mult
- field of the FIRST encountered neighbour v in v's neighbour
- list
- */
- t_dlcl *p;
- int v_w_in_embed, v_w_prev;
- boolean do_twins, start, do_first_edge;
-
- IF_DEB_EMBED_MULT(
- fprintf(stdout, "in recover emb. mult, v %d w %d mult %d\n",
- v, w, mult);
- )
-
- /*
- the current index_embed value is the edge [v, w]:
- I must record this value as it will be needed
- later
- */
- v_w_in_embed = *index_embed;
- start = TRUE;
- *set_next = TRUE;
- do_twins = FALSE;
- *first_edge = NIL;
- do_first_edge = FALSE;
- v_w_prev = NIL;
- while (mult > 0)
- {
- ASSERT(!embedg_dlcl_is_empty(mult_edges[v]));
- p = embedg_dlcl_find(mult_edges[v], w);
- /*
- note that using embedg_dlcl_find to always find
- the first in the list with p->info == w
- is ok here since any previous such records would
- have been deleted/removed from the list
- */
- ASSERT(p != NP);
- /*
- otherwise we couldn't have mult > 0 !
- */
-
- *index_embed += 1;
-
- /*
- once again I must use a similar sort of trick as in the
- main function to deal with the inverse edge:
-
- the inverse edge is to be found in mult_edges[w]:
- if p->twin_in_adjl (which was initialised to NIL
- and has NOT been set in the DFS preprocessing),
- if p->twin_in_adjl != NIL, then
- a. its inverse in mult_edges[w] has already been embedded
- in *embedding
- b. its index there is stored in p->twin_in_adjl
- precisely
- */
- if (p->twin_in_adjl != NIL)
- {
- if (! start)
- /*
- if the first the multiple edges' inverse is already
- stored, then this is true for ALL of them
- */
- {
- ASSERT(do_twins == TRUE);
- }
- do_twins = TRUE;
- }
- else
- /*
- similarly, if the first the multiple edges' inverse is
- not already stored, then this is true for ALL of them
- */
- {
- ASSERT(do_twins == FALSE);
- }
-
- embedding[*index_embed].in_adjl = p->in_adjl;
- embedding[*index_embed].mark = NIL;
-
- /*
- as we will see do_twins has to be treated differently
- */
- if (!do_twins)
- /*
- this is pretty standard as works as the
- main recover function
- */
- {
- t_dlcl *i_m_l, *i_p;
-
- embedding[*index_embed].next = *index_embed + 1;
-
- /*
- we store the current index in the embedding in
- the twin/inverse's twin_in_adjl field
- */
- i_p = i_m_l = mult_edges[w];
- ASSERT(!embedg_dlcl_is_empty(i_m_l));
- i_p = embedg_dlcl_find_with_NIL_twin_in_adjl(i_m_l, v);
- ASSERT(i_p != NP);
- ASSERT(i_p->twin_in_adjl == NIL);
-
- i_p->twin_in_adjl = *index_embed;
-
- /*
- to finish off this bit we set embedding[*index_embed + 1].prev
-
- but I can only set this prev field if I haven't reached
- the end of the embedding[] array: this is why we needed
- nbr_e (total number of edges to embed) as input
- */
-
- if (*index_embed < 2 * nbr_e - 1)
- {
- embedding[*index_embed + 1].prev = *index_embed;
- }
- }
- else
- /*
- how to insert the inverses of multiple edges already
- in the embedding:
-
- if one studies how the twin_in_adjl field has been
- set while dealing with the inverses of the
- present multiple edges one sees that
- the latter must be inserted in counter clockwise
- order (assuming that the inverses were inserted
- in clockwise order)
-
- this is necessariy to ensure a correct matching between
- the edge and its inverse
- */
- {
-
- embedding[*index_embed].inv = p->twin_in_adjl;
-
- /*
- fix the twin by the same token
- */
- embedding[p->twin_in_adjl].inv = *index_embed;
-
- /*
- general (reverse) insertion for these edges
- */
- embedding[*index_embed].prev = *index_embed + 1;
- embedding[*index_embed].next = *index_embed - 1;
-
- /*
- ok, that was the easy bit, things are a bit more complicated
- below...
- */
- if (start)
- /*
- the edges are "wedged" between
- embedding[v_w_in_embed].prev and v_w_in_embed,
-
- hence the following
- */
- {
- v_w_prev = embedding[v_w_in_embed].prev;
- if (v_w_prev == v_w_in_embed)
- /*
- in this case the first edge in the adj. list
- of the vertex whose first_edges is v_w_in_embed
- will be changed
- (because we insert in reverse order)
- */
- {
- do_first_edge = TRUE;
- }
-
- embedding[*index_embed].next = v_w_in_embed;
- embedding[v_w_in_embed].prev = *index_embed;
-
- ASSERT(embedding[embedding[*index_embed].inv].prev ==
- embedding[v_w_in_embed].inv);
- ASSERT(embedding[embedding[v_w_in_embed].inv].next ==
- embedding[*index_embed].inv);
- }
-
- if (mult == 1)
- /*
- last inv. edge in this list to add
- */
- {
- ASSERT(v_w_prev != NIL);
-
- /*
- must fix embedding[v_w_prev].next appropriately
- (and embedding[*index_embed].prev)
-
- this may be overwritten later on, but not necessarily so
-
- the next_set flag will enable us to decide
- which edge ends this adjacency list: see above
- */
-
- embedding[*index_embed].prev = v_w_prev;
- embedding[v_w_prev].next = *index_embed;
- *set_next = FALSE;
-
- ASSERT(embedding[embedding[*index_embed].inv].prev ==
- embedding[*index_embed - 1].inv);
- ASSERT(embedding[embedding[*index_embed - 1].inv].next ==
- embedding[*index_embed].inv);
-
- if (do_first_edge)
- /*
- the first edge is the last one added
- */
- {
- *first_edge = *index_embed;
- }
-
- embedding[v_w_in_embed].next = *index_embed + 1;
- if (*index_embed < 2 * nbr_e - 1)
- {
- embedding[*index_embed + 1].prev = v_w_in_embed;
- }
- }
-
- ASSERT(embedding[embedding[*index_embed].inv].prev ==
- embedding[embedding[*index_embed].next].inv);
- }
-
- /*
- to finish off this bit we delete the p record from m_l
- and set embedding[*index_embed + 1].prev
- */
- mult_edges[v] = embedg_dlcl_delete_rec(mult_edges[v], p);
-
- mult--;
- start = FALSE;
- }
- /*
- conclusion: sevral days to get this working! *sigh*
- */
-}
-
-
-
-
-static void
-embedg_recover_embedding_embed_loops (t_dlcl **mult_edges,
- t_embed_sparse_rep *embedding, int nbr_e, int v,
- int *index_embed, boolean *set_next)
- /*
- embed the [v, v] loops
- */
-{
- /*
- the loops' multiplicity is stored in the mult
- field of the FIRST encountered neighbour v in v's neighbour
- list
- */
- t_dlcl *p;
- int nbr_loops;
-
- /*
- have a look if there are any [v. v] loops
- */
- p = embedg_dlcl_find(mult_edges[v], v);
- if (p == NP)
- {
- return;
- }
-
- /*
- when there are loops to add to the adjaceny list,
- edge insertion resume in the "normal" clockwaise saya, way:
- so we reset set_next to true
- */
- *set_next = TRUE;
-
- nbr_loops = p->mult;
- ASSERT(nbr_loops % 2 == 0);
- /*
- we counted directed edges
- */
- nbr_loops /= 2;
-
- IF_DEB_EMBED_LOOPS(
- fprintf(stdout, "in recover emb. loops, nbr_loops [v, v] %d\n",
- nbr_loops);
- )
-
- while (nbr_loops > 0)
- /*
- a loop requires to embed two directed edges
- */
- {
- p = embedg_dlcl_find(mult_edges[v], v);
- ASSERT(p != NP);
-
- *index_embed += 1;
-
- embedding[*index_embed].in_adjl = p->in_adjl;
- embedding[*index_embed].next = *index_embed + 1;
- embedding[*index_embed].mark = NIL;
- embedding[*index_embed].inv = *index_embed + 1;
- embedding[*index_embed + 1].prev = *index_embed;
-
- mult_edges[v] = embedg_dlcl_delete_rec(mult_edges[v], p);
-
- IF_DEB_EMBED_LOOPS(
- fprintf(stdout, "in recover emb. loops, mid\n");
- embedg_dlcl_print(mult_edges[v]);
- );
-
- /*
- now do the "inverse" loop
- */
- p = embedg_dlcl_find(mult_edges[v], v);
- ASSERT(p != NP);
-
- *index_embed += 1;
-
- embedding[*index_embed].in_adjl = p->in_adjl;
- embedding[*index_embed].next = *index_embed + 1;
- embedding[*index_embed].mark = NIL;
- embedding[*index_embed].inv = *index_embed - 1;
-
- if (*index_embed < 2 * nbr_e - 1)
- {
- embedding[*index_embed + 1].prev = *index_embed;
- }
- mult_edges[v] = embedg_dlcl_delete_rec(mult_edges[v], p);
-
- nbr_loops--;
-
- IF_DEB_EMBED_LOOPS(
- fprintf(stdout, "in recover emb. loops, end\n");
- embedg_dlcl_print(mult_edges[v]);
- );
- }
-}
-
-
-
-
-void
-embedg_recov_embed_walk_proper_face (int n, int e, t_adjl_sparse_rep *A,
- t_embed_sparse_rep *embedding, boolean MARK, int mark)
- /*
- do a proper face walk in the recovered embedding starting
- at index e in the embedding
- */
-{
- int cur, next;
-
- IF_DEB_FACES(
- fprintf(stdout, "recov. emb. proper face walk\n");
- fprintf(stdout, "[-, %d] ",
- A[embedding[e].in_adjl].end_vertex);
- )
-
- cur = e;
- next = NIL;
- while (next != e)
- /*
- to get the next in a proper face traversal:
- get the previous of the cur's inverse
- */
- {
- int inv;
-
- inv = embedding[cur].inv;
- next = embedding[inv].prev;
-
- ASSERT(embedding[next].mark != mark);
-
- if (MARK)
- {
- embedding[next].mark = mark;
- }
-
- cur = next;
- IF_DEB_FACES(
- fprintf(stdout, "[-, %d] ",
- A[embedding[cur].in_adjl].end_vertex);
- )
- }
- IF_DEB_FACES(
- fprintf(stdout, "\n");
- )
-}
-
-
-
-boolean
-embedg_check_recov_embedding (int n, int nbr_e, int nbr_comp,
- t_ver_sparse_rep *vertices, t_adjl_sparse_rep *A,
- t_embed_sparse_rep *embedding)
- /*
- check if the recovered embedding is a valid embedding
- SHOULD ONLY be use after creation, that is, after having
- recovered the embedding from the VES structure
- (because of the mark MIN_EMBED_MARK we use)
- */
-{
- int v, e, f;
-
- f = 0;
- /*
- do all the edges in embedding:
- careful: we have 2 * nbr_e to visit (the edge and its inverse!)
- */
- for (e = 0; e < 2 * nbr_e; e++)
- {
- /*
- we check if the current edge is marked: if not, we
- traverse a proper face bordered by this edge
- */
- if (embedding[e].mark != MIN_EMBED_MARK)
- /*
- we --hopefully-- perform this check only after creation
- where mark == NIL
- */
- {
- embedg_recov_embed_walk_proper_face(n, e, A, embedding,
- TRUE, MIN_EMBED_MARK);
- f++;
- }
- }
-
- /*
- must also count a face for each isolated vertex
- */
- for (v = 0; v < n; v++)
- {
- if (vertices[v].first_edge == NIL)
- f++;
- }
-
- IF_DEB_CHECK_EMBED(
- fprintf(stdout, "recovered embedding, n: %d\t e: %d\t C: %d\t f: %d\n",
- n, nbr_e, nbr_comp, f);
- )
-
- return f == 2 * nbr_comp + nbr_e - n ? TRUE : FALSE;
-}
-
-
-t_dlcl **
-embedg_recover_obstruction (t_ver_edge *embed_graph, int n, minor m, int *nbr_e)
- /*
- recover the obstruction as a t_dlcl * structure:
- and return the number of edges: lets say we agree on returning
- the number of undirected edges
- -- I don't know yet which way to do, directed or undirected???
-
- so far in the algorithm we only dealt with DFIs,
- but now, we retrieve the obstruction not wrt DFIs but
- wrt the vertices' labels
- */
-{
- /*
- so I am looking, in embed_graph, for the vertices and edges
- marked MARK_MINORS(n)
- */
-
- int v;
- t_dlcl **obs;
-
- obs = (t_dlcl **) mem_malloc(sizeof(t_dlcl *) * n);
- for (v = 0; v < n; v++)
- obs[v] = NP;
-
- *nbr_e = 0;
- for (v = 0; v < 2*n; v++)
- /*
- must check real vertices as well as virtual vertices
- */
- {
- int e;
-
- if (embed_graph[v].link[0] == v)
- /*
- isolated vertex case
- */
- {
- ASSERT(embed_graph[v].link[1] == v);
- continue;
- }
-
- e = embed_graph[v].link[0];
- while (e != v)
- {
- ASSERT(embedg_VES_is_edge(n, e));
- if (embed_graph[e].visited == MARK_MINORS(n))
- {
- int cur_v, neigh;
-
- /*
- virtual vertices may still hang around
- */
- /*
- let's get the "actual" v:
- note that the statement below is safe since if v were
- not a valid virtual vertex (ie [v - n].DFS_parent = n)
- it would have an empty
- adjacency list and we wouldn't be there anyway
- */
- cur_v = embedg_VES_get_ver(embed_graph, n, v);
-
- neigh = embedg_VES_get_ver(embed_graph, n,
- embed_graph[e].neighbour);
-
- /*
- again, cur_v and neigh are DFIs,
- we want vertex labels at this stage
- */
- cur_v = embed_graph[cur_v].label;
- neigh = embed_graph[neigh].label;
- sparseg_dlcl_append_to_neigh_list(obs, n, cur_v, neigh,
- embed_graph[e].in_adjl);
- (*nbr_e)++;
- }
- e = embed_graph[e].link[0];
- }
- }
-
- IF_DEB_OBS(
- fprintf(stdout, "recovering the obstruction\n");
- sparseg_dlcl_print(obs, n);
- );
-
- ASSERT(*nbr_e % 2 == 0);
- *nbr_e /= 2;
-
- return obs;
-}
-
-
-static t_dlcl **
-embedg_get_reduced_obs (t_dlcl **obs, int n)
- /*
- reduce the obstruction by removing all degree 2 vertices
- (so that they become isolated vertices)
- */
-{
- t_dlcl **reduced;
- int v;
-
- reduced = (t_dlcl **) mem_malloc(sizeof(t_dlcl *) * n);
- for (v = 0; v < n; v++)
- {
- reduced[v] = embedg_dlcl_copy(obs[v]);
- }
-
- for (v = 0; v < n; v++)
- {
- t_dlcl *n_l, *n_l_b, *p, *new_n_v, *n_l_x, *b_in_n_x;
- int a, b, n_x;
-
- n_l = reduced[v];
- while (!embedg_dlcl_is_empty(n_l)
- && embedg_dlcl_list_last(n_l) == embedg_dlcl_list_next(n_l))
- /*
- pick out which vertices have deg 2
- */
- {
- a = n_l->info;
- b = embedg_dlcl_list_next(n_l)->info;
- /*
- we remove the edge (v, b), or rather, we identify v and b:
- b will then be an isolated vertex
-
- fix v's neighbour list: all of b's neighbours
- are now v's neighbours
- */
- reduced[v] = n_l =
- embedg_dlcl_delete_rec(n_l, embedg_dlcl_list_last(n_l));
-
- p = n_l_b = reduced[b];
- ASSERT(!embedg_dlcl_is_empty(n_l_b));
- n_x = p->info;
- if (n_x != v)
- {
- new_n_v = embedg_dlcl_rec_new(n_x);
- reduced[v] = n_l = embedg_dlcl_cat(n_l, new_n_v);
-
- /*
- and in n_x neighbour list, we must replace b by v
- */
- n_l_x = reduced[n_x];
- b_in_n_x = embedg_dlcl_find(n_l_x, b);
- b_in_n_x->info = v;
- }
- /*
- and do this for all of b's neighbours
- */
- p = embedg_dlcl_list_next(p);
- while (p != n_l_b)
- {
- n_x = p->info;
- if (n_x != v)
- {
- new_n_v = embedg_dlcl_rec_new(n_x);
- reduced[v] = n_l = embedg_dlcl_cat(n_l, new_n_v);
- n_l_x = reduced[n_x];
- b_in_n_x = embedg_dlcl_find(n_l_x, b);
- b_in_n_x->info = v;
- }
- p = embedg_dlcl_list_next(p);
- }
- embedg_dlcl_delete(reduced[b]);
- reduced[b] = NP;
- }
- }
-
- IF_DEB_CHECK_OBS(
- fprintf(stdout, "reducing the obstruction\n");
- sparseg_dlcl_print(reduced, n);
- )
-
- /*
- now check no degree 2 vertices are left
- */
- for (v = 0; v < n; v++)
- {
- t_dlcl *n_l;
-
- n_l = reduced[v];
- if (!embedg_dlcl_is_empty(n_l))
- {
- ASSERT(embedg_dlcl_list_last(n_l) != embedg_dlcl_list_next(n_l));
- }
- }
-
- return reduced;
-}
-
-static boolean
-embedg_is_red_obs_K33 (t_dlcl **reduced, int n)
- /*
- check if the (reduced) obstruction is indeed K33
- */
-{
- int v, order, vs[6], i, b1[3];
-
- /*
- check that order == 6 and that the obstruction is cubic
- */
- order = 0;
- for (v = 0; v < n; v++)
- {
- if (!embedg_dlcl_is_empty(reduced[v]))
- {
- if (order == 6)
- {
- return FALSE;
- }
- order++;
- vs[order - 1] = v;
-
- if (embedg_dlcl_length(reduced[v]) != 3)
- {
- return FALSE;
- }
- }
- }
- if (order != 6)
- {
- return FALSE;
- }
-
- /*
- check if bipartite
- */
- v = vs[0];
- ASSERT(!embedg_dlcl_is_empty(reduced[v]));
- b1[0] = reduced[v]->info;
- b1[1] = embedg_dlcl_list_next(reduced[v])->info;
- b1[2] = embedg_dlcl_list_prev(reduced[v])->info;
-
- for (i = 1; i < 6; i++)
- {
- t_dlcl *n_v;
-
- v = vs[i];
- n_v = reduced[v];
- ASSERT(!embedg_dlcl_is_empty(n_v));
- if (n_v->info == b1[0]
- || embedg_dlcl_list_next(n_v)->info == b1[0]
- || embedg_dlcl_list_prev(n_v)->info == b1[0])
- {
- if ((n_v->info != b1[1]
- && embedg_dlcl_list_next(n_v)->info != b1[1]
- && embedg_dlcl_list_prev(n_v)->info != b1[1])
- &&
- (n_v->info != b1[2]
- && embedg_dlcl_list_next(n_v)->info != b1[2]
- && embedg_dlcl_list_prev(n_v)->info != b1[2]))
- {
- return FALSE;
- }
- }
- else
- {
- if ((n_v->info == b1[1]
- || embedg_dlcl_list_next(n_v)->info == b1[1]
- || embedg_dlcl_list_prev(n_v)->info == b1[1])
- ||
- (n_v->info == b1[2]
- || embedg_dlcl_list_next(n_v)->info == b1[2]
- || embedg_dlcl_list_prev(n_v)->info == b1[2]))
- {
- return FALSE;
- }
- }
- }
-
- return TRUE;
-}
-
-
-static boolean
-embedg_is_red_obs_K5 (t_dlcl **reduced, int n)
- /*
- check if the (reduced) obstruction is indeed K5
- */
-{
- int v, order;
-
- /*
- check that order == 5 and that the obstruction is quadric
- */
- order = 0;
- for (v = 0; v < n; v++)
- {
- if (!embedg_dlcl_is_empty(reduced[v]))
- {
- if (order == 5)
- {
- return FALSE;
- }
- order++;
-
- if (embedg_dlcl_length(reduced[v]) != 4)
- {
- return FALSE;
- }
- }
- }
-
- return TRUE;
-}
-
-
-boolean
-embedg_check_recov_obs (t_dlcl **obs, int n, minor m)
- /*
- check if the recovered obstruction is one of K33 or K5
- */
-{
- t_dlcl **reduced;
- boolean ans;
-
- reduced = embedg_get_reduced_obs(obs, n);
- if (m != MINOR_E5)
- {
- ans = embedg_is_red_obs_K33(reduced, n);
- }
- else
- {
- ans = embedg_is_red_obs_K5(reduced, n);
- }
-
- sparseg_dlcl_delete(reduced, n);
- return ans;
-}
-/*
- * obstruction.c
- */
-
-/*
- What:
- *****
-
- Implementing:
-
- The graph is not planar: we recover the obstruction from the VES structure
- and check it as well.
- (Some of these checks will disappear later)
-
-
-
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++
- from
-
- Simplified O(n) Planarity Algorithms (draft)
- ************************************
-
- John Boyer JBoyer@PureEdge.com, jboyer@acm.org
- Wendy Myrvold wendym@csr.uvic.ca
-
-
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++
- authors:
- ********
-
- Paulette Lieby (Magma), Brendan McKay (ANU)
-
- Started October 2001
-*/
-
-
-#include "planarity.h"
-
-#define IF_DEB(x) {}
-#define IF_VERB(x) {}
-#define IF_DEB_OBS(x) {}
-#define IF_DEB_CHECK_OBS(x) {}
-#define IF_CPU(x) {}
-#define IF_DEB_MINOR(x) {}
-
-
-/* aproto: header embed_graph_protos.h */
-
-void
-embedg_obstruction (
- t_ver_sparse_rep *V,
- t_adjl_sparse_rep *A, /* the input graph as a sparse graph */
- t_dlcl **dfs_tree, /* a sparse graph rep. for the dfs tree
- -- vertices are as DFIs
- -- and children are ordered wrt
- lowpoint value
- */
- t_dlcl **back_edges, /* for each vertex v, a dlcl
- of the back edges [v, x] incident to v
- where x is a DESCENDANT of v
- (vertices are given as DFIs)
- */
- t_ver_edge *embed_graph, /* output of tester */
- int n, /* size of the graph */
- int *edge_pos, /* pos. in embed_graph for addition
- of the next edge */
- int v,
- int w_in, /* the unembedded directed back edge
- [w_in, v]
- */
- t_ver_sparse_rep **OV, /* the obstruction as an adjacency list */
- t_adjl_sparse_rep **OA,
- int *nbr_e_obs /* obstruction's #edges */
-)
-
- /*
- the graph is non planar: we must mark & recover the K33 or K5
- homeomorph
- */
-{
- int *ver_orient;
- minor m;
- t_dlcl **obs;
-
- /*
- this is magma code - must be removed
- */
- float sttime, time_to_now;
-
- IF_CPU(
- sttime = time_current_user();
- )
-
- /*
- we will NOT remove the short-cut edges at this stage:
- we'll have to perform another walkdown in embedg_iso_is_minor_A
- so
- 1. saves time when looking for ext. active vertices
- 2. more importantly this enables us to ascertain that the number of
- edges in embed_graph (even after completing whichever obstruction
- applying in this case) will NEVER be > 3*n - 5!!!
- 3. SCEs are then removed in embedg_iso_is_minor_A
- (obligatory path for every possible case)
- */
-
- /*
- we must compute each vertex's orientation (wrt flipped bicomps)
- and set the edges' orientation:
-
- the other day I was wondering why this was necessary in this
- instance (because after all we won't get an embedding):
- orientation is required bacause later in the piece we
- do a proper face traversal (I guess for Minor C testing)
- */
- ver_orient = embedg_vertices_orientation(embed_graph, n);
- embedg_VES_set_orientation(embed_graph, n, ver_orient);
- mem_free(ver_orient);
-
- m = embedg_mark_obstruction(dfs_tree, back_edges,
- embed_graph, n, edge_pos, v, w_in);
-
- /*
- get the obstruction
- */
- obs = embedg_recover_obstruction(embed_graph, n, m, nbr_e_obs);
-
- /*
- and check it
- */
- if (!embedg_check_recov_obs(obs, n, m))
- {
- sparseg_dlcl_delete(obs, n);
- DIE();
- }
-
- sparseg_dlcl_to_sparseg(obs, n, *nbr_e_obs, OV, OA);
- sparseg_dlcl_delete(obs, n);
-
- /*
- just for the sake of it, chcek if the obstruction is
- a subgraph of the input graph
- */
- if (!sparseg_adjl_sub(*OV, n, *OA, V, n, A))
- {
- DIE();
- }
-
- IF_DEB_OBS(
- sparseg_adjl_print(*V, n, *A, FALSE);
- )
-
- IF_CPU(
- fprintf(stdout, "CPU for obstruction recovering %f\n",
- (time_current_user() - sttime));
- )
-}
-
-
-
-
-
-
-
-minor
-embedg_mark_obstruction (
- t_dlcl **dfs_tree, /* a sparse graph rep. for the dfs tree
- -- vertices are as DFIs
- -- and children are ordered wrt
- lowpoint value
- */
- t_dlcl **back_edges, /* for each vertex v, a dlcl
- of the back edges [v, x] incident to v
- where x is a DESCENDANT of v
- (vertices are given as DFIs)
- */
- t_ver_edge *embed_graph, /* output of tester */
- int n, /* size of the graph */
- int *edge_pos, /* pos. in embed_graph for addition
- of the next edge */
- int v,
- int w_in /* the unembedded directed back edge
- [w_in, v]
- */
-)
- /*
- the graph is non planar: we must mark & recover the K33 or K5
- homeomorph
- */
-{
- int c, vr, x, y, w;
- int *path_v, *path_e, nbr_v, entry_in_path_e;
- boolean px_attached_high, py_attached_high, is_minor_D;
- minor m;
-
-
- IF_CPU(
- float sttime; float time_to_now;
-
- sttime = time_current_user();
- )
-
-
- /*
- find c such that v^c is the root of the biconnected
- component on which the walkdown failed
- */
- c = embedg_iso_get_c_of_v(embed_graph, n, v, w_in);
-
- /*
- now: decide which minor we are dealing with and mark the
- appropriate one (vertices/edges marked as MARK_MINOR(n)
- in embed_graph)
- */
- if (embedg_iso_is_minor_A(embed_graph, n, edge_pos, v, c, &vr))
- {
- embedg_mark_minor_A(dfs_tree, back_edges,
- embed_graph, n, edge_pos, v, c, vr);
-
- IF_DEB_MINOR(
- fprintf(stdout, "Minor A\n");
- )
-
- return MINOR_A;
- }
-
- /*
- get the externally active vertices x & y and the pertinent w
- on the external face of the bicomp rooted by v^c
-
- and determine if minor B
- */
- if (embedg_iso_is_minor_B(embed_graph, n, edge_pos, v, c,
- &x, &y, &w))
- {
- embedg_mark_minor_B(dfs_tree, back_edges,
- embed_graph, n, edge_pos, v, c,
- x, y, w);
- IF_DEB_MINOR(
- fprintf(stdout, "Minor B\n");
- )
-
- IF_CPU(
- fprintf(stdout, "CPU for obstruction isolation %f\n",
- time_current_user() - sttime);
- )
-
- return MINOR_B;
- }
-
- /*
- the remaining cases: must get the highest x-y path
-
- it will be containing in path_v (vertices), path_e (edges)
- */
- embedg_iso_get_highest_x_y_path(embed_graph, n, MARK_EXT_FACE(n),
- MARK_EXT_FACE_L(n),
- MARK_EXT_FACE_R(n),
- v, c, x, y, w,
- &path_v, &path_e,
- &nbr_v, &entry_in_path_e,
- &px_attached_high,
- &py_attached_high,
- &is_minor_D);
-
- /*
- we are in the minor C case if either one of p_x or p_y
- is attached high
- */
- if (px_attached_high || py_attached_high)
- {
- embedg_mark_minor_C(dfs_tree, back_edges, embed_graph, n, edge_pos,
- v, c, x, y, w,
- path_v, path_e, nbr_v,
- px_attached_high, py_attached_high);
- IF_DEB_MINOR(
- fprintf(stdout, "Minor C\n");
- )
-
- mem_free(path_v);
- mem_free(path_e);
-
- IF_CPU(
- fprintf(stdout, "CPU for obstruction isolation %f\n",
- time_current_user() - sttime);
- )
-
- return MINOR_C;
- }
-
- if (is_minor_D)
- {
- embedg_mark_minor_D(dfs_tree, back_edges, embed_graph, n, edge_pos,
- v, c, x, y, w,
- path_v, path_e, nbr_v, entry_in_path_e);
- IF_DEB_MINOR(
- fprintf(stdout, "Minor D\n");
- )
-
- mem_free(path_v);
- mem_free(path_e);
-
- IF_CPU(
- fprintf(stdout, "CPU for obstruction isolation %f\n",
- time_current_user() - sttime);
- )
-
- return MINOR_D;
- }
-
- /*
- finally, the minor E case
- */
- m = embedg_mark_minor_E(dfs_tree, back_edges, embed_graph, n, edge_pos,
- v, c, x, y, w,
- path_v, path_e, nbr_v);
- switch (m)
- {
- case MINOR_E1:
- IF_DEB_MINOR(
- fprintf(stdout, "Minor E1\n");
- )
- break;
- case MINOR_E2:
- IF_DEB_MINOR(
- fprintf(stdout, "Minor E2\n");
- )
- break;
- case MINOR_E3:
- IF_DEB_MINOR(
- fprintf(stdout, "Minor E3\n");
- )
- break;
- case MINOR_E4:
- IF_DEB_MINOR(
- fprintf(stdout, "Minor E4\n");
- )
- break;
- case MINOR_E5:
- IF_DEB_MINOR(
- fprintf(stdout, "Minor E5\n");
- )
- break;
- case MINOR_A:
- case MINOR_B:
- case MINOR_C:
- case MINOR_D:
- case MINOR_E:
- case NBR_MINORS:
- break;
- }
-
- mem_free(path_v);
- mem_free(path_e);
-
- IF_CPU(
- fprintf(stdout, "CPU (scaled) for obstruction isolation %f\n",
- (time_current_user() - sttime) / e);
- )
-
- return m;
-}
-/*
- * isolator.c
- */
-
-/*
- What:
- *****
-
- Implementing:
-
- The graph is non planar: we isolate the obstruction.
-
-
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++
- from
-
- Simplified O(n) Planarity Algorithms (draft)
- ************************************
-
- John Boyer JBoyer@PureEdge.com, jboyer@acm.org
- Wendy Myrvold wendym@csr.uvic.ca
-
-
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++
- authors:
- ********
-
- Paulette Lieby (Magma), Brendan McKay (ANU)
-
- Started October 2001
-*/
-
-
-#include "planarity.h"
-
-#define IF_DEB(x) {}
-#define IF_VERB(x) {}
-#define IF_DEB_TREE(x) {}
-#define IF_DEB_EDGES(x) {}
-#define IF_CPU(x) {}
-/* #define IF_DEB_MINOR(x) {x} -- Not Used */
-
-
-/* aproto: header embed_graph_protos.h */
-
-#ifndef PLANAR_IN_MAGMA
-#endif
-
-
-
-
-int
-embedg_iso_get_c_of_v (t_ver_edge *embed_graph, int n, int v, int w)
- /*
- the edge [v, w] (w a descendant of v) remains unembedded
- after the walkdown returns
-
- find c such that v^c is the root of the biconnected
- component on which the walkdown failed
- */
-{
- /*
- how to do this??? easy! follow the DFS tree path as given
- by the field DFS_parent
- */
-
- int u;
-
- u = embed_graph[w].DFS_parent;
- while (embed_graph[u].DFS_parent != v)
- {
- u = embed_graph[u].DFS_parent;
- }
- /*
- this is guaranteed to succeed given the structure of the DFS tree
- and the fact that there exists a back edge [w, v]
- */
-
- return u;
-}
-
-
-boolean
-embedg_iso_is_minor_A (t_ver_edge *embed_graph, int n,
- int *edge_pos, int v, int c, int *vr)
- /*
- determines if the obstruction is a minor A
- */
-{
- /*
- to do this we again call the walkdown routine with v^c as input,
- the walkdown routine will fail (since there will be an
- un-embedded back edge incident to v and to a vertex
- in the subtree rooted by v^c)
-
- the obstruction is a minor A if the merge queue returned by the
- walkdown is non-empty, if this is the case we return
- the bicomp last appended to the queue
- */
- int vv;
- t_merge_queue q;
-
- vv = c + n;
-
- q = embedg_walkdown(embed_graph, n, edge_pos, vv);
- /*
- we MUST remove the SCEs here: this is the only place where it
- will be done when looking for and recovering an obstruction
-
- this is safe since this very function applies to ALL cases!
- */
- embedg_remove_SCE(embed_graph, n, *edge_pos);
-
- if (!embedg_merge_queue_empty(q))
- /*
- the bicomp of interest is the last in the queue
- */
- {
- int r, rin, vrout;
-
- embedg_merge_queue_prune(&q, &r, &rin, vr, &vrout);
- embedg_merge_queue_delete(q);
- return TRUE;
- }
- else
- {
- embedg_merge_queue_delete(q);
- return FALSE;
- }
-}
-
-
-void
-embedg_iso_get_x_y_w (t_ver_edge *embed_graph, int n, int v, int r,
- int c, int mark, int mark_l, int mark_r, int *x, int *y, int *w)
- /*
- the obstruction is one of minor B, C, D, E.
-
- get the externally active vertices x & y along the
- external face paths starting at r^c
-
- get a pertinent vertex w along the lower external
- face path between x and y
-
- external activity and pertinence are wrt v
-
- all the vertices on the external face r^c...x...w
- and r^c...y...w will be marked (the visited field)
- */
-{
- int vr, vrin, x_y[4];
- int s, sin, cur, curin;
-
- vr = c + n;
-
- /*
- find x and y first:
-
- note that we mark the vertices on the external face r^c...x
- and r^c...y
-
- more on that below
- */
- embed_graph[vr].visited = mark;
- for (vrin = 0; vrin <= 1; vrin++)
- {
- int m;
-
- m = vrin == 0 ? mark_l : mark_r;
- embedg_VES_get_succ_ext_active_on_ext_face(embed_graph, n, v,
- vr, vrin,
- TRUE, m,
- &s, &sin);
- x_y[vrin] = s;
- x_y[vrin + 2] = sin;
- /*
- note the bizarre way I store the active vertex
- and the direction out of which to continue a walk
- on the lower external face as described above
- */
- }
- *x = x_y[0];
- *y = x_y[1];
-
- /*
- next get the pertinent w on the lower external face from x to y
- */
- cur = x_y[0];
- curin = x_y[2];
- embedg_VES_get_succ_pertinent_on_ext_face(embed_graph, n, v,
- cur, curin,
- TRUE, mark_l, w, &sin);
-
- /*
- now all the vertices r^c...x...w and r^c...y have been marked,
- it remains to mark the vertices on the y...w external face path
-
- (will need to be able to distinguish the external face later on)
-
- Note the way the external face is marked (needed when recovering
- the highest x-y path):
- mark_l for the path v^c...x...w
- mark_r for the path v^c...y
- mark for the lower external face y...w
- */
- cur = x_y[1];
- curin = x_y[3];
- s = n;
- while (s != *w)
- {
- embedg_VES_get_succ_pertinent_on_ext_face(embed_graph, n, v,
- cur, curin,
- TRUE, mark, &s, &sin);
- cur = s;
- curin = sin;
- }
-
- IF_DEB(
- fprintf(stdout, "get x, y & w: the external face\n");
- fprintf(stdout, "%d\t", vr);
- cur = vr;
- curin = 0;
- while (s != vr)
- {
- embedg_VES_get_succ_on_ext_face(embed_graph, n,
- cur, curin,
- FALSE, 0, &s, &sin);
- cur = s;
- curin = sin;
- fprintf(stdout, "%d\t", s);
- }
- fprintf(stdout, "\n");
- )
-}
-
-
-
-
-boolean
-embedg_iso_is_minor_B (t_ver_edge *embed_graph, int n, int *edge_pos,
- int v, int c, int *x, int *y, int *w)
- /*
- determines if the obstruction is a minor B and return x, y
- (ext. active) and w (pertinent)
- */
-{
- /*
- get x & y the ext. active vertices on the (external face)
- path out of v^c,
- and w the pertinent vertex on the lower external face x-y
-
- PLUS mark the whole external face with MARK_EXT_FACE(n)
- */
- embedg_iso_get_x_y_w(embed_graph, n, v, v, c,
- MARK_EXT_FACE(n),
- MARK_EXT_FACE_L(n), MARK_EXT_FACE_R(n),
- x, y, w);
-
- if (embedg_dlcl_is_empty(embed_graph[*w].pertinent_bicomp_list))
- /*
- w has no pertinent child bicomp: not a minor B
- */
- return FALSE;
- else
- {
- t_dlcl *pert_l;
- int l;
-
- pert_l = embed_graph[*w].pertinent_bicomp_list;
- l = embedg_dlcl_list_last(pert_l)->info;
- /*
- if w has an ext. active pertinent child bicomp then minor B
-
- note that we need to know if w has an ext. active AND pertinent
- bicomp child: so it is NOT good enough to test
- w's separated_DFS_child_list as is done in
- embedg_VES_is_ver_ext_active!!!!!!!!!
-
- PLUS: l is actually a VIRTUAL vertex: to check its lowpoint
- I must take its DFS child l - n !!!!!!!!
- */
- ASSERT(embedg_VES_is_virtual_vertex(n, l));
- l = l - n;
- return embed_graph[l].lowpoint < v ? TRUE : FALSE;
- }
-}
-
-void
-embedg_iso_get_highest_x_y_path (
- t_ver_edge *embed_graph,
- int n,
- int mark,
- int mark_l,
- int mark_r,
- int v,
- int c,
- int x,
- int y,
- int w,
- int **path_v, /* stack of vertices in x-y path */
- int **path_e, /* stack of egdes in x-y path */
- int *nbr_v, /* number of vertices in path_v */
- int *entry_in_path_e, /* the in direction for the FIRST edge in
- path_e: needed later on *sigh*
- */
- boolean *px_attached_high,
- boolean *py_attached_high,
- boolean *is_minor_D
-)
- /*
- the obstruction is one of minor C, D, E.
-
- we want to recover the highest x-y path:
- the obstructing path attached to the external faces v^c - x - w
- and v^c - y - w
-
- while doing all this we also determine if the case is a minor C
- or a minor D
- */
-{
- /*
- the path is obtained by walking the proper face starting at v
- where ALL the edges incident to v^c BUT the ones bordering
- the external face have been removed
-
- I won't I don't think remove these edges, but instead I'll be
- implementing an "avoidance" walk
- */
-
- int vv, s, sin, p_x, p_y, cur_v, cur_vin;
- int e, ein, s_e, s_ein;
- boolean avoid_vv;
-
- /*
- must start the walk at edge embed_graph[v^c].link[1 ^ 0],
- (vvin = 0 is in direction of x, see embedg_iso_get_x_y_w)
- */
- vv = c + n;
- e = embed_graph[vv].link[1];
- ein = 0; /* because of adjacency list consistency */
-
- *path_v = (int *) mem_malloc(sizeof(int) * n);
- *path_e = (int *) mem_malloc(sizeof(int) * n);
- (*nbr_v) = -1;
-
- /*
- recall that in embedg_iso_get_x_y_w we did mark
- (with mark, mark_l, mark_r)
- ALL the vertices lying on the external face walk starting
- & ending at v^c: we will use this fact to enable us
- to decide if a vertex is on the external face
- (as opposed to being on the internal face)
- */
-
- s = embed_graph[e].neighbour;
- ASSERT(embed_graph[s].visited == mark_l);
- /*
- this must be the case since s lies on the external face
- starting at v^c in x's direction
- -- we push s onto the stack
- */
- (*path_v)[++(*nbr_v)] = s;
-
- /*
- start the proper face walk which "avoids" v^c since the
- internal edges incident to v^c are supposed to have
- been removed
-
- please read on
- */
- avoid_vv = FALSE;
- while (TRUE)
- {
- boolean av;
-
- av =
- embedg_VES_get_succ_on_proper_face_with_avoidance(
- embed_graph, n,
- e, ein, vv,
- FALSE, 0,
- &s, &s_e, &s_ein);
- avoid_vv = av == TRUE ? av : avoid_vv;
- if (embed_graph[s].visited == mark_l)
- /*
- means that s is still on the external face:
- empty the path's stack and push s
- */
- {
- (*nbr_v) = -1;
- (*path_v)[++(*nbr_v)] = s;
- e = s_e;
- ein = s_ein;
- }
- else if (*nbr_v == 0)
- /*
- s is the first encountered vertex after
- path_v[0] which does not
- lie on the external face v^c...c...w
-
- given the way we pushed things on the vertex stack, path_v[0]
- will be the point of attachement of the x-y path
- on the v^c...x...w external face
-
- path_e[0] will contain nothing: a dummy
-
- path_e[1] will be the first edge in the x-y path
- (and entry_in_path will give the in-direction to this edge)
-
- oh yes!, we break the loop at this point if
- the vertex s lies on the v^c...y...w external face
- */
- {
- ASSERT(embed_graph[(*path_v)[0]].visited == mark_l);
- /*
- the first vertex on the path must be on the
- v^c...x...w external face
- */
- (*path_v)[++(*nbr_v)] = s;
- /*
- and now we also push the edge on the edge stack
-
- I'll need this later to initiate a proper face walk
- starting at the first vertex/edge in the x-y path,
- which is the same as starting from s_e
- */
- (*path_e)[*nbr_v] = s_e;
- *entry_in_path_e = s_ein;
- e = s_e;
- ein = s_ein;
-
- /*
- since we are at the start of the path, we must not
- forget to reset avoid_vv
- */
- avoid_vv = FALSE;
-
- if (embed_graph[s].visited == mark_r
- || embed_graph[s].visited == mark)
- /*
- we have reached the v^c...y...w external face:
- we can stop here
- */
- {
- break;
- }
-
- /*
- if not finished yet,
- we also mark s (and path_v[0]) as visited:
- later on we'll need to recognise which of the vertices
- in path have already been encountered
- (in case of encountering a cut-vertex due to the
- "removal" of the "internal" edges incidnet ot v^c)
-
- note that we mark s as visited iff s if not already
- on the v^c..y..w external face
- */
-
- ASSERT(embedg_VES_is_vertex(n, (*path_v)[0]));
- ASSERT(embedg_VES_is_vertex(n, s));
-
- embed_graph[s].visited = MARK_X_Y_PATH(n);
- }
- else if (embed_graph[s].visited == MARK_X_Y_PATH(n))
- /*
- this means that s is a cut vertex on the internal
- face walk: pop all the vertices from path
- until s's last occurrence in path
- */
- {
- ASSERT((*nbr_v) >= 0);
- while ((*path_v)[(*nbr_v)] != s)
- {
- (*nbr_v)--;
- ASSERT((*nbr_v) >= 0);
- /*
- note that s should be somewhere in path!
- */
- }
- /*
- note also that popping from path_v also implies
- popping from path_e
- */
- e = s_e;
- ein = s_ein;
- }
- else
- /*
- we push s and s_e on their respective stacks
- */
- {
- (*path_v)[++(*nbr_v)] = s;
- (*path_e)[*nbr_v] = s_e;
- e = s_e;
- ein = s_ein;
-
- if (embed_graph[s].visited == mark_r
- || embed_graph[s].visited == mark)
- /*
- again, s lies on the v^c...y...w external face:
- we end the walk: path_v now contains the highest x-y path
-
- note that there can be no conflict between
- mark_r or mark and MARK_X_Y_PATH(n) since
- we mark with MARK_X_Y_PATH iff the vertex
- is NOT marked with mark_r/mark!
- */
- {
- break;
- }
- else
- /*
- we must mark this vertex as MARK_X_Y_PATH since we aren't
- finished yet
- */
- {
- embed_graph[s].visited = MARK_X_Y_PATH(n);
- }
- }
- }
-
- /*
- there is only one thing remaining to do: see if p_x or
- p_y are attached high
- (ie closer to v^c than x or y resp.)
-
- we walk the external face starting at v^c in y's direction
- (again see embedg_iso_get_x_y_w)
- */
- *px_attached_high = TRUE;
- p_x = (*path_v)[0];
- /*
- p_y denotes the attachement point of the x-y path
- on the v^c...y...w external face
- */
-
- s = n;
- cur_v = vv;
- cur_vin = 0;
- while (s != p_x)
- {
- embedg_VES_get_succ_on_ext_face(embed_graph, n,
- cur_v, cur_vin,
- FALSE, 0, &s, &sin);
- if (s == x)
- {
- *px_attached_high = FALSE;
- break;
- }
- cur_v = s;
- cur_vin = sin;
- }
-
- *py_attached_high = TRUE;
- p_y = (*path_v)[*nbr_v];
- /*
- p_y denotes the attachement point of the x-y path
- on the v^c...y...w external face
- */
-
- s = n;
- cur_v = vv;
- cur_vin = 1;
- while (s != p_y)
- {
- embedg_VES_get_succ_on_ext_face(embed_graph, n,
- cur_v, cur_vin,
- FALSE, 0, &s, &sin);
- if (s == y)
- {
- *py_attached_high = FALSE;
- break;
- }
- cur_v = s;
- cur_vin = sin;
- }
-
- /*
- now we are in the minor C case if either p_x or p_y are
- attached high
-
- the minor D case:
- this happens when there is a path v^c - z where z lies
- on the x-y path
-
- that is, when
-
- either v^c has been effectively "avoided" within the
- embedg_VES_get_succ_on_proper_face_with_avoidance function
- BUT ONLY if this "avoidance" happened AFTER having
- encountered the very first vertex on the x-y path!
-
- or when a cut vertex has been encountered on the x-y path:
- separable components on this walk can only occur
- if one walks the face while skipping the edges incident to v^c
-
- in any case this means that checking the return from
- the embedg_VES_get_succ_on_proper_face_with_avoidance function
- is enough: this is the purpose of avoid_vv.
- */
-
- *is_minor_D = !(*px_attached_high || *py_attached_high) && avoid_vv;
-
-
- IF_DEB(
- int i;
-
- fprintf(stdout, "x-y path\t");
- for (i = 0; i <= *nbr_v; i++)
- fprintf(stdout, "%d\t", (*path_v)[i]);
- fprintf(stdout, "\n");
- )
-}
-
-
-/*
- * embedg_misc.c
- */
-
-/*
- What:
- *****
-
- Implementing:
-
- Some high level routinse for the VES structure.
- See VES_misc.c.
-
-
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++
- from
-
- Simplified O(n) Planarity Algorithms (draft)
- ************************************
-
- John Boyer JBoyer@PureEdge.com, jboyer@acm.org
- Wendy Myrvold wendym@csr.uvic.ca
-
-
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++
- authors:
- ********
-
- Paulette Lieby (Magma), Brendan McKay (ANU)
-
- Started October 2001
-*/
-
-
-#include "planarity.h"
-
-#define IF_DEB(x) {}
-#define IF_VERB(x) {}
-#define IF_DEB_TREE(x) {}
-
-
-
-/* aproto: header embed_graph_protos.h */
-
-#ifndef PLANAR_IN_MAGMA
-#endif
-
-
-
-
-void
-embedg_VES_delete (t_ver_edge *embed_graph, int n)
-{
- int i;
-
- for (i = 0; i < n; i++)
- {
- embedg_dlcl_delete(embed_graph[i].separated_DFS_child_list);
- /*
- embedg_dlcl_delete(embed_graph[i].rep_in_parent_list);
-
- NO!!! this points to something in separated_DFS_child_list
- */
- embedg_dlcl_delete(embed_graph[i].pertinent_bicomp_list);
- }
- mem_free(embed_graph);
-}
-
-
-
-void
-embedg_VES_print (t_ver_edge *embed_graph, int n)
-{
- int i;
-
- fprintf(stdout, "vertices\n");
- for (i = 0; i < n; i++)
- {
- t_ver_edge rec;
-
- rec = embed_graph[i];
-
- fprintf(stdout, "\nDFI\t%d\tlabel\t%d\n", i, rec.label);
- fprintf(stdout, "DFS parent\t%d\tleast_a\t%d\tlowpoint\t%d\n",
- rec.DFS_parent, rec.least_ancestor, rec.lowpoint);
- fprintf(stdout, "separated_DFS_child_list\n");
- embedg_dlcl_print(rec.separated_DFS_child_list);
- }
-
- fprintf(stdout, "\nvirtual vertices\n");
- for (i = n; i < 2*n; i++)
- {
- int c;
-
- c = i - n;
- fprintf(stdout, "%d^%d\t", embed_graph[c].DFS_parent, c);
- }
- fprintf(stdout, "\n");
-
- embedg_VES_print_bigcomps(embed_graph, n);
-}
-
-
-void
-embedg_VES_print_bigcomps (t_ver_edge *embed_graph, int n)
- /*
- walking the external faces of all the bicomp; for testing only
- */
-{
- int i;
-
- fprintf(stdout, "bicomponents\n");
- /*
- to get to the bicomps, it makes sense to start at the
- virtual vertices????
- */
- for (i = n + 1; i < 2*n; i++)
- /*
- a note of caution: there is no virtual vertex at
- embed_graph[n] since that would mean a virtual vertex x^0
- which makes no sense (0 is the root of the dfs_tree)
- */
- {
- embedg_VES_walk_bicomp(embed_graph, n, i, 0);
- }
- fprintf(stdout, "\n");
-}
-/*
- * planar_alg_init.c
- */
-
-/*
- What:
- *****
-
- Implementing:
-
- Initialising the embed_graph aka VES data structure from the information
- collected from the DFS.
-
- The embed_graph/VES data structure is an array consisting of vertices,
- virtual vertices and edges;
- vertices, virtual vertices and edges share a common record structure;
- one of the particular features is that any vertex is linked
- together with its incident edges into a doubly circular linked list.
-
- See also VES_misc.c.
-
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++
- from
-
- Simplified O(n) Planarity Algorithms (draft)
- ************************************
-
- John Boyer JBoyer@PureEdge.com, jboyer@acm.org
- Wendy Myrvold wendym@csr.uvic.ca
-
-
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++
- authors:
- ********
-
- Paulette Lieby (Magma), Brendan McKay (ANU)
-
- Started October 2001
-*/
-
-#include "planarity.h"
-
-#define IF_DEB(x) {}
-#define IF_DEB_DFS(x) {}
-#define IF_VERB(x) {}
-#define IF_DEB_TREE(x) {}
-#define IF_CPU(x) {}
-
-
-/* aproto: header embed_graph_protos.h */
-
-/* aproto: beginstatic -- don't touch this!! */
-static void embedg_init_insert_TE (t_ver_edge *, int, int *, t_dlcl *);
-/* aproto: endstatic -- don't touch this!! */
-
-#ifndef PLANAR_IN_MAGMA
-#endif
-
-
-t_ver_edge *
-embedg_planar_alg_init (
- t_ver_sparse_rep *V,
- int n,
- t_adjl_sparse_rep *A, /* input sparse graph */
- int *nbr_c, /* size of the graph, #components*/
- int *edge_pos, /* pos in the struct where the last edge
- has been inserted
- */
- t_dlcl ***dfs_tree, /* a sparse graph rep. for the dfs tree
- -- vertices are as DFIs
- */
- t_dlcl ***back_edges, /* for each vertex v, a dlcl
- of the back edges [v, x] incident to v
- where x is a DESCENDANT of v
- -- vertices are as DFIs
- */
- t_dlcl ***mult_edges /* for each vertex v, a dlcl
- of the back edges [v, x] incident to v
- where x is a DESCENDANT of v
- -- vertices are as DFIs
- */
-)
- /*
- initialising embed_graph, the fundamental data structure
- underpinning the tester and obstruction isolator
-
- from there on, a vertex is exclusively referred to by its DFI!!
- -- so forget about labels
- */
-{
- int *dfs_nbr; /* dfs numbering for each vertex */
- int *dfs_order; /* vertices in dfs order */
- int *lowpoint; /* lowpoint value for each DFI */
- int *dfs_parent; /* for each DFI, its DFS ancestor
- as a DFI (DFS index)
- */
- int *least_a; /* for each DFI, its least ancestor's DFI
- (via a back edge exclusively)
- */
-
- t_ver_edge *embed_graph;
- int i;
-
-
- IF_CPU(
- float sttime; float time_to_now;
-
- sttime = time_current_user();
- )
-
- ASSERT(n >= 1);
-
- /*
- DFS and lowpoint calculations + ordering
- */
- sparseg_adjl_dfs_preprocessing(V, n, A, nbr_c,
- &dfs_nbr, &dfs_order, &lowpoint,
- dfs_tree, back_edges,
- &dfs_parent, &least_a, mult_edges);
-
- IF_CPU(
- fprintf(stdout, "CPU for DFS only %f\n",
- (time_current_user() - sttime));
- sttime = time_current_user();
- )
-
- IF_DEB_DFS(
- fprintf(stdout, "DFS indices\n");
- for (i = 0; i < n; i++)
- fprintf(stdout, "%d ", dfs_nbr[i]);
- fprintf(stdout, "\n");
-
- fprintf(stdout, "DFS order\n");
- for (i = 0; i < n; i++)
- fprintf(stdout, "%d ", dfs_order[i]);
- fprintf(stdout, "\n");
-
- fprintf(stdout, "lowpoint values\n");
- for (i = 0; i < n; i++)
- fprintf(stdout, "%d ", lowpoint[i]);
- fprintf(stdout, "\n");
- );
-
- IF_VERB(
- fprintf(stdout, "DFS parent\n");
- for (i = 0; i < n; i++)
- fprintf(stdout, "%d ", dfs_parent[i]);
- fprintf(stdout, "\n");
- );
-
- IF_VERB(
- fprintf(stdout, "least ancestors\n");
- for (i = 0; i < n; i++)
- fprintf(stdout, "%d ", least_a[i]);
- fprintf(stdout, "\n");
- );
-
- IF_VERB(
- for (i = 0; i < n; i++)
- {
- fprintf(stdout, "the list of children ordered by lowpoint for %d\n",
- i);
- embedg_dlcl_print((*dfs_tree)[i]);
- }
- );
-
- IF_DEB_DFS(
- fprintf(stdout, "the tree edges\n");
- sparseg_dlcl_print(*dfs_tree, n);
-
- fprintf(stdout, "the back edges\n");
- sparseg_dlcl_print(*back_edges, n);
-
- fprintf(stdout, "multiple edges\n");
- sparseg_dlcl_print(*mult_edges, n);
- );
-
- /*
- create the data structure for the embedded graph:
- it will have (max) size 2*n + 2 * MAXE(n)
-
- we will see that that number of edges is sufficient
- even when later adding short-cut edges (see embedg_walkdown)
- */
- embed_graph = (t_ver_edge *) mem_malloc(sizeof(t_ver_edge)
- * (2*n + 2 * MAXE(n)));
- /*
- initialisation
- */
- for (i = 0; i < 2*n + 2 * MAXE(n); i++)
- /*
- some fields are initialised to n as n is actually
- an "invalid" value
- */
- {
- t_ver_edge rec;
-
- rec.label = NIL;
- rec.DFS_parent = n;
- rec.least_ancestor = n;
- rec.lowpoint = n;
- rec.separated_DFS_child_list = NP;
- rec.rep_in_parent_list = NP;
- rec.pertinent_bicomp_list = NP;
- rec.adjacent_to = n;
- rec.visited = n;
- rec.neighbour = n;
- rec.in_adjl = NIL;
- rec.twin_in_adjl = NIL;
- rec.mult = 0;
- rec.type = NIL;
- rec.sign = NILSIGN;
- /*
- make the links refer back to self
- */
- rec.link[0] = rec.link[1] = i;
-
- embed_graph[i] = rec;
- }
-
- /*
- embed_graph[0..n-1]: the n vertices
- ATTENTION: the vertices are stored according to their DFS numbering
- */
- for (i = 0; i < n; i++)
- {
- t_ver_edge rec;
-
- rec = embed_graph[i];
-
- rec.label = dfs_order[i];
- rec.DFS_parent = dfs_parent[i];
- rec.least_ancestor = least_a[i];
- rec.lowpoint = lowpoint[i];
- rec.separated_DFS_child_list = embedg_dlcl_copy((*dfs_tree)[i]);
-
- IF_VERB(
- fprintf(stdout, "the list of children ordered by lowpoint for DFI %d\n",
- i);
- embedg_dlcl_print(rec.separated_DFS_child_list);
- );
-
- embed_graph[i] = rec;
- }
-
- /*
- one more thing to do for these vertices:
- fix the rep_in_parent_list field
- */
- for (i = 1; i < n; i++)
- {
- t_dlcl *parent_list, *rep;
- int parent;
-
- parent = embed_graph[i].DFS_parent; /* careful: this is a DFI */
- /*
- recall that the vertices in embed_graph are accessed via their DFI
- */
-
- if (parent != n)
- /*
- when parent == n this means that i the root of a DFS tree
- in the disconnected graph
- */
- {
- parent_list = embed_graph[parent].separated_DFS_child_list;
- rep = embedg_dlcl_find(parent_list, i);
- ASSERT(rep != NP);
- embed_graph[i].rep_in_parent_list = rep;
- }
- }
-
- /*
- embed_graph[n..2*n-1]: the n virtual vertices
- do I need to do anything here?????
-
- no - I don't think so
-
- let's try to explain what virtual vertices are:
- let v^c be a virtual vertex:
- - it is at position c + n in the array,
- - c is the DFS child of v,
- - v can be retrieved by taking embed_graph[c].DFS_parent,
- - v^c is said virtual as long as the bicomp rooted by v^c is not
- merged with the vertex v
- - once v is merged (identified?) with v^c, then v^c
- is of no relevance anymore
-
- below we will see that we embed all the tree edges as singleton
- bicomps (bicomponent): (0^1, 1), (1^2, 2) etc...:
- this is what virtual vertices are there for:
- to distinguish them from their "real" counterpart with
- which they will be ultimately merged
-
- the primary reason for this is:
- while testing for planarity virtual vertices are the roots of bicomps
- */
-
- /*
- now the edges:
- we actually embed the tree edges so that each tree edge
- forms a (singleton) biconnected component
-
- embedding an edge in effect means creating the
- doubly linked circular list of [virtual] vertices & the edges incident
- to it
-
- this list is built using the links 0 & 1 in embed_graph[i]
- */
-
- /*
- for each tree edge (v,u) we embed (v^u, u) (v^u is the virtual vertex)
-
- CAREFUL: when talking about vertex v say,
- we mean the vertex with DFI v, and NOT the vertex with label v
- **************************************************************
- */
- *edge_pos = 2*n - 1;
- /*
- edge_pos will tell us where to insert the next edge in embed_graph[]
- */
- for (i = 0; i < n; i++)
- {
- t_dlcl *te_l, *p;
-
- te_l = (*dfs_tree)[i];
- p = te_l;
-
- if (!embedg_dlcl_is_empty(p))
- {
- /*
- the test below is a bit stupid... well...
- */
- ASSERT(embed_graph[p->info].DFS_parent == i);
-
- embedg_init_insert_TE(embed_graph, n, edge_pos, p);
- p = embedg_dlcl_list_next(p);
- while (p != te_l)
- {
- ASSERT(embed_graph[p->info].DFS_parent == i);
- embedg_init_insert_TE(embed_graph, n, edge_pos, p);
-
- p = embedg_dlcl_list_next(p);
- }
- }
- }
-
- mem_free(dfs_nbr);
- mem_free(dfs_order);
- mem_free(lowpoint);
-
- mem_free(dfs_parent);
- mem_free(least_a);
-
- IF_CPU(
- fprintf(stdout, "CPU for remainder of initialisation %f\n",
- (time_current_user() - sttime));
- )
-
- return embed_graph;
-}
-
-
-static void
-embedg_init_insert_TE (t_ver_edge *embed_graph, int n, int *edge_pos, t_dlcl *p)
- /*
- init and insert a tree edge in embed graph:
-
- the tree edge will form a singleton bicomponent (v^c, c)
- where c is p->info and v is c.DFS_parent
- */
-{
- int c, v;
-
- c = p->info;
- v = embed_graph[c].DFS_parent;
- ASSERT(v >= 0 && v < n);
-
- /*
- now (v, c) is a tree edge; embed the directed edge [v^c, c]
-
- -- and recall that v^c is a virtual vertex, at position c + n
- in embed_graph, and that vertex c is at position c
- */
-
- /*
- first, set this edge with the appropriate info
- */
- (*edge_pos)++;
- ASSERT(*edge_pos < 2*n + 2 * MAXE(n));
- embed_graph[*edge_pos].neighbour = c;
- embed_graph[*edge_pos].in_adjl = p->in_adjl;
- embed_graph[*edge_pos].twin_in_adjl = p->twin_in_adjl;
-
- ASSERT(p->mult % 2 == 0);
- /*
- we want the number of undirected edges
- */
- embed_graph[*edge_pos].mult = p->mult / 2;
- embed_graph[*edge_pos].type = TE;
- embed_graph[*edge_pos].sign = CCLOCKW;
-
- /*
- link this with vertex v^c in a doubly linked circular list
- */
- embed_graph[c + n].link[0] =
- embed_graph[c + n].link[1] = *edge_pos;
- embed_graph[*edge_pos].link[0] =
- embed_graph[*edge_pos].link[1] = c + n;
-
- /*
- now create/set the twin edge, the directed edge [c, v^c]
- */
- (*edge_pos)++;
- ASSERT(*edge_pos < 2*n + 2 * MAXE(n));
- embed_graph[*edge_pos].neighbour = c + n;
- embed_graph[*edge_pos].in_adjl = p->twin_in_adjl;
- embed_graph[*edge_pos].twin_in_adjl = p->in_adjl;
- embed_graph[*edge_pos].mult = p->mult / 2;
- embed_graph[*edge_pos].type = TE;
- embed_graph[*edge_pos].sign = CCLOCKW;
-
- /*
- and link it with vertex c in a doubly linked circular list
- */
- embed_graph[c].link[0] = embed_graph[c].link[1] = *edge_pos;
- embed_graph[*edge_pos].link[0] =
- embed_graph[*edge_pos].link[1] = c;
-}
-/*
- * dfs_preprocessing.c
- */
-
-/*
- What:
- *****
-
- Implementing:
-
- A DFS as an initialisation step for the planarity tester.
- This is an especially beefed up DFS that collects lots of
- marginal information:
-
- - a DFS tree as a list of DFS children for each vertex
- - the DFS children are sorted according to their lowpoint value
- - a back_edge structure as a list of descendants v for each
- vertex u such that [v, u] is a back edge
- - a multiple edges structure which stores multiple (directed) edges
- NOT in the DFS tree nor in the back_edge struc, and loops
-
- - the vertices in DFS order
- - the DFS index (DFI) for each vertex
- - the lowpoint value for each vertex
- - the number of components of the (possibly disconnected) graph
- - for each vertex, its DFS parent
- - for each vertex v, its least ancestor u such that [v, u]
- is a back edge
-
- ALL info above (except the vertices in DFS order) is given
- in terms of the vertices' DFIs and NOT their labels.
-
-
-
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++
- from
-
- Simplified O(n) Planarity Algorithms (draft)
- ************************************
-
- John Boyer JBoyer@PureEdge.com, jboyer@acm.org
- Wendy Myrvold wendym@csr.uvic.ca
-
-
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++
- authors:
- ********
-
- Paulette Lieby (Magma), Brendan McKay (ANU)
-
- Started October 2001
-*/
-
-
-/*
- There are some dodgy things which need some thought; it would be nice
- to fix them so that the code get's cleaner:
-
- - we do store in_adj (and twin_in_adjl) for each directed edge:
-
- + this is ONLY needed at the time of recovering the embedding
- (see embedg_recover_embedding) and its sole use is to establish
- a link between an edge in the embedding structure
- t_embed_sparse_rep *E and the corresponding edge
- in the t_adjl_sparse_rep *A struc.
-
- + well, I cannot recall why I thought this correspondence
- was needed in the first place and it might well be the case
- that there is no use for it; in which case recovering the
- embedding is simplified
- (we would store the end-vertex in the embedding's edges instead
- of their index in the adjacency list)
-
- - there are some non-linear bits in the DFS below: when searching
- for an already existing tree/back/multiple edge.
- I couldn't fix this in less then one hour so I leave it as it is...
- for now.
-
- This shouldn't be a major issue, overall timings of the planarity
- tester do not show this non-linear "bump"...
-
- - also, this algorithm has been growing incrementally and I now
- realise that I am using some redundant data structures:
- for example visited[] and the vertex and could be dispensed with...
- ...more things to clean up...
-
- Paulette 07/02/02
-*/
-
-
-#include "planarity.h"
-
-#define IF_DEB(x) {}
-#define IF_VERB(x) {}
-#define IF_DEB_TREE(x) {}
-
-
-/* aproto: header embed_graph_protos.h */
-
-#ifndef PLANAR_IN_MAGMA
-#endif
-
-
-void
-sparseg_adjl_dfs_preprocessing (
- t_ver_sparse_rep *V,
- int n, /* size of the graph */
- t_adjl_sparse_rep *A, /* input sparse graph */
- int *c, /* nbr of components */
- int **dfs_nbr, /* dfs numbering for each vertex */
- int **dfs_order, /* vertices in dfs order */
- int **lowpoint, /* lowpoint value for each DFI */
- t_dlcl ***dfs_tree, /* a sparse graph rep. for the dfs tree:
- for each DFI, a list of its children's
- DFI ordered wrt their lowpoint values
- */
- t_dlcl ***back_edges, /* for each DFI v, a dlcl
- of the back edges [v, x] incident to v
- where x is a DESCENDANT of v */
- int **dfs_parent, /* for each DFI its DFS ancestor */
- int **least_a, /* for each DFI, its least ancestor's DFI
- via a back edge exclusively */
- t_dlcl ***mult_edges /* for each DFI v, a dlcl
- of the multiple directed
- edges NOT included
- in either dfs_tree or back_edges
- */
-)
-
- /*
- in ALL the returned info above BUT dfs_order[] we store
- the vertices' DFIs (DFS indices) and NOT their labels!
-
- -- shuffling between labels and vertices can then be done
- via dfs_nbr[] and dfs_order[]
- */
-{
- int pos_v_stack, pos_e_stack, dfs_n;
- int *visited, *vertex_stack, *edge_stack, *lowpoint_order;
- int *TE_in_adjl, *TE_twin_in_adjl, *TE_mult;
- int v, lp, cur, cur_e, next;
- t_dlcl **temp, *lowpoint_list, **new_dfs_tree;
-
- /*
- create the dfs tree as a sparse graph
- */
- *dfs_tree = (t_dlcl **) mem_malloc(sizeof(t_dlcl *) * n);
- /*
- the DFS numbering for the vertices
- */
- *dfs_nbr = (int *) mem_malloc(sizeof(int) * n);
- /*
- the vertices as ordered by their DFS index
- */
- *dfs_order = (int *) mem_malloc(sizeof(int) * n);
- /*
- the lowpoint value for each DFI
- */
- *lowpoint = (int *) mem_malloc(sizeof(int) * n);
-
- /*
- the (directed) back edges
- */
- *back_edges = (t_dlcl **) mem_malloc(sizeof(t_dlcl *) * n);
-
-
- /*
- the DFS parent for each DFI
- */
- *dfs_parent = (int *) mem_malloc(sizeof(int) * n);
- /*
- the least ancestor (via a back edge exlusively) for each DFI
- */
- *least_a = (int *) mem_malloc(sizeof(int) * n);
-
- /*
- the (directed) multiple edges
- */
- *mult_edges = (t_dlcl **) mem_malloc(sizeof(t_dlcl *) * n);
-
- /*
- the vertices visited while DFS
- */
- visited = (int *) mem_malloc(sizeof(int) * n);
- /*
- stack of vertices: last current vertex
- */
- vertex_stack = (int *) mem_malloc(sizeof(int) * n);
- /*
- stack of (tree) edges: last added tree edge
- */
- edge_stack = (int *) mem_malloc(sizeof(int) * n);
-
- /*
- the following will be used in order to recreate the dfs_tree
- so that the DFS children of each DFI are ordered
- according to their lowpoint value
- */
- lowpoint_order = (int *) mem_malloc(sizeof(int) * n);
- temp = (t_dlcl **) mem_malloc(sizeof(t_dlcl *) * n);
- new_dfs_tree = (t_dlcl **) mem_malloc(sizeof(t_dlcl *) * n);
-
- /*
- finally, three more holding arrays: a trick to remember which
- tree edges we are talking about:
-
- when constructing dfs_tree, back_edges, mult_edges
- - we NEED to record the index in A (the adjacency list)
- of some of the edges and their twins/inverses
- we are currently storing in either of these structures
- - we also need to record the number of multiple (directed)
- edges we encounter when the graph is not simple
-
- this is easy to do when storing back edges and multiple edges,
- and tree edges also: but this lattest set of neighbour lists (dfs_tree)
- is subsequently reordered so that DFS children are ordered
- wrt lowpoint values;
- - consequently the info about position in adjacency list
- and edge multiplicity are lost in the ordering process
-
- the two following arrays will remember the info we'll need later
- - more about this below
- */
- TE_in_adjl = (int *) mem_malloc(sizeof(int) * n);
- TE_twin_in_adjl = (int *) mem_malloc(sizeof(int) * n);
- TE_mult = (int *) mem_malloc(sizeof(int) * n);
-
-
- /*
- initialization of the data structures
- */
- for (v = 0; v < n; v++)
- {
- (*dfs_tree)[v] = (*back_edges)[v] = (*mult_edges)[v] = NP;
- visited[v] = TE_mult[v] = 0;
- (*dfs_parent)[v] = (*least_a)[v] = n;
- temp[v] = new_dfs_tree[v] = NP;
- TE_in_adjl[v] = TE_twin_in_adjl[v] = NIL;
- /*
- note that in the 3rd last statement n is considered
- as an "invalid" value;
- will be if importance in the overall algorithm
- */
- }
-
- /*
- the DFS tree is rooted at vertex 0
- */
- dfs_n = -1;
- pos_v_stack = -1;
- pos_e_stack = -1;
- *c = 0;
- for (v = 0; v < n; v++)
- {
- if (visited[v])
- /*
- we come only at this level when looking for
- a new subtree (when graph is disconnected)
- */
- {
- continue;
- }
- else
- {
- (*c)++;
- }
-
- cur = v;
- visited[cur] = 1;
- (*dfs_nbr)[cur] = ++dfs_n;
- (*lowpoint)[(*dfs_nbr)[cur]] = dfs_n;
- (*dfs_order)[dfs_n] = cur;
-
- cur_e = V[cur].first_edge == NIL ? NIL : V[cur].first_edge;
- while (TRUE)
- {
- if (cur_e != NIL)
- {
- t_dlcl *existing_e;
-
- next = A[cur_e].end_vertex;
- if (!visited[next])
- /*
- adding tree edges (careful: directed edges)
-
- AND tree edges are stored as
- [dfs_nbr[u], dfs_nbr[cv]]
- instead of [u, cv]: that is we store the edges
- according to the vertices' DFIs
- */
- {
- IF_DEB_TREE(
- io_printf("add tree edge %d\t%d\n",
- cur+1, next+1);
- );
-
- (*dfs_nbr)[next] = ++dfs_n;
- (*lowpoint)[(*dfs_nbr)[next]] = dfs_n;
- (*dfs_order)[dfs_n] = next;
-
- sparseg_dlcl_append_to_neigh_list(*dfs_tree, n,
- (*dfs_nbr)[cur],
- (*dfs_nbr)[next],
- NIL);
- TE_in_adjl[(*dfs_nbr)[next]] = cur_e;
- TE_mult[(*dfs_nbr)[next]]++;
-
- /*
- we push cur and the edge (cur, cur_e) on their
- respective stacks
- */
- vertex_stack[++pos_v_stack] = cur;
- edge_stack[++pos_e_stack] = cur_e;
-
- /*
- and mark next as visited
- */
- visited[next] = 1;
-
- /*
- update dfs_parent (always deal with DFIs rembember!)
- */
- (*dfs_parent)[(*dfs_nbr)[next]] = (*dfs_nbr)[cur];
-
- /*
- the DFS goes one level deeper
- */
- cur = next;
- cur_e = V[cur].first_edge == NIL ?
- NIL : V[cur].first_edge;
- }
- /*
- the next three tests deal with multiple edges
- and loops: apart from storing these (DIRECTED) edges
- in mult_edges, we also need to update
- the multipliciaty information about these edges
- */
- else if (sparseg_dlcl_is_adjacent(*dfs_tree, n,
- (*dfs_nbr)[cur],
- (*dfs_nbr)[next],
- &existing_e))
- /*
- [cur, next] is a tree edge
- */
- {
- sparseg_dlcl_append_to_neigh_list(*mult_edges, n,
- (*dfs_nbr)[cur],
- (*dfs_nbr)[next],
- cur_e);
- TE_mult[(*dfs_nbr)[next]]++;
-
- cur_e = A[cur_e].next; /* next in cur's adjacency list */
- }
- else if (sparseg_dlcl_is_adjacent(*back_edges, n,
- (*dfs_nbr)[next],
- (*dfs_nbr)[cur],
- &existing_e))
- /*
- [cur, next] is a back edge
- */
- {
- sparseg_dlcl_append_to_neigh_list(*mult_edges, n,
- (*dfs_nbr)[cur],
- (*dfs_nbr)[next],
- cur_e);
- (existing_e->mult)++;
-
- cur_e = A[cur_e].next; /* next in cur's adjacency list */
- }
- else if (next == cur)
- /*
- the case of a loop
- */
- {
- if (sparseg_dlcl_is_adjacent(*mult_edges, n,
- (*dfs_nbr)[next],
- (*dfs_nbr)[cur],
- &existing_e))
- /*
- in this case we must update the multiplicity
- of this edge: note that the elt. in cur's
- neighbours list that gets updated is the first
- in the list
-
- dodgy??? certainly, but can't think
- of a better way to do this
-
- eventually it will happen that even myself
- won't understand what I am doing..........
- */
- {
- (existing_e->mult)++;
- }
- sparseg_dlcl_append_to_neigh_list(*mult_edges, n,
- (*dfs_nbr)[cur],
- (*dfs_nbr)[next],
- cur_e);
-
- cur_e = A[cur_e].next; /* next in cur's adjacency list */
- }
- else if (sparseg_dlcl_is_adjacent(*dfs_tree, n,
- (*dfs_nbr)[next],
- (*dfs_nbr)[cur],
- &existing_e))
- /*
- [next, cur] is a tree edge:
- that is, [cur, next] is [next, cur]'s twin/inverse:
-
- 1. if it is the first time one encounters
- [cur, next] (as it would always be the case
- for a simple graph) then all I need to do
- is to update the tree edge's multiplicity,
- and the twin info in TE_[]
-
- 2. if [cur, next] is actually a multiple edge,
- then I'll need to store it in mult_edges;
- and I update the tree edge's multiplicity too.
- No twin info will be required here.
- Why? see how recover.c embeds the multiple
- edges in the planar embedding.
-
- 3. how do I know it is the first time I encounter
- [cur, next]?:
- when TE_twin_in_adjl = NIL
-
- 4. finally, note that the present counting scheme
- implies that the mult field always holds
- the number of directed edges:
- ie, if [a, b] is a tree edge, [a, b].mult = 2
- because we would have counted [a, b] and [b, a]
-
- this applies to tree edges, back edges, and loops
- */
- {
- ASSERT(TE_in_adjl[(*dfs_nbr)[cur]] != NIL);
- if (TE_twin_in_adjl[(*dfs_nbr)[cur]] == NIL)
- {
- TE_twin_in_adjl[(*dfs_nbr)[cur]] = cur_e;
- }
- else
- {
- sparseg_dlcl_append_to_neigh_list(*mult_edges, n,
- (*dfs_nbr)[cur],
- (*dfs_nbr)[next],
- cur_e);
- }
-
- TE_mult[(*dfs_nbr)[cur]]++;
-
- cur_e = A[cur_e].next; /* next in cur's adjacency list */
- }
- else if (sparseg_dlcl_is_adjacent(*back_edges, n,
- (*dfs_nbr)[cur],
- (*dfs_nbr)[next],
- &existing_e))
- /*
- [next, cur] is a back edge: [cur, next] is its inverse:
- we proceed as for the tree edge case above
- */
- {
- ASSERT(existing_e->in_adjl != NIL);
- if (existing_e->twin_in_adjl == NIL)
- {
- existing_e->twin_in_adjl = cur_e;
- }
- else
- {
- sparseg_dlcl_append_to_neigh_list(*mult_edges, n,
- (*dfs_nbr)[cur],
- (*dfs_nbr)[next],
- cur_e);
- }
-
- (existing_e->mult)++;
-
- cur_e = A[cur_e].next; /* next in cur's adjacency list */
- }
- /*
- the next bit concludes the DFS: it deals with the case
- where a back edge needs to be added
- */
- else
- /*
- that is, next is visited and neither
- the tree edge [next, cur] nor
- the back edge [next, cur] exist:
-
- this implies that [cur, next] is a back edge
- that must be added to the back_edges structure
- (with dfs_nbr(next) < dfs_nbr(cur))
- */
- {
- IF_DEB_TREE(
- io_printf("add back edge %d\t%d\n",
- cur+1, next+1);
- );
-
- ASSERT(visited[next]);
- ASSERT((*dfs_nbr)[cur] > (*dfs_nbr)[next]);
-
- sparseg_dlcl_append_to_neigh_list(*back_edges, n,
- (*dfs_nbr)[next],
- (*dfs_nbr)[cur],
- cur_e);
-
- /*
- update cur's lowpoint
- */
- (*lowpoint)[(*dfs_nbr)[cur]] =
- (*dfs_nbr)[next] < (*lowpoint)[(*dfs_nbr)[cur]] ?
- (*dfs_nbr)[next] : (*lowpoint)[(*dfs_nbr)[cur]];
-
- /*
- update least_a (of cur)
- (always deal with DFIs remember!)
- */
- (*least_a)[(*dfs_nbr)[cur]] =
- (*dfs_nbr)[next] < (*least_a)[(*dfs_nbr)[cur]] ?
- (*dfs_nbr)[next] : (*least_a)[(*dfs_nbr)[cur]];
-
- /*
- get the next edge in cur's adjacency list
- */
- cur_e = A[cur_e].next;
- }
- }
-
- if (cur_e == NIL)
- /*
- we are either at a leaf or have finished scanning
- cur's adjacency list: backtrack
- */
- {
- if (pos_v_stack == -1) /* no previous vertex */
- {
- /*
- no edge left on the stack: DFS ends for
- this subtree:
- we visit the next vertex
- */
- ASSERT(pos_e_stack == -1);
- break;
- }
- else
- {
- int prev_e;
- /*
- Otherwise backtrack and pop cur from the stack
- as well as the last tree edge added to the tree.
- We use next to get a new lowpoint value for cur:
- This value will be min(lowpoint(cur), lowpoint(next)).
- */
- cur = vertex_stack[pos_v_stack--];
- prev_e = edge_stack[pos_e_stack--];
- next = A[prev_e].end_vertex;
- (*lowpoint)[(*dfs_nbr)[cur]] =
- (*lowpoint)[(*dfs_nbr)[cur]]
- < (*lowpoint)[(*dfs_nbr)[next]] ?
- (*lowpoint)[(*dfs_nbr)[cur]]
- : (*lowpoint)[(*dfs_nbr)[next]];
-
- cur_e = A[prev_e].next;
- }
- /*
- we proceed with DFS
- */
- }
- }
- }
- mem_free(vertex_stack);
- mem_free(edge_stack);
-
- /*
- just for the sake of it, check that all vertices have
- been visited
- */
-#ifdef ASSERTIONS
- for (v = 0; v < n; v++)
- {
- ASSERT(visited[v]);
- }
-#endif
- mem_free(visited);
-
- /*
- we now order the DFIs wrt lowpoint values:
- use bucket sort (linear time)
- */
- /*
- for each lowpoint value, collect the DFIs (in a t_dlcl)
- with that lowpoint value
- (IMPORTANT: we want the DFIs since the aim is to rewrite dfs_tree
- which stores DFIs and not labels!)
- */
- for (v = 0; v < n; v++)
- /*
- v is taken as a DFI here
- */
- {
- t_dlcl *r;
-
- r = embedg_dlcl_rec_new(v);
- temp[(*lowpoint)[v]] =
- embedg_dlcl_rec_append(temp[(*lowpoint)[v]], r);
- }
-
- /*
- concatenate these lists now
- */
- lowpoint_list = temp[0];
- for (lp = 1; lp < n; lp++)
- {
- lowpoint_list = embedg_dlcl_cat(lowpoint_list, temp[lp]);
- }
- ASSERT(embedg_dlcl_length(lowpoint_list) == n);
-
- lowpoint_order[0] = lowpoint_list->info;
- for (lp = 1; lp < n; lp++)
- {
- lowpoint_list = embedg_dlcl_list_next(lowpoint_list);
- lowpoint_order[lp] = lowpoint_list->info;
- }
- embedg_dlcl_delete(lowpoint_list);
- mem_free(temp);
-
- IF_DEB(
- fprintf(stdout, "dfs_preprocessing, lowpoint_order\n");
- for (lp = 0; lp < n; lp++)
- fprintf(stdout, "%d ", lowpoint_order[lp]);
- fprintf(stdout, "\n");
- fprintf(stdout, "dfs_preprocessing, lowpoint\n");
- for (lp = 0; lp < n; lp++)
- fprintf(stdout, "%d ", (*lowpoint)[lp]);
- fprintf(stdout, "\n");
- )
-
- /*
- we now use this order to rewrite dfs_tree such that
- the DFS children of each vertex are ordered wrt lowpoint values
- */
- for (lp = 0; lp < n; lp ++)
- /*
- for each DFI in lowpoint_order[] I know its DFS_parent
- from dfs_parent[] -- the rest is then trivial
- */
- {
- int parent;
-
- v = lowpoint_order[lp];
- /*
- lowpoint_order stores DFIs as does dfs_parent, so the lot
- makes sense
- */
- parent = (*dfs_parent)[v];
- if (parent != n)
- /*
- v may be the root of a DFS tree
- */
- {
- t_dlcl *temp;
-
- temp = embedg_dlcl_rec_new(v);
-
- /*
- this is where the TE_ holding arrays are useful *sigh*
- */
- ASSERT(TE_in_adjl[v] != NIL);
- temp->in_adjl = TE_in_adjl[v];
-
- ASSERT(TE_twin_in_adjl[v] != NIL);
- temp->twin_in_adjl = TE_twin_in_adjl[v];
-
- ASSERT(TE_mult[v] != 0 && TE_mult[v] % 2 == 0);
- temp->mult = TE_mult[v];
-
- new_dfs_tree[parent] =
- embedg_dlcl_rec_append(new_dfs_tree[parent], temp);
- }
- }
- mem_free(lowpoint_order);
- mem_free(TE_in_adjl);
- mem_free(TE_twin_in_adjl);
- mem_free(TE_mult);
-
- /*
- some checks are in order here
- */
-#ifdef ASSERTIONS
- for (v = 0; v < n; v++)
- {
- ASSERT(embedg_dlcl_length((*dfs_tree)[v])
- == embedg_dlcl_length(new_dfs_tree[v]));
-
- IF_DEB(
- fprintf(stdout, "dfs_preprocessing dfs_tree for %d\n", v);
- embedg_dlcl_print((*dfs_tree)[v]);
- fprintf(stdout, "dfs_preprocessing new_dfs_tree for %d\n", v);
- embedg_dlcl_print(new_dfs_tree[v]);
- );
- }
-#endif
-
- sparseg_dlcl_delete(*dfs_tree, n);
- *dfs_tree = new_dfs_tree;
-}
-
-/*
- * embedding.c
- */
-
-/*
- What:
- *****
-
- Implementing:
-
- The graph is planar: we recover the embedding from the VES structure
- and check it as well.
- (Some of these checks will disappear later)
-
-
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++
- from
-
- Simplified O(n) Planarity Algorithms (draft)
- ************************************
-
- John Boyer JBoyer@PureEdge.com, jboyer@acm.org
- Wendy Myrvold wendym@csr.uvic.ca
-
-
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++
- authors:
- ********
-
- Paulette Lieby (Magma), Brendan McKay (ANU)
-
- Started October 2001
-*/
-
-
-#include "planarity.h"
-
-#define IF_DEB(x) {}
-#define IF_DEB_EMBED(x) {}
-#define IF_DEB_CHECK_EMBED(x) {}
-#define IF_DEB_FACES(x) {}
-#define IF_VERB(x) {}
-#define IF_DEB_SCE(x) {}
-#define IF_CPU(x) {}
-
-
-/* aproto: header embed_graph_protos.h */
-
-
-#ifndef PLANAR_IN_MAGMA
-#endif
-
-void
-embedg_embedding (t_ver_sparse_rep *V, t_adjl_sparse_rep *A,
- t_ver_edge *embed_graph, int n, int e, int nbr_c,
- int edge_pos, t_dlcl **mult_edges, t_ver_sparse_rep **vertices,
- t_embed_sparse_rep **embedding)
- /*
- recovering the embedding for the (planar) graph
-
- - the embedding is returned in vertices and embedding, vertices
- indexes embedding, the ordered list of edges
- - edges in the embedding are given as their index in A, the graph's
- adajacency list
- - the nbr of edges in the embedding is given as nbr_e_embed:
- this may be different form the original number of edges when the graph
- iss not simple
- */
-{
- int *ver_orient, nbr_comp, nbr_e_embed;
-
- IF_CPU(
- float sttime; float time_to_now;
-
- sttime = time_current_user();
- )
-
- IF_DEB(
- fprintf(stdout, "embedding, begin, which edges have been flipped\n");
- embedg_VES_print_flipped_edges(embed_graph, n, edge_pos);
- )
-
- IF_DEB(
- fprintf(stdout, "embedding, before removing SCE\n");
- embedg_VES_print_bigcomps(embed_graph, n);
- )
-
- /*
- several things to do:
- 1. removing the short-cut edges
- */
- embedg_remove_SCE(embed_graph, n, edge_pos);
-
- IF_DEB(
- fprintf(stdout, "embedding, after removing SCE\n");
- embedg_VES_print_bigcomps(embed_graph, n);
- )
-
- /*
- 2. computing each vertex's orientation (wrt flipped bicomps)
- */
- ver_orient = embedg_vertices_orientation(embed_graph, n);
-
-
- /*
- 3. merging the remaining virtual vertices with their
- non-virtual counterpart
- */
- nbr_comp = embedg_merge_remaining_virtual(embed_graph, n);
- /*
- actually there is no need to return the nbr of components
- from the above function
- but let's do it for the sake of it and for possible checking
- */
- ASSERT(nbr_c == nbr_comp);
-
- IF_DEB(
- fprintf(stdout, "embedding, after merging of remaining vertices\n");
- )
-
- /*
- 4. to be on the safe side: check that the embedding is a valid one
-
- for now, we DIE if not
- */
-
- if (!embedg_is_embed_valid(embed_graph, n, nbr_comp, edge_pos,
- ver_orient, &nbr_e_embed))
- {
- mem_free(ver_orient);
- DIE();
- }
- mem_free(ver_orient);
-
- ASSERT(nbr_e_embed <= e);
- /*
- when the graph is not simple, multiple edges and loops are
- not in embed_graph[]: they will be added to the final
- embedding in embedg_recover_embedding below
- */
-
- /*
- 5. recover the embedding in preparation for the Magma type,
- and check it as well
- */
- embedg_recover_embedding(V, A, embed_graph, n, e,
- mult_edges, vertices, embedding);
- if (!embedg_check_recov_embedding(n, e, nbr_comp,
- *vertices, A, *embedding))
- {
- mem_free(*vertices);
- mem_free(*embedding);
-
- IF_CPU(
- fprintf(stdout, "CPU for embedding recovering %f\n",
- time_current_user() - sttime);
- )
-
- DIE();
- }
-
- IF_DEB_EMBED(
- fprintf(stdout, "embedding, original graph and embedding\n");
- sparseg_adjl_print(V, n, A, FALSE);
- fprintf(stdout, "\n");
- sparseg_adjl_embed_print(*vertices, n, A, *embedding,
- FALSE);
- )
-
- IF_CPU(
- fprintf(stdout, "CPU for embedding recovering %f\n",
- time_current_user() - sttime);
- )
-}
-
-
-void
-embedg_remove_SCE (t_ver_edge *embed_graph, int n, int edge_pos)
- /*
- remove all the short-cut edges from the embedding
- */
-{
- int i, c;
-
- c = 0;
- for (i = 2*n; i <= edge_pos; i += 2)
- /*
- and edge and its twin occupy consecutive positions in embed_graph:
- need only to examine one out of two
- (removing an edge also entails removing its twin of course
- */
- {
- if (embedg_VES_is_short_cut_edge(embed_graph, n, i))
- {
- IF_DEB_SCE(
- fprintf(stdout, "remove SCE\n");
- embedg_VES_print_edge(embed_graph, n, i);
- )
-
- embedg_VES_remove_edge(embed_graph, n, i);
- c++;
- }
- }
-
- IF_DEB_SCE(
- fprintf(stdout, "nbr of SCE edges removed %d\n", c);
- )
-}
-
-
-int *
-embedg_vertices_orientation (t_ver_edge *embed_graph, int n)
- /*
- for each vertex return its orientation from the
- bicomps in embed_graph:
- perform a DFS of each bicomp
- */
-{
- int i, vv, prod_sign;
- int *stack, *ver_orient, to_prev;
-
- /*
- the whole lot makes sense iff the adjacency lists are consistent:
- this is a very important issue and it might be the case
- that the ASSERT warrants replacement by a DIE
- (the check is linear - I think)
- */
- ASSERT(embedg_VES_are_adj_lists_consistent(embed_graph, n));
-
- ver_orient = (int *) mem_malloc(sizeof(int) * n);
- for (i = 0; i < n; i++)
- {
- ver_orient[i] = CCLOCKW;
- }
-
- /*
- create the stack for the DFS
- */
- stack = (int *) mem_malloc(sizeof(int) * 3*n);
- to_prev = -1;
-
- IF_DEB(
- fprintf(stdout, "vertex orientation, one line (of vert.) for each bicomp\n");
- )
-
- /*
- now visit all the bicomps, ie, all the virtual vertices
- in embed_graph
- */
- for (vv = n; vv < 2*n; vv++)
- {
- int c, cur, cur_e;
- boolean NEW_BICOMP;
-
- if (embed_graph[vv].link[0] == vv)
- /*
- means that vv is disabled and is not the root of a bicomp
- */
- {
- continue;
- }
-
- c = vv - n;
- IF_DEB(
- fprintf(stdout, "%d ", c);
- )
- /*
- orientation for c (vv is as yet unembedded) is CCLOCKW
-
- now find the orientation of all its DFS descendants
- */
-
- if (embed_graph[c].DFS_parent == n)
- /*
- this means that actually c is an isolated vertex:
- we initialise the sign to CCLOCKW
- */
- {
- prod_sign = CCLOCKW;
- }
- else
- /*
- we initialise the sign to CCLOCKW to the sign of c's parent
- */
- {
- prod_sign = ver_orient[embed_graph[c].DFS_parent];
- }
-
- /*
- we must not forget to set c's sign!!
- (won't be done below)
- */
- ver_orient[c] = prod_sign;
-
- NEW_BICOMP = FALSE;
- cur = c;
- cur_e = embed_graph[cur].link[0];
- ASSERT(embedg_VES_is_edge(n, cur_e));
-
- ASSERT(to_prev == -1);
- while (TRUE)
- {
- while (!embedg_VES_is_tree_edge(embed_graph, n, cur_e)
- || !embedg_VES_is_vertex(n,
- embed_graph[cur_e].neighbour)
- || embed_graph[cur_e].neighbour <= cur)
- /*
- want to find a tree edge [cur, u]
- where u is a descendant of cur
- */
- {
- cur_e = embed_graph[cur_e].link[0];
-
- while (cur_e == cur)
- /*
- back to the vertex where we started from:
- no edge has been found:
- cur is a leaf, backtrack
- */
- {
- if (to_prev == -1)
- {
- NEW_BICOMP = TRUE;
- break;
- }
- prod_sign = stack[to_prev--];
- cur_e = stack[to_prev--];
- /*
- must advance one more edge
- */
- cur_e = embed_graph[cur_e].link[0];
- cur = stack[to_prev--];
- }
- if (NEW_BICOMP)
- {
- break;
- }
- }
-
- if (NEW_BICOMP)
- {
- break;
- }
- else
- /*
- now cur_e is the edge we were looking for, get its sign
- */
- {
- /*
- push on stack the current vertex, the edge where we
- stopped the DFS, AND the sign carried by that vertex
-
- and go down one level in the DFS
- */
- stack[++to_prev] = cur;
- stack[++to_prev] = cur_e;
- stack[++to_prev] = prod_sign;
-
- cur = embed_graph[cur_e].neighbour;
- prod_sign *= embed_graph[cur_e].sign;
- ver_orient[cur] = prod_sign;
-
- cur_e = embed_graph[cur].link[0];
- ASSERT(embedg_VES_is_edge(n, cur_e));
-
- IF_DEB(
- fprintf(stdout, "%d with sign %d\n", cur, prod_sign);
- )
- }
- }
-
- IF_DEB(
- fprintf(stdout, "\n");
- )
- }
-
- IF_DEB(
- fprintf(stdout, "vertex orientation\n");
- for (i = 0; i < n; i++)
- {
- fprintf(stdout, "%d ", ver_orient[i]);
- }
- fprintf(stdout, "\n");
- )
-
- mem_free(stack);
- return ver_orient;
-}
-
-
-int
-embedg_merge_remaining_virtual (t_ver_edge *embed_graph, int n)
- /*
- after the short-cut edges have been removed and the vertices'
- orientation computed, one finishes by merging all
- remaining virtual vertices with their virtual counterpart
- (without flip of course)
-
- and use this routine to return the number of disconnected
- components of the graph
- */
-{
- /*
- at this stage it is easy to see that all remaining
- virtual vertices are DFS roots (if the graph is not connected)
- or cut vertices
- */
-
- int vv, nbr_comp;
-
- nbr_comp = 0;
- for (vv = n; vv < 2*n; vv++)
- {
- int v, c;
-
-
- c = vv - n;
- v = embed_graph[c].DFS_parent;
-
- /*
- must fish out which virtual vertices are actual roots
- of DFS trees (esp. for the disconnected graph case):
- roots of DFS trees are those virtual vertices for which
- v = embed_graph[c].DFS_parent = n
- */
- if (v == n)
- {
- nbr_comp++;
- continue;
- }
-
- if (embed_graph[vv].link[0] == vv)
- /*
- means that vv is disabled and is not the root of a bicomp
- */
- {
- continue;
- }
-
- embedg_VES_merge_simple_bicomps(embed_graph, n,
- vv, 1, v, 0);
- /*
- note:
- since v is a cut vertex in this intance the bicomp
- rooted by vv will be merged without flip;
- therefore we could have done
- embedg_VES_merge_simple_bicomps(embed_graph, n,
- vv, 0, v, 1)
- as well, the important thing being that vin != vvout
- (see embedg_VES_merge_simple_bicomps)
- */
- }
-
- return nbr_comp;
-}
-
-
-int
-embedg_nbr_faces (t_ver_edge *embed_graph, int n, int edge_pos,
- int *ver_orient, int *nbr_e_embed)
- /*
- count the number of faces and the number of edges of the embedding
- */
-{
- int v, e, f, total_e;
-
- IF_DEB_FACES(
- int v;
-
- fprintf(stdout, "nbr of faces, the vertices' adj. lists\n");
- for (v = 0; v < n; v++)
- embedg_VES_print_adj_list(embed_graph, n,
- v, TRUE);
- )
-
- /*
- the following is no more than a quick check -- certainly
- not very useful -- or could be done elsewhere
- */
- total_e = 0;
- for (e = 2*n; e <= edge_pos; e++)
- {
- if (!embedg_VES_is_short_cut_edge(embed_graph, n, e))
- {
- total_e++;
- }
- }
- ASSERT(total_e % 2 == 0);
- *nbr_e_embed = total_e / 2;
-
- /*
- I now set each edge's orientation
-
- QUESTION: do I really need to do this???
- so far, when doing a proper face traversal, the way in which
- the adjacency list of an edge must be traversed is given
- by the vertex's (in that list) orientation...
- So this seems sensible to me huh?
- */
- embedg_VES_set_orientation(embed_graph, n, ver_orient);
-
- /*
- I will be using the visited field to enable me to check
- if all edges have been traversed
-
- let's be smart (?!): so far the visited field has been used
- and set in the following circumstances:
- + initialisation: set to n
- + walkup: set to whatever DFI of interest
-
- so here we set it to MARK_EMBED(n)
- */
- f = 0;
- for (e = 2*n; e <= edge_pos; e++)
- {
- if (!embedg_VES_is_short_cut_edge(embed_graph, n, e)
- /*
- arrghh!!! I must also skip the SCE!!!
- */
- && embed_graph[e].visited != MARK_EMBED(n))
- {
- int ein;
-
- IF_DEB_FACES(
- fprintf(stdout, "nbr of faces, edges not visited\n");
- embedg_VES_print_edge(embed_graph, n, e);
- )
-
- ein = embed_graph[e].sign == CCLOCKW ? 0 : 1;
- /*
- the way I enter e in dependent on its sign:
- all the proper face traversal must obviously be done
- with the same orientation!
- */
- embedg_VES_walk_proper_face(embed_graph, n, e,
- ein,
- TRUE,
- MARK_EMBED(n));
- f++;
- }
- }
-
- /*
- counting the faces by traversing all the edges does not
- account of the face defined by isolated vertices
- -- we do that now
-
- we only need to check which vertices refer to self, ie with
- no incident edges
- */
- for (v = 0; v < n; v++)
- {
- if (embed_graph[v].link[0] == v)
- {
- ASSERT(embed_graph[v].link[1] == v);
- f++;
- }
- }
-
- return f;
-}
-
-
-boolean
-embedg_is_embed_valid (t_ver_edge *embed_graph, int n, int nbr_comp,
- int edge_pos, int *ver_orient, int *nbr_e_embed)
- /*
- use Euler's formula to assertain that the embedding is a valid
- embedding:
-
- f = 2 * nbr_comp + nbr_e_embed - n
-
- */
-{
- int v, f;
-
- f = embedg_nbr_faces(embed_graph, n, edge_pos, ver_orient, nbr_e_embed);
-
- IF_DEB_CHECK_EMBED(
- fprintf(stdout, "embedding, n: %d\t e: %d\t C: %d\t f: %d\n",
- n, nbr_e, nbr_comp, f);
- )
-
- return f == 2 * nbr_comp + *nbr_e_embed - n ? TRUE : FALSE;
-}
-/*
- * ext_face_walk.c
- */
-
-/*
- What:
- *****
-
- Implementing the external face walk of a bicomponent.
- The concept of an external face --in the context of the VES
- data structure-- makes only sense when talking
- about a bicomp.
-
- Recall that a vertex is linked together with the edges
- incident from it in a circular (doubly) linked list
- (this is the VES structure).
-
- One particular feature is that if a vertex v is on
- the external face of a component and if in the list
- we have edges e1, e2 such as e1 -> v -> e2
- then e1 and e2 border the external face.
-
- In other words, in the circular list of vertex v and edges,
- v is ALWAYS between the two edges bordering the external face
-
- Of course, when v is (maybe) pushed into the internal face
- (by embedding of some edge) then we don't care about this any more
- (for v that is).
-
-
-
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++
- from
-
- Simplified O(n) Planarity Algorithms (draft)
- ************************************
-
- John Boyer JBoyer@PureEdge.com, jboyer@acm.org
- Wendy Myrvold wendym@csr.uvic.ca
-
-
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++
- authors:
- ********
-
- Paulette Lieby (Magma), Brendan McKay (ANU)
-
- Started October 2001
-*/
-
-
-#include "planarity.h"
-
-#define IF_DEB(x) {}
-#define IF_VERB(x) {}
-
-
-/* aproto: header embed_graph_protos.h */
-
-
-void
-embedg_VES_get_succ_on_ext_face (t_ver_edge *embed_graph, int n, int v,
- int vin, boolean MARK, int mark, int *s, int *sin)
- /*
- find the successor s of v (entered via vin) on the external face
- -- also return the direction in which s has been entered
-
- if MARK true mark the succ. vertex and the edges traversed
- with mark (the visited field)
- */
-{
- int e, twin;
- int vout, ein, eout, tout;
-
- ASSERT(embedg_VES_is_vertex(n, v)
- || embedg_VES_is_virtual_vertex(n, v));
-
- IF_DEB(
- fprintf(stdout, "get_succ_on_ext_face, of %d:%d\n", v, vin);
- )
-
- /*
- find the direction out of the vertex, and get the edge
- */
- vout = vin == 0 ? 1 : 0;
- e = embed_graph[v].link[vout];
- if (embedg_VES_is_virtual_vertex(n, v) && e == v)
- /*
- this can happen if a virtual vertex has been "disabled"
-
- -- this should not never happen since we can only walk
- on the external face of a bicomp!
- */
- {
- *s = v;
- *sin = vin;
- return;
- }
-
- /*
- otherwise we must have an edge:
- note that it is entirely irrelevant if I walk SCEs:
- those are precisely there to "jump" over inactive vertices
- */
- ASSERT(embedg_VES_is_edge(n, e));
-
- /*
- get the twin edge
- */
- twin = embedg_VES_get_twin_edge(embed_graph, n, e);
-
- IF_DEB(
- fprintf(stdout, "get_succ_on_ext_face, edge [%d, %d]\n",
- v, embed_graph[e].neighbour);
- fprintf(stdout, "get_succ_on_ext_face, twin edge [%d, %d]\n",
- embed_graph[e].neighbour, embed_graph[twin].neighbour);
- )
- /*
- find which of twin's link links a vertex
- */
- tout = embedg_VES_is_vertex(n, embed_graph[twin].link[0])
- || embedg_VES_is_virtual_vertex(n,
- embed_graph[twin].link[0])
- ?
- 0 : 1;
-
- /*
- get this vertex: this is v's successor on the external face
- */
- *s = embed_graph[twin].link[tout];
-
- /*
- one more thing to do: find the direction in which s was entered
- */
- *sin = embed_graph[*s].link[0] == twin ? 0 : 1;
-
- IF_DEB(
- fprintf(stdout, "get_succ_on_ext_face, succ is %d:%d\n",
- *s, *sin);
- )
- /*
- a special case: when the bicomp is a singleton bicomp
- (ie a single edge)
- */
- if (embed_graph[*s].link[0] == (embed_graph[*s].link[1]))
- {
- ASSERT(embed_graph[*s].link[0] == twin);
- *sin = vin;
- }
-
- /*
- finally, mark the vertex and edges if so requested
- */
- if (MARK)
- {
- embed_graph[*s].visited = mark;
- embed_graph[e].visited = mark;
- embed_graph[twin].visited = mark;
- }
-}
-
-void
-embedg_VES_get_succ_active_on_ext_face (t_ver_edge *embed_graph, int n,
- int v, int w, int win, boolean MARK, int mark, int *s, int *sin)
- /*
- find the ACTIVE (wrt v) successor s of w (entered via win)
- on the external face
- -- also return the direction in which s has been entered
-
- if MARK true mark the succ. vertex (and the edge)
- with mark (the visited field)
- */
-{
- /*
- simply repeatedly calls embedg_VES_get_succ_on_ext_face
- until an active vertex is found
- */
- ASSERT(embedg_VES_is_vertex(n, w)
- || embedg_VES_is_virtual_vertex(n, w));
-
- embedg_VES_get_succ_on_ext_face(embed_graph, n,
- w, win, MARK, mark, s, sin);
- while (embedg_VES_is_ver_inactive(embed_graph, n, v, *s))
- {
- embedg_VES_get_succ_on_ext_face(embed_graph, n,
- *s, *sin, MARK, mark, s, sin);
- }
- ASSERT(!embedg_VES_is_ver_inactive(embed_graph, n, v, *s));
-}
-
-void
-embedg_VES_get_succ_ext_active_on_ext_face (t_ver_edge *embed_graph, int n,
- int v, int w, int win, boolean MARK, int mark, int *s, int *sin)
- /*
- find the externally active (wrt v) successor s of w (entered via win)
- on the external face
- -- also return the direction in which s has been entered
-
- if MARK true mark the succ. vertex (and the edge)
- with mark (the visited field)
- */
-{
- ASSERT(embedg_VES_is_vertex(n, w)
- || embedg_VES_is_virtual_vertex(n, w));
-
- embedg_VES_get_succ_on_ext_face(embed_graph, n,
- w, win, MARK, mark, s, sin);
- while (!embedg_VES_is_ver_ext_active(embed_graph, n, v, *s))
- {
- embedg_VES_get_succ_on_ext_face(embed_graph, n,
- *s, *sin, MARK, mark, s, sin);
- }
- ASSERT(embedg_VES_is_ver_ext_active(embed_graph, n, v, *s));
-}
-
-void
-embedg_VES_get_succ_pertinent_on_ext_face (t_ver_edge *embed_graph, int n,
- int v, int w, int win, boolean MARK, int mark, int *s, int *sin)
- /*
- find the pertinent (wrt v) successor s of w (entered via win)
- on the external face
- -- also return the direction in which s has been entered
-
- if MARK true mark the succ. vertex (and the edge)
- with mark (the visited field)
- */
-{
- ASSERT(embedg_VES_is_vertex(n, w)
- || embedg_VES_is_virtual_vertex(n, w));
-
- embedg_VES_get_succ_on_ext_face(embed_graph, n,
- w, win, MARK, mark, s, sin);
- while (!embedg_VES_is_ver_pertinent(embed_graph, n, v, *s))
- {
- embedg_VES_get_succ_on_ext_face(embed_graph, n,
- *s, *sin, MARK, mark, s, sin);
- }
- ASSERT(embedg_VES_is_ver_pertinent(embed_graph, n, v, *s));
-}
-
-/*
- * mark_kur.c
- */
-
-/*
- What:
- *****
-
- Implementing:
-
- Marking the Kuratowski obstruction (in the VES structure):
- this we do once we know which minor we are talking about
- (see isolator.c).
-
-
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++
- from
-
- Simplified O(n) Planarity Algorithms (draft)
- ************************************
-
- John Boyer JBoyer@PureEdge.com, jboyer@acm.org
- Wendy Myrvold wendym@csr.uvic.ca
-
-
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++
- authors:
- ********
-
- Paulette Lieby (Magma), Brendan McKay (ANU)
-
- Started October 2001
-*/
-
-
-#include "planarity.h"
-
-#define IF_DEB(x) {}
-#define IF_VERB(x) {}
-#define IF_DEB_TREE(x) {}
-#define IF_DEB_EDGES(x) {}
-#define IF_CPU(x) {}
-
-
-
-/* aproto: header embed_graph_protos.h */
-
-/* aproto: beginstatic -- don't touch this!! */
-static void embedg_VES_walk_mark_part_ext_face (t_ver_edge *, int, int, int, int, int, int);
-static void embedg_VES_walk_mark_ext_face (t_ver_edge *, int, int, int);
-static void embedg_VES_walk_mark_part_proper_face (t_ver_edge *, int, int, int, int, int);
-static boolean embedg_VES_is_part_ext_face_marked (t_ver_edge *, int, int, int, int, int, int);
-static void embedg_get_u_x (t_ver_edge *, int, int, int, int *);
-static int embedg_get_least_neigh (t_dlcl **, t_dlcl **, int, int, int);
-static void embedg_add_mark_u_x (t_dlcl **, t_dlcl **, t_ver_edge *, int, int *, int, int, int *, int);
-static void embedg_mark_tree_path (t_ver_edge *, int, int, int, int);
-static void embedg_add_mark_v_w (t_dlcl **, t_dlcl **, t_ver_edge *, int, int *, int, int, int);
-static void embedg_add_mark_v_w_for_B (t_dlcl **, t_dlcl **, t_ver_edge *, int, int *, int, int, int *, int);
-static void embedg_mark_x_y_path (t_ver_edge *, int, int *, int *, int, int);
-/* aproto: endstatic -- don't touch this!! */
-
-#ifndef PLANAR_IN_MAGMA
-#endif
-
-
-
-static void
-embedg_VES_walk_mark_part_ext_face (t_ver_edge *embed_graph, int n,
- int v, int vin, int from, int to, int mark)
- /*
- walk & mark the external face:
- walk in the direction vin -> v -> vout and mark
- */
-{
- int cur, curin, next, nextin;
-
- embed_graph[from].visited = mark;
- embed_graph[to].visited = mark;
-
- IF_DEB(
- fprintf(stdout, "part. ext face marked\t");
- fprintf(stdout, "%d\t", from);
- )
-
- next = cur = v;
- curin = vin;
- while (next != from)
- {
- embedg_VES_get_succ_on_ext_face(embed_graph, n, cur, curin,
- FALSE, 0, &next, &nextin);
- cur = next;
- curin = nextin;
- }
- next = n;
- while (next != to)
- {
- embedg_VES_get_succ_on_ext_face(embed_graph, n, cur, curin,
- TRUE, mark, &next, &nextin);
- cur = next;
- curin = nextin;
-
- IF_DEB(
- fprintf(stdout, "%d\t", next);
- )
- }
- IF_DEB(
- fprintf(stdout, "\n");
- )
-}
-
-static void
-embedg_VES_walk_mark_ext_face (t_ver_edge *embed_graph, int n, int v, int mark)
- /*
- walk & mark the external face, starting & ending at vertex v
- */
-{
- embedg_VES_walk_mark_part_ext_face(embed_graph, n, v, 0, v, v,
- mark);
-}
-
-
-
-static void
-embedg_VES_walk_mark_part_proper_face (t_ver_edge *embed_graph, int n,
- int from_e, int from_ein, int to, int mark)
- /*
- walk & mark a proper face starting at EDGE from_e and ending
- at VERTEX to
-
- walk in the direction from_ein -> from_e -> to and mark
- everything in between
- */
-{
- int s, cur_e, cur_ein, next_e, next_ein;
-
- next_e = s = n; /* this is an invalid value for an edge/vertex */
-
- cur_e = from_e;
- cur_ein = from_ein;
- while (s != to)
- {
- ASSERT(embedg_VES_is_edge(n, cur_e));
- ASSERT(!embedg_VES_is_short_cut_edge(embed_graph,
- n, cur_e));
-
- embedg_VES_get_succ_on_proper_face(embed_graph, n,
- cur_e, cur_ein,
- TRUE, mark,
- &s, &next_e, &next_ein);
- cur_e = next_e;
- cur_ein = next_ein;
- }
-}
-
-
-
-static boolean
-embedg_VES_is_part_ext_face_marked (t_ver_edge *embed_graph, int n, int v,
- int vin, int from, int to, int mark)
- /*
- simple check to see if all the vertices on the external
- face walk starting at vin -> v -> vout are marked
- (with mark)
- */
-{
- int cur, curin, next, nextin;
-
- if (embed_graph[from].visited != mark || embed_graph[to].visited != mark)
- return FALSE;
-
- cur = v;
- curin = vin;
- next = n;
- while (next != from)
- {
- embedg_VES_get_succ_on_ext_face(embed_graph, n, cur, curin,
- FALSE, 0, &next, &nextin);
- cur = next;
- curin = nextin;
- }
- while (next != to)
- {
- embedg_VES_get_succ_on_ext_face(embed_graph, n, cur, curin,
- FALSE, 0, &next, &nextin);
- if (embed_graph[next].visited != mark)
- return FALSE;
-
- cur = next;
- curin = nextin;
- }
-
- return TRUE;
-}
-
-
-boolean
-embedg_VES_is_ext_face_marked (t_ver_edge *embed_graph, int n, int v, int mark)
- /*
- simple check to see if all the vertices on the external
- face walk starting/ending at v are marked (with mark)
- */
-{
- return embedg_VES_is_part_ext_face_marked(embed_graph, n, v, 0,
- v, v, mark);
-}
-
-
-static void
-embedg_get_u_x (t_ver_edge *embed_graph, int n, int v, int x, int *u_x)
- /*
- x is an externally active vertex (wrt v):
- we want u_x, the lowest point of "attachement" for
- the unembedded directed edge [x, u_x]
- */
-{
- int c;
- t_dlcl *child_list;
-
- ASSERT(embedg_VES_is_ver_ext_active(embed_graph, n, v, x));
- if (embed_graph[x].least_ancestor < v)
- /*
- then there is a single unembedded back edge (u_x, x),
- u_x an ancestor of v
- */
- {
- *u_x = embed_graph[x].least_ancestor;
- return;
- }
-
- /*
- else there is a tree path x to d_x and an
- unembedded back edge (u_x, d_x)
-
- get the lowpoint of the first elt. in separated_DFS_child_list of x
- */
- child_list = embed_graph[x].separated_DFS_child_list;
- ASSERT(!embedg_dlcl_is_empty(child_list));
- c = child_list->info;
- *u_x = embed_graph[c].lowpoint;
-}
-
-static int
-embedg_get_least_neigh (t_dlcl **dfs_tree, t_dlcl **back_edges,
- int n, int v, int c)
- /*
- get the least neighbour of v >= c, ie a vertex in the sub tree
- rooted by c
-
- somehow this must always succeed
- */
-{
- int least_n;
- t_dlcl *tree_l, *back_l, *p;
-
- /*
- neighbours are found in either dfs_tree[v] or back_edges[v]
- */
-
- tree_l = dfs_tree[v];
- back_l = back_edges[v];
- ASSERT(!embedg_dlcl_is_empty(tree_l) || !embedg_dlcl_is_empty(back_l));
-
- least_n = n; /* ok, invalid value for any neighbour */
- p = tree_l;
- if (!embedg_dlcl_is_empty(p))
- {
- if (p->info >= c)
- {
- least_n = p->info < least_n ? p->info : least_n;
- }
- p = embedg_dlcl_list_next(p);
- while (p != tree_l)
- {
- if (p->info >= c)
- {
- least_n = p->info < least_n ? p->info : least_n;
- }
- p = embedg_dlcl_list_next(p);
- }
- }
- p = back_l;
- if (!embedg_dlcl_is_empty(p))
- {
- if (p->info >= c)
- {
- least_n = p->info < least_n ? p->info : least_n;
- }
- p = embedg_dlcl_list_next(p);
- while (p != back_l)
- {
- if (p->info >= c)
- {
- least_n = p->info < least_n ? p->info : least_n;
- }
- p = embedg_dlcl_list_next(p);
- }
- }
-
- ASSERT(least_n >= c);
- /*
- this is so because of the context where this function is called from
- */
- return least_n;
-}
-
-static void
-embedg_add_mark_u_x (t_dlcl **dfs_tree, t_dlcl **back_edges,
- t_ver_edge *embed_graph, int n, int *edge_pos, int v,
- int x, int *u_x, int mark)
- /*
- marking a Kuratowski homeomorph:
-
- marking and adding the unembedded dotted edge (u, x),
- x an ext. active vertex wrt v
- */
-{
- int c, d_x;
- t_dlcl *child_list;
-
- ASSERT(embedg_VES_is_ver_ext_active(embed_graph, n, v, x));
- if (embed_graph[x].least_ancestor < v)
- /*
- then there is a single unembedded back edge (u_x, x),
- u_x an ancestor of v
- */
- {
- *u_x = embed_graph[x].least_ancestor;
- embed_graph[x].visited = mark;
- embed_graph[*u_x].visited = mark;
- embedg_VES_add_edge(embed_graph, n, edge_pos, *u_x, x,
- TRUE, mark);
- return;
- }
-
- /*
- else there is a tree path x to d_x and an
- unembedded back edge (u_x, d_x)
-
- get the lowpoint of the first elt. in separated_DFS_child_list of x
- */
- child_list = embed_graph[x].separated_DFS_child_list;
- ASSERT(!embedg_dlcl_is_empty(child_list));
- c = child_list->info;
- *u_x = embed_graph[c].lowpoint;
-
- /*
- search for the least neighbour of *u_x >= c,
- that is in the subtree rooted by c
- */
- d_x = embedg_get_least_neigh(dfs_tree, back_edges, n, *u_x, c);
- ASSERT(d_x >= c);
- /*
- this must be true since u_x is incident to a descendant of x
- (remember: x is externally active)
- */
-
- /*
- mark the DFS tree path from d_x to x
- */
- embedg_mark_tree_path(embed_graph, n, d_x, x, mark);
- /*
- add the unembedded (u_x, d_x) edge
- */
- embedg_VES_add_edge(embed_graph, n, edge_pos, *u_x, d_x,
- TRUE, mark);
-}
-
-static void
-embedg_mark_tree_path (t_ver_edge *embed_graph, int n, int d_x, int x, int mark)
- /*
- marking the DFS tree path d_x...x where x is an ancestor of d_x
- */
-{
- int cur_v, te, twe;
-
- ASSERT(d_x >= x);
-
- cur_v = d_x;
-
- while (cur_v != x)
- {
- embed_graph[cur_v].visited = mark;
- te = embed_graph[cur_v].link[0];
- ASSERT(embedg_VES_is_edge(n, te));
- while (!embedg_VES_is_tree_edge(embed_graph, n, te)
- || (embed_graph[te].neighbour > cur_v
- && embed_graph[te].neighbour != cur_v + n))
- /*
- want to find a tree edge incident to an ancestor of d_x:
- given that d_x..x is a tree path, we MUST find such an edge!
-
- note also that I must take account of the fact that
- [te].neighbour could be a virtual vertex, in which case
- it can only be cur_v + n!
- */
- {
- te = embed_graph[te].link[0];
- }
- ASSERT(embedg_VES_is_tree_edge(embed_graph, n, te));
- ASSERT(embed_graph[te].neighbour == embed_graph[cur_v].DFS_parent
- || embed_graph[te].neighbour == cur_v + n);
-
- embed_graph[te].visited = mark;
- twe = embedg_VES_get_twin_edge(embed_graph, n, te);
- embed_graph[twe].visited = mark;
-
- /*
- want only to deal with real vertices instead of virtual vertices
- */
- cur_v = embed_graph[te].neighbour < cur_v ?
- embed_graph[te].neighbour : embed_graph[cur_v].DFS_parent;
- }
- embed_graph[x].visited = MARK_MINORS(n);
-}
-
-
-static void
-embedg_add_mark_v_w (t_dlcl **dfs_tree, t_dlcl **back_edges,
- t_ver_edge *embed_graph, int n, int *edge_pos, int v, int w, int mark)
- /*
- marking a Kuratowski homeomorph:
-
- marking and adding the unembedded dotted edge (v, w),
- w is pertinent wrt v
- */
-{
- int vw, c, d_w;
- t_dlcl *bicomp_list;
-
- if (embed_graph[w].adjacent_to == v)
- /*
- then there is a single unembedded back edge (v, w)
- w an ancestor of w
- */
- {
- embed_graph[v].visited = mark;
- embed_graph[w].visited = mark;
- embedg_VES_add_edge(embed_graph, n, edge_pos, v, w,
- TRUE, mark);
- return;
- }
-
- /*
- else there is a tree path w to d_w and an
- unembedded back edge (v, d_w)
-
- get the last elt in w's bicomp list
- */
- bicomp_list = embed_graph[w].pertinent_bicomp_list;
- ASSERT(!embedg_dlcl_is_empty(bicomp_list));
- vw = (embedg_dlcl_list_last(bicomp_list))->info;
- c = vw - n;
-
- /*
- search for the least neighbour of v >= c,
- that is in the subtree rooted by c
- */
- d_w = embedg_get_least_neigh(dfs_tree, back_edges, n, v, c);
- ASSERT(d_w >= c);
- /*
- this must be true since v is incident to a descendant of w
- (remember: w is pertinent)
- */
-
- /*
- mark the DFS tree path from d_w to w
- */
- embedg_mark_tree_path(embed_graph, n, d_w, w, mark);
- /*
- add the unembedded (d_w, v) edge
- */
- embedg_VES_add_edge(embed_graph, n, edge_pos, d_w, v,
- TRUE, mark);
-}
-
-
-static void
-embedg_add_mark_v_w_for_B (t_dlcl **dfs_tree, t_dlcl **back_edges,
- t_ver_edge *embed_graph, int n, int *edge_pos, int v, int w,
- int *u_z, int mark)
- /*
- marking a Kuratowski homeomorph:
-
- marking and adding the unembedded dotted edge (v, w) for minor B:
- w is pertinent wrt v
- */
-{
- int vz, z, d_z, d_w;
- t_dlcl *bicomp_list;
-
- /*
- get the last elt in w's bicomp list
- */
- bicomp_list = embed_graph[w].pertinent_bicomp_list;
- ASSERT(!embedg_dlcl_is_empty(bicomp_list));
- vz = (embedg_dlcl_list_last(bicomp_list))->info;
- z = vz - n;
-
- /*
- get the lowpoint of z
- */
- *u_z = embed_graph[z].lowpoint;
-
- /*
- search for the least neighbour of *u_z >= z,
- that is in the subtree rooted by c
- */
- d_z = embedg_get_least_neigh(dfs_tree, back_edges, n, *u_z, z);
- ASSERT(d_z >= z);
- /*
- this must be true since u_z is incident to z or a descendant of z
- */
-
- /*
- now do the same for neighbours of v
- */
- d_w = embedg_get_least_neigh(dfs_tree, back_edges, n, v, z);
- ASSERT(d_w >= z);
- /*
- this must be true since v is incident to a descendant of w
- (remember: w is pertinent)
- */
-
- /*
- mark the DFS tree path from d_w to w
- */
- embedg_mark_tree_path(embed_graph, n, d_w, w, mark);
- /*
- mark the DFS tree path from d_z to z
- */
- embedg_mark_tree_path(embed_graph, n, d_z, z, mark);
- /*
- add & mark the edges (u_z, d_z), (v, d_w)
- */
- embedg_VES_add_edge(embed_graph, n, edge_pos, *u_z, d_z,
- TRUE, mark);
- embedg_VES_add_edge(embed_graph, n, edge_pos, v, d_w,
- TRUE, mark);
-}
-
-static void
-embedg_mark_x_y_path (t_ver_edge *embed_graph, int n, int *path_v,
- int *path_e, int nbr_v, int mark)
-{
- int i;
-
- /*
- have a look at embedg_iso_get_highest_x_y_path
- to see that path_e[0] is a dummy
-
- (note: path_v and path_e contain nbr_v + 1 elts!)
- */
- embed_graph[path_v[0]].visited = mark;
- for (i = 1; i <= nbr_v; i++)
- {
- int e, twin;
-
- embed_graph[path_v[i]].visited = mark;
- e = path_e[i];
- twin = embedg_VES_get_twin_edge(embed_graph, n, e);
- embed_graph[e].visited =
- embed_graph[twin].visited = mark;
- }
-}
-
-void
-embedg_mark_minor_A (t_dlcl **dfs_tree, t_dlcl **back_edges,
- t_ver_edge *embed_graph, int n, int *edge_pos, int v, int c, int vr)
-{
- int r, r_c, x, y, w, u_x, u_y, u;
-
- ASSERT(embedg_VES_is_virtual_vertex(n, vr));
- r_c = vr - n;
- r = embed_graph[r_c].DFS_parent;
-
- /*
- find the ext. active x & y, and the pertinent w,
- and mark the external face of the bicomp rooted at vr
- */
- embedg_iso_get_x_y_w(embed_graph, n, v, r, r_c,
- MARK_MINORS(n),
- MARK_MINORS(n), MARK_MINORS(n), &x, &y, &w);
-
- /*
- mark the edges (u, x), (u, y), (v, w)
- */
- embedg_add_mark_u_x(dfs_tree, back_edges,
- embed_graph, n, edge_pos, v, x, &u_x,
- MARK_MINORS(n));
- embedg_add_mark_u_x(dfs_tree, back_edges,
- embed_graph, n, edge_pos, v, y, &u_y,
- MARK_MINORS(n));
- embedg_add_mark_v_w(dfs_tree, back_edges,
- embed_graph, n, edge_pos, v, w,
- MARK_MINORS(n));
-
- /*
- mark the tree path from r to min(u_x, u_y)
- */
- u = u_x <= u_y ? u_x : u_y;
- embedg_mark_tree_path(embed_graph, n, r, u, MARK_MINORS(n));
-
- IF_DEB(
- fprintf(stdout, "mark minor A\n");
- fprintf(stdout, "v %d\t c %d\t r %d\t r_c %d\t x %d\t y %d\t w %d\t u_x %d\t u_y %d\n",
- v, c, r, r_c, x, y, w, u_x, u_y);
- )
-}
-
-void
-embedg_mark_minor_B (t_dlcl **dfs_tree, t_dlcl **back_edges,
- t_ver_edge *embed_graph, int n, int *edge_pos, int v,
- int c, int x, int y, int w)
-{
- int vv, u_x, u_y, vz, u_z, u_max, u_min;
-
- vv = c + n;
-
- /*
- mark the external face of the bicomp rooted by v^c
- */
- embedg_VES_walk_mark_ext_face(embed_graph, n, vv, MARK_MINORS(n));
- ASSERT(embedg_VES_is_ext_face_marked(embed_graph, n, vv,
- MARK_MINORS(n)));
-
- /*
- mark the edges (u, x), (u, y)
- */
- embedg_add_mark_u_x(dfs_tree, back_edges,
- embed_graph, n, edge_pos, v, x, &u_x,
- MARK_MINORS(n));
- embedg_add_mark_u_x(dfs_tree, back_edges,
- embed_graph, n, edge_pos, v, y, &u_y,
- MARK_MINORS(n));
-
- /*
- mark the dotted edges (v, w), (v, u)
- */
- embedg_add_mark_v_w_for_B(dfs_tree, back_edges,
- embed_graph, n, edge_pos, v, w,
- &u_z, MARK_MINORS(n));
-
- /*
- mark the tree path from max(u_x, u_y, u_z) to min(u_x, u_y, u_z)
- */
- u_max = u_x > u_y ? u_x : u_y;
- u_max = u_max > u_z ? u_max : u_z;
- u_min = u_x < u_y ? u_x : u_y;
- u_min = u_min < u_z ? u_min : u_z;
- embedg_mark_tree_path(embed_graph, n, u_max, u_min, MARK_MINORS(n));
-
- IF_DEB(
- fprintf(stdout, "mark minor B\n");
- fprintf(stdout, "v %d\t c %d\t x %d\t y %d\t w %d\t u_x %d\t u_y %d\t u_z %d\n",
- v, c, x, y, w, u_x, u_y, u_z);
- )
-}
-
-void
-embedg_mark_minor_C (t_dlcl **dfs_tree, t_dlcl **back_edges,
- t_ver_edge *embed_graph, int n, int *edge_pos, int v,
- int c, int x, int y, int w, int *path_v, int *path_e,
- int nbr_v, boolean px_attached_high, boolean py_attached_high)
-{
- int vv, p_x, p_y, u_x, u_y, u;
-
- vv = c + n;
- p_x = path_v[0];
- p_y = path_v[nbr_v];
- /*
- see embedg_iso_get_highest_x_y_path for the above
- */
-
- if (px_attached_high)
- /*
- mark the external face:
- - from v^c to p_y if py_attached_high
- - from v^c to y if !py_attached_high
-
- not too sure about that one....
-
- from v^c to p_y: so vvin = 0,
- in x's direction
- */
- {
- if (py_attached_high)
- embedg_VES_walk_mark_part_ext_face(embed_graph, n, vv, 0,
- vv, p_y, MARK_MINORS(n));
- else
- embedg_VES_walk_mark_part_ext_face(embed_graph, n, vv, 0,
- vv, y, MARK_MINORS(n));
- }
- else
- /*
- symmetric case:
- mark the external face from v^c to p_x: so vvin = 1,
- in y's direction
- */
- {
- if (px_attached_high)
- embedg_VES_walk_mark_part_ext_face(embed_graph, n, vv, 1,
- vv, p_x, MARK_MINORS(n));
- else
- embedg_VES_walk_mark_part_ext_face(embed_graph, n, vv, 1,
- vv, x, MARK_MINORS(n));
- }
-
- /*
- mark the edges (u, x), (u, y), (v, w)
- */
- embedg_add_mark_u_x(dfs_tree, back_edges,
- embed_graph, n, edge_pos, v, x, &u_x,
- MARK_MINORS(n));
- embedg_add_mark_u_x(dfs_tree, back_edges,
- embed_graph, n, edge_pos, v, y, &u_y,
- MARK_MINORS(n));
- embedg_add_mark_v_w(dfs_tree, back_edges,
- embed_graph, n, edge_pos, v, w,
- MARK_MINORS(n));
-
- /*
- mark the tree path from v to min(u_x, u_y)
- */
- u = u_x <= u_y ? u_x : u_y;
- embedg_mark_tree_path(embed_graph, n, v, u, MARK_MINORS(n));
-
- /*
- finally, mark the x-y path, ie the vertices in path_v
- and the edges in path_e
- */
- embedg_mark_x_y_path(embed_graph, n, path_v, path_e, nbr_v,
- MARK_MINORS(n));
-
- IF_DEB(
- fprintf(stdout, "mark minor C p_x high %d\t p_y high %d\n",
- px_attached_high, py_attached_high);
- fprintf(stdout, "v %d\t c %d\t x %d\t y %d\t w %d\t p_x %d\t p_y %d\t u_x %d\t u_y %d\n",
- v, c, x, y, w, p_x, p_y, u_x, u_y);
- )
-}
-
-void
-embedg_mark_minor_D (t_dlcl **dfs_tree, t_dlcl **back_edges,
- t_ver_edge *embed_graph, int n, int *edge_pos, int v,
- int c, int x, int y, int w, int *path_v, int *path_e,
- int nbr_v, int entry_in_path_e)
-{
- int i, vv, p_x, p_y, u_x, u_y, u;
-
- vv = c + n;
- p_x = path_v[0];
- p_y = path_v[nbr_v];
- /*
- see embedg_iso_get_highest_x_y_path for the above
- */
-
- /*
- mark the lower external face from x to y: we can walk in
- either direction
- */
- embedg_VES_walk_mark_part_ext_face(embed_graph, n, vv, 0,
- x, y, MARK_MINORS(n));
-
- /*
- mark the internal path which goes from the x-y path to v
- - since I haven't stored those vertices/edges I assume
- that a proper face walk should suffice
-
- BUT a walk that say starts at p_x and ends at vv,
- that is, a walk starting at path_e[1] entered from entry_in_path_e
- (recall that path_e[0] is a dummy)
- */
- embedg_VES_walk_mark_part_proper_face(embed_graph, n,
- path_e[1], entry_in_path_e,
- vv, MARK_MINORS(n));
-
- /*
- a note of caution here:
- ALWAYS mark external/internal faces before adding any other edges:
- since adding edges destroys the faces' consistency
- (adding edges makes no sense of face since we are in a non-planar
- situation)
- */
- /*
- mark the edges (u, x), (u, y), (v, w)
- */
- embedg_add_mark_u_x(dfs_tree, back_edges,
- embed_graph, n, edge_pos, v, x, &u_x,
- MARK_MINORS(n));
- embedg_add_mark_u_x(dfs_tree, back_edges,
- embed_graph, n, edge_pos, v, y, &u_y,
- MARK_MINORS(n));
- embedg_add_mark_v_w(dfs_tree, back_edges,
- embed_graph, n, edge_pos, v, w,
- MARK_MINORS(n));
-
- /*
- mark the tree path from v to min(u_x, u_y)
- */
- u = u_x <= u_y ? u_x : u_y;
- embedg_mark_tree_path(embed_graph, n, v, u, MARK_MINORS(n));
-
- /*
- mark the x-y path, ie the vertices in path_v
- and the edges in path_e
- */
- embedg_mark_x_y_path(embed_graph, n, path_v, path_e, nbr_v,
- MARK_MINORS(n));
-
- IF_DEB(
- fprintf(stdout, "mark minor D\n");
- fprintf(stdout, "v %d\t c %d\t x %d\t y %d\t w %d\t p_x %d\t p_y %d\t u_x %d\t u_y %d\n",
- v, c, x, y, w, p_x, p_y, u_x, u_y);
- )
-}
-
-
-
-
-minor
-embedg_mark_minor_E (t_dlcl **dfs_tree, t_dlcl **back_edges,
- t_ver_edge *embed_graph, int n, int *edge_pos, int v,
- int c, int x, int y, int w, int *path_v, int *path_e, int nbr_v)
- /*
- while marking minor E return which of the minors we are dealing with
- */
-{
- int vv, p_x, p_y, u_x, u_y, u_w, u, u_max, u_min;
-
- vv = c + n;
- p_x = path_v[0];
- p_y = path_v[nbr_v];
- /*
- see embedg_iso_get_highest_x_y_path for the above
- */
-
- if (!embedg_VES_is_ver_ext_active(embed_graph, n, v, w))
- /*
- minor E1 case: we must find an ext. active z, distinct from w,
- on the external face p_x..w..p_y
- */
- {
- int s, sin, cur, curin, z, u_z, u_xy;
-
- s = n;
- /*
- start searching at vv entered from 0 (in x's direction)
- -- we MUST reach p_x - hopefully! :)
- */
- cur = vv;
- curin = 0;
- while (s != p_x)
- /*
- first advance to p_x: we are sure of reaching it
- */
- {
- embedg_VES_get_succ_on_ext_face(embed_graph, n,
- cur, curin,
- FALSE, 0, &s, &sin);
- cur = s;
- curin = sin;
- }
-
- /*
- continue the walk on the external face:
- stop if either s is ext. active OR s == w
-
- we'll mark the lot later on
- */
- while (
- !(embedg_VES_is_ver_ext_active(embed_graph, n, v,
- s)
- && s != p_x)
- && s != w)
- {
- embedg_VES_get_succ_on_ext_face(embed_graph, n, cur, curin,
- FALSE, 0, &s, &sin);
- cur = s;
- curin = sin;
- }
- /*
- now we must decide which symmetry we are in
- */
- if (embedg_VES_is_ver_ext_active(embed_graph, n, v, s))
- /*
- z is between x and w (recall that w is NOT ext. active)
- */
- {
- z = s;
- ASSERT(z != w);
-
- /*
- mark the external face from v^c to y in x's direction
- */
- embedg_VES_walk_mark_part_ext_face(embed_graph, n, vv, 0,
- vv, y, MARK_MINORS(n));
- /*
- add/mark dotted edge (u, y)
- */
- embedg_add_mark_u_x(dfs_tree, back_edges,
- embed_graph, n, edge_pos,
- v, y, &u_xy, MARK_MINORS(n));
- }
- else
- /*
- this is the symmetric case: must find z between w and p_y
- */
- {
- ASSERT(s == w);
- embedg_VES_get_succ_ext_active_on_ext_face(embed_graph, n,
- v, cur, curin,
- FALSE, 0,
- &s, &sin);
- /*
- and z is distinct from p_y!
- */
- z = s;
- ASSERT(z != p_y);
-
- /*
- mark the external face from v^c to x in y's direction
- */
- embedg_VES_walk_mark_part_ext_face(embed_graph, n, vv, 1,
- vv, x, MARK_MINORS(n));
- /*
- add/mark dotted edge (u, x)
- */
- embedg_add_mark_u_x(dfs_tree, back_edges,
- embed_graph, n, edge_pos,
- v, x, &u_xy, MARK_MINORS(n));
- }
- /*
- now the marked bits which are common to both cases:
- dotted edges (u, z), (v, w), the x-y path,
- the tree path (v, min(u_xy, u_z))
- */
- embedg_add_mark_u_x(dfs_tree, back_edges,
- embed_graph, n, edge_pos,
- v, z, &u_z, MARK_MINORS(n));
- embedg_add_mark_v_w(dfs_tree, back_edges,
- embed_graph, n, edge_pos, v, w,
- MARK_MINORS(n));
-
- embedg_mark_x_y_path(embed_graph, n, path_v, path_e, nbr_v,
- MARK_MINORS(n));
-
- u = u_z <= u_xy ? u_z : u_xy;
- embedg_mark_tree_path(embed_graph, n, v, u, MARK_MINORS(n));
-
- IF_DEB(
- fprintf(stdout, "mark minor E1\n");
- fprintf(stdout, "v %d\t c %d\t x %d\t y %d\t z %d\t w %d\t p_x %d\t p_y %d\t u_xy %d\t u_z %d\n",
- v, c, x, y, z, w, p_x, p_y, u_xy, u_z);
- )
-
- return MINOR_E1;
- }
-
- /*
- in all other cases we get u_x, u_y, u_w back
- from the ext. active vertices x, y, w resp.
-
- again, I CANNOT embed these edges now since that would destroy
- my external/internal faces
- */
-
- embedg_get_u_x(embed_graph, n, v, x, &u_x);
- embedg_get_u_x(embed_graph, n, v, y, &u_y);
- embedg_get_u_x(embed_graph, n, v, w, &u_w);
-
- if (u_w > u_x && u_w > u_y)
- /*
- minor E2 case:
- we mark the whole external face rooted by v^c
- and the tree path (v, min(u_x, u_y))
- */
- {
- embedg_VES_walk_mark_ext_face(embed_graph, n, vv,
- MARK_MINORS(n));
- u = u_x <= u_y ? u_x : u_y;
- embedg_mark_tree_path(embed_graph, n, v, u, MARK_MINORS(n));
-
- /*
- embed dotted edges (u, x), (u, y) & (u, w)
- */
- embedg_add_mark_u_x(dfs_tree, back_edges,
- embed_graph, n, edge_pos,
- v, x, &u_x, MARK_MINORS(n));
- embedg_add_mark_u_x(dfs_tree, back_edges,
- embed_graph, n, edge_pos,
- v, y, &u_y, MARK_MINORS(n));
- embedg_add_mark_u_x(dfs_tree, back_edges,
- embed_graph, n, edge_pos,
- v, w, &u_w, MARK_MINORS(n));
-
- IF_DEB(
- fprintf(stdout, "mark minor E2\n");
- fprintf(stdout, "v %d\t c %d\t x %d\t y %d\t w %d\t p_x %d\t p_y %d\t u_x %d\t u_y %d\t u_w %d\n",
- v, c, x, y, w, p_x, p_y, u_x, u_y, u_w);
- )
-
- return MINOR_E2;
- }
-
- /*
- two more things common to all remaining cases:
-
- mark the dotted edge (v, w) (but we MUST do that later)
-
- mark the x-y path
- */
- embedg_mark_x_y_path(embed_graph, n, path_v, path_e, nbr_v,
- MARK_MINORS(n));
-
- if (u_x < u_y && u_w < u_y)
- /*
- minor E3 case: one of the symmetric cases:
- the external face rooted at v_c from vv to x (in x's direction)
- the external face rooted at v_c from y to w (in y's direction)
- the (v, min(u_w, u_x)) tree path
- */
- {
- embedg_VES_walk_mark_part_ext_face(embed_graph, n, vv, 0,
- vv, p_x, MARK_MINORS(n));
- embedg_VES_walk_mark_part_ext_face(embed_graph, n, vv, 1,
- y, w, MARK_MINORS(n));
-
- u = u_x <= u_w ? u_x : u_w;
- embedg_mark_tree_path(embed_graph, n, v, u, MARK_MINORS(n));
-
- /*
- embed dotted edges (u, x), (u, y), (u, w), (v, w)
- */
- embedg_add_mark_u_x(dfs_tree, back_edges,
- embed_graph, n, edge_pos,
- v, x, &u_x, MARK_MINORS(n));
- embedg_add_mark_u_x(dfs_tree, back_edges,
- embed_graph, n, edge_pos,
- v, y, &u_y, MARK_MINORS(n));
- embedg_add_mark_u_x(dfs_tree, back_edges,
- embed_graph, n, edge_pos,
- v, w, &u_w, MARK_MINORS(n));
- embedg_add_mark_v_w(dfs_tree, back_edges,
- embed_graph, n, edge_pos, v, w,
- MARK_MINORS(n));
-
- IF_DEB(
- fprintf(stdout, "mark minor E3/a\n");
- fprintf(stdout, "v %d\t c %d\t x %d\t y %d\t w %d\t p_x %d\t p_y %d\t u_x %d\t u_y %d\t u_w %d\n",
- v, c, x, y, w, p_x, p_y, u_x, u_y, u_w);
- )
-
- return MINOR_E3;
- }
- if (u_y < u_x && u_w < u_x)
- /*
- minor E3 case: the other symmetric case:
- the external face rooted at v_c from vv to y (in y's direction)
- the external face rooted at v_c from x to w (in x's direction)
- the (v, min(u_w, u_y)) tree path
- */
- {
- embedg_VES_walk_mark_part_ext_face(embed_graph, n, vv, 1,
- vv, p_y, MARK_MINORS(n));
- embedg_VES_walk_mark_part_ext_face(embed_graph, n, vv, 0,
- x, w, MARK_MINORS(n));
-
- u = u_y <= u_w ? u_y : u_w;
- embedg_mark_tree_path(embed_graph, n, v, u, MARK_MINORS(n));
-
- /*
- embed dotted edges (u, x), (u, y), (u, w), (v, w)
- */
- embedg_add_mark_u_x(dfs_tree, back_edges,
- embed_graph, n, edge_pos,
- v, x, &u_x, MARK_MINORS(n));
- embedg_add_mark_u_x(dfs_tree, back_edges,
- embed_graph, n, edge_pos,
- v, y, &u_y, MARK_MINORS(n));
- embedg_add_mark_u_x(dfs_tree, back_edges,
- embed_graph, n, edge_pos,
- v, w, &u_w, MARK_MINORS(n));
- embedg_add_mark_v_w(dfs_tree, back_edges,
- embed_graph, n, edge_pos, v, w,
- MARK_MINORS(n));
-
- IF_DEB(
- fprintf(stdout, "mark minor E3/b\n");
- fprintf(stdout, "v %d\t c %d\t x %d\t y %d\t w %d\t p_x %d\t p_y %d\t u_x %d\t u_y %d\t u_w %d\n",
- v, c, x, y, w, p_x, p_y, u_x, u_y, u_w);
- )
-
- return MINOR_E3;
- }
-
- if (p_x != x)
- /*
- minor E4 case: one of the symmetric cases:
- the external face rooted at v_c from vv to w (in x's direction)
- the external face rooted at v_c from vv to p_y (in y's direction)
- the tree path from max(u_x, u_y, u_w) to min(u_x, u_y, u_w)
- */
- {
- embedg_VES_walk_mark_part_ext_face(embed_graph, n, vv, 0,
- vv, w, MARK_MINORS(n));
- embedg_VES_walk_mark_part_ext_face(embed_graph, n, vv, 1,
- vv, p_y, MARK_MINORS(n));
-
- u_max = u_x > u_y ? u_x : u_y;
- u_max = u_max > u_w ? u_max : u_w;
- u_min = u_x < u_y ? u_x : u_y;
- u_min = u_min < u_w ? u_min : u_w;
- embedg_mark_tree_path(embed_graph, n, u_max, u_min, MARK_MINORS(n));
-
- /*
- embed dotted edges (u, x), (u, y), (u, w), (v, w)
- */
- embedg_add_mark_u_x(dfs_tree, back_edges,
- embed_graph, n, edge_pos,
- v, x, &u_x, MARK_MINORS(n));
- embedg_add_mark_u_x(dfs_tree, back_edges,
- embed_graph, n, edge_pos,
- v, y, &u_y, MARK_MINORS(n));
- embedg_add_mark_u_x(dfs_tree, back_edges,
- embed_graph, n, edge_pos,
- v, w, &u_w, MARK_MINORS(n));
- embedg_add_mark_v_w(dfs_tree, back_edges,
- embed_graph, n, edge_pos, v, w,
- MARK_MINORS(n));
-
- IF_DEB(
- fprintf(stdout, "mark minor E4/a\n");
- fprintf(stdout, "v %d\t c %d\t x %d\t y %d\t w %d\t p_x %d\t p_y %d\t u_x %d\t u_y %d\t u_w %d\n",
- v, c, x, y, w, p_x, p_y, u_x, u_y, u_w);
- )
-
- return MINOR_E4;
- }
- if (p_y != y)
- /*
- minor E4 case: the other symmetric case:
- the external face rooted at v_c from vv to w (in y's direction)
- the external face rooted at v_c from vv to x (in x's direction)
- (here p_x = x!)
- the tree path from max(u_x, u_y, u_w) to min(u_x, u_y, u_w)
- */
- {
- embedg_VES_walk_mark_part_ext_face(embed_graph, n, vv, 1,
- vv, w, MARK_MINORS(n));
- embedg_VES_walk_mark_part_ext_face(embed_graph, n, vv, 0,
- vv, x, MARK_MINORS(n));
-
- u_max = u_x > u_y ? u_x : u_y;
- u_max = u_max > u_w ? u_max : u_w;
- u_min = u_x < u_y ? u_x : u_y;
- u_min = u_min < u_w ? u_min : u_w;
- embedg_mark_tree_path(embed_graph, n, u_max, u_min, MARK_MINORS(n));
-
- /*
- embed dotted edges (u, x), (u, y), (u, w), (v, w)
- */
- embedg_add_mark_u_x(dfs_tree, back_edges,
- embed_graph, n, edge_pos,
- v, x, &u_x, MARK_MINORS(n));
- embedg_add_mark_u_x(dfs_tree, back_edges,
- embed_graph, n, edge_pos,
- v, y, &u_y, MARK_MINORS(n));
- embedg_add_mark_u_x(dfs_tree, back_edges,
- embed_graph, n, edge_pos,
- v, w, &u_w, MARK_MINORS(n));
- embedg_add_mark_v_w(dfs_tree, back_edges,
- embed_graph, n, edge_pos, v, w,
- MARK_MINORS(n));
-
- IF_DEB(
- fprintf(stdout, "mark minor E$/b\n");
- fprintf(stdout, "v %d\t c %d\t x %d\t y %d\t w %d\t p_x %d\t p_y %d\t u_x %d\t u_y %d\t u_w %d\n",
- v, c, x, y, w, p_x, p_y, u_x, u_y, u_w);
- )
-
- return MINOR_E4;
- }
-
- /*
- this is the last case for minor E: when the homeomorph is K5
-
- mark the whole external face rooted at v^c
- mark the tree path from v to min(u_x, u_y, u_w)
- */
-
- embedg_VES_walk_mark_ext_face(embed_graph, n, vv, MARK_MINORS(n));
-
- u = u_x < u_y ? u_x : u_y;
- u = u < u_w ? u : u_w;
- embedg_mark_tree_path(embed_graph, n, v, u, MARK_MINORS(n));
-
- /*
- embed dotted edges (u, x), (u, y), (u, w), (v, w)
- */
- embedg_add_mark_u_x(dfs_tree, back_edges,
- embed_graph, n, edge_pos,
- v, x, &u_x, MARK_MINORS(n));
- embedg_add_mark_u_x(dfs_tree, back_edges,
- embed_graph, n, edge_pos,
- v, y, &u_y, MARK_MINORS(n));
- embedg_add_mark_u_x(dfs_tree, back_edges,
- embed_graph, n, edge_pos,
- v, w, &u_w, MARK_MINORS(n));
- embedg_add_mark_v_w(dfs_tree, back_edges,
- embed_graph, n, edge_pos, v, w,
- MARK_MINORS(n));
-
- IF_DEB(
- fprintf(stdout, "mark minor E5\n");
- fprintf(stdout, "v %d\t c %d\t x %d\t y %d\t w %d\t p_x %d\t p_y %d\t u_x %d\t u_y %d\t u_w %d\n",
- v, c, x, y, w, p_x, p_y, u_x, u_y, u_w);
- )
-
- return MINOR_E5;
-}
-/*
- * proper_face_walk.c
- */
-
-/*
- What:
- *****
-
- Implementing a proper face walk within the VES structure.
- This is obviously not the same as an external face walk,
- but is simply the standard face walk in a planar embedding.
-
- Not much to say, if only to emphasize that for our
- purposes here we assume:
-
- 1. the short-cut edges have been removed from the VES structure
- 2. each vertex/edge has been given its orientation
- 2. the adjacency lists (vertex + plus its incident edges)
- are consistent: (and this is IMPORTANT)
- that is, the way to traverse an adj. list (ie what
- constitute previous and next in the list which actually
- is a planar embedding at this stage) is indicated
- by the vertex/edge's orientation
-
-
- try to explain this better another time.... sorry...
-
-
-
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++
- from
-
- Simplified O(n) Planarity Algorithms (draft)
- ************************************
-
- John Boyer JBoyer@PureEdge.com, jboyer@acm.org
- Wendy Myrvold wendym@csr.uvic.ca
-
-
- ++++++++++++++++++++++++++++++++++++++++++++++++++++++
- authors:
- ********
-
- Paulette Lieby (Magma), Brendan McKay (ANU)
-
- Started October 2001
-*/
-
-
-#include "planarity.h"
-
-#define IF_DEB(x) {}
-#define IF_DEB_PROPER_FACE(x) {}
-#define IF_VERB(x) {}
-
-
-
-/* aproto: header embed_graph_protos.h */
-
-
-
-
-boolean
-embedg_VES_get_succ_on_proper_face_with_avoidance (t_ver_edge *embed_graph,
- int n, int e, int ein, int a, boolean MARK, int mark, int *s,
- int *next_e, int *next_ein)
- /*
- find the successor s of embed_graph[e].neighbour
- (entered via ein) on a proper face traversal
- which avoids (the vertex) a if a != n
-
- also returns the edge next_e such that
- embed_graph[next_e].neighbour = s (to allow for continuation
- of the walk)
-
- assumes that short-cut edges have been removed and that each
- edge/vertex has been given its orientation
-
- and (more importantly) assumes that adjacency lists are consistent
-
- this function has been written especially to retrieve the highest
- x-y path for the isolator;
- (see embedg_iso_get_highest_x_y_path)
- but as I discovered later (when marking an internal face
- as for minor D) this function is general purpose
-
- PLUS: return true if the proper face walk has to skip an edge
- incident to a (ie had to "avoid" a)
-
- PLUS: mark s & next_e if so requested
- */
-{
- int eout;
- int twin, twinout;
- boolean avoid_a;
-
- ASSERT(embedg_VES_is_edge(n, e));
- ASSERT(!embedg_VES_is_short_cut_edge(embed_graph, n, e));
-
- IF_DEB(
- fprintf(stdout, "get_succ_on_proper_face, \n");
- )
-
- avoid_a = FALSE;
- /*
- find the direction out of the edge
- */
- eout = 1 ^ ein;
-
- /*
- get the twin edge
- */
- twin = embedg_VES_get_twin_edge(embed_graph, n, e);
-
- /*
- for each edge we must set the way to get to the next
- in the adjacency list:
- adjacency lists are traversed according to the vertex/edges
- orientation (one unique orientation per list of course)
- */
- if (embed_graph[e].sign != embed_graph[twin].sign)
- /*
- invert traversal
- */
- {
- twinout = 1 ^ eout;
- }
- else
- /*
- traversal is identical
- */
- {
- twinout = eout;
- }
-
- /*
- now, we want the edge previous to twin in twin's adjacency list,
- ie link[1 ^ twinout]
- */
- *next_e = embed_graph[twin].link[1 ^ twinout];
- /*
- next_e could be a vertex, I need an edge
- */
- if (embedg_VES_is_vertex(n, *next_e)
- || embedg_VES_is_virtual_vertex(n, *next_e))
- /*
- at this stage all virtual vertices should have
- been disabled BUT the vertices rooting the bicomps!!!
- */
- {
- *next_e = embed_graph[*next_e].link[1 ^ twinout];
- }
- ASSERT(embedg_VES_is_edge(n, *next_e));
- ASSERT(!embedg_VES_is_short_cut_edge(embed_graph, n, e));
- *s = embed_graph[*next_e].neighbour;
-
- if (*s == a)
- /*
- want to avoid this vertex, so must get yet previous
- edge in adjacency list
- */
- {
- avoid_a = TRUE;
-
- *next_e = embed_graph[*next_e].link[1 ^ twinout];
- if (embedg_VES_is_vertex(n, *next_e)
- || embedg_VES_is_virtual_vertex(n, *next_e))
- {
- *next_e = embed_graph[*next_e].link[1 ^ twinout];
- }
- ASSERT(embedg_VES_is_edge(n, *next_e));
- ASSERT(!embedg_VES_is_short_cut_edge(embed_graph, n, e));
- }
- *s = embed_graph[*next_e].neighbour;
- ASSERT(*s != a);
-
- /*
- finally (again, because lists are consistent)
- */
- *next_ein = 1 ^ twinout;
-
- /*
- now mark s and next_e if required
- */
- if (MARK)
- {
- embed_graph[*s].visited =
- embed_graph[*next_e].visited = mark;
- /*
- ouuh... must mark the twin as well....
- but ONLY when we mark the minors....
- that is poor design, can we do better????
- -- don't think so...
-
- (when we mark when counting the faces, we MUST only
- mark the edge and NOT its twin)
- */
- if (mark == MARK_MINORS(n))
- {
- twin =
- embedg_VES_get_twin_edge(embed_graph, n, *next_e);
- embed_graph[twin].visited = mark;
- }
- }
-
- return avoid_a;
-}
-
-
-
-void
-embedg_VES_get_succ_on_proper_face (t_ver_edge *embed_graph, int n, int e,
- int ein, int MARK, int mark, int *s, int *next_e, int *next_ein)
- /*
- same as above but without avoidance
- */
-{
- boolean avoid;
-
- avoid =
- embedg_VES_get_succ_on_proper_face_with_avoidance(
- embed_graph, n,
- e, ein, n,
- MARK, mark,
- s, next_e, next_ein);
- ASSERT(avoid == FALSE);
-}
-
-
-void
-embedg_VES_walk_proper_face (t_ver_edge *embed_graph, int n, int e,
- int ein, boolean MARK, int mark)
- /*
- traversing a proper face starting at edge e which has been entered
- via ein
-
- -- we mark the visited edges with mark if so requested
-
- assumes that short-cut edges have been removed and that each
- edge/vertex has been given its orientation
- */
-{
- int s, cur_e, cur_ein, next_e, next_ein;
-
- next_e = n; /* this is an invalid value for an edge */
-
- IF_DEB_PROPER_FACE(
- fprintf(stdout, "proper face traversal\n");
- )
-
- cur_e = e;
- cur_ein = ein;
- while (next_e != e)
- {
- ASSERT(embedg_VES_is_edge(n, cur_e));
- ASSERT(!embedg_VES_is_short_cut_edge(embed_graph,
- n, cur_e));
- IF_DEB_PROPER_FACE(
- embedg_VES_print_edge(embed_graph, n, cur_e);
- )
-
- embedg_VES_get_succ_on_proper_face(embed_graph, n,
- cur_e, cur_ein,
- MARK, mark,
- &s, &next_e, &next_ein);
- cur_e = next_e;
- cur_ein = next_ein;
- }
-
- /*
- note that by doing so we would have marked e and the first of e's
- endpoints since by exiting the loop e = next_e and s is the
- actual starting vertex of the walk
- */
-}
-
-
-
-
-
+/* planarity.c - code for planarity testing of undirected graphs.
+ * Method of Boyer and Myrvold, programmed by Paulette Lieby.
+ * The copyright of this program is owned by the Magma project.
+ * Distributed with nauty by permission.
+ ***************************************************************/
+
+/*
+ * sparseg_adjl.c
+ */
+
+/*
+ What:
+ *****
+
+ Implementing:
+
+ Some high-level functions on the sparse graph as
+ an adjacency list.
+ In particular, testing if it is planar.
+
+
+
+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ authors:
+ ********
+
+ Paulette Lieby (Magma), Brendan McKay (ANU)
+
+ Started October 2001
+*/
+
+#include "planarity.h"
+
+#define IF_DEB(x) {}
+#define IF_VERB(x) {}
+
+
+
+/* aproto: header embed_graph_protos.h */
+
+
+#ifndef PLANAR_IN_MAGMA
+#endif
+
+
+boolean
+sparseg_adjl_plan_and_iso (t_ver_sparse_rep *V, int n, t_adjl_sparse_rep *A,
+ int e, int *c, t_ver_sparse_rep **VR, t_adjl_sparse_rep **AR,
+ t_embed_sparse_rep **ER, int *nbr_e_obs)
+ /*
+ the input graph is given as an adjacency list:
+ V: array of vertices
+ n: size of graph
+ A: adjacency list
+ e: number of edges
+
+ if the graph is planar the embedding is stored in VR and ER;
+ the embedding contains e edges
+ (nbr_e_obs not used)
+
+ if the graph is non planar the obstruction is returned in
+ VR and AR together with the number of edges in nbr_e_obs
+
+ in all cases is also returned the number of components (in c)
+ */
+{
+ t_dlcl **dfs_tree, **back_edges, **mult_edges;
+ int edge_pos, v, w;
+ boolean ans;
+ t_ver_edge *embed_graph;
+
+ ans = sparseg_adjl_is_planar(V, n, A, c,
+ &dfs_tree, &back_edges, &mult_edges,
+ &embed_graph, &edge_pos, &v, &w);
+
+ if (!ans)
+ {
+ embedg_obstruction(V, A, dfs_tree, back_edges,
+ embed_graph, n, &edge_pos,
+ v, w, VR, AR, nbr_e_obs);
+ }
+ else
+ {
+ embedg_embedding(V, A, embed_graph, n, e, *c, edge_pos, mult_edges,
+ VR, ER);
+ }
+
+ sparseg_dlcl_delete(dfs_tree, n);
+ sparseg_dlcl_delete(back_edges, n);
+ sparseg_dlcl_delete(mult_edges, n);
+ embedg_VES_delete(embed_graph, n);
+
+ return ans;
+}
+
+
+
+int *
+sparseg_adjl_footprint (t_ver_sparse_rep *V, int n,
+ t_adjl_sparse_rep *A, int v)
+ /*
+ return v's footprint:
+ an array fp of size n where fp[i] = index of (directed)
+ edge [v, i] in A
+ */
+{
+ /*
+ note that we won't initialise the array:
+ its subsequent usage doesn't require it
+ */
+ int *fp, e;
+
+ fp = (int *) mem_malloc(sizeof(int) * n);
+
+ if (V[v].first_edge == NIL)
+ /*
+ do nothing
+ */
+ return fp;
+
+ e = V[v].first_edge;
+ while (e != NIL)
+ {
+ fp[A[e].end_vertex] = e;
+ e = A[e].next;
+ }
+
+ return fp;
+}
+
+
+void
+sparseg_adjl_print (t_ver_sparse_rep *V, int n,
+ t_adjl_sparse_rep *A, boolean user_level)
+{
+ int v;
+
+ for (v = 0; v < n; v++)
+ {
+ int next;
+
+ if (user_level)
+ fprintf(stdout, "%d:\t", v + 1);
+ else
+ fprintf(stdout, "%d:\t", v);
+
+ next = V[v].first_edge;
+ while (next != NIL)
+ {
+ if (user_level)
+ fprintf(stdout, "%d ", A[next].end_vertex + 1);
+ else
+ fprintf(stdout, "%d ", A[next].end_vertex);
+
+ next = A[next].next;
+ }
+ fprintf(stdout, "\n");
+ }
+}
+
+
+
+
+void
+sparseg_adjl_embed_print (t_ver_sparse_rep *V_e, int n,
+ t_adjl_sparse_rep *A, t_embed_sparse_rep *E, boolean user_level)
+ /*
+ print the embedding given by E,
+ edges are referred to by their index in A
+
+ and V_e[v].first_edge is the index in E of the first edge
+ (in the embedding's order) incident from v
+
+ note that E is NOT indexed by the same vertices' array
+ that indexes A (at the creation of the sparse graph)
+ */
+{
+ int v;
+
+ for (v = 0; v < n; v++)
+ {
+ int start, next;
+
+ if (user_level)
+ fprintf(stdout, "%d:\t", v + 1);
+ else
+ fprintf(stdout, "%d:\t", v);
+
+ if (V_e[v].first_edge == NIL)
+ {
+ fprintf(stdout, "\n");
+ continue;
+ }
+ start = next = V_e[v].first_edge;
+
+ if (user_level)
+ fprintf(stdout, "%d ", A[ E[next].in_adjl ].end_vertex + 1);
+ else
+ fprintf(stdout, "%d ", A[ E[next].in_adjl ].end_vertex);
+
+ next = E[next].next;
+
+ while (next != start)
+ /*
+ recall that in E edges are linked into a circular list
+ */
+ {
+ if (user_level)
+ fprintf(stdout, "%d ", A[ E[next].in_adjl ].end_vertex + 1);
+ else
+ fprintf(stdout, "%d ", A[ E[next].in_adjl ].end_vertex);
+
+ next = E[next].next;
+ }
+ fprintf(stdout, "\n");
+ }
+}
+
+graph *
+sparseg_adjl_to_nauty_graph (t_ver_sparse_rep *V, int n, t_adjl_sparse_rep *A)
+ /*
+ write the sparse graph as a nauty graph
+ */
+{
+ int m, v, e, i;
+ graph *g;
+
+ m = (n + WORDSIZE - 1) / WORDSIZE;
+ g = (graph *) mem_malloc(n * m * sizeof(graph));
+ for (i = (long) m * n; --i >= 0;)
+ g[i] = 0;
+
+ /*
+ we first copy V and A's information into g
+ */
+ for (v = 0; v < n; v++)
+ {
+ e = V[v].first_edge;
+ while (e != NIL)
+ /*
+ A[e].end_vertex is the next neighbour in the list,
+ A[e].next points to the next edge in the list
+ */
+ {
+ if (A[e].end_vertex != v) /* no loops */
+ {
+ ADDELEMENT(GRAPHROW(g, v, m), A[e].end_vertex);
+ }
+ e = A[e].next;
+ }
+ }
+
+ return g;
+}
+
+
+
+#if 0
+t_edge_sparse_rep *
+sparseg_adjl_edges (t_ver_sparse_rep *V, int n,
+ t_adjl_sparse_rep *A, int e, boolean digraph)
+ /*
+ e is the number of edges
+ */
+{
+ t_edge_sparse_rep *edges;
+ int m, u, v, pos_e;
+ graph *g;
+
+ edges = (t_edge_sparse_rep *) mem_malloc(sizeof(t_edge_sparse_rep) * e);
+
+ m = (n + WORDSIZE - 1) / WORDSIZE;
+ g = sparseg_adjl_to_nauty_graph(V, n, A);
+
+ pos_e = 0;
+ for (u = 0; u < n; u++)
+ {
+ v = digraph == TRUE ? 0 : u + 1;
+ for (; v < n; v++)
+ {
+ if (ISELEMENT(GRAPHROW(g, u, m), v))
+ {
+ t_edge_sparse_rep edge;
+
+ edge.ends[0] = u;
+ edge.ends[1] = v;
+ edges[pos_e++] = edge;
+ }
+ }
+ }
+ ASSERT(pos_e == e);
+ mem_free(g);
+
+ return edges;
+}
+#endif
+
+
+
+t_edge_sparse_rep *
+sparseg_adjl_edges (t_ver_sparse_rep *V, int n, t_adjl_sparse_rep *A,
+ int e, boolean digraph)
+ /*
+ e is the number of edges
+ */
+{
+#if 0
+ t_edge_sparse_rep *edges;
+ int u, v, pos_e, *loops, *foot_print;
+ graph *g;
+
+ loops = (int *) mem_malloc(sizeof(int) * n);
+ for (v = 0; v < n; v++)
+ {
+ loops[v] = 0;
+ }
+
+ edges = (t_edge_sparse_rep *) mem_malloc(sizeof(t_edge_sparse_rep) * e);
+ pos_e = 0;
+
+ foot_print = (int *) mem_malloc(sizeof(int) * n);
+ for (u = 0; u < n; u++)
+ foot_print[u] = NIL;
+
+ for (v = 0; v < n; v++)
+ {
+ int ne;
+ t_edge_sparse_rep edge;
+
+ ne = V[v].first_edge;
+ while (ne != NIL)
+ {
+ u = A[ne].end_vertex;
+ if (digraph
+ || (!digraph && u > v))
+ {
+ foot_print[u] = v;
+ }
+ else if (!digraph && u == v)
+ {
+ if (loops[v] == 0)
+ {
+ foot_print[u] = v;
+ }
+
+ loops[v] ^= 1;
+ }
+
+ ne = A[ne].next;
+ }
+
+ for (u = 0; u < n; u++)
+ if (foot_print[u] == v)
+ {
+ edge.ends[0] = v;
+ edge.ends[1] = u;
+ edges[pos_e++] = edge;
+ }
+ }
+ ASSERT(pos_e == e);
+ mem_free(loops);
+ mem_free(foot_print);
+
+ return edges;
+
+#endif
+ /*
+ there must be a simpler way
+ */
+#if 0
+ typedef struct edge_list {
+ int size;
+ t_edge_sparse_rep *edges;
+ } t_edge_list;
+
+ t_edge_list *edge_table;
+ t_edge_sparse_rep *edges;
+ int u, v, nbr_e, pos_e, *loops;
+ graph *g;
+
+ loops = (int *) mem_malloc(sizeof(int) * n);
+ for (v = 0; v < n; v++)
+ {
+ loops[v] = 0;
+ }
+
+ /*
+ now create an edge table as follows:
+ - there are n lists in total
+ - their respective size is given by size
+ - their contents by *edges:
+
+ edge_table[i] will contain all the edges whose end-point is i:
+ these edges, by construction, will be sorted according to their
+ starting point
+
+ what for? to finish off each start-vertex processing
+ with a bucket sort so that
+ the edges are sorted wrt start- & end-point
+
+ bucket sort is linear, hence why...
+ */
+ edge_table = (t_edge_list *) mem_malloc(sizeof(t_edge_list) * n);
+ for (v = 0; v < n; v++)
+ {
+ edge_table[v].size = 0;
+ edge_table[v].edges = NP;
+ }
+
+ edges = (t_edge_sparse_rep *) mem_malloc(sizeof(t_edge_sparse_rep) * e);
+
+ nbr_e = 0;
+ pos_e = 0;
+ for (v = 0; v < n; v++)
+ {
+ int ne, w, u;
+
+ ne = V[v].first_edge;
+ while (ne != NIL)
+ {
+ u = A[ne].end_vertex;
+ if (digraph
+ || (!digraph && u > v))
+ {
+ t_edge_sparse_rep edge;
+
+ edge.ends[0] = v;
+ edge.ends[1] = u;
+
+ /*
+ now stick this edge into the table: one may ponder
+ as to the cost of constantly reallocating memory...
+ some cursory tests in another context tell me that
+ this is pretty much ok
+ (and certainly better than allocating n^2 storage space)
+ */
+ if (edge_table[u].size == 0)
+ {
+ edge_table[u].edges = (t_edge_sparse_rep *)
+ mem_malloc(sizeof(t_edge_sparse_rep));
+ }
+ else
+ {
+ edge_table[u].edges = (t_edge_sparse_rep *)
+ mem_realloc(edge_table[u].edges,
+ sizeof(t_edge_sparse_rep)
+ * (edge_table[u].size + 1));
+ }
+
+ (edge_table[u].edges)[edge_table[u].size] = edge;
+ edge_table[u].size += 1;
+ nbr_e++;
+ }
+ else if (!digraph && u == v)
+ {
+ if (loops[v] == 0)
+ {
+ t_edge_sparse_rep edge;
+
+ edge.ends[0] = v;
+ edge.ends[1] = u;
+
+ if (edge_table[u].size == 0)
+ {
+ edge_table[u].edges = (t_edge_sparse_rep *)
+ mem_malloc(sizeof(t_edge_sparse_rep));
+ }
+ else
+ {
+ edge_table[u].edges = (t_edge_sparse_rep *)
+ mem_realloc(edge_table[u].edges,
+ sizeof(t_edge_sparse_rep)
+ * (edge_table[u].size + 1));
+ }
+
+ (edge_table[u].edges)[edge_table[u].size] = edge;
+ edge_table[u].size += 1;
+ nbr_e++;
+ }
+
+ loops[v] ^= 1;
+ }
+
+ ne = A[ne].next;
+ }
+
+ /*
+ bucket sort must take place here:
+ of course the whole lot is not exactly linear!
+ since we perform the sort n times; but we can hope for
+ a "good" ?? average behaviour:
+
+ in any case this must be better that checking adjacencies
+ n^2 times in a sparse rep. (see edge_set_iset_assure)
+ */
+ for (w = 0; w < n; w++)
+ {
+ if (edge_table[w].size > 0)
+ {
+ for (u = 0; u < edge_table[w].size; u++)
+ {
+ ASSERT((edge_table[w].edges)[u].ends[0] == v);
+ edges[pos_e++] = (edge_table[w].edges)[u];
+ }
+ mem_free(edge_table[w].edges);
+ edge_table[w].size = 0;
+ edge_table[w].edges = NP;
+ }
+ }
+ }
+ ASSERT(nbr_e == e);
+ ASSERT(pos_e == e);
+ mem_free(loops);
+ mem_free(edge_table);
+
+ return edges;
+#endif
+
+ t_edge_sparse_rep *edges;
+ int v, pos_e, *loops;
+
+ edges = (t_edge_sparse_rep *) mem_malloc(sizeof(t_edge_sparse_rep) * e);
+ loops = (int *) mem_malloc(sizeof(int) * n);
+ for (v = 0; v < n; v++)
+ {
+ loops[v] = 0;
+ }
+
+ pos_e = 0;
+ for (v = 0; v < n; v++)
+ {
+ int ne;
+
+ ne = V[v].first_edge;
+ while (ne != NIL)
+ {
+ int u;
+
+ u = A[ne].end_vertex;
+ if (digraph
+ || (!digraph && u > v))
+ {
+ t_edge_sparse_rep edge;
+
+ edge.ends[0] = v;
+ edge.ends[1] = u;
+ edges[pos_e++] = edge;
+ }
+ else if (!digraph && u == v)
+ {
+ if (loops[v] == 0)
+ {
+ t_edge_sparse_rep edge;
+
+ edge.ends[0] = v;
+ edge.ends[1] = u;
+ edges[pos_e++] = edge;
+ }
+
+ loops[v] ^= 1;
+ }
+ ne = A[ne].next;
+ }
+ }
+ ASSERT(pos_e == e);
+ mem_free(loops);
+
+ return edges;
+
+}
+
+/*
+ * sparseg_adjl_modify.c
+ */
+
+/*
+ What:
+ *****
+
+ Implementing:
+
+ Some high-level functions on the sparse graph as
+ an adjacency list.
+ In particular, adding/removing vertices/edges.
+
+
+ NOTE: Most of the functions implicitely assume that the
+ graph is undirected;
+ this must be slightly rewritten for the general case
+ -- just haven't got the time right now...
+
+
+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ authors:
+ ********
+
+ Paulette Lieby (Magma), Brendan McKay (ANU)
+
+ Started October 2001
+*/
+
+#include "planarity.h"
+
+#define IF_DEB(x) {}
+#define IF_VERB(x) {}
+
+
+/* aproto: header embed_graph_protos.h */
+
+
+
+#ifndef PLANAR_IN_MAGMA
+#endif
+
+
+
+boolean
+sparseg_adjl_add_edge (t_ver_sparse_rep *V, int n, t_adjl_sparse_rep **A,
+ int *size_A, int *pos, int u, int v, boolean CHECK)
+ /*
+ add the UNDIRECTED edge to the sparse graph (V, n, A)
+ - pos records where to add the next edge in A
+ - if pos + 1 == size_A, we must extend A
+
+ we check if the edge is already in the graph iff CHECK true
+
+ also we assume that the graph (V, n, A) is undirected
+ */
+{
+ boolean edge_exists;
+
+ edge_exists = FALSE;
+ if (CHECK)
+ {
+ edge_exists = sparseg_adjl_dir_edge_exists(V, n, *A, u, v);
+
+ if (edge_exists)
+ return FALSE;
+ }
+
+ if (*pos == *size_A)
+ {
+ IF_DEB(
+ fprintf(stdout, "realloc \n");
+ )
+
+ *size_A += 2; /* add two directed edges */
+ *A = (t_adjl_sparse_rep *)
+ mem_realloc(*A, sizeof(t_adjl_sparse_rep) * *size_A);
+ }
+ else if (*pos + 1 == *size_A)
+ {
+ IF_DEB(
+ fprintf(stdout, "realloc \n");
+ )
+
+ *size_A += 1; /* add two directed edges */
+ *A = (t_adjl_sparse_rep *)
+ mem_realloc(*A, sizeof(t_adjl_sparse_rep) * *size_A);
+ }
+ ASSERT(*pos + 1 < *size_A);
+
+ sparseg_adjl_add_dir_edge(V, n, A, size_A, pos, u, v, FALSE);
+ sparseg_adjl_add_dir_edge(V, n, A, size_A, pos, v, u, FALSE);
+
+ return TRUE;
+}
+
+boolean
+sparseg_adjl_add_edge_no_extend (t_ver_sparse_rep *V, int n,
+ t_adjl_sparse_rep *A, int size_A, int *pos, int u, int v, boolean CHECK)
+ /*
+ like sparseg_adjl_add_edge but here we are guaranteed
+ that pos + 1 < size_A
+ (unless that for some reason we attempt to add
+ an edge which is already there)
+
+ this feature is required when A is part of a Magma block:
+ we do not want to reallocate A here
+ (would be done at a higher level)
+
+ we check if the edge is already in the graph iff CHECK true
+
+ also, we assume that we use this procedur only when dealing
+ with an undirected graph
+ */
+{
+ boolean edge_added;
+
+ edge_added =
+ sparseg_adjl_add_dir_edge_no_extend(V, n, A, size_A, pos, u, v,
+ CHECK);
+
+ if (edge_added)
+ sparseg_adjl_add_dir_edge_no_extend(V, n, A, size_A, pos, v, u,
+ FALSE);
+
+ return edge_added;
+}
+
+
+boolean
+sparseg_adjl_add_dir_edge (t_ver_sparse_rep *V, int n,
+ t_adjl_sparse_rep **A, int *size_A, int *pos, int u, int v,
+ boolean CHECK)
+ /*
+ add the DIRECTED edge to the sparse graph (V, n, A)
+ - pos records where to add the next edge in A
+ - if pos >= size_A, we must extend A
+
+ we check if the edge is already in the graph iff CHECK true
+ */
+{
+ boolean edge_exists;
+
+ edge_exists = FALSE;
+ if (CHECK)
+ {
+ edge_exists = sparseg_adjl_dir_edge_exists(V, n, *A, u, v);
+
+ if (edge_exists)
+ return FALSE;
+ }
+
+ if (*pos == *size_A)
+ {
+ *size_A += 1; /* add one directed edge */
+ *A = (t_adjl_sparse_rep *)
+ mem_realloc(*A, sizeof(t_adjl_sparse_rep) * *size_A);
+ }
+ ASSERT(*pos < *size_A);
+
+ sparseg_adjl_add_dir_edge_no_extend(V, n, *A, *size_A, pos, u, v,
+ FALSE);
+
+ return TRUE;
+}
+
+boolean
+sparseg_adjl_add_dir_edge_no_extend (t_ver_sparse_rep *V, int n,
+ t_adjl_sparse_rep *A, int size_A, int *pos, int u, int v, boolean CHECK)
+ /*
+ add an edge where A is guaranteed to be be big enough
+ (unless that for some reason we attempt to add
+ an edge which is already there)
+
+ this feature is required when A is part of a Magma block:
+ we do not want to reallocate A here
+ (would be done at a higher level)
+
+ we check if the edge is already in the graph iff CHECK true
+ */
+{
+ /*
+ given the way V and A represent the graph, it is simplest
+ to add the new edge at the beginning of i's adj. list
+ */
+ int i_v;
+ t_adjl_sparse_rep a;
+
+ if (CHECK && sparseg_adjl_dir_edge_exists(V, n, A, u, v))
+ return FALSE;
+
+ if (*pos >= size_A)
+ DIE();
+
+ /*
+ otherwise always add the edge
+ */
+ i_v = *pos;
+ a.end_vertex = v;
+ a.next = V[u].first_edge;
+ A[(*pos)++] = a;
+ V[u].first_edge = i_v;
+
+ return TRUE;
+}
+
+
+
+boolean
+sparseg_adjl_remove_edge_no_red (t_ver_sparse_rep *V, t_adjl_sparse_rep *A,
+ int u, int v)
+ /*
+ remove the UNDIRECTED edge from sparse graph (V, A)
+ if (u, v) is not an edge then nothing changes (and return FALSE)
+
+ A will be left with "holes"
+ */
+{
+ sparseg_adjl_remove_dir_edge_no_red(V, A, u, v);
+ return sparseg_adjl_remove_dir_edge_no_red(V, A, v, u);
+}
+
+
+boolean
+sparseg_adjl_remove_dir_edge_no_red (t_ver_sparse_rep *V,
+ t_adjl_sparse_rep *A, int u, int v)
+ /*
+ remove the DIRECTED edge from the sparse graph (V, n, A)
+ if (u, v) is not an edge then nothing changes (and return FALSE)
+
+ A will be left with "holes"
+ */
+{
+ int cur_e, prev_e;
+
+ cur_e = V[u].first_edge;
+ if (cur_e == NIL)
+ /*
+ (u, v) is not an edge
+ */
+ return FALSE;
+
+ if (A[cur_e].end_vertex == v)
+ {
+ V[u].first_edge = A[cur_e].next;
+ return TRUE; /* done */
+ }
+
+ while (A[cur_e].end_vertex != v)
+ /*
+ if (u, v) is an edge then this loop will terminate
+ */
+ {
+ prev_e = cur_e;
+ cur_e = A[cur_e].next;
+ if (cur_e == NIL)
+ /*
+ (u, v) is not an edge
+ */
+ return FALSE;
+ }
+ ASSERT(A[cur_e].end_vertex == v);
+
+ A[prev_e].next = A[cur_e].next;
+ return TRUE;
+}
+
+int
+sparseg_adjl_remove_all_dir_edge_no_red (t_ver_sparse_rep *V,
+ t_adjl_sparse_rep *A, int u, int v)
+ /*
+ remove all DIRECTED edges [u, v] from the non-simple
+ sparse graph (V, n, A)
+ if (u, v) is not an edge then nothing changes;
+ we return the number of edges removed
+
+ A will be left with "holes"
+ */
+{
+ int cur_e, prev_e, e_removed;
+
+ if (V[u].first_edge == NIL)
+ /*
+ (u, v) is not an edge
+ */
+ return 0;
+
+ e_removed = 0;
+ while (A[V[u].first_edge].end_vertex == v)
+ {
+ V[u].first_edge = A[V[u].first_edge].next;
+ e_removed++;
+
+ if (V[u].first_edge == NIL)
+ return e_removed;
+ }
+ ASSERT(A[V[u].first_edge].end_vertex != v);
+
+ prev_e = V[u].first_edge;
+ cur_e = A[prev_e].next;
+ while (cur_e != NIL)
+ {
+ if (A[cur_e].end_vertex == v)
+ {
+ A[prev_e].next = A[cur_e].next;
+ e_removed++;
+ cur_e = A[cur_e].next;
+ }
+ else
+ {
+ prev_e = cur_e;
+ cur_e = A[cur_e].next;
+ }
+ }
+
+ return e_removed;
+}
+
+
+
+void
+sparseg_adjl_add_vertices (t_ver_sparse_rep **V, int n, int nmore)
+ /*
+ add nmore vertices
+ V is assumed to have length n
+ */
+{
+ *V = (t_ver_sparse_rep *)
+ mem_realloc(*V, sizeof(t_ver_sparse_rep) * (n + nmore));
+
+ sparseg_adjl_add_vertices_no_extend(*V, n, nmore);
+}
+
+void
+sparseg_adjl_add_vertices_no_extend (t_ver_sparse_rep *V, int n, int nmore)
+ /*
+ add nmore vertices,
+ here V is assumed to have length n + nmore (ie V has already
+ been made bigger)
+ */
+{
+ int v;
+
+ for (v = n; v < n + nmore; v++)
+ {
+ V[v].first_edge = NIL;
+ }
+}
+
+void
+sparseg_adjl_remove_vertex (t_ver_sparse_rep **V, int n,
+ t_adjl_sparse_rep *A, int pos_A, int w, int *e)
+ /*
+ V is assumed to have length n: we will reallocate
+ V so that V will have length n-1
+
+ A is occupied from [0..pos-1], A will be left with holes
+
+ we also assume that the graph can have loops and multiple edges;
+ further, we the edge counting implicitely assumes that graph
+ is undirected!!!
+
+ this must be eventually fixed
+ */
+{
+ int v, nv, edge, loops;
+ t_ver_sparse_rep *new_V;
+
+ /*
+ we first count the loops if any
+ */
+ loops = 0;
+ edge = (*V)[w].first_edge;
+ while (edge != NIL)
+ {
+ loops = A[edge].end_vertex == w ? loops + 1 : loops;
+ edge = A[edge].next;
+ }
+ ASSERT(loops % 2 == 0);
+ loops /= 2;
+
+ /*
+ we recreate the vertices array
+ */
+ new_V = (t_ver_sparse_rep *)
+ mem_malloc(sizeof(t_ver_sparse_rep) * (n - 1));
+
+ for (v = 0, nv = 0; v < n; v++, nv++)
+ {
+ if (v == w)
+ {
+ nv--;
+ }
+ else
+ {
+ new_V[nv].first_edge = (*V)[v].first_edge;
+ }
+ }
+ mem_free(*V);
+ *V = new_V;
+
+ *e -= loops;
+ sparseg_adjl_remove_vertex_no_red(*V, n, A, w, e);
+
+ /*
+ oops! not relabelling vertices can wreck havock!
+ */
+ sparseg_adjl_relabel_vertex(A, pos_A, w);
+}
+
+void
+sparseg_adjl_remove_vertex_no_red (t_ver_sparse_rep *V, int n,
+ t_adjl_sparse_rep *A, int w, int *e)
+ /*
+ here V has already size n - 1 and has been initialised,
+ all what remains to do is to remove the edges incident
+ from w in A
+
+ A will be left with holes
+ */
+{
+ int v, nbr_e_removed;
+
+ nbr_e_removed = 0;
+ for (v = 0; v < n - 1; v++)
+ {
+ nbr_e_removed += sparseg_adjl_remove_all_dir_edge_no_red(V, A, v, w);
+ }
+
+ *e= *e - nbr_e_removed;
+}
+
+void
+sparseg_adjl_relabel_vertex (t_adjl_sparse_rep *A, int pos, int u)
+ /*
+ relabel all vertices v > u as v-1
+ (required when removing a vertex)
+ */
+{
+ int i;
+
+ for (i = 0; i < pos; i++)
+ {
+ A[i].end_vertex = A[i].end_vertex > u ?
+ A[i].end_vertex - 1 : A[i].end_vertex;
+ }
+}
+
+/*
+ * sparseg_adjl_pred.c
+ */
+
+/*
+ What:
+ *****
+
+ Implementing:
+
+ Some high-level functions on the sparse graph as
+ an adjacency list: predicates.
+
+
+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ authors:
+ ********
+
+ Paulette Lieby (Magma), Brendan McKay (ANU)
+
+ Started October 2001
+*/
+
+#include "planarity.h"
+
+#define IF_DEB(x) {}
+#define IF_VERB(x) {}
+
+
+
+/* aproto: header embed_graph_protos.h */
+
+
+#ifndef PLANAR_IN_MAGMA
+#endif
+
+boolean
+sparseg_adjl_dir_edge_exists (t_ver_sparse_rep *V, int n,
+ t_adjl_sparse_rep *A, int u, int v)
+ /*
+ does the directed edge [u, v] already exist in the graph
+ */
+{
+ int cur_e, prev_e;
+
+ cur_e = V[u].first_edge;
+ if (cur_e == NIL)
+ return FALSE;
+
+ if (A[cur_e].end_vertex == v)
+ {
+ return TRUE;
+ }
+
+ while (A[cur_e].end_vertex != v)
+ {
+ prev_e = cur_e;
+ cur_e = A[cur_e].next;
+ if (cur_e == NIL)
+ /*
+ (u, v) is not an edge
+ */
+ return FALSE;
+ }
+ ASSERT(A[cur_e].end_vertex == v);
+ return TRUE;
+}
+
+
+
+boolean
+sparseg_adjl_u_adj_v (t_ver_sparse_rep *V, int n, t_adjl_sparse_rep *A,
+ int u, int v)
+ /*
+ is u adj. to v
+ */
+{
+ return sparseg_adjl_dir_edge_exists(V, n, A, u, v);
+}
+
+
+boolean
+sparseg_adjl_sub (t_ver_sparse_rep *V1, int n1, t_adjl_sparse_rep *A1,
+ t_ver_sparse_rep *V2, int n2, t_adjl_sparse_rep *A2)
+ /*
+ test if the (V1, n1, A1) sparse graph is a subgraph of
+ the (V2, n2, A2) graph
+ */
+{
+ int v, *fp, n, bign, i;
+
+ n = n1 > n2 ? n2 : n1;
+ bign = n1 > n2 ? n1 : 0;
+ fp = (int *) mem_malloc(sizeof(int) * n);
+ for (i = 0; i < n; i++)
+ fp[i] = NIL;
+
+ for (v = 0; v < n; v++)
+ {
+ int ne1, ne2;
+
+ ne1 = V1[v].first_edge;
+ ne2 = V2[v].first_edge;
+ if (ne1 == NIL)
+ {
+ continue;
+ }
+ else if (ne2 == NIL)
+ {
+ mem_free(fp);
+ return FALSE;
+ }
+
+ while (ne2 != NIL)
+ {
+ int u2;
+
+ u2 = A2[ne2].end_vertex;
+ fp[u2] = v;
+ ne2 = A2[ne2].next;
+ }
+
+ while (ne1 != NIL)
+ {
+ int u1;
+
+ u1 = A1[ne1].end_vertex;
+ if (fp[u1] != v)
+ {
+ mem_free(fp);
+ return FALSE;
+ }
+ ne1 = A1[ne1].next;
+ }
+ }
+ mem_free(fp);
+
+ for (v = n; v < bign; v++)
+ /*
+ those vertices must not be end points of edges:
+ this chcek is only necessary in the digraph case
+ */
+ {
+ if (V1[v].first_edge != NIL)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+
+boolean
+sparseg_adjl_eq (t_ver_sparse_rep *V1, int n1, t_adjl_sparse_rep *A1,
+ t_ver_sparse_rep *V2, int n2, t_adjl_sparse_rep *A2)
+ /*
+ compare the two sparse graphs (V1, n1, A1) & (V2, n2, A2)
+ we don't know their number of edges
+ */
+{
+ if (n1 != n2)
+ return FALSE;
+
+ return sparseg_adjl_sub(V1, n1, A1, V2, n2, A2)
+ && sparseg_adjl_sub(V2, n2, A2, V1, n1, A1);
+}
+
+
+
+/*
+ * sparseg_dlcl_misc.c
+ */
+
+/*
+ What:
+ *****
+
+ Implementing:
+
+ Housekeeping for an internal sparse graph representation
+ internal to the planarity tester and obstruction isolator.
+
+ This sparse graph consists of an array of doubly linked circular lists
+ (the neighbour lists for each vertex).
+
+
+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ authors:
+ ********
+
+ Paulette Lieby (Magma), Brendan McKay (ANU)
+
+ Started October 2001
+*/
+
+#include "planarity.h"
+
+#define IF_DEB(x) {}
+#define IF_VERB(x) {}
+
+
+/* aproto: header embed_graph_protos.h */
+
+/* aproto: beginstatic -- don't touch this!! */
+static boolean sparseg_dlcl_is_present (t_dlcl *, int, t_dlcl **);
+/* aproto: endstatic -- don't touch this!! */
+
+
+#ifndef PLANAR_IN_MAGMA
+#endif
+
+
+void
+sparseg_dlcl_delete (t_dlcl **g, int n)
+{
+ int i;
+
+ for (i = 0; i < n; i++)
+ {
+ embedg_dlcl_delete(g[i]);
+ }
+ mem_free(g);
+}
+
+void
+sparseg_dlcl_print (t_dlcl **g, int n)
+{
+ int i;
+
+ for (i = 0; i < n; i++)
+ {
+ fprintf(stdout,"%d:\t", i);
+ embedg_dlcl_print(g[i]);
+ }
+}
+
+
+static boolean
+sparseg_dlcl_is_present (t_dlcl *l, int label, t_dlcl **p)
+{
+ *p = embedg_dlcl_find(l, label);
+ return *p == NP ? FALSE : TRUE;
+}
+
+
+boolean
+sparseg_dlcl_is_adjacent (t_dlcl **g, int n, int v, int u, t_dlcl **p)
+ /*
+ is u adjacent to v
+ */
+{
+ ASSERT(v >= 0 && v < n && u >= 0 && u < n);
+ return sparseg_dlcl_is_present(g[v], u, p);
+}
+
+void
+sparseg_dlcl_append_to_neigh_list (t_dlcl **g, int n, int v, int u, int in_adjl)
+ /*
+ append u to the neighbour list of v
+ */
+{
+ t_dlcl *u_rec;
+
+ u_rec = embedg_dlcl_rec_new(u);
+ u_rec->in_adjl = in_adjl;
+ g[v] = embedg_dlcl_rec_append(g[v], u_rec);
+}
+
+
+
+
+void
+sparseg_dlcl_to_sparseg (t_dlcl **g, int n, int e,
+ t_ver_sparse_rep **V, t_adjl_sparse_rep **A)
+ /*
+ e is the number of undirected edges of g
+
+ convert a dlcl into the standard sparseg rep. as an
+ adjacency list
+ */
+{
+ int i_e, v;
+
+ *V = (t_ver_sparse_rep *) mem_malloc(sizeof(t_ver_sparse_rep) * n);
+ *A = (t_adjl_sparse_rep *) mem_malloc(sizeof(t_adjl_sparse_rep) * 2 * e);
+
+ for (v = 0; v < n; v++)
+ (*V)[v].first_edge = NIL;
+
+ i_e = 0;
+ for (v = 0; v < n; v++)
+ {
+ t_dlcl *l, *p;
+
+ l = p = g[v];
+ if (!embedg_dlcl_is_empty(p))
+ {
+ t_adjl_sparse_rep a;
+
+ ASSERT((*V)[v].first_edge == NIL);
+ (*V)[v].first_edge = i_e;
+ a.end_vertex = p->info;
+ a.next = i_e + 1;
+ (*A)[i_e++] = a;
+
+ p = embedg_dlcl_list_next(p);
+ while (p != l)
+ {
+ a.end_vertex = p->info;
+ a.next = i_e + 1;
+ (*A)[i_e++] = a;
+
+ p = embedg_dlcl_list_next(p);
+ }
+
+ /*
+ end of list for v
+ */
+ (*A)[i_e - 1].next = NIL;
+ }
+ }
+ ASSERT(i_e == 2 * e);
+}
+
+boolean
+sparseg_dlcl_sub (t_dlcl **g1, int n1, t_dlcl **g2, int n2)
+ /*
+ is g2 a subgraph of g1
+
+ I request that both graphs have same order
+
+ This is not used anywhere... do we need it???
+ */
+{
+ int n, v, *fp;
+
+ if (n1 != n2)
+ return FALSE;
+
+ n = n1;
+ fp = (int *) mem_malloc(sizeof(int) * n);
+ for (v = 0; v < n; v++)
+ fp[v] = NIL;
+
+ for (v = 0; v < n; v++)
+ {
+ t_dlcl *l1, *p1, *l2, *p2;
+
+ l1 = p1 = g1[v];
+ l2 = p2 = g2[v];
+ if (embedg_dlcl_is_empty(p1) && !embedg_dlcl_is_empty(p2))
+ {
+ mem_free(fp);
+ return FALSE;
+ }
+ if (embedg_dlcl_is_empty(p2))
+ {
+ continue;
+ }
+
+ fp[p1->info] = v;
+ p1 = embedg_dlcl_list_next(p1);
+ while (p1 != l1)
+ {
+ fp[p1->info] = v;
+ p1 = embedg_dlcl_list_next(p1);
+ }
+
+ if (fp[p2->info] != v)
+ {
+ mem_free(fp);
+ return FALSE;
+ }
+ p2 = embedg_dlcl_list_next(p2);
+ while (p2 != l2)
+ {
+ if (fp[p2->info] != v)
+ {
+ mem_free(fp);
+ return FALSE;
+ }
+ }
+ }
+ mem_free(fp);
+
+ return TRUE;
+}
+/*
+ * VES_misc.c
+ */
+
+/*
+ What:
+ *****
+
+ Implementing:
+
+ All low-level routines for the VES structure:
+
+ - the VES structure is solely used within the planarity tester
+ and obstruction isolator
+
+ - it stores vertices, virtual vertices and edges
+ --more on this later--
+
+ - it allows for circular doubly linked lists, hence
+ enabling us -among other things- to store the
+ graph embedding if the tester is successful
+
+ - basic features:
+ + the VES has exactly size 2n + 2(3n-5) :
+ we add at most one more edge than the max for a planar graph
+ (need to x by 2: we store directed edges)
+ + a vertex and the edges incident FROM it are linked in a doubly
+ linked circular list
+ + where a vertex is inserted between two of its outcoming edges
+ determines an external face walk for a bicomponent
+ + the twin edge is more commonly known as the inverse edge
+ + we have tree and back edges (from the DFS), and short-cut edges
+ which are added by the tester
+ -but short-cut edges are added in such a way as to maintain
+ planarity (in a local sense)
+ + vertices and edges can be marked (visited for example)
+ + they have an orientation which must be eventuall recovered
+ and which is set in the merge_bicomp routine
+ + vertices are essentially known via their DFI or DFS index
+ (though their label is stored too)
+
+ blah, blah.... later then.
+ Have a look at embedg_planar_alg_init which initialises the VES
+ structure
+
+
+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ from
+
+ Simplified O(n) Planarity Algorithms (draft)
+ ************************************
+
+ John Boyer JBoyer@PureEdge.com, jboyer@acm.org
+ Wendy Myrvold wendym@csr.uvic.ca
+
+
+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ authors:
+ ********
+
+ Paulette Lieby (Magma), Brendan McKay (ANU)
+
+ Started October 2001
+*/
+
+
+#include "planarity.h"
+
+#define IF_DEB(x) {}
+#define IF_DEB_SCE(x) {}
+#define IF_DEB_PROPER_FACE(x) {}
+#define IF_VERB(x) {}
+
+
+/* aproto: header embed_graph_protos.h */
+
+boolean
+embedg_VES_is_vertex (int n, int i)
+ /*
+ is this a vertex
+ (relative to the "big" array of size 2n + 2(3n-5))
+ */
+{
+ return i < n ? TRUE : FALSE;
+}
+
+boolean
+embedg_VES_is_virtual_vertex (int n, int i)
+ /*
+ is this a virtual vertex
+ (relative to the "big" array of size 2n + 2(3n-5))
+
+ a virtual vertex is a vertex v^c which denotes the
+ DFS parent of the child c
+
+ see embedg_planar_alg_init for more
+ */
+{
+ return i >= n && i < 2*n ? TRUE : FALSE;
+}
+
+boolean
+embedg_VES_is_edge (int n, int i)
+ /*
+ is this an edge
+ (relative to the "big" array of size 2n + 2(3n-5))
+ */
+{
+ return i >= 2*n ? TRUE : FALSE;
+}
+
+boolean
+embedg_VES_is_tree_edge (t_ver_edge *embed_graph, int n, int i)
+ /*
+ is this s tree edge
+ */
+{
+ return embedg_VES_is_edge(n, i)
+ && embed_graph[i].type == TE;
+}
+
+boolean
+embedg_VES_is_back_edge (t_ver_edge *embed_graph, int n, int i)
+ /*
+ is this a back edge
+ */
+{
+ return embedg_VES_is_edge(n, i)
+ && embed_graph[i].type == BE;
+}
+
+boolean
+embedg_VES_is_short_cut_edge (t_ver_edge *embed_graph, int n, int i)
+ /*
+ as the name indicates...
+ */
+{
+ return embedg_VES_is_edge(n, i)
+ && embed_graph[i].type == SCE;
+}
+
+void
+embedg_VES_print_vertex (int n, int v)
+{
+ ASSERT(embedg_VES_is_vertex(n, v));
+ fprintf(stdout, "%d ", v);
+}
+
+void
+embedg_VES_print_virtual_vertex (t_ver_edge *embed_graph, int n, int v)
+{
+ int c;
+
+ ASSERT(embedg_VES_is_virtual_vertex(n, v));
+ c = v - n;
+ fprintf(stdout, "%d^%d ", embed_graph[c].DFS_parent, c);
+}
+
+void
+embedg_VES_print_any_vertex (t_ver_edge *embed_graph, int n, int v)
+{
+ if (embedg_VES_is_vertex(n, v))
+ {
+ embedg_VES_print_vertex(n, v);
+ }
+ else
+ {
+ embedg_VES_print_virtual_vertex(embed_graph, n, v);
+ }
+}
+
+void
+embedg_VES_print_any_rec (t_ver_edge *embed_graph, int n, int r)
+{
+ if (embedg_VES_is_edge(n, r))
+ {
+ embedg_VES_print_edge(embed_graph, n, r);
+ }
+ else
+ {
+ embedg_VES_print_any_vertex(embed_graph, n, r);
+ }
+}
+
+void
+embedg_VES_print_edge (t_ver_edge *embed_graph, int n, int e)
+{
+ int v, prev, cur;
+
+ ASSERT(embedg_VES_is_edge(n, e));
+
+ /*
+ must find the vertex in the doubly linked circular list
+ of vertices/edges
+ */
+
+ prev = e;
+ cur = v = embed_graph[e].link[0];
+ if (embedg_VES_is_vertex(n, v)
+ || embedg_VES_is_virtual_vertex(n, v))
+ {
+ embedg_VES_print_any_vertex(embed_graph, n, v);
+ fprintf(stdout, ", ");
+ embedg_VES_print_any_vertex(embed_graph, n,
+ embed_graph[e].neighbour);
+ fprintf(stdout, "):0\n");
+ }
+ else while (!embedg_VES_is_vertex(n, v)
+ && !embedg_VES_is_virtual_vertex(n, v))
+ {
+ v = embedg_VES_get_next_in_dlcl(embed_graph, n,
+ cur, prev);
+
+ if (embedg_VES_is_vertex(n, v)
+ || embedg_VES_is_virtual_vertex(n, v))
+ {
+ embedg_VES_print_any_vertex(embed_graph, n, v);
+ fprintf(stdout, ", ");
+ embedg_VES_print_any_vertex(embed_graph, n,
+ embed_graph[e].neighbour);
+ fprintf(stdout, "):0\n");
+ }
+ else
+ {
+ prev = cur;
+ cur = v;
+ }
+ }
+}
+
+void
+embedg_VES_print_flipped_edges (t_ver_edge *embed_graph, int n, int edge_pos)
+ /*
+ print those edges in the structure whose sign is CLOCKW,
+ ie which have been flipped at some stage
+ */
+{
+ int e;
+
+ for (e = 2*n; e <= edge_pos; e++)
+ {
+ if (!embedg_VES_is_short_cut_edge(embed_graph, n, e))
+ /*
+ we don't care about the short-cut edges
+ */
+ {
+ if (embed_graph[e].sign != CCLOCKW)
+ {
+ embedg_VES_print_edge(embed_graph, n, e);
+ }
+ }
+ }
+}
+
+#if 0
+int
+embedg_VES_get_edge_from_ver (t_ver_edge *embed_graph, int n, int v)
+ /*
+ not used anywhere; why is this here???
+ */
+{
+ int in, e;
+
+ ASSERT(embedg_VES_is_vertex(n, v)
+ || embedg_VES_is_virtual_vertex(n, v));
+
+ in = embedg_VES_is_edge(n, embed_graph[v].link[0]) ? 0 : 1;
+ e = embed_graph[v].link[in];
+ ASSERT(embedg_VES_is_edge(n, e));
+
+ return e;
+}
+
+int
+embedg_VES_get_ver_from_edge (t_ver_edge *embed_graph, int n, int e)
+{
+ int in, v;
+
+ ASSERT(embedg_VES_is_edge(n, e));
+
+ in = embedg_VES_is_vertex(n, embed_graph[e].link[0])
+ || embedg_VES_is_virtual_vertex(n, embed_graph[e].link[0])
+ ?
+ 0 : 1;
+
+ v = embed_graph[e].link[in];
+ ASSERT(embedg_VES_is_vertex(n, v)
+ || embedg_VES_is_virtual_vertex(n, v));
+
+ return v;
+}
+#endif
+
+int
+embedg_VES_get_twin_edge (t_ver_edge *embed_graph, int n, int e)
+ /*
+ the twin edge is understood as being the inverse edge
+ */
+{
+ int twin;
+
+ ASSERT(embedg_VES_is_edge(n, e));
+
+ twin = e % 2 == 0 ? e + 1 : e - 1;
+ ASSERT(embedg_VES_is_edge(n, twin));
+
+ return twin;
+}
+
+int
+embedg_VES_get_ver_from_virtual (t_ver_edge *embed_graph, int n, int vv)
+ /*
+ get v from the virtual vertex v^c
+ */
+{
+ int v;
+
+ ASSERT(embedg_VES_is_virtual_vertex(n, vv));
+ v = embed_graph[vv - n].DFS_parent;
+
+ return v;
+}
+
+int
+embedg_VES_get_ver (t_ver_edge *embed_graph, int n, int v)
+{
+ if (embedg_VES_is_virtual_vertex(n, v))
+ return embedg_VES_get_ver_from_virtual(embed_graph, n, v);
+
+ return v;
+}
+
+
+int
+embedg_VES_get_next_in_dlcl (t_ver_edge *embed_graph, int n, int r, int prev)
+ /*
+ r is a (virtual) vertex or edge record in embed_graph:
+ get the next in the list (formed by the .link[] fields)
+ in the doubly linked circular list
+
+ so that prev != next
+ -- NOTE: a priori these lists always contain 2 elts at least
+ so that there shouldn't be any problem...
+ --> huh? is that true?
+ */
+{
+ return embed_graph[r].link[0] == prev ?
+ embed_graph[r].link[1] : embed_graph[r].link[0];
+}
+
+
+void
+embedg_VES_walk_bicomp (t_ver_edge *embed_graph, int n, int v, int vin)
+ /*
+ walk the external face of the bicomp starting
+ at VIRTUAL vertex v entered via vin
+
+ this of course assumes that the "thing" rooted at
+ v is a bicomponent -- depending where we are at in the
+ tester this is not necessarily the case
+ -- I comment upon this in merge_bicomps.c:
+ embedg_VES_merge_pertinent_bicomps
+ */
+{
+ int start, startin, s, sin;
+
+ ASSERT(embedg_VES_is_virtual_vertex(n, v));
+
+ embedg_VES_print_virtual_vertex(embed_graph, n, v);
+
+ s = NIL;
+ start = v;
+ startin = vin;
+ while (s != v)
+ {
+ embedg_VES_get_succ_on_ext_face(embed_graph, n, start, startin,
+ FALSE, 0, &s, &sin);
+ if (embedg_VES_is_virtual_vertex(n, s))
+ {
+ embedg_VES_print_virtual_vertex(embed_graph, n, s);
+ }
+ else
+ {
+ embedg_VES_print_vertex(n, s);
+ }
+ start = s;
+ startin = sin;
+ }
+ fprintf(stdout, "\n");
+}
+
+void
+embedg_VES_print_adj_list (t_ver_edge *embed_graph, int n, int r,
+ boolean consistent)
+ /*
+ print r's adjacency list - r can be a vertex or edge
+
+ the boolean if true assumes that
+ the list is consistent (will determine the way we traverse the list)
+
+ a priori we should get the same result either way
+ */
+{
+ if (consistent)
+ {
+ int next;
+
+ embedg_VES_print_any_rec(embed_graph, n, r);
+
+ next = embed_graph[r].link[0];
+ while (next != r)
+ {
+ embedg_VES_print_any_rec(embed_graph, n, next);
+ next = embed_graph[next].link[0];
+ }
+ }
+ else
+ {
+ int prev, cur, next;
+
+ embedg_VES_print_any_rec(embed_graph, n, r);
+
+ prev = r;
+ cur = embed_graph[r].link[0];
+
+ while (cur != r)
+ {
+ embedg_VES_print_any_rec(embed_graph, n, cur);
+ next = embedg_VES_get_next_in_dlcl(embed_graph, n,
+ cur, prev);
+ prev = cur;
+ cur = next;
+ }
+ }
+}
+
+boolean
+embedg_VES_is_adj_list_consistent (t_ver_edge *embed_graph, int n, int r)
+ /*
+ checks that r's adjacency list is consistent:
+ ie, that either traversing it using link[0] always
+ or traversing it using embedg_VES_get_next_in_dlcl
+ gives the SAME result
+ */
+{
+ int *list_link, *list_n_dldl, il, id, i;
+
+ list_link = (int *) mem_malloc(sizeof(int) * 2 * n);
+ list_n_dldl = (int *) mem_malloc(sizeof(int) * 2 * n);
+ /*
+ must allocate 2*n space: I could have TE and SCE with same neighbour
+ (or BE and SCE as well)
+ */
+ il = id = -1;
+
+ /*
+ traversing the list via link[0]
+ */
+ {
+ int next;
+
+ list_link[++il] = r;
+
+ next = embed_graph[r].link[0];
+ while (next != r)
+ {
+ list_link[++il] = next;
+ next = embed_graph[next].link[0];
+ }
+ }
+
+ /*
+ traversing the list using embedg_VES_get_next_in_dlcl
+ */
+ {
+ int prev, cur, next;
+
+ list_n_dldl[++id] = r;
+ prev = r;
+ cur = embed_graph[r].link[0];
+
+ while (cur != r)
+ {
+ list_n_dldl[++id] = cur;
+ next = embedg_VES_get_next_in_dlcl(embed_graph, n,
+ cur, prev);
+ prev = cur;
+ cur = next;
+ }
+ }
+
+ if (il != id)
+ {
+ mem_free(list_link);
+ mem_free(list_n_dldl);
+ return FALSE;
+ }
+
+ for (i = 0; i <= il; i++)
+ {
+ if (list_link[i] != list_n_dldl[i])
+ {
+ mem_free(list_link);
+ mem_free(list_n_dldl);
+ return FALSE;
+ }
+ }
+
+ mem_free(list_link);
+ mem_free(list_n_dldl);
+ return TRUE;
+}
+
+
+boolean
+embedg_VES_are_adj_lists_consistent (t_ver_edge *embed_graph, int n)
+ /*
+ checks that the adjacency list of each vertex is consistent
+ in the manner of embedg_VES_is_adj_list_consistent
+ */
+{
+ int i;
+
+ /*
+ it is enough to visit the vertices and virtual vertices only
+ (I don't think it is enough to do the vertices only --??)
+ */
+ for (i = 0; i < 2*n; i++)
+ if (!embedg_VES_is_adj_list_consistent(embed_graph, n, i))
+ return FALSE;
+
+ return TRUE;
+}
+
+
+
+void
+embedg_VES_remove_edge (t_ver_edge *embed_graph, int n, int e)
+ /*
+ remove edge e from the embedding
+ */
+{
+ int r1, r2, r1out, r2in, twin;
+
+ ASSERT(embedg_VES_is_edge(n, e));
+
+ IF_DEB_SCE(
+ fprintf(stdout, "removing an SCE, enter\n");
+ embedg_VES_print_edge(embed_graph, n, e);
+ )
+
+ r1 = embed_graph[e].link[0];
+ r2 = embed_graph[e].link[1];
+
+ /*
+ disable e and link r1 and r2 together:
+ we had r1 -> e -> r2
+ */
+ embed_graph[e].link[0] = embed_graph[e].link[1] = e;
+
+ r1out = embed_graph[r1].link[0] == e ? 0 : 1;
+ r2in = embed_graph[r2].link[0] == e ? 0 : 1;
+
+ if (r1 == r2)
+ /*
+ this I think should never happen, but one never knows...
+ */
+ {
+ embed_graph[r1].link[0] = embed_graph[r1].link[1] = r1;
+ }
+ else
+ {
+ embed_graph[r1].link[r1out] = r2;
+ embed_graph[r2].link[r2in] = r1;
+ }
+
+ ASSERT(embedg_VES_is_adj_list_consistent(embed_graph, n, r1));
+
+ /*
+ now we must do a similar thing for the twin
+ (which must get reomved as well)
+ */
+ twin = embedg_VES_get_twin_edge(embed_graph, n, e);
+
+ IF_DEB_SCE(
+ fprintf(stdout, "removing an SCE, the twin\n");
+ embedg_VES_print_edge(embed_graph, n, twin);
+ )
+
+ r1 = embed_graph[twin].link[0];
+ r2 = embed_graph[twin].link[1];
+
+ embed_graph[twin].link[0] = embed_graph[twin].link[1] = twin;
+
+ r1out = embed_graph[r1].link[0] == twin ? 0 : 1;
+ r2in = embed_graph[r2].link[0] == twin ? 0 : 1;
+
+ if (r1 == r2)
+ {
+ embed_graph[r1].link[0] = embed_graph[r1].link[1] = r1;
+ }
+ else
+ {
+ embed_graph[r1].link[r1out] = r2;
+ embed_graph[r2].link[r2in] = r1;
+ }
+
+ ASSERT(embedg_VES_is_adj_list_consistent(embed_graph, n, r1));
+}
+
+
+void
+embedg_VES_set_orientation (t_ver_edge *embed_graph, int n, int *ver_orient)
+ /*
+ using the vertices' orientation as given in ver_orient
+ we set the orientation for each edge in the adjacency list
+ for each vertex
+
+ to do this we use the field sign which is NOT needed
+ anymore by the tester since by the time we call this
+ function we would have finished with that bit (the tester)
+
+ sign is only set when merging bicomps
+ - even though we'll perform another walkdown when
+ recovering an obstruction (if any) no bicomp merging will occur,
+ so we are safe
+ */
+{
+ int v;
+
+ for (v = 0; v < n; v++)
+ {
+ int o, e;
+
+ o = ver_orient[v];
+ embed_graph[v].sign = o;
+
+ e = embed_graph[v].link[0];
+
+ while (e != v)
+ /*
+ just as a note: note the way I get the next in the list
+ here (as opposed to using
+ embedg_VES_get_next_in_dlcl):
+ this is because I implicitely assume that
+ the adjacency lists are consistent
+
+ Also note that edges can be SCE, it doesn't really matter
+ anyway (they may not have been removed yet
+ -- see the way we recover the obstruction:
+ embedg_mark_obstruction)
+ */
+ {
+ embed_graph[e].sign = o;
+ e = embed_graph[e].link[0];
+ }
+ }
+}
+
+
+/*
+ * dlcl_misc.c
+ */
+
+/*
+ What:
+ *****
+
+ Implementing:
+
+ Housekeeping for a simple doubly linked circular list:
+ this is a data structure ONLY used WITHIN
+ the planarity tester and obstruction isolator and is not to be
+ confused with the VES structure mentionned elsewhere.
+
+ The VES structure is an array, while the dlcl one is a list of
+ pointers.
+
+ The dlcl is especially useful as it allows for the storage
+ of an ordered list.
+
+
+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ from
+
+ Simplified O(n) Planarity Algorithms (draft)
+ ************************************
+
+ John Boyer JBoyer@PureEdge.com, jboyer@acm.org
+ Wendy Myrvold wendym@csr.uvic.ca
+
+
+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ authors:
+ ********
+
+ Paulette Lieby (Magma), Brendan McKay (ANU)
+
+ Started October 2001
+*/
+
+
+#include "planarity.h"
+
+#define IF_DEB(x) {}
+#define IF_VERB(x) {}
+
+
+/* aproto: header embed_graph_protos.h */
+
+/* aproto: beginstatic -- don't touch this!! */
+static void embedg_dlcl_rec_free (t_dlcl *);
+static void embedg_dlcl_rec_insert_right (t_dlcl *, t_dlcl *);
+static void embedg_dlcl_rec_insert_left (t_dlcl *, t_dlcl *);
+static void embedg_dlcl_rec_retrieve (t_dlcl *);
+static void embedg_dlcl_rec_delete (t_dlcl *);
+static boolean embedg_dlcl_is_singleton (t_dlcl *);
+/* aproto: endstatic -- don't touch this!! */
+
+
+#ifndef PLANAR_IN_MAGMA
+#endif
+
+
+t_dlcl *
+embedg_dlcl_rec_new (int info)
+ /*
+ create a new record with info in the global array
+ to insert in the list
+ */
+{
+ t_dlcl *r;
+
+ r = (t_dlcl *) mem_malloc(sizeof(t_dlcl));
+ r->info = info;
+ r->in_adjl = r->twin_in_adjl = NIL;
+ r->mult = 1;
+ r->right = r;
+ r->left = r;
+ return r;
+}
+
+static void
+embedg_dlcl_rec_free (t_dlcl *r)
+ /*
+ free
+ */
+{
+ mem_free(r);
+}
+
+void
+embedg_dlcl_rec_print (t_dlcl *r)
+{
+ fprintf(stdout,"%d ", r->info);
+}
+
+void
+embedg_dlcl_print (t_dlcl *l)
+{
+ t_dlcl *p = l;
+
+ if (!embedg_dlcl_is_empty(p))
+ {
+ embedg_dlcl_rec_print(p);
+ p = embedg_dlcl_list_next(p);
+ while (p != l)
+ {
+ embedg_dlcl_rec_print(p);
+ p = embedg_dlcl_list_next(p);
+ }
+ }
+ fprintf(stdout,"\n");
+}
+
+
+static void
+embedg_dlcl_rec_insert_right (t_dlcl *l, t_dlcl *r)
+{
+ t_dlcl *tmp_r, *tmp_l;
+
+ tmp_r = l->right;
+ tmp_l = tmp_r->left;
+
+ l->right = r;
+ r->right = tmp_r;
+
+ r->left = tmp_l;
+ tmp_r->left = r;
+}
+
+
+static void
+embedg_dlcl_rec_insert_left (t_dlcl *l, t_dlcl *r)
+{
+ t_dlcl *tmp_r, *tmp_l;
+
+ tmp_l = l->left;
+ tmp_r = tmp_l->right;
+
+ l->left = r;
+ r->left = tmp_l;
+
+ r->right = tmp_r;
+ tmp_l->right = r;
+}
+
+t_dlcl *
+embedg_dlcl_rec_append (t_dlcl *l, t_dlcl *r)
+{
+ if (embedg_dlcl_is_empty(l))
+ return r;
+
+ embedg_dlcl_rec_insert_left(l, r);
+ return l;
+}
+
+t_dlcl *
+embedg_dlcl_rec_prepend (t_dlcl *l, t_dlcl *r)
+{
+ if (embedg_dlcl_is_empty(l))
+ return r;
+
+ embedg_dlcl_rec_insert_left(l, r);
+ return r;
+}
+
+t_dlcl *
+embedg_dlcl_cat (t_dlcl *l, t_dlcl *m)
+ /*
+ concatenate m to the RIGHT of the end of l
+ WITHOUT copying m
+ */
+{
+ t_dlcl *h1, *h2, *e1, *e2;
+
+ if (embedg_dlcl_is_empty(l))
+ return m;
+ if (embedg_dlcl_is_empty(m))
+ return l;
+
+ h1 = l;
+ e1 = l->left;
+ h2 = m;
+ e2 = m->left;
+
+ e1->right = h2;
+ h2->left = e1;
+ e2->right = h1;
+ h1->left = e2;
+
+ return l;
+}
+
+t_dlcl *
+embedg_dlcl_find (t_dlcl *l, int info)
+{
+ t_dlcl *p = l;
+
+ if (!embedg_dlcl_is_empty(p))
+ {
+ if (p->info == info)
+ {
+ return p;
+ }
+ p = embedg_dlcl_list_next(p);
+ while (p != l)
+ {
+ if (p->info == info)
+ {
+ return p;
+ }
+ p = embedg_dlcl_list_next(p);
+ }
+ }
+ return NP;
+}
+
+t_dlcl *
+embedg_dlcl_find_with_NIL_twin_in_adjl (t_dlcl *l, int info)
+{
+ t_dlcl *p = l;
+
+ if (!embedg_dlcl_is_empty(p))
+ {
+ if (p->info == info && p->twin_in_adjl == NIL)
+ {
+ return p;
+ }
+ p = embedg_dlcl_list_next(p);
+ while (p != l)
+ {
+ if (p->info == info && p->twin_in_adjl == NIL)
+ {
+ return p;
+ }
+ p = embedg_dlcl_list_next(p);
+ }
+ }
+ return NP;
+}
+
+
+
+static void
+embedg_dlcl_rec_retrieve (t_dlcl *r)
+{
+ t_dlcl *right, *left;
+
+ right = r->right;
+ left = r->left;
+
+ left->right = right;
+ right->left = left;
+
+ r->right = r;
+ r->left = r;
+}
+
+static void
+embedg_dlcl_rec_delete (t_dlcl *r)
+{
+ embedg_dlcl_rec_retrieve(r);
+ embedg_dlcl_rec_free(r);
+}
+
+
+t_dlcl *
+embedg_dlcl_delete_first (t_dlcl *l)
+ /*
+ prune the list from the head:
+ - set new head to right of old head
+ - delete old head
+ */
+{
+ t_dlcl *new_head;
+
+ ASSERT(!embedg_dlcl_is_empty(l));
+ if (embedg_dlcl_is_singleton(l))
+ {
+ new_head = NP;
+ }
+ else
+ {
+ new_head = l->right;
+ }
+ embedg_dlcl_rec_delete(l);
+ return new_head;
+}
+
+
+t_dlcl *
+embedg_dlcl_delete_rec (t_dlcl *l, t_dlcl *r)
+ /*
+ delete r from l;
+ if r == l, set new head to right of old head
+ */
+{
+ if (r == l)
+ {
+ return embedg_dlcl_delete_first(l);
+ }
+ embedg_dlcl_rec_delete(r);
+ return l;
+}
+
+
+boolean
+embedg_dlcl_is_empty (t_dlcl *l)
+{
+ return (l == NP) ? TRUE : FALSE;
+}
+
+
+static boolean
+embedg_dlcl_is_singleton (t_dlcl *l)
+{
+ return (l->right == l) ? TRUE : FALSE;
+ /*
+ same as l->left == l
+ */
+}
+
+t_dlcl *
+embedg_dlcl_list_next (t_dlcl *l)
+ /*
+ this assumes no choice in the direction of the walking
+ (always to the right)
+ -- good enough when deleting for example or when
+ the direction of the walking does not matter
+ */
+{
+ return l->right;
+}
+
+
+t_dlcl *
+embedg_dlcl_list_prev (t_dlcl *l)
+ /*
+ this assumes no choice in the direction of the walking
+ (always to the right)
+ */
+{
+ return l->left;
+}
+
+t_dlcl *
+embedg_dlcl_list_last (t_dlcl *l)
+{
+ return embedg_dlcl_list_prev(l);
+}
+
+
+
+void
+embedg_dlcl_delete (t_dlcl *l)
+{
+ if (!embedg_dlcl_is_empty(l))
+ {
+ while (!embedg_dlcl_is_singleton(l))
+ {
+ t_dlcl *next;
+
+ next = embedg_dlcl_list_next(l);
+ embedg_dlcl_rec_delete(next);
+ }
+ embedg_dlcl_rec_delete(l);
+ }
+}
+
+t_dlcl *
+embedg_dlcl_copy (t_dlcl *l)
+{
+ t_dlcl *p, *c;
+
+ if (embedg_dlcl_is_empty(l))
+ return NP;
+
+ c = embedg_dlcl_rec_new(l->info);
+
+ p = embedg_dlcl_list_next(l);
+ while (p != l)
+ {
+ t_dlcl *temp;
+
+ temp = embedg_dlcl_rec_new(p->info);
+ temp->in_adjl = p->in_adjl;
+ temp->twin_in_adjl = p->twin_in_adjl;
+ temp->mult = p->mult;
+ c = embedg_dlcl_rec_append(c, temp);
+ p = embedg_dlcl_list_next(p);
+ }
+ return c;
+}
+
+
+int
+embedg_dlcl_length (t_dlcl *l)
+{
+ t_dlcl *p;
+ int n;
+
+ if (embedg_dlcl_is_empty(l))
+ return 0;
+
+ p = embedg_dlcl_list_next(l);
+ n = 1;
+ while (p != l)
+ {
+ n++;
+ p = embedg_dlcl_list_next(p);
+ }
+ return n;
+}
+/*
+ * planar_by_edge_addition.c
+ */
+
+/*
+ What:
+ *****
+
+ Implementing:
+
+ The top level for the planarity tester.
+
+
+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ from
+
+ Simplified O(n) Planarity Algorithms (draft)
+ ************************************
+
+ John Boyer JBoyer@PureEdge.com, jboyer@acm.org
+ Wendy Myrvold wendym@csr.uvic.ca
+
+
+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ authors:
+ ********
+
+ Paulette Lieby (Magma), Brendan McKay (ANU)
+
+ Started October 2001
+*/
+
+
+#include "planarity.h"
+
+#define IF_DEB(x) {}
+#define IF_VERB(x) {}
+#define IF_DEB_TREE(x) {}
+#define IF_DEB_EDGES(x) {}
+#define IF_CPU(x) {}
+
+
+/* aproto: header embed_graph_protos.h */
+
+
+#ifndef PLANAR_IN_MAGMA
+#endif
+
+
+boolean
+sparseg_adjl_is_planar (
+ t_ver_sparse_rep *V,
+ int n,
+ t_adjl_sparse_rep *A, /* input sparse graph */
+ int *nbr_c, /* size of the graph, #components
+ */
+ t_dlcl ***dfs_tree, /* a sparse graph rep. for the dfs tree
+ -- vertices are as DFIs
+ -- and children are ordered wrt
+ lowpoint value
+ */
+ t_dlcl ***back_edges, /* for each vertex v, a dlcl
+ of the back edges [v, x] incident to v
+ where x is a DESCENDANT of v
+ (vertices are given as DFIs)
+ */
+ t_dlcl ***mult_edges, /* for each vertex v, a dlcl
+ of the back edges [v, x] incident to v
+ where x is a DESCENDANT of v
+ (vertices are given as DFIs)
+ */
+ t_ver_edge **embed_graph, /* output graph embedding -- more on that
+ later
+ */
+ int *edge_pos, /* pos. in embed_graph for addition
+ of the next edge */
+ int *vr,
+ int *wr /* if graph is non planar, return
+ the unembedded edge
+ (where wr descendant of vr)
+ */
+)
+ /*
+ as the name indicates: is the graph planar?
+ */
+{
+ int v;
+
+ IF_CPU(
+ float sttime; float time_to_now;
+ )
+
+
+ *embed_graph =
+ embedg_planar_alg_init(V, n, A, nbr_c,
+ edge_pos, dfs_tree, back_edges, mult_edges);
+ IF_CPU(
+ sttime = time_current_user();
+ )
+
+ for (v = n - 1; v >= 0; v--)
+ /*
+ visit all vertices in descending DFI order
+ */
+ {
+ t_dlcl *be_l, *te_l, *p;
+
+ IF_DEB(
+ fprintf(stdout, "top level, vertex %d\n", v);
+ )
+
+ /*
+ find all the back edges [w, v] where w is a descendant of v
+ and perform a walkup from w to v
+ (ie determine which bicomps are pertinent)
+ */
+ be_l = (*back_edges)[v];
+ p = be_l;
+
+ if (!embedg_dlcl_is_empty(p))
+ {
+ int w;
+
+ w = p->info;
+ IF_DEB(
+ fprintf(stdout, "top level, before walkup for w %d\n", w);
+ )
+ embedg_walkup(*embed_graph, n, v, p);
+
+ p = embedg_dlcl_list_next(p);
+ while (p != be_l)
+ {
+ w = p->info;
+ IF_DEB(
+ fprintf(stdout, "top level, before walkup for w %d\n", w);
+ )
+ embedg_walkup(*embed_graph, n, v, p);
+
+ p = embedg_dlcl_list_next(p);
+ }
+ }
+
+ /*
+ perform a walkdown for each tree edge [v, c], c a descendant of v
+ (ie attempt to embed all back edges on the pertinent bicomps)
+ */
+ te_l = (*dfs_tree)[v];
+ p = te_l;
+
+ if (!embedg_dlcl_is_empty(p))
+ {
+ int c, vv;
+ t_merge_queue q;
+
+ c = p->info;
+ vv = c + n;
+ IF_DEB(
+ fprintf(stdout, "top level, before walkdown for c %d\n", c);
+ )
+ q = embedg_walkdown(*embed_graph, n, edge_pos, vv);
+
+ IF_DEB(
+ fprintf(stdout, "top level, after walkdown for c %d, state of edges'sign\n", c);
+ embedg_VES_print_flipped_edges(*embed_graph,
+ n, *edge_pos);
+ )
+
+ /*
+ temp only
+ */
+ embedg_merge_queue_delete(q);
+ p = embedg_dlcl_list_next(p);
+ while (p != te_l)
+ {
+ c = p->info;
+ vv = c + n;
+ IF_DEB(
+ fprintf(stdout, "top level, before walkdown for c %d\n", c);
+ )
+ q = embedg_walkdown(*embed_graph, n, edge_pos, vv);
+
+ IF_DEB(
+ fprintf(stdout, "top level, after walkdown for c %d, state of edges'sign\n", c);
+ embedg_VES_print_flipped_edges(*embed_graph,
+ n, *edge_pos);
+ )
+
+ /*
+ temp only
+ */
+ embedg_merge_queue_delete(q);
+
+ p = embedg_dlcl_list_next(p);
+ }
+ }
+
+
+ /*
+ check that each back edge [w, v], w a descendant of v,
+ has been embedded
+ */
+ be_l = (*back_edges)[v];
+ p = be_l;
+
+ if (!embedg_dlcl_is_empty(p))
+ {
+ int w;
+
+ w = p->info;
+ IF_DEB(
+ fprintf(stdout, "top level, before checking embedding for w %d\n",
+ w);
+ )
+ if ((*embed_graph)[w].adjacent_to == v)
+ /*
+ this edge hasn't been embedded:
+ the graph is non-planar
+ */
+ {
+ /*
+ before returning we really want to ensure that
+ the vertices' adjacency lists are consistent
+ */
+ ASSERT(embedg_VES_are_adj_lists_consistent(
+ *embed_graph, n));
+
+ IF_CPU(
+ fprintf(stdout, "CPU for tester only %f\n",
+ (time_current_user() - sttime));
+ )
+
+ *vr = v;
+ *wr = w;
+ return FALSE;
+ }
+
+ p = embedg_dlcl_list_next(p);
+ while (p != be_l)
+ {
+ w = p->info;
+ IF_DEB(
+ fprintf(stdout, "top level, before checking embedding for w %d\n",
+ w);
+ )
+ if ((*embed_graph)[w].adjacent_to == v)
+ {
+ /*
+ before returning we really want to ensure that
+ the vertices' adjacency lists are consistent
+ */
+ ASSERT(embedg_VES_are_adj_lists_consistent(
+ *embed_graph, n));
+
+ IF_CPU(
+ fprintf(stdout, "CPU for tester only %f\n",
+ (time_current_user() - sttime));
+ )
+
+ *vr = v;
+ *wr = w;
+ return FALSE;
+ }
+
+ p = embedg_dlcl_list_next(p);
+ }
+ }
+ }
+ IF_DEB_EDGES(
+ fprintf(stdout, "top level, total number of edges in embedding %d\n",
+ *edge_pos - 2 * n + 1);
+ )
+
+
+ /*
+ before returning we really want to ensure that
+ the vertices' adjacency lists are consistent
+ */
+ ASSERT(embedg_VES_are_adj_lists_consistent(*embed_graph, n));
+
+ IF_CPU(
+ fprintf(stdout, "CPU for tester only %f\n",
+ (time_current_user() - sttime));
+ )
+
+ return TRUE;
+}
+
+
+
+/*
+ * walkup.c
+ */
+
+/*
+ What:
+ *****
+
+ Implementing:
+
+ The walkup routine within the VES structure:
+
+ Walking up from w where [w, v^c] is a (directed)
+ back edge to be embeeding later.
+ Along the way collect all the pertinent bicomps that
+ will need to be merged before embedding the back edges
+ to v^c.
+
+
+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ from
+
+ Simplified O(n) Planarity Algorithms (draft)
+ ************************************
+
+ John Boyer JBoyer@PureEdge.com, jboyer@acm.org
+ Wendy Myrvold wendym@csr.uvic.ca
+
+
+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ authors:
+ ********
+
+ Paulette Lieby (Magma), Brendan McKay (ANU)
+
+ Started October 2001
+*/
+
+
+#include "planarity.h"
+
+#define IF_DEB(x) {}
+#define IF_VERB(x) {}
+
+
+
+/* aproto: header embed_graph_protos.h */
+
+
+#ifndef PLANAR_IN_MAGMA
+#endif
+
+
+void
+embedg_walkup (t_ver_edge *embed_graph, int n, int v, t_dlcl *p)
+ /*
+ walkup from w = p->info to v: [w, v] is a back edge where w is a DFS
+ descendant of v
+ */
+{
+ int w, x, xin, y, yin;
+
+ w = p->info;
+
+ IF_DEB(
+ fprintf(stdout, "walkup from %d to %d, enter\n", w, v);
+ )
+
+ embed_graph[w].adjacent_to = v;
+ /*
+ dirty trick to record some information about the BE [w, v]
+ which will be useful at the time of creation and insertion of
+ this BE: this happens in the walkdown procedure
+
+ note that what I am doing here is safe: [w].in_adjl,
+ [w].twin_in_adjl, [w].mult had no use so far since w is a vertex
+ (and not an edge...)
+ */
+ embed_graph[w].in_adjl = p->in_adjl;
+ embed_graph[w].twin_in_adjl = p->twin_in_adjl;
+ embed_graph[w].mult = p->mult;
+
+ /*
+ set up the traversal contexts for w: one in each direction
+ */
+ x = w;
+ xin = 1;
+ y = w;
+ yin = 0;
+
+ while (x != v)
+ {
+ int vz, z, c;
+
+ IF_DEB(
+ fprintf(stdout, "walkup, x %d and y %d\n", x, y);
+ )
+
+ if (embed_graph[x].visited == v
+ || embed_graph[y].visited == v)
+ {
+ IF_DEB(
+ if (embed_graph[x].visited == v)
+ fprintf(stdout, "walkup, x visited\n");
+ else
+ fprintf(stdout, "walkup, y visited\n");
+ )
+ break;
+ }
+
+ /*
+ set x and y as visited!
+ */
+ embed_graph[x].visited = embed_graph[y].visited = v;
+
+ vz = embedg_VES_is_virtual_vertex(n, x) ? x : NIL;
+ vz = embedg_VES_is_virtual_vertex(n, y) ? y : vz;
+
+ if (vz != NIL)
+ /*
+ that is, x (or y) is a virtual vertex
+ -- in other words, we are set to find the root of the bicomp
+ containing w, or of the bicomp r^c such that w is in the tree
+ rooted by c
+
+ consequently, by definition, vz is PERTINENT
+ */
+ {
+ c = vz - n;
+ z = embed_graph[c].DFS_parent;
+
+ IF_DEB(
+ fprintf(stdout, "walkup, vz is virtual, %d^%d\n",
+ z, c);
+ )
+
+ if (z != v)
+ /*
+ determine if vz externally or internally active
+ */
+ {
+ if (embed_graph[c].lowpoint < v)
+ /*
+ vz is externally active: APPEND to the list
+ of pertinent bicomps
+ */
+ {
+ IF_DEB(
+ fprintf(stdout, "walkup, vz is ext. active\n");
+ )
+
+ embed_graph[z].pertinent_bicomp_list =
+ embedg_dlcl_rec_append(
+ embed_graph[z].pertinent_bicomp_list,
+ embedg_dlcl_rec_new(vz));
+ }
+ else
+ /*
+ vz is internally active: PREPEND to the list
+ of pertinent bicomps
+ */
+ {
+ IF_DEB(
+ fprintf(stdout, "walkup, vz is pertinent\n");
+ )
+
+ embed_graph[z].pertinent_bicomp_list =
+ embedg_dlcl_rec_prepend(
+ embed_graph[z].pertinent_bicomp_list,
+ embedg_dlcl_rec_new(vz));
+ }
+ }
+
+ /*
+ continue the walkup, look if there are any other
+ pertinent bicomps
+ -- here "jump" to the next bicomp "up"
+ */
+ x = z;
+ xin = 1;
+ y = z;
+ yin = 0;
+ }
+ else
+ /*
+ continue the traversal of the bicomp until one finds
+ its (virtual) root
+ */
+ {
+ embedg_VES_get_succ_on_ext_face(embed_graph, n,
+ x, xin, FALSE, 0, &x, &xin);
+ embedg_VES_get_succ_on_ext_face(embed_graph, n,
+ y, yin, FALSE, 0, &y, &yin);
+ }
+ }
+}
+
+/*
+ * walkdown.c
+ */
+
+/*
+ What:
+ *****
+
+ Implementing:
+
+ The walkdown routine within the VES structure:
+
+ walking down a bicomp rooted by a virtual vertex v^c
+ and attempting to embed the back edges.
+ This cannot be done if the walk has to stop due to the
+ presence of externally active vertices on both
+ the clockwise and the anticlockwise side of the bicomp.
+
+
+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ from
+
+ Simplified O(n) Planarity Algorithms (draft)
+ ************************************
+
+ John Boyer JBoyer@PureEdge.com, jboyer@acm.org
+ Wendy Myrvold wendym@csr.uvic.ca
+
+
+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ authors:
+ ********
+
+ Paulette Lieby (Magma), Brendan McKay (ANU)
+
+ Started October 2001
+*/
+
+
+#include "planarity.h"
+
+#define IF_DEB(x) {}
+#define IF_DEB_EMBED(x) {}
+#define IF_DEB_BE(x) {}
+#define IF_DEB_SCE(x) {}
+#define IF_VERB(x) {}
+
+
+
+/* aproto: header embed_graph_protos.h */
+
+
+#ifndef PLANAR_IN_MAGMA
+#endif
+
+
+
+t_merge_queue
+embedg_walkdown (t_ver_edge *embed_graph, int n, int *edge_pos, int vv)
+ /*
+ walkdown from the virtual vertex:
+ embed any back edges incident to vv if any
+ and merge the encountered bicomps while walking down
+ (very informative isn't it? :))
+
+ ... and return the merge queue: will be useful when
+ isolating the Kuratowski subgraphs
+ */
+{
+ t_merge_queue q;
+ int v, c, vvout;
+
+ ASSERT(embedg_VES_is_virtual_vertex(n, vv));
+
+ /*
+ find v and c such that v^c = vv
+ */
+ c = vv - n;
+ v = embed_graph[c].DFS_parent;
+
+ IF_DEB(
+ fprintf(stdout, "walkdown from %d^%d, enter\n", v, c);
+ )
+
+ IF_DEB_EMBED(
+ fprintf(stdout, "walkdown, embedding at start\n");
+ embedg_VES_print_bigcomps(embed_graph, n);
+ )
+
+ /*
+ create an empty merge queue
+ */
+ q = embedg_merge_queue_new(n);
+
+ for (vvout = 0; vvout <= 1; vvout++)
+ /*
+ chose a direction for the walk, but walk in both
+ directions unless a stopping vertex is encountered
+ and other conditions are satisfied (see below)
+ */
+ {
+ int w, win;
+
+ embedg_VES_get_succ_on_ext_face(embed_graph, n, vv, vvout ^ 1,
+ FALSE, 0, &w, &win);
+
+ IF_DEB(
+ fprintf(stdout, "walkdown, successor (outside while loop) from %d^%d:%d is %d:%d\n",
+ embed_graph[vv-n].DFS_parent, vv-n, vvout ^ 1,
+ w, win);
+ )
+
+ while (w != vv)
+ /*
+ is there no danger we walk the whole way back to vv
+ and that all the vertices along the walk are inactive?
+
+ answer: no, because of the short-cut edges.
+
+ Short-cut edges are precisely inserted to remove the inactive
+ vertices from the external face (ie they are "pushed"
+ to the internal face of the bicomp)
+ */
+ {
+ if (embed_graph[w].adjacent_to == v)
+ /*
+ ie there is a (directed) back edge [w, v]
+ (would have been set in the previous walkup routine):
+ embed this edge, but before that, merge all the bicomps
+ previouslsy collected
+ */
+ {
+ IF_DEB(
+ fprintf(stdout, "walkdown, embed BE (%d^%d:%d, %d:%d)\n",
+ embed_graph[vv-n].DFS_parent, vv - n, vvout,
+ w, win);
+ fprintf(stdout, "walkdown, queue before pulling elts\n");
+ embedg_merge_queue_print(q);
+ )
+
+ while (!embedg_merge_queue_empty(q))
+ {
+ int u, uin, vu, vuout;
+
+ embedg_merge_queue_get(&q, &u, &uin, &vu, &vuout);
+
+ IF_DEB(
+ fprintf(stdout, "walkdown, pull from queue (%d:%d, %d^%d:%d)\n",
+ u, uin,
+ embed_graph[vu-n].DFS_parent, vu-n,
+ vuout);
+ )
+
+ embedg_VES_merge_pertinent_bicomps(
+ embed_graph, n,
+ vu, vuout, u, uin);
+ }
+ IF_DEB_BE(
+ fprintf(stdout, "walkdown, before embed BE [%d^%d:%d, %d:%d]\n",
+ embed_graph[vv-n].DFS_parent, vv - n,
+ vvout, w, win);
+ embedg_VES_print_adj_list(
+ embed_graph, n, vv,
+ TRUE);
+ fprintf(stdout, "\n");
+ embedg_VES_print_adj_list(
+ embed_graph, n, vv,
+ FALSE);
+ )
+
+ embedg_VES_embed_edge(embed_graph, n, edge_pos,
+ BE, vv, vvout, w, win);
+
+ IF_DEB_BE(
+ fprintf(stdout, "walkdown, after embed BE [%d^%d:%d, %d:%d]\n",
+ embed_graph[vv-n].DFS_parent, vv - n,
+ vvout, w, win);
+ embedg_VES_print_adj_list(
+ embed_graph, n, vv,
+ TRUE);
+ fprintf(stdout, "\n");
+ embedg_VES_print_adj_list(
+ embed_graph, n, vv,
+ FALSE);
+ )
+ IF_DEB_EMBED(
+ fprintf(stdout, "walkdown, embedding after bicomp merge & back edge embedding\n");
+ embedg_VES_print_bigcomps(embed_graph, n);
+ )
+
+ /*
+ clear the adjacent_to flag
+ */
+ embed_graph[w].adjacent_to = n; /* "invalid" value */
+ }
+
+ if (!embedg_dlcl_is_empty(embed_graph[w].pertinent_bicomp_list))
+ /*
+ each pertinent child bicomp of w
+ (pertinent: contains active (ie more back edges to embed)
+ elts)
+ must be traversed
+ and pushed onto the queue for later bicomp merging
+ */
+ {
+ int vw, vwout, x, xin, y, yin, s, sin;
+
+ IF_DEB(
+ fprintf(stdout, "walkdown, pertinent list for %d\n",
+ w);
+ embedg_dlcl_print(embed_graph[w].pertinent_bicomp_list);
+ )
+
+ /*
+ get the first child in the pertinent list
+ (see how the list is built in embedg_walkup)
+
+ the child will eventually be removed from that list
+ when merging the bicomps, and surely
+ this bicomp (rooted at vw) will be merged (later)
+ because it is active and hence pushed on
+ the merge queue
+ */
+
+ /*
+ we can start by pushing the vertex (w, win) on
+ the merge queue
+ */
+ embedg_merge_queue_append_vertex(&q, embed_graph, n, w, win);
+
+ IF_DEB(
+ fprintf(stdout, "walkdown, push 1rst 2-tuple on queue\n");
+ embedg_merge_queue_print(q);
+ )
+
+ /*
+ get the first child in the pertinent list
+ */
+ vw = (embed_graph[w].pertinent_bicomp_list)->info;
+
+ IF_DEB(
+ fprintf(stdout, "walkdown, get pertinent %d^%d\n",
+ embed_graph[vw - n].DFS_parent, vw - n);
+ )
+
+ /*
+ start two walks starting at vw
+ */
+ embedg_VES_get_succ_active_on_ext_face(embed_graph, n,
+ v , vw, 1,
+ FALSE, 0, &x, &xin);
+ embedg_VES_get_succ_active_on_ext_face(embed_graph, n,
+ v, vw, 0,
+ FALSE, 0, &y, &yin);
+
+ /*
+ because of the trick of inserting short-cut edges
+ at previous stages, neighbours of vw are guaranteed
+ to be active
+
+ (however I'll use the more general
+ embedg_VES_get_succ_active_on_ext_face
+ instead of the restrictive
+ embedg_VES_get_succ_on_ext_face
+ because the walkdown may be used later to isolate
+ Kuratowski minors, in a situation where SCEs could have
+ been removed and thus where the successor on the
+ external face will no longer be guaranteed to be active)
+ (* actually I have decided to remove the SCE at the
+ very last moment hence the above pb
+ does not occur in the present implementation)
+
+
+ it only remains to chose the next vertex where from
+ to continue the walk; the choice is made in that order:
+ - an internally active vertex
+ (incident to v via a backedge but whose lowpoint
+ is NO less than v)
+ - a (externally active) pertinent vertex
+ (incident to v via a backedge but whose lowpoint
+ is less than v: ie which is also externally active)
+ - as a last resort, a non-pertinent externally vertex,
+ which is then a stopping vertex
+ */
+ IF_DEB(
+ fprintf(stdout, "walkdown, x and y: %d, %d\n", x, y);
+ )
+
+ if (embedg_VES_is_ver_int_active(embed_graph, n,
+ v, x))
+ /*
+ x is internally active
+ */
+ {
+ IF_DEB(
+ fprintf(stdout, "walkdown, x is int. active\n");
+ )
+
+ s = x;
+ sin = xin;
+ }
+ else if (embedg_VES_is_ver_int_active(
+ embed_graph, n,
+ v, y))
+ /*
+ y is internally active
+ */
+ {
+ IF_DEB(
+ fprintf(stdout, "walkdown, y is int. active\n");
+ )
+
+ s = y;
+ sin = yin;
+ }
+ else if (embedg_VES_is_ver_pertinent(
+ embed_graph, n,
+ v, x))
+ /*
+ x is pertinent
+ */
+ {
+ IF_DEB(
+ fprintf(stdout, "walkdown, x is pertinent\n");
+ )
+
+ s = x;
+ sin = xin;
+ }
+ else
+ /*
+ tough luck: y may be externally active
+ */
+ {
+ IF_DEB(
+ fprintf(stdout, "walkdown, tough luck\n");
+ )
+
+ s = y;
+ sin = yin;
+ }
+
+ IF_DEB(
+ fprintf(stdout, "walkdown, succ. on pertinent bicomp is %d:%d\n", s, sin);
+ )
+
+ /*
+ set vwout to respect consistency of traversal
+ */
+ vwout = s == x ? 0 : 1;
+
+ /*
+ now that we know vwout we can push (vw, vwout)
+ on the merge queue, thus completing the 4-tuple
+ (w, win, vw, vwout) describing a bicomp merge
+ to occur at a later stage
+ */
+ embedg_merge_queue_append_virtual_vertex(&q, embed_graph, n,
+ vw, vwout);
+
+ IF_DEB(
+ fprintf(stdout, "walkdown, push on queue (%d:%d, %d^%d:%d)\n",
+ w, win, embed_graph[vw-n].DFS_parent, vw - n,
+ vwout);
+ embedg_merge_queue_print(q);
+ )
+
+ /*
+ we continue the walk
+ */
+ w = s;
+ win = sin;
+ }
+ /*
+ at this point, w is either inactive or externally active
+ (w can't be pertinent: its pertinent bicomp list is empty,
+ and the back edge [w, v], if any, has already been embedded)
+ */
+ else if (embedg_VES_is_ver_inactive(embed_graph, n,
+ v, w))
+ /*
+ w is inactive: continue with the walk on the external face
+ and, insert a short cut edge so that w is removed
+ from the external face
+ */
+ {
+ int s, sin;
+
+ IF_DEB(
+ fprintf(stdout, "walkdown, %d has no pertinent bicomps and is inactive\n", w);
+ )
+
+ embedg_VES_get_succ_on_ext_face(embed_graph, n,
+ w, win,
+ FALSE, 0, &s, &sin);
+
+ IF_DEB(
+ fprintf(stdout, "walkdown, successor from %d:%d is %d:%d\n",
+ w, win, s, sin);
+ )
+
+ /*
+ s is the successor of w: we embed a short circuit edge
+ [vv, s] if
+ - the bicomp is externally active (to ensure that
+ at a later stage this new face gets bisected:
+ so that we don't end up with a face of degree 2
+ (parallel edges))
+ - if [s, vv] is not a back edge
+
+ CONSEQUENTLY, adding SCE edges
+ + does not destroy the planarity of the graph
+ + ensures that each face has degree > 2 so that
+ |E| <= 3 * |V| - 6 remains valid at all times
+ + that the space allocated to the edges in embed_graph
+ (via MAXDE(n)) is sufficient
+
+ NOTE:
+ the above still allows to embed a short-cut edge
+ as an edge parallel to a tree edge OR a back edge
+ (which then has been embedded previously
+ so that [w].adjacent has been cleared)
+
+ but again, since the degree of the face will be
+ > 2, that's ok
+
+ recall that c = vv - n
+ */
+ if (embed_graph[c].lowpoint < v
+ /*
+ bicomp rooted at vv is externally active
+ */
+ && embed_graph[s].adjacent_to != v)
+ /*
+ [s, vv] is not a back edge
+ */
+ {
+ IF_DEB_SCE(
+ fprintf(stdout, "walkdown, before embed SCE [%d^%d:%d, %d:%d]\n",
+ embed_graph[vv-n].DFS_parent, vv - n,
+ vvout, s, sin);
+ embedg_VES_print_adj_list(
+ embed_graph, n, vv,
+ TRUE);
+ fprintf(stdout, "\n");
+ embedg_VES_print_adj_list(
+ embed_graph, n, vv,
+ FALSE);
+
+ )
+
+ embedg_VES_embed_edge(embed_graph,
+ n, edge_pos,
+ SCE, vv, vvout, s, sin);
+ /*
+ note also that the addition of short cut edges
+ does not change the fact that the graph is planar
+ (when it is, so we never run into the problem
+ of creating/adding too many edges to embed-graph)
+ */
+ IF_DEB_SCE(
+ fprintf(stdout, "walkdown, after embed SCE [%d^%d:%d, %d:%d]\n",
+ embed_graph[vv-n].DFS_parent, vv - n,
+ vvout, s, sin);
+ embedg_VES_print_adj_list(
+ embed_graph, n, vv,
+ TRUE);
+ fprintf(stdout, "\n");
+ embedg_VES_print_adj_list(
+ embed_graph, n, vv,
+ FALSE);
+
+ )
+ IF_DEB(
+ fprintf(stdout, "walkdown, embed SCE [%d^%d:%d, %d:%d]\n",
+ embed_graph[vv-n].DFS_parent, vv - n,
+ vvout, s, sin);
+ )
+
+ }
+ /*
+ continue the walk
+ */
+ w = s;
+ win = sin;
+ }
+ else
+ /*
+ w is non-pertinent and externally active:
+ it is a stopping vertex:
+ we stop here and see if we can walk in the other direction
+ */
+ {
+ IF_DEB(
+ fprintf(stdout, "walkdown, %d is externally active\n", w);
+ )
+ break;
+ }
+ }
+ if (!embedg_merge_queue_empty(q))
+ /*
+ mumm.... don't understand this one... let's see:
+ the queue constains pertinent bicomps collected during one of
+ the traversal of the external face, so that once
+ a stopping vertex has been encountered and the queue
+ is not empty, this means that we will be unable
+ to embed any remaining back edges:
+
+ it is important to remember that when w is a stopping vertex
+ there is no choice left, since we walk the pertinent
+ bicomp in both directions at once, and always choose
+ the "best" possible vertex
+ (see the choice strategy: (a) internally active, (b) pertinent,
+ (c) the rest)
+ */
+ {
+ IF_DEB(
+ fprintf(stdout, "walkdown, merge queue is not empty\n");
+ )
+ break;
+ }
+ }
+
+ /*
+ and return the merge queue
+ */
+ return q;
+}
+
+
+
+
+/*
+ * merge_queue_misc.c
+ */
+
+/*
+ What:
+ *****
+
+ Implementing:
+
+ The merge queue stores the pertinent bicomps waiting to
+ be merged before a subsequent back edge embedding.
+ See walkdown.c
+
+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ from
+
+ Simplified O(n) Planarity Algorithms (draft)
+ ************************************
+
+ John Boyer JBoyer@PureEdge.com, jboyer@acm.org
+ Wendy Myrvold wendym@csr.uvic.ca
+
+
+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ authors:
+ ********
+
+ Paulette Lieby (Magma), Brendan McKay (ANU)
+
+ Started October 2001
+*/
+
+
+#include "planarity.h"
+
+#define IF_DEB(x) {}
+#define IF_VERB(x) {}
+
+
+
+/* aproto: header embed_graph_protos.h */
+
+
+#ifndef PLANAR_IN_MAGMA
+#endif
+
+t_merge_queue
+embedg_merge_queue_new (int n)
+ /*
+ create a merge queue of 4 * (n-1) elts:
+ we can only have at most n-1 virtual vertices,
+ and for each of those we need to store 4 bits of info
+ */
+{
+ t_merge_queue q;
+
+ q.start = q.end = 0;
+ q.b = (int *) mem_malloc(sizeof(int) * 4 * (n - 1));
+
+ return q;
+}
+
+void
+embedg_merge_queue_delete (t_merge_queue q)
+{
+ mem_free(q.b);
+}
+
+
+boolean
+embedg_merge_queue_empty (t_merge_queue q)
+{
+ return q.start == q.end ? TRUE : FALSE;
+}
+
+void
+embedg_merge_queue_print (t_merge_queue q)
+{
+ int i;
+
+ for (i = q.start; i < q.end; i++)
+ {
+ fprintf(stdout, "%d:%d ", q.b[i], q.b[i+1]);
+ ++i;
+ }
+ fprintf(stdout, "\n");
+}
+
+void
+embedg_merge_queue_append (t_merge_queue *q, t_ver_edge *embed_graph,
+ int n, int v, int vin, int vv, int vvout)
+ /*
+ append the 4-tuple (v, vin, vv, vvout)
+ where v is a vertex and vv is its virtual counterpart
+
+ we don't do much here, most of the work is done
+ when pulling a bicomp/4-tuple from the queue
+ */
+{
+ /*
+ is this really necessary? - YES!!!
+ */
+ ASSERT((*q).end < 4 * (n - 2));
+ ASSERT(embedg_VES_is_vertex(n, v));
+ ASSERT(embedg_VES_is_virtual_vertex(n, vv));
+ ASSERT(embed_graph[vv - n].DFS_parent == v);
+
+ (*q).b[(*q).end++] = v;
+ (*q).b[(*q).end++] = vin;
+ (*q).b[(*q).end++] = vv;
+ (*q).b[(*q).end++] = vvout;
+}
+
+void
+embedg_merge_queue_append_vertex (t_merge_queue *q, t_ver_edge *embed_graph,
+ int n, int v, int vin)
+ /*
+ same as above but were we only append the 2-tuple (v, vin),
+ appending the 2-tuple (vv, vvout) at a later stage
+ (see embedg_merge_queue_append_virtual_vertex)
+ */
+{
+ ASSERT((*q).end < 4 * (n - 2));
+ ASSERT(embedg_VES_is_vertex(n, v));
+
+ (*q).b[(*q).end++] = v;
+ (*q).b[(*q).end++] = vin;
+
+ IF_DEB(
+ fprintf(stdout, "merge_queue_append_vertex, after, end is %d\n",
+ (*q).end);
+ )
+}
+
+void
+embedg_merge_queue_append_virtual_vertex (t_merge_queue *q,
+ t_ver_edge *embed_graph, int n, int vv, int vvout)
+ /*
+ counterpart to embedg_merge_queue_append_vertex:
+ here we append the 2-tuple (vv, vvout), vv = v^c,
+ where the 2-tuple (v, vin) is already in the queue
+ (see embedg_merge_queue_append_vertex)
+ */
+{
+ ASSERT(!embedg_merge_queue_empty(*q));
+ ASSERT(embedg_VES_is_virtual_vertex(n, vv));
+ ASSERT(embed_graph[vv - n].DFS_parent == (*q).b[(*q).end - 2]);
+
+ (*q).b[(*q).end++] = vv;
+ (*q).b[(*q).end++] = vvout;
+
+ IF_DEB(
+ fprintf(stdout, "merge_queue_append_virtual_vertex, after, end is %d\n",
+ (*q).end);
+ )
+}
+
+void
+embedg_merge_queue_get (t_merge_queue *q, int *v, int *vin, int *vv, int *vvout)
+ /*
+ pulling out a 4-tuple from the beginning of the FIFO queue
+ */
+{
+ ASSERT(!embedg_merge_queue_empty((*q)));
+
+ *v = (*q).b[(*q).start++];
+ *vin = (*q).b[(*q).start++];
+ *vv = (*q).b[(*q).start++];
+ *vvout = (*q).b[(*q).start++];
+}
+
+void
+embedg_merge_queue_prune (t_merge_queue *q, int *v,
+ int *vin, int *vv, int *vvout)
+ /*
+ pulling out a 4-tuple from the end of the FIFO queue
+ */
+{
+ ASSERT(!embedg_merge_queue_empty((*q)));
+
+ *vvout = (*q).b[--((*q).end)];
+ *vv = (*q).b[--((*q).end)];
+ *vin = (*q).b[--((*q).end)];
+ *v = (*q).b[--((*q).end)];
+}
+
+/*
+ * vertex_activity.c
+ */
+
+/*
+ What:
+ *****
+
+ Implementing:
+
+ Determining a vertex's activity. This takes place within
+ the VES structure.
+
+
+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ from
+
+ Simplified O(n) Planarity Algorithms (draft)
+ ************************************
+
+ John Boyer JBoyer@PureEdge.com, jboyer@acm.org
+ Wendy Myrvold wendym@csr.uvic.ca
+
+
+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ authors:
+ ********
+
+ Paulette Lieby (Magma), Brendan McKay (ANU)
+
+ Started October 2001
+*/
+
+#include "planarity.h"
+
+#define IF_DEB(x) {}
+#define IF_VERB(x) {}
+
+
+
+/* aproto: header embed_graph_protos.h */
+
+
+#ifndef PLANAR_IN_MAGMA
+#endif
+
+
+
+
+boolean
+embedg_VES_is_ver_pertinent (t_ver_edge *embed_graph, int n, int v, int w)
+ /*
+ is w pertinent (wrt v)
+ - the field adjacent_to = v: means there is a back edge [w, v]
+ - or w has a non empty pertinent_bicomp_list
+ */
+{
+ boolean ans;
+
+ ans = embed_graph[w].adjacent_to == v ? TRUE : FALSE;
+
+ if (ans)
+ return TRUE;
+ else
+ return embedg_dlcl_is_empty(embed_graph[w].pertinent_bicomp_list) ?
+ FALSE : TRUE;
+}
+
+boolean
+embedg_VES_is_ver_ext_active (t_ver_edge *embed_graph, int n, int v, int w)
+ /*
+ is w externally active (wrt v)
+ this is the case when either w's least_ancestor < v
+ or the first member of w's separated_DFS_child_list has lowpoint < v
+ (the vertices in separated_DFS_child_list are ordered by lowpoint)
+
+ why? because w's separated_DFS_child_list may be empty
+ (due to prior bicomp merging say) and so its children are in effect
+ inactive
+ */
+{
+ boolean ans;
+
+ ans = embed_graph[w].least_ancestor < v ? TRUE : FALSE;
+
+ if (ans)
+ return TRUE;
+ else
+ {
+ if (embedg_dlcl_is_empty(embed_graph[w].separated_DFS_child_list))
+ {
+ return FALSE;
+ }
+ else
+ {
+ int c;
+
+ c = (embed_graph[w].separated_DFS_child_list)->info;
+ return embed_graph[c].lowpoint < v ? TRUE : FALSE;
+ }
+ }
+}
+
+
+boolean
+embedg_VES_is_ver_int_active (t_ver_edge *embed_graph, int n, int v, int w)
+ /*
+ is w internally active (wrt v):
+ this happens when w is pertinent but NOT externally active
+ */
+{
+ return embedg_VES_is_ver_pertinent(embed_graph, n, v, w)
+ && !embedg_VES_is_ver_ext_active(embed_graph, n, v, w);
+}
+
+boolean
+embedg_VES_is_ver_inactive (t_ver_edge *embed_graph, int n, int v, int w)
+ /*
+ is w inactive (wrt v), that is w nor pertinent nor externally activ
+ */
+{
+ return !embedg_VES_is_ver_pertinent(embed_graph, n, v, w)
+ && !embedg_VES_is_ver_ext_active(embed_graph, n, v, w);
+}
+
+/*
+ * merge_bicomps.c
+ */
+
+/*
+ What:
+ *****
+
+ Implementing:
+
+ In the VES structure, merging two bicomponents.
+ That is, merging the virtual vertex v^c with the
+ actual vertex v while merging their respective
+ adjacency lists.
+ This must be done in a very specific manner so as to able to
+ determine the subsequent internal/external faces.
+ Also, great care must be taken so that the resulting
+ adj. list for v is consistent (wrt to the direction
+ of traversal).
+
+
+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ from
+
+ Simplified O(n) Planarity Algorithms (draft)
+ ************************************
+
+ John Boyer JBoyer@PureEdge.com, jboyer@acm.org
+ Wendy Myrvold wendym@csr.uvic.ca
+
+
+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ authors:
+ ********
+
+ Paulette Lieby (Magma), Brendan McKay (ANU)
+
+ Started October 2001
+*/
+
+#include "planarity.h"
+
+#define IF_DEB(x) {}
+#define IF_DEB_ADJL(x) {}
+#define IF_VERB(x) {}
+
+
+
+/* aproto: header embed_graph_protos.h */
+
+
+void
+embedg_VES_merge_simple_bicomps (t_ver_edge *embed_graph, int n, int vv,
+ int vvout, int v, int vin)
+ /*
+ merge the bicomp rooted at vv (vv a virtual vertex) with
+ its counterpart v so that the resulting adjacency list for v
+ is consistent and is the union of the adjacency lists for vv and v
+
+ we treat the case that the bicomp may be flipped (vvout == vin)
+ here
+ */
+{
+ int c, edge, twin, root_edge, cur, prev;
+ int vout, vvin, e1, e2, e3, e4, e1out, e3out, e4in;
+
+ /*
+ find c such that [v^c, c] is the root edge of the bicomp
+ rooted at vv = v^c
+ */
+ c = vv - n;
+ ASSERT(embed_graph[c].DFS_parent == v);
+
+ IF_DEB(
+ fprintf(stdout, "merge_simple_bicomp, start: merge\n");
+ embedg_VES_print_virtual_vertex(embed_graph, n, vv);
+ fprintf(stdout, ":%d & ", vvout);
+ embedg_VES_print_vertex(n, v);
+ fprintf(stdout, ":%d\n", vin);
+ )
+
+ IF_DEB_ADJL(
+ fprintf(stdout, "merge_simple_bicomp, adj. list for %d (before)\n", vv);
+ embedg_VES_print_adj_list(embed_graph, n, vv,
+ TRUE);
+ fprintf(stdout, "\n");
+ embedg_VES_print_adj_list(embed_graph, n, vv,
+ FALSE);
+ fprintf(stdout, "\n");
+
+ fprintf(stdout, "merge_simple_bicomp, adj. list for %d (before)\n", v);
+ embedg_VES_print_adj_list(embed_graph, n, v,
+ TRUE);
+ fprintf(stdout, "\n");
+ embedg_VES_print_adj_list(embed_graph, n, v,
+ FALSE);
+ )
+ /*
+ find all edges incident to vv and (re)set all references
+ to incidence to vv to incidence to v
+
+ by the same token, find the root_edge [v^c, c]
+
+ MOREVOVER, when vin == vvout, the bicomp (rooted by v^v = vv)
+ will be flipped:
+ we must invert the links of all the edges incident
+ to vv so that their further union with v's adjacency list
+ results in a consistent adjacency list for v!
+
+ we do everything in one go
+ */
+
+ /*
+ very careful here: a root edge must ALSO be a TE
+ (because the same edge could have been added as a SCE)
+ */
+
+ root_edge = NIL;
+ edge = embed_graph[vv].link[vvout];
+ ASSERT(embedg_VES_is_edge(n, edge));
+ if (embed_graph[edge].neighbour == c
+ && embedg_VES_is_tree_edge(embed_graph, n, edge))
+ {
+ root_edge = edge;
+ }
+
+ if (vin == vvout)
+ /*
+ invert the links
+ */
+ {
+ int in, out;
+
+ in = embed_graph[edge].link[0];
+ out = embed_graph[edge].link[1];
+ embed_graph[edge].link[0] = out;
+ embed_graph[edge].link[1] = in;
+ }
+ /*
+ get the twin and set the neighbour there to v (was vv originally)
+ */
+ twin = embedg_VES_get_twin_edge(embed_graph, n, edge);
+ ASSERT(embed_graph[twin].neighbour == vv);
+ embed_graph[twin].neighbour = v;
+
+ prev = vv;
+ cur = edge;
+ while (edge != vv)
+ {
+ edge =
+ embedg_VES_get_next_in_dlcl(embed_graph, n,
+ cur, prev);
+
+ if (embedg_VES_is_edge(n, edge))
+ /*
+ get the twin again (and invert the links if need be)
+ */
+ {
+ if (embed_graph[edge].neighbour == c
+ && embedg_VES_is_tree_edge(embed_graph, n, edge))
+ {
+ root_edge = edge;
+ }
+
+ if (vin == vvout)
+ {
+ int in, out;
+
+ in = embed_graph[edge].link[0];
+ out = embed_graph[edge].link[1];
+ embed_graph[edge].link[0] = out;
+ embed_graph[edge].link[1] = in;
+ }
+
+ twin =
+ embedg_VES_get_twin_edge(embed_graph, n, edge);
+ ASSERT(embed_graph[twin].neighbour == vv);
+ embed_graph[twin].neighbour = v;
+
+ prev = cur;
+ cur = edge;
+ }
+ else
+ {
+ ASSERT(edge == vv);
+ /*
+ only one vertex in the whole circular list
+ */
+ }
+ }
+ ASSERT(root_edge != NIL);
+
+ /*
+ and now union the adjacency lists of v and vv:
+
+ let e1 be the edge record used to enter v
+ e2 exit v
+ e3 enter vv
+ e4 exit vv :
+
+ e1 -> v -> e2
+ e3 -> vv -> e4
+
+ the union of the list is done in such a way that
+ - e1 and e4 are consecutive in v's adjacency list:
+ they are now in the internal face
+ - e3 is now the edge record used to enter v:
+ it is on the external face (along with e2) :
+
+ e1 -> e4
+ e3 -> v -> e2
+
+ (note that this does not assume that e1 & e2 are distinct
+ or that e3 & e4 are distinct)
+ */
+ /*
+ I must not forget the case where v is a lone vertex:
+ this is the case where v has no DFS ancestor, ie when
+ v is the root of a tree in the DFS forest
+ */
+
+ e1 = embed_graph[v].link[vin];
+ vout = 1 ^ vin;
+ e2 = embed_graph[v].link[vout];
+
+ if (e1 != v)
+ {
+ ASSERT(e2 != v);
+ ASSERT(embedg_VES_is_edge(n, e1));
+ ASSERT(embedg_VES_is_edge(n, e2));
+ }
+
+ e4 = embed_graph[vv].link[vvout];
+ ASSERT(embedg_VES_is_edge(n, e4));
+
+ vvin = 1 ^ vvout;
+ e3 = embed_graph[vv].link[vvin];
+ ASSERT(embedg_VES_is_edge(n, e3));
+
+ /*
+ must take care of the adjacency list's consistency of traversal
+ (will be important only when recovering the embedding)
+ */
+ if (e1 == e2)
+ {
+ ASSERT(embed_graph[e1].link[0] == embed_graph[e1].link[1]);
+ if (vin == vvout)
+ /*
+ the bicomp will be flipped:
+ must take 1 ^ vvout - difficult to explain -- later...
+ */
+ {
+ e1out = 1 ^ vvout;
+ }
+ else
+ {
+ e1out = vvout;
+ }
+ }
+ else
+ {
+ e1out = embed_graph[e1].link[0] == v ? 0 : 1;
+ }
+ if (e3 == e4)
+ {
+ ASSERT(embed_graph[e3].link[0] == embed_graph[e3].link[1]);
+ e3out = 1 ^ vin;
+ e4in = vin;
+ }
+ else
+ {
+ e4in = embed_graph[e4].link[0] == vv ? 0 : 1;
+ e3out = embed_graph[e3].link[0] == vv ? 0 : 1;
+ }
+
+ IF_DEB(
+ fprintf(stdout, "merge_simple_bicomp, before union of lists, e1\n");
+ embedg_VES_print_edge(embed_graph, n, e1);
+ fprintf(stdout, "merge_simple_bicomp, e3\n");
+ embedg_VES_print_edge(embed_graph, n, e3);
+ fprintf(stdout, "merge_simple_bicomp, e4\n");
+ embedg_VES_print_edge(embed_graph, n, e4);
+ )
+
+ /*
+ make e1 and e4 consecutive in the adjacency list
+ */
+ embed_graph[e1].link[e1out] = e4;
+ embed_graph[e4].link[e4in] = e1;
+ embed_graph[e3].link[e3out] = v;
+ embed_graph[v].link[vin] = e3;
+
+ IF_DEB(
+ fprintf(stdout, "merge_simple_bicomp, after union of lists, e1\n");
+ embedg_VES_print_edge(embed_graph, n, e1);
+ fprintf(stdout, "merge_simple_bicomp, e3\n");
+ embedg_VES_print_edge(embed_graph, n, e3);
+ fprintf(stdout, "merge_simple_bicomp, e4\n");
+ embedg_VES_print_edge(embed_graph, n, e4);
+ )
+
+ /*
+ also, want to "disable" vv links, meaning then that
+ vv is no longer a root of a bicomp
+ */
+ embed_graph[vv].link[0] = embed_graph[vv].link[1] = vv;
+
+ IF_DEB_ADJL(
+ fprintf(stdout, "merge_simple_bicomp, adj. list for %d (after)\n", vv);
+ embedg_VES_print_adj_list(embed_graph, n, vv,
+ TRUE);
+ fprintf(stdout, "\n");
+ embedg_VES_print_adj_list(embed_graph, n, vv,
+ FALSE);
+ fprintf(stdout, "\n");
+
+ fprintf(stdout, "merge_simple_bicomp, adj. list for %d (after)\n", v);
+ embedg_VES_print_adj_list(embed_graph, n, v,
+ TRUE);
+ fprintf(stdout, "\n");
+ embedg_VES_print_adj_list(embed_graph, n, v,
+ FALSE);
+ )
+
+ ASSERT(embedg_VES_is_adj_list_consistent(embed_graph, n, v));
+
+ /*
+ finally, give an orientation to the (formerly) root edge [vv, c]
+ to keep traversal consistent (when recovering embedding)
+ */
+ if (vin == vvout)
+ /*
+ flip: set the sign of the root edge to clockwise
+
+ note: a bicomp is merged only once, so there is no need to
+ "flip" the root_edge's sign: it is set once at initialisation
+ and then changed here if need be.
+ */
+ {
+ embed_graph[root_edge].sign = CLOCKW;
+
+ IF_VERB(
+ fprintf(stdout, "merge_simple_bicomp, flip for %d, sign is now %d for %d of type %d\n",
+ c, embed_graph[root_edge].sign, root_edge, embed_graph[root_edge].type);
+ embedg_VES_print_edge(embed_graph, n, root_edge);
+ )
+ }
+}
+
+
+
+void
+embedg_VES_merge_pertinent_bicomps (t_ver_edge *embed_graph, int n,
+ int vv, int vvout, int v, int vin)
+ /*
+ the bicomps to be merged are pertinent: on top (and before)
+ performing a simple merge, there are several things to do
+ related to the merging to pertinent bicomps
+ */
+{
+ /*
+ a note of caution:
+ it is (very) likely that after a bicomp merge the resulting
+ bicomp is not biconnected (and hence traversal of the external face
+ of the bicomp via embedg_VES_get_succ_on_ext_face is non-sensical)
+
+ remembering that a PERTINENT bicomp merge is ALWAYS followed
+ by a back edge embedding we see that the end result is then a bicomp
+ where again traversal of the external face
+ via embedg_VES_get_succ_on_ext_face will make sense
+ */
+ t_dlcl *pertinent_list, *head, *rep_in_parent_list, *parent_list;
+ int c;
+
+ /*
+ find c such that [v^c, c] is the root edge of the bicomp
+ rooted at vv = v^c
+ */
+ c = vv - n;
+ ASSERT(embed_graph[c].DFS_parent == v);
+
+ /*
+ two things to do first:
+ - remove vv from head of pertinent_bicomp_list of v
+ - remove c from separated_DFS_child_list of v
+
+ one may ask the point of this since the separated_DFS_child_list
+ seems to mirror pertinent_bicomp_list: but this is not exactly so:
+ + pertinent_bicomp_list is ordered according to the activity
+ of the (virtual) vertices
+ + separated_DFS_child_list is ordered according to the vertices'
+ lowpoint values
+ in effect, it could (almost?*) be said that these two lists
+ are in reverse order (the *almost bit would warrant some thinking here)
+ */
+
+ /*
+ remove vv from head of pertinent_bicomp_list of v
+ */
+ pertinent_list = head = embed_graph[v].pertinent_bicomp_list;
+ ASSERT(!embedg_dlcl_is_empty(pertinent_list));
+ ASSERT(head->info == vv);
+
+ IF_DEB(
+ fprintf(stdout, "merge_pertinent_bicomp, start: merge\n");
+ embedg_VES_print_virtual_vertex(embed_graph, n, vv);
+ fprintf(stdout, ":%d & ", vvout);
+ embedg_VES_print_vertex(n, v);
+ fprintf(stdout, ":%d\n", vin);
+ )
+
+ IF_DEB(
+ fprintf(stdout, "merge_pertinent_bicomp, pertinent bicomp_list of %d (before)\n", v);
+ embedg_dlcl_print(embed_graph[v].pertinent_bicomp_list);
+ )
+
+
+ embed_graph[v].pertinent_bicomp_list =
+ embedg_dlcl_delete_first(pertinent_list);
+
+ IF_DEB(
+ fprintf(stdout, "merge_pertinent_bicomp, pertinent bicomp_list of %d (after)\n", v);
+ embedg_dlcl_print(embed_graph[v].pertinent_bicomp_list);
+ )
+
+ /*
+ vv = v^c: remove c from separated_DFS_child_list of v
+ */
+ rep_in_parent_list = embed_graph[c].rep_in_parent_list;
+ ASSERT(!embedg_dlcl_is_empty(rep_in_parent_list));
+
+ parent_list = embed_graph[v].separated_DFS_child_list;
+ ASSERT(!embedg_dlcl_is_empty(parent_list));
+ embed_graph[v].separated_DFS_child_list =
+ embedg_dlcl_delete_rec(parent_list, rep_in_parent_list);
+
+ /*
+ that's it, it remains to merge, ie. union the adjacency list,
+ and flipping the bicomp if necessary
+ */
+ embedg_VES_merge_simple_bicomps(embed_graph, n,
+ vv, vvout, v, vin);
+}
+
+
+
+
+
+
+/*
+ * embed_edge.c
+ */
+
+/*
+ What:
+ *****
+
+ Implementing:
+
+ Embedding an edge so that it lies on the external face of a bicomp.
+ We work here with the VES structure.
+
+
+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ from
+
+ Simplified O(n) Planarity Algorithms (draft)
+ ************************************
+
+ John Boyer JBoyer@PureEdge.com, jboyer@acm.org
+ Wendy Myrvold wendym@csr.uvic.ca
+
+
+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ authors:
+ ********
+
+ Paulette Lieby (Magma), Brendan McKay (ANU)
+
+ Started October 2001
+*/
+
+
+#include "planarity.h"
+
+#define IF_DEB(x) {}
+#define IF_DEB_EMBED(x) {}
+#define IF_VERB(x) {}
+
+
+/* aproto: header embed_graph_protos.h */
+
+
+void
+embedg_VES_embed_edge (t_ver_edge *embed_graph, int n, int *edge_pos,
+ int edge_type, int vv, int vvout, int w, int win)
+ /*
+ embed the edge (vv, w) (vv a virtual vertex, w a vertex) between
+ vv and the edge vvout
+ and the edge win and w
+
+ so that after the embedding, one exits vv via (vv, w) and
+ enters w via the twin (w, vv)
+ */
+{
+ int temp, tempin, tempout;
+
+ ASSERT(edge_type == BE || edge_type == SCE);
+ ASSERT(embedg_VES_is_virtual_vertex(n, vv));
+ ASSERT(embedg_VES_is_vertex(n, w));
+
+ IF_DEB(
+ fprintf(stdout, "embed_edge, (%d:%d)\n", vv, w);
+ )
+
+ /*
+ first, set the edge [vv, w] with the appropriate info
+
+ when [vv, w] is a back edge there is some more work to do
+ (see the walkup procedure for the extra information we need
+ to copy here
+ */
+ (*edge_pos)++;
+ ASSERT(*edge_pos < 2*n + 2 * MAXE(n));
+ embed_graph[*edge_pos].neighbour = w;
+ embed_graph[*edge_pos].type = edge_type;
+ embed_graph[*edge_pos].sign = CCLOCKW;
+ if (edge_type == BE)
+ {
+ ASSERT(embed_graph[w].adjacent_to ==
+ embed_graph[vv - n].DFS_parent);
+
+ /*
+ PLUS: originally when the back edge [w, vv] was
+ created (in the dfs preprocessing stage), it carried in
+ .in_adjl the index of this directed edge in the
+ adjacency list
+
+ but now, note that we are actually inserting the
+ directed edge [vv, w] in vv's adjacency list,
+ meaning that in_adjl and twin_in_adjl
+ must be exchanged!
+ */
+ embed_graph[*edge_pos].in_adjl = embed_graph[w].twin_in_adjl;
+ embed_graph[*edge_pos].twin_in_adjl = embed_graph[w].in_adjl;
+
+ ASSERT(embed_graph[w].mult % 2 == 0);
+ /*
+ the original graph is always undirected:
+ we store its number of undirected edges
+ */
+ embed_graph[*edge_pos].mult = embed_graph[w].mult / 2;
+ }
+
+ /*
+ insert this edge between vertex record for vv
+ and edge record vv.link[vvout]
+ */
+ temp = embed_graph[vv].link[vvout];
+
+ if (embed_graph[temp].link[0] == embed_graph[temp].link[1])
+ /*
+ this needs special treatment to ensure consistency of
+ orientation
+ */
+ {
+ ASSERT(embed_graph[temp].link[0] == vv);
+ tempin = 1 ^ vvout;
+ }
+ else
+ {
+ tempin = embed_graph[temp].link[0] == vv ? 0 : 1;
+ }
+
+ IF_DEB(
+ fprintf(stdout, "embed_edge, edge out of vv\n");
+ embedg_VES_print_edge(embed_graph, n, temp);
+ )
+
+ embed_graph[vv].link[vvout] = *edge_pos;
+ embed_graph[temp].link[tempin] = *edge_pos;
+ /*
+ the links for *edge_pos must also be "consistent"
+ */
+ embed_graph[*edge_pos].link[vvout] = temp;
+ embed_graph[*edge_pos].link[vvout ^ 1] = vv;
+
+ /*
+ now create/set the twin edge, the directed edge [w, vv]
+ */
+ (*edge_pos)++;
+ ASSERT(*edge_pos < 2*n + 2 * MAXE(n));
+ embed_graph[*edge_pos].neighbour = vv;
+ embed_graph[*edge_pos].type = edge_type;
+ embed_graph[*edge_pos].sign = CCLOCKW;
+ if (edge_type == BE)
+ {
+ embed_graph[*edge_pos].in_adjl = embed_graph[w].in_adjl;
+ embed_graph[*edge_pos].twin_in_adjl = embed_graph[w].twin_in_adjl;
+ embed_graph[*edge_pos].mult = embed_graph[w].mult / 2;
+ }
+
+ /*
+ and insert the twin edge between edge record w.link[win]
+ and vertex record for w
+ */
+ temp = embed_graph[w].link[win];
+
+ if (embed_graph[temp].link[0] == embed_graph[temp].link[1])
+ /*
+ again, special treatment to ensure consistency of orientation
+ */
+ {
+ ASSERT(embed_graph[temp].link[0] == w);
+ tempout = 1 ^ win;
+ }
+ else
+ {
+ tempout = embed_graph[temp].link[0] == w ? 0 : 1;
+ }
+
+ IF_DEB(
+ fprintf(stdout, "embed_edge, edge in of w\n");
+ embedg_VES_print_edge(embed_graph, n, temp);
+ )
+
+ embed_graph[w].link[win] = *edge_pos;
+ embed_graph[temp].link[tempout] = *edge_pos;
+ /*
+ and consistent orientation
+ */
+ embed_graph[*edge_pos].link[win] = temp;
+ embed_graph[*edge_pos].link[win ^ 1] = w;
+}
+
+
+
+void
+embedg_VES_add_edge (t_ver_edge *embed_graph, int n, int *edge_pos,
+ int v, int w, boolean MARK, int mark)
+ /*
+ add the edge (v, w): this is DIFFERENT from
+ embedg_VES_embed_edge in the sense
+ that the present function will only be used
+ when building the Kuratowski homeomorphs:
+
+ that is, we are in a situation where the graph is NON planar
+
+ consequently it doesn't matter much where in the adjacency
+ lists of v & w the edge is added:
+ let's say that we always add it at the beginning
+
+ for our sanity's sake, we'll ensure that the resulting
+ adjacency lists remain consistent!
+
+ and we add the edge as a BE!
+ PLUS we mark it with mark in MARK true
+ */
+{
+ int temp;
+
+ ASSERT(embedg_VES_is_vertex(n, v) ||
+ embedg_VES_is_virtual_vertex(n, v));
+ ASSERT(embedg_VES_is_vertex(n, w) ||
+ embedg_VES_is_virtual_vertex(n, w));
+
+ IF_DEB(
+ fprintf(stdout, "add_edge, (%d:%d)\n", v, w);
+ )
+
+ /*
+ not sure this is the best place to do this: mark the endpoints
+ */
+ if (MARK)
+ {
+ embed_graph[v].visited = mark;
+ embed_graph[w].visited = mark;
+ }
+
+ /*
+ first, set the edge [v, w] with the appropriate info
+ */
+ (*edge_pos)++;
+ ASSERT(*edge_pos < 2*n + 2 * MAXE(n));
+ embed_graph[*edge_pos].neighbour = w;
+ embed_graph[*edge_pos].type = BE;
+ /*
+ the edge's orientation will be the same as the vertex
+ */
+ embed_graph[*edge_pos].sign = embed_graph[v].sign;
+ /*
+ and mark the edge
+ */
+ if (MARK)
+ {
+ embed_graph[*edge_pos].visited = mark;
+ }
+
+ /*
+ insert this edge between vertex record for v
+ and edge record v.link[1]
+ */
+ temp = embed_graph[v].link[1];
+
+ IF_DEB(
+ fprintf(stdout, "add_edge, edge out of v\n");
+ embedg_VES_print_edge(embed_graph, n, temp);
+ )
+
+ embed_graph[v].link[1] = *edge_pos;
+ embed_graph[temp].link[0] = *edge_pos;
+ /*
+ the links for *edge_pos must also be "consistent"
+ */
+ embed_graph[*edge_pos].link[1] = temp;
+ embed_graph[*edge_pos].link[0] = v;
+
+ /*
+ now create/set the twin edge, the directed edge [w, v]
+ */
+ (*edge_pos)++;
+ ASSERT(*edge_pos < 2*n + 2 * MAXE(n));
+ embed_graph[*edge_pos].neighbour = v;
+ embed_graph[*edge_pos].type = BE;
+ embed_graph[*edge_pos].sign = embed_graph[w].sign;
+ if (MARK)
+ {
+ embed_graph[*edge_pos].visited = mark;
+ }
+
+ /*
+ insert this edge between vertex record for w
+ and edge record w.link[1]
+ */
+ temp = embed_graph[w].link[1];
+
+ IF_DEB(
+ fprintf(stdout, "add_edge, edge out of w\n");
+ embedg_VES_print_edge(embed_graph, n, temp);
+ )
+
+ embed_graph[w].link[1] = *edge_pos;
+ embed_graph[temp].link[0] = *edge_pos;
+ /*
+ and consistent orientation
+ */
+ embed_graph[*edge_pos].link[1] = temp;
+ embed_graph[*edge_pos].link[0] = w;
+}
+
+
+/*
+ * recover.c
+ */
+
+/*
+ What:
+ *****
+
+ Implementing:
+
+ From the VES data structure recover either the embedding ot
+ the obstruction into the
+
+ t_sparseg_ver_struct,
+ t_sparseg_adjl_struct,
+ t_sparseg_embed_struct
+
+ data types.
+
+
+ (This is no even quite true: for some obscure reason
+ I recover the obstruction as a dlcl[] structure to be
+ converted later.
+ The obvious reason being that it is easier to check as such.
+ Maybe I leave it as it is...)
+
+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ from
+
+ Simplified O(n) Planarity Algorithms (draft)
+ ************************************
+
+ John Boyer JBoyer@PureEdge.com, jboyer@acm.org
+ Wendy Myrvold wendym@csr.uvic.ca
+
+
+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ authors:
+ ********
+
+ Paulette Lieby (Magma), Brendan McKay (ANU)
+
+ Started October 2001
+*/
+
+
+#include "planarity.h"
+
+#define IF_DEB(x) {}
+#define IF_DEB_EMBED_MULT(x) {}
+#define IF_DEB_EMBED_LOOPS(x) {}
+#define IF_DEB_EMBED(x) {}
+#define IF_DEB_CHECK_EMBED(x) {}
+#define IF_DEB_FACES(x) {}
+#define IF_VERB(x) {}
+#define IF_DEB_SCE(x) {}
+#define IF_DEB_OBS(x) {}
+#define IF_DEB_CHECK_OBS(x) {}
+#define IF_CPU(x) {}
+
+
+
+/* aproto: header embed_graph_protos.h */
+
+/* aproto: beginstatic -- don't touch this!! */
+static void embedg_recover_embedding_embed_mult
+ (t_dlcl **, t_embed_sparse_rep *, int, int, int, int, int *, boolean *, int *);
+static void embedg_recover_embedding_embed_loops
+ (t_dlcl **, t_embed_sparse_rep *, int, int, int *, boolean *);
+static t_dlcl **embedg_get_reduced_obs (t_dlcl **, int);
+static boolean embedg_is_red_obs_K33 (t_dlcl **, int);
+static boolean embedg_is_red_obs_K5 (t_dlcl **, int);
+/* aproto: endstatic -- don't touch this!! */
+
+
+#ifndef PLANAR_IN_MAGMA
+#endif
+
+void
+embedg_recover_embedding (
+ t_ver_sparse_rep *V,
+ t_adjl_sparse_rep *A, /* input (original sparse graph) */
+ t_ver_edge *embed_graph,
+ int n,
+ int nbr_e,
+ t_dlcl **mult_edges,
+ t_ver_sparse_rep **vertices,
+ t_embed_sparse_rep **embedding
+)
+ /*
+ recover the embedding
+ to prepare for the final Magma type for sparse & embedded graph
+
+ we assume that all vertices/edges have been given their
+ orientation
+
+ at this stage we also embed the multiple edges and loops
+ which were set aside in mult_edges by
+ sparseg_adjl_dfs_preprocessing:
+
+ as it turns out the last bit is pretty hairy!
+ */
+{
+ /*
+ the idea is to return an array of vertices and an array
+ representing the embedding
+ (careful: need to weedout the SCE)
+
+ vertices: (*vertices)[i].first_edge contains index
+ to first edge in embedding
+
+ embedding: a doubly linked circular list of edges,
+ for each record/edge e = (*embedding)[i]:
+ e.in_adjl: index in A of e
+ e.next: next edge in CLOCKW
+ (as an index in the embedding)
+ e.prev: previous edge in CLOCKW
+ (as an index in embedding)
+ e.inv: inverse edge (as an index in embedding)
+ e.mark: a mark for this edge
+
+ let's say that this new array is a slimmed down version of embed_graph
+
+ one issue to address:
+ - for edge e, find its index in A: this should be found
+ in either the embed_graph[v] record of the mult_edges[v] record
+ */
+ int index_embed, v, mult, w, v_w_in_embed, new_first_edge;
+ boolean set_next;
+
+ IF_DEB(
+ fprintf(stdout, "in recover emb.\n");
+ sparseg_dlcl_print(mult_edges, n);
+ );
+
+ *vertices = (t_ver_sparse_rep *)
+ mem_malloc(sizeof(t_ver_sparse_rep) * n);
+ *embedding = (t_embed_sparse_rep *)
+ mem_malloc(sizeof(t_embed_sparse_rep) * 2 * nbr_e);
+
+ index_embed = 0;
+ set_next = TRUE;
+ for (v = 0; v < n; v++)
+ {
+ int v_l, orient, in, out, e, cur_e, next_e;
+
+ /*
+ we take v's label
+ */
+ v_l = embed_graph[v].label;
+
+ /*
+ first let's deal with the isolated vertex case: those
+ that refer to self
+ */
+ if (embed_graph[v].link[0] == v)
+ {
+ int temp_index_embed;
+
+ ASSERT(embed_graph[v].link[1] == v);
+
+ /*
+ there may be [v, v] loops for this vertex, must check this
+ */
+ temp_index_embed = index_embed - 1;
+ /*
+ temp_index_embed is pre-increased below
+ */
+ embedg_recover_embedding_embed_loops(mult_edges, *embedding,
+ nbr_e, v,
+ &temp_index_embed,
+ &set_next);
+
+ if (temp_index_embed > index_embed - 1)
+ /*
+ must fix beginning and end of adjacency list:
+ */
+ {
+ (*vertices)[v_l].first_edge = index_embed;
+ (*embedding)[temp_index_embed].next =
+ (*vertices)[v_l].first_edge;
+ (*embedding)[(*vertices)[v_l].first_edge].prev =
+ temp_index_embed;
+
+ index_embed = temp_index_embed;
+ index_embed += 1;
+ }
+ else
+ {
+ (*vertices)[v_l].first_edge = NIL;
+ }
+ continue;
+ }
+
+ /*
+ get v's orientation, and from this decide the way in which
+ v's adjacency list will be traversed
+ (recall that the list is supposed to be consistent, so no bad
+ surprises)
+ */
+ orient = embed_graph[v].sign;
+ in = orient == CCLOCKW ? 0 : 1;
+ out = 1 ^ in;
+
+ e = embed_graph[v].link[out];
+ while (embedg_VES_is_short_cut_edge(embed_graph, n, e))
+ {
+ e = embed_graph[e].link[out];
+ }
+ ASSERT(embedg_VES_is_edge(n, e)
+ && !embedg_VES_is_short_cut_edge(embed_graph, n, e));
+ /*
+ strictly speaking there should be no SCEs left at this stage...
+
+ if there are SCEs in v's list, it must be the case that
+ the list also contains tree or back edges...
+ */
+
+ (*vertices)[v_l].first_edge = index_embed;
+
+ IF_DEB_EMBED(
+ fprintf(stdout, "recov. embed. DFI %d vertex %d at %d (edges) and %d (embedding)\n",
+ v, v_l, index_e, (*vertices)[v_l].first_edge);
+ )
+
+ cur_e = e;
+ while (TRUE)
+ {
+ next_e = embed_graph[cur_e].link[out];
+ while (embedg_VES_is_short_cut_edge(embed_graph, n, next_e))
+ {
+ next_e = embed_graph[next_e].link[out];
+ }
+ ASSERT(!embedg_VES_is_short_cut_edge(embed_graph, n, next_e));
+
+ if (next_e == v)
+ /*
+ end of adjacency list
+ */
+ {
+ break;
+ }
+
+ ASSERT(embedg_VES_is_edge(n, next_e));
+
+ (*embedding)[index_embed].in_adjl = embed_graph[cur_e].in_adjl;
+ (*embedding)[index_embed].next = index_embed + 1; /* next in adj.
+ list */
+ (*embedding)[index_embed].mark = NIL; /* mark */
+
+ /*
+ cur_e's twin is trickier:
+ we'll use twin's label field to store cur_e's index in
+ the embedding
+
+ if cur_e's label != NIL this means that cur_e's twin
+ is already stored in edges/embedding and consequently
+ that cur_e.label = index of its twin (in the embedding)
+
+ note that it is safe to do so since an edge's label
+ has no meaning
+ */
+ if (embed_graph[cur_e].label != NIL)
+ {
+ (*embedding)[index_embed].inv = embed_graph[cur_e].label;
+
+ /*
+ but fix the twin by the same token
+ */
+ (*embedding)[embed_graph[cur_e].label].inv = index_embed;
+ ASSERT((*embedding)[embed_graph[cur_e].label].in_adjl ==
+ embed_graph[cur_e].twin_in_adjl);
+ }
+ else
+ /*
+ we store cur_e's index in the embedding in twin's label
+ */
+ {
+ int twin;
+
+ twin = embedg_VES_get_twin_edge(embed_graph, n, cur_e);
+ embed_graph[twin].label = index_embed;
+ }
+
+ /*
+ so the only thing we couldn't update yet is
+ (*embedding)[index_embed].prev, cur_e previous edge in the list
+
+ but we can do this for next_e
+ */
+ (*embedding)[index_embed + 1].prev = index_embed;
+
+ /*
+ we check if there are any multiple edges or loops
+ to embed
+ */
+ w = embed_graph[cur_e].neighbour;
+ mult = embed_graph[cur_e].mult - 1;
+ /*
+ one was for the TE or BE edge
+ */
+
+ if (index_embed == (*vertices)[v_l].first_edge)
+ /*
+ when looking for multiple edges/loops
+ we must temporarily "close" this ordered
+ list of vertices when in presence of the first
+ edge in the list:
+
+ not doing this would mean that
+ (*embedding)[(*vertices)[v_l].first_edge].prev
+ contains some irrelevant value which may cause
+ (major) trouble when embedding inverses of
+ multiple edges...
+ */
+ {
+ (*embedding)[(*vertices)[v_l].first_edge].prev = index_embed;
+ }
+
+ embedg_recover_embedding_embed_mult(mult_edges, *embedding,
+ nbr_e, v, w, mult,
+ &index_embed, &set_next,
+ &new_first_edge);
+ embedg_recover_embedding_embed_loops(mult_edges, *embedding,
+ nbr_e, v, &index_embed,
+ &set_next);
+ set_next = TRUE;
+
+ /*
+ yes, it may be the case that (*vertices)[v_l].first_edge
+ change while in embedg_recover_embedding_embed_mult
+ -- see that function for more
+ */
+ (*vertices)[v_l].first_edge = new_first_edge == NIL ?
+ (*vertices)[v_l].first_edge : new_first_edge;
+
+ /*
+ that's all, we proceed to read a new edge in the list
+ */
+ index_embed += 1;
+ cur_e = next_e;
+ }
+
+ /*
+ now next_e = v so that cur_e is the last edge in v's adjacency list
+ we must deal with this case separately
+ */
+
+ /*
+ fix cur_e in embedding (and its twin)
+ */
+ (*embedding)[index_embed].in_adjl = embed_graph[cur_e].in_adjl;
+
+ /*
+ we temporarily set next of cur_e in to index_embed + 1
+ */
+ (*embedding)[index_embed].next = index_embed + 1;
+ (*embedding)[index_embed].mark = NIL; /* mark */
+
+ /*
+ fix cur_e's twin
+ */
+ if (embed_graph[cur_e].label != NIL)
+ {
+ (*embedding)[index_embed].inv = embed_graph[cur_e].label;
+ (*embedding)[embed_graph[cur_e].label].inv = index_embed;
+ ASSERT((*embedding)[embed_graph[cur_e].label].in_adjl ==
+ embed_graph[cur_e].twin_in_adjl);
+ }
+ else
+ {
+ int twin;
+
+ twin = embedg_VES_get_twin_edge(embed_graph, n, cur_e);
+ embed_graph[twin].label = index_embed;
+ }
+
+ /*
+ we temporarily set the next record's prev field:
+ but we can do that only if we haven't processed
+ all the edges yet
+ */
+ if (index_embed < 2 * nbr_e - 1)
+ {
+ (*embedding)[index_embed + 1].prev = index_embed;
+
+ /*
+ again, check if there are any multiple edges/loops
+ to embed
+ */
+ w = embed_graph[cur_e].neighbour;
+ mult = embed_graph[cur_e].mult - 1;
+ /*
+ one was for the TE or BE edge
+ */
+ v_w_in_embed = index_embed;
+
+ if (index_embed == (*vertices)[v_l].first_edge)
+ /*
+ same comment as above
+ */
+ {
+ (*embedding)[(*vertices)[v_l].first_edge].prev = index_embed;
+ }
+
+ embedg_recover_embedding_embed_mult(mult_edges, *embedding,
+ nbr_e, v, w, mult,
+ &index_embed, &set_next,
+ &new_first_edge);
+ embedg_recover_embedding_embed_loops(mult_edges, *embedding,
+ nbr_e, v, &index_embed,
+ &set_next);
+
+ /*
+ same comment as above
+ */
+ (*vertices)[v_l].first_edge = new_first_edge == NIL ?
+ (*vertices)[v_l].first_edge : new_first_edge;
+ }
+
+ /*
+ to finish off, we must set:
+
+ cur_e's next field:
+ next of cur_e in the list is ... vertices[v_l].first_edge
+
+ cur_e's next's previous field...
+ */
+ if (set_next)
+ /*
+ set_next (poorly named) is used to indicate which
+ edges must be updated to "close off" the list:
+
+ if set_next is TRUE, we are in the standard case
+ where the last edge in the ordered adj. list
+ is at index_embed
+
+ if set_next is FALSE, the last edge in the ordered adj. list
+ is at v_w_in_embed: because it could have happened
+ (in embedg_recover_embedding_embed_mult only)
+ that the edges have been "wedged" between
+ v_w_in_embed.prev and v_w_in_embed,
+ leaving v_w_in_embed the last in the list
+ */
+ {
+ (*embedding)[index_embed].next = (*vertices)[v_l].first_edge;
+ (*embedding)[(*vertices)[v_l].first_edge].prev = index_embed;
+ }
+ else
+ {
+ (*embedding)[v_w_in_embed].next = (*vertices)[v_l].first_edge;
+ (*embedding)[(*vertices)[v_l].first_edge].prev = v_w_in_embed;
+ }
+ set_next = TRUE;
+
+ /*
+ a simple check
+ */
+ ASSERT(embedg_dlcl_is_empty(mult_edges[v]));
+
+ /*
+ we can process another vertex
+ */
+ index_embed += 1;
+ }
+ /*
+ when this is done there are a few things that must hold
+ */
+ ASSERT(index_embed == 2 * nbr_e);
+}
+
+
+static void
+embedg_recover_embedding_embed_mult (t_dlcl **mult_edges,
+ t_embed_sparse_rep *embedding, int nbr_e, int v, int w,
+ int mult, int *index_embed, boolean *set_next, int *first_edge)
+ /*
+ see if the directed edge [v, w] is multiple: if so embed it
+ in embedding
+
+ moreover if there are any [v, v] loops do that too
+ */
+{
+ /*
+ we take care of multiple edges: for tree edges and back
+ edges their multiplicity is indicated by the
+ embed_graph[cur_e].mult field (which records the number
+ of undirected edges)
+
+ for loops hovewer this information is stored in the mult
+ field of the FIRST encountered neighbour v in v's neighbour
+ list
+ */
+ t_dlcl *p;
+ int v_w_in_embed, v_w_prev;
+ boolean do_twins, start, do_first_edge;
+
+ IF_DEB_EMBED_MULT(
+ fprintf(stdout, "in recover emb. mult, v %d w %d mult %d\n",
+ v, w, mult);
+ )
+
+ /*
+ the current index_embed value is the edge [v, w]:
+ I must record this value as it will be needed
+ later
+ */
+ v_w_in_embed = *index_embed;
+ start = TRUE;
+ *set_next = TRUE;
+ do_twins = FALSE;
+ *first_edge = NIL;
+ do_first_edge = FALSE;
+ v_w_prev = NIL;
+ while (mult > 0)
+ {
+ ASSERT(!embedg_dlcl_is_empty(mult_edges[v]));
+ p = embedg_dlcl_find(mult_edges[v], w);
+ /*
+ note that using embedg_dlcl_find to always find
+ the first in the list with p->info == w
+ is ok here since any previous such records would
+ have been deleted/removed from the list
+ */
+ ASSERT(p != NP);
+ /*
+ otherwise we couldn't have mult > 0 !
+ */
+
+ *index_embed += 1;
+
+ /*
+ once again I must use a similar sort of trick as in the
+ main function to deal with the inverse edge:
+
+ the inverse edge is to be found in mult_edges[w]:
+ if p->twin_in_adjl (which was initialised to NIL
+ and has NOT been set in the DFS preprocessing),
+ if p->twin_in_adjl != NIL, then
+ a. its inverse in mult_edges[w] has already been embedded
+ in *embedding
+ b. its index there is stored in p->twin_in_adjl
+ precisely
+ */
+ if (p->twin_in_adjl != NIL)
+ {
+ if (! start)
+ /*
+ if the first the multiple edges' inverse is already
+ stored, then this is true for ALL of them
+ */
+ {
+ ASSERT(do_twins == TRUE);
+ }
+ do_twins = TRUE;
+ }
+ else
+ /*
+ similarly, if the first the multiple edges' inverse is
+ not already stored, then this is true for ALL of them
+ */
+ {
+ ASSERT(do_twins == FALSE);
+ }
+
+ embedding[*index_embed].in_adjl = p->in_adjl;
+ embedding[*index_embed].mark = NIL;
+
+ /*
+ as we will see do_twins has to be treated differently
+ */
+ if (!do_twins)
+ /*
+ this is pretty standard as works as the
+ main recover function
+ */
+ {
+ t_dlcl *i_m_l, *i_p;
+
+ embedding[*index_embed].next = *index_embed + 1;
+
+ /*
+ we store the current index in the embedding in
+ the twin/inverse's twin_in_adjl field
+ */
+ i_p = i_m_l = mult_edges[w];
+ ASSERT(!embedg_dlcl_is_empty(i_m_l));
+ i_p = embedg_dlcl_find_with_NIL_twin_in_adjl(i_m_l, v);
+ ASSERT(i_p != NP);
+ ASSERT(i_p->twin_in_adjl == NIL);
+
+ i_p->twin_in_adjl = *index_embed;
+
+ /*
+ to finish off this bit we set embedding[*index_embed + 1].prev
+
+ but I can only set this prev field if I haven't reached
+ the end of the embedding[] array: this is why we needed
+ nbr_e (total number of edges to embed) as input
+ */
+
+ if (*index_embed < 2 * nbr_e - 1)
+ {
+ embedding[*index_embed + 1].prev = *index_embed;
+ }
+ }
+ else
+ /*
+ how to insert the inverses of multiple edges already
+ in the embedding:
+
+ if one studies how the twin_in_adjl field has been
+ set while dealing with the inverses of the
+ present multiple edges one sees that
+ the latter must be inserted in counter clockwise
+ order (assuming that the inverses were inserted
+ in clockwise order)
+
+ this is necessariy to ensure a correct matching between
+ the edge and its inverse
+ */
+ {
+
+ embedding[*index_embed].inv = p->twin_in_adjl;
+
+ /*
+ fix the twin by the same token
+ */
+ embedding[p->twin_in_adjl].inv = *index_embed;
+
+ /*
+ general (reverse) insertion for these edges
+ */
+ embedding[*index_embed].prev = *index_embed + 1;
+ embedding[*index_embed].next = *index_embed - 1;
+
+ /*
+ ok, that was the easy bit, things are a bit more complicated
+ below...
+ */
+ if (start)
+ /*
+ the edges are "wedged" between
+ embedding[v_w_in_embed].prev and v_w_in_embed,
+
+ hence the following
+ */
+ {
+ v_w_prev = embedding[v_w_in_embed].prev;
+ if (v_w_prev == v_w_in_embed)
+ /*
+ in this case the first edge in the adj. list
+ of the vertex whose first_edges is v_w_in_embed
+ will be changed
+ (because we insert in reverse order)
+ */
+ {
+ do_first_edge = TRUE;
+ }
+
+ embedding[*index_embed].next = v_w_in_embed;
+ embedding[v_w_in_embed].prev = *index_embed;
+
+ ASSERT(embedding[embedding[*index_embed].inv].prev ==
+ embedding[v_w_in_embed].inv);
+ ASSERT(embedding[embedding[v_w_in_embed].inv].next ==
+ embedding[*index_embed].inv);
+ }
+
+ if (mult == 1)
+ /*
+ last inv. edge in this list to add
+ */
+ {
+ ASSERT(v_w_prev != NIL);
+
+ /*
+ must fix embedding[v_w_prev].next appropriately
+ (and embedding[*index_embed].prev)
+
+ this may be overwritten later on, but not necessarily so
+
+ the next_set flag will enable us to decide
+ which edge ends this adjacency list: see above
+ */
+
+ embedding[*index_embed].prev = v_w_prev;
+ embedding[v_w_prev].next = *index_embed;
+ *set_next = FALSE;
+
+ ASSERT(embedding[embedding[*index_embed].inv].prev ==
+ embedding[*index_embed - 1].inv);
+ ASSERT(embedding[embedding[*index_embed - 1].inv].next ==
+ embedding[*index_embed].inv);
+
+ if (do_first_edge)
+ /*
+ the first edge is the last one added
+ */
+ {
+ *first_edge = *index_embed;
+ }
+
+ embedding[v_w_in_embed].next = *index_embed + 1;
+ if (*index_embed < 2 * nbr_e - 1)
+ {
+ embedding[*index_embed + 1].prev = v_w_in_embed;
+ }
+ }
+
+ ASSERT(embedding[embedding[*index_embed].inv].prev ==
+ embedding[embedding[*index_embed].next].inv);
+ }
+
+ /*
+ to finish off this bit we delete the p record from m_l
+ and set embedding[*index_embed + 1].prev
+ */
+ mult_edges[v] = embedg_dlcl_delete_rec(mult_edges[v], p);
+
+ mult--;
+ start = FALSE;
+ }
+ /*
+ conclusion: sevral days to get this working! *sigh*
+ */
+}
+
+
+
+
+static void
+embedg_recover_embedding_embed_loops (t_dlcl **mult_edges,
+ t_embed_sparse_rep *embedding, int nbr_e, int v,
+ int *index_embed, boolean *set_next)
+ /*
+ embed the [v, v] loops
+ */
+{
+ /*
+ the loops' multiplicity is stored in the mult
+ field of the FIRST encountered neighbour v in v's neighbour
+ list
+ */
+ t_dlcl *p;
+ int nbr_loops;
+
+ /*
+ have a look if there are any [v. v] loops
+ */
+ p = embedg_dlcl_find(mult_edges[v], v);
+ if (p == NP)
+ {
+ return;
+ }
+
+ /*
+ when there are loops to add to the adjaceny list,
+ edge insertion resume in the "normal" clockwaise saya, way:
+ so we reset set_next to true
+ */
+ *set_next = TRUE;
+
+ nbr_loops = p->mult;
+ ASSERT(nbr_loops % 2 == 0);
+ /*
+ we counted directed edges
+ */
+ nbr_loops /= 2;
+
+ IF_DEB_EMBED_LOOPS(
+ fprintf(stdout, "in recover emb. loops, nbr_loops [v, v] %d\n",
+ nbr_loops);
+ )
+
+ while (nbr_loops > 0)
+ /*
+ a loop requires to embed two directed edges
+ */
+ {
+ p = embedg_dlcl_find(mult_edges[v], v);
+ ASSERT(p != NP);
+
+ *index_embed += 1;
+
+ embedding[*index_embed].in_adjl = p->in_adjl;
+ embedding[*index_embed].next = *index_embed + 1;
+ embedding[*index_embed].mark = NIL;
+ embedding[*index_embed].inv = *index_embed + 1;
+ embedding[*index_embed + 1].prev = *index_embed;
+
+ mult_edges[v] = embedg_dlcl_delete_rec(mult_edges[v], p);
+
+ IF_DEB_EMBED_LOOPS(
+ fprintf(stdout, "in recover emb. loops, mid\n");
+ embedg_dlcl_print(mult_edges[v]);
+ );
+
+ /*
+ now do the "inverse" loop
+ */
+ p = embedg_dlcl_find(mult_edges[v], v);
+ ASSERT(p != NP);
+
+ *index_embed += 1;
+
+ embedding[*index_embed].in_adjl = p->in_adjl;
+ embedding[*index_embed].next = *index_embed + 1;
+ embedding[*index_embed].mark = NIL;
+ embedding[*index_embed].inv = *index_embed - 1;
+
+ if (*index_embed < 2 * nbr_e - 1)
+ {
+ embedding[*index_embed + 1].prev = *index_embed;
+ }
+ mult_edges[v] = embedg_dlcl_delete_rec(mult_edges[v], p);
+
+ nbr_loops--;
+
+ IF_DEB_EMBED_LOOPS(
+ fprintf(stdout, "in recover emb. loops, end\n");
+ embedg_dlcl_print(mult_edges[v]);
+ );
+ }
+}
+
+
+
+
+void
+embedg_recov_embed_walk_proper_face (int n, int e, t_adjl_sparse_rep *A,
+ t_embed_sparse_rep *embedding, boolean MARK, int mark)
+ /*
+ do a proper face walk in the recovered embedding starting
+ at index e in the embedding
+ */
+{
+ int cur, next;
+
+ IF_DEB_FACES(
+ fprintf(stdout, "recov. emb. proper face walk\n");
+ fprintf(stdout, "[-, %d] ",
+ A[embedding[e].in_adjl].end_vertex);
+ )
+
+ cur = e;
+ next = NIL;
+ while (next != e)
+ /*
+ to get the next in a proper face traversal:
+ get the previous of the cur's inverse
+ */
+ {
+ int inv;
+
+ inv = embedding[cur].inv;
+ next = embedding[inv].prev;
+
+ ASSERT(embedding[next].mark != mark);
+
+ if (MARK)
+ {
+ embedding[next].mark = mark;
+ }
+
+ cur = next;
+ IF_DEB_FACES(
+ fprintf(stdout, "[-, %d] ",
+ A[embedding[cur].in_adjl].end_vertex);
+ )
+ }
+ IF_DEB_FACES(
+ fprintf(stdout, "\n");
+ )
+}
+
+
+
+boolean
+embedg_check_recov_embedding (int n, int nbr_e, int nbr_comp,
+ t_ver_sparse_rep *vertices, t_adjl_sparse_rep *A,
+ t_embed_sparse_rep *embedding)
+ /*
+ check if the recovered embedding is a valid embedding
+ SHOULD ONLY be use after creation, that is, after having
+ recovered the embedding from the VES structure
+ (because of the mark MIN_EMBED_MARK we use)
+ */
+{
+ int v, e, f;
+
+ f = 0;
+ /*
+ do all the edges in embedding:
+ careful: we have 2 * nbr_e to visit (the edge and its inverse!)
+ */
+ for (e = 0; e < 2 * nbr_e; e++)
+ {
+ /*
+ we check if the current edge is marked: if not, we
+ traverse a proper face bordered by this edge
+ */
+ if (embedding[e].mark != MIN_EMBED_MARK)
+ /*
+ we --hopefully-- perform this check only after creation
+ where mark == NIL
+ */
+ {
+ embedg_recov_embed_walk_proper_face(n, e, A, embedding,
+ TRUE, MIN_EMBED_MARK);
+ f++;
+ }
+ }
+
+ /*
+ must also count a face for each isolated vertex
+ */
+ for (v = 0; v < n; v++)
+ {
+ if (vertices[v].first_edge == NIL)
+ f++;
+ }
+
+ IF_DEB_CHECK_EMBED(
+ fprintf(stdout, "recovered embedding, n: %d\t e: %d\t C: %d\t f: %d\n",
+ n, nbr_e, nbr_comp, f);
+ )
+
+ return f == 2 * nbr_comp + nbr_e - n ? TRUE : FALSE;
+}
+
+
+t_dlcl **
+embedg_recover_obstruction (t_ver_edge *embed_graph, int n, minor m, int *nbr_e)
+ /*
+ recover the obstruction as a t_dlcl * structure:
+ and return the number of edges: lets say we agree on returning
+ the number of undirected edges
+ -- I don't know yet which way to do, directed or undirected???
+
+ so far in the algorithm we only dealt with DFIs,
+ but now, we retrieve the obstruction not wrt DFIs but
+ wrt the vertices' labels
+ */
+{
+ /*
+ so I am looking, in embed_graph, for the vertices and edges
+ marked MARK_MINORS(n)
+ */
+
+ int v;
+ t_dlcl **obs;
+
+ obs = (t_dlcl **) mem_malloc(sizeof(t_dlcl *) * n);
+ for (v = 0; v < n; v++)
+ obs[v] = NP;
+
+ *nbr_e = 0;
+ for (v = 0; v < 2*n; v++)
+ /*
+ must check real vertices as well as virtual vertices
+ */
+ {
+ int e;
+
+ if (embed_graph[v].link[0] == v)
+ /*
+ isolated vertex case
+ */
+ {
+ ASSERT(embed_graph[v].link[1] == v);
+ continue;
+ }
+
+ e = embed_graph[v].link[0];
+ while (e != v)
+ {
+ ASSERT(embedg_VES_is_edge(n, e));
+ if (embed_graph[e].visited == MARK_MINORS(n))
+ {
+ int cur_v, neigh;
+
+ /*
+ virtual vertices may still hang around
+ */
+ /*
+ let's get the "actual" v:
+ note that the statement below is safe since if v were
+ not a valid virtual vertex (ie [v - n].DFS_parent = n)
+ it would have an empty
+ adjacency list and we wouldn't be there anyway
+ */
+ cur_v = embedg_VES_get_ver(embed_graph, n, v);
+
+ neigh = embedg_VES_get_ver(embed_graph, n,
+ embed_graph[e].neighbour);
+
+ /*
+ again, cur_v and neigh are DFIs,
+ we want vertex labels at this stage
+ */
+ cur_v = embed_graph[cur_v].label;
+ neigh = embed_graph[neigh].label;
+ sparseg_dlcl_append_to_neigh_list(obs, n, cur_v, neigh,
+ embed_graph[e].in_adjl);
+ (*nbr_e)++;
+ }
+ e = embed_graph[e].link[0];
+ }
+ }
+
+ IF_DEB_OBS(
+ fprintf(stdout, "recovering the obstruction\n");
+ sparseg_dlcl_print(obs, n);
+ );
+
+ ASSERT(*nbr_e % 2 == 0);
+ *nbr_e /= 2;
+
+ return obs;
+}
+
+
+static t_dlcl **
+embedg_get_reduced_obs (t_dlcl **obs, int n)
+ /*
+ reduce the obstruction by removing all degree 2 vertices
+ (so that they become isolated vertices)
+ */
+{
+ t_dlcl **reduced;
+ int v;
+
+ reduced = (t_dlcl **) mem_malloc(sizeof(t_dlcl *) * n);
+ for (v = 0; v < n; v++)
+ {
+ reduced[v] = embedg_dlcl_copy(obs[v]);
+ }
+
+ for (v = 0; v < n; v++)
+ {
+ t_dlcl *n_l, *n_l_b, *p, *new_n_v, *n_l_x, *b_in_n_x;
+ int a, b, n_x;
+
+ n_l = reduced[v];
+ while (!embedg_dlcl_is_empty(n_l)
+ && embedg_dlcl_list_last(n_l) == embedg_dlcl_list_next(n_l))
+ /*
+ pick out which vertices have deg 2
+ */
+ {
+ a = n_l->info;
+ b = embedg_dlcl_list_next(n_l)->info;
+ /*
+ we remove the edge (v, b), or rather, we identify v and b:
+ b will then be an isolated vertex
+
+ fix v's neighbour list: all of b's neighbours
+ are now v's neighbours
+ */
+ reduced[v] = n_l =
+ embedg_dlcl_delete_rec(n_l, embedg_dlcl_list_last(n_l));
+
+ p = n_l_b = reduced[b];
+ ASSERT(!embedg_dlcl_is_empty(n_l_b));
+ n_x = p->info;
+ if (n_x != v)
+ {
+ new_n_v = embedg_dlcl_rec_new(n_x);
+ reduced[v] = n_l = embedg_dlcl_cat(n_l, new_n_v);
+
+ /*
+ and in n_x neighbour list, we must replace b by v
+ */
+ n_l_x = reduced[n_x];
+ b_in_n_x = embedg_dlcl_find(n_l_x, b);
+ b_in_n_x->info = v;
+ }
+ /*
+ and do this for all of b's neighbours
+ */
+ p = embedg_dlcl_list_next(p);
+ while (p != n_l_b)
+ {
+ n_x = p->info;
+ if (n_x != v)
+ {
+ new_n_v = embedg_dlcl_rec_new(n_x);
+ reduced[v] = n_l = embedg_dlcl_cat(n_l, new_n_v);
+ n_l_x = reduced[n_x];
+ b_in_n_x = embedg_dlcl_find(n_l_x, b);
+ b_in_n_x->info = v;
+ }
+ p = embedg_dlcl_list_next(p);
+ }
+ embedg_dlcl_delete(reduced[b]);
+ reduced[b] = NP;
+ }
+ }
+
+ IF_DEB_CHECK_OBS(
+ fprintf(stdout, "reducing the obstruction\n");
+ sparseg_dlcl_print(reduced, n);
+ )
+
+ /*
+ now check no degree 2 vertices are left
+ */
+ for (v = 0; v < n; v++)
+ {
+ t_dlcl *n_l;
+
+ n_l = reduced[v];
+ if (!embedg_dlcl_is_empty(n_l))
+ {
+ ASSERT(embedg_dlcl_list_last(n_l) != embedg_dlcl_list_next(n_l));
+ }
+ }
+
+ return reduced;
+}
+
+static boolean
+embedg_is_red_obs_K33 (t_dlcl **reduced, int n)
+ /*
+ check if the (reduced) obstruction is indeed K33
+ */
+{
+ int v, order, vs[6], i, b1[3];
+
+ /*
+ check that order == 6 and that the obstruction is cubic
+ */
+ order = 0;
+ for (v = 0; v < n; v++)
+ {
+ if (!embedg_dlcl_is_empty(reduced[v]))
+ {
+ if (order == 6)
+ {
+ return FALSE;
+ }
+ order++;
+ vs[order - 1] = v;
+
+ if (embedg_dlcl_length(reduced[v]) != 3)
+ {
+ return FALSE;
+ }
+ }
+ }
+ if (order != 6)
+ {
+ return FALSE;
+ }
+
+ /*
+ check if bipartite
+ */
+ v = vs[0];
+ ASSERT(!embedg_dlcl_is_empty(reduced[v]));
+ b1[0] = reduced[v]->info;
+ b1[1] = embedg_dlcl_list_next(reduced[v])->info;
+ b1[2] = embedg_dlcl_list_prev(reduced[v])->info;
+
+ for (i = 1; i < 6; i++)
+ {
+ t_dlcl *n_v;
+
+ v = vs[i];
+ n_v = reduced[v];
+ ASSERT(!embedg_dlcl_is_empty(n_v));
+ if (n_v->info == b1[0]
+ || embedg_dlcl_list_next(n_v)->info == b1[0]
+ || embedg_dlcl_list_prev(n_v)->info == b1[0])
+ {
+ if ((n_v->info != b1[1]
+ && embedg_dlcl_list_next(n_v)->info != b1[1]
+ && embedg_dlcl_list_prev(n_v)->info != b1[1])
+ &&
+ (n_v->info != b1[2]
+ && embedg_dlcl_list_next(n_v)->info != b1[2]
+ && embedg_dlcl_list_prev(n_v)->info != b1[2]))
+ {
+ return FALSE;
+ }
+ }
+ else
+ {
+ if ((n_v->info == b1[1]
+ || embedg_dlcl_list_next(n_v)->info == b1[1]
+ || embedg_dlcl_list_prev(n_v)->info == b1[1])
+ ||
+ (n_v->info == b1[2]
+ || embedg_dlcl_list_next(n_v)->info == b1[2]
+ || embedg_dlcl_list_prev(n_v)->info == b1[2]))
+ {
+ return FALSE;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+
+static boolean
+embedg_is_red_obs_K5 (t_dlcl **reduced, int n)
+ /*
+ check if the (reduced) obstruction is indeed K5
+ */
+{
+ int v, order;
+
+ /*
+ check that order == 5 and that the obstruction is quadric
+ */
+ order = 0;
+ for (v = 0; v < n; v++)
+ {
+ if (!embedg_dlcl_is_empty(reduced[v]))
+ {
+ if (order == 5)
+ {
+ return FALSE;
+ }
+ order++;
+
+ if (embedg_dlcl_length(reduced[v]) != 4)
+ {
+ return FALSE;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+
+boolean
+embedg_check_recov_obs (t_dlcl **obs, int n, minor m)
+ /*
+ check if the recovered obstruction is one of K33 or K5
+ */
+{
+ t_dlcl **reduced;
+ boolean ans;
+
+ reduced = embedg_get_reduced_obs(obs, n);
+ if (m != MINOR_E5)
+ {
+ ans = embedg_is_red_obs_K33(reduced, n);
+ }
+ else
+ {
+ ans = embedg_is_red_obs_K5(reduced, n);
+ }
+
+ sparseg_dlcl_delete(reduced, n);
+ return ans;
+}
+/*
+ * obstruction.c
+ */
+
+/*
+ What:
+ *****
+
+ Implementing:
+
+ The graph is not planar: we recover the obstruction from the VES structure
+ and check it as well.
+ (Some of these checks will disappear later)
+
+
+
+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ from
+
+ Simplified O(n) Planarity Algorithms (draft)
+ ************************************
+
+ John Boyer JBoyer@PureEdge.com, jboyer@acm.org
+ Wendy Myrvold wendym@csr.uvic.ca
+
+
+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ authors:
+ ********
+
+ Paulette Lieby (Magma), Brendan McKay (ANU)
+
+ Started October 2001
+*/
+
+
+#include "planarity.h"
+
+#define IF_DEB(x) {}
+#define IF_VERB(x) {}
+#define IF_DEB_OBS(x) {}
+#define IF_DEB_CHECK_OBS(x) {}
+#define IF_CPU(x) {}
+#define IF_DEB_MINOR(x) {}
+
+
+/* aproto: header embed_graph_protos.h */
+
+void
+embedg_obstruction (
+ t_ver_sparse_rep *V,
+ t_adjl_sparse_rep *A, /* the input graph as a sparse graph */
+ t_dlcl **dfs_tree, /* a sparse graph rep. for the dfs tree
+ -- vertices are as DFIs
+ -- and children are ordered wrt
+ lowpoint value
+ */
+ t_dlcl **back_edges, /* for each vertex v, a dlcl
+ of the back edges [v, x] incident to v
+ where x is a DESCENDANT of v
+ (vertices are given as DFIs)
+ */
+ t_ver_edge *embed_graph, /* output of tester */
+ int n, /* size of the graph */
+ int *edge_pos, /* pos. in embed_graph for addition
+ of the next edge */
+ int v,
+ int w_in, /* the unembedded directed back edge
+ [w_in, v]
+ */
+ t_ver_sparse_rep **OV, /* the obstruction as an adjacency list */
+ t_adjl_sparse_rep **OA,
+ int *nbr_e_obs /* obstruction's #edges */
+)
+
+ /*
+ the graph is non planar: we must mark & recover the K33 or K5
+ homeomorph
+ */
+{
+ int *ver_orient;
+ minor m;
+ t_dlcl **obs;
+
+ /*
+ this is magma code - must be removed
+ */
+ float sttime, time_to_now;
+
+ IF_CPU(
+ sttime = time_current_user();
+ )
+
+ /*
+ we will NOT remove the short-cut edges at this stage:
+ we'll have to perform another walkdown in embedg_iso_is_minor_A
+ so
+ 1. saves time when looking for ext. active vertices
+ 2. more importantly this enables us to ascertain that the number of
+ edges in embed_graph (even after completing whichever obstruction
+ applying in this case) will NEVER be > 3*n - 5!!!
+ 3. SCEs are then removed in embedg_iso_is_minor_A
+ (obligatory path for every possible case)
+ */
+
+ /*
+ we must compute each vertex's orientation (wrt flipped bicomps)
+ and set the edges' orientation:
+
+ the other day I was wondering why this was necessary in this
+ instance (because after all we won't get an embedding):
+ orientation is required bacause later in the piece we
+ do a proper face traversal (I guess for Minor C testing)
+ */
+ ver_orient = embedg_vertices_orientation(embed_graph, n);
+ embedg_VES_set_orientation(embed_graph, n, ver_orient);
+ mem_free(ver_orient);
+
+ m = embedg_mark_obstruction(dfs_tree, back_edges,
+ embed_graph, n, edge_pos, v, w_in);
+
+ /*
+ get the obstruction
+ */
+ obs = embedg_recover_obstruction(embed_graph, n, m, nbr_e_obs);
+
+ /*
+ and check it
+ */
+ if (!embedg_check_recov_obs(obs, n, m))
+ {
+ sparseg_dlcl_delete(obs, n);
+ DIE();
+ }
+
+ sparseg_dlcl_to_sparseg(obs, n, *nbr_e_obs, OV, OA);
+ sparseg_dlcl_delete(obs, n);
+
+ /*
+ just for the sake of it, chcek if the obstruction is
+ a subgraph of the input graph
+ */
+ if (!sparseg_adjl_sub(*OV, n, *OA, V, n, A))
+ {
+ DIE();
+ }
+
+ IF_DEB_OBS(
+ sparseg_adjl_print(*V, n, *A, FALSE);
+ )
+
+ IF_CPU(
+ fprintf(stdout, "CPU for obstruction recovering %f\n",
+ (time_current_user() - sttime));
+ )
+}
+
+
+
+
+
+
+
+minor
+embedg_mark_obstruction (
+ t_dlcl **dfs_tree, /* a sparse graph rep. for the dfs tree
+ -- vertices are as DFIs
+ -- and children are ordered wrt
+ lowpoint value
+ */
+ t_dlcl **back_edges, /* for each vertex v, a dlcl
+ of the back edges [v, x] incident to v
+ where x is a DESCENDANT of v
+ (vertices are given as DFIs)
+ */
+ t_ver_edge *embed_graph, /* output of tester */
+ int n, /* size of the graph */
+ int *edge_pos, /* pos. in embed_graph for addition
+ of the next edge */
+ int v,
+ int w_in /* the unembedded directed back edge
+ [w_in, v]
+ */
+)
+ /*
+ the graph is non planar: we must mark & recover the K33 or K5
+ homeomorph
+ */
+{
+ int c, vr, x, y, w;
+ int *path_v, *path_e, nbr_v, entry_in_path_e;
+ boolean px_attached_high, py_attached_high, is_minor_D;
+ minor m;
+
+
+ IF_CPU(
+ float sttime; float time_to_now;
+
+ sttime = time_current_user();
+ )
+
+
+ /*
+ find c such that v^c is the root of the biconnected
+ component on which the walkdown failed
+ */
+ c = embedg_iso_get_c_of_v(embed_graph, n, v, w_in);
+
+ /*
+ now: decide which minor we are dealing with and mark the
+ appropriate one (vertices/edges marked as MARK_MINOR(n)
+ in embed_graph)
+ */
+ if (embedg_iso_is_minor_A(embed_graph, n, edge_pos, v, c, &vr))
+ {
+ embedg_mark_minor_A(dfs_tree, back_edges,
+ embed_graph, n, edge_pos, v, c, vr);
+
+ IF_DEB_MINOR(
+ fprintf(stdout, "Minor A\n");
+ )
+
+ return MINOR_A;
+ }
+
+ /*
+ get the externally active vertices x & y and the pertinent w
+ on the external face of the bicomp rooted by v^c
+
+ and determine if minor B
+ */
+ if (embedg_iso_is_minor_B(embed_graph, n, edge_pos, v, c,
+ &x, &y, &w))
+ {
+ embedg_mark_minor_B(dfs_tree, back_edges,
+ embed_graph, n, edge_pos, v, c,
+ x, y, w);
+ IF_DEB_MINOR(
+ fprintf(stdout, "Minor B\n");
+ )
+
+ IF_CPU(
+ fprintf(stdout, "CPU for obstruction isolation %f\n",
+ time_current_user() - sttime);
+ )
+
+ return MINOR_B;
+ }
+
+ /*
+ the remaining cases: must get the highest x-y path
+
+ it will be containing in path_v (vertices), path_e (edges)
+ */
+ embedg_iso_get_highest_x_y_path(embed_graph, n, MARK_EXT_FACE(n),
+ MARK_EXT_FACE_L(n),
+ MARK_EXT_FACE_R(n),
+ v, c, x, y, w,
+ &path_v, &path_e,
+ &nbr_v, &entry_in_path_e,
+ &px_attached_high,
+ &py_attached_high,
+ &is_minor_D);
+
+ /*
+ we are in the minor C case if either one of p_x or p_y
+ is attached high
+ */
+ if (px_attached_high || py_attached_high)
+ {
+ embedg_mark_minor_C(dfs_tree, back_edges, embed_graph, n, edge_pos,
+ v, c, x, y, w,
+ path_v, path_e, nbr_v,
+ px_attached_high, py_attached_high);
+ IF_DEB_MINOR(
+ fprintf(stdout, "Minor C\n");
+ )
+
+ mem_free(path_v);
+ mem_free(path_e);
+
+ IF_CPU(
+ fprintf(stdout, "CPU for obstruction isolation %f\n",
+ time_current_user() - sttime);
+ )
+
+ return MINOR_C;
+ }
+
+ if (is_minor_D)
+ {
+ embedg_mark_minor_D(dfs_tree, back_edges, embed_graph, n, edge_pos,
+ v, c, x, y, w,
+ path_v, path_e, nbr_v, entry_in_path_e);
+ IF_DEB_MINOR(
+ fprintf(stdout, "Minor D\n");
+ )
+
+ mem_free(path_v);
+ mem_free(path_e);
+
+ IF_CPU(
+ fprintf(stdout, "CPU for obstruction isolation %f\n",
+ time_current_user() - sttime);
+ )
+
+ return MINOR_D;
+ }
+
+ /*
+ finally, the minor E case
+ */
+ m = embedg_mark_minor_E(dfs_tree, back_edges, embed_graph, n, edge_pos,
+ v, c, x, y, w,
+ path_v, path_e, nbr_v);
+ switch (m)
+ {
+ case MINOR_E1:
+ IF_DEB_MINOR(
+ fprintf(stdout, "Minor E1\n");
+ )
+ break;
+ case MINOR_E2:
+ IF_DEB_MINOR(
+ fprintf(stdout, "Minor E2\n");
+ )
+ break;
+ case MINOR_E3:
+ IF_DEB_MINOR(
+ fprintf(stdout, "Minor E3\n");
+ )
+ break;
+ case MINOR_E4:
+ IF_DEB_MINOR(
+ fprintf(stdout, "Minor E4\n");
+ )
+ break;
+ case MINOR_E5:
+ IF_DEB_MINOR(
+ fprintf(stdout, "Minor E5\n");
+ )
+ break;
+ case MINOR_A:
+ case MINOR_B:
+ case MINOR_C:
+ case MINOR_D:
+ case MINOR_E:
+ case NBR_MINORS:
+ break;
+ }
+
+ mem_free(path_v);
+ mem_free(path_e);
+
+ IF_CPU(
+ fprintf(stdout, "CPU (scaled) for obstruction isolation %f\n",
+ (time_current_user() - sttime) / e);
+ )
+
+ return m;
+}
+/*
+ * isolator.c
+ */
+
+/*
+ What:
+ *****
+
+ Implementing:
+
+ The graph is non planar: we isolate the obstruction.
+
+
+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ from
+
+ Simplified O(n) Planarity Algorithms (draft)
+ ************************************
+
+ John Boyer JBoyer@PureEdge.com, jboyer@acm.org
+ Wendy Myrvold wendym@csr.uvic.ca
+
+
+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ authors:
+ ********
+
+ Paulette Lieby (Magma), Brendan McKay (ANU)
+
+ Started October 2001
+*/
+
+
+#include "planarity.h"
+
+#define IF_DEB(x) {}
+#define IF_VERB(x) {}
+#define IF_DEB_TREE(x) {}
+#define IF_DEB_EDGES(x) {}
+#define IF_CPU(x) {}
+/* #define IF_DEB_MINOR(x) {x} -- Not Used */
+
+
+/* aproto: header embed_graph_protos.h */
+
+#ifndef PLANAR_IN_MAGMA
+#endif
+
+
+
+
+int
+embedg_iso_get_c_of_v (t_ver_edge *embed_graph, int n, int v, int w)
+ /*
+ the edge [v, w] (w a descendant of v) remains unembedded
+ after the walkdown returns
+
+ find c such that v^c is the root of the biconnected
+ component on which the walkdown failed
+ */
+{
+ /*
+ how to do this??? easy! follow the DFS tree path as given
+ by the field DFS_parent
+ */
+
+ int u;
+
+ u = embed_graph[w].DFS_parent;
+ while (embed_graph[u].DFS_parent != v)
+ {
+ u = embed_graph[u].DFS_parent;
+ }
+ /*
+ this is guaranteed to succeed given the structure of the DFS tree
+ and the fact that there exists a back edge [w, v]
+ */
+
+ return u;
+}
+
+
+boolean
+embedg_iso_is_minor_A (t_ver_edge *embed_graph, int n,
+ int *edge_pos, int v, int c, int *vr)
+ /*
+ determines if the obstruction is a minor A
+ */
+{
+ /*
+ to do this we again call the walkdown routine with v^c as input,
+ the walkdown routine will fail (since there will be an
+ un-embedded back edge incident to v and to a vertex
+ in the subtree rooted by v^c)
+
+ the obstruction is a minor A if the merge queue returned by the
+ walkdown is non-empty, if this is the case we return
+ the bicomp last appended to the queue
+ */
+ int vv;
+ t_merge_queue q;
+
+ vv = c + n;
+
+ q = embedg_walkdown(embed_graph, n, edge_pos, vv);
+ /*
+ we MUST remove the SCEs here: this is the only place where it
+ will be done when looking for and recovering an obstruction
+
+ this is safe since this very function applies to ALL cases!
+ */
+ embedg_remove_SCE(embed_graph, n, *edge_pos);
+
+ if (!embedg_merge_queue_empty(q))
+ /*
+ the bicomp of interest is the last in the queue
+ */
+ {
+ int r, rin, vrout;
+
+ embedg_merge_queue_prune(&q, &r, &rin, vr, &vrout);
+ embedg_merge_queue_delete(q);
+ return TRUE;
+ }
+ else
+ {
+ embedg_merge_queue_delete(q);
+ return FALSE;
+ }
+}
+
+
+void
+embedg_iso_get_x_y_w (t_ver_edge *embed_graph, int n, int v, int r,
+ int c, int mark, int mark_l, int mark_r, int *x, int *y, int *w)
+ /*
+ the obstruction is one of minor B, C, D, E.
+
+ get the externally active vertices x & y along the
+ external face paths starting at r^c
+
+ get a pertinent vertex w along the lower external
+ face path between x and y
+
+ external activity and pertinence are wrt v
+
+ all the vertices on the external face r^c...x...w
+ and r^c...y...w will be marked (the visited field)
+ */
+{
+ int vr, vrin, x_y[4];
+ int s, sin, cur, curin;
+
+ vr = c + n;
+
+ /*
+ find x and y first:
+
+ note that we mark the vertices on the external face r^c...x
+ and r^c...y
+
+ more on that below
+ */
+ embed_graph[vr].visited = mark;
+ for (vrin = 0; vrin <= 1; vrin++)
+ {
+ int m;
+
+ m = vrin == 0 ? mark_l : mark_r;
+ embedg_VES_get_succ_ext_active_on_ext_face(embed_graph, n, v,
+ vr, vrin,
+ TRUE, m,
+ &s, &sin);
+ x_y[vrin] = s;
+ x_y[vrin + 2] = sin;
+ /*
+ note the bizarre way I store the active vertex
+ and the direction out of which to continue a walk
+ on the lower external face as described above
+ */
+ }
+ *x = x_y[0];
+ *y = x_y[1];
+
+ /*
+ next get the pertinent w on the lower external face from x to y
+ */
+ cur = x_y[0];
+ curin = x_y[2];
+ embedg_VES_get_succ_pertinent_on_ext_face(embed_graph, n, v,
+ cur, curin,
+ TRUE, mark_l, w, &sin);
+
+ /*
+ now all the vertices r^c...x...w and r^c...y have been marked,
+ it remains to mark the vertices on the y...w external face path
+
+ (will need to be able to distinguish the external face later on)
+
+ Note the way the external face is marked (needed when recovering
+ the highest x-y path):
+ mark_l for the path v^c...x...w
+ mark_r for the path v^c...y
+ mark for the lower external face y...w
+ */
+ cur = x_y[1];
+ curin = x_y[3];
+ s = n;
+ while (s != *w)
+ {
+ embedg_VES_get_succ_pertinent_on_ext_face(embed_graph, n, v,
+ cur, curin,
+ TRUE, mark, &s, &sin);
+ cur = s;
+ curin = sin;
+ }
+
+ IF_DEB(
+ fprintf(stdout, "get x, y & w: the external face\n");
+ fprintf(stdout, "%d\t", vr);
+ cur = vr;
+ curin = 0;
+ while (s != vr)
+ {
+ embedg_VES_get_succ_on_ext_face(embed_graph, n,
+ cur, curin,
+ FALSE, 0, &s, &sin);
+ cur = s;
+ curin = sin;
+ fprintf(stdout, "%d\t", s);
+ }
+ fprintf(stdout, "\n");
+ )
+}
+
+
+
+
+boolean
+embedg_iso_is_minor_B (t_ver_edge *embed_graph, int n, int *edge_pos,
+ int v, int c, int *x, int *y, int *w)
+ /*
+ determines if the obstruction is a minor B and return x, y
+ (ext. active) and w (pertinent)
+ */
+{
+ /*
+ get x & y the ext. active vertices on the (external face)
+ path out of v^c,
+ and w the pertinent vertex on the lower external face x-y
+
+ PLUS mark the whole external face with MARK_EXT_FACE(n)
+ */
+ embedg_iso_get_x_y_w(embed_graph, n, v, v, c,
+ MARK_EXT_FACE(n),
+ MARK_EXT_FACE_L(n), MARK_EXT_FACE_R(n),
+ x, y, w);
+
+ if (embedg_dlcl_is_empty(embed_graph[*w].pertinent_bicomp_list))
+ /*
+ w has no pertinent child bicomp: not a minor B
+ */
+ return FALSE;
+ else
+ {
+ t_dlcl *pert_l;
+ int l;
+
+ pert_l = embed_graph[*w].pertinent_bicomp_list;
+ l = embedg_dlcl_list_last(pert_l)->info;
+ /*
+ if w has an ext. active pertinent child bicomp then minor B
+
+ note that we need to know if w has an ext. active AND pertinent
+ bicomp child: so it is NOT good enough to test
+ w's separated_DFS_child_list as is done in
+ embedg_VES_is_ver_ext_active!!!!!!!!!
+
+ PLUS: l is actually a VIRTUAL vertex: to check its lowpoint
+ I must take its DFS child l - n !!!!!!!!
+ */
+ ASSERT(embedg_VES_is_virtual_vertex(n, l));
+ l = l - n;
+ return embed_graph[l].lowpoint < v ? TRUE : FALSE;
+ }
+}
+
+void
+embedg_iso_get_highest_x_y_path (
+ t_ver_edge *embed_graph,
+ int n,
+ int mark,
+ int mark_l,
+ int mark_r,
+ int v,
+ int c,
+ int x,
+ int y,
+ int w,
+ int **path_v, /* stack of vertices in x-y path */
+ int **path_e, /* stack of egdes in x-y path */
+ int *nbr_v, /* number of vertices in path_v */
+ int *entry_in_path_e, /* the in direction for the FIRST edge in
+ path_e: needed later on *sigh*
+ */
+ boolean *px_attached_high,
+ boolean *py_attached_high,
+ boolean *is_minor_D
+)
+ /*
+ the obstruction is one of minor C, D, E.
+
+ we want to recover the highest x-y path:
+ the obstructing path attached to the external faces v^c - x - w
+ and v^c - y - w
+
+ while doing all this we also determine if the case is a minor C
+ or a minor D
+ */
+{
+ /*
+ the path is obtained by walking the proper face starting at v
+ where ALL the edges incident to v^c BUT the ones bordering
+ the external face have been removed
+
+ I won't I don't think remove these edges, but instead I'll be
+ implementing an "avoidance" walk
+ */
+
+ int vv, s, sin, p_x, p_y, cur_v, cur_vin;
+ int e, ein, s_e, s_ein;
+ boolean avoid_vv;
+
+ /*
+ must start the walk at edge embed_graph[v^c].link[1 ^ 0],
+ (vvin = 0 is in direction of x, see embedg_iso_get_x_y_w)
+ */
+ vv = c + n;
+ e = embed_graph[vv].link[1];
+ ein = 0; /* because of adjacency list consistency */
+
+ *path_v = (int *) mem_malloc(sizeof(int) * n);
+ *path_e = (int *) mem_malloc(sizeof(int) * n);
+ (*nbr_v) = -1;
+
+ /*
+ recall that in embedg_iso_get_x_y_w we did mark
+ (with mark, mark_l, mark_r)
+ ALL the vertices lying on the external face walk starting
+ & ending at v^c: we will use this fact to enable us
+ to decide if a vertex is on the external face
+ (as opposed to being on the internal face)
+ */
+
+ s = embed_graph[e].neighbour;
+ ASSERT(embed_graph[s].visited == mark_l);
+ /*
+ this must be the case since s lies on the external face
+ starting at v^c in x's direction
+ -- we push s onto the stack
+ */
+ (*path_v)[++(*nbr_v)] = s;
+
+ /*
+ start the proper face walk which "avoids" v^c since the
+ internal edges incident to v^c are supposed to have
+ been removed
+
+ please read on
+ */
+ avoid_vv = FALSE;
+ while (TRUE)
+ {
+ boolean av;
+
+ av =
+ embedg_VES_get_succ_on_proper_face_with_avoidance(
+ embed_graph, n,
+ e, ein, vv,
+ FALSE, 0,
+ &s, &s_e, &s_ein);
+ avoid_vv = av == TRUE ? av : avoid_vv;
+ if (embed_graph[s].visited == mark_l)
+ /*
+ means that s is still on the external face:
+ empty the path's stack and push s
+ */
+ {
+ (*nbr_v) = -1;
+ (*path_v)[++(*nbr_v)] = s;
+ e = s_e;
+ ein = s_ein;
+ }
+ else if (*nbr_v == 0)
+ /*
+ s is the first encountered vertex after
+ path_v[0] which does not
+ lie on the external face v^c...c...w
+
+ given the way we pushed things on the vertex stack, path_v[0]
+ will be the point of attachement of the x-y path
+ on the v^c...x...w external face
+
+ path_e[0] will contain nothing: a dummy
+
+ path_e[1] will be the first edge in the x-y path
+ (and entry_in_path will give the in-direction to this edge)
+
+ oh yes!, we break the loop at this point if
+ the vertex s lies on the v^c...y...w external face
+ */
+ {
+ ASSERT(embed_graph[(*path_v)[0]].visited == mark_l);
+ /*
+ the first vertex on the path must be on the
+ v^c...x...w external face
+ */
+ (*path_v)[++(*nbr_v)] = s;
+ /*
+ and now we also push the edge on the edge stack
+
+ I'll need this later to initiate a proper face walk
+ starting at the first vertex/edge in the x-y path,
+ which is the same as starting from s_e
+ */
+ (*path_e)[*nbr_v] = s_e;
+ *entry_in_path_e = s_ein;
+ e = s_e;
+ ein = s_ein;
+
+ /*
+ since we are at the start of the path, we must not
+ forget to reset avoid_vv
+ */
+ avoid_vv = FALSE;
+
+ if (embed_graph[s].visited == mark_r
+ || embed_graph[s].visited == mark)
+ /*
+ we have reached the v^c...y...w external face:
+ we can stop here
+ */
+ {
+ break;
+ }
+
+ /*
+ if not finished yet,
+ we also mark s (and path_v[0]) as visited:
+ later on we'll need to recognise which of the vertices
+ in path have already been encountered
+ (in case of encountering a cut-vertex due to the
+ "removal" of the "internal" edges incidnet ot v^c)
+
+ note that we mark s as visited iff s if not already
+ on the v^c..y..w external face
+ */
+
+ ASSERT(embedg_VES_is_vertex(n, (*path_v)[0]));
+ ASSERT(embedg_VES_is_vertex(n, s));
+
+ embed_graph[s].visited = MARK_X_Y_PATH(n);
+ }
+ else if (embed_graph[s].visited == MARK_X_Y_PATH(n))
+ /*
+ this means that s is a cut vertex on the internal
+ face walk: pop all the vertices from path
+ until s's last occurrence in path
+ */
+ {
+ ASSERT((*nbr_v) >= 0);
+ while ((*path_v)[(*nbr_v)] != s)
+ {
+ (*nbr_v)--;
+ ASSERT((*nbr_v) >= 0);
+ /*
+ note that s should be somewhere in path!
+ */
+ }
+ /*
+ note also that popping from path_v also implies
+ popping from path_e
+ */
+ e = s_e;
+ ein = s_ein;
+ }
+ else
+ /*
+ we push s and s_e on their respective stacks
+ */
+ {
+ (*path_v)[++(*nbr_v)] = s;
+ (*path_e)[*nbr_v] = s_e;
+ e = s_e;
+ ein = s_ein;
+
+ if (embed_graph[s].visited == mark_r
+ || embed_graph[s].visited == mark)
+ /*
+ again, s lies on the v^c...y...w external face:
+ we end the walk: path_v now contains the highest x-y path
+
+ note that there can be no conflict between
+ mark_r or mark and MARK_X_Y_PATH(n) since
+ we mark with MARK_X_Y_PATH iff the vertex
+ is NOT marked with mark_r/mark!
+ */
+ {
+ break;
+ }
+ else
+ /*
+ we must mark this vertex as MARK_X_Y_PATH since we aren't
+ finished yet
+ */
+ {
+ embed_graph[s].visited = MARK_X_Y_PATH(n);
+ }
+ }
+ }
+
+ /*
+ there is only one thing remaining to do: see if p_x or
+ p_y are attached high
+ (ie closer to v^c than x or y resp.)
+
+ we walk the external face starting at v^c in y's direction
+ (again see embedg_iso_get_x_y_w)
+ */
+ *px_attached_high = TRUE;
+ p_x = (*path_v)[0];
+ /*
+ p_y denotes the attachement point of the x-y path
+ on the v^c...y...w external face
+ */
+
+ s = n;
+ cur_v = vv;
+ cur_vin = 0;
+ while (s != p_x)
+ {
+ embedg_VES_get_succ_on_ext_face(embed_graph, n,
+ cur_v, cur_vin,
+ FALSE, 0, &s, &sin);
+ if (s == x)
+ {
+ *px_attached_high = FALSE;
+ break;
+ }
+ cur_v = s;
+ cur_vin = sin;
+ }
+
+ *py_attached_high = TRUE;
+ p_y = (*path_v)[*nbr_v];
+ /*
+ p_y denotes the attachement point of the x-y path
+ on the v^c...y...w external face
+ */
+
+ s = n;
+ cur_v = vv;
+ cur_vin = 1;
+ while (s != p_y)
+ {
+ embedg_VES_get_succ_on_ext_face(embed_graph, n,
+ cur_v, cur_vin,
+ FALSE, 0, &s, &sin);
+ if (s == y)
+ {
+ *py_attached_high = FALSE;
+ break;
+ }
+ cur_v = s;
+ cur_vin = sin;
+ }
+
+ /*
+ now we are in the minor C case if either p_x or p_y are
+ attached high
+
+ the minor D case:
+ this happens when there is a path v^c - z where z lies
+ on the x-y path
+
+ that is, when
+
+ either v^c has been effectively "avoided" within the
+ embedg_VES_get_succ_on_proper_face_with_avoidance function
+ BUT ONLY if this "avoidance" happened AFTER having
+ encountered the very first vertex on the x-y path!
+
+ or when a cut vertex has been encountered on the x-y path:
+ separable components on this walk can only occur
+ if one walks the face while skipping the edges incident to v^c
+
+ in any case this means that checking the return from
+ the embedg_VES_get_succ_on_proper_face_with_avoidance function
+ is enough: this is the purpose of avoid_vv.
+ */
+
+ *is_minor_D = !(*px_attached_high || *py_attached_high) && avoid_vv;
+
+
+ IF_DEB(
+ int i;
+
+ fprintf(stdout, "x-y path\t");
+ for (i = 0; i <= *nbr_v; i++)
+ fprintf(stdout, "%d\t", (*path_v)[i]);
+ fprintf(stdout, "\n");
+ )
+}
+
+
+/*
+ * embedg_misc.c
+ */
+
+/*
+ What:
+ *****
+
+ Implementing:
+
+ Some high level routinse for the VES structure.
+ See VES_misc.c.
+
+
+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ from
+
+ Simplified O(n) Planarity Algorithms (draft)
+ ************************************
+
+ John Boyer JBoyer@PureEdge.com, jboyer@acm.org
+ Wendy Myrvold wendym@csr.uvic.ca
+
+
+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ authors:
+ ********
+
+ Paulette Lieby (Magma), Brendan McKay (ANU)
+
+ Started October 2001
+*/
+
+
+#include "planarity.h"
+
+#define IF_DEB(x) {}
+#define IF_VERB(x) {}
+#define IF_DEB_TREE(x) {}
+
+
+
+/* aproto: header embed_graph_protos.h */
+
+#ifndef PLANAR_IN_MAGMA
+#endif
+
+
+
+
+void
+embedg_VES_delete (t_ver_edge *embed_graph, int n)
+{
+ int i;
+
+ for (i = 0; i < n; i++)
+ {
+ embedg_dlcl_delete(embed_graph[i].separated_DFS_child_list);
+ /*
+ embedg_dlcl_delete(embed_graph[i].rep_in_parent_list);
+
+ NO!!! this points to something in separated_DFS_child_list
+ */
+ embedg_dlcl_delete(embed_graph[i].pertinent_bicomp_list);
+ }
+ mem_free(embed_graph);
+}
+
+
+
+void
+embedg_VES_print (t_ver_edge *embed_graph, int n)
+{
+ int i;
+
+ fprintf(stdout, "vertices\n");
+ for (i = 0; i < n; i++)
+ {
+ t_ver_edge rec;
+
+ rec = embed_graph[i];
+
+ fprintf(stdout, "\nDFI\t%d\tlabel\t%d\n", i, rec.label);
+ fprintf(stdout, "DFS parent\t%d\tleast_a\t%d\tlowpoint\t%d\n",
+ rec.DFS_parent, rec.least_ancestor, rec.lowpoint);
+ fprintf(stdout, "separated_DFS_child_list\n");
+ embedg_dlcl_print(rec.separated_DFS_child_list);
+ }
+
+ fprintf(stdout, "\nvirtual vertices\n");
+ for (i = n; i < 2*n; i++)
+ {
+ int c;
+
+ c = i - n;
+ fprintf(stdout, "%d^%d\t", embed_graph[c].DFS_parent, c);
+ }
+ fprintf(stdout, "\n");
+
+ embedg_VES_print_bigcomps(embed_graph, n);
+}
+
+
+void
+embedg_VES_print_bigcomps (t_ver_edge *embed_graph, int n)
+ /*
+ walking the external faces of all the bicomp; for testing only
+ */
+{
+ int i;
+
+ fprintf(stdout, "bicomponents\n");
+ /*
+ to get to the bicomps, it makes sense to start at the
+ virtual vertices????
+ */
+ for (i = n + 1; i < 2*n; i++)
+ /*
+ a note of caution: there is no virtual vertex at
+ embed_graph[n] since that would mean a virtual vertex x^0
+ which makes no sense (0 is the root of the dfs_tree)
+ */
+ {
+ embedg_VES_walk_bicomp(embed_graph, n, i, 0);
+ }
+ fprintf(stdout, "\n");
+}
+/*
+ * planar_alg_init.c
+ */
+
+/*
+ What:
+ *****
+
+ Implementing:
+
+ Initialising the embed_graph aka VES data structure from the information
+ collected from the DFS.
+
+ The embed_graph/VES data structure is an array consisting of vertices,
+ virtual vertices and edges;
+ vertices, virtual vertices and edges share a common record structure;
+ one of the particular features is that any vertex is linked
+ together with its incident edges into a doubly circular linked list.
+
+ See also VES_misc.c.
+
+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ from
+
+ Simplified O(n) Planarity Algorithms (draft)
+ ************************************
+
+ John Boyer JBoyer@PureEdge.com, jboyer@acm.org
+ Wendy Myrvold wendym@csr.uvic.ca
+
+
+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ authors:
+ ********
+
+ Paulette Lieby (Magma), Brendan McKay (ANU)
+
+ Started October 2001
+*/
+
+#include "planarity.h"
+
+#define IF_DEB(x) {}
+#define IF_DEB_DFS(x) {}
+#define IF_VERB(x) {}
+#define IF_DEB_TREE(x) {}
+#define IF_CPU(x) {}
+
+
+/* aproto: header embed_graph_protos.h */
+
+/* aproto: beginstatic -- don't touch this!! */
+static void embedg_init_insert_TE (t_ver_edge *, int, int *, t_dlcl *);
+/* aproto: endstatic -- don't touch this!! */
+
+#ifndef PLANAR_IN_MAGMA
+#endif
+
+
+t_ver_edge *
+embedg_planar_alg_init (
+ t_ver_sparse_rep *V,
+ int n,
+ t_adjl_sparse_rep *A, /* input sparse graph */
+ int *nbr_c, /* size of the graph, #components*/
+ int *edge_pos, /* pos in the struct where the last edge
+ has been inserted
+ */
+ t_dlcl ***dfs_tree, /* a sparse graph rep. for the dfs tree
+ -- vertices are as DFIs
+ */
+ t_dlcl ***back_edges, /* for each vertex v, a dlcl
+ of the back edges [v, x] incident to v
+ where x is a DESCENDANT of v
+ -- vertices are as DFIs
+ */
+ t_dlcl ***mult_edges /* for each vertex v, a dlcl
+ of the back edges [v, x] incident to v
+ where x is a DESCENDANT of v
+ -- vertices are as DFIs
+ */
+)
+ /*
+ initialising embed_graph, the fundamental data structure
+ underpinning the tester and obstruction isolator
+
+ from there on, a vertex is exclusively referred to by its DFI!!
+ -- so forget about labels
+ */
+{
+ int *dfs_nbr; /* dfs numbering for each vertex */
+ int *dfs_order; /* vertices in dfs order */
+ int *lowpoint; /* lowpoint value for each DFI */
+ int *dfs_parent; /* for each DFI, its DFS ancestor
+ as a DFI (DFS index)
+ */
+ int *least_a; /* for each DFI, its least ancestor's DFI
+ (via a back edge exclusively)
+ */
+
+ t_ver_edge *embed_graph;
+ int i;
+
+
+ IF_CPU(
+ float sttime; float time_to_now;
+
+ sttime = time_current_user();
+ )
+
+ ASSERT(n >= 1);
+
+ /*
+ DFS and lowpoint calculations + ordering
+ */
+ sparseg_adjl_dfs_preprocessing(V, n, A, nbr_c,
+ &dfs_nbr, &dfs_order, &lowpoint,
+ dfs_tree, back_edges,
+ &dfs_parent, &least_a, mult_edges);
+
+ IF_CPU(
+ fprintf(stdout, "CPU for DFS only %f\n",
+ (time_current_user() - sttime));
+ sttime = time_current_user();
+ )
+
+ IF_DEB_DFS(
+ fprintf(stdout, "DFS indices\n");
+ for (i = 0; i < n; i++)
+ fprintf(stdout, "%d ", dfs_nbr[i]);
+ fprintf(stdout, "\n");
+
+ fprintf(stdout, "DFS order\n");
+ for (i = 0; i < n; i++)
+ fprintf(stdout, "%d ", dfs_order[i]);
+ fprintf(stdout, "\n");
+
+ fprintf(stdout, "lowpoint values\n");
+ for (i = 0; i < n; i++)
+ fprintf(stdout, "%d ", lowpoint[i]);
+ fprintf(stdout, "\n");
+ );
+
+ IF_VERB(
+ fprintf(stdout, "DFS parent\n");
+ for (i = 0; i < n; i++)
+ fprintf(stdout, "%d ", dfs_parent[i]);
+ fprintf(stdout, "\n");
+ );
+
+ IF_VERB(
+ fprintf(stdout, "least ancestors\n");
+ for (i = 0; i < n; i++)
+ fprintf(stdout, "%d ", least_a[i]);
+ fprintf(stdout, "\n");
+ );
+
+ IF_VERB(
+ for (i = 0; i < n; i++)
+ {
+ fprintf(stdout, "the list of children ordered by lowpoint for %d\n",
+ i);
+ embedg_dlcl_print((*dfs_tree)[i]);
+ }
+ );
+
+ IF_DEB_DFS(
+ fprintf(stdout, "the tree edges\n");
+ sparseg_dlcl_print(*dfs_tree, n);
+
+ fprintf(stdout, "the back edges\n");
+ sparseg_dlcl_print(*back_edges, n);
+
+ fprintf(stdout, "multiple edges\n");
+ sparseg_dlcl_print(*mult_edges, n);
+ );
+
+ /*
+ create the data structure for the embedded graph:
+ it will have (max) size 2*n + 2 * MAXE(n)
+
+ we will see that that number of edges is sufficient
+ even when later adding short-cut edges (see embedg_walkdown)
+ */
+ embed_graph = (t_ver_edge *) mem_malloc(sizeof(t_ver_edge)
+ * (2*n + 2 * MAXE(n)));
+ /*
+ initialisation
+ */
+ for (i = 0; i < 2*n + 2 * MAXE(n); i++)
+ /*
+ some fields are initialised to n as n is actually
+ an "invalid" value
+ */
+ {
+ t_ver_edge rec;
+
+ rec.label = NIL;
+ rec.DFS_parent = n;
+ rec.least_ancestor = n;
+ rec.lowpoint = n;
+ rec.separated_DFS_child_list = NP;
+ rec.rep_in_parent_list = NP;
+ rec.pertinent_bicomp_list = NP;
+ rec.adjacent_to = n;
+ rec.visited = n;
+ rec.neighbour = n;
+ rec.in_adjl = NIL;
+ rec.twin_in_adjl = NIL;
+ rec.mult = 0;
+ rec.type = NIL;
+ rec.sign = NILSIGN;
+ /*
+ make the links refer back to self
+ */
+ rec.link[0] = rec.link[1] = i;
+
+ embed_graph[i] = rec;
+ }
+
+ /*
+ embed_graph[0..n-1]: the n vertices
+ ATTENTION: the vertices are stored according to their DFS numbering
+ */
+ for (i = 0; i < n; i++)
+ {
+ t_ver_edge rec;
+
+ rec = embed_graph[i];
+
+ rec.label = dfs_order[i];
+ rec.DFS_parent = dfs_parent[i];
+ rec.least_ancestor = least_a[i];
+ rec.lowpoint = lowpoint[i];
+ rec.separated_DFS_child_list = embedg_dlcl_copy((*dfs_tree)[i]);
+
+ IF_VERB(
+ fprintf(stdout, "the list of children ordered by lowpoint for DFI %d\n",
+ i);
+ embedg_dlcl_print(rec.separated_DFS_child_list);
+ );
+
+ embed_graph[i] = rec;
+ }
+
+ /*
+ one more thing to do for these vertices:
+ fix the rep_in_parent_list field
+ */
+ for (i = 1; i < n; i++)
+ {
+ t_dlcl *parent_list, *rep;
+ int parent;
+
+ parent = embed_graph[i].DFS_parent; /* careful: this is a DFI */
+ /*
+ recall that the vertices in embed_graph are accessed via their DFI
+ */
+
+ if (parent != n)
+ /*
+ when parent == n this means that i the root of a DFS tree
+ in the disconnected graph
+ */
+ {
+ parent_list = embed_graph[parent].separated_DFS_child_list;
+ rep = embedg_dlcl_find(parent_list, i);
+ ASSERT(rep != NP);
+ embed_graph[i].rep_in_parent_list = rep;
+ }
+ }
+
+ /*
+ embed_graph[n..2*n-1]: the n virtual vertices
+ do I need to do anything here?????
+
+ no - I don't think so
+
+ let's try to explain what virtual vertices are:
+ let v^c be a virtual vertex:
+ - it is at position c + n in the array,
+ - c is the DFS child of v,
+ - v can be retrieved by taking embed_graph[c].DFS_parent,
+ - v^c is said virtual as long as the bicomp rooted by v^c is not
+ merged with the vertex v
+ - once v is merged (identified?) with v^c, then v^c
+ is of no relevance anymore
+
+ below we will see that we embed all the tree edges as singleton
+ bicomps (bicomponent): (0^1, 1), (1^2, 2) etc...:
+ this is what virtual vertices are there for:
+ to distinguish them from their "real" counterpart with
+ which they will be ultimately merged
+
+ the primary reason for this is:
+ while testing for planarity virtual vertices are the roots of bicomps
+ */
+
+ /*
+ now the edges:
+ we actually embed the tree edges so that each tree edge
+ forms a (singleton) biconnected component
+
+ embedding an edge in effect means creating the
+ doubly linked circular list of [virtual] vertices & the edges incident
+ to it
+
+ this list is built using the links 0 & 1 in embed_graph[i]
+ */
+
+ /*
+ for each tree edge (v,u) we embed (v^u, u) (v^u is the virtual vertex)
+
+ CAREFUL: when talking about vertex v say,
+ we mean the vertex with DFI v, and NOT the vertex with label v
+ **************************************************************
+ */
+ *edge_pos = 2*n - 1;
+ /*
+ edge_pos will tell us where to insert the next edge in embed_graph[]
+ */
+ for (i = 0; i < n; i++)
+ {
+ t_dlcl *te_l, *p;
+
+ te_l = (*dfs_tree)[i];
+ p = te_l;
+
+ if (!embedg_dlcl_is_empty(p))
+ {
+ /*
+ the test below is a bit stupid... well...
+ */
+ ASSERT(embed_graph[p->info].DFS_parent == i);
+
+ embedg_init_insert_TE(embed_graph, n, edge_pos, p);
+ p = embedg_dlcl_list_next(p);
+ while (p != te_l)
+ {
+ ASSERT(embed_graph[p->info].DFS_parent == i);
+ embedg_init_insert_TE(embed_graph, n, edge_pos, p);
+
+ p = embedg_dlcl_list_next(p);
+ }
+ }
+ }
+
+ mem_free(dfs_nbr);
+ mem_free(dfs_order);
+ mem_free(lowpoint);
+
+ mem_free(dfs_parent);
+ mem_free(least_a);
+
+ IF_CPU(
+ fprintf(stdout, "CPU for remainder of initialisation %f\n",
+ (time_current_user() - sttime));
+ )
+
+ return embed_graph;
+}
+
+
+static void
+embedg_init_insert_TE (t_ver_edge *embed_graph, int n, int *edge_pos, t_dlcl *p)
+ /*
+ init and insert a tree edge in embed graph:
+
+ the tree edge will form a singleton bicomponent (v^c, c)
+ where c is p->info and v is c.DFS_parent
+ */
+{
+ int c, v;
+
+ c = p->info;
+ v = embed_graph[c].DFS_parent;
+ ASSERT(v >= 0 && v < n);
+
+ /*
+ now (v, c) is a tree edge; embed the directed edge [v^c, c]
+
+ -- and recall that v^c is a virtual vertex, at position c + n
+ in embed_graph, and that vertex c is at position c
+ */
+
+ /*
+ first, set this edge with the appropriate info
+ */
+ (*edge_pos)++;
+ ASSERT(*edge_pos < 2*n + 2 * MAXE(n));
+ embed_graph[*edge_pos].neighbour = c;
+ embed_graph[*edge_pos].in_adjl = p->in_adjl;
+ embed_graph[*edge_pos].twin_in_adjl = p->twin_in_adjl;
+
+ ASSERT(p->mult % 2 == 0);
+ /*
+ we want the number of undirected edges
+ */
+ embed_graph[*edge_pos].mult = p->mult / 2;
+ embed_graph[*edge_pos].type = TE;
+ embed_graph[*edge_pos].sign = CCLOCKW;
+
+ /*
+ link this with vertex v^c in a doubly linked circular list
+ */
+ embed_graph[c + n].link[0] =
+ embed_graph[c + n].link[1] = *edge_pos;
+ embed_graph[*edge_pos].link[0] =
+ embed_graph[*edge_pos].link[1] = c + n;
+
+ /*
+ now create/set the twin edge, the directed edge [c, v^c]
+ */
+ (*edge_pos)++;
+ ASSERT(*edge_pos < 2*n + 2 * MAXE(n));
+ embed_graph[*edge_pos].neighbour = c + n;
+ embed_graph[*edge_pos].in_adjl = p->twin_in_adjl;
+ embed_graph[*edge_pos].twin_in_adjl = p->in_adjl;
+ embed_graph[*edge_pos].mult = p->mult / 2;
+ embed_graph[*edge_pos].type = TE;
+ embed_graph[*edge_pos].sign = CCLOCKW;
+
+ /*
+ and link it with vertex c in a doubly linked circular list
+ */
+ embed_graph[c].link[0] = embed_graph[c].link[1] = *edge_pos;
+ embed_graph[*edge_pos].link[0] =
+ embed_graph[*edge_pos].link[1] = c;
+}
+/*
+ * dfs_preprocessing.c
+ */
+
+/*
+ What:
+ *****
+
+ Implementing:
+
+ A DFS as an initialisation step for the planarity tester.
+ This is an especially beefed up DFS that collects lots of
+ marginal information:
+
+ - a DFS tree as a list of DFS children for each vertex
+ - the DFS children are sorted according to their lowpoint value
+ - a back_edge structure as a list of descendants v for each
+ vertex u such that [v, u] is a back edge
+ - a multiple edges structure which stores multiple (directed) edges
+ NOT in the DFS tree nor in the back_edge struc, and loops
+
+ - the vertices in DFS order
+ - the DFS index (DFI) for each vertex
+ - the lowpoint value for each vertex
+ - the number of components of the (possibly disconnected) graph
+ - for each vertex, its DFS parent
+ - for each vertex v, its least ancestor u such that [v, u]
+ is a back edge
+
+ ALL info above (except the vertices in DFS order) is given
+ in terms of the vertices' DFIs and NOT their labels.
+
+
+
+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ from
+
+ Simplified O(n) Planarity Algorithms (draft)
+ ************************************
+
+ John Boyer JBoyer@PureEdge.com, jboyer@acm.org
+ Wendy Myrvold wendym@csr.uvic.ca
+
+
+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ authors:
+ ********
+
+ Paulette Lieby (Magma), Brendan McKay (ANU)
+
+ Started October 2001
+*/
+
+
+/*
+ There are some dodgy things which need some thought; it would be nice
+ to fix them so that the code get's cleaner:
+
+ - we do store in_adj (and twin_in_adjl) for each directed edge:
+
+ + this is ONLY needed at the time of recovering the embedding
+ (see embedg_recover_embedding) and its sole use is to establish
+ a link between an edge in the embedding structure
+ t_embed_sparse_rep *E and the corresponding edge
+ in the t_adjl_sparse_rep *A struc.
+
+ + well, I cannot recall why I thought this correspondence
+ was needed in the first place and it might well be the case
+ that there is no use for it; in which case recovering the
+ embedding is simplified
+ (we would store the end-vertex in the embedding's edges instead
+ of their index in the adjacency list)
+
+ - there are some non-linear bits in the DFS below: when searching
+ for an already existing tree/back/multiple edge.
+ I couldn't fix this in less then one hour so I leave it as it is...
+ for now.
+
+ This shouldn't be a major issue, overall timings of the planarity
+ tester do not show this non-linear "bump"...
+
+ - also, this algorithm has been growing incrementally and I now
+ realise that I am using some redundant data structures:
+ for example visited[] and the vertex and could be dispensed with...
+ ...more things to clean up...
+
+ Paulette 07/02/02
+*/
+
+
+#include "planarity.h"
+
+#define IF_DEB(x) {}
+#define IF_VERB(x) {}
+#define IF_DEB_TREE(x) {}
+
+
+/* aproto: header embed_graph_protos.h */
+
+#ifndef PLANAR_IN_MAGMA
+#endif
+
+
+void
+sparseg_adjl_dfs_preprocessing (
+ t_ver_sparse_rep *V,
+ int n, /* size of the graph */
+ t_adjl_sparse_rep *A, /* input sparse graph */
+ int *c, /* nbr of components */
+ int **dfs_nbr, /* dfs numbering for each vertex */
+ int **dfs_order, /* vertices in dfs order */
+ int **lowpoint, /* lowpoint value for each DFI */
+ t_dlcl ***dfs_tree, /* a sparse graph rep. for the dfs tree:
+ for each DFI, a list of its children's
+ DFI ordered wrt their lowpoint values
+ */
+ t_dlcl ***back_edges, /* for each DFI v, a dlcl
+ of the back edges [v, x] incident to v
+ where x is a DESCENDANT of v */
+ int **dfs_parent, /* for each DFI its DFS ancestor */
+ int **least_a, /* for each DFI, its least ancestor's DFI
+ via a back edge exclusively */
+ t_dlcl ***mult_edges /* for each DFI v, a dlcl
+ of the multiple directed
+ edges NOT included
+ in either dfs_tree or back_edges
+ */
+)
+
+ /*
+ in ALL the returned info above BUT dfs_order[] we store
+ the vertices' DFIs (DFS indices) and NOT their labels!
+
+ -- shuffling between labels and vertices can then be done
+ via dfs_nbr[] and dfs_order[]
+ */
+{
+ int pos_v_stack, pos_e_stack, dfs_n;
+ int *visited, *vertex_stack, *edge_stack, *lowpoint_order;
+ int *TE_in_adjl, *TE_twin_in_adjl, *TE_mult;
+ int v, lp, cur, cur_e, next;
+ t_dlcl **temp, *lowpoint_list, **new_dfs_tree;
+
+ /*
+ create the dfs tree as a sparse graph
+ */
+ *dfs_tree = (t_dlcl **) mem_malloc(sizeof(t_dlcl *) * n);
+ /*
+ the DFS numbering for the vertices
+ */
+ *dfs_nbr = (int *) mem_malloc(sizeof(int) * n);
+ /*
+ the vertices as ordered by their DFS index
+ */
+ *dfs_order = (int *) mem_malloc(sizeof(int) * n);
+ /*
+ the lowpoint value for each DFI
+ */
+ *lowpoint = (int *) mem_malloc(sizeof(int) * n);
+
+ /*
+ the (directed) back edges
+ */
+ *back_edges = (t_dlcl **) mem_malloc(sizeof(t_dlcl *) * n);
+
+
+ /*
+ the DFS parent for each DFI
+ */
+ *dfs_parent = (int *) mem_malloc(sizeof(int) * n);
+ /*
+ the least ancestor (via a back edge exlusively) for each DFI
+ */
+ *least_a = (int *) mem_malloc(sizeof(int) * n);
+
+ /*
+ the (directed) multiple edges
+ */
+ *mult_edges = (t_dlcl **) mem_malloc(sizeof(t_dlcl *) * n);
+
+ /*
+ the vertices visited while DFS
+ */
+ visited = (int *) mem_malloc(sizeof(int) * n);
+ /*
+ stack of vertices: last current vertex
+ */
+ vertex_stack = (int *) mem_malloc(sizeof(int) * n);
+ /*
+ stack of (tree) edges: last added tree edge
+ */
+ edge_stack = (int *) mem_malloc(sizeof(int) * n);
+
+ /*
+ the following will be used in order to recreate the dfs_tree
+ so that the DFS children of each DFI are ordered
+ according to their lowpoint value
+ */
+ lowpoint_order = (int *) mem_malloc(sizeof(int) * n);
+ temp = (t_dlcl **) mem_malloc(sizeof(t_dlcl *) * n);
+ new_dfs_tree = (t_dlcl **) mem_malloc(sizeof(t_dlcl *) * n);
+
+ /*
+ finally, three more holding arrays: a trick to remember which
+ tree edges we are talking about:
+
+ when constructing dfs_tree, back_edges, mult_edges
+ - we NEED to record the index in A (the adjacency list)
+ of some of the edges and their twins/inverses
+ we are currently storing in either of these structures
+ - we also need to record the number of multiple (directed)
+ edges we encounter when the graph is not simple
+
+ this is easy to do when storing back edges and multiple edges,
+ and tree edges also: but this lattest set of neighbour lists (dfs_tree)
+ is subsequently reordered so that DFS children are ordered
+ wrt lowpoint values;
+ - consequently the info about position in adjacency list
+ and edge multiplicity are lost in the ordering process
+
+ the two following arrays will remember the info we'll need later
+ - more about this below
+ */
+ TE_in_adjl = (int *) mem_malloc(sizeof(int) * n);
+ TE_twin_in_adjl = (int *) mem_malloc(sizeof(int) * n);
+ TE_mult = (int *) mem_malloc(sizeof(int) * n);
+
+
+ /*
+ initialization of the data structures
+ */
+ for (v = 0; v < n; v++)
+ {
+ (*dfs_tree)[v] = (*back_edges)[v] = (*mult_edges)[v] = NP;
+ visited[v] = TE_mult[v] = 0;
+ (*dfs_parent)[v] = (*least_a)[v] = n;
+ temp[v] = new_dfs_tree[v] = NP;
+ TE_in_adjl[v] = TE_twin_in_adjl[v] = NIL;
+ /*
+ note that in the 3rd last statement n is considered
+ as an "invalid" value;
+ will be if importance in the overall algorithm
+ */
+ }
+
+ /*
+ the DFS tree is rooted at vertex 0
+ */
+ dfs_n = -1;
+ pos_v_stack = -1;
+ pos_e_stack = -1;
+ *c = 0;
+ for (v = 0; v < n; v++)
+ {
+ if (visited[v])
+ /*
+ we come only at this level when looking for
+ a new subtree (when graph is disconnected)
+ */
+ {
+ continue;
+ }
+ else
+ {
+ (*c)++;
+ }
+
+ cur = v;
+ visited[cur] = 1;
+ (*dfs_nbr)[cur] = ++dfs_n;
+ (*lowpoint)[(*dfs_nbr)[cur]] = dfs_n;
+ (*dfs_order)[dfs_n] = cur;
+
+ cur_e = V[cur].first_edge == NIL ? NIL : V[cur].first_edge;
+ while (TRUE)
+ {
+ if (cur_e != NIL)
+ {
+ t_dlcl *existing_e;
+
+ next = A[cur_e].end_vertex;
+ if (!visited[next])
+ /*
+ adding tree edges (careful: directed edges)
+
+ AND tree edges are stored as
+ [dfs_nbr[u], dfs_nbr[cv]]
+ instead of [u, cv]: that is we store the edges
+ according to the vertices' DFIs
+ */
+ {
+ IF_DEB_TREE(
+ io_printf("add tree edge %d\t%d\n",
+ cur+1, next+1);
+ );
+
+ (*dfs_nbr)[next] = ++dfs_n;
+ (*lowpoint)[(*dfs_nbr)[next]] = dfs_n;
+ (*dfs_order)[dfs_n] = next;
+
+ sparseg_dlcl_append_to_neigh_list(*dfs_tree, n,
+ (*dfs_nbr)[cur],
+ (*dfs_nbr)[next],
+ NIL);
+ TE_in_adjl[(*dfs_nbr)[next]] = cur_e;
+ TE_mult[(*dfs_nbr)[next]]++;
+
+ /*
+ we push cur and the edge (cur, cur_e) on their
+ respective stacks
+ */
+ vertex_stack[++pos_v_stack] = cur;
+ edge_stack[++pos_e_stack] = cur_e;
+
+ /*
+ and mark next as visited
+ */
+ visited[next] = 1;
+
+ /*
+ update dfs_parent (always deal with DFIs rembember!)
+ */
+ (*dfs_parent)[(*dfs_nbr)[next]] = (*dfs_nbr)[cur];
+
+ /*
+ the DFS goes one level deeper
+ */
+ cur = next;
+ cur_e = V[cur].first_edge == NIL ?
+ NIL : V[cur].first_edge;
+ }
+ /*
+ the next three tests deal with multiple edges
+ and loops: apart from storing these (DIRECTED) edges
+ in mult_edges, we also need to update
+ the multipliciaty information about these edges
+ */
+ else if (sparseg_dlcl_is_adjacent(*dfs_tree, n,
+ (*dfs_nbr)[cur],
+ (*dfs_nbr)[next],
+ &existing_e))
+ /*
+ [cur, next] is a tree edge
+ */
+ {
+ sparseg_dlcl_append_to_neigh_list(*mult_edges, n,
+ (*dfs_nbr)[cur],
+ (*dfs_nbr)[next],
+ cur_e);
+ TE_mult[(*dfs_nbr)[next]]++;
+
+ cur_e = A[cur_e].next; /* next in cur's adjacency list */
+ }
+ else if (sparseg_dlcl_is_adjacent(*back_edges, n,
+ (*dfs_nbr)[next],
+ (*dfs_nbr)[cur],
+ &existing_e))
+ /*
+ [cur, next] is a back edge
+ */
+ {
+ sparseg_dlcl_append_to_neigh_list(*mult_edges, n,
+ (*dfs_nbr)[cur],
+ (*dfs_nbr)[next],
+ cur_e);
+ (existing_e->mult)++;
+
+ cur_e = A[cur_e].next; /* next in cur's adjacency list */
+ }
+ else if (next == cur)
+ /*
+ the case of a loop
+ */
+ {
+ if (sparseg_dlcl_is_adjacent(*mult_edges, n,
+ (*dfs_nbr)[next],
+ (*dfs_nbr)[cur],
+ &existing_e))
+ /*
+ in this case we must update the multiplicity
+ of this edge: note that the elt. in cur's
+ neighbours list that gets updated is the first
+ in the list
+
+ dodgy??? certainly, but can't think
+ of a better way to do this
+
+ eventually it will happen that even myself
+ won't understand what I am doing..........
+ */
+ {
+ (existing_e->mult)++;
+ }
+ sparseg_dlcl_append_to_neigh_list(*mult_edges, n,
+ (*dfs_nbr)[cur],
+ (*dfs_nbr)[next],
+ cur_e);
+
+ cur_e = A[cur_e].next; /* next in cur's adjacency list */
+ }
+ else if (sparseg_dlcl_is_adjacent(*dfs_tree, n,
+ (*dfs_nbr)[next],
+ (*dfs_nbr)[cur],
+ &existing_e))
+ /*
+ [next, cur] is a tree edge:
+ that is, [cur, next] is [next, cur]'s twin/inverse:
+
+ 1. if it is the first time one encounters
+ [cur, next] (as it would always be the case
+ for a simple graph) then all I need to do
+ is to update the tree edge's multiplicity,
+ and the twin info in TE_[]
+
+ 2. if [cur, next] is actually a multiple edge,
+ then I'll need to store it in mult_edges;
+ and I update the tree edge's multiplicity too.
+ No twin info will be required here.
+ Why? see how recover.c embeds the multiple
+ edges in the planar embedding.
+
+ 3. how do I know it is the first time I encounter
+ [cur, next]?:
+ when TE_twin_in_adjl = NIL
+
+ 4. finally, note that the present counting scheme
+ implies that the mult field always holds
+ the number of directed edges:
+ ie, if [a, b] is a tree edge, [a, b].mult = 2
+ because we would have counted [a, b] and [b, a]
+
+ this applies to tree edges, back edges, and loops
+ */
+ {
+ ASSERT(TE_in_adjl[(*dfs_nbr)[cur]] != NIL);
+ if (TE_twin_in_adjl[(*dfs_nbr)[cur]] == NIL)
+ {
+ TE_twin_in_adjl[(*dfs_nbr)[cur]] = cur_e;
+ }
+ else
+ {
+ sparseg_dlcl_append_to_neigh_list(*mult_edges, n,
+ (*dfs_nbr)[cur],
+ (*dfs_nbr)[next],
+ cur_e);
+ }
+
+ TE_mult[(*dfs_nbr)[cur]]++;
+
+ cur_e = A[cur_e].next; /* next in cur's adjacency list */
+ }
+ else if (sparseg_dlcl_is_adjacent(*back_edges, n,
+ (*dfs_nbr)[cur],
+ (*dfs_nbr)[next],
+ &existing_e))
+ /*
+ [next, cur] is a back edge: [cur, next] is its inverse:
+ we proceed as for the tree edge case above
+ */
+ {
+ ASSERT(existing_e->in_adjl != NIL);
+ if (existing_e->twin_in_adjl == NIL)
+ {
+ existing_e->twin_in_adjl = cur_e;
+ }
+ else
+ {
+ sparseg_dlcl_append_to_neigh_list(*mult_edges, n,
+ (*dfs_nbr)[cur],
+ (*dfs_nbr)[next],
+ cur_e);
+ }
+
+ (existing_e->mult)++;
+
+ cur_e = A[cur_e].next; /* next in cur's adjacency list */
+ }
+ /*
+ the next bit concludes the DFS: it deals with the case
+ where a back edge needs to be added
+ */
+ else
+ /*
+ that is, next is visited and neither
+ the tree edge [next, cur] nor
+ the back edge [next, cur] exist:
+
+ this implies that [cur, next] is a back edge
+ that must be added to the back_edges structure
+ (with dfs_nbr(next) < dfs_nbr(cur))
+ */
+ {
+ IF_DEB_TREE(
+ io_printf("add back edge %d\t%d\n",
+ cur+1, next+1);
+ );
+
+ ASSERT(visited[next]);
+ ASSERT((*dfs_nbr)[cur] > (*dfs_nbr)[next]);
+
+ sparseg_dlcl_append_to_neigh_list(*back_edges, n,
+ (*dfs_nbr)[next],
+ (*dfs_nbr)[cur],
+ cur_e);
+
+ /*
+ update cur's lowpoint
+ */
+ (*lowpoint)[(*dfs_nbr)[cur]] =
+ (*dfs_nbr)[next] < (*lowpoint)[(*dfs_nbr)[cur]] ?
+ (*dfs_nbr)[next] : (*lowpoint)[(*dfs_nbr)[cur]];
+
+ /*
+ update least_a (of cur)
+ (always deal with DFIs remember!)
+ */
+ (*least_a)[(*dfs_nbr)[cur]] =
+ (*dfs_nbr)[next] < (*least_a)[(*dfs_nbr)[cur]] ?
+ (*dfs_nbr)[next] : (*least_a)[(*dfs_nbr)[cur]];
+
+ /*
+ get the next edge in cur's adjacency list
+ */
+ cur_e = A[cur_e].next;
+ }
+ }
+
+ if (cur_e == NIL)
+ /*
+ we are either at a leaf or have finished scanning
+ cur's adjacency list: backtrack
+ */
+ {
+ if (pos_v_stack == -1) /* no previous vertex */
+ {
+ /*
+ no edge left on the stack: DFS ends for
+ this subtree:
+ we visit the next vertex
+ */
+ ASSERT(pos_e_stack == -1);
+ break;
+ }
+ else
+ {
+ int prev_e;
+ /*
+ Otherwise backtrack and pop cur from the stack
+ as well as the last tree edge added to the tree.
+ We use next to get a new lowpoint value for cur:
+ This value will be min(lowpoint(cur), lowpoint(next)).
+ */
+ cur = vertex_stack[pos_v_stack--];
+ prev_e = edge_stack[pos_e_stack--];
+ next = A[prev_e].end_vertex;
+ (*lowpoint)[(*dfs_nbr)[cur]] =
+ (*lowpoint)[(*dfs_nbr)[cur]]
+ < (*lowpoint)[(*dfs_nbr)[next]] ?
+ (*lowpoint)[(*dfs_nbr)[cur]]
+ : (*lowpoint)[(*dfs_nbr)[next]];
+
+ cur_e = A[prev_e].next;
+ }
+ /*
+ we proceed with DFS
+ */
+ }
+ }
+ }
+ mem_free(vertex_stack);
+ mem_free(edge_stack);
+
+ /*
+ just for the sake of it, check that all vertices have
+ been visited
+ */
+#ifdef ASSERTIONS
+ for (v = 0; v < n; v++)
+ {
+ ASSERT(visited[v]);
+ }
+#endif
+ mem_free(visited);
+
+ /*
+ we now order the DFIs wrt lowpoint values:
+ use bucket sort (linear time)
+ */
+ /*
+ for each lowpoint value, collect the DFIs (in a t_dlcl)
+ with that lowpoint value
+ (IMPORTANT: we want the DFIs since the aim is to rewrite dfs_tree
+ which stores DFIs and not labels!)
+ */
+ for (v = 0; v < n; v++)
+ /*
+ v is taken as a DFI here
+ */
+ {
+ t_dlcl *r;
+
+ r = embedg_dlcl_rec_new(v);
+ temp[(*lowpoint)[v]] =
+ embedg_dlcl_rec_append(temp[(*lowpoint)[v]], r);
+ }
+
+ /*
+ concatenate these lists now
+ */
+ lowpoint_list = temp[0];
+ for (lp = 1; lp < n; lp++)
+ {
+ lowpoint_list = embedg_dlcl_cat(lowpoint_list, temp[lp]);
+ }
+ ASSERT(embedg_dlcl_length(lowpoint_list) == n);
+
+ lowpoint_order[0] = lowpoint_list->info;
+ for (lp = 1; lp < n; lp++)
+ {
+ lowpoint_list = embedg_dlcl_list_next(lowpoint_list);
+ lowpoint_order[lp] = lowpoint_list->info;
+ }
+ embedg_dlcl_delete(lowpoint_list);
+ mem_free(temp);
+
+ IF_DEB(
+ fprintf(stdout, "dfs_preprocessing, lowpoint_order\n");
+ for (lp = 0; lp < n; lp++)
+ fprintf(stdout, "%d ", lowpoint_order[lp]);
+ fprintf(stdout, "\n");
+ fprintf(stdout, "dfs_preprocessing, lowpoint\n");
+ for (lp = 0; lp < n; lp++)
+ fprintf(stdout, "%d ", (*lowpoint)[lp]);
+ fprintf(stdout, "\n");
+ )
+
+ /*
+ we now use this order to rewrite dfs_tree such that
+ the DFS children of each vertex are ordered wrt lowpoint values
+ */
+ for (lp = 0; lp < n; lp ++)
+ /*
+ for each DFI in lowpoint_order[] I know its DFS_parent
+ from dfs_parent[] -- the rest is then trivial
+ */
+ {
+ int parent;
+
+ v = lowpoint_order[lp];
+ /*
+ lowpoint_order stores DFIs as does dfs_parent, so the lot
+ makes sense
+ */
+ parent = (*dfs_parent)[v];
+ if (parent != n)
+ /*
+ v may be the root of a DFS tree
+ */
+ {
+ t_dlcl *temp;
+
+ temp = embedg_dlcl_rec_new(v);
+
+ /*
+ this is where the TE_ holding arrays are useful *sigh*
+ */
+ ASSERT(TE_in_adjl[v] != NIL);
+ temp->in_adjl = TE_in_adjl[v];
+
+ ASSERT(TE_twin_in_adjl[v] != NIL);
+ temp->twin_in_adjl = TE_twin_in_adjl[v];
+
+ ASSERT(TE_mult[v] != 0 && TE_mult[v] % 2 == 0);
+ temp->mult = TE_mult[v];
+
+ new_dfs_tree[parent] =
+ embedg_dlcl_rec_append(new_dfs_tree[parent], temp);
+ }
+ }
+ mem_free(lowpoint_order);
+ mem_free(TE_in_adjl);
+ mem_free(TE_twin_in_adjl);
+ mem_free(TE_mult);
+
+ /*
+ some checks are in order here
+ */
+#ifdef ASSERTIONS
+ for (v = 0; v < n; v++)
+ {
+ ASSERT(embedg_dlcl_length((*dfs_tree)[v])
+ == embedg_dlcl_length(new_dfs_tree[v]));
+
+ IF_DEB(
+ fprintf(stdout, "dfs_preprocessing dfs_tree for %d\n", v);
+ embedg_dlcl_print((*dfs_tree)[v]);
+ fprintf(stdout, "dfs_preprocessing new_dfs_tree for %d\n", v);
+ embedg_dlcl_print(new_dfs_tree[v]);
+ );
+ }
+#endif
+
+ sparseg_dlcl_delete(*dfs_tree, n);
+ *dfs_tree = new_dfs_tree;
+}
+
+/*
+ * embedding.c
+ */
+
+/*
+ What:
+ *****
+
+ Implementing:
+
+ The graph is planar: we recover the embedding from the VES structure
+ and check it as well.
+ (Some of these checks will disappear later)
+
+
+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ from
+
+ Simplified O(n) Planarity Algorithms (draft)
+ ************************************
+
+ John Boyer JBoyer@PureEdge.com, jboyer@acm.org
+ Wendy Myrvold wendym@csr.uvic.ca
+
+
+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ authors:
+ ********
+
+ Paulette Lieby (Magma), Brendan McKay (ANU)
+
+ Started October 2001
+*/
+
+
+#include "planarity.h"
+
+#define IF_DEB(x) {}
+#define IF_DEB_EMBED(x) {}
+#define IF_DEB_CHECK_EMBED(x) {}
+#define IF_DEB_FACES(x) {}
+#define IF_VERB(x) {}
+#define IF_DEB_SCE(x) {}
+#define IF_CPU(x) {}
+
+
+/* aproto: header embed_graph_protos.h */
+
+
+#ifndef PLANAR_IN_MAGMA
+#endif
+
+void
+embedg_embedding (t_ver_sparse_rep *V, t_adjl_sparse_rep *A,
+ t_ver_edge *embed_graph, int n, int e, int nbr_c,
+ int edge_pos, t_dlcl **mult_edges, t_ver_sparse_rep **vertices,
+ t_embed_sparse_rep **embedding)
+ /*
+ recovering the embedding for the (planar) graph
+
+ - the embedding is returned in vertices and embedding, vertices
+ indexes embedding, the ordered list of edges
+ - edges in the embedding are given as their index in A, the graph's
+ adajacency list
+ - the nbr of edges in the embedding is given as nbr_e_embed:
+ this may be different form the original number of edges when the graph
+ iss not simple
+ */
+{
+ int *ver_orient, nbr_comp, nbr_e_embed;
+
+ IF_CPU(
+ float sttime; float time_to_now;
+
+ sttime = time_current_user();
+ )
+
+ IF_DEB(
+ fprintf(stdout, "embedding, begin, which edges have been flipped\n");
+ embedg_VES_print_flipped_edges(embed_graph, n, edge_pos);
+ )
+
+ IF_DEB(
+ fprintf(stdout, "embedding, before removing SCE\n");
+ embedg_VES_print_bigcomps(embed_graph, n);
+ )
+
+ /*
+ several things to do:
+ 1. removing the short-cut edges
+ */
+ embedg_remove_SCE(embed_graph, n, edge_pos);
+
+ IF_DEB(
+ fprintf(stdout, "embedding, after removing SCE\n");
+ embedg_VES_print_bigcomps(embed_graph, n);
+ )
+
+ /*
+ 2. computing each vertex's orientation (wrt flipped bicomps)
+ */
+ ver_orient = embedg_vertices_orientation(embed_graph, n);
+
+
+ /*
+ 3. merging the remaining virtual vertices with their
+ non-virtual counterpart
+ */
+ nbr_comp = embedg_merge_remaining_virtual(embed_graph, n);
+ /*
+ actually there is no need to return the nbr of components
+ from the above function
+ but let's do it for the sake of it and for possible checking
+ */
+ ASSERT(nbr_c == nbr_comp);
+
+ IF_DEB(
+ fprintf(stdout, "embedding, after merging of remaining vertices\n");
+ )
+
+ /*
+ 4. to be on the safe side: check that the embedding is a valid one
+
+ for now, we DIE if not
+ */
+
+ if (!embedg_is_embed_valid(embed_graph, n, nbr_comp, edge_pos,
+ ver_orient, &nbr_e_embed))
+ {
+ mem_free(ver_orient);
+ DIE();
+ }
+ mem_free(ver_orient);
+
+ ASSERT(nbr_e_embed <= e);
+ /*
+ when the graph is not simple, multiple edges and loops are
+ not in embed_graph[]: they will be added to the final
+ embedding in embedg_recover_embedding below
+ */
+
+ /*
+ 5. recover the embedding in preparation for the Magma type,
+ and check it as well
+ */
+ embedg_recover_embedding(V, A, embed_graph, n, e,
+ mult_edges, vertices, embedding);
+ if (!embedg_check_recov_embedding(n, e, nbr_comp,
+ *vertices, A, *embedding))
+ {
+ mem_free(*vertices);
+ mem_free(*embedding);
+
+ IF_CPU(
+ fprintf(stdout, "CPU for embedding recovering %f\n",
+ time_current_user() - sttime);
+ )
+
+ DIE();
+ }
+
+ IF_DEB_EMBED(
+ fprintf(stdout, "embedding, original graph and embedding\n");
+ sparseg_adjl_print(V, n, A, FALSE);
+ fprintf(stdout, "\n");
+ sparseg_adjl_embed_print(*vertices, n, A, *embedding,
+ FALSE);
+ )
+
+ IF_CPU(
+ fprintf(stdout, "CPU for embedding recovering %f\n",
+ time_current_user() - sttime);
+ )
+}
+
+
+void
+embedg_remove_SCE (t_ver_edge *embed_graph, int n, int edge_pos)
+ /*
+ remove all the short-cut edges from the embedding
+ */
+{
+ int i, c;
+
+ c = 0;
+ for (i = 2*n; i <= edge_pos; i += 2)
+ /*
+ and edge and its twin occupy consecutive positions in embed_graph:
+ need only to examine one out of two
+ (removing an edge also entails removing its twin of course
+ */
+ {
+ if (embedg_VES_is_short_cut_edge(embed_graph, n, i))
+ {
+ IF_DEB_SCE(
+ fprintf(stdout, "remove SCE\n");
+ embedg_VES_print_edge(embed_graph, n, i);
+ )
+
+ embedg_VES_remove_edge(embed_graph, n, i);
+ c++;
+ }
+ }
+
+ IF_DEB_SCE(
+ fprintf(stdout, "nbr of SCE edges removed %d\n", c);
+ )
+}
+
+
+int *
+embedg_vertices_orientation (t_ver_edge *embed_graph, int n)
+ /*
+ for each vertex return its orientation from the
+ bicomps in embed_graph:
+ perform a DFS of each bicomp
+ */
+{
+ int i, vv, prod_sign;
+ int *stack, *ver_orient, to_prev;
+
+ /*
+ the whole lot makes sense iff the adjacency lists are consistent:
+ this is a very important issue and it might be the case
+ that the ASSERT warrants replacement by a DIE
+ (the check is linear - I think)
+ */
+ ASSERT(embedg_VES_are_adj_lists_consistent(embed_graph, n));
+
+ ver_orient = (int *) mem_malloc(sizeof(int) * n);
+ for (i = 0; i < n; i++)
+ {
+ ver_orient[i] = CCLOCKW;
+ }
+
+ /*
+ create the stack for the DFS
+ */
+ stack = (int *) mem_malloc(sizeof(int) * 3*n);
+ to_prev = -1;
+
+ IF_DEB(
+ fprintf(stdout, "vertex orientation, one line (of vert.) for each bicomp\n");
+ )
+
+ /*
+ now visit all the bicomps, ie, all the virtual vertices
+ in embed_graph
+ */
+ for (vv = n; vv < 2*n; vv++)
+ {
+ int c, cur, cur_e;
+ boolean NEW_BICOMP;
+
+ if (embed_graph[vv].link[0] == vv)
+ /*
+ means that vv is disabled and is not the root of a bicomp
+ */
+ {
+ continue;
+ }
+
+ c = vv - n;
+ IF_DEB(
+ fprintf(stdout, "%d ", c);
+ )
+ /*
+ orientation for c (vv is as yet unembedded) is CCLOCKW
+
+ now find the orientation of all its DFS descendants
+ */
+
+ if (embed_graph[c].DFS_parent == n)
+ /*
+ this means that actually c is an isolated vertex:
+ we initialise the sign to CCLOCKW
+ */
+ {
+ prod_sign = CCLOCKW;
+ }
+ else
+ /*
+ we initialise the sign to CCLOCKW to the sign of c's parent
+ */
+ {
+ prod_sign = ver_orient[embed_graph[c].DFS_parent];
+ }
+
+ /*
+ we must not forget to set c's sign!!
+ (won't be done below)
+ */
+ ver_orient[c] = prod_sign;
+
+ NEW_BICOMP = FALSE;
+ cur = c;
+ cur_e = embed_graph[cur].link[0];
+ ASSERT(embedg_VES_is_edge(n, cur_e));
+
+ ASSERT(to_prev == -1);
+ while (TRUE)
+ {
+ while (!embedg_VES_is_tree_edge(embed_graph, n, cur_e)
+ || !embedg_VES_is_vertex(n,
+ embed_graph[cur_e].neighbour)
+ || embed_graph[cur_e].neighbour <= cur)
+ /*
+ want to find a tree edge [cur, u]
+ where u is a descendant of cur
+ */
+ {
+ cur_e = embed_graph[cur_e].link[0];
+
+ while (cur_e == cur)
+ /*
+ back to the vertex where we started from:
+ no edge has been found:
+ cur is a leaf, backtrack
+ */
+ {
+ if (to_prev == -1)
+ {
+ NEW_BICOMP = TRUE;
+ break;
+ }
+ prod_sign = stack[to_prev--];
+ cur_e = stack[to_prev--];
+ /*
+ must advance one more edge
+ */
+ cur_e = embed_graph[cur_e].link[0];
+ cur = stack[to_prev--];
+ }
+ if (NEW_BICOMP)
+ {
+ break;
+ }
+ }
+
+ if (NEW_BICOMP)
+ {
+ break;
+ }
+ else
+ /*
+ now cur_e is the edge we were looking for, get its sign
+ */
+ {
+ /*
+ push on stack the current vertex, the edge where we
+ stopped the DFS, AND the sign carried by that vertex
+
+ and go down one level in the DFS
+ */
+ stack[++to_prev] = cur;
+ stack[++to_prev] = cur_e;
+ stack[++to_prev] = prod_sign;
+
+ cur = embed_graph[cur_e].neighbour;
+ prod_sign *= embed_graph[cur_e].sign;
+ ver_orient[cur] = prod_sign;
+
+ cur_e = embed_graph[cur].link[0];
+ ASSERT(embedg_VES_is_edge(n, cur_e));
+
+ IF_DEB(
+ fprintf(stdout, "%d with sign %d\n", cur, prod_sign);
+ )
+ }
+ }
+
+ IF_DEB(
+ fprintf(stdout, "\n");
+ )
+ }
+
+ IF_DEB(
+ fprintf(stdout, "vertex orientation\n");
+ for (i = 0; i < n; i++)
+ {
+ fprintf(stdout, "%d ", ver_orient[i]);
+ }
+ fprintf(stdout, "\n");
+ )
+
+ mem_free(stack);
+ return ver_orient;
+}
+
+
+int
+embedg_merge_remaining_virtual (t_ver_edge *embed_graph, int n)
+ /*
+ after the short-cut edges have been removed and the vertices'
+ orientation computed, one finishes by merging all
+ remaining virtual vertices with their virtual counterpart
+ (without flip of course)
+
+ and use this routine to return the number of disconnected
+ components of the graph
+ */
+{
+ /*
+ at this stage it is easy to see that all remaining
+ virtual vertices are DFS roots (if the graph is not connected)
+ or cut vertices
+ */
+
+ int vv, nbr_comp;
+
+ nbr_comp = 0;
+ for (vv = n; vv < 2*n; vv++)
+ {
+ int v, c;
+
+
+ c = vv - n;
+ v = embed_graph[c].DFS_parent;
+
+ /*
+ must fish out which virtual vertices are actual roots
+ of DFS trees (esp. for the disconnected graph case):
+ roots of DFS trees are those virtual vertices for which
+ v = embed_graph[c].DFS_parent = n
+ */
+ if (v == n)
+ {
+ nbr_comp++;
+ continue;
+ }
+
+ if (embed_graph[vv].link[0] == vv)
+ /*
+ means that vv is disabled and is not the root of a bicomp
+ */
+ {
+ continue;
+ }
+
+ embedg_VES_merge_simple_bicomps(embed_graph, n,
+ vv, 1, v, 0);
+ /*
+ note:
+ since v is a cut vertex in this intance the bicomp
+ rooted by vv will be merged without flip;
+ therefore we could have done
+ embedg_VES_merge_simple_bicomps(embed_graph, n,
+ vv, 0, v, 1)
+ as well, the important thing being that vin != vvout
+ (see embedg_VES_merge_simple_bicomps)
+ */
+ }
+
+ return nbr_comp;
+}
+
+
+int
+embedg_nbr_faces (t_ver_edge *embed_graph, int n, int edge_pos,
+ int *ver_orient, int *nbr_e_embed)
+ /*
+ count the number of faces and the number of edges of the embedding
+ */
+{
+ int v, e, f, total_e;
+
+ IF_DEB_FACES(
+ int v;
+
+ fprintf(stdout, "nbr of faces, the vertices' adj. lists\n");
+ for (v = 0; v < n; v++)
+ embedg_VES_print_adj_list(embed_graph, n,
+ v, TRUE);
+ )
+
+ /*
+ the following is no more than a quick check -- certainly
+ not very useful -- or could be done elsewhere
+ */
+ total_e = 0;
+ for (e = 2*n; e <= edge_pos; e++)
+ {
+ if (!embedg_VES_is_short_cut_edge(embed_graph, n, e))
+ {
+ total_e++;
+ }
+ }
+ ASSERT(total_e % 2 == 0);
+ *nbr_e_embed = total_e / 2;
+
+ /*
+ I now set each edge's orientation
+
+ QUESTION: do I really need to do this???
+ so far, when doing a proper face traversal, the way in which
+ the adjacency list of an edge must be traversed is given
+ by the vertex's (in that list) orientation...
+ So this seems sensible to me huh?
+ */
+ embedg_VES_set_orientation(embed_graph, n, ver_orient);
+
+ /*
+ I will be using the visited field to enable me to check
+ if all edges have been traversed
+
+ let's be smart (?!): so far the visited field has been used
+ and set in the following circumstances:
+ + initialisation: set to n
+ + walkup: set to whatever DFI of interest
+
+ so here we set it to MARK_EMBED(n)
+ */
+ f = 0;
+ for (e = 2*n; e <= edge_pos; e++)
+ {
+ if (!embedg_VES_is_short_cut_edge(embed_graph, n, e)
+ /*
+ arrghh!!! I must also skip the SCE!!!
+ */
+ && embed_graph[e].visited != MARK_EMBED(n))
+ {
+ int ein;
+
+ IF_DEB_FACES(
+ fprintf(stdout, "nbr of faces, edges not visited\n");
+ embedg_VES_print_edge(embed_graph, n, e);
+ )
+
+ ein = embed_graph[e].sign == CCLOCKW ? 0 : 1;
+ /*
+ the way I enter e in dependent on its sign:
+ all the proper face traversal must obviously be done
+ with the same orientation!
+ */
+ embedg_VES_walk_proper_face(embed_graph, n, e,
+ ein,
+ TRUE,
+ MARK_EMBED(n));
+ f++;
+ }
+ }
+
+ /*
+ counting the faces by traversing all the edges does not
+ account of the face defined by isolated vertices
+ -- we do that now
+
+ we only need to check which vertices refer to self, ie with
+ no incident edges
+ */
+ for (v = 0; v < n; v++)
+ {
+ if (embed_graph[v].link[0] == v)
+ {
+ ASSERT(embed_graph[v].link[1] == v);
+ f++;
+ }
+ }
+
+ return f;
+}
+
+
+boolean
+embedg_is_embed_valid (t_ver_edge *embed_graph, int n, int nbr_comp,
+ int edge_pos, int *ver_orient, int *nbr_e_embed)
+ /*
+ use Euler's formula to assertain that the embedding is a valid
+ embedding:
+
+ f = 2 * nbr_comp + nbr_e_embed - n
+
+ */
+{
+ int v, f;
+
+ f = embedg_nbr_faces(embed_graph, n, edge_pos, ver_orient, nbr_e_embed);
+
+ IF_DEB_CHECK_EMBED(
+ fprintf(stdout, "embedding, n: %d\t e: %d\t C: %d\t f: %d\n",
+ n, nbr_e, nbr_comp, f);
+ )
+
+ return f == 2 * nbr_comp + *nbr_e_embed - n ? TRUE : FALSE;
+}
+/*
+ * ext_face_walk.c
+ */
+
+/*
+ What:
+ *****
+
+ Implementing the external face walk of a bicomponent.
+ The concept of an external face --in the context of the VES
+ data structure-- makes only sense when talking
+ about a bicomp.
+
+ Recall that a vertex is linked together with the edges
+ incident from it in a circular (doubly) linked list
+ (this is the VES structure).
+
+ One particular feature is that if a vertex v is on
+ the external face of a component and if in the list
+ we have edges e1, e2 such as e1 -> v -> e2
+ then e1 and e2 border the external face.
+
+ In other words, in the circular list of vertex v and edges,
+ v is ALWAYS between the two edges bordering the external face
+
+ Of course, when v is (maybe) pushed into the internal face
+ (by embedding of some edge) then we don't care about this any more
+ (for v that is).
+
+
+
+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ from
+
+ Simplified O(n) Planarity Algorithms (draft)
+ ************************************
+
+ John Boyer JBoyer@PureEdge.com, jboyer@acm.org
+ Wendy Myrvold wendym@csr.uvic.ca
+
+
+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ authors:
+ ********
+
+ Paulette Lieby (Magma), Brendan McKay (ANU)
+
+ Started October 2001
+*/
+
+
+#include "planarity.h"
+
+#define IF_DEB(x) {}
+#define IF_VERB(x) {}
+
+
+/* aproto: header embed_graph_protos.h */
+
+
+void
+embedg_VES_get_succ_on_ext_face (t_ver_edge *embed_graph, int n, int v,
+ int vin, boolean MARK, int mark, int *s, int *sin)
+ /*
+ find the successor s of v (entered via vin) on the external face
+ -- also return the direction in which s has been entered
+
+ if MARK true mark the succ. vertex and the edges traversed
+ with mark (the visited field)
+ */
+{
+ int e, twin;
+ int vout, ein, eout, tout;
+
+ ASSERT(embedg_VES_is_vertex(n, v)
+ || embedg_VES_is_virtual_vertex(n, v));
+
+ IF_DEB(
+ fprintf(stdout, "get_succ_on_ext_face, of %d:%d\n", v, vin);
+ )
+
+ /*
+ find the direction out of the vertex, and get the edge
+ */
+ vout = vin == 0 ? 1 : 0;
+ e = embed_graph[v].link[vout];
+ if (embedg_VES_is_virtual_vertex(n, v) && e == v)
+ /*
+ this can happen if a virtual vertex has been "disabled"
+
+ -- this should not never happen since we can only walk
+ on the external face of a bicomp!
+ */
+ {
+ *s = v;
+ *sin = vin;
+ return;
+ }
+
+ /*
+ otherwise we must have an edge:
+ note that it is entirely irrelevant if I walk SCEs:
+ those are precisely there to "jump" over inactive vertices
+ */
+ ASSERT(embedg_VES_is_edge(n, e));
+
+ /*
+ get the twin edge
+ */
+ twin = embedg_VES_get_twin_edge(embed_graph, n, e);
+
+ IF_DEB(
+ fprintf(stdout, "get_succ_on_ext_face, edge [%d, %d]\n",
+ v, embed_graph[e].neighbour);
+ fprintf(stdout, "get_succ_on_ext_face, twin edge [%d, %d]\n",
+ embed_graph[e].neighbour, embed_graph[twin].neighbour);
+ )
+ /*
+ find which of twin's link links a vertex
+ */
+ tout = embedg_VES_is_vertex(n, embed_graph[twin].link[0])
+ || embedg_VES_is_virtual_vertex(n,
+ embed_graph[twin].link[0])
+ ?
+ 0 : 1;
+
+ /*
+ get this vertex: this is v's successor on the external face
+ */
+ *s = embed_graph[twin].link[tout];
+
+ /*
+ one more thing to do: find the direction in which s was entered
+ */
+ *sin = embed_graph[*s].link[0] == twin ? 0 : 1;
+
+ IF_DEB(
+ fprintf(stdout, "get_succ_on_ext_face, succ is %d:%d\n",
+ *s, *sin);
+ )
+ /*
+ a special case: when the bicomp is a singleton bicomp
+ (ie a single edge)
+ */
+ if (embed_graph[*s].link[0] == (embed_graph[*s].link[1]))
+ {
+ ASSERT(embed_graph[*s].link[0] == twin);
+ *sin = vin;
+ }
+
+ /*
+ finally, mark the vertex and edges if so requested
+ */
+ if (MARK)
+ {
+ embed_graph[*s].visited = mark;
+ embed_graph[e].visited = mark;
+ embed_graph[twin].visited = mark;
+ }
+}
+
+void
+embedg_VES_get_succ_active_on_ext_face (t_ver_edge *embed_graph, int n,
+ int v, int w, int win, boolean MARK, int mark, int *s, int *sin)
+ /*
+ find the ACTIVE (wrt v) successor s of w (entered via win)
+ on the external face
+ -- also return the direction in which s has been entered
+
+ if MARK true mark the succ. vertex (and the edge)
+ with mark (the visited field)
+ */
+{
+ /*
+ simply repeatedly calls embedg_VES_get_succ_on_ext_face
+ until an active vertex is found
+ */
+ ASSERT(embedg_VES_is_vertex(n, w)
+ || embedg_VES_is_virtual_vertex(n, w));
+
+ embedg_VES_get_succ_on_ext_face(embed_graph, n,
+ w, win, MARK, mark, s, sin);
+ while (embedg_VES_is_ver_inactive(embed_graph, n, v, *s))
+ {
+ embedg_VES_get_succ_on_ext_face(embed_graph, n,
+ *s, *sin, MARK, mark, s, sin);
+ }
+ ASSERT(!embedg_VES_is_ver_inactive(embed_graph, n, v, *s));
+}
+
+void
+embedg_VES_get_succ_ext_active_on_ext_face (t_ver_edge *embed_graph, int n,
+ int v, int w, int win, boolean MARK, int mark, int *s, int *sin)
+ /*
+ find the externally active (wrt v) successor s of w (entered via win)
+ on the external face
+ -- also return the direction in which s has been entered
+
+ if MARK true mark the succ. vertex (and the edge)
+ with mark (the visited field)
+ */
+{
+ ASSERT(embedg_VES_is_vertex(n, w)
+ || embedg_VES_is_virtual_vertex(n, w));
+
+ embedg_VES_get_succ_on_ext_face(embed_graph, n,
+ w, win, MARK, mark, s, sin);
+ while (!embedg_VES_is_ver_ext_active(embed_graph, n, v, *s))
+ {
+ embedg_VES_get_succ_on_ext_face(embed_graph, n,
+ *s, *sin, MARK, mark, s, sin);
+ }
+ ASSERT(embedg_VES_is_ver_ext_active(embed_graph, n, v, *s));
+}
+
+void
+embedg_VES_get_succ_pertinent_on_ext_face (t_ver_edge *embed_graph, int n,
+ int v, int w, int win, boolean MARK, int mark, int *s, int *sin)
+ /*
+ find the pertinent (wrt v) successor s of w (entered via win)
+ on the external face
+ -- also return the direction in which s has been entered
+
+ if MARK true mark the succ. vertex (and the edge)
+ with mark (the visited field)
+ */
+{
+ ASSERT(embedg_VES_is_vertex(n, w)
+ || embedg_VES_is_virtual_vertex(n, w));
+
+ embedg_VES_get_succ_on_ext_face(embed_graph, n,
+ w, win, MARK, mark, s, sin);
+ while (!embedg_VES_is_ver_pertinent(embed_graph, n, v, *s))
+ {
+ embedg_VES_get_succ_on_ext_face(embed_graph, n,
+ *s, *sin, MARK, mark, s, sin);
+ }
+ ASSERT(embedg_VES_is_ver_pertinent(embed_graph, n, v, *s));
+}
+
+/*
+ * mark_kur.c
+ */
+
+/*
+ What:
+ *****
+
+ Implementing:
+
+ Marking the Kuratowski obstruction (in the VES structure):
+ this we do once we know which minor we are talking about
+ (see isolator.c).
+
+
+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ from
+
+ Simplified O(n) Planarity Algorithms (draft)
+ ************************************
+
+ John Boyer JBoyer@PureEdge.com, jboyer@acm.org
+ Wendy Myrvold wendym@csr.uvic.ca
+
+
+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ authors:
+ ********
+
+ Paulette Lieby (Magma), Brendan McKay (ANU)
+
+ Started October 2001
+*/
+
+
+#include "planarity.h"
+
+#define IF_DEB(x) {}
+#define IF_VERB(x) {}
+#define IF_DEB_TREE(x) {}
+#define IF_DEB_EDGES(x) {}
+#define IF_CPU(x) {}
+
+
+
+/* aproto: header embed_graph_protos.h */
+
+/* aproto: beginstatic -- don't touch this!! */
+static void embedg_VES_walk_mark_part_ext_face (t_ver_edge *, int, int, int, int, int, int);
+static void embedg_VES_walk_mark_ext_face (t_ver_edge *, int, int, int);
+static void embedg_VES_walk_mark_part_proper_face (t_ver_edge *, int, int, int, int, int);
+static boolean embedg_VES_is_part_ext_face_marked (t_ver_edge *, int, int, int, int, int, int);
+static void embedg_get_u_x (t_ver_edge *, int, int, int, int *);
+static int embedg_get_least_neigh (t_dlcl **, t_dlcl **, int, int, int);
+static void embedg_add_mark_u_x (t_dlcl **, t_dlcl **, t_ver_edge *, int, int *, int, int, int *, int);
+static void embedg_mark_tree_path (t_ver_edge *, int, int, int, int);
+static void embedg_add_mark_v_w (t_dlcl **, t_dlcl **, t_ver_edge *, int, int *, int, int, int);
+static void embedg_add_mark_v_w_for_B (t_dlcl **, t_dlcl **, t_ver_edge *, int, int *, int, int, int *, int);
+static void embedg_mark_x_y_path (t_ver_edge *, int, int *, int *, int, int);
+/* aproto: endstatic -- don't touch this!! */
+
+#ifndef PLANAR_IN_MAGMA
+#endif
+
+
+
+static void
+embedg_VES_walk_mark_part_ext_face (t_ver_edge *embed_graph, int n,
+ int v, int vin, int from, int to, int mark)
+ /*
+ walk & mark the external face:
+ walk in the direction vin -> v -> vout and mark
+ */
+{
+ int cur, curin, next, nextin;
+
+ embed_graph[from].visited = mark;
+ embed_graph[to].visited = mark;
+
+ IF_DEB(
+ fprintf(stdout, "part. ext face marked\t");
+ fprintf(stdout, "%d\t", from);
+ )
+
+ next = cur = v;
+ curin = vin;
+ while (next != from)
+ {
+ embedg_VES_get_succ_on_ext_face(embed_graph, n, cur, curin,
+ FALSE, 0, &next, &nextin);
+ cur = next;
+ curin = nextin;
+ }
+ next = n;
+ while (next != to)
+ {
+ embedg_VES_get_succ_on_ext_face(embed_graph, n, cur, curin,
+ TRUE, mark, &next, &nextin);
+ cur = next;
+ curin = nextin;
+
+ IF_DEB(
+ fprintf(stdout, "%d\t", next);
+ )
+ }
+ IF_DEB(
+ fprintf(stdout, "\n");
+ )
+}
+
+static void
+embedg_VES_walk_mark_ext_face (t_ver_edge *embed_graph, int n, int v, int mark)
+ /*
+ walk & mark the external face, starting & ending at vertex v
+ */
+{
+ embedg_VES_walk_mark_part_ext_face(embed_graph, n, v, 0, v, v,
+ mark);
+}
+
+
+
+static void
+embedg_VES_walk_mark_part_proper_face (t_ver_edge *embed_graph, int n,
+ int from_e, int from_ein, int to, int mark)
+ /*
+ walk & mark a proper face starting at EDGE from_e and ending
+ at VERTEX to
+
+ walk in the direction from_ein -> from_e -> to and mark
+ everything in between
+ */
+{
+ int s, cur_e, cur_ein, next_e, next_ein;
+
+ next_e = s = n; /* this is an invalid value for an edge/vertex */
+
+ cur_e = from_e;
+ cur_ein = from_ein;
+ while (s != to)
+ {
+ ASSERT(embedg_VES_is_edge(n, cur_e));
+ ASSERT(!embedg_VES_is_short_cut_edge(embed_graph,
+ n, cur_e));
+
+ embedg_VES_get_succ_on_proper_face(embed_graph, n,
+ cur_e, cur_ein,
+ TRUE, mark,
+ &s, &next_e, &next_ein);
+ cur_e = next_e;
+ cur_ein = next_ein;
+ }
+}
+
+
+
+static boolean
+embedg_VES_is_part_ext_face_marked (t_ver_edge *embed_graph, int n, int v,
+ int vin, int from, int to, int mark)
+ /*
+ simple check to see if all the vertices on the external
+ face walk starting at vin -> v -> vout are marked
+ (with mark)
+ */
+{
+ int cur, curin, next, nextin;
+
+ if (embed_graph[from].visited != mark || embed_graph[to].visited != mark)
+ return FALSE;
+
+ cur = v;
+ curin = vin;
+ next = n;
+ while (next != from)
+ {
+ embedg_VES_get_succ_on_ext_face(embed_graph, n, cur, curin,
+ FALSE, 0, &next, &nextin);
+ cur = next;
+ curin = nextin;
+ }
+ while (next != to)
+ {
+ embedg_VES_get_succ_on_ext_face(embed_graph, n, cur, curin,
+ FALSE, 0, &next, &nextin);
+ if (embed_graph[next].visited != mark)
+ return FALSE;
+
+ cur = next;
+ curin = nextin;
+ }
+
+ return TRUE;
+}
+
+
+boolean
+embedg_VES_is_ext_face_marked (t_ver_edge *embed_graph, int n, int v, int mark)
+ /*
+ simple check to see if all the vertices on the external
+ face walk starting/ending at v are marked (with mark)
+ */
+{
+ return embedg_VES_is_part_ext_face_marked(embed_graph, n, v, 0,
+ v, v, mark);
+}
+
+
+static void
+embedg_get_u_x (t_ver_edge *embed_graph, int n, int v, int x, int *u_x)
+ /*
+ x is an externally active vertex (wrt v):
+ we want u_x, the lowest point of "attachement" for
+ the unembedded directed edge [x, u_x]
+ */
+{
+ int c;
+ t_dlcl *child_list;
+
+ ASSERT(embedg_VES_is_ver_ext_active(embed_graph, n, v, x));
+ if (embed_graph[x].least_ancestor < v)
+ /*
+ then there is a single unembedded back edge (u_x, x),
+ u_x an ancestor of v
+ */
+ {
+ *u_x = embed_graph[x].least_ancestor;
+ return;
+ }
+
+ /*
+ else there is a tree path x to d_x and an
+ unembedded back edge (u_x, d_x)
+
+ get the lowpoint of the first elt. in separated_DFS_child_list of x
+ */
+ child_list = embed_graph[x].separated_DFS_child_list;
+ ASSERT(!embedg_dlcl_is_empty(child_list));
+ c = child_list->info;
+ *u_x = embed_graph[c].lowpoint;
+}
+
+static int
+embedg_get_least_neigh (t_dlcl **dfs_tree, t_dlcl **back_edges,
+ int n, int v, int c)
+ /*
+ get the least neighbour of v >= c, ie a vertex in the sub tree
+ rooted by c
+
+ somehow this must always succeed
+ */
+{
+ int least_n;
+ t_dlcl *tree_l, *back_l, *p;
+
+ /*
+ neighbours are found in either dfs_tree[v] or back_edges[v]
+ */
+
+ tree_l = dfs_tree[v];
+ back_l = back_edges[v];
+ ASSERT(!embedg_dlcl_is_empty(tree_l) || !embedg_dlcl_is_empty(back_l));
+
+ least_n = n; /* ok, invalid value for any neighbour */
+ p = tree_l;
+ if (!embedg_dlcl_is_empty(p))
+ {
+ if (p->info >= c)
+ {
+ least_n = p->info < least_n ? p->info : least_n;
+ }
+ p = embedg_dlcl_list_next(p);
+ while (p != tree_l)
+ {
+ if (p->info >= c)
+ {
+ least_n = p->info < least_n ? p->info : least_n;
+ }
+ p = embedg_dlcl_list_next(p);
+ }
+ }
+ p = back_l;
+ if (!embedg_dlcl_is_empty(p))
+ {
+ if (p->info >= c)
+ {
+ least_n = p->info < least_n ? p->info : least_n;
+ }
+ p = embedg_dlcl_list_next(p);
+ while (p != back_l)
+ {
+ if (p->info >= c)
+ {
+ least_n = p->info < least_n ? p->info : least_n;
+ }
+ p = embedg_dlcl_list_next(p);
+ }
+ }
+
+ ASSERT(least_n >= c);
+ /*
+ this is so because of the context where this function is called from
+ */
+ return least_n;
+}
+
+static void
+embedg_add_mark_u_x (t_dlcl **dfs_tree, t_dlcl **back_edges,
+ t_ver_edge *embed_graph, int n, int *edge_pos, int v,
+ int x, int *u_x, int mark)
+ /*
+ marking a Kuratowski homeomorph:
+
+ marking and adding the unembedded dotted edge (u, x),
+ x an ext. active vertex wrt v
+ */
+{
+ int c, d_x;
+ t_dlcl *child_list;
+
+ ASSERT(embedg_VES_is_ver_ext_active(embed_graph, n, v, x));
+ if (embed_graph[x].least_ancestor < v)
+ /*
+ then there is a single unembedded back edge (u_x, x),
+ u_x an ancestor of v
+ */
+ {
+ *u_x = embed_graph[x].least_ancestor;
+ embed_graph[x].visited = mark;
+ embed_graph[*u_x].visited = mark;
+ embedg_VES_add_edge(embed_graph, n, edge_pos, *u_x, x,
+ TRUE, mark);
+ return;
+ }
+
+ /*
+ else there is a tree path x to d_x and an
+ unembedded back edge (u_x, d_x)
+
+ get the lowpoint of the first elt. in separated_DFS_child_list of x
+ */
+ child_list = embed_graph[x].separated_DFS_child_list;
+ ASSERT(!embedg_dlcl_is_empty(child_list));
+ c = child_list->info;
+ *u_x = embed_graph[c].lowpoint;
+
+ /*
+ search for the least neighbour of *u_x >= c,
+ that is in the subtree rooted by c
+ */
+ d_x = embedg_get_least_neigh(dfs_tree, back_edges, n, *u_x, c);
+ ASSERT(d_x >= c);
+ /*
+ this must be true since u_x is incident to a descendant of x
+ (remember: x is externally active)
+ */
+
+ /*
+ mark the DFS tree path from d_x to x
+ */
+ embedg_mark_tree_path(embed_graph, n, d_x, x, mark);
+ /*
+ add the unembedded (u_x, d_x) edge
+ */
+ embedg_VES_add_edge(embed_graph, n, edge_pos, *u_x, d_x,
+ TRUE, mark);
+}
+
+static void
+embedg_mark_tree_path (t_ver_edge *embed_graph, int n, int d_x, int x, int mark)
+ /*
+ marking the DFS tree path d_x...x where x is an ancestor of d_x
+ */
+{
+ int cur_v, te, twe;
+
+ ASSERT(d_x >= x);
+
+ cur_v = d_x;
+
+ while (cur_v != x)
+ {
+ embed_graph[cur_v].visited = mark;
+ te = embed_graph[cur_v].link[0];
+ ASSERT(embedg_VES_is_edge(n, te));
+ while (!embedg_VES_is_tree_edge(embed_graph, n, te)
+ || (embed_graph[te].neighbour > cur_v
+ && embed_graph[te].neighbour != cur_v + n))
+ /*
+ want to find a tree edge incident to an ancestor of d_x:
+ given that d_x..x is a tree path, we MUST find such an edge!
+
+ note also that I must take account of the fact that
+ [te].neighbour could be a virtual vertex, in which case
+ it can only be cur_v + n!
+ */
+ {
+ te = embed_graph[te].link[0];
+ }
+ ASSERT(embedg_VES_is_tree_edge(embed_graph, n, te));
+ ASSERT(embed_graph[te].neighbour == embed_graph[cur_v].DFS_parent
+ || embed_graph[te].neighbour == cur_v + n);
+
+ embed_graph[te].visited = mark;
+ twe = embedg_VES_get_twin_edge(embed_graph, n, te);
+ embed_graph[twe].visited = mark;
+
+ /*
+ want only to deal with real vertices instead of virtual vertices
+ */
+ cur_v = embed_graph[te].neighbour < cur_v ?
+ embed_graph[te].neighbour : embed_graph[cur_v].DFS_parent;
+ }
+ embed_graph[x].visited = MARK_MINORS(n);
+}
+
+
+static void
+embedg_add_mark_v_w (t_dlcl **dfs_tree, t_dlcl **back_edges,
+ t_ver_edge *embed_graph, int n, int *edge_pos, int v, int w, int mark)
+ /*
+ marking a Kuratowski homeomorph:
+
+ marking and adding the unembedded dotted edge (v, w),
+ w is pertinent wrt v
+ */
+{
+ int vw, c, d_w;
+ t_dlcl *bicomp_list;
+
+ if (embed_graph[w].adjacent_to == v)
+ /*
+ then there is a single unembedded back edge (v, w)
+ w an ancestor of w
+ */
+ {
+ embed_graph[v].visited = mark;
+ embed_graph[w].visited = mark;
+ embedg_VES_add_edge(embed_graph, n, edge_pos, v, w,
+ TRUE, mark);
+ return;
+ }
+
+ /*
+ else there is a tree path w to d_w and an
+ unembedded back edge (v, d_w)
+
+ get the last elt in w's bicomp list
+ */
+ bicomp_list = embed_graph[w].pertinent_bicomp_list;
+ ASSERT(!embedg_dlcl_is_empty(bicomp_list));
+ vw = (embedg_dlcl_list_last(bicomp_list))->info;
+ c = vw - n;
+
+ /*
+ search for the least neighbour of v >= c,
+ that is in the subtree rooted by c
+ */
+ d_w = embedg_get_least_neigh(dfs_tree, back_edges, n, v, c);
+ ASSERT(d_w >= c);
+ /*
+ this must be true since v is incident to a descendant of w
+ (remember: w is pertinent)
+ */
+
+ /*
+ mark the DFS tree path from d_w to w
+ */
+ embedg_mark_tree_path(embed_graph, n, d_w, w, mark);
+ /*
+ add the unembedded (d_w, v) edge
+ */
+ embedg_VES_add_edge(embed_graph, n, edge_pos, d_w, v,
+ TRUE, mark);
+}
+
+
+static void
+embedg_add_mark_v_w_for_B (t_dlcl **dfs_tree, t_dlcl **back_edges,
+ t_ver_edge *embed_graph, int n, int *edge_pos, int v, int w,
+ int *u_z, int mark)
+ /*
+ marking a Kuratowski homeomorph:
+
+ marking and adding the unembedded dotted edge (v, w) for minor B:
+ w is pertinent wrt v
+ */
+{
+ int vz, z, d_z, d_w;
+ t_dlcl *bicomp_list;
+
+ /*
+ get the last elt in w's bicomp list
+ */
+ bicomp_list = embed_graph[w].pertinent_bicomp_list;
+ ASSERT(!embedg_dlcl_is_empty(bicomp_list));
+ vz = (embedg_dlcl_list_last(bicomp_list))->info;
+ z = vz - n;
+
+ /*
+ get the lowpoint of z
+ */
+ *u_z = embed_graph[z].lowpoint;
+
+ /*
+ search for the least neighbour of *u_z >= z,
+ that is in the subtree rooted by c
+ */
+ d_z = embedg_get_least_neigh(dfs_tree, back_edges, n, *u_z, z);
+ ASSERT(d_z >= z);
+ /*
+ this must be true since u_z is incident to z or a descendant of z
+ */
+
+ /*
+ now do the same for neighbours of v
+ */
+ d_w = embedg_get_least_neigh(dfs_tree, back_edges, n, v, z);
+ ASSERT(d_w >= z);
+ /*
+ this must be true since v is incident to a descendant of w
+ (remember: w is pertinent)
+ */
+
+ /*
+ mark the DFS tree path from d_w to w
+ */
+ embedg_mark_tree_path(embed_graph, n, d_w, w, mark);
+ /*
+ mark the DFS tree path from d_z to z
+ */
+ embedg_mark_tree_path(embed_graph, n, d_z, z, mark);
+ /*
+ add & mark the edges (u_z, d_z), (v, d_w)
+ */
+ embedg_VES_add_edge(embed_graph, n, edge_pos, *u_z, d_z,
+ TRUE, mark);
+ embedg_VES_add_edge(embed_graph, n, edge_pos, v, d_w,
+ TRUE, mark);
+}
+
+static void
+embedg_mark_x_y_path (t_ver_edge *embed_graph, int n, int *path_v,
+ int *path_e, int nbr_v, int mark)
+{
+ int i;
+
+ /*
+ have a look at embedg_iso_get_highest_x_y_path
+ to see that path_e[0] is a dummy
+
+ (note: path_v and path_e contain nbr_v + 1 elts!)
+ */
+ embed_graph[path_v[0]].visited = mark;
+ for (i = 1; i <= nbr_v; i++)
+ {
+ int e, twin;
+
+ embed_graph[path_v[i]].visited = mark;
+ e = path_e[i];
+ twin = embedg_VES_get_twin_edge(embed_graph, n, e);
+ embed_graph[e].visited =
+ embed_graph[twin].visited = mark;
+ }
+}
+
+void
+embedg_mark_minor_A (t_dlcl **dfs_tree, t_dlcl **back_edges,
+ t_ver_edge *embed_graph, int n, int *edge_pos, int v, int c, int vr)
+{
+ int r, r_c, x, y, w, u_x, u_y, u;
+
+ ASSERT(embedg_VES_is_virtual_vertex(n, vr));
+ r_c = vr - n;
+ r = embed_graph[r_c].DFS_parent;
+
+ /*
+ find the ext. active x & y, and the pertinent w,
+ and mark the external face of the bicomp rooted at vr
+ */
+ embedg_iso_get_x_y_w(embed_graph, n, v, r, r_c,
+ MARK_MINORS(n),
+ MARK_MINORS(n), MARK_MINORS(n), &x, &y, &w);
+
+ /*
+ mark the edges (u, x), (u, y), (v, w)
+ */
+ embedg_add_mark_u_x(dfs_tree, back_edges,
+ embed_graph, n, edge_pos, v, x, &u_x,
+ MARK_MINORS(n));
+ embedg_add_mark_u_x(dfs_tree, back_edges,
+ embed_graph, n, edge_pos, v, y, &u_y,
+ MARK_MINORS(n));
+ embedg_add_mark_v_w(dfs_tree, back_edges,
+ embed_graph, n, edge_pos, v, w,
+ MARK_MINORS(n));
+
+ /*
+ mark the tree path from r to min(u_x, u_y)
+ */
+ u = u_x <= u_y ? u_x : u_y;
+ embedg_mark_tree_path(embed_graph, n, r, u, MARK_MINORS(n));
+
+ IF_DEB(
+ fprintf(stdout, "mark minor A\n");
+ fprintf(stdout, "v %d\t c %d\t r %d\t r_c %d\t x %d\t y %d\t w %d\t u_x %d\t u_y %d\n",
+ v, c, r, r_c, x, y, w, u_x, u_y);
+ )
+}
+
+void
+embedg_mark_minor_B (t_dlcl **dfs_tree, t_dlcl **back_edges,
+ t_ver_edge *embed_graph, int n, int *edge_pos, int v,
+ int c, int x, int y, int w)
+{
+ int vv, u_x, u_y, vz, u_z, u_max, u_min;
+
+ vv = c + n;
+
+ /*
+ mark the external face of the bicomp rooted by v^c
+ */
+ embedg_VES_walk_mark_ext_face(embed_graph, n, vv, MARK_MINORS(n));
+ ASSERT(embedg_VES_is_ext_face_marked(embed_graph, n, vv,
+ MARK_MINORS(n)));
+
+ /*
+ mark the edges (u, x), (u, y)
+ */
+ embedg_add_mark_u_x(dfs_tree, back_edges,
+ embed_graph, n, edge_pos, v, x, &u_x,
+ MARK_MINORS(n));
+ embedg_add_mark_u_x(dfs_tree, back_edges,
+ embed_graph, n, edge_pos, v, y, &u_y,
+ MARK_MINORS(n));
+
+ /*
+ mark the dotted edges (v, w), (v, u)
+ */
+ embedg_add_mark_v_w_for_B(dfs_tree, back_edges,
+ embed_graph, n, edge_pos, v, w,
+ &u_z, MARK_MINORS(n));
+
+ /*
+ mark the tree path from max(u_x, u_y, u_z) to min(u_x, u_y, u_z)
+ */
+ u_max = u_x > u_y ? u_x : u_y;
+ u_max = u_max > u_z ? u_max : u_z;
+ u_min = u_x < u_y ? u_x : u_y;
+ u_min = u_min < u_z ? u_min : u_z;
+ embedg_mark_tree_path(embed_graph, n, u_max, u_min, MARK_MINORS(n));
+
+ IF_DEB(
+ fprintf(stdout, "mark minor B\n");
+ fprintf(stdout, "v %d\t c %d\t x %d\t y %d\t w %d\t u_x %d\t u_y %d\t u_z %d\n",
+ v, c, x, y, w, u_x, u_y, u_z);
+ )
+}
+
+void
+embedg_mark_minor_C (t_dlcl **dfs_tree, t_dlcl **back_edges,
+ t_ver_edge *embed_graph, int n, int *edge_pos, int v,
+ int c, int x, int y, int w, int *path_v, int *path_e,
+ int nbr_v, boolean px_attached_high, boolean py_attached_high)
+{
+ int vv, p_x, p_y, u_x, u_y, u;
+
+ vv = c + n;
+ p_x = path_v[0];
+ p_y = path_v[nbr_v];
+ /*
+ see embedg_iso_get_highest_x_y_path for the above
+ */
+
+ if (px_attached_high)
+ /*
+ mark the external face:
+ - from v^c to p_y if py_attached_high
+ - from v^c to y if !py_attached_high
+
+ not too sure about that one....
+
+ from v^c to p_y: so vvin = 0,
+ in x's direction
+ */
+ {
+ if (py_attached_high)
+ embedg_VES_walk_mark_part_ext_face(embed_graph, n, vv, 0,
+ vv, p_y, MARK_MINORS(n));
+ else
+ embedg_VES_walk_mark_part_ext_face(embed_graph, n, vv, 0,
+ vv, y, MARK_MINORS(n));
+ }
+ else
+ /*
+ symmetric case:
+ mark the external face from v^c to p_x: so vvin = 1,
+ in y's direction
+ */
+ {
+ if (px_attached_high)
+ embedg_VES_walk_mark_part_ext_face(embed_graph, n, vv, 1,
+ vv, p_x, MARK_MINORS(n));
+ else
+ embedg_VES_walk_mark_part_ext_face(embed_graph, n, vv, 1,
+ vv, x, MARK_MINORS(n));
+ }
+
+ /*
+ mark the edges (u, x), (u, y), (v, w)
+ */
+ embedg_add_mark_u_x(dfs_tree, back_edges,
+ embed_graph, n, edge_pos, v, x, &u_x,
+ MARK_MINORS(n));
+ embedg_add_mark_u_x(dfs_tree, back_edges,
+ embed_graph, n, edge_pos, v, y, &u_y,
+ MARK_MINORS(n));
+ embedg_add_mark_v_w(dfs_tree, back_edges,
+ embed_graph, n, edge_pos, v, w,
+ MARK_MINORS(n));
+
+ /*
+ mark the tree path from v to min(u_x, u_y)
+ */
+ u = u_x <= u_y ? u_x : u_y;
+ embedg_mark_tree_path(embed_graph, n, v, u, MARK_MINORS(n));
+
+ /*
+ finally, mark the x-y path, ie the vertices in path_v
+ and the edges in path_e
+ */
+ embedg_mark_x_y_path(embed_graph, n, path_v, path_e, nbr_v,
+ MARK_MINORS(n));
+
+ IF_DEB(
+ fprintf(stdout, "mark minor C p_x high %d\t p_y high %d\n",
+ px_attached_high, py_attached_high);
+ fprintf(stdout, "v %d\t c %d\t x %d\t y %d\t w %d\t p_x %d\t p_y %d\t u_x %d\t u_y %d\n",
+ v, c, x, y, w, p_x, p_y, u_x, u_y);
+ )
+}
+
+void
+embedg_mark_minor_D (t_dlcl **dfs_tree, t_dlcl **back_edges,
+ t_ver_edge *embed_graph, int n, int *edge_pos, int v,
+ int c, int x, int y, int w, int *path_v, int *path_e,
+ int nbr_v, int entry_in_path_e)
+{
+ int i, vv, p_x, p_y, u_x, u_y, u;
+
+ vv = c + n;
+ p_x = path_v[0];
+ p_y = path_v[nbr_v];
+ /*
+ see embedg_iso_get_highest_x_y_path for the above
+ */
+
+ /*
+ mark the lower external face from x to y: we can walk in
+ either direction
+ */
+ embedg_VES_walk_mark_part_ext_face(embed_graph, n, vv, 0,
+ x, y, MARK_MINORS(n));
+
+ /*
+ mark the internal path which goes from the x-y path to v
+ - since I haven't stored those vertices/edges I assume
+ that a proper face walk should suffice
+
+ BUT a walk that say starts at p_x and ends at vv,
+ that is, a walk starting at path_e[1] entered from entry_in_path_e
+ (recall that path_e[0] is a dummy)
+ */
+ embedg_VES_walk_mark_part_proper_face(embed_graph, n,
+ path_e[1], entry_in_path_e,
+ vv, MARK_MINORS(n));
+
+ /*
+ a note of caution here:
+ ALWAYS mark external/internal faces before adding any other edges:
+ since adding edges destroys the faces' consistency
+ (adding edges makes no sense of face since we are in a non-planar
+ situation)
+ */
+ /*
+ mark the edges (u, x), (u, y), (v, w)
+ */
+ embedg_add_mark_u_x(dfs_tree, back_edges,
+ embed_graph, n, edge_pos, v, x, &u_x,
+ MARK_MINORS(n));
+ embedg_add_mark_u_x(dfs_tree, back_edges,
+ embed_graph, n, edge_pos, v, y, &u_y,
+ MARK_MINORS(n));
+ embedg_add_mark_v_w(dfs_tree, back_edges,
+ embed_graph, n, edge_pos, v, w,
+ MARK_MINORS(n));
+
+ /*
+ mark the tree path from v to min(u_x, u_y)
+ */
+ u = u_x <= u_y ? u_x : u_y;
+ embedg_mark_tree_path(embed_graph, n, v, u, MARK_MINORS(n));
+
+ /*
+ mark the x-y path, ie the vertices in path_v
+ and the edges in path_e
+ */
+ embedg_mark_x_y_path(embed_graph, n, path_v, path_e, nbr_v,
+ MARK_MINORS(n));
+
+ IF_DEB(
+ fprintf(stdout, "mark minor D\n");
+ fprintf(stdout, "v %d\t c %d\t x %d\t y %d\t w %d\t p_x %d\t p_y %d\t u_x %d\t u_y %d\n",
+ v, c, x, y, w, p_x, p_y, u_x, u_y);
+ )
+}
+
+
+
+
+minor
+embedg_mark_minor_E (t_dlcl **dfs_tree, t_dlcl **back_edges,
+ t_ver_edge *embed_graph, int n, int *edge_pos, int v,
+ int c, int x, int y, int w, int *path_v, int *path_e, int nbr_v)
+ /*
+ while marking minor E return which of the minors we are dealing with
+ */
+{
+ int vv, p_x, p_y, u_x, u_y, u_w, u, u_max, u_min;
+
+ vv = c + n;
+ p_x = path_v[0];
+ p_y = path_v[nbr_v];
+ /*
+ see embedg_iso_get_highest_x_y_path for the above
+ */
+
+ if (!embedg_VES_is_ver_ext_active(embed_graph, n, v, w))
+ /*
+ minor E1 case: we must find an ext. active z, distinct from w,
+ on the external face p_x..w..p_y
+ */
+ {
+ int s, sin, cur, curin, z, u_z, u_xy;
+
+ s = n;
+ /*
+ start searching at vv entered from 0 (in x's direction)
+ -- we MUST reach p_x - hopefully! :)
+ */
+ cur = vv;
+ curin = 0;
+ while (s != p_x)
+ /*
+ first advance to p_x: we are sure of reaching it
+ */
+ {
+ embedg_VES_get_succ_on_ext_face(embed_graph, n,
+ cur, curin,
+ FALSE, 0, &s, &sin);
+ cur = s;
+ curin = sin;
+ }
+
+ /*
+ continue the walk on the external face:
+ stop if either s is ext. active OR s == w
+
+ we'll mark the lot later on
+ */
+ while (
+ !(embedg_VES_is_ver_ext_active(embed_graph, n, v,
+ s)
+ && s != p_x)
+ && s != w)
+ {
+ embedg_VES_get_succ_on_ext_face(embed_graph, n, cur, curin,
+ FALSE, 0, &s, &sin);
+ cur = s;
+ curin = sin;
+ }
+ /*
+ now we must decide which symmetry we are in
+ */
+ if (embedg_VES_is_ver_ext_active(embed_graph, n, v, s))
+ /*
+ z is between x and w (recall that w is NOT ext. active)
+ */
+ {
+ z = s;
+ ASSERT(z != w);
+
+ /*
+ mark the external face from v^c to y in x's direction
+ */
+ embedg_VES_walk_mark_part_ext_face(embed_graph, n, vv, 0,
+ vv, y, MARK_MINORS(n));
+ /*
+ add/mark dotted edge (u, y)
+ */
+ embedg_add_mark_u_x(dfs_tree, back_edges,
+ embed_graph, n, edge_pos,
+ v, y, &u_xy, MARK_MINORS(n));
+ }
+ else
+ /*
+ this is the symmetric case: must find z between w and p_y
+ */
+ {
+ ASSERT(s == w);
+ embedg_VES_get_succ_ext_active_on_ext_face(embed_graph, n,
+ v, cur, curin,
+ FALSE, 0,
+ &s, &sin);
+ /*
+ and z is distinct from p_y!
+ */
+ z = s;
+ ASSERT(z != p_y);
+
+ /*
+ mark the external face from v^c to x in y's direction
+ */
+ embedg_VES_walk_mark_part_ext_face(embed_graph, n, vv, 1,
+ vv, x, MARK_MINORS(n));
+ /*
+ add/mark dotted edge (u, x)
+ */
+ embedg_add_mark_u_x(dfs_tree, back_edges,
+ embed_graph, n, edge_pos,
+ v, x, &u_xy, MARK_MINORS(n));
+ }
+ /*
+ now the marked bits which are common to both cases:
+ dotted edges (u, z), (v, w), the x-y path,
+ the tree path (v, min(u_xy, u_z))
+ */
+ embedg_add_mark_u_x(dfs_tree, back_edges,
+ embed_graph, n, edge_pos,
+ v, z, &u_z, MARK_MINORS(n));
+ embedg_add_mark_v_w(dfs_tree, back_edges,
+ embed_graph, n, edge_pos, v, w,
+ MARK_MINORS(n));
+
+ embedg_mark_x_y_path(embed_graph, n, path_v, path_e, nbr_v,
+ MARK_MINORS(n));
+
+ u = u_z <= u_xy ? u_z : u_xy;
+ embedg_mark_tree_path(embed_graph, n, v, u, MARK_MINORS(n));
+
+ IF_DEB(
+ fprintf(stdout, "mark minor E1\n");
+ fprintf(stdout, "v %d\t c %d\t x %d\t y %d\t z %d\t w %d\t p_x %d\t p_y %d\t u_xy %d\t u_z %d\n",
+ v, c, x, y, z, w, p_x, p_y, u_xy, u_z);
+ )
+
+ return MINOR_E1;
+ }
+
+ /*
+ in all other cases we get u_x, u_y, u_w back
+ from the ext. active vertices x, y, w resp.
+
+ again, I CANNOT embed these edges now since that would destroy
+ my external/internal faces
+ */
+
+ embedg_get_u_x(embed_graph, n, v, x, &u_x);
+ embedg_get_u_x(embed_graph, n, v, y, &u_y);
+ embedg_get_u_x(embed_graph, n, v, w, &u_w);
+
+ if (u_w > u_x && u_w > u_y)
+ /*
+ minor E2 case:
+ we mark the whole external face rooted by v^c
+ and the tree path (v, min(u_x, u_y))
+ */
+ {
+ embedg_VES_walk_mark_ext_face(embed_graph, n, vv,
+ MARK_MINORS(n));
+ u = u_x <= u_y ? u_x : u_y;
+ embedg_mark_tree_path(embed_graph, n, v, u, MARK_MINORS(n));
+
+ /*
+ embed dotted edges (u, x), (u, y) & (u, w)
+ */
+ embedg_add_mark_u_x(dfs_tree, back_edges,
+ embed_graph, n, edge_pos,
+ v, x, &u_x, MARK_MINORS(n));
+ embedg_add_mark_u_x(dfs_tree, back_edges,
+ embed_graph, n, edge_pos,
+ v, y, &u_y, MARK_MINORS(n));
+ embedg_add_mark_u_x(dfs_tree, back_edges,
+ embed_graph, n, edge_pos,
+ v, w, &u_w, MARK_MINORS(n));
+
+ IF_DEB(
+ fprintf(stdout, "mark minor E2\n");
+ fprintf(stdout, "v %d\t c %d\t x %d\t y %d\t w %d\t p_x %d\t p_y %d\t u_x %d\t u_y %d\t u_w %d\n",
+ v, c, x, y, w, p_x, p_y, u_x, u_y, u_w);
+ )
+
+ return MINOR_E2;
+ }
+
+ /*
+ two more things common to all remaining cases:
+
+ mark the dotted edge (v, w) (but we MUST do that later)
+
+ mark the x-y path
+ */
+ embedg_mark_x_y_path(embed_graph, n, path_v, path_e, nbr_v,
+ MARK_MINORS(n));
+
+ if (u_x < u_y && u_w < u_y)
+ /*
+ minor E3 case: one of the symmetric cases:
+ the external face rooted at v_c from vv to x (in x's direction)
+ the external face rooted at v_c from y to w (in y's direction)
+ the (v, min(u_w, u_x)) tree path
+ */
+ {
+ embedg_VES_walk_mark_part_ext_face(embed_graph, n, vv, 0,
+ vv, p_x, MARK_MINORS(n));
+ embedg_VES_walk_mark_part_ext_face(embed_graph, n, vv, 1,
+ y, w, MARK_MINORS(n));
+
+ u = u_x <= u_w ? u_x : u_w;
+ embedg_mark_tree_path(embed_graph, n, v, u, MARK_MINORS(n));
+
+ /*
+ embed dotted edges (u, x), (u, y), (u, w), (v, w)
+ */
+ embedg_add_mark_u_x(dfs_tree, back_edges,
+ embed_graph, n, edge_pos,
+ v, x, &u_x, MARK_MINORS(n));
+ embedg_add_mark_u_x(dfs_tree, back_edges,
+ embed_graph, n, edge_pos,
+ v, y, &u_y, MARK_MINORS(n));
+ embedg_add_mark_u_x(dfs_tree, back_edges,
+ embed_graph, n, edge_pos,
+ v, w, &u_w, MARK_MINORS(n));
+ embedg_add_mark_v_w(dfs_tree, back_edges,
+ embed_graph, n, edge_pos, v, w,
+ MARK_MINORS(n));
+
+ IF_DEB(
+ fprintf(stdout, "mark minor E3/a\n");
+ fprintf(stdout, "v %d\t c %d\t x %d\t y %d\t w %d\t p_x %d\t p_y %d\t u_x %d\t u_y %d\t u_w %d\n",
+ v, c, x, y, w, p_x, p_y, u_x, u_y, u_w);
+ )
+
+ return MINOR_E3;
+ }
+ if (u_y < u_x && u_w < u_x)
+ /*
+ minor E3 case: the other symmetric case:
+ the external face rooted at v_c from vv to y (in y's direction)
+ the external face rooted at v_c from x to w (in x's direction)
+ the (v, min(u_w, u_y)) tree path
+ */
+ {
+ embedg_VES_walk_mark_part_ext_face(embed_graph, n, vv, 1,
+ vv, p_y, MARK_MINORS(n));
+ embedg_VES_walk_mark_part_ext_face(embed_graph, n, vv, 0,
+ x, w, MARK_MINORS(n));
+
+ u = u_y <= u_w ? u_y : u_w;
+ embedg_mark_tree_path(embed_graph, n, v, u, MARK_MINORS(n));
+
+ /*
+ embed dotted edges (u, x), (u, y), (u, w), (v, w)
+ */
+ embedg_add_mark_u_x(dfs_tree, back_edges,
+ embed_graph, n, edge_pos,
+ v, x, &u_x, MARK_MINORS(n));
+ embedg_add_mark_u_x(dfs_tree, back_edges,
+ embed_graph, n, edge_pos,
+ v, y, &u_y, MARK_MINORS(n));
+ embedg_add_mark_u_x(dfs_tree, back_edges,
+ embed_graph, n, edge_pos,
+ v, w, &u_w, MARK_MINORS(n));
+ embedg_add_mark_v_w(dfs_tree, back_edges,
+ embed_graph, n, edge_pos, v, w,
+ MARK_MINORS(n));
+
+ IF_DEB(
+ fprintf(stdout, "mark minor E3/b\n");
+ fprintf(stdout, "v %d\t c %d\t x %d\t y %d\t w %d\t p_x %d\t p_y %d\t u_x %d\t u_y %d\t u_w %d\n",
+ v, c, x, y, w, p_x, p_y, u_x, u_y, u_w);
+ )
+
+ return MINOR_E3;
+ }
+
+ if (p_x != x)
+ /*
+ minor E4 case: one of the symmetric cases:
+ the external face rooted at v_c from vv to w (in x's direction)
+ the external face rooted at v_c from vv to p_y (in y's direction)
+ the tree path from max(u_x, u_y, u_w) to min(u_x, u_y, u_w)
+ */
+ {
+ embedg_VES_walk_mark_part_ext_face(embed_graph, n, vv, 0,
+ vv, w, MARK_MINORS(n));
+ embedg_VES_walk_mark_part_ext_face(embed_graph, n, vv, 1,
+ vv, p_y, MARK_MINORS(n));
+
+ u_max = u_x > u_y ? u_x : u_y;
+ u_max = u_max > u_w ? u_max : u_w;
+ u_min = u_x < u_y ? u_x : u_y;
+ u_min = u_min < u_w ? u_min : u_w;
+ embedg_mark_tree_path(embed_graph, n, u_max, u_min, MARK_MINORS(n));
+
+ /*
+ embed dotted edges (u, x), (u, y), (u, w), (v, w)
+ */
+ embedg_add_mark_u_x(dfs_tree, back_edges,
+ embed_graph, n, edge_pos,
+ v, x, &u_x, MARK_MINORS(n));
+ embedg_add_mark_u_x(dfs_tree, back_edges,
+ embed_graph, n, edge_pos,
+ v, y, &u_y, MARK_MINORS(n));
+ embedg_add_mark_u_x(dfs_tree, back_edges,
+ embed_graph, n, edge_pos,
+ v, w, &u_w, MARK_MINORS(n));
+ embedg_add_mark_v_w(dfs_tree, back_edges,
+ embed_graph, n, edge_pos, v, w,
+ MARK_MINORS(n));
+
+ IF_DEB(
+ fprintf(stdout, "mark minor E4/a\n");
+ fprintf(stdout, "v %d\t c %d\t x %d\t y %d\t w %d\t p_x %d\t p_y %d\t u_x %d\t u_y %d\t u_w %d\n",
+ v, c, x, y, w, p_x, p_y, u_x, u_y, u_w);
+ )
+
+ return MINOR_E4;
+ }
+ if (p_y != y)
+ /*
+ minor E4 case: the other symmetric case:
+ the external face rooted at v_c from vv to w (in y's direction)
+ the external face rooted at v_c from vv to x (in x's direction)
+ (here p_x = x!)
+ the tree path from max(u_x, u_y, u_w) to min(u_x, u_y, u_w)
+ */
+ {
+ embedg_VES_walk_mark_part_ext_face(embed_graph, n, vv, 1,
+ vv, w, MARK_MINORS(n));
+ embedg_VES_walk_mark_part_ext_face(embed_graph, n, vv, 0,
+ vv, x, MARK_MINORS(n));
+
+ u_max = u_x > u_y ? u_x : u_y;
+ u_max = u_max > u_w ? u_max : u_w;
+ u_min = u_x < u_y ? u_x : u_y;
+ u_min = u_min < u_w ? u_min : u_w;
+ embedg_mark_tree_path(embed_graph, n, u_max, u_min, MARK_MINORS(n));
+
+ /*
+ embed dotted edges (u, x), (u, y), (u, w), (v, w)
+ */
+ embedg_add_mark_u_x(dfs_tree, back_edges,
+ embed_graph, n, edge_pos,
+ v, x, &u_x, MARK_MINORS(n));
+ embedg_add_mark_u_x(dfs_tree, back_edges,
+ embed_graph, n, edge_pos,
+ v, y, &u_y, MARK_MINORS(n));
+ embedg_add_mark_u_x(dfs_tree, back_edges,
+ embed_graph, n, edge_pos,
+ v, w, &u_w, MARK_MINORS(n));
+ embedg_add_mark_v_w(dfs_tree, back_edges,
+ embed_graph, n, edge_pos, v, w,
+ MARK_MINORS(n));
+
+ IF_DEB(
+ fprintf(stdout, "mark minor E$/b\n");
+ fprintf(stdout, "v %d\t c %d\t x %d\t y %d\t w %d\t p_x %d\t p_y %d\t u_x %d\t u_y %d\t u_w %d\n",
+ v, c, x, y, w, p_x, p_y, u_x, u_y, u_w);
+ )
+
+ return MINOR_E4;
+ }
+
+ /*
+ this is the last case for minor E: when the homeomorph is K5
+
+ mark the whole external face rooted at v^c
+ mark the tree path from v to min(u_x, u_y, u_w)
+ */
+
+ embedg_VES_walk_mark_ext_face(embed_graph, n, vv, MARK_MINORS(n));
+
+ u = u_x < u_y ? u_x : u_y;
+ u = u < u_w ? u : u_w;
+ embedg_mark_tree_path(embed_graph, n, v, u, MARK_MINORS(n));
+
+ /*
+ embed dotted edges (u, x), (u, y), (u, w), (v, w)
+ */
+ embedg_add_mark_u_x(dfs_tree, back_edges,
+ embed_graph, n, edge_pos,
+ v, x, &u_x, MARK_MINORS(n));
+ embedg_add_mark_u_x(dfs_tree, back_edges,
+ embed_graph, n, edge_pos,
+ v, y, &u_y, MARK_MINORS(n));
+ embedg_add_mark_u_x(dfs_tree, back_edges,
+ embed_graph, n, edge_pos,
+ v, w, &u_w, MARK_MINORS(n));
+ embedg_add_mark_v_w(dfs_tree, back_edges,
+ embed_graph, n, edge_pos, v, w,
+ MARK_MINORS(n));
+
+ IF_DEB(
+ fprintf(stdout, "mark minor E5\n");
+ fprintf(stdout, "v %d\t c %d\t x %d\t y %d\t w %d\t p_x %d\t p_y %d\t u_x %d\t u_y %d\t u_w %d\n",
+ v, c, x, y, w, p_x, p_y, u_x, u_y, u_w);
+ )
+
+ return MINOR_E5;
+}
+/*
+ * proper_face_walk.c
+ */
+
+/*
+ What:
+ *****
+
+ Implementing a proper face walk within the VES structure.
+ This is obviously not the same as an external face walk,
+ but is simply the standard face walk in a planar embedding.
+
+ Not much to say, if only to emphasize that for our
+ purposes here we assume:
+
+ 1. the short-cut edges have been removed from the VES structure
+ 2. each vertex/edge has been given its orientation
+ 2. the adjacency lists (vertex + plus its incident edges)
+ are consistent: (and this is IMPORTANT)
+ that is, the way to traverse an adj. list (ie what
+ constitute previous and next in the list which actually
+ is a planar embedding at this stage) is indicated
+ by the vertex/edge's orientation
+
+
+ try to explain this better another time.... sorry...
+
+
+
+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ from
+
+ Simplified O(n) Planarity Algorithms (draft)
+ ************************************
+
+ John Boyer JBoyer@PureEdge.com, jboyer@acm.org
+ Wendy Myrvold wendym@csr.uvic.ca
+
+
+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ authors:
+ ********
+
+ Paulette Lieby (Magma), Brendan McKay (ANU)
+
+ Started October 2001
+*/
+
+
+#include "planarity.h"
+
+#define IF_DEB(x) {}
+#define IF_DEB_PROPER_FACE(x) {}
+#define IF_VERB(x) {}
+
+
+
+/* aproto: header embed_graph_protos.h */
+
+
+
+
+boolean
+embedg_VES_get_succ_on_proper_face_with_avoidance (t_ver_edge *embed_graph,
+ int n, int e, int ein, int a, boolean MARK, int mark, int *s,
+ int *next_e, int *next_ein)
+ /*
+ find the successor s of embed_graph[e].neighbour
+ (entered via ein) on a proper face traversal
+ which avoids (the vertex) a if a != n
+
+ also returns the edge next_e such that
+ embed_graph[next_e].neighbour = s (to allow for continuation
+ of the walk)
+
+ assumes that short-cut edges have been removed and that each
+ edge/vertex has been given its orientation
+
+ and (more importantly) assumes that adjacency lists are consistent
+
+ this function has been written especially to retrieve the highest
+ x-y path for the isolator;
+ (see embedg_iso_get_highest_x_y_path)
+ but as I discovered later (when marking an internal face
+ as for minor D) this function is general purpose
+
+ PLUS: return true if the proper face walk has to skip an edge
+ incident to a (ie had to "avoid" a)
+
+ PLUS: mark s & next_e if so requested
+ */
+{
+ int eout;
+ int twin, twinout;
+ boolean avoid_a;
+
+ ASSERT(embedg_VES_is_edge(n, e));
+ ASSERT(!embedg_VES_is_short_cut_edge(embed_graph, n, e));
+
+ IF_DEB(
+ fprintf(stdout, "get_succ_on_proper_face, \n");
+ )
+
+ avoid_a = FALSE;
+ /*
+ find the direction out of the edge
+ */
+ eout = 1 ^ ein;
+
+ /*
+ get the twin edge
+ */
+ twin = embedg_VES_get_twin_edge(embed_graph, n, e);
+
+ /*
+ for each edge we must set the way to get to the next
+ in the adjacency list:
+ adjacency lists are traversed according to the vertex/edges
+ orientation (one unique orientation per list of course)
+ */
+ if (embed_graph[e].sign != embed_graph[twin].sign)
+ /*
+ invert traversal
+ */
+ {
+ twinout = 1 ^ eout;
+ }
+ else
+ /*
+ traversal is identical
+ */
+ {
+ twinout = eout;
+ }
+
+ /*
+ now, we want the edge previous to twin in twin's adjacency list,
+ ie link[1 ^ twinout]
+ */
+ *next_e = embed_graph[twin].link[1 ^ twinout];
+ /*
+ next_e could be a vertex, I need an edge
+ */
+ if (embedg_VES_is_vertex(n, *next_e)
+ || embedg_VES_is_virtual_vertex(n, *next_e))
+ /*
+ at this stage all virtual vertices should have
+ been disabled BUT the vertices rooting the bicomps!!!
+ */
+ {
+ *next_e = embed_graph[*next_e].link[1 ^ twinout];
+ }
+ ASSERT(embedg_VES_is_edge(n, *next_e));
+ ASSERT(!embedg_VES_is_short_cut_edge(embed_graph, n, e));
+ *s = embed_graph[*next_e].neighbour;
+
+ if (*s == a)
+ /*
+ want to avoid this vertex, so must get yet previous
+ edge in adjacency list
+ */
+ {
+ avoid_a = TRUE;
+
+ *next_e = embed_graph[*next_e].link[1 ^ twinout];
+ if (embedg_VES_is_vertex(n, *next_e)
+ || embedg_VES_is_virtual_vertex(n, *next_e))
+ {
+ *next_e = embed_graph[*next_e].link[1 ^ twinout];
+ }
+ ASSERT(embedg_VES_is_edge(n, *next_e));
+ ASSERT(!embedg_VES_is_short_cut_edge(embed_graph, n, e));
+ }
+ *s = embed_graph[*next_e].neighbour;
+ ASSERT(*s != a);
+
+ /*
+ finally (again, because lists are consistent)
+ */
+ *next_ein = 1 ^ twinout;
+
+ /*
+ now mark s and next_e if required
+ */
+ if (MARK)
+ {
+ embed_graph[*s].visited =
+ embed_graph[*next_e].visited = mark;
+ /*
+ ouuh... must mark the twin as well....
+ but ONLY when we mark the minors....
+ that is poor design, can we do better????
+ -- don't think so...
+
+ (when we mark when counting the faces, we MUST only
+ mark the edge and NOT its twin)
+ */
+ if (mark == MARK_MINORS(n))
+ {
+ twin =
+ embedg_VES_get_twin_edge(embed_graph, n, *next_e);
+ embed_graph[twin].visited = mark;
+ }
+ }
+
+ return avoid_a;
+}
+
+
+
+void
+embedg_VES_get_succ_on_proper_face (t_ver_edge *embed_graph, int n, int e,
+ int ein, int MARK, int mark, int *s, int *next_e, int *next_ein)
+ /*
+ same as above but without avoidance
+ */
+{
+ boolean avoid;
+
+ avoid =
+ embedg_VES_get_succ_on_proper_face_with_avoidance(
+ embed_graph, n,
+ e, ein, n,
+ MARK, mark,
+ s, next_e, next_ein);
+ ASSERT(avoid == FALSE);
+}
+
+
+void
+embedg_VES_walk_proper_face (t_ver_edge *embed_graph, int n, int e,
+ int ein, boolean MARK, int mark)
+ /*
+ traversing a proper face starting at edge e which has been entered
+ via ein
+
+ -- we mark the visited edges with mark if so requested
+
+ assumes that short-cut edges have been removed and that each
+ edge/vertex has been given its orientation
+ */
+{
+ int s, cur_e, cur_ein, next_e, next_ein;
+
+ next_e = n; /* this is an invalid value for an edge */
+
+ IF_DEB_PROPER_FACE(
+ fprintf(stdout, "proper face traversal\n");
+ )
+
+ cur_e = e;
+ cur_ein = ein;
+ while (next_e != e)
+ {
+ ASSERT(embedg_VES_is_edge(n, cur_e));
+ ASSERT(!embedg_VES_is_short_cut_edge(embed_graph,
+ n, cur_e));
+ IF_DEB_PROPER_FACE(
+ embedg_VES_print_edge(embed_graph, n, cur_e);
+ )
+
+ embedg_VES_get_succ_on_proper_face(embed_graph, n,
+ cur_e, cur_ein,
+ MARK, mark,
+ &s, &next_e, &next_ein);
+ cur_e = next_e;
+ cur_ein = next_ein;
+ }
+
+ /*
+ note that by doing so we would have marked e and the first of e's
+ endpoints since by exiting the loop e = next_e and s is the
+ actual starting vertex of the walk
+ */
+}
+
+
+
+
+
diff --git a/src/plugin0.c b/src/plugin0.c
index 5793fae..c093ac2 100644
--- a/src/plugin0.c
+++ b/src/plugin0.c
@@ -1,14 +1,14 @@
-/* This is a plugin for surge that removes molecules with
- two atoms having three or more common neighbours.
- Also, arsenic at valence 3 and 5 is added. */
-
-#define HELPTEXT2 " This version removes molecules with K(2,3).\n"
-
-#define SURGEPLUGIN_INIT \
- addelement("As","As",3,3); addelement("Az","As",5,5);
-
-#define SURGEPLUGIN_STEP0 \
- { int ii,jj; \
- for (jj = n; --jj >= 1; ) \
- for (ii = jj; --ii >= 0; ) \
- if (POPCOUNT(g[ii] & g[jj]) >= 3) return 1; }
+/* This is a plugin for surge that removes molecules with
+ two atoms having three or more common neighbours.
+ Also, arsenic at valence 3 and 5 is added. */
+
+#define HELPTEXT2 " This version removes molecules with K(2,3).\n"
+
+#define SURGEPLUGIN_INIT \
+ addelement("As","As",3,3); addelement("Az","As",5,5);
+
+#define SURGEPLUGIN_STEP0 \
+ { int ii,jj; \
+ for (jj = n; --jj >= 1; ) \
+ for (ii = jj; --ii >= 0; ) \
+ if (POPCOUNT(g[ii] & g[jj]) >= 3) return 1; }
diff --git a/src/plugin1.c b/src/plugin1.c
index f642376..23a7cef 100644
--- a/src/plugin1.c
+++ b/src/plugin1.c
@@ -1,17 +1,17 @@
-/* This is a plugin for surge that implements an extra option
- -F# or -F#:# for the number of atoms with exactly 4 distinct
- non-H neighbours. */
-
-#define HELPTEXT2 \
-" -F# -F#:# Specify number of atoms with exactly 4 non-H neighbours\n"
-
-static boolean Fswitch = FALSE;
-static long Fmin,Fmax;
-
-#define SURGEPLUGIN_STEP1 \
- { int ii,Fval; Fval=0; \
- for (ii = 0; ii < n; ++ii) if (deg[ii] == 4) ++Fval; \
- if (Fswitch && (Fval < Fmin || Fval > Fmax)) return; }
-
-#define SURGEPLUGIN_SWITCHES \
- SWRANGE('F',":-",Fswitch,Fmin,Fmax,"surge -F")
+/* This is a plugin for surge that implements an extra option
+ -V# or -V#:# for the number of atoms with exactly 4 distinct
+ non-H neighbours. */
+
+#define HELPTEXT2 \
+" -V# -V#:# Specify number of atoms with exactly 4 non-H neighbours\n"
+
+static boolean Vswitch = FALSE;
+static long Vmin,Vmax;
+
+#define SURGEPLUGIN_STEP1 \
+ { int ii,Vval; Vval=0; \
+ for (ii = 0; ii < n; ++ii) if (deg[ii] == 4) ++Vval; \
+ if (Vswitch && (Vval < Vmin || Vval > Vmax)) return; }
+
+#define SURGEPLUGIN_SWITCHES \
+ SWRANGE('V',":-",Vswitch,Vmin,Vmax,"surge -V")
diff --git a/src/plugin2.c b/src/plugin2.c
index 919cf03..f207abf 100644
--- a/src/plugin2.c
+++ b/src/plugin2.c
@@ -1,15 +1,15 @@
-/* This is a plugin for surge that optionally forbids
- adjacent oxygen atoms. */
-
-#define HELPTEXT2 \
-" This version forbids adjacent oxygen atoms if -Y is given.\n"
-
-static boolean Yswitch = FALSE;
-#define SURGEPLUGIN_SWITCHES SWBOOLEAN('Y',Yswitch)
-
-static int oxygenindex = -1;
-#define SURGEPLUGIN_STEP2 \
- if (oxygenindex < 0) oxygenindex = elementindex("O"); \
- if (Yswitch) { int ii; for (ii = 0; ii < ne; ++ii) \
- if (vcol[edge[ii].x] == oxygenindex \
- && vcol[edge[ii].y] == oxygenindex) return; }
+/* This is a plugin for surge that optionally forbids
+ adjacent oxygen atoms. */
+
+#define HELPTEXT2 \
+" This version forbids adjacent oxygen atoms if -Y is given.\n"
+
+static boolean Yswitch = FALSE;
+#define SURGEPLUGIN_SWITCHES SWBOOLEAN('Y',Yswitch)
+
+static int oxygenindex = -1;
+#define SURGEPLUGIN_STEP2 \
+ if (oxygenindex < 0) oxygenindex = elementindex("O"); \
+ if (Yswitch) { int ii; for (ii = 0; ii < ne; ++ii) \
+ if (vcol[edge[ii].x] == oxygenindex \
+ && vcol[edge[ii].y] == oxygenindex) return; }
diff --git a/src/plugin3.c b/src/plugin3.c
index a279347..89c8bd2 100644
--- a/src/plugin3.c
+++ b/src/plugin3.c
@@ -1,19 +1,17 @@
-/* This is a plugin for surge that counts how many hydrogen atoms
- are attached to carbon atoms. */
-
-#define HELPTEXT2 \
-" This version counts the hydrogen atoms attached to carbon atoms.\n"
-
-static int carbonindex = -1; /* index into element table */
-static long long CHcount[5*MAXN+1];
-
-#define SURGEPLUGIN_STEP3 \
- if (carbonindex < 0) carbonindex = elementindex("C"); \
- { int ii,CHval; CHval=0; for (ii = 0; ii < n; ++ii) \
- if (vcol[ii] == carbonindex) CHval += hyd[ii]; \
- ++CHcount[CHval]; }
-
-#define SURGEPLUGIN_SUMMARY \
- fprintf(stderr,"Counts by the number of hydrogens attached to carbons:\n"); \
- { int ii; for (ii = 0; ii <= 5*MAXN; ++ii) \
- if (CHcount[ii] > 0) fprintf(stderr," %2d : %lld\n",ii,CHcount[ii]); }
+/* This is a plugin for surge that counts how many hydrogen atoms
+ are attached to carbon atoms. */
+
+#define HELPTEXT2 \
+" This version counts the hydrogen atoms attached to carbon atoms.\n"
+
+static long long CHcount[5*MAXN+1]={0};
+
+#define SURGEPLUGIN_STEP3 \
+ { int ii,CHval; CHval=0; for (ii = 0; ii < n; ++ii) \
+ if (vcol[ii] == carbonindex) CHval += hyd[ii]; \
+ ++CHcount[CHval]; }
+
+#define SURGEPLUGIN_SUMMARY \
+ fprintf(stderr,"Counts by the number of hydrogens attached to carbons:\n"); \
+ { int ii; for (ii = 0; ii <= 5*MAXN; ++ii) \
+ if (CHcount[ii] > 0) fprintf(stderr," %2d : %lld\n",ii,CHcount[ii]); }
diff --git a/src/surge.c b/src/surge.c
index 8d4a31f..bad0383 100644
--- a/src/surge.c
+++ b/src/surge.c
@@ -1,2587 +1,2931 @@
-/* This is a molecule generator based on geng.
- Version 1.0, November 11, 2021.
-
- Unix-style compilation command would be:
-
- gcc -o surge -O3 -DWORDSIZE=32 -DMAXN=WORDSIZE -DOUTPROC=surgeproc \
- -march=native -mtune=native -DPREPRUNE=surgepreprune \
- -DPRUNE=surgeprune -DGENG_MAIN=geng_main \
- surge.c geng.c planarity.c nautyW1.a
-
- You can build-in gzip output using the zlib library (https://zlib.net).
- Add -DZLIB to the compilation, and link with the zlib library either
- by adding -lz or libz.a . This will activate the -z command to gzip
- the output.
-
- gcc -o surge -O3 -DWORDSIZE=32 -DMAXN=WORDSIZE -DOUTPROC=surgeproc \
- -march=native -mtune=native -DPREPRUNE=surgepreprune -DZLIB \
- -DPRUNE=surgeprune -DGENG_MAIN=geng_main \
- surge.c geng.c planarity.c nautyW1.a -lz
-
- There is a makefile in the package; edit the first few lines.
-
- This version works best with geng version 3.2 or later. To use
- with an earlier version, add -DOLDGENG to the compilation command.
-
-***********************************************************************/
-
-#ifdef ZLIB
-#define USAGE \
- "[-oFILE] [-z] [-u|-A|-S] [-T] [-e#|-e#:#] [-d#] [-c#] [-m#/#] formula"
-#else
-#define USAGE \
- "[-oFILE] [-u|-A|-S] [-T] [-e#|-e#:#] [-d#] [-c#] [-m#/#] formula"
-#endif
-
-#define HELPUSECMD
-
-#define HELPTEXT1 \
-"Make chemical graphs from a formula. Version 1.0.\n" \
-" Known elements are C,B,N,P,O,S,H,Cl,F,Br,I at their lowest valences.\n" \
-" Higher valences can be selected using Nx (Nitrogen/5), Sx,Sy (Sulfur 4/6)\n" \
-" Px (Phosphorus/5).\n" \
-"\n" \
-" formula = a formula like C8H6N2\n" \
-"\n" \
-" -u Just count, don't write\n" \
-" -S Output in SMILES format\n" \
-" -A Output in alphabetical format\n" \
-" -O# Output stage: 1 after geng, 2 after vcolg, 3 after multig\n" \
-" Default is to write SDfile format\n" \
-" -e# -e#:# Restrict to given range of distinct non-H bonds\n" \
-" -t# -t#:# Limit number of rings of length 3\n" \
-" -f# -f#:# Limit number of cycles of length 4\n" \
-" -p# -p#:# Limit number of cycles of length 5\n" \
-" -b Only rings of even length (same as only cycles of even length)\n" \
-" -T Disallow triple bonds\n" \
-" -P Require planarity\n" \
-" -d# Maximum degree not counting bond multiplicity or hydrogens (default 4)\n" \
-" -c# Maximum coordination number (default 4). This is the maximum number\n" \
-" of distinct atoms (including H) that an atom can be bonded to\n" \
-" Coordination number > 4 is only allowed if no neighbours are H\n" \
-" -B#,...,# Specify sets of substructures to avoid (details in manual)\n" \
-" 1 = no triple bonds in rings up to length 7\n" \
-" 2 = Bredt's rule for two rings ij with one bond in\n" \
-" common (33, 34, 35, 36, 44, 45)\n" \
-" 3 = Bredt's rule for two rings ij with two bonds in\n" \
-" common (i,j up to 56)\n" \
-" 4 = Bredt's rule for two rings of length 6 sharing three bonds\n" \
-" 5 = no substructures A=A=A (in ring or not)\n" \
-" 6 = no substructures A=A=A in rings up to length 8\n" \
-" For -B5 and -B6, the central atom only has 2 non-H neighbours\n" \
-" 7 = no K_33 or K_24 structure\n" \
-" 8 = none of cone of P4 or K4 with 3-ear\n" \
-" 9 = no atom in more than one ring of length 3 or 4\n" \
-" -v Write more information to stderr\n" \
-" -m#/# Do only a part. The two numbers are res/mod where 0<=res
-#ifdef ZLIB
-#include "zlib.h"
-#endif
-
-#define DEBUG 0
-
-static struct smilesstruct
-{
- int item;
- int x,y,r;
-} smilesskeleton[4*MAXN+6*MAXNE];
-/* Values for the item field */
-#define SM_ATOM 1 /* Atom number x */
-#define SM_BOND 2 /* Bond x,y */
-#define SM_OPEN 3 /* Open ( */
-#define SM_CLOSE 4 /* Close ) */
-#define SM_RING0 5 /* Broken bond x,y for new ring r */
-#define SM_RING1 6 /* End of ring r */
-static int smileslen;
-
-typedef unsigned long long counter; /* For counters that might overflow */
-
-static int hydrogens;
-static int nv; /* number of atoms except H */
-static int numtypes; /* different elements except H */
-#define FORMULALEN 50
-static int elementtype[FORMULALEN],elementcount[FORMULALEN];
- /* Not H, decreasing valence */
-static char canonform[2*FORMULALEN]; /* The formula in canonical order */
-static int valencesum; /* Sum of valences for non-H */
-static int maxtype[8]; /* maxtype[d] is maximum index into
- elementtype/elementcount for vertices of degree d */
-
-/* Used with -A */
-static boolean alphabetic;
-static int newlabel[MAXN]; /* New number of atom i */
-
-#define BADLISTS 9 /* Number of defined bad lists */
-static boolean bad1; /* Avoid triple edges in rings up to length 7 */
- /* bad1 is turned off if -T is given */
-static boolean bad2; /* Bredt's rule for one common bond */
-static boolean bad3; /* Bredt's rule for two common bonds */
-static boolean bad4; /* Bredt's rule for three common bonds */
-static boolean bad5; /* Avoid =A= even if not in a ring */
-static boolean bad6; /* Avoid =A= in rings up to length 8 */
- /* Note that bad6 is turned off if bad5 is set */
-static boolean bad7; /* Avoid K_{2,4} and K_{3,3} */
-static boolean bad8; /* Avoid cone(P4) and K4 with 3-ear */
- /* bad8 is turned off if -t and -f options make it impossible */
-static boolean bad9; /* No atom on two rings of length 3 or 4 */
- /* bad9 is turned off if -t and -f options make it impossible */
-
-static boolean needcoordtest;
-
-static boolean needcycles;
-static int maxcycles=0;
-#define MAXCYCLES 200
-static setword inducedcycle[MAXCYCLES];
-static int cyclecount;
-
-int GENG_MAIN(int argc, char *argv[]); /* geng main() */
-static int min1,min12,max34,max4; /* bounds on degree counts on geng output */
-
-static counter gengout=0, genggood=0;
-static counter vcolgnontriv=0,vcolgout=0;
-static counter multignontriv=0,multigout=0;
-static long maxvgroup,maxegroup;
-
-static boolean uswitch; /* suppress output */
-static boolean verbose; /* print more information to stderr */
-static int outlevel; /* 1 = geng only, 2 = geng+vcolg,
- 3 = geng+vcolg+multig, 4 = everything */
-static boolean smiles; /* output in SMILES format */
-static int maxbond; /* maximum mult -1 of bonds (1 if -t, else 2) */
-
-static boolean planar; /* Molecules must be planar */
-static int maxcoord; /* Maximum coordination number allowed */ //UNUSED
-static boolean xswitch; /* Undocumented, used for development */
-
-/* In the following, the counts are only meaningful if the
- corresponding boolean is true. */
-static boolean tswitch;
-static long min3rings,max3rings; /* number of rings of length 3 */
-static int count3ring[MAXN+1];
-static boolean fswitch;
-static long min4rings,max4rings; /* number of rings of length 4 */
-static int count4ring[MAXN+1];
-static boolean pswitch;
-static long min5rings,max5rings; /* number of rings of length 5 */
-static int count5ring[MAXN+1];
-static boolean bipartite;
-
-/* The following is only used if bad9 is selected */
-static setword cycle34verts[MAXN+1]; /* set of vertices on rings
- of length 3 or 4 */
-
-static long vgroupsize; /* vcolg group size */
-static size_t vgroupalloc=0; /* Space allocated for vertex groups */
-static int *vgroup=NULL; /* Store of vertex group */
-static long vgroupcount;
-
-static long egroupsize; /* multig group size */
-static size_t egroupalloc=0; /* Space allocated for edge groups */
-static int *egroup=NULL; /* Store of edge group */
-
-typedef struct edge
-{
- int x,y; /* The atoms with this bond */
- int maxmult; /* This edge can have multiplicity 1..maxmult+1 */
- int allenemate1,allenemate2;
- /* Previous bond that cannot be multiple at the same time as this */
- setword xy;
-} edgetype;
-static int numedges;
-static edgetype edge[MAXNE];
-static int edgenumber[MAXN][MAXN];
-static int deg[MAXN]; /* Simple graph degree */
-
-static FILE *outfile;
-#ifdef ZLIB
-static gzFile gzoutfile;
-#endif
-static boolean gzip;
-
-/* Macros for appending to a string using pointer p */
-#define PUTINT(xx) { unsigned long ul = (xx); char *sp,s[15]; \
- if (ul == 0) *(p++) = '0'; \
- else { sp = s; while (ul) { *(sp++) = (ul % 10) + '0'; ul /= 10; } \
- while (sp > s) { *(p++) = *(--sp); } }}
-#define SPC *(p++) = ' '
-#define PUTSTR(xx) { char *sp = (xx); \
- while (*sp != '\0') *(p++) = *(sp++); }
-#define PUTBND(xx) { int bnd = (xx); if (bnd == 0) *(p++) = '-'; \
- else if (bnd == 1) *(p++) = '='; else *(p++) = '#'; }
-
-/******************************************************************/
-
-#define MAXELEMENTS 30
-static struct elementstruct
-{
- char *inputname,*name;
- boolean organic; /* Belongs to organic subset */
- int valence;
- int lowervalence; /* Next lower valence, or 0 if none */
- int maxcoord; /* Maximum number of distinct neighbours including H */
- int index; /* Used in -T style outputs */
-} element[MAXELEMENTS] =
-{ /* The order of listing does not matter.
- Other elements can be added to the table by following the same
- pattern, up to any limit. All extra elements must be marked
- as non-organic. The inputname field must have the form X or Xx
- and must be unique. */
- { "C", "C", TRUE, 4,0,4, 0 },
- { "N", "N", TRUE, 3,0,3, 1 },
- { "Nx","N", TRUE, 5,3,4, 10 },
- { "P", "P", TRUE, 3,0,3, 3 },
- { "Px","P", TRUE, 5,3,5, 13 },
- { "B", "B", TRUE, 3,0,3, 9 },
- { "O", "O", TRUE, 2,0,2, 2 },
- { "S", "S", TRUE, 2,0,2, 4 },
- { "Sx","S", TRUE, 4,2,4, 11 },
- { "Sy","S", TRUE, 6,4,6, 12 },
- { "H", "H", FALSE, 1,0,1, 99 },
- { "F", "F", TRUE, 1,0,1, 5 },
- { "Cl","Cl",TRUE, 1,0,1, 6 },
- { "Br","Br",TRUE, 1,0,1, 7 },
- { "I", "I", TRUE, 1,0,1, 8 },
- { "Si","Si",FALSE, 4,0,4, 14 },
- { NULL,NULL,FALSE, 0,0,0, 0 }
-};
-static int numelements; /* Actual number in the table */
-static int maxindex; /* Max value of the index field except 99 */
-#define ISHYDROGEN(i) (strcmp(element[i].name,"H") == 0)
-
-/******************************************************************/
-
-static int
-elementindex(char *inputname)
-/* Index into element[] of element with this element input name,
- or -1 if it isn't present. */
-{
- int i;
-
- for (i = 0; i < numelements; ++i)
- if (strcmp(element[i].inputname,inputname) == 0)
- break;
-
- if (i < numelements) return i;
- else
- {
- fprintf(stderr,">E Unknown element %s\n",inputname);
- exit(1);
- }
-}
-
-static void
-addelement(char *inputname, char *name, int valence, int maxcoord)
-/* Add an element to element[]. inputname and name must be of the
-form A or Aa. If name==NULL then name is the same as inputname. If
-maxcoord==0 or maxcoord > valence then maxcoord is the same as valence. */
-{
- int i;
-
- if (numelements == MAXELEMENTS)
- gt_abort(">E increase MAXELEMENTS\n");
-
- if (name == NULL) name = inputname;
- if (maxcoord == 0 || maxcoord > valence) maxcoord = valence;
-
- if (inputname == NULL || valence <= 0)
- gt_abort(">E invalid parameters in addelement()\n");
-
- for (i = 0; i < numelements; ++i)
- {
- if (strcmp(element[i].inputname,inputname) == 0)
- {
- fprintf(stderr,">E element %s is already present\n",name);
- exit(1);
- }
- }
-
- if (!isupper(inputname[0]) || (inputname[1] != '\0'
- && (!islower(inputname[1]) || inputname[2] != '\0')))
- gt_abort(">E element names must have form A or Aa\n");
- if (!isupper(name[0]) || (name[1] != '\0'
- && (!islower(name[1]) || name[2] != '\0')))
- gt_abort(">E element names must have form A or Aa\n");
-
- if (3*maxcoord < valence || valence < 0)
- gt_abort(">E impossible maxcoord/valence\n");
-
- element[numelements].inputname = strdup(inputname);
- element[numelements].name = strdup(name);
- element[numelements].valence = valence;
- /* lowervalence is only used for SMILES output of
- atoms in the organic subset and since those are
- in the table already we don't need a value to
- be set here. */
- element[numelements].lowervalence = 0;
- element[numelements].maxcoord = maxcoord;
- element[numelements].organic = FALSE;
- element[numelements].index = ++maxindex;
-
- if (verbose)
- fprintf(stderr,"Added element %s input %s, valence=%d maxcoord=%d\n",
- element[numelements].name,element[numelements].inputname,
- element[numelements].valence,element[numelements].maxcoord);
- ++numelements;
-}
-
-#undef HELPTEXT2
-#ifdef SURGEPLUGIN
-/* Note that the value of SURGEPLUGIN must be a filename in quotes, so on
- the compilation command you will probably need something like
- -DSURGEPLUGIN='"myplugin.h"' . */
-#include SURGEPLUGIN
-#endif
-
-#ifndef HELPTEXT2
-#define HELPTEXT2 ""
-#endif
-
-/******************************************************************/
-
-#if 0
-static unsigned long long
-molcode(int *vcol, int *mult, int n, int ne)
-/* This makes an isomorph-invariant code for a molecule. It is intended
-for debugging and can't be relied on to distinguish between different
-molecules. It only works up to MAXN/2 atoms (but you can compile the
-program with WORDSIZE=64 if you really need to). */
-{
- graph g[MAXN],h[MAXN];
- int lab[MAXN],ptn[MAXN],orbits[MAXN],weight[MAXN];
- static DEFAULTOPTIONS_GRAPH(options);
- statsblk stats;
- setword workspace[2*MAXN];
- int i,x,y,nv;
- unsigned long long ans1,ans2;
-
- nv = 2*n;
- if (nv >= MAXN) gt_abort(">E surge : too big for longcode\n");
-
- for (i = 0; i < n; ++i) { weight[i] = vcol[i]; weight[n+i] = n+vcol[i]; }
- setlabptn(weight,lab,ptn,nv);
-
- for (i = 0; i < nv; ++i) g[i] = 0;
-
- for (i = 0; i < n; ++i) { g[i] |= bit[n+i]; g[n+i] |= bit[i]; }
-
- for (i = 0; i < ne; ++i)
- {
- x = edge[i].x;
- y = edge[i].y;
- g[x] |= bit[y]; g[y] |= bit[x];
- if (mult[i] > 0) { g[n+x] |= bit[n+y]; g[n+y] |= bit[n+x]; }
- if (mult[i] > 1)
- {
- g[x] |= bit[y+n]; g[y+n] |= bit[x];
- g[x+n] |= bit[y]; g[y] |= bit[x+n];
- }
- }
-
- options.defaultptn = FALSE;
- options.getcanon = TRUE;
-
- nauty(g,lab,ptn,NULL,orbits,&options,&stats,workspace,2*MAXN,1,nv,h);
-
- ans1 = n;
- ans2 = ne;
- for (i = 0; i < nv; ++i)
- {
- ans1 = 177*ans1 + (h[i] >> (WORDSIZE-nv));
- ans2 = 1237*ans2 + (h[i] >> (WORDSIZE-nv));
- }
-
- return ans1^ans2;
-}
-#endif
-
-/******************************************************************/
-
-void dummy()
-{
-}
-
-/******************************************************************/
-
-static boolean
-isplanar(graph *g, int n)
-/* Check if g is planar, assuming g is connected */
-{
- t_ver_sparse_rep V[MAXN];
- t_adjl_sparse_rep A[2*MAXNE+1];
- t_dlcl **dfs_tree,**back_edges,**mult_edges;
- t_ver_edge *embed_graph;
- int i,j,k,pop,nv,ne,c;
- int edge_pos,v,w;
- setword ww;
- boolean ans;
- graph h[MAXN];
- int newlab[MAXN];
- setword queue;
-
- queue = 0;
- ne = 0;
- for (i = 0; i < n; ++i)
- {
- h[i] = g[i];
- pop = POPCOUNT(h[i]);
- ne += pop;
- if (pop <= 2) queue |= bit[i];
- }
- ne /= 2;
- nv = n;
-
- while (queue && ne >= nv+3)
- {
- TAKEBIT(i,queue);
- pop = POPCOUNT(h[i]);
- if (pop == 1) /* i--j with deg(i)=1 */
- {
- j = FIRSTBITNZ(h[i]);
- h[i] = 0;
- h[j] &= ~bit[i];
- --nv;
- --ne;
- queue |= bit[j];
- }
- else if (pop == 2) /* j--i--k with deg(i)=2 */
- {
- j = FIRSTBITNZ(h[i]);
- k = FIRSTBITNZ(h[i] & ~bit[j]);
- h[i] = 0;
- h[j] &= ~bit[i];
- h[k] &= ~bit[i];
- --nv;
- if ((h[j] & bit[k]))
- {
- ne -= 2;
- queue |= (bit[j] | bit[k]);
- }
- else
- {
- --ne;
- h[j] |= bit[k];
- h[k] |= bit[j];
- }
- }
- }
-
- if (ne <= nv + 2) return TRUE;
- if (nv == 5 && ne <= 9) return TRUE;
-
- nv = 0;
- for (i = 0; i < n; ++i) if (h[i] != 0) newlab[i] = nv++;
-
- k = 0;
- for (i = 0; i < n; ++i)
- if (h[i] != 0)
- {
- V[newlab[i]].first_edge = k;
- ww = h[i];
- while (ww)
- {
- TAKEBIT(j,ww);
- A[k].end_vertex = newlab[j];
- A[k].next = k+1;
- ++k;
- }
- A[k-1].next = NIL;
- }
-
- ne = k/2;
-
- ans = sparseg_adjl_is_planar(V,nv,A,&c,&dfs_tree,&back_edges,
- &mult_edges,&embed_graph,&edge_pos,&v,&w);
-
- sparseg_dlcl_delete(dfs_tree,nv);
- sparseg_dlcl_delete(back_edges,nv);
- sparseg_dlcl_delete(mult_edges,nv);
- embedg_VES_delete(embed_graph,nv);
-
- return ans;
-}
-
-/******************************************************************/
-
-static void
-SMILESoutput(int *vcol, int n, int *hyd, int *mult, int ne)
-/* Write molecules in SMILES format */
-{
- char *p,line[20*MAXNE];
- int i,x,y,r,m;
- const struct elementstruct *thiselement;
-
- p = line;
-
- for (i = 0; i < smileslen; ++i)
- {
- x = smilesskeleton[i].x;
- y = smilesskeleton[i].y;
- switch(smilesskeleton[i].item)
- {
- case SM_ATOM :
- thiselement = &element[vcol[x]];
-#if 0
- /* This version seems to meet the OpenSmiles standard, but some
- problems with obabel have not been tracked down. */
- if (!thiselement->organic ||
- thiselement->valence - hyd[x] <= thiselement->lowervalence)
-#else
- /* This version gives explicit H for atoms in higher valences even
- if they are in the organic subset. */
- if (!thiselement->organic ||
- (thiselement->lowervalence > 0 && hyd[x] > 0))
-#endif
- {
- *(p++) = '[';
- PUTSTR(thiselement->name);
- if (hyd[x] > 0) *(p++) = 'H';
- if (hyd[x] > 1) PUTINT(hyd[x])
- *(p++) = ']';
- }
- else
- PUTSTR(thiselement->name);
- break;
- case SM_BOND :
- m = mult[edgenumber[x][y]];
- if (m == 1) *(p++) = '=';
- else if (m == 2) *(p++) = '#';
- break;
- case SM_OPEN :
- *(p++) = '(';
- break;
- case SM_CLOSE :
- *(p++) = ')';
- break;
- case SM_RING0 :
- m = mult[edgenumber[x][y]];
- if (m == 1) *(p++) = '=';
- else if (m == 2) *(p++) = '#';
- r = smilesskeleton[i].r;
- if (r < 10)
- *(p++) = '0' + r;
- else
- {
- *(p++) = '%';
- *(p++) = '0' + r/10;
- *(p++) = '0' + r%10;
- }
- break;
- case SM_RING1 :
- r = smilesskeleton[i].r;
- if (r < 10)
- *(p++) = '0' + r;
- else
- {
- *(p++) = '%';
- *(p++) = '0' + r/10;
- *(p++) = '0' + r%10;
- }
- break;
- }
- }
-
- *(p++) = '\n';
- *p = '\0';
-
-#ifdef ZLIB
- if (gzip)
- {
- if (gzputs(gzoutfile,line) < 0)
- gt_abort(">E surge : zlib output error\n");
- return;
- }
-#endif
-
- if (fputs(line,outfile) == EOF) gt_abort(">E surge : output error\n");
-}
-
-/******************************************************************/
-
-static void
-SDFformat(int *vcol, int n, int *hyd, int *mult, int ne)
-/* Write molecules in SDF format */
-{
- int i,j;
-
-#ifdef ZLIB
- if (gzip)
- {
- gzprintf(gzoutfile,"\nSurge 1.0\n\n");
- gzprintf(gzoutfile,"%3d%3d 0 0 0 0 999 V2000\n",n,ne);
-
- for (i = 0; i < n; ++i)
- gzprintf(gzoutfile," 0.0000 0.0000 0.0000 %-2s"
- " 0 0 0 0 0%3d 0 0 0 0 0 0\n",
- element[vcol[i]].name,element[vcol[i]].valence);
-
- for (i = 0; i < ne; ++i)
- gzprintf(gzoutfile,"%3d%3d%3d 0 0 0 0\n",
- edge[i].x+1,edge[i].y+1,mult[i]+1);
-
- gzprintf(gzoutfile,"M END\n$$$$\n");
-
- return;
- }
-#endif
-
- fprintf(outfile,"\nSurge 1.0\n\n");
- fprintf(outfile,"%3d%3d 0 0 0 0 999 V2000\n",n,ne);
-
- for (i = 0; i < n; ++i)
- fprintf(outfile," 0.0000 0.0000 0.0000 %-2s"
- " 0 0 0 0 0%3d 0 0 0 0 0 0\n",
- element[vcol[i]].name,element[vcol[i]].valence);
-
- for (i = 0; i < ne; ++i)
- fprintf(outfile,"%3d%3d%3d 0 0 0 0\n",
- edge[i].x+1,edge[i].y+1,mult[i]+1);
-
- fprintf(outfile,"M END\n$$$$\n");
-}
-
-/****************************************************************/
-
-static void
-multigoutput(int *vcol, int n, int *mult, int ne)
-/* Write output equal to the multig -T format. */
-{
- char line[10+60*MAXN],*p;
- int i;
-
- p = line;
- PUTINT(n); SPC; PUTINT(ne);
- for (i = 0; i < n; ++i)
- {
- SPC;
- PUTINT(element[vcol[i]].index);
- }
- SPC;
- for (i = 0; i < ne; ++i)
- {
- SPC; PUTINT(edge[i].x);
- SPC; PUTINT(edge[i].y);
- SPC; PUTINT(mult[i]);
- }
- *(p++) = '\n';
- *p = '\0';
-
-#ifdef ZLIB
- if (gzip)
- {
- if (gzputs(gzoutfile,line) < 0)
- gt_abort(">E surge : zlib output error\n");
- return;
- }
-#endif
-
- if (fputs(line,outfile) == EOF) gt_abort(">E surge : output error\n");
-}
-
-/****************************************************************/
-
-static void
-alphabeticoutput(int *vcol, int n, int *mult, int ne)
-/* Write alphabetic output */
-{
- char line[10+60*MAXN],*p;
- int i,xx,yy;
-
- p = line;
- PUTINT(n);
- SPC;
- PUTINT(ne);
- SPC;
- PUTSTR(canonform);
-
- for (i = 0; i < ne; ++i)
- {
- SPC;
- xx = newlabel[edge[i].x];
- yy = newlabel[edge[i].y];
- if (xx < yy)
- {
- PUTINT(xx); PUTBND(mult[i]); PUTINT(yy);
- }
- else
- {
- PUTINT(yy); PUTBND(mult[i]); PUTINT(xx);
- }
- }
- *(p++) = '\n';
- *p = '\0';
-
-#ifdef ZLIB
- if (gzip)
- {
- if (gzputs(gzoutfile,line) < 0)
- gt_abort(">E surge : zlib output error\n");
- return;
- }
-#endif
-
- if (fputs(line,outfile) == EOF) gt_abort(">E surge : output error\n");
-}
-
-/******************************************************************/
-
-static void
-gotone(int *vcol, int n, int *hyd, int *mult, int ne, int level)
-/* Now we have a completed molecule.
- deg[0..n-1] is the simple graph degrees
- hyd[0..n-1] is the number of implicit hydrogens
-*/
-{
- int i,val;
-
- for (i = level; i < ne; ++i) mult[i] = 0;
-
- if (needcoordtest)
- for (i = 0; i < n; ++i)
- {
- if (deg[i] + hyd[i] > element[vcol[i]].maxcoord) return;
- if (deg[i] + hyd[i] > 4 && hyd[i] > 0) return;
- }
-
-#ifdef SURGEPLUGIN_STEP3
- SURGEPLUGIN_STEP3
-#endif
-
- ++multigout;
- if (uswitch) return;
-
- if (outlevel == 3)
- multigoutput(vcol,n,mult,ne);
- else if (alphabetic)
- alphabeticoutput(vcol,n,mult,ne);
- else if (smiles)
- SMILESoutput(vcol,n,hyd,mult,ne);
- else
- SDFformat(vcol,n,hyd,mult,ne);
-}
-
-/******************************************************************/
-
-static boolean
-testemax(int *mult, int ne, int level)
-/* Test if edge colouring is maximum wrt group. */
-{
- int *gp,i,j;
- long kgp;
-
- for (i = level; i < ne; ++i) mult[i] = 0;
-
- /* kgp really starts at 1 on the next line */
- for (kgp = 1, gp = egroup; kgp < egroupsize; ++kgp, gp += ne)
- {
- for (i = 0; i < ne; ++i)
- {
- j = gp[i];
- if (mult[j] > mult[i]) return FALSE;
- else if (mult[j] < mult[i]) break;
- }
- }
-
- return TRUE;
-}
-
-/***************************************************************************/
-
-static void
-/* Recursive scan for multiplying edges
- Version which checks allenemate for -B3 */
-escan2(int level, int needed, int *vcol, int *hyd, int *prev, int n, int *mult, int ne)
-{
- int lev,maxlev,k,max,x,y;
-
- if (needed == 0)
- {
- if (egroupsize > 1 && !testemax(mult,ne,level))
- return;
- gotone(vcol,n,hyd,mult,ne,level);
- return;
- }
- else
- {
- maxlev = ne + 1 - (needed+maxbond-1)/maxbond;
- for (lev = level; lev < maxlev; ++lev)
- {
- x = edge[lev].x;
- y = edge[lev].y;
- max = edge[lev].maxmult;
-
- if (needed < max) max = needed;
- if (hyd[x] < max) max = hyd[x];
- if (hyd[y] < max) max = hyd[y];
- if (prev[lev] >= 0 && mult[prev[lev]] < max) max = mult[prev[lev]];
- if (edge[lev].allenemate1 >= 0 && mult[edge[lev].allenemate1] >= 1)
- max = 0;
- if (edge[lev].allenemate2 >= 0 && mult[edge[lev].allenemate2] >= 1)
- max = 0;
-
- for (k = 1; k <= max; ++k)
- {
- mult[lev] = k;
- hyd[x] -= k;
- hyd[y] -= k;
- escan2(lev+1,needed-k,vcol,hyd,prev,n,mult,ne);
- hyd[x] += k;
- hyd[y] += k;
- }
-
- mult[lev] = 0;
- }
- }
-
- return;
-}
-
-/***************************************************************************/
-
-static void
-/* Recursive scan for multiplying edges */
-escan(int level, int needed, int *vcol, int *hyd,
- int *prev, int n, int *mult, int ne)
-{
- int lev,maxlev,k,max,x,y;
-
- if (needed == 0)
- {
- if (egroupsize > 1 && !testemax(mult,ne,level))
- return;
- gotone(vcol,n,hyd,mult,ne,level);
- return;
- }
- else
- {
- maxlev = ne + 1 - (needed+maxbond-1)/maxbond;
- for (lev = level; lev < maxlev; ++lev)
- {
- x = edge[lev].x;
- y = edge[lev].y;
- max = edge[lev].maxmult;
-
- if (needed < max) max = needed;
- if (hyd[x] < max) max = hyd[x];
- if (hyd[y] < max) max = hyd[y];
- if (prev[lev] >= 0 && mult[prev[lev]] < max)
- max = mult[prev[lev]];
-
- for (k = 1; k <= max; ++k)
- {
- mult[lev] = k;
- hyd[x] -= k;
- hyd[y] -= k;
- escan(lev+1,needed-k,vcol,hyd,prev,n,mult,ne);
- hyd[x] += k;
- hyd[y] += k;
- }
-
- mult[lev] = 0;
- }
- }
-
- return;
-}
-
-/******************************************************************/
-
-static void
-findegroup(int *vcol, int n, int ne)
-/* Set egroup to the set of vertex-colour-preserving vgroup elements */
-{
- int *vgp,*egp,i,j,kgp;
-
- egp = egroup;
-
- /* kgp really starts at 1 on the next line */
- for (kgp = 1, vgp = vgroup; kgp < vgroupsize; ++kgp, vgp += n)
- {
- for (i = 0; i < n; ++i)
- if (vcol[vgp[i]] != vcol[i]) break;
- if (i == n)
- for (j = 0; j < ne; ++j)
- *(egp++) = edgenumber[vgp[edge[j].x]][vgp[edge[j].y]];
- }
-
- egroupsize = 1 + (egp - egroup) / ne;
-}
-
-/****************************************************************/
-
-static void
-colouredges(graph *g, int *vcolindex, int n)
-/* This procedure receives graphs from the vcolg phase and
- colours the edges. */
-{
- int hyd[MAXN]; /* Remaining degree of vertex */
- int i,j,k,ne;
- int mult[MAXNE];
- int prev[MAXNE]; /* If >= 0, earlier point that must have greater colour */
- int needed; /* Extra edges needed */
- int iter[FORMULALEN];
- int vcol[MAXN]; /* index into element[] */
-
- ne = numedges;
-
- /* hyd[i] starts with the number of hydrogens needed if all bonds are
- single and is reduced as bonds are multiplied */
-
- needcoordtest = FALSE;
- for (i = 0; i < n; ++i)
- {
- vcol[i] = elementtype[vcolindex[i]];
- hyd[i] = element[vcol[i]].valence - deg[i];
- if (element[vcol[i]].valence > element[vcol[i]].maxcoord
- || (element[vcol[i]].valence > 4 && hyd[i] > 0))
- needcoordtest = TRUE;
- }
-
-#ifdef SURGEPLUGIN_STEP2
- SURGEPLUGIN_STEP2
-#endif
-
- needed = (valencesum - hydrogens)/2 - ne; /* Extra edges needed */
-
- if (ne == 0 && needed > 0) return;
-
- if (alphabetic)
- {
- iter[0] = 0;
- for (i = 1; i < numtypes; ++i) iter[i] = iter[i-1] + elementcount[i-1];
-
- for (i = 0; i < n; ++i) newlabel[i] = iter[vcolindex[i]]++;
- }
-
- if (needed == 0)
- {
- gotone(vcol,n,hyd,mult,ne,0);
- return;
- }
-
- for (i = 0; i < ne; ++i) prev[i] = -1;
-
- for (i = 0; i < n; ++i)
- {
- if (deg[i] != 1) continue;
- /* Find most recent equivalent j */
- for (j = i; --j >= 0; )
- if (g[i] == g[j] && vcol[j] == vcol[i])
- break;
-
- if (j >= 0)
- {
- k = FIRSTBITNZ(g[i]);
- prev[edgenumber[i][k]] = edgenumber[j][k];
- }
- }
-
- if (vgroupsize == 1)
- egroupsize = 1;
- else
- {
- if (egroupalloc < vgroupsize*ne)
- {
- if (egroup) free(egroup);
- if ((egroup = malloc((vgroupsize+48)*ne*sizeof(int))) == NULL)
- gt_abort(">E surge : Can't allocate space for egroup\n");
- egroupalloc = (vgroupsize+48) * ne;
- }
- findegroup(vcol,n,ne);
- if (vgroupsize % egroupsize != 0) gt_abort(">E egroup error\n");
- }
-
- if (egroupsize == 1 && needed == 1)
- {
- for (i = 0; i < ne; ++i) mult[i] = 0;
- for (i = 0; i < ne; ++i)
- if (prev[i] < 0 && edge[i].maxmult >= 1
- && hyd[edge[i].x] > 0 && hyd[edge[i].y] > 0)
- {
- mult[i] = 1;
- --hyd[edge[i].x];
- --hyd[edge[i].y];
- gotone(vcol,n,hyd,mult,ne,ne);
- ++hyd[edge[i].x];
- ++hyd[edge[i].y];
- mult[i] = 0;
- }
- return;
- }
-
- if (egroupsize != 1) ++multignontriv;
- if (egroupsize > maxegroup) maxegroup = egroupsize;
-
- if (bad5 || bad6) escan2(0,needed,vcol,hyd,prev,n,mult,ne);
- else escan(0,needed,vcol,hyd,prev,n,mult,ne);
-}
-
-/******************************************************************/
-
-static void
-vcolgoutput(graph *g, int *vcolindex, int n)
-/* Write output equal to the vcolg -T format. */
-{
- char line[10+30*MAXN],*p;
- int i,j,ne;
- setword w;
-
- ne = 0;
- for (i = 0; i < n; ++i) ne += POPCOUNT(g[i]);
- ne /= 2;
-
- p = line;
- PUTINT(n); SPC; PUTINT(ne);
- for (i = 0; i < n; ++i)
- {
- SPC;
- PUTINT(element[elementtype[vcolindex[i]]].index);
- }
- SPC;
- for (i = 0; i < n; ++i)
- {
- w = g[i] & BITMASK(i);
- while (w)
- {
- TAKEBIT(j,w);
- SPC; PUTINT(i); SPC; PUTINT(j);
- }
- }
- *(p++) = '\n';
- *p = '\0';
-
-#ifdef ZLIB
- if (gzip)
- {
- if (gzputs(gzoutfile,line) < 0)
- gt_abort(">E surge : zlib output error\n");
- return;
- }
-#endif
-
- if (fputs(line,outfile) == EOF) gt_abort(">E surge : output error\n");
-}
-
-/******************************************************************/
-
-static boolean
-testvmax(int *colindex, int n)
-/* Test if vertex colouring is maximum wrt group. If so, return group.
- If not, return a safe level to return to. */
-{
- int *gp,i,j;
- long kgp;
-
- /* kgp really starts at 1 on the next line */
- for (kgp = 1, gp = vgroup; kgp < vgroupsize; ++kgp, gp += n)
- {
- for (i = 0; i < n; ++i)
- {
- j = gp[i];
- if (colindex[j] > colindex[i]) return FALSE;
- else if (colindex[j] < colindex[i]) break;
- }
- }
-
- return TRUE;
-}
-
-/**********************************************************************/
-
-static void
-vscan(int level, int *colindex, graph *g, int *prev,
- int *maxcolindex, int *remain, int n)
-/* Recursive vertex colour scan */
-{
- int k,max;
-
- if (level == n)
- {
- if (vgroupsize == 1 || testvmax(colindex,n))
- {
- ++vcolgout;
- if (outlevel == 2)
- {
- if (!uswitch) vcolgoutput(g,colindex,n);
- }
- else
- colouredges(g,colindex,n);
- }
- return;
- }
-
- max = maxcolindex[level];
- if (prev[level] >= 0 && colindex[prev[level]] < max)
- max = colindex[prev[level]];
-
- for (k = 0; k <= max; ++k)
- {
- if (remain[k] == 0) continue;
- colindex[level] = k;
- --remain[k];
- vscan(level+1,colindex,g,prev,maxcolindex,remain,n);
- ++remain[k];
- }
-}
-
-/**********************************************************************/
-
-static void
-storevgroup(int *p, int n)
-/* Called by allgroup; store full group at vcolg phase */
-{
- int *gp,i;
-
- if (vgroupcount == 0)
- {
- vgroupcount = 1; /* Don't store identity */
- return;
- }
-
- gp = vgroup + n * (vgroupcount-1);
- for (i = 0; i < n; ++i) gp[i] = p[i];
-
- ++vgroupcount;
-}
-
-/**********************************************************************/
-
-static void
-colourvertices(graph *g, int n)
-/* Main procedure for vcolg phase */
-{
- static DEFAULTOPTIONS_GRAPH(options);
- statsblk stats;
- setword workspace[2*MAXN];
- grouprec *group;
- int lab[MAXN],ptn[MAXN],orbits[MAXN];
- int prev[MAXN]; /* If >= 0, earlier point that must have greater colour */
- int weight[MAXN];
- int maxcolindex[MAXN]; /* Max colour index for each vertex */
- int remain[FORMULALEN]; /* Remaining number of colours for each vertex */
- int vcolindex[MAXN]; /* Index into elementtype[] */
- int i,j;
-
-#ifdef SURGEPLUGIN_STEP1
- SURGEPLUGIN_STEP1
-#endif
-
- for (i = 0; i < n; ++i)
- {
- prev[i] = -1;
- weight[i] = n*POPCOUNT(g[i]);
- }
-
- for (i = 0; i < n; ++i)
- {
- if (POPCOUNT(g[i]) != 1) continue;
- /* Find most recent equivalent j */
- for (j = i; --j >= 0; )
- if (g[j] == g[i]) break;
-
- if (j >= 0)
- {
- prev[i] = j;
- weight[i] = weight[j] + 1;
- }
- }
-
- options.userautomproc = groupautomproc;
- options.userlevelproc = grouplevelproc;
- options.defaultptn = FALSE;
-
- setlabptn(weight,lab,ptn,n);
-
- nauty(g,lab,ptn,NULL,orbits,&options,&stats,workspace,2*MAXN,1,n,NULL);
-
- if (stats.grpsize2 > 0 || stats.grpsize1 > 1e7)
- gt_abort(">E surge : vgroup size greater than 10^7 encountered\n");
- vgroupsize = stats.grpsize1 + 0.01;
-
- for (i = 0; i < numtypes; ++i) remain[i] = elementcount[i];
- for (i = 0; i < n; ++i) maxcolindex[i] = maxtype[deg[i]];
-
- if (vgroupsize == 1) /* Trivial group */
- {
- vscan(0,vcolindex,g,prev,maxcolindex,remain,n);
- return;
- }
-
- ++vcolgnontriv;
- if (vgroupsize > maxvgroup) maxvgroup = vgroupsize;
-
- group = groupptr(FALSE);
- makecosetreps(group);
-
- if (vgroupalloc < (vgroupsize-1) * n)
- {
- if (vgroup) free(vgroup);
- if ((vgroup = malloc((vgroupsize+48)*n*sizeof(int))) == NULL)
- gt_abort(">E surge : Can't allocate space for vgroup\n");
- vgroupalloc = (vgroupsize+48) * n;
- }
-
- vgroupcount = 0;
- allgroup(group,storevgroup);
-
- if (vgroupcount != vgroupsize) gt_abort(">E surge : vgroup error\n");
-
- /* Check the logic of the next section. What about maxdeg? */
- if (numtypes == 1)
- {
- for (i = 0; i < n; ++i) vcolindex[i] = 0;
- ++vcolgout;
- colouredges(g,vcolindex,n);
- return;
- }
-
- j = n; /* Can choose a better orbit? */
- for (i = 0; i < n; ++i)
- if (orbits[i] < i && orbits[i] < j) j = orbits[i];
- for (i = j + 1; i < n; ++i)
- if (orbits[i] == j) prev[i] = j;
-
- vscan(0,vcolindex,g,prev,maxcolindex,remain,n);
-}
-
-/******************************************************************/
-
-extern int geng_maxe;
-
-int
-surgepreprune(graph *g, int n, int maxn)
-/* This function is called by the PREPRUNE service of geng.
- It does -B9 which is more efficient here. If OLDGENG is
- defined, it also speeds up the generation of connected
- graphs. (For new geng, that function is built in.) */
-{
- setword notvisited,queue;
- setword c34,w,ww,cyc;
- int ne,nc,i;
-
- if (bad9) /* cycle34verts */
- {
- if (n <= 2)
- cycle34verts[n] = 0;
- else
- {
- c34 = cycle34verts[n-1];
- if (!tswitch)
- {
- w = g[n-1];
- while (w)
- {
- TAKEBIT(i,w);
- ww = g[i] & w;
- if (POPCOUNT(ww) > 1) return 1;
- if ((ww))
- {
- cyc = bit[n-1] | bit[i] | ww;
- if ((c34 & cyc)) return 1;
- c34 |= cyc;
- }
- }
- }
- if (!fswitch)
- {
- for (i = n-1; --i >= 0;)
- {
- w = g[i] & g[n-1];
- if (POPCOUNT(w) > 2) return 1;
- if (POPCOUNT(w) == 2)
- {
- cyc = bit[n-1] | bit[i] | w;
- if ((c34 & cyc)) return 1;
- c34 |= cyc;
- }
- }
- }
- cycle34verts[n] = c34;
- }
- }
-
-#ifdef OLDGENG
- if (n == maxn || geng_maxe - maxn >= 5) return 0;
-
- ne = 0;
- for (i = 0; i < n; ++i) ne += POPCOUNT(g[i]);
- ne /= 2;
-
- nc = 0;
- notvisited = ALLMASK(n);
-
- while (notvisited)
- {
- ++nc;
- queue = SWHIBIT(notvisited);
- notvisited &= ~queue;
- while (queue)
- {
- TAKEBIT(i,queue);
- notvisited &= ~bit[i];
- queue |= g[i] & notvisited;
- }
- }
-
- if (ne - n + nc > geng_maxe - maxn + 1) return 1;
-#endif
-
- return 0;
-}
-
-int
-surgeprune(graph *g, int n, int nmax)
-/* This is a procedure that geng will call at each level
-using the PRUNE service.
-The options -t, -f, -p, -B7,8 are implemented here by
-incrementally updating the required counts. */
-{
- setword w,ax,ay,gx,gy,gxy,gxya,gi,gj;
- int i,j,x,y,k,a,b,extra;
- int i1,i2,i3,i4,d1,d2,d3,d4;
- int v[MAXN];
-
- if (tswitch)
- {
- if (n <= 2)
- count3ring[n] = 0;
- else
- {
- extra = 0;
- w = g[n-1];
- while (w)
- {
- TAKEBIT(i,w);
- extra += POPCOUNT(g[i]&w);
- }
- count3ring[n] = count3ring[n-1] + extra;
- if (count3ring[n] > max3rings) return 1;
- }
- if (n == nmax && count3ring[n] < min3rings)
- return 1;
- }
-
- if (fswitch)
- {
- if (n <= 3)
- count4ring[n] = 0;
- else
- {
- extra = 0;
- for (i = 0; i < n-1; ++i)
- {
- k = POPCOUNT(g[i]&g[n-1]);
- extra += k*k - k;
- }
- count4ring[n] = count4ring[n-1] + extra/2;
- if (count4ring[n] > max4rings) return 1;
- }
- if (n == nmax && count4ring[n] < min4rings)
- return 1;
- }
-
- if (pswitch)
- {
- if (n <= 4)
- count5ring[n] = 0;
- else
- {
- extra = 0;
- for (y = 1; y < n-1; ++y)
- {
- w = g[y] & ~BITMASK(y);
- while (w)
- {
- TAKEBIT(x,w);
- ax = (g[x] & g[n-1]) & ~bit[y];
- ay = (g[y] & g[n-1]) & ~bit[x];
- extra += POPCOUNT(ax)*POPCOUNT(ay) - POPCOUNT(ax&ay);
- }
- }
- count5ring[n] = count5ring[n-1] + extra;
- if (count5ring[n] > max5rings) return 1;
- }
- if (n == nmax && count5ring[n] < min5rings)
- return 1;
- }
-
- if (bad7 && n >= 6)
- {
- /* For K_33 we can assume that vertex n-1 is included */
- for (x = n-1; --x >= 1;)
- if (POPCOUNT(g[x]&g[n-1]) >= 3)
- {
- for (y = x; --y >= 0;)
- if (POPCOUNT(g[y]&g[x]&g[n-1]) >= 3) return 1;
- }
-
- /* But for K_24 we can't */
- for (x = n; --x >= 1;)
- if (POPCOUNT(g[x]) >= 4)
- {
- for (y = x; --y >= 0;)
- if (POPCOUNT(g[x]&g[y]) >= 4) return 1;
- }
- }
-
- if (bad8)
- {
- /* cone of P4 */
- if (n >= 5)
- {
- for (x = n; --x >= 0;)
- if (POPCOUNT(g[x]) == 4)
- {
- /* Think of a better way to do this */
- w = gx = g[x];
- TAKEBIT(i1,w);
- TAKEBIT(i2,w);
- TAKEBIT(i3,w);
- i4 = FIRSTBITNZ(w);
- d1 = POPCOUNT(g[i1]&gx);
- d2 = POPCOUNT(g[i2]&gx);
- d3 = POPCOUNT(g[i3]&gx);
- d4 = POPCOUNT(g[i4]&gx);
- if (d1 > 0 && d2 > 0 && d3 > 0 && d4 > 0
- && ((d1 >= 2)+(d2 >= 2)+(d3 >= 2)+(d4 >= 2)) >= 2)
- return 1;
- }
- else if (POPCOUNT(g[x]) > 4)
- {
- w = gx = g[x];
- i = 0;
- while (w)
- {
- TAKEBIT(y,w);
- if (POPCOUNT(g[y]&gx) >= 2) v[i++] = y;
- }
- for (--i; i >= 1; --i)
- for (j = 0; j < i; ++j)
- if ((g[v[i]] & bit[v[j]]) &&
- POPCOUNT((g[v[i]]|g[v[j]])&gx) >= 4)
- return 1;
- }
- }
-
- /* K4 with a path of 3 edges between two of its vertices */
-
- if (n >= 6)
- {
- for (x = n; --x >= 1;)
- if (POPCOUNT(g[x]) >= 4)
- {
- for (y = x; --y >= 0;)
- if (POPCOUNT(g[y]) >= 4 && (g[y]&bit[x]))
- {
- gxy = g[x] & g[y];
- while (gxy)
- {
- TAKEBIT(a,gxy);
- gxya = gxy & g[a];
- while (gxya)
- {
- TAKEBIT(b,gxya);
- w = bit[x] | bit[y] | bit[a] | bit[b];
- gi = g[x] & ~w;
- gj = g[y] & ~w;
- while (gi)
- {
- TAKEBIT(i,gi);
- if ((g[i] & gj)) return 1;
- }
- }
- }
- }
- }
- }
- }
-
-#ifdef SURGEPLUGIN_STEP0
- SURGEPLUGIN_STEP0
-#endif
-
- return 0;
-}
-
-/******************************************************************/
-
-static void
-smilesdfs(graph *g, setword *seen, int v, int par, graph *back,
- struct smilesstruct *smilestemp, int *len)
-/* Recursive DFS to collect SMILES information */
-{
- setword gv,w;
- int k;
- boolean first;
-
- gv = g[v];
- first = TRUE;
- *seen |= bit[v];
-
- while (gv)
- {
- TAKEBIT(k,gv);
- if ((*seen & bit[k]))
- {
- if (k != par)
- {
- back[v] |= bit[k];
- back[k] |= bit[v];
- }
- }
- else
- {
- if (first)
- {
- if (POPCOUNT(g[k]) == 1)
- {
- if ((w = (gv & ~*seen))) /* really = */
- {
- gv |= bit[k];
- k = FIRSTBITNZ(w);
- gv &= ~bit[k];
- }
- }
- smilesdfs(g,seen,k,v,back,smilestemp,len);
- first = FALSE;
- }
- else
- {
- smilestemp[(*len)++].item = SM_CLOSE;
- smilesdfs(g,seen,k,v,back,smilestemp,len);
- smilestemp[(*len)++].item = SM_OPEN;
- }
- }
- }
-
- smilestemp[*len].item = SM_ATOM;
- smilestemp[*len].x = v;
- ++*len;
- if (par >= 0)
- {
- smilestemp[*len].item = SM_BOND;
- smilestemp[*len].x = par;
- smilestemp[*len].y = v;
- ++*len;
- }
-}
-
-static void
-makesmilesskeleton(graph *g, int n)
-/* Make a skeleton SMILES structure for use in SMILESoutput */
-{
- struct smilesstruct smilestemp[4*MAXN+6*MAXNE];
- graph back[MAXN],ring[MAXN];
- setword w,seen;
- int len,ringnumber;
- int i,j,v;
-
- for (v = n; --v >= 0; ) if (POPCOUNT(g[v]) == 1) break;
- if (v < 0) v = n-1;
-
- len = 0;
- seen = 0;
- for (i = 0; i < n; ++i) back[i] = 0;
- for (i = 0; i < n; ++i) ring[i] = 0;
-
- smilesdfs(g,&seen,v,-1,back,smilestemp,&len);
-
- smileslen = 0;
- ringnumber = 0;
- for (i = len; --i >= 0; )
- {
- smilesskeleton[smileslen++] = smilestemp[i];
- if (smilestemp[i].item == SM_ATOM)
- {
- v = smilestemp[i].x;
- w = ring[v];
- while (w)
- {
- TAKEBIT(j,w);
- smilesskeleton[smileslen].item = SM_RING1;
- smilesskeleton[smileslen].r = j+1;
- ++smileslen;
- }
- w = back[v];
- while (w)
- {
- TAKEBIT(j,w);
- ++ringnumber;
- smilesskeleton[smileslen].item = SM_RING0;
- smilesskeleton[smileslen].x = v;
- smilesskeleton[smileslen].y = j;
- smilesskeleton[smileslen].r = ringnumber;
- ++smileslen;
- ring[j] |= bit[ringnumber-1];
- back[j] &= ~bit[v];
- }
- }
- }
-
-#ifdef SMILESSKELETON
- /* This will print the SMILES skeleton to stdout */
-
- fprintf(stdout,"len=%d",smileslen);
- for (i = 0; i < smileslen; ++i)
- {
- switch (smilesskeleton[i].item)
- {
- case SM_ATOM :
- fprintf(stdout," %d",smilesskeleton[i].x);
- break;
- case SM_BOND :
- fprintf(stdout," %d-%d",smilesskeleton[i].x,smilesskeleton[i].y);
- break;
- case SM_OPEN :
- fprintf(stdout," (");
- break;
- case SM_CLOSE :
- fprintf(stdout," )");
- break;
- case SM_RING0 :
- fprintf(stdout," R%d:%d-%d",smilesskeleton[i].r,
- smilesskeleton[i].x,smilesskeleton[i].y);
- break;
- case SM_RING1 :
- fprintf(stdout," R%d",smilesskeleton[i].r);
- }
- }
- fprintf(stdout,"\n");
-#endif
-}
-
-/******************************************************************/
-
-static void
-inducedpaths(graph *g, int origin, int start, setword body,
- setword last, setword path)
-/* Number of induced paths in g starting at start, extravertices within
- * body and ending in last.
- * {start}, body and last should be disjoint. */
-{
- setword gs,w;
- int i;
-
- gs = g[start];
-
- w = gs & last;
- while (w)
- {
- TAKEBIT(i,w);
- inducedcycle[cyclecount++]
- = path | bit[edgenumber[start][i]] | bit[edgenumber[origin][i]];
- }
-
- w = gs & body;
- while (w)
- {
- TAKEBIT(i,w);
- inducedpaths(g,origin,i,body&~gs,last&~bit[i]&~gs,
- path|bit[edgenumber[start][i]]);
- }
-}
-
-static void
-findinducedcycles(graph *g, int n)
-/* Find all the induced cycles */
-{
- setword body,last,cni;
- int i,j;
-
-#if 0
- body = 0;
- for (i = 0; i < n; ++i)
- if (POPCOUNT(g[i]) > 1) body |= bit[i];
-#else
- body = ALLMASK(n);
-#endif
-
- cyclecount = 0;
-
- for (i = 0; i < n-2; ++i)
- {
- body &= ~bit[i];
- last = g[i] & body;
- cni = g[i] | bit[i];
- while (last)
- {
- TAKEBIT(j,last);
- inducedpaths(g,i,j,body&~cni,last,bit[edgenumber[i][j]]);
- }
- if (cyclecount > 3*MAXCYCLES/4) gt_abort(">E increase MAXCYCLES\n");
- }
-
- if (cyclecount > maxcycles) maxcycles = cyclecount;
-
-#if 0
- printf("cyclecount=%d\n",cyclecount);
-
- {
- setword cyc; int i,j;
-
- for (i = 0; i < cyclecount; ++i)
- {
- cyc = inducedcycle[i];
- while (cyc)
- {
- TAKEBIT(j,cyc);
- printf(" %d-%d",edge[j].x,edge[j].y);
- }
- printf("\n");
- }
- }
-#endif
-}
-
-/******************************************************************/
-
-void
-surgeproc(FILE *outfile, graph *gin, int n)
-/* This is called by geng for each graph. */
-{
- int i,j,k,d,n1,n12,n34,n4,ne;
- int isize,jsize;
- graph g[MAXN];
- setword w,wxy,ww,pw,cyc,cycle8;
- int x,y,e1,e2,e3;
-
- n1 = n12 = n34 = n4 = 0;
-
- ++gengout;
-
- for (i = 0; i < n; ++i)
- {
- d = POPCOUNT(gin[i]); /* deg[i] is not defined yet */
- if (d == 1) { ++n1; ++n12; }
- else if (d == 2) ++n12;
- else if (d == 3) ++n34;
- else { ++n34; ++n4; }
- }
-
- if (n > 1 && (n1 < min1 || n12 < min12 || n34 > max34 || n4 > max4))
- return;
-
- if (planar && !isplanar(gin,n)) return; /* Try later */
-
- ++genggood;
-
- /* Reverse to put higher degrees first */
-
- for (i = 0; i < n; ++i)
- {
- w = gin[n-i-1];
- pw = 0;
- while (w)
- {
- TAKEBIT(j,w);
- pw |= bit[n-j-1];
- }
- g[i] = pw;
- }
-
- /* Make the edge list with default parameters */
-
- ne = 0;
- for (i = 0; i < n; ++i)
- {
- deg[i] = POPCOUNT(g[i]);
- w = g[i] & BITMASK(i);
- while (w)
- {
- TAKEBIT(j,w);
- edge[ne].x = i;
- edge[ne].y = j;
- edge[ne].xy = bit[i] | bit[j];
- edge[ne].maxmult = maxbond;
- edge[ne].allenemate1 = edge[ne].allenemate2 = -1;
- edgenumber[i][j] = edgenumber[j][i] = ne;
- ++ne;
- }
- }
- numedges = ne;
-
- if (needcycles)
- {
- if (ne > WORDSIZE)
- gt_abort(">E surge : too many edges for badlists\n");
- findinducedcycles(g,n);
- }
-
- if (bad1) /* no triple bonds in rings smaller than 7 */
- {
- for (i = 0; i < cyclecount; ++i)
- {
- cyc = inducedcycle[i];
- if (POPCOUNT(cyc) <= 7)
- while (cyc)
- {
- TAKEBIT(j,cyc);
- if (edge[j].maxmult == 2) edge[j].maxmult = 1;
- }
- }
- }
-
- if (bad2) /* Bredt's rule for one common bond */
- {
- for (i = 0; i < cyclecount-1; ++i)
- {
- isize = POPCOUNT(inducedcycle[i]);
- if (isize > 6) continue;
- for (j = i+1; j < cyclecount; ++j)
- {
- jsize = POPCOUNT(inducedcycle[j]);
- if (jsize > 6) continue;
-
- w = inducedcycle[i] & inducedcycle[j];
- if (POPCOUNT(w) != 1) continue;
-
- if (isize*jsize <= 15)
- edge[FIRSTBITNZ(w)].maxmult = 0;
-
- if (isize+jsize <= 9)
- {
- wxy = edge[FIRSTBITNZ(w)].xy;
- ww = (inducedcycle[i] | inducedcycle[j]) & ~w;
- while (ww)
- {
- TAKEBIT(k,ww);
- if ((edge[k].xy & wxy)) edge[k].maxmult = 0;
- }
- }
- }
- }
- }
-
- if (bad3) /* Bredt's rule for two common bonds */
- {
- for (i = 0; i < cyclecount-1; ++i)
- {
- isize = POPCOUNT(inducedcycle[i]);
- if (isize == 3 || isize > 6) continue;
-
- for (j = i+1; j < cyclecount; ++j)
- {
- jsize = POPCOUNT(inducedcycle[j]);
- if (jsize == 3 || jsize > 6 || isize+jsize == 12) continue;
-
- w = inducedcycle[i] & inducedcycle[j];
- if (POPCOUNT(w) != 2) continue;
-
- ww = w;
- TAKEBIT(k,ww);
- edge[k].maxmult = 0;
- edge[FIRSTBITNZ(ww)].maxmult = 0;
- wxy = edge[k].xy ^ edge[FIRSTBITNZ(ww)].xy;
-
- ww = (inducedcycle[i] | inducedcycle[j]) & ~w;
- while (ww)
- {
- TAKEBIT(k,ww);
- if ((edge[k].xy & wxy)) edge[k].maxmult = 0;
- }
- }
- }
- }
-
- if (bad4) /* Bredt's rule for two hexagons with 3 bonds in common */
- {
- for (i = 0; i < cyclecount-1; ++i)
- {
- isize = POPCOUNT(inducedcycle[i]);
- if (isize != 6) continue;
-
- for (j = i+1; j < cyclecount; ++j)
- {
- jsize = POPCOUNT(inducedcycle[j]);
- if (jsize != 6) continue;
-
- w = inducedcycle[i] & inducedcycle[j];
- if (POPCOUNT(w) != 3) continue;
-
- ww = inducedcycle[i] | inducedcycle[j];
-
- TAKEBIT(e1,w);
- TAKEBIT(e2,w);
- e3 = FIRSTBITNZ(w);
-
- wxy = edge[e1].xy ^ edge[e2].xy ^ edge[e3].xy;
- while (ww)
- {
- TAKEBIT(k,ww);
- if ((edge[k].xy & wxy)) edge[k].maxmult = 0;
- }
- }
- }
- }
-
- if (bad5) /* No A=A=A, whether in ring or not */
- {
- for (i = 0; i < n; ++i)
- if (deg[i] == 2)
- {
- x = FIRSTBITNZ(g[i]);
- y = FIRSTBITNZ(g[i]&~bit[x]);
- e1 = edgenumber[i][x];
- e2 = edgenumber[i][y];
- if (edge[e2].allenemate1 < 0)
- {
- if (e1 < e2) edge[e2].allenemate1 = e1;
- else edge[e1].allenemate1 = e2;
- }
- else
- {
- if (e1 < e2) edge[e2].allenemate2 = e1;
- else edge[e1].allenemate2 = e2;
- }
- }
- }
-
- if (bad6) /* No A=A=A in rings up to length 8 */
- {
- cycle8 = 0;
- for (i = 0; i < cyclecount; ++i)
- if (POPCOUNT(inducedcycle[i]) <= 8) cycle8 |= inducedcycle[i];
-
- for (i = 0; i < n; ++i)
- if (deg[i] == 2)
- {
- x = FIRSTBITNZ(g[i]);
- y = FIRSTBITNZ(g[i]&~bit[x]);
- e1 = edgenumber[i][x];
- if (!(bit[e1] & cycle8)) continue;
- e2 = edgenumber[i][y];
- if (edge[e2].allenemate1 < 0)
- {
- if (e1 < e2) edge[e2].allenemate1 = e1;
- else edge[e1].allenemate1 = e2;
- }
- else
- {
- if (e1 < e2) edge[e2].allenemate2 = e1;
- else edge[e1].allenemate2 = e2;
- }
- }
- }
-
- if (outlevel == 1)
- {
- if (!uswitch) writeg6(outfile,g,1,n);
- return;
- }
-
- /* Make a SMILES skeleton structure for later use */
- if (smiles) makesmilesskeleton(g,n);
-
- colourvertices(g,n);
-}
-
-/****************************************************************/
-
-static void
-decode_formula(char *formula, int *nv,
- int *mine, int *maxe, int *maxd, int *maxc)
-/* Parse the input formula. The number of hydrogens goes to hydrogens.
- The other distinct elements go to elementlist[0..numtypes-1] and
- elementcount[0..numtypes-1].
- *mine and *maxe have an edge range from -e and are updated.
- *mind and *maxc come from -d and -c and are updated.
- *nv gets the number of non-H atoms.
-*/
-{
- int i,j,d,mult,val,cnt,totval,dbe,forced;
- int maxvcoord,localmine,localmaxe,localmaxd,xi,yi;
- char *s1,*s2,*p;
- int count[FORMULALEN];
-
- if (numelements > FORMULALEN)
- gt_abort(">E surge : increase FORMULALEN\n");
-
- /* First we fill in count[*], which is parallel to element[*] */
-
- for (i = 0; i < numelements; ++i) count[i] = 0;
-
- for (s1 = formula; *s1 != '\0'; s1 = s2)
- {
- if (!isupper(*s1)) gt_abort(">E surge : unknown element name\n");
- for (s2 = s1+1; islower(*s2); ++s2) {}
- for (i = 0; i < numelements; ++i)
- {
- for (j = 0; element[i].inputname[j] != '\0'
- && s1+j != s2 && element[i].inputname[j] == s1[j]; ++j) {}
- if (element[i].inputname[j] == '\0' && s1+j == s2) break;
- }
- if (i == numelements) gt_abort(">E surge : unknown element name\n");
- s1 = s2;
- if (!isdigit(*s2))
- ++count[i];
- else
- {
- mult = *s2 - '0';
- for (s2 = s1+1; isdigit(*s2); ++s2) mult = 10*mult+(*s2-'0');
- count[i] += mult;
- }
- }
-
- /* Next we collect elements actually used into elementtype[0..numtypes-1]
- and elementcount[0..numtypes-1], except for H which we just count. */
-
- numtypes = hydrogens = 0;
- for (i = 0; i < numelements; ++i)
- {
- cnt = count[i];
- if (cnt > 0)
- {
- if (ISHYDROGEN(i))
- hydrogens = cnt;
- else
- {
- elementtype[numtypes] = i;
- elementcount[numtypes] = cnt;
- ++numtypes;
- }
- }
- }
-
- /* Next we adjust *maxd and *maxc, as well as the maxcoord
- fields of elements */
-
- maxvcoord = 0;
- for (i = 0; i < numtypes; ++i)
- if (element[elementtype[i]].maxcoord > maxvcoord)
- maxvcoord = element[elementtype[i]].maxcoord;
- if (maxvcoord < *maxc)
- *maxc = maxvcoord;
- else if (maxvcoord > *maxc)
- for (i = 0; i < numtypes; ++i)
- if (element[elementtype[i]].maxcoord > *maxc)
- element[elementtype[i]].maxcoord = *maxc;
- if (*maxd > *maxc) *maxd = *maxc;
-
- /* Next we find some bounds on the number of vertices
- with various simple degrees. */
-
- min1 = min12 = max34 = max4 = 0;
- for (i = 0; i < numelements; ++i)
- {
- if (!ISHYDROGEN(i))
- {
- cnt = count[i];
- val = element[i].maxcoord;
- if (val <= 1) min1 += cnt; // Check logic
- if (val <= 2) min12 += cnt;
- if (val >= 3) max34 += cnt;
- if (val >= 4) max4 += cnt;
- // Could add max5, could use both bounds everywhere
- }
- }
-
- /* Now sort by decreasing maximum coordination number */
-
- for (i = 1; i < numtypes; ++i) /* really 1 */
- {
- xi = elementtype[i];
- yi = elementcount[i];
- for (j = i; element[elementtype[j-1]].maxcoord < element[xi].maxcoord; )
- {
- elementtype[j] = elementtype[j-1];
- elementcount[j] = elementcount[j-1];
- if (--j < 1) break;
- }
- elementtype[j] = xi;
- elementcount[j] = yi;
- }
-
- /* Make "canonical" molecule name (used for -A output) */
-
- p = canonform;
- for (i = 0; i < numtypes; ++i)
- {
- PUTSTR(element[elementtype[i]].inputname);
- if (elementcount[i] > 1) PUTINT(elementcount[i]);
- }
- if (hydrogens > 0) PUTSTR("H");
- if (hydrogens > 1) PUTINT(hydrogens);
- *p = '\0';
-
- /* Calculate *nv, totalval, forced which all exclude H */
-
- *nv = forced = 0;
- totval = hydrogens;
- for (i = 0; i < numtypes; ++i)
- {
- j = elementtype[i];
- cnt = elementcount[i];
- *nv += cnt;
- totval += cnt * element[j].valence;
- if (element[j].valence > element[j].maxcoord)
- forced += element[j].valence - element[j].maxcoord;
- }
- forced = (forced+1) / 2;
-
- if ((totval & 1)) gt_abort(">E surge : impossible parity\n");
- dbe = totval / 2 - (*nv + hydrogens - 1) - forced;
- if (dbe < 0) gt_abort(">E surge : negative DBE\n");
- if (*nv > MAXN) gt_abort(">E surge : too many non-hydrogen atoms\n");
- if (*nv == 0) gt_abort(">E surge : only hydrogen\n");
- valencesum = totval - hydrogens;
-
- localmine = *nv - 1;
- localmaxe = localmine + dbe;
- if (localmaxe > *nv * (*nv-1) / 2) localmaxe = *nv * (*nv-1) / 2;
-
- if (localmine > *mine) *mine = localmine;
- if (localmaxe < *maxe) *maxe = localmaxe;
- if (*mine > *maxe) gt_abort(">E surge : edge range is empty\n");
-
- fprintf(stderr,"%s ",canonform);
- fprintf(stderr,"H=%d",hydrogens);
- for (i = 0; i < numtypes; ++i)
- fprintf(stderr," %s=%d",element[elementtype[i]].inputname,elementcount[i]);
-
- fprintf(stderr," nv=%d edges=%d-%d DBE=%d maxd=%d maxc=%d\n",
- *nv,*mine,*maxe,dbe,*maxd,*maxc);
- if (*maxe > MAXNE) gt_abort(">E surge : too many edges\n");
-
- for (d = 1; d <= *maxd; ++d)
- {
- for (i = 0; i < numtypes; ++i)
- {
- val = element[elementtype[i]].maxcoord;
- if (d <= val) maxtype[d] = i;
- }
- }
-}
-
-/****************************************************************/
-
-static void
-start_geng(int n, int maxd, int maxc,
- int mine, int maxe, char *extra1, long res, long mod)
-/* start geng with arguments, extra1 before n, extra2 after n */
-{
- int i,geng_argc,mind;
- char *geng_argv[20];
- char arga[30],argb[30];
- char resmod[40];
- char gengargs[80];
- char edgecount[40];
-
- mind = 1;
- if (hydrogens == 0)
- {
- for (i = 0; i < numtypes; ++i)
- if (element[elementtype[i]].valence < 4) break;
- if (i == numtypes) mind = 2;
- }
-
- if (n == 1) mind = 0;
-
- sprintf(arga,"-qcd%dD%d",mind,maxd);
- sprintf(argb,"%d",n);
- sprintf(edgecount,"%d:%d",mine,maxe);
-
- geng_argv[0] = "geng_surge";
- geng_argv[1] = arga;
- geng_argc = 2;
-
- if (tswitch && max3rings == 0)
- {
- geng_argv[geng_argc++] = "-t";
- tswitch = FALSE;
- }
-
- if (fswitch && max4rings == 0)
- {
- geng_argv[geng_argc++] = "-f";
- fswitch = FALSE;
- }
-
- if (bipartite) geng_argv[geng_argc++] = "-b";
-
- if (extra1)
- {
- snprintf(gengargs,78,"-%s",extra1);
- geng_argv[geng_argc++] = gengargs;
- }
- geng_argv[geng_argc++] = argb;
- geng_argv[geng_argc++] = edgecount;
- if (mod > 1)
- {
- sprintf(resmod,"%ld/%ld",res,mod);
- geng_argv[geng_argc++] = resmod;
- }
- geng_argv[geng_argc] = NULL;
-
- if (verbose)
- {
- fprintf(stderr,">geng");
- for (i = 1; geng_argv[i] != NULL; ++i)
- fprintf(stderr," %s",geng_argv[i]);
- fprintf(stderr,"\n");
- }
-
- geng_main(geng_argc,geng_argv);
-}
-
-/*******************************************************************/
-
-static void
-processEswitch(char **ps, char *id)
-/* Process -E starting at *ps = the character after E, and update *ps.
- The value has the form [,][],
- where and are either or and
- and are single digits. Inputname comes first. */
-{
- char inputname[3],name[3];
- int valence,maxcoord;
- char *s;
- int state,err;
-
- s = *ps;
- state = 0;
-
- while (state < 5)
- {
- switch (state)
- {
- case 0:
- if (!isupper(*s))
- {
- state = 6;
- break;
- }
- inputname[0] = *s++;
- if (islower(*s))
- {
- inputname[1] = *s++;
- inputname[2] = '\0';
- }
- else
- inputname[1] = '\0';
- state = 1;
- break;
- case 1:
- if (isupper(*s))
- state = 2;
- else
- {
- name[0] = inputname[0];
- name[1] = inputname[1];
- name[2] = inputname[2];
- state = 3;
- }
- break;
- case 2:
- name[0] = *s++;
- if (islower(*s))
- {
- name[1] = *s++;
- name[2] = '\0';
- }
- else
- name[1] = '\0';
- state = 3;
- break;
- case 3:
- if (!isdigit(*s))
- {
- state = 7;
- break;
- }
- valence = *s++ - '0';
- if (!isdigit(*s))
- maxcoord = valence;
- else
- maxcoord = *s++ - '0';
- state = 5;
- break;
- }
- }
-
- if (state == 6)
- {
- fprintf(stderr,">E %s : bad element name\n",id);
- exit(1);
- }
- if (state == 7)
- {
- fprintf(stderr,">E %s : bad valence or maxcoord\n",id);
- exit(1);
- }
-
- *ps = s;
-
- addelement(inputname,name,valence,maxcoord);
-}
-
-#define SWELEMENT(c,id) if (sw==c) {processEswitch(&arg,id);}
-
-/*******************************************************************/
-
-int
-main(int argc, char *argv[])
-{
- int argnum,i,j;
- boolean badargs,Gswitch,mswitch,Oswitch,eswitch,notriples;
- boolean oswitch,Bswitch,cswitch,Dswitch;
- char *extra1,*extra2,*formula,*arg,sw,*outfilename;
- long res,mod;
- int mine,maxe,maxd,maxc;
- long eminval,emaxval;
- double t1,t2;
- long badlist[BADLISTS];
- int badlen;
-
- HELP;
-
- nauty_check(WORDSIZE,1,1,NAUTYVERSIONID);
-
- maxindex = 0;
- for (i = 0; i < MAXELEMENTS; ++i)
- if (element[i].inputname == NULL) break;
- else if (element[i].index < maxindex && !ISHYDROGEN(i))
- maxindex = element[i].index;
- numelements = i;
-
-#ifdef SURGEPLUGIN_INIT
- SURGEPLUGIN_INIT;
-#endif
-
- argnum = 0;
- badargs = verbose = Gswitch = mswitch = FALSE;
- uswitch = eswitch = notriples = smiles = FALSE;
- oswitch = gzip = alphabetic = Bswitch = FALSE;
- tswitch = fswitch = pswitch = bipartite = FALSE;
- cswitch = planar = xswitch = Dswitch = FALSE;
- Oswitch = FALSE; outlevel = 4;
- extra1 = extra2 = formula = NULL;
- bad1 = bad2 = bad3 = bad4 = bad5 = bad6 = bad7 = bad8 = bad9 = FALSE;
-
- for (j = 1; !badargs && j < argc; ++j)
- {
- arg = argv[j];
- if (arg[0] == '-' && arg[1] != '\0')
- {
- ++arg;
- while (*arg != '\0')
- {
- sw = *arg++;
- if (sw == 'G')
- {
- if (Gswitch)
- gt_abort(">E surge: -G is only allowed once\n");
- Gswitch = TRUE;
- extra1 = arg;
- break;
- }
- else if (sw == 'o')
- {
- if (oswitch)
- gt_abort(">E surge : -o is only allowed once\n");
- oswitch = TRUE;
- outfilename = arg;
- break;
- }
- else SWRANGE('m',"/",mswitch,res,mod,"surge -m")
- else SWINT('O',Oswitch,outlevel,"surge -O")
- else SWINT('c',cswitch,maxc,"surge -c")
- else SWINT('d',Dswitch,maxd,"surge -d")
- else SWBOOLEAN('u',uswitch)
- else SWBOOLEAN('v',verbose)
- else SWBOOLEAN('T',notriples)
- else SWBOOLEAN('S',smiles)
- else SWBOOLEAN('z',gzip)
- else SWBOOLEAN('A',alphabetic)
- else SWBOOLEAN('b',bipartite)
- else SWBOOLEAN('P',planar)
- else SWBOOLEAN('x',xswitch)
- else SWSEQUENCEMIN('B',",",Bswitch,badlist,1,BADLISTS,badlen,"surge -B")
- else SWRANGE('e',":-",eswitch,eminval,emaxval,"surge -e")
- else SWRANGE('t',":-",tswitch,min3rings,max3rings,"surge -t")
- else SWRANGE('f',":-",fswitch,min4rings,max4rings,"surge -f")
- else SWRANGE('p',":-",pswitch,min5rings,max5rings,"surge -p")
- else SWELEMENT('E',"surge -E")
-#ifdef SURGEPLUGIN_SWITCHES
- else SURGEPLUGIN_SWITCHES
-#endif
- else badargs = TRUE;
-
- if (Bswitch)
- {
- for (i = 0; i < badlen; ++i)
- {
- if (badlist[i] < 1 || badlist[i] > BADLISTS)
- gt_abort(">E surge : invalid bad list number\n");
- if (badlist[i] == 1) bad1 = TRUE;
- else if (badlist[i] == 2) bad2 = TRUE;
- else if (badlist[i] == 3) bad3 = TRUE;
- else if (badlist[i] == 4) bad4 = TRUE;
- else if (badlist[i] == 5) bad5 = TRUE;
- else if (badlist[i] == 6) bad6 = TRUE;
- else if (badlist[i] == 7) bad7 = TRUE;
- else if (badlist[i] == 8) bad8 = TRUE;
- else if (badlist[i] == 9) bad9 = TRUE;
- /* Don't forget initialization if you add more */
- }
- Bswitch = FALSE;
- }
- }
- }
- else
- {
- ++argnum;
- if (argnum == 1) formula = arg;
- else badargs = TRUE;
- }
- }
-
- if (badargs)
- {
- fprintf(stderr,">E Usage: %s\n",USAGE EXTRAUSAGE);
- GETHELP;
- exit(1);
- }
-
- if (Oswitch && (outlevel <= 0 || outlevel >= 5))
- gt_abort(">E surge : unknown value for -O\n");
-
- if (!cswitch) maxc = 4;
- if (!Dswitch) maxd = 4;
-
-#ifndef ZLIB
- if (gzip)
- gt_abort(">E surge : -z is only allowed if zlib is compiled in\n");
-#endif
-
- if (uswitch) gzip = oswitch = alphabetic = FALSE;
-
- if (alphabetic && smiles)
- gt_abort(">E surge : -A and -S are incompatible\n");
-
- if (!oswitch || (oswitch && strcmp(outfilename,"-") == 0))
- outfilename = "stdout";
-
- if (bad5) bad6 = FALSE; /* bad6 is a subset of bad5 */
- if (notriples) bad1 = FALSE;
- if (tswitch && fswitch && max3rings+max4rings <= 1)
- bad9 = FALSE;
-
- needcycles = (bad1 || bad2 || bad3 || bad4 || bad6);
-
- if (fswitch && max4rings < 6) bad7 = FALSE;
-
- if (tswitch && max3rings < 3) bad8 = FALSE;
- if (fswitch && max4rings < 2) bad8 = FALSE;
- if (pswitch && max5rings == 0) bad8 = FALSE;
-
- if (gzip)
- {
-#ifdef ZLIB
- if (strcmp(outfilename,"stdout") == 0)
- gzoutfile = gzdopen(fileno(stdout),"wb");
- else
- gzoutfile = gzopen(outfilename,"wb");
- if (!gzoutfile)
- gt_abort(">E surge : unable to open compressed stream\n");
- gzbuffer(gzoutfile,1<<16); /* Remove this line if gzbuffer()
- is not found; it means you have a old version of zlib. */
-#endif
- }
- else
- {
- if (strcmp(outfilename,"stdout") == 0)
- outfile = stdout;
- else
- outfile = fopen(outfilename,"w");
- if (!outfile)
- gt_abort(">E surge : can't open output file\n");
- }
-
- maxbond = (notriples ? 1 : 2);
-
- if (!Oswitch) outlevel = 4;
-
- if (mswitch)
- {
- if (res < 0 || res >= mod)
- gt_abort(">E surge : -mres/mod needs 0 <= res < mod\n");
- }
- else
- {
- res = 0;
- mod = 1;
- }
-
- if (badargs || argnum != 1)
- {
- fprintf(stderr,">E Usage: %s\n",USAGE);
- GETHELP;
- exit(1);
- }
-
- if (eswitch)
- {
- mine = (int)eminval;
- maxe = (int)emaxval;
- }
- else
- {
- mine = 0;
- maxe = NOLIMIT;
- }
-
- decode_formula(formula,&nv,&mine,&maxe,&maxd,&maxc);
-
- t1 = CPUTIME;
- start_geng(nv,maxd,maxc,mine,maxe,extra1,res,mod);
-#ifdef ZLIB
- if (gzip)
- if (gzclose(gzoutfile) != Z_OK)
- gt_abort(">E surge : error on closing compressed stream\n");
-#endif
- t2 = CPUTIME;
-
- if (vgroup) free(vgroup); /* Make valgrind happy */
- if (egroup) free(egroup);
-
- if (verbose)
- {
- fprintf(stderr,">G geng made %lld graphs, %lld accepted\n",
- gengout,genggood);
- if (outlevel > 1)
- fprintf(stderr,">V vcolg %lld nontrivial groups, max size"
- " %ld, made %lld graphs\n",vcolgnontriv,maxvgroup,vcolgout);
- if (outlevel > 2)
- fprintf(stderr,">M multig %lld nontrivial groups, max size"
- " %ld, made %lld graphs\n",multignontriv,maxegroup,multigout);
- }
-
- if (needcycles) fprintf(stderr,"Max cycles = %d\n",maxcycles);
-
-#ifdef SURGEPLUGIN_SUMMARY
- SURGEPLUGIN_SUMMARY
-#endif
-
- fprintf(stderr,">Z %s %llu -> %llu -> %llu in %.2f sec\n",
- (uswitch ? "generated" : "wrote"),
- gengout,vcolgout,multigout,t2-t1);
-
- return 0;
-}
+/* This is a molecule generator based on geng.
+ Version 2.0, January 2026.
+
+ Brendan McKay
+ Christoph Steinbeck
+
+ A typical Unix-style compilation command is:
+
+ gcc -o surge -O3 -DWORDSIZE=32 -DMAXN=WORDSIZE -DOUTPROC=surgeproc \
+ -march=native -DPREPRUNE=surgepreprune \
+ -DPRUNE=surgeprune -DGENG_MAIN=geng_main \
+ surge.c geng.c planarity.c nautyW1.a
+
+ But use the makefile if you can.
+
+ You can build-in gzip output using the zlib library (https://zlib.net).
+ Add -DZLIB to the compilation, and link with the zlib library either
+ by adding -lz or libz.a . This will activate the -z command to gzip
+ the output.
+
+ gcc -o surge -O3 -DWORDSIZE=32 -DMAXN=WORDSIZE -DOUTPROC=surgeproc \
+ -march=native -mtune=native -DPREPRUNE=surgepreprune -DZLIB \
+ -DPRUNE=surgeprune -DGENG_MAIN=geng_main \
+ surge.c geng.c planarity.c nautyW1.a -lz
+
+ There is a makefile in the package; edit the first few lines.
+
+ This version works best with geng version 3.3 or later. To use
+ with an earlier version, add -DOLDGENG to the compilation command.
+
+ Changes since version 1.0.
+
+ 1.1: For SDfile output, a single output call is made for each molecule
+ rather than each line. This gives a small improvement in throughput.
+
+ Counting only (-u) is now the default.
+ To obtain SDfile output, use -F.
+
+ 2.0: The new -R switch removes all but one molecule from each set of
+ Kekule structures equivalent under carbon-ring aromaticity.
+ See the manual for a precise definition. The summary line now
+ shows the counts before and after the filtering.
+
+ -h# and -h#:# restrict the number of hexagons
+ -C# and -C#:# restrict the number of carbon 6-rings
+
+***********************************************************************/
+
+#ifdef ZLIB
+#define USAGE \
+ "[-oFILE] [-z] [-A|-S|-F] [-T] [-e#|-e#:#] [-R] [-d#] [-c#] [-m#/#] formula"
+#else
+#define USAGE \
+ "[-oFILE] [-A|-S|-F] [-T] [-e#|-e#:#] [-R] [-d#] [-c#] [-m#/#] formula"
+#endif
+
+#define HELPUSECMD
+
+#define HELPTEXT1 \
+"Make chemical graphs from a formula. Version 2.0.\n" \
+" Known elements are C,B,N,P,O,S,H,Cl,F,Br,I at their lowest valences.\n" \
+" Higher valences can be selected using Nx (Nitrogen/5), Sx,Sy (Sulfur 4/6)\n" \
+" Px (Phosphorus/5).\n" \
+"\n" \
+" formula = a formula like C8H6N2\n" \
+"\n" \
+" -u Just count, don't write molecules (default)\n" \
+" -S Output in SMILES format\n" \
+" -F Output in SDfile format\n" \
+" -A Output in alphabetical format\n" \
+" -e# -e#:# Limit the number of distinct non-H bonds\n" \
+" -t# -t#:# Limit the number of cycles of length 3\n" \
+" -f# -f#:# Limit the number of cycles of length 4\n" \
+" -p# -p#:# Limit the number of cycles of length 5\n" \
+" -h# -h#:# Limit the number of cycles of length 6\n" \
+" -C# -C#:# Limit the number of chord-free cycles 6 carbon atoms\n" \
+" -b Only rings of even length (same as only cycles of even length)\n" \
+" -T Disallow triple bonds\n" \
+" -P Require planarity\n" \
+" -d# Maximum degree not counting bond multiplicity or hydrogens (default 4)\n" \
+" -c# Maximum coordination number (default 4). This is the maximum number\n" \
+" of distinct atoms (including H) that an atom can be bonded to\n" \
+" Coordination number > 4 is only allowed if no neighbours are H\n" \
+" -B#,...,# Specify sets of substructures to avoid (details in manual)\n" \
+" 1 = no triple bonds in rings up to length 7\n" \
+" 2 = Bredt's rule for two rings ij with one bond in\n" \
+" common (33, 34, 35, 36, 44, 45)\n" \
+" 3 = Bredt's rule for two rings ij with two bonds in\n" \
+" common (i,j up to 56)\n" \
+" 4 = Bredt's rule for two rings of length 6 sharing three bonds\n" \
+" 5 = no substructures A=A=A (in ring or not)\n" \
+" 6 = no substructures A=A=A in rings up to length 8\n" \
+" For -B5 and -B6, the central atom only has 2 non-H neighbours\n" \
+" 7 = no K_33 or K_24 structure\n" \
+" 8 = none of cone of P4 or K4 with 3-ear\n" \
+" 9 = no atom in more than one ring of length 3 or 4\n" \
+" -R Enable aromaticity detection (filters duplicate Kekule structures)\n" \
+" -v Write more information to stderr\n" \
+" -m#/# Do only a part. The two numbers are res/mod where 0<=res
+#ifdef ZLIB
+#include "zlib.h"
+#endif
+
+#define DEBUG 0
+
+static struct smilesstruct
+{
+ int item;
+ int x,y,r;
+} smilesskeleton[4*MAXN+6*MAXNE];
+/* Values for the item field */
+#define SM_ATOM 1 /* Atom number x */
+#define SM_BOND 2 /* Bond x,y */
+#define SM_OPEN 3 /* Open ( */
+#define SM_CLOSE 4 /* Close ) */
+#define SM_RING0 5 /* Broken bond x,y for new ring r */
+#define SM_RING1 6 /* End of ring r */
+static int smileslen;
+
+typedef unsigned long long counter; /* For counters that might overflow */
+
+static int hydrogens;
+static int nv; /* number of atoms except H */
+static int numtypes; /* different elements except H */
+#define FORMULALEN 50
+static int elementtype[FORMULALEN],elementcount[FORMULALEN];
+ /* Not H, decreasing valence */
+static char canonform[2*FORMULALEN]; /* The formula in canonical order */
+static int valencesum; /* Sum of valences for non-H */
+static int maxtype[8]; /* maxtype[d] is maximum index into
+ elementtype/elementcount for vertices of degree d */
+
+/* Used with -A */
+static boolean alphabetic;
+static int newlabel[MAXN]; /* New number of atom i */
+
+#define BADLISTS 9 /* Number of defined bad lists */
+static boolean bad1; /* Avoid triple edges in rings up to length 7 */
+ /* bad1 is turned off if -T is given */
+static boolean bad2; /* Bredt's rule for one common bond */
+static boolean bad3; /* Bredt's rule for two common bonds */
+static boolean bad4; /* Bredt's rule for three common bonds */
+static boolean bad5; /* Avoid =A= even if not in a ring */
+static boolean bad6; /* Avoid =A= in rings up to length 8 */
+ /* Note that bad6 is turned off if bad5 is set */
+static boolean bad7; /* Avoid K_{2,4} and K_{3,3} */
+static boolean bad8; /* Avoid cone(P4) and K4 with 3-ear */
+ /* bad8 is turned off if -t and -f options make it impossible */
+static boolean bad9; /* No atom on two rings of length 3 or 4 */
+ /* bad9 is turned off if -t and -f options make it impossible */
+
+static boolean needcoordtest;
+
+static boolean needrings; /* List of induced cycles needed */
+static int maxrings=0,maxcycles=0;
+#define MAXCYCLES 300
+static setword inducedcycle[MAXCYCLES]; /* Only if needrings */
+static int ringcount;
+static setword sixring[MAXCYCLES]; /* Only if Cswitch */
+static int sixringcount;
+
+int GENG_MAIN(int argc, char *argv[]); /* geng main() */
+static int min1,min12,max34,max4; /* bounds on degree counts on geng output */
+
+static counter gengout=0, genggood=0;
+static counter vcolgnontriv=0,vcolgout=0;
+static counter multignontriv=0,multigout=0;
+static counter molnum=0;
+static long maxvgroup,maxegroup;
+
+static boolean uswitch; /* suppress output */
+static boolean verbose; /* print more information to stderr */
+static int outlevel; /* 1 = geng only, 2 = geng+vcolg,
+ 3 = geng+vcolg+multig, 4 = everything */
+static boolean smiles; /* output in SMILES format */
+static boolean SDFoutput; /* output in SDfile format */
+static int maxbond; /* maximum mult -1 of bonds (1 if -t, else 2) */
+
+static boolean planar; /* Molecules must be planar */
+static int maxcoord; /* Maximum coordination number allowed */ //UNUSED
+static boolean xswitch; /* Undocumented, used for development */
+static boolean Rswitch; /* Enable aromaticity detection */
+
+static int carbonindex = -1; /* Index of C */
+
+/* Pre-computed aromatic-size cycles
+ * (computed once per graph after geng if -R is given) */
+#define AROM_MAXCYCLES 1024
+static struct cycle { setword odd,even; } arom_cycles[AROM_MAXCYCLES];
+static int arom_cyclecount;
+
+/* In the following, the counts are only meaningful if the
+ corresponding boolean is true. */
+static boolean tswitch;
+static long min3cycles,max3cycles; /* number of rings of length 3 */
+static int count3cyc[MAXN+1];
+static boolean fswitch;
+static long min4cycles,max4cycles; /* number of cycles of length 4 */
+static int count4cyc[MAXN+1];
+static boolean pswitch;
+static long min5cycles,max5cycles; /* number of cycles of length 5 */
+static int count5cyc[MAXN+1];
+static boolean hswitch;
+static long min6cycles,max6cycles; /* number of cycles of length 6 */
+static int count6cyc[MAXN+1];
+static boolean Cswitch;
+static long minCrings,maxCrings; /* Number of carbon 6-rings */
+
+static boolean bipartite;
+
+/* The following is only used if bad9 is selected */
+static setword cycle34verts[MAXN+1]; /* set of vertices on rings
+ of length 3 or 4 */
+
+static long vgroupsize; /* vcolg group size */
+static size_t vgroupalloc=0; /* Space allocated for vertex groups */
+static int *vgroup=NULL; /* Store of vertex group */
+static long vgroupcount;
+
+static long egroupsize; /* multig group size */
+static size_t egroupalloc=0; /* Space allocated for edge groups */
+static int *egroup=NULL; /* Store of edge group */
+
+typedef struct edge
+{
+ int x,y; /* The atoms with this bond */
+ int maxmult; /* This edge can have multiplicity 1..maxmult+1 */
+ int allenemate1,allenemate2;
+ /* Previous bond that cannot be multiple at the same time as this */
+ setword xy;
+} edgetype;
+static int numedges;
+static edgetype edge[MAXNE];
+static int edgenumber[MAXN][MAXN];
+static int deg[MAXN]; /* Simple graph degree */
+
+static FILE *outfile;
+#ifdef ZLIB
+static gzFile gzoutfile;
+#endif
+static boolean gzip;
+
+/* Macros for appending to a string using pointer p */
+#define PUTINT(xx) { unsigned long ul = (xx); char *sp,s[15]; \
+ if (ul == 0) *(p++) = '0'; \
+ else { sp = s; while (ul) { *(sp++) = (ul % 10) + '0'; ul /= 10; } \
+ while (sp > s) { *(p++) = *(--sp); } }}
+#define SPC *(p++) = ' '
+#define PUTSTR(xx) { char *sp = (xx); \
+ while (*sp != '\0') *(p++) = *(sp++); }
+#define PUTBND(xx) { int bnd = (xx); if (bnd == 0) *(p++) = '-'; \
+ else if (bnd == 1) *(p++) = '='; else *(p++) = '#'; }
+
+/******************************************************************/
+
+#define MAXELEMENTS 30
+static struct elementstruct
+{
+ char *inputname,*name;
+ boolean organic; /* Belongs to organic subset */
+ int valence;
+ int lowervalence; /* Next lower valence, or 0 if none */
+ int maxcoord; /* Maximum number of distinct neighbours including H */
+ int index; /* Used in -T style outputs */
+} element[MAXELEMENTS] =
+{ /* The order of listing does not matter.
+ Other elements can be added to the table by following the same
+ pattern, up to any limit. All extra elements must be marked
+ as non-organic. The inputname field must have the form X or Xx
+ and must be unique. */
+ { "C", "C", TRUE, 4,0,4, 0 },
+ { "N", "N", TRUE, 3,0,3, 1 },
+ { "Nx","N", TRUE, 5,3,4, 10 },
+ { "P", "P", TRUE, 3,0,3, 3 },
+ { "Px","P", TRUE, 5,3,5, 13 },
+ { "B", "B", TRUE, 3,0,3, 9 },
+ { "O", "O", TRUE, 2,0,2, 2 },
+ { "S", "S", TRUE, 2,0,2, 4 },
+ { "Sx","S", TRUE, 4,2,4, 11 },
+ { "Sy","S", TRUE, 6,4,6, 12 },
+ { "H", "H", FALSE, 1,0,1, 99 },
+ { "F", "F", TRUE, 1,0,1, 5 },
+ { "Cl","Cl",TRUE, 1,0,1, 6 },
+ { "Br","Br",TRUE, 1,0,1, 7 },
+ { "I", "I", TRUE, 1,0,1, 8 },
+ { "Si","Si",FALSE, 4,0,4, 14 },
+ { NULL,NULL,FALSE, 0,0,0, 0 }
+};
+static int numelements; /* Actual number in the table */
+static int maxindex; /* Max value of the index field except 99 */
+#define ISHYDROGEN(i) (strcmp(element[i].name,"H") == 0)
+
+/******************************************************************/
+
+static int
+elementindex(char *inputname)
+/* Index into element[] of element with this element input name,
+ or -1 if it isn't present. */
+{
+ int i;
+
+ for (i = 0; i < numelements; ++i)
+ if (strcmp(element[i].inputname,inputname) == 0)
+ break;
+
+ if (i < numelements) return i;
+ else
+ {
+ fprintf(stderr,">E Unknown element %s\n",inputname);
+ exit(1);
+ }
+}
+
+static void
+addelement(char *inputname, char *name, int valence, int maxcoord)
+/* Add an element to element[]. inputname and name must be of the
+form A or Aa. If name==NULL then name is the same as inputname. If
+maxcoord==0 or maxcoord > valence then maxcoord is the same as valence. */
+{
+ int i;
+
+ if (numelements == MAXELEMENTS)
+ gt_abort(">E increase MAXELEMENTS\n");
+
+ if (name == NULL) name = inputname;
+ if (maxcoord == 0 || maxcoord > valence) maxcoord = valence;
+
+ if (inputname == NULL || valence <= 0)
+ gt_abort(">E invalid parameters in addelement()\n");
+
+ for (i = 0; i < numelements; ++i)
+ {
+ if (strcmp(element[i].inputname,inputname) == 0)
+ {
+ fprintf(stderr,">E element %s is already present\n",name);
+ exit(1);
+ }
+ }
+
+ if (!isupper(inputname[0]) || (inputname[1] != '\0'
+ && (!islower(inputname[1]) || inputname[2] != '\0')))
+ gt_abort(">E element names must have form A or Aa\n");
+ if (!isupper(name[0]) || (name[1] != '\0'
+ && (!islower(name[1]) || name[2] != '\0')))
+ gt_abort(">E element names must have form A or Aa\n");
+
+ if (3*maxcoord < valence || valence < 0)
+ gt_abort(">E impossible maxcoord/valence\n");
+
+ element[numelements].inputname = strdup(inputname);
+ element[numelements].name = strdup(name);
+ element[numelements].valence = valence;
+ /* lowervalence is only used for SMILES output of
+ atoms in the organic subset and since those are
+ in the table already we don't need a value to
+ be set here. */
+ element[numelements].lowervalence = 0;
+ element[numelements].maxcoord = maxcoord;
+ element[numelements].organic = FALSE;
+ element[numelements].index = ++maxindex;
+
+ if (verbose)
+ fprintf(stderr,"Added element %s input %s, valence=%d maxcoord=%d\n",
+ element[numelements].name,element[numelements].inputname,
+ element[numelements].valence,element[numelements].maxcoord);
+ ++numelements;
+}
+
+#undef HELPTEXT2
+#ifdef SURGEPLUGIN
+/* Note that the value of SURGEPLUGIN must be a filename in quotes, so on
+ the compilation command you will probably need something like
+ -DSURGEPLUGIN='"myplugin.h"' . */
+#include SURGEPLUGIN
+#endif
+
+#ifndef HELPTEXT2
+#define HELPTEXT2 ""
+#endif
+
+/******************************************************************/
+
+#if 0
+static unsigned long long
+molcode(int *vcol, int *mult, int n, int ne)
+/* This makes an isomorph-invariant code for a molecule. It is intended
+for debugging and can't be relied on to distinguish between different
+molecules. It only works up to MAXN/2 atoms (but you can compile the
+program with WORDSIZE=64 if you really need to). */
+{
+ graph g[MAXN],h[MAXN];
+ int lab[MAXN],ptn[MAXN],orbits[MAXN],weight[MAXN];
+ static DEFAULTOPTIONS_GRAPH(options);
+ statsblk stats;
+ setword workspace[2*MAXN];
+ int i,x,y,nv;
+ unsigned long long ans1,ans2;
+
+ nv = 2*n;
+ if (nv >= MAXN) gt_abort(">E surge : too big for longcode\n");
+
+ for (i = 0; i < n; ++i) { weight[i] = vcol[i]; weight[n+i] = n+vcol[i]; }
+ setlabptn(weight,lab,ptn,nv);
+
+ for (i = 0; i < nv; ++i) g[i] = 0;
+
+ for (i = 0; i < n; ++i) { g[i] |= bit[n+i]; g[n+i] |= bit[i]; }
+
+ for (i = 0; i < ne; ++i)
+ {
+ x = edge[i].x;
+ y = edge[i].y;
+ g[x] |= bit[y]; g[y] |= bit[x];
+ if (mult[i] > 0) { g[n+x] |= bit[n+y]; g[n+y] |= bit[n+x]; }
+ if (mult[i] > 1)
+ {
+ g[x] |= bit[y+n]; g[y+n] |= bit[x];
+ g[x+n] |= bit[y]; g[y] |= bit[x+n];
+ }
+ }
+
+ options.defaultptn = FALSE;
+ options.getcanon = TRUE;
+
+ nauty(g,lab,ptn,NULL,orbits,&options,&stats,workspace,2*MAXN,1,nv,h);
+
+ ans1 = n;
+ ans2 = ne;
+ for (i = 0; i < nv; ++i)
+ {
+ ans1 = 177*ans1 + (h[i] >> (WORDSIZE-nv));
+ ans2 = 1237*ans2 + (h[i] >> (WORDSIZE-nv));
+ }
+
+ return ans1^ans2;
+}
+#endif
+
+/******************************************************************/
+
+void dummy(counter cnt)
+{
+ fprintf(stderr,">C %llu\n",cnt);
+}
+
+/******************************************************************/
+
+static void
+printset(FILE *f, setword w, int newline)
+/* Write the set to f with optional newline. */
+{
+ int i,first;
+
+ if (w == 0)
+ fprintf(f,"{}");
+ else
+ {
+ first = 1;
+ while (w)
+ {
+ TAKEBIT(i,w);
+ if (first) fprintf(f,"{%d",i);
+ else fprintf(f,",%d",i);
+ first = 0;
+ }
+ fprintf(f,"}");
+ }
+ if (newline) fprintf(f,"\n");
+}
+
+/******************************************************************/
+
+static boolean
+isplanar(graph *g, int n)
+/* Check if g is planar, assuming g is connected */
+{
+ t_ver_sparse_rep V[MAXN];
+ t_adjl_sparse_rep A[2*MAXNE+1];
+ t_dlcl **dfs_tree,**back_edges,**mult_edges;
+ t_ver_edge *embed_graph;
+ int i,j,k,pop,nv,ne,c;
+ int edge_pos,v,w;
+ setword ww;
+ boolean ans;
+ graph h[MAXN];
+ int newlab[MAXN];
+ setword queue;
+
+ queue = 0;
+ ne = 0;
+ for (i = 0; i < n; ++i)
+ {
+ h[i] = g[i];
+ pop = POPCOUNT(h[i]);
+ ne += pop;
+ if (pop <= 2) queue |= bit[i];
+ }
+ ne /= 2;
+ nv = n;
+
+ while (queue && ne >= nv+3)
+ {
+ TAKEBIT(i,queue);
+ pop = POPCOUNT(h[i]);
+ if (pop == 1) /* i--j with deg(i)=1 */
+ {
+ j = FIRSTBITNZ(h[i]);
+ h[i] = 0;
+ h[j] &= ~bit[i];
+ --nv;
+ --ne;
+ queue |= bit[j];
+ }
+ else if (pop == 2) /* j--i--k with deg(i)=2 */
+ {
+ j = FIRSTBITNZ(h[i]);
+ k = FIRSTBITNZ(h[i] & ~bit[j]);
+ h[i] = 0;
+ h[j] &= ~bit[i];
+ h[k] &= ~bit[i];
+ --nv;
+ if ((h[j] & bit[k]))
+ {
+ ne -= 2;
+ queue |= (bit[j] | bit[k]);
+ }
+ else
+ {
+ --ne;
+ h[j] |= bit[k];
+ h[k] |= bit[j];
+ }
+ }
+ }
+
+ if (ne <= nv + 2) return TRUE;
+ if (nv == 5 && ne <= 9) return TRUE;
+
+ nv = 0;
+ for (i = 0; i < n; ++i) if (h[i] != 0) newlab[i] = nv++;
+
+ k = 0;
+ for (i = 0; i < n; ++i)
+ if (h[i] != 0)
+ {
+ V[newlab[i]].first_edge = k;
+ ww = h[i];
+ while (ww)
+ {
+ TAKEBIT(j,ww);
+ A[k].end_vertex = newlab[j];
+ A[k].next = k+1;
+ ++k;
+ }
+ A[k-1].next = NIL;
+ }
+
+ ne = k/2;
+
+ ans = sparseg_adjl_is_planar(V,nv,A,&c,&dfs_tree,&back_edges,
+ &mult_edges,&embed_graph,&edge_pos,&v,&w);
+
+ sparseg_dlcl_delete(dfs_tree,nv);
+ sparseg_dlcl_delete(back_edges,nv);
+ sparseg_dlcl_delete(mult_edges,nv);
+ embedg_VES_delete(embed_graph,nv);
+
+ return ans;
+}
+
+/******************************************************************/
+
+static void
+SMILESoutput(int *vcol, int n, int *hyd, int *mult, int ne)
+/* Write molecules in SMILES format */
+{
+ char *p,line[20*MAXNE];
+ int i,x,y,r,m;
+ const struct elementstruct *thiselement;
+
+ p = line;
+
+ for (i = 0; i < smileslen; ++i)
+ {
+ x = smilesskeleton[i].x;
+ y = smilesskeleton[i].y;
+ switch(smilesskeleton[i].item)
+ {
+ case SM_ATOM :
+ thiselement = &element[vcol[x]];
+#if 0
+ /* This version seems to meet the OpenSmiles standard, but some
+ problems with obabel have not been tracked down. */
+ if (!thiselement->organic ||
+ thiselement->valence - hyd[x] <= thiselement->lowervalence)
+#else
+ /* This version gives explicit H for atoms in higher valences even
+ if they are in the organic subset. */
+ if (!thiselement->organic ||
+ (thiselement->lowervalence > 0 && hyd[x] > 0))
+#endif
+ {
+ *(p++) = '[';
+ PUTSTR(thiselement->name);
+ if (hyd[x] > 0) *(p++) = 'H';
+ if (hyd[x] > 1) PUTINT(hyd[x])
+ *(p++) = ']';
+ }
+ else
+ PUTSTR(thiselement->name);
+ break;
+ case SM_BOND :
+ m = mult[edgenumber[x][y]];
+ if (m == 1) *(p++) = '=';
+ else if (m == 2) *(p++) = '#';
+ break;
+ case SM_OPEN :
+ *(p++) = '(';
+ break;
+ case SM_CLOSE :
+ *(p++) = ')';
+ break;
+ case SM_RING0 :
+ m = mult[edgenumber[x][y]];
+ if (m == 1) *(p++) = '=';
+ else if (m == 2) *(p++) = '#';
+ r = smilesskeleton[i].r;
+ if (r < 10)
+ *(p++) = '0' + r;
+ else
+ {
+ *(p++) = '%';
+ *(p++) = '0' + r/10;
+ *(p++) = '0' + r%10;
+ }
+ break;
+ case SM_RING1 :
+ r = smilesskeleton[i].r;
+ if (r < 10)
+ *(p++) = '0' + r;
+ else
+ {
+ *(p++) = '%';
+ *(p++) = '0' + r/10;
+ *(p++) = '0' + r%10;
+ }
+ break;
+ }
+ }
+
+ *(p++) = '\n';
+ *p = '\0';
+
+#ifdef ZLIB
+ if (gzip)
+ {
+ if (gzputs(gzoutfile,line) < 0)
+ gt_abort(">E surge : zlib output error\n");
+ return;
+ }
+#endif
+
+ if (fputs(line,outfile) == EOF) gt_abort(">E surge : output error\n");
+}
+
+static char SDbuffer[70+70*MAXN+22*MAXNE]; /* Used for SDfile output */
+
+/******************************************************************/
+
+static void
+SDFformat(int *vcol, int n, int *hyd, int *mult, int ne)
+/* Write molecules in SDF format */
+{
+ int i;
+ char *p;
+
+ p = SDbuffer;
+ sprintf(p,"\nSurge 2.0\n\n");
+ p += 12;
+ sprintf(p,"%3d%3d 0 0 0 0 999 V2000\n",n,ne);
+ p += 40;
+
+ for (i = 0; i < n; ++i)
+ {
+ sprintf(p," 0.0000 0.0000 0.0000 %-2s"
+ " 0 0 0 0 0%3d 0 0 0 0 0 0\n",
+ element[vcol[i]].name,element[vcol[i]].valence);
+ p += 70;
+ }
+
+ for (i = 0; i < ne; ++i)
+ {
+ sprintf(p,"%3d%3d%3d 0 0 0 0\n",
+ edge[i].x+1,edge[i].y+1,mult[i]+1);
+ p += 22;
+ }
+
+ sprintf(p,"M END\n$$$$\n");
+
+#ifdef ZLIB
+ if (gzip)
+ {
+ gzwrite(gzoutfile,SDbuffer,64+70*n+22*ne);
+
+ return;
+ }
+#endif
+
+ fwrite(SDbuffer,1,64+70*n+22*ne,outfile);
+}
+
+/****************************************************************/
+
+static void
+multigoutput(int *vcol, int n, int *mult, int ne)
+/* Write output equal to the multig -T format. */
+{
+ char line[10+60*MAXN],*p;
+ int i;
+
+ p = line;
+ PUTINT(n); SPC; PUTINT(ne);
+ for (i = 0; i < n; ++i)
+ {
+ SPC;
+ PUTINT(element[vcol[i]].index);
+ }
+ SPC;
+ for (i = 0; i < ne; ++i)
+ {
+ SPC; PUTINT(edge[i].x);
+ SPC; PUTINT(edge[i].y);
+ SPC; PUTINT(mult[i]);
+ }
+ *(p++) = '\n';
+ *p = '\0';
+
+#ifdef ZLIB
+ if (gzip)
+ {
+ if (gzputs(gzoutfile,line) < 0)
+ gt_abort(">E surge : zlib output error\n");
+ return;
+ }
+#endif
+
+ if (fputs(line,outfile) == EOF) gt_abort(">E surge : output error\n");
+}
+
+/****************************************************************/
+
+static void
+alphabeticoutput(int *vcol, int n, int *mult, int ne)
+/* Write alphabetic output */
+{
+ char line[10+60*MAXN],*p;
+ int i,xx,yy;
+
+ p = line;
+ PUTINT(n);
+ SPC;
+ PUTINT(ne);
+ SPC;
+ PUTSTR(canonform);
+
+ for (i = 0; i < ne; ++i)
+ {
+ SPC;
+ xx = newlabel[edge[i].x];
+ yy = newlabel[edge[i].y];
+ if (xx < yy)
+ {
+ PUTINT(xx); PUTBND(mult[i]); PUTINT(yy);
+ }
+ else
+ {
+ PUTINT(yy); PUTBND(mult[i]); PUTINT(xx);
+ }
+ }
+ *(p++) = '\n';
+ *p = '\0';
+
+#ifdef ZLIB
+ if (gzip)
+ {
+ if (gzputs(gzoutfile,line) < 0)
+ gt_abort(">E surge : zlib output error\n");
+ return;
+ }
+#endif
+
+ if (fputs(line,outfile) == EOF) gt_abort(">E surge : output error\n");
+}
+
+/******************************************************************/
+/* Aromaticity detection functions */
+
+static void
+pathscan(graph *g, int first, int start, setword body,
+ setword last, setword thiseven, setword thisodd)
+/* Paths in g starting at start, lying within body and
+ ending in last. {start} and last should be disjoint subsets of body. */
+{
+ setword gs,w;
+ int i,nc,len;
+
+ gs = g[start];
+ w = gs & last;
+ nc = POPCOUNT(w);
+ len = POPCOUNT(thisodd|thiseven);
+ if (len % 4 == 0 && nc)
+ {
+ if (arom_cyclecount + nc > AROM_MAXCYCLES)
+ gt_abort(">E surge: increase AROM_MAXCYCLES\n");
+ while (w)
+ {
+ TAKEBIT(i,w);
+ arom_cycles[arom_cyclecount].even
+ = thiseven | bit[edgenumber[first][i]];
+ arom_cycles[arom_cyclecount].odd
+ = thisodd | bit[edgenumber[start][i]];
+ ++arom_cyclecount;
+ }
+ }
+
+ body &= ~bit[start];
+ w = gs & body;
+ while (w)
+ {
+ TAKEBIT(i,w);
+ if (len % 2 == 0)
+ pathscan(g,first,i,body,last&~bit[i],
+ thiseven,thisodd|bit[edgenumber[start][i]]);
+ else
+ pathscan(g,first,i,body,last&~bit[i],
+ thiseven|bit[edgenumber[start][i]],thisodd);
+ }
+}
+
+static void
+arom_find_cycles(graph *g, int n)
+{
+ setword body,nbhd;
+ int first,j,d;
+
+ arom_cyclecount = 0;
+
+ body = 0;
+ for (j = 0; j < n; ++j)
+ {
+ d = POPCOUNT(g[j]);
+ if (d > 1 && d < 4) body |= bit[j];
+ }
+
+ while (body)
+ {
+ TAKEBIT(first,body);
+ nbhd = g[first] & body;
+ while (nbhd)
+ {
+ TAKEBIT(j,nbhd);
+ pathscan(g,first,j,body,nbhd,0,bit[edgenumber[first][j]]);
+ }
+ }
+
+ if (arom_cyclecount > maxcycles) maxcycles = arom_cyclecount;
+}
+
+static boolean
+largermult(setword singles, setword doubles, int *mult, int ne)
+/* Return TRUE if an non-trivial edge group element takes this to
+ * something greater than mult[]. Otherwise return FALSE. */
+{
+ int newmult[MAXNE];
+ int i,j,*gp,res;
+ long kgp;
+
+ res = 0;
+ for (i = 0; i < ne; ++i)
+ {
+ if ((singles&bit[i])) newmult[i] = 0;
+ else if ((doubles&bit[i])) newmult[i] = 1;
+ else newmult[i] = mult[i];
+ }
+
+ /* The identity is not stored, kgp starts at 1 below */
+ for (kgp = 1, gp = egroup; kgp < egroupsize; ++kgp, gp += ne)
+ {
+ for (i = 0; i < ne; ++i)
+ {
+ j = gp[i];
+ if (newmult[j] > mult[i]) return TRUE;
+ else if (newmult[j] < mult[i]) break;
+ }
+ }
+ return FALSE;
+}
+
+/* Used by -R to list single C-C bonds for aromaticity test.
+ Bound on number of labelled molecules that can be obtained by
+ repeated rotation of aromatic cycles. */
+#define MAXAROMATES 512
+static setword singlelist[MAXAROMATES];
+static int arom_parent[MAXAROMATES];
+
+static boolean
+is_not_maximal(setword singles, setword doubles, int *mult, int ne)
+/* Return TRUE iff is not a maximal form. */
+{
+ setword allCC,sing,doub,newsing;
+ int ci,head,tail,i,par;
+
+ allCC = singles | doubles; /* All CC bonds */
+
+ /* We use a queue to find everything reached by rotating aromatic
+ * cycles recursively. For each one, we test if it is minimal
+ * under the edge group. */
+
+ tail = 0;
+ head = 1;
+ singlelist[0] = singles;
+ arom_parent[0] = -1;
+ while (tail < head)
+ {
+ par = arom_parent[tail];
+ sing = singlelist[tail++];
+ doub = allCC ^ sing;
+ for (ci = 0; ci < arom_cyclecount; ++ci)
+ if (ci != par)
+ {
+ if ((!(arom_cycles[ci].odd & ~sing)
+ && !(arom_cycles[ci].even & ~doub))
+ || (!(arom_cycles[ci].odd & ~doub)
+ && !(arom_cycles[ci].even & ~sing)))
+ {
+ /* Now this is an aromatic cycle */
+ if ((arom_cycles[ci].odd & ~sing))
+ newsing = (sing | arom_cycles[ci].odd) & ~arom_cycles[ci].even;
+ else
+ newsing = (sing | arom_cycles[ci].even) & ~arom_cycles[ci].odd;
+ for (i = 0; i < head; ++i) /* Check if new */
+ if (newsing == singlelist[i]) break;
+ if (i == head)
+ {
+ if (newsing < singles) return TRUE;
+ if (egroupsize > 1
+ && largermult(newsing,allCC^newsing,mult,ne)) return TRUE;
+ if (head == MAXAROMATES)
+ {
+ fprintf(stderr,">E surge: increase MAXAROMATES\n");
+ exit(1);
+ }
+ arom_parent[head] = ci;
+ singlelist[head++] = newsing;
+ }
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+static boolean
+arom_check_duplicate(int *vcol, int n, int *hyd, int *mult, int ne)
+/* Returns TRUE iff this molecule is a duplicate aromatic structure. */
+{
+ int ei;
+ setword singles,doubles; /* C-C and C=C bonds */
+
+ singles = doubles = 0;
+
+ for (ei = 0; ei < ne; ++ei)
+ {
+ if (vcol[edge[ei].x] == carbonindex
+ && vcol[edge[ei].y] == carbonindex)
+ {
+ if (mult[ei] == 0) singles |= bit[ei];
+ if (mult[ei] == 1) doubles |= bit[ei];
+ }
+ }
+
+ return is_not_maximal(singles,doubles,mult,ne);
+}
+
+/******************************************************************/
+
+static void
+gotone(int *vcol, int n, int *hyd, int *mult, int ne, int level)
+/* Now we have a completed molecule.
+ deg[0..n-1] is the simple graph degrees
+ hyd[0..n-1] is the number of implicit hydrogens
+*/
+{
+ int i;
+
+ for (i = level; i < ne; ++i) mult[i] = 0;
+
+ if (needcoordtest)
+ for (i = 0; i < n; ++i)
+ {
+ if (deg[i] + hyd[i] > element[vcol[i]].maxcoord) return;
+ if (deg[i] + hyd[i] > 4 && hyd[i] > 0) return;
+ }
+
+ ++molnum;
+
+ /* Check for duplicate aromatic structure if -R enabled */
+ if (Rswitch && arom_check_duplicate(vcol, n, hyd, mult, ne)) return;
+
+#ifdef SURGEPLUGIN_STEP3
+ SURGEPLUGIN_STEP3
+#endif
+
+ ++multigout;
+ if (uswitch) return;
+
+ if (outlevel == 3)
+ multigoutput(vcol,n,mult,ne);
+ else if (alphabetic)
+ alphabeticoutput(vcol,n,mult,ne);
+ else if (smiles)
+ SMILESoutput(vcol,n,hyd,mult,ne);
+ else
+ SDFformat(vcol,n,hyd,mult,ne);
+}
+
+/******************************************************************/
+
+static boolean
+testemax(int *mult, int ne, int level)
+/* Test if edge colouring is maximum wrt group. */
+{
+ int *gp,i,j;
+ long kgp;
+
+ for (i = level; i < ne; ++i) mult[i] = 0;
+
+ /* kgp really starts at 1 on the next line */
+ for (kgp = 1, gp = egroup; kgp < egroupsize; ++kgp, gp += ne)
+ {
+ for (i = 0; i < ne; ++i)
+ {
+ j = gp[i];
+ if (mult[j] > mult[i]) return FALSE;
+ else if (mult[j] < mult[i]) break;
+ }
+ }
+
+ return TRUE;
+}
+
+/***************************************************************************/
+
+static void
+/* Recursive scan for multiplying edges
+ Version which checks allenemate for -B3 */
+escan2(int level, int needed, int *vcol, int *hyd, int *prev, int n, int *mult, int ne)
+{
+ int lev,maxlev,k,max,x,y;
+
+ if (needed == 0)
+ {
+ if (egroupsize > 1 && !testemax(mult,ne,level))
+ return;
+ gotone(vcol,n,hyd,mult,ne,level);
+ return;
+ }
+ else
+ {
+ maxlev = ne + 1 - (needed+maxbond-1)/maxbond;
+ for (lev = level; lev < maxlev; ++lev)
+ {
+ x = edge[lev].x;
+ y = edge[lev].y;
+ max = edge[lev].maxmult;
+
+ if (needed < max) max = needed;
+ if (hyd[x] < max) max = hyd[x];
+ if (hyd[y] < max) max = hyd[y];
+ if (prev[lev] >= 0 && mult[prev[lev]] < max) max = mult[prev[lev]];
+ if (edge[lev].allenemate1 >= 0 && mult[edge[lev].allenemate1] >= 1)
+ max = 0;
+ if (edge[lev].allenemate2 >= 0 && mult[edge[lev].allenemate2] >= 1)
+ max = 0;
+
+ for (k = 1; k <= max; ++k)
+ {
+ mult[lev] = k;
+ hyd[x] -= k;
+ hyd[y] -= k;
+ escan2(lev+1,needed-k,vcol,hyd,prev,n,mult,ne);
+ hyd[x] += k;
+ hyd[y] += k;
+ }
+
+ mult[lev] = 0;
+ }
+ }
+
+ return;
+}
+
+/***************************************************************************/
+
+static void
+/* Recursive scan for multiplying edges */
+escan(int level, int needed, int *vcol, int *hyd,
+ int *prev, int n, int *mult, int ne)
+{
+ int lev,maxlev,k,max,x,y;
+
+ if (needed == 0)
+ {
+ if (egroupsize > 1 && !testemax(mult,ne,level))
+ return;
+ gotone(vcol,n,hyd,mult,ne,level);
+ return;
+ }
+ else
+ {
+ maxlev = ne + 1 - (needed+maxbond-1)/maxbond;
+ for (lev = level; lev < maxlev; ++lev)
+ {
+ x = edge[lev].x;
+ y = edge[lev].y;
+ max = edge[lev].maxmult;
+
+ if (needed < max) max = needed;
+ if (hyd[x] < max) max = hyd[x];
+ if (hyd[y] < max) max = hyd[y];
+ if (prev[lev] >= 0 && mult[prev[lev]] < max)
+ max = mult[prev[lev]];
+
+ for (k = 1; k <= max; ++k)
+ {
+ mult[lev] = k;
+ hyd[x] -= k;
+ hyd[y] -= k;
+ escan(lev+1,needed-k,vcol,hyd,prev,n,mult,ne);
+ hyd[x] += k;
+ hyd[y] += k;
+ }
+
+ mult[lev] = 0;
+ }
+ }
+
+ return;
+}
+
+/******************************************************************/
+
+static void
+findegroup(int *vcol, int n, int ne)
+/* Set egroup to the set of vertex-colour-preserving vgroup elements */
+{
+ int *vgp,*egp,i,j,kgp;
+
+ egp = egroup;
+
+ /* kgp really starts at 1 on the next line */
+ for (kgp = 1, vgp = vgroup; kgp < vgroupsize; ++kgp, vgp += n)
+ {
+ for (i = 0; i < n; ++i)
+ if (vcol[vgp[i]] != vcol[i]) break;
+ if (i == n)
+ for (j = 0; j < ne; ++j)
+ *(egp++) = edgenumber[vgp[edge[j].x]][vgp[edge[j].y]];
+ }
+
+ egroupsize = 1 + (egp - egroup) / ne;
+}
+
+/****************************************************************/
+
+static void
+colouredges(graph *g, int *vcolindex, int n)
+/* This procedure receives graphs from the vcolg phase and
+ colours the edges. */
+{
+ int hyd[MAXN]; /* Remaining degree of vertex */
+ int i,j,k,ne;
+ int mult[MAXNE];
+ int prev[MAXNE]; /* If >= 0, earlier point that must have greater colour */
+ int needed; /* Extra edges needed */
+ int iter[FORMULALEN];
+ int vcol[MAXN]; /* index into element[] */
+ setword CCbonds;
+ int Cringcount;
+
+ ne = numedges;
+
+ /* hyd[i] starts with the number of hydrogens needed if all bonds are
+ single and is reduced as bonds are multiplied */
+
+ needcoordtest = FALSE;
+ for (i = 0; i < n; ++i)
+ {
+ vcol[i] = elementtype[vcolindex[i]];
+ hyd[i] = element[vcol[i]].valence - deg[i];
+ if (element[vcol[i]].valence > element[vcol[i]].maxcoord
+ || (element[vcol[i]].valence > 4 && hyd[i] > 0))
+ needcoordtest = TRUE;
+ }
+
+#ifdef SURGEPLUGIN_STEP2
+ SURGEPLUGIN_STEP2
+#endif
+
+ needed = (valencesum - hydrogens)/2 - ne; /* Extra edges needed */
+
+ if (ne == 0 && needed > 0) return;
+
+ if (Cswitch)
+ {
+ CCbonds = 0;
+ for (i = 0; i < ne; ++i)
+ if (vcol[edge[i].x] == carbonindex
+ && vcol[edge[i].y] == carbonindex)
+ CCbonds |= bit[i];
+ Cringcount = 0;
+ for (i = 0; i < sixringcount; ++i)
+ if (!(sixring[i] & ~CCbonds)) ++Cringcount;
+ if (Cringcount < minCrings || Cringcount > maxCrings)
+ return;
+ }
+
+ if (alphabetic)
+ {
+ iter[0] = 0;
+ for (i = 1; i < numtypes; ++i) iter[i] = iter[i-1] + elementcount[i-1];
+
+ for (i = 0; i < n; ++i) newlabel[i] = iter[vcolindex[i]]++;
+ }
+
+ if (needed == 0)
+ {
+ gotone(vcol,n,hyd,mult,ne,0);
+ return;
+ }
+
+ for (i = 0; i < ne; ++i) prev[i] = -1;
+
+ for (i = 0; i < n; ++i)
+ {
+ if (deg[i] != 1) continue;
+ /* Find most recent equivalent j */
+ for (j = i; --j >= 0; )
+ if (g[i] == g[j] && vcol[j] == vcol[i])
+ break;
+
+ if (j >= 0)
+ {
+ k = FIRSTBITNZ(g[i]);
+ prev[edgenumber[i][k]] = edgenumber[j][k];
+ }
+ }
+
+ if (vgroupsize == 1)
+ egroupsize = 1;
+ else
+ {
+ if (egroupalloc < vgroupsize*ne)
+ {
+ if (egroup) free(egroup);
+ if ((egroup = malloc((vgroupsize+48)*ne*sizeof(int))) == NULL)
+ gt_abort(">E surge : Can't allocate space for egroup\n");
+ egroupalloc = (vgroupsize+48) * ne;
+ }
+ findegroup(vcol,n,ne);
+ if (vgroupsize % egroupsize != 0) gt_abort(">E egroup error\n");
+ }
+
+ if (egroupsize == 1 && needed == 1)
+ {
+ for (i = 0; i < ne; ++i) mult[i] = 0;
+ for (i = 0; i < ne; ++i)
+ if (prev[i] < 0 && edge[i].maxmult >= 1
+ && hyd[edge[i].x] > 0 && hyd[edge[i].y] > 0)
+ {
+ mult[i] = 1;
+ --hyd[edge[i].x];
+ --hyd[edge[i].y];
+ gotone(vcol,n,hyd,mult,ne,ne);
+ ++hyd[edge[i].x];
+ ++hyd[edge[i].y];
+ mult[i] = 0;
+ }
+ return;
+ }
+
+ if (egroupsize != 1) ++multignontriv;
+ if (egroupsize > maxegroup) maxegroup = egroupsize;
+
+ if (bad5 || bad6) escan2(0,needed,vcol,hyd,prev,n,mult,ne);
+ else escan(0,needed,vcol,hyd,prev,n,mult,ne);
+}
+
+/******************************************************************/
+
+static void
+vcolgoutput(graph *g, int *vcolindex, int n)
+/* Write output equal to the vcolg -T format. */
+{
+ char line[10+30*MAXN],*p;
+ int i,j,ne;
+ setword w;
+
+ ne = 0;
+ for (i = 0; i < n; ++i) ne += POPCOUNT(g[i]);
+ ne /= 2;
+
+ p = line;
+ PUTINT(n); SPC; PUTINT(ne);
+ for (i = 0; i < n; ++i)
+ {
+ SPC;
+ PUTINT(element[elementtype[vcolindex[i]]].index);
+ }
+ SPC;
+ for (i = 0; i < n; ++i)
+ {
+ w = g[i] & BITMASK(i);
+ while (w)
+ {
+ TAKEBIT(j,w);
+ SPC; PUTINT(i); SPC; PUTINT(j);
+ }
+ }
+ *(p++) = '\n';
+ *p = '\0';
+
+#ifdef ZLIB
+ if (gzip)
+ {
+ if (gzputs(gzoutfile,line) < 0)
+ gt_abort(">E surge : zlib output error\n");
+ return;
+ }
+#endif
+
+ if (fputs(line,outfile) == EOF) gt_abort(">E surge : output error\n");
+}
+
+/******************************************************************/
+
+static boolean
+testvmax(int *colindex, int n)
+/* Test if vertex colouring is maximum wrt group. If so, return group.
+ If not, return a safe level to return to. */
+{
+ int *gp,i,j;
+ long kgp;
+
+ /* kgp really starts at 1 on the next line */
+ for (kgp = 1, gp = vgroup; kgp < vgroupsize; ++kgp, gp += n)
+ {
+ for (i = 0; i < n; ++i)
+ {
+ j = gp[i];
+ if (colindex[j] > colindex[i]) return FALSE;
+ else if (colindex[j] < colindex[i]) break;
+ }
+ }
+
+ return TRUE;
+}
+
+/**********************************************************************/
+
+static void
+vscan(int level, int *colindex, graph *g, int *prev,
+ int *maxcolindex, int *remain, int n)
+/* Recursive vertex colour scan */
+{
+ int k,max;
+
+ if (level == n)
+ {
+ if (vgroupsize == 1 || testvmax(colindex,n))
+ {
+ ++vcolgout;
+ if (outlevel == 2)
+ {
+ if (!uswitch) vcolgoutput(g,colindex,n);
+ }
+ else
+ colouredges(g,colindex,n);
+ }
+ return;
+ }
+
+ max = maxcolindex[level];
+ if (prev[level] >= 0 && colindex[prev[level]] < max)
+ max = colindex[prev[level]];
+
+ for (k = 0; k <= max; ++k)
+ {
+ if (remain[k] == 0) continue;
+ colindex[level] = k;
+ --remain[k];
+ vscan(level+1,colindex,g,prev,maxcolindex,remain,n);
+ ++remain[k];
+ }
+}
+
+/**********************************************************************/
+
+static void
+storevgroup(int *p, int n)
+/* Called by allgroup; store full group at vcolg phase */
+{
+ int *gp,i;
+
+ if (vgroupcount == 0)
+ {
+ vgroupcount = 1; /* Don't store identity */
+ return;
+ }
+
+ gp = vgroup + n * (vgroupcount-1);
+ for (i = 0; i < n; ++i) gp[i] = p[i];
+
+ ++vgroupcount;
+}
+
+/**********************************************************************/
+
+static void
+colourvertices(graph *g, int n)
+/* Main procedure for vcolg phase */
+{
+ static DEFAULTOPTIONS_GRAPH(options);
+ statsblk stats;
+ setword workspace[2*MAXN];
+ grouprec *group;
+ int lab[MAXN],ptn[MAXN],orbits[MAXN];
+ int prev[MAXN]; /* If >= 0, earlier point that must have greater colour */
+ int weight[MAXN];
+ int maxcolindex[MAXN]; /* Max colour index for each vertex */
+ int remain[FORMULALEN]; /* Remaining number of colours for each vertex */
+ int vcolindex[MAXN]; /* Index into elementtype[] */
+ int i,j;
+
+#ifdef SURGEPLUGIN_STEP1
+ SURGEPLUGIN_STEP1
+#endif
+
+ for (i = 0; i < n; ++i)
+ {
+ prev[i] = -1;
+ weight[i] = n*POPCOUNT(g[i]);
+ }
+
+ for (i = 0; i < n; ++i)
+ {
+ if (POPCOUNT(g[i]) != 1) continue;
+ /* Find most recent equivalent j */
+ for (j = i; --j >= 0; )
+ if (g[j] == g[i]) break;
+
+ if (j >= 0)
+ {
+ prev[i] = j;
+ weight[i] = weight[j] + 1;
+ }
+ }
+
+ options.userautomproc = groupautomproc;
+ options.userlevelproc = grouplevelproc;
+ options.defaultptn = FALSE;
+
+ setlabptn(weight,lab,ptn,n);
+
+ nauty(g,lab,ptn,NULL,orbits,&options,&stats,workspace,2*MAXN,1,n,NULL);
+
+ if (stats.grpsize2 > 0 || stats.grpsize1 > 1e7)
+ gt_abort(">E surge : vgroup size greater than 10^7 encountered\n");
+ vgroupsize = stats.grpsize1 + 0.01;
+
+ for (i = 0; i < numtypes; ++i) remain[i] = elementcount[i];
+ for (i = 0; i < n; ++i) maxcolindex[i] = maxtype[deg[i]];
+
+ if (vgroupsize == 1) /* Trivial group */
+ {
+ vscan(0,vcolindex,g,prev,maxcolindex,remain,n);
+ return;
+ }
+
+ ++vcolgnontriv;
+ if (vgroupsize > maxvgroup) maxvgroup = vgroupsize;
+
+ group = groupptr(FALSE);
+ makecosetreps(group);
+
+ if (vgroupalloc < (vgroupsize-1) * n)
+ {
+ if (vgroup) free(vgroup);
+ if ((vgroup = malloc((vgroupsize+48)*n*sizeof(int))) == NULL)
+ gt_abort(">E surge : Can't allocate space for vgroup\n");
+ vgroupalloc = (vgroupsize+48) * n;
+ }
+
+ vgroupcount = 0;
+ allgroup(group,storevgroup);
+
+ if (vgroupcount != vgroupsize) gt_abort(">E surge : vgroup error\n");
+
+ /* Check the logic of the next section. What about maxdeg? */
+ if (numtypes == 1)
+ {
+ for (i = 0; i < n; ++i) vcolindex[i] = 0;
+ ++vcolgout;
+ colouredges(g,vcolindex,n);
+ return;
+ }
+
+ j = n; /* Can choose a better orbit? */
+ for (i = 0; i < n; ++i)
+ if (orbits[i] < i && orbits[i] < j) j = orbits[i];
+ for (i = j + 1; i < n; ++i)
+ if (orbits[i] == j) prev[i] = j;
+
+ vscan(0,vcolindex,g,prev,maxcolindex,remain,n);
+}
+
+/******************************************************************/
+
+extern int geng_maxe;
+
+int
+surgepreprune(graph *g, int n, int maxn)
+/* This function is called by the PREPRUNE service of geng.
+ It does -B9 which is more efficient here. If OLDGENG is
+ defined, it also speeds up the generation of connected
+ graphs. (For new geng, that function is built in.) */
+{
+ setword notvisited,queue;
+ setword c34,w,ww,cyc;
+ int ne,nc,i;
+
+ if (bad9) /* cycle34verts */
+ {
+ if (n <= 2)
+ cycle34verts[n] = 0;
+ else
+ {
+ c34 = cycle34verts[n-1];
+ if (!tswitch)
+ {
+ w = g[n-1];
+ while (w)
+ {
+ TAKEBIT(i,w);
+ ww = g[i] & w;
+ if (POPCOUNT(ww) > 1) return 1;
+ if ((ww))
+ {
+ cyc = bit[n-1] | bit[i] | ww;
+ if ((c34 & cyc)) return 1;
+ c34 |= cyc;
+ }
+ }
+ }
+ if (!fswitch)
+ {
+ for (i = n-1; --i >= 0;)
+ {
+ w = g[i] & g[n-1];
+ if (POPCOUNT(w) > 2) return 1;
+ if (POPCOUNT(w) == 2)
+ {
+ cyc = bit[n-1] | bit[i] | w;
+ if ((c34 & cyc)) return 1;
+ c34 |= cyc;
+ }
+ }
+ }
+ cycle34verts[n] = c34;
+ }
+ }
+
+#ifdef OLDGENG
+ if (n == maxn || geng_maxe - maxn >= 5) return 0;
+
+ ne = 0;
+ for (i = 0; i < n; ++i) ne += POPCOUNT(g[i]);
+ ne /= 2;
+
+ nc = 0;
+ notvisited = ALLMASK(n);
+
+ while (notvisited)
+ {
+ ++nc;
+ queue = SWHIBIT(notvisited);
+ notvisited &= ~queue;
+ while (queue)
+ {
+ TAKEBIT(i,queue);
+ notvisited &= ~bit[i];
+ queue |= g[i] & notvisited;
+ }
+ }
+
+ if (ne - n + nc > geng_maxe - maxn + 1) return 1;
+#endif
+
+ return 0;
+}
+
+int
+surgeprune(graph *g, int n, int nmax)
+/* This is a procedure that geng will call at each level
+using the PRUNE service.
+The options -t, -f, -p, -h, -B7,8 are implemented here by
+incrementally updating the required counts. */
+{
+ setword w,ax,ay,gx,gxy,gxya,gi,gj,gn,gxn,gyn,bitxyn;
+ int i,j,x,y,k,a,b,extra;
+ int i1,i2,i3,i4,d1,d2,d3,d4,xy,xn,yn,xyn;
+ int v[MAXN];
+
+ if (tswitch)
+ {
+ if (n <= 2)
+ count3cyc[n] = 0;
+ else
+ {
+ extra = 0;
+ w = g[n-1];
+ while (w)
+ {
+ TAKEBIT(i,w);
+ extra += POPCOUNT(g[i]&w);
+ }
+ count3cyc[n] = count3cyc[n-1] + extra;
+ if (count3cyc[n] > max3cycles) return 1;
+ }
+ if (n == nmax && count3cyc[n] < min3cycles)
+ return 1;
+ }
+
+ if (fswitch)
+ {
+ if (n <= 3)
+ count4cyc[n] = 0;
+ else
+ {
+ extra = 0;
+ for (i = 0; i < n-1; ++i)
+ {
+ k = POPCOUNT(g[i]&g[n-1]);
+ extra += k*k - k;
+ }
+ count4cyc[n] = count4cyc[n-1] + extra/2;
+ if (count4cyc[n] > max4cycles) return 1;
+ }
+ if (n == nmax && count4cyc[n] < min4cycles)
+ return 1;
+ }
+
+ if (pswitch)
+ {
+ if (n <= 4)
+ count5cyc[n] = 0;
+ else
+ {
+ extra = 0;
+ for (y = 1; y < n-1; ++y)
+ {
+ w = g[y] & ~BITMASK(y);
+ while (w)
+ {
+ TAKEBIT(x,w);
+ ax = (g[x] & g[n-1]) & ~bit[y];
+ ay = (g[y] & g[n-1]) & ~bit[x];
+ extra += POPCOUNT(ax)*POPCOUNT(ay) - POPCOUNT(ax&ay);
+ }
+ }
+ count5cyc[n] = count5cyc[n-1] + extra;
+ if (count5cyc[n] > max5cycles) return 1;
+ }
+ if (n == nmax && count5cyc[n] < min5cycles)
+ return 1;
+ }
+
+ if (hswitch)
+ {
+ if (n <= 4)
+ count6cyc[n] = 0;
+ else
+ {
+ extra = 0;
+ gn = g[n-1];
+ for (y = 1; y < n-1; ++y)
+ if ((g[y] & gn))
+ {
+ for (x = 0; x < y; ++x)
+ if ((g[x] & g[y]) && (g[x] & gn))
+ {
+ bitxyn = bit[x] | bit[y] | bit[n-1];
+ gxy = g[x] & g[y] & ~bitxyn;
+ gxn = g[x] & gn & ~bitxyn;
+ gyn = g[y] & gn & ~bitxyn;
+ xy = POPCOUNT(gxy);
+ xn = POPCOUNT(gxn);
+ yn = POPCOUNT(gyn);
+ xyn = POPCOUNT(gxy&gxn&gyn);
+ extra += xy*xn*yn - xyn*(xy+xn+yn-2);
+ }
+ }
+ count6cyc[n] = count6cyc[n-1] + extra;
+ if (count6cyc[n] > max6cycles) return 1;
+ }
+ if (n == nmax && count6cyc[n] < min6cycles)
+ return 1;
+ }
+
+ if (bad7 && n >= 6)
+ {
+ /* For K_33 we can assume that vertex n-1 is included */
+ for (x = n-1; --x >= 1;)
+ if (POPCOUNT(g[x]&g[n-1]) >= 3)
+ {
+ for (y = x; --y >= 0;)
+ if (POPCOUNT(g[y]&g[x]&g[n-1]) >= 3) return 1;
+ }
+
+ /* But for K_24 we can't */
+ for (x = n; --x >= 1;)
+ if (POPCOUNT(g[x]) >= 4)
+ {
+ for (y = x; --y >= 0;)
+ if (POPCOUNT(g[x]&g[y]) >= 4) return 1;
+ }
+ }
+
+ if (bad8)
+ {
+ /* cone of P4 */
+ if (n >= 5)
+ {
+ for (x = n; --x >= 0;)
+ if (POPCOUNT(g[x]) == 4)
+ {
+ /* Think of a better way to do this */
+ w = gx = g[x];
+ TAKEBIT(i1,w);
+ TAKEBIT(i2,w);
+ TAKEBIT(i3,w);
+ i4 = FIRSTBITNZ(w);
+ d1 = POPCOUNT(g[i1]&gx);
+ d2 = POPCOUNT(g[i2]&gx);
+ d3 = POPCOUNT(g[i3]&gx);
+ d4 = POPCOUNT(g[i4]&gx);
+ if (d1 > 0 && d2 > 0 && d3 > 0 && d4 > 0
+ && ((d1 >= 2)+(d2 >= 2)+(d3 >= 2)+(d4 >= 2)) >= 2)
+ return 1;
+ }
+ else if (POPCOUNT(g[x]) > 4)
+ {
+ w = gx = g[x];
+ i = 0;
+ while (w)
+ {
+ TAKEBIT(y,w);
+ if (POPCOUNT(g[y]&gx) >= 2) v[i++] = y;
+ }
+ for (--i; i >= 1; --i)
+ for (j = 0; j < i; ++j)
+ if ((g[v[i]] & bit[v[j]]) &&
+ POPCOUNT((g[v[i]]|g[v[j]])&gx) >= 4)
+ return 1;
+ }
+ }
+
+ /* K4 with a path of 3 edges between two of its vertices */
+
+ if (n >= 6)
+ {
+ for (x = n; --x >= 1;)
+ if (POPCOUNT(g[x]) >= 4)
+ {
+ for (y = x; --y >= 0;)
+ if (POPCOUNT(g[y]) >= 4 && (g[y]&bit[x]))
+ {
+ gxy = g[x] & g[y];
+ while (gxy)
+ {
+ TAKEBIT(a,gxy);
+ gxya = gxy & g[a];
+ while (gxya)
+ {
+ TAKEBIT(b,gxya);
+ w = bit[x] | bit[y] | bit[a] | bit[b];
+ gi = g[x] & ~w;
+ gj = g[y] & ~w;
+ while (gi)
+ {
+ TAKEBIT(i,gi);
+ if ((g[i] & gj)) return 1;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+#ifdef SURGEPLUGIN_STEP0
+ SURGEPLUGIN_STEP0
+#endif
+
+ return 0;
+}
+
+/******************************************************************/
+
+static void
+smilesdfs(graph *g, setword *seen, int v, int par, graph *back,
+ struct smilesstruct *smilestemp, int *len)
+/* Recursive DFS to collect SMILES information */
+{
+ setword gv,w;
+ int k;
+ boolean first;
+
+ gv = g[v];
+ first = TRUE;
+ *seen |= bit[v];
+
+ while (gv)
+ {
+ TAKEBIT(k,gv);
+ if ((*seen & bit[k]))
+ {
+ if (k != par)
+ {
+ back[v] |= bit[k];
+ back[k] |= bit[v];
+ }
+ }
+ else
+ {
+ if (first)
+ {
+ if (POPCOUNT(g[k]) == 1)
+ {
+ if ((w = (gv & ~*seen))) /* really = */
+ {
+ gv |= bit[k];
+ k = FIRSTBITNZ(w);
+ gv &= ~bit[k];
+ }
+ }
+ smilesdfs(g,seen,k,v,back,smilestemp,len);
+ first = FALSE;
+ }
+ else
+ {
+ smilestemp[(*len)++].item = SM_CLOSE;
+ smilesdfs(g,seen,k,v,back,smilestemp,len);
+ smilestemp[(*len)++].item = SM_OPEN;
+ }
+ }
+ }
+
+ smilestemp[*len].item = SM_ATOM;
+ smilestemp[*len].x = v;
+ ++*len;
+ if (par >= 0)
+ {
+ smilestemp[*len].item = SM_BOND;
+ smilestemp[*len].x = par;
+ smilestemp[*len].y = v;
+ ++*len;
+ }
+}
+
+static void
+makesmilesskeleton(graph *g, int n)
+/* Make a skeleton SMILES structure for use in SMILESoutput */
+{
+ struct smilesstruct smilestemp[4*MAXN+6*MAXNE];
+ graph back[MAXN],ring[MAXN];
+ setword w,seen;
+ int len,ringnumber;
+ int i,j,v;
+
+ for (v = n; --v >= 0; ) if (POPCOUNT(g[v]) == 1) break;
+ if (v < 0) v = n-1;
+
+ len = 0;
+ seen = 0;
+ for (i = 0; i < n; ++i) back[i] = 0;
+ for (i = 0; i < n; ++i) ring[i] = 0;
+
+ smilesdfs(g,&seen,v,-1,back,smilestemp,&len);
+
+ smileslen = 0;
+ ringnumber = 0;
+ for (i = len; --i >= 0; )
+ {
+ smilesskeleton[smileslen++] = smilestemp[i];
+ if (smilestemp[i].item == SM_ATOM)
+ {
+ v = smilestemp[i].x;
+ w = ring[v];
+ while (w)
+ {
+ TAKEBIT(j,w);
+ smilesskeleton[smileslen].item = SM_RING1;
+ smilesskeleton[smileslen].r = j+1;
+ ++smileslen;
+ }
+ w = back[v];
+ while (w)
+ {
+ TAKEBIT(j,w);
+ ++ringnumber;
+ smilesskeleton[smileslen].item = SM_RING0;
+ smilesskeleton[smileslen].x = v;
+ smilesskeleton[smileslen].y = j;
+ smilesskeleton[smileslen].r = ringnumber;
+ ++smileslen;
+ ring[j] |= bit[ringnumber-1];
+ back[j] &= ~bit[v];
+ }
+ }
+ }
+
+#ifdef SMILESSKELETON
+ /* This will print the SMILES skeleton to stdout */
+
+ fprintf(stdout,"len=%d",smileslen);
+ for (i = 0; i < smileslen; ++i)
+ {
+ switch (smilesskeleton[i].item)
+ {
+ case SM_ATOM :
+ fprintf(stdout," %d",smilesskeleton[i].x);
+ break;
+ case SM_BOND :
+ fprintf(stdout," %d-%d",smilesskeleton[i].x,smilesskeleton[i].y);
+ break;
+ case SM_OPEN :
+ fprintf(stdout," (");
+ break;
+ case SM_CLOSE :
+ fprintf(stdout," )");
+ break;
+ case SM_RING0 :
+ fprintf(stdout," R%d:%d-%d",smilesskeleton[i].r,
+ smilesskeleton[i].x,smilesskeleton[i].y);
+ break;
+ case SM_RING1 :
+ fprintf(stdout," R%d",smilesskeleton[i].r);
+ }
+ }
+ fprintf(stdout,"\n");
+#endif
+}
+
+/******************************************************************/
+
+static void
+inducedpaths(graph *g, int origin, int start, setword body,
+ setword last, setword path)
+/* Trace induced paths in g starting at start, extra vertices within
+ * body and ending in last. This is used to find induced cycles.
+ * {start}, body and last should be disjoint. */
+{
+ setword gs,w;
+ int i;
+
+ gs = g[start];
+
+ w = gs & last;
+ while (w)
+ {
+ TAKEBIT(i,w);
+ if (ringcount == MAXCYCLES) gt_abort(">E Increase MAXCYCLES\n");
+ inducedcycle[ringcount++]
+ = path | bit[edgenumber[start][i]] | bit[edgenumber[origin][i]];
+ }
+
+ w = gs & body;
+ while (w)
+ {
+ TAKEBIT(i,w);
+ inducedpaths(g,origin,i,body&~gs,last&~bit[i]&~gs,
+ path|bit[edgenumber[start][i]]);
+ }
+}
+
+static void
+findinducedcycles(graph *g, int n)
+/* Find all the induced cycles (called rings in the manual) */
+{
+ setword body,last,cni;
+ int i,j;
+
+ body = 0;
+ for (i = 0; i < n; ++i)
+ if (POPCOUNT(g[i]) > 1) body |= bit[i];
+
+ ringcount = 0;
+
+ while (body)
+ {
+ TAKEBIT(i,body);
+ last = g[i] & body;
+ cni = g[i] | bit[i];
+ while (last)
+ {
+ TAKEBIT(j,last);
+ inducedpaths(g,i,j,body&~cni,last,bit[edgenumber[i][j]]);
+ }
+ }
+
+ if (ringcount > maxrings) maxrings = ringcount;
+
+ if (verbose)
+ {
+ setword cyc; int i,j;
+ fprintf(stderr, "SURGE rings (%d):", ringcount);
+ for (i = 0; i < ringcount; ++i)
+ {
+ fprintf(stderr, " [");
+ cyc = inducedcycle[i];
+ int first = 1;
+ while (cyc)
+ {
+ TAKEBIT(j,cyc);
+ if (!first) fprintf(stderr, ",");
+ fprintf(stderr, "%d-%d", edge[j].x, edge[j].y);
+ first = 0;
+ }
+ fprintf(stderr, "](%d)", POPCOUNT(inducedcycle[i]));
+ }
+ fprintf(stderr, "\n");
+ }
+}
+
+static void
+find6rings(void)
+{
+ int i;
+
+ sixringcount = 0;
+ for (i = 0; i < ringcount; ++i)
+ if (POPCOUNT(inducedcycle[i]) == 6)
+ sixring[sixringcount++] = inducedcycle[i];
+}
+
+/******************************************************************/
+
+void
+surgeproc(FILE *outfile, graph *gin, int n)
+/* This is called by geng for each graph. */
+{
+ int i,j,k,d,n1,n12,n34,n4,ne;
+ int isize,jsize;
+ graph g[MAXN];
+ setword w,wxy,ww,pw,cyc,cycle8;
+ int x,y,e1,e2,e3;
+
+ n1 = n12 = n34 = n4 = 0;
+
+ ++gengout;
+
+ for (i = 0; i < n; ++i)
+ {
+ d = POPCOUNT(gin[i]); /* deg[i] is not defined yet */
+ if (d == 1) { ++n1; ++n12; }
+ else if (d == 2) ++n12;
+ else if (d == 3) ++n34;
+ else { ++n34; ++n4; }
+ }
+
+ if (n > 1 && (n1 < min1 || n12 < min12 || n34 > max34 || n4 > max4))
+ return;
+
+ if (planar && !isplanar(gin,n)) return;
+
+ ++genggood;
+
+ /* Reverse to put higher degrees first */
+
+ for (i = 0; i < n; ++i)
+ {
+ w = gin[n-i-1];
+ pw = 0;
+ while (w)
+ {
+ TAKEBIT(j,w);
+ pw |= bit[n-j-1];
+ }
+ g[i] = pw;
+ }
+
+ /* Make the edge list with default parameters */
+
+ ne = 0;
+ for (i = 0; i < n; ++i)
+ {
+ deg[i] = POPCOUNT(g[i]);
+ w = g[i] & BITMASK(i);
+ while (w)
+ {
+ TAKEBIT(j,w);
+ edge[ne].x = i;
+ edge[ne].y = j;
+ edge[ne].xy = bit[i] | bit[j];
+ edge[ne].maxmult = maxbond;
+ edge[ne].allenemate1 = edge[ne].allenemate2 = -1;
+ edgenumber[i][j] = edgenumber[j][i] = ne;
+ ++ne;
+ }
+ }
+ numedges = ne;
+
+ if (ne > WORDSIZE && (needrings || Rswitch))
+ if (ne > WORDSIZE)
+ gt_abort(">E surge : too many edges for badlists or -R\n");
+
+ if (needrings) findinducedcycles(g,n);
+ if (Cswitch)
+ {
+ find6rings();
+ if (sixringcount < minCrings) return;
+ }
+ if (Rswitch) arom_find_cycles(g,n);
+
+ if (bad1) /* no triple bonds in rings smaller than 7 */
+ {
+ for (i = 0; i < ringcount; ++i)
+ {
+ cyc = inducedcycle[i];
+ if (POPCOUNT(cyc) <= 7)
+ while (cyc)
+ {
+ TAKEBIT(j,cyc);
+ if (edge[j].maxmult == 2) edge[j].maxmult = 1;
+ }
+ }
+ }
+
+ if (bad2) /* Bredt's rule for one common bond */
+ {
+ for (i = 0; i < ringcount-1; ++i)
+ {
+ isize = POPCOUNT(inducedcycle[i]);
+ if (isize > 6) continue;
+ for (j = i+1; j < ringcount; ++j)
+ {
+ jsize = POPCOUNT(inducedcycle[j]);
+ if (jsize > 6) continue;
+
+ w = inducedcycle[i] & inducedcycle[j];
+ if (POPCOUNT(w) != 1) continue;
+
+ if (isize*jsize <= 15)
+ edge[FIRSTBITNZ(w)].maxmult = 0;
+
+ if (isize+jsize <= 9)
+ {
+ wxy = edge[FIRSTBITNZ(w)].xy;
+ ww = (inducedcycle[i] | inducedcycle[j]) & ~w;
+ while (ww)
+ {
+ TAKEBIT(k,ww);
+ if ((edge[k].xy & wxy)) edge[k].maxmult = 0;
+ }
+ }
+ }
+ }
+ }
+
+ if (bad3) /* Bredt's rule for two common bonds */
+ {
+ for (i = 0; i < ringcount-1; ++i)
+ {
+ isize = POPCOUNT(inducedcycle[i]);
+ if (isize == 3 || isize > 6) continue;
+
+ for (j = i+1; j < ringcount; ++j)
+ {
+ jsize = POPCOUNT(inducedcycle[j]);
+ if (jsize == 3 || jsize > 6 || isize+jsize == 12) continue;
+
+ w = inducedcycle[i] & inducedcycle[j];
+ if (POPCOUNT(w) != 2) continue;
+
+ ww = w;
+ TAKEBIT(k,ww);
+ edge[k].maxmult = 0;
+ edge[FIRSTBITNZ(ww)].maxmult = 0;
+ wxy = edge[k].xy ^ edge[FIRSTBITNZ(ww)].xy;
+
+ ww = (inducedcycle[i] | inducedcycle[j]) & ~w;
+ while (ww)
+ {
+ TAKEBIT(k,ww);
+ if ((edge[k].xy & wxy)) edge[k].maxmult = 0;
+ }
+ }
+ }
+ }
+
+ if (bad4) /* Bredt's rule for two hexagons with 3 bonds in common */
+ {
+ for (i = 0; i < ringcount-1; ++i)
+ {
+ isize = POPCOUNT(inducedcycle[i]);
+ if (isize != 6) continue;
+
+ for (j = i+1; j < ringcount; ++j)
+ {
+ jsize = POPCOUNT(inducedcycle[j]);
+ if (jsize != 6) continue;
+
+ w = inducedcycle[i] & inducedcycle[j];
+ if (POPCOUNT(w) != 3) continue;
+
+ ww = inducedcycle[i] | inducedcycle[j];
+
+ TAKEBIT(e1,w);
+ TAKEBIT(e2,w);
+ e3 = FIRSTBITNZ(w);
+
+ wxy = edge[e1].xy ^ edge[e2].xy ^ edge[e3].xy;
+ while (ww)
+ {
+ TAKEBIT(k,ww);
+ if ((edge[k].xy & wxy)) edge[k].maxmult = 0;
+ }
+ }
+ }
+ }
+
+ if (bad5) /* No A=A=A, whether in ring or not */
+ {
+ for (i = 0; i < n; ++i)
+ if (deg[i] == 2)
+ {
+ x = FIRSTBITNZ(g[i]);
+ y = FIRSTBITNZ(g[i]&~bit[x]);
+ e1 = edgenumber[i][x];
+ e2 = edgenumber[i][y];
+ if (edge[e2].allenemate1 < 0)
+ {
+ if (e1 < e2) edge[e2].allenemate1 = e1;
+ else edge[e1].allenemate1 = e2;
+ }
+ else
+ {
+ if (e1 < e2) edge[e2].allenemate2 = e1;
+ else edge[e1].allenemate2 = e2;
+ }
+ }
+ }
+
+ if (bad6) /* No A=A=A in rings up to length 8 */
+ {
+ cycle8 = 0;
+ for (i = 0; i < ringcount; ++i)
+ if (POPCOUNT(inducedcycle[i]) <= 8) cycle8 |= inducedcycle[i];
+
+ for (i = 0; i < n; ++i)
+ if (deg[i] == 2)
+ {
+ x = FIRSTBITNZ(g[i]);
+ y = FIRSTBITNZ(g[i]&~bit[x]);
+ e1 = edgenumber[i][x];
+ if (!(bit[e1] & cycle8)) continue;
+ e2 = edgenumber[i][y];
+ if (edge[e2].allenemate1 < 0)
+ {
+ if (e1 < e2) edge[e2].allenemate1 = e1;
+ else edge[e1].allenemate1 = e2;
+ }
+ else
+ {
+ if (e1 < e2) edge[e2].allenemate2 = e1;
+ else edge[e1].allenemate2 = e2;
+ }
+ }
+ }
+
+ if (outlevel == 1)
+ {
+ if (!uswitch) writeg6(outfile,g,1,n);
+ return;
+ }
+
+ /* Make a SMILES skeleton structure for later use */
+ if (smiles) makesmilesskeleton(g,n);
+
+ colourvertices(g,n);
+}
+
+/****************************************************************/
+
+static void
+decode_formula(char *formula, int *nv,
+ int *mine, int *maxe, int *maxd, int *maxc)
+/* Parse the input formula. The number of hydrogens goes to hydrogens.
+ The other distinct elements go to elementlist[0..numtypes-1] and
+ elementcount[0..numtypes-1].
+ *mine and *maxe have an edge range from -e and are updated.
+ *mind and *maxc come from -d and -c and are updated.
+ *nv gets the number of non-H atoms.
+*/
+{
+ int i,j,d,mult,val,cnt,totval,dbe,forced;
+ int maxvcoord,localmine,localmaxe,xi,yi;
+ char *s1,*s2,*p;
+ int count[FORMULALEN];
+
+ if (numelements > FORMULALEN)
+ gt_abort(">E surge : increase FORMULALEN\n");
+
+ /* First we fill in count[*], which is parallel to element[*] */
+
+ for (i = 0; i < numelements; ++i) count[i] = 0;
+
+ for (s1 = formula; *s1 != '\0'; s1 = s2)
+ {
+ if (!isupper(*s1)) gt_abort(">E surge : unknown element name\n");
+ for (s2 = s1+1; islower(*s2); ++s2) {}
+ for (i = 0; i < numelements; ++i)
+ {
+ for (j = 0; element[i].inputname[j] != '\0'
+ && s1+j != s2 && element[i].inputname[j] == s1[j]; ++j) {}
+ if (element[i].inputname[j] == '\0' && s1+j == s2) break;
+ }
+ if (i == numelements) gt_abort(">E surge : unknown element name\n");
+ s1 = s2;
+ if (!isdigit(*s2))
+ ++count[i];
+ else
+ {
+ mult = *s2 - '0';
+ for (s2 = s1+1; isdigit(*s2); ++s2) mult = 10*mult+(*s2-'0');
+ count[i] += mult;
+ }
+ }
+
+ /* Next we collect elements actually used into elementtype[0..numtypes-1]
+ and elementcount[0..numtypes-1], except for H which we just count. */
+
+ numtypes = hydrogens = 0;
+ for (i = 0; i < numelements; ++i)
+ {
+ cnt = count[i];
+ if (cnt > 0)
+ {
+ if (ISHYDROGEN(i))
+ hydrogens = cnt;
+ else
+ {
+ elementtype[numtypes] = i;
+ elementcount[numtypes] = cnt;
+ ++numtypes;
+ }
+ }
+ }
+
+ /* Next we adjust *maxd and *maxc, as well as the maxcoord
+ fields of elements */
+
+ maxvcoord = 0;
+ for (i = 0; i < numtypes; ++i)
+ if (element[elementtype[i]].maxcoord > maxvcoord)
+ maxvcoord = element[elementtype[i]].maxcoord;
+ if (maxvcoord < *maxc)
+ *maxc = maxvcoord;
+ else if (maxvcoord > *maxc)
+ for (i = 0; i < numtypes; ++i)
+ if (element[elementtype[i]].maxcoord > *maxc)
+ element[elementtype[i]].maxcoord = *maxc;
+ if (*maxd > *maxc) *maxd = *maxc;
+
+ /* Next we find some bounds on the number of vertices
+ with various simple degrees. */
+
+ min1 = min12 = max34 = max4 = 0;
+ for (i = 0; i < numelements; ++i)
+ {
+ if (!ISHYDROGEN(i))
+ {
+ cnt = count[i];
+ val = element[i].maxcoord;
+ if (val <= 1) min1 += cnt; // Check logic
+ if (val <= 2) min12 += cnt;
+ if (val >= 3) max34 += cnt;
+ if (val >= 4) max4 += cnt;
+ // Could add max5, could use both bounds everywhere
+ }
+ }
+
+ /* Now sort by decreasing maximum coordination number */
+
+ for (i = 1; i < numtypes; ++i) /* really 1 */
+ {
+ xi = elementtype[i];
+ yi = elementcount[i];
+ for (j = i; element[elementtype[j-1]].maxcoord < element[xi].maxcoord; )
+ {
+ elementtype[j] = elementtype[j-1];
+ elementcount[j] = elementcount[j-1];
+ if (--j < 1) break;
+ }
+ elementtype[j] = xi;
+ elementcount[j] = yi;
+ }
+
+ /* Make "canonical" molecule name (used for -A output) */
+
+ p = canonform;
+ for (i = 0; i < numtypes; ++i)
+ {
+ PUTSTR(element[elementtype[i]].inputname);
+ if (elementcount[i] > 1) PUTINT(elementcount[i]);
+ }
+ if (hydrogens > 0) PUTSTR("H");
+ if (hydrogens > 1) PUTINT(hydrogens);
+ *p = '\0';
+
+ /* Calculate *nv, totalval, forced which all exclude H */
+
+ *nv = forced = 0;
+ totval = hydrogens;
+ for (i = 0; i < numtypes; ++i)
+ {
+ j = elementtype[i];
+ cnt = elementcount[i];
+ *nv += cnt;
+ totval += cnt * element[j].valence;
+ if (element[j].valence > element[j].maxcoord)
+ forced += element[j].valence - element[j].maxcoord;
+ }
+ forced = (forced+1) / 2;
+
+ if ((totval & 1)) gt_abort(">E surge : impossible parity\n");
+ dbe = totval / 2 - (*nv + hydrogens - 1) - forced;
+ if (dbe < 0) gt_abort(">E surge : negative DBE\n");
+ if (*nv > MAXN) gt_abort(">E surge : too many non-hydrogen atoms\n");
+ if (*nv == 0) gt_abort(">E surge : only hydrogen\n");
+ valencesum = totval - hydrogens;
+
+ localmine = *nv - 1;
+ localmaxe = localmine + dbe;
+ if (localmaxe > *nv * (*nv-1) / 2) localmaxe = *nv * (*nv-1) / 2;
+
+ if (localmine > *mine) *mine = localmine;
+ if (localmaxe < *maxe) *maxe = localmaxe;
+ if (*mine > *maxe) gt_abort(">E surge : edge range is empty\n");
+
+ fprintf(stderr,"%s ",canonform);
+ fprintf(stderr,"H=%d",hydrogens);
+ for (i = 0; i < numtypes; ++i)
+ fprintf(stderr," %s=%d",element[elementtype[i]].inputname,elementcount[i]);
+
+ fprintf(stderr," nv=%d edges=%d-%d DBE=%d maxd=%d maxc=%d\n",
+ *nv,*mine,*maxe,dbe,*maxd,*maxc);
+ if (*maxe > MAXNE) gt_abort(">E surge : too many edges\n");
+
+ for (d = 1; d <= *maxd; ++d)
+ {
+ for (i = 0; i < numtypes; ++i)
+ {
+ val = element[elementtype[i]].maxcoord;
+ if (d <= val) maxtype[d] = i;
+ }
+ }
+}
+
+/****************************************************************/
+
+static void
+start_geng(int n, int maxd, int maxc,
+ int mine, int maxe, char *extra1, long res, long mod)
+/* start geng with arguments, extra1 before n, extra2 after n */
+{
+ int i,geng_argc,mind;
+ char *geng_argv[20];
+ char arga[30],argb[30];
+ char resmod[40];
+ char gengargs[80];
+ char edgecount[40];
+
+ mind = 1;
+ if (hydrogens == 0)
+ {
+ for (i = 0; i < numtypes; ++i)
+ if (element[elementtype[i]].valence < 4) break;
+ if (i == numtypes) mind = 2;
+ }
+
+ if (n == 1) mind = 0;
+
+ sprintf(arga,"-qcd%dD%d",mind,maxd);
+ sprintf(argb,"%d",n);
+ sprintf(edgecount,"%d:%d",mine,maxe);
+
+ geng_argv[0] = "geng_surge";
+ geng_argv[1] = arga;
+ geng_argc = 2;
+
+ if (tswitch && max3cycles == 0)
+ {
+ geng_argv[geng_argc++] = "-t";
+ tswitch = FALSE;
+ }
+
+ if (fswitch && max4cycles == 0)
+ {
+ geng_argv[geng_argc++] = "-f";
+ fswitch = FALSE;
+ }
+
+ if (bipartite) geng_argv[geng_argc++] = "-b";
+
+ if (extra1)
+ {
+ snprintf(gengargs,78,"-%s",extra1);
+ geng_argv[geng_argc++] = gengargs;
+ }
+ geng_argv[geng_argc++] = argb;
+ geng_argv[geng_argc++] = edgecount;
+ if (mod > 1)
+ {
+ sprintf(resmod,"%ld/%ld",res,mod);
+ geng_argv[geng_argc++] = resmod;
+ }
+ geng_argv[geng_argc] = NULL;
+
+ if (verbose)
+ {
+ fprintf(stderr,">geng");
+ for (i = 1; geng_argv[i] != NULL; ++i)
+ fprintf(stderr," %s",geng_argv[i]);
+ fprintf(stderr,"\n");
+ }
+
+ geng_main(geng_argc,geng_argv);
+}
+
+/*******************************************************************/
+
+static void
+processEswitch(char **ps, char *id)
+/* Process -E starting at *ps = the character after E, and update *ps.
+ The value has the form [][],
+ where and are either or and
+ and are single digits. Inputname comes first. */
+{
+ char inputname[3],name[3];
+ int valence,maxcoord;
+ char *s;
+ int state;
+
+ s = *ps;
+ state = 0;
+
+ while (state < 5)
+ {
+ switch (state)
+ {
+ case 0:
+ if (!isupper(*s))
+ {
+ state = 6;
+ break;
+ }
+ inputname[0] = *s++;
+ if (islower(*s))
+ {
+ inputname[1] = *s++;
+ inputname[2] = '\0';
+ }
+ else
+ inputname[1] = '\0';
+ state = 1;
+ break;
+ case 1:
+ if (isupper(*s))
+ state = 2;
+ else
+ {
+ name[0] = inputname[0];
+ name[1] = inputname[1];
+ name[2] = inputname[2];
+ state = 3;
+ }
+ break;
+ case 2:
+ name[0] = *s++;
+ if (islower(*s))
+ {
+ name[1] = *s++;
+ name[2] = '\0';
+ }
+ else
+ name[1] = '\0';
+ state = 3;
+ break;
+ case 3:
+ if (!isdigit(*s))
+ {
+ state = 7;
+ break;
+ }
+ valence = *s++ - '0';
+ if (!isdigit(*s))
+ maxcoord = valence;
+ else
+ maxcoord = *s++ - '0';
+ state = 5;
+ break;
+ }
+ }
+
+ if (state == 6)
+ {
+ fprintf(stderr,">E %s : bad element name\n",id);
+ exit(1);
+ }
+ if (state == 7)
+ {
+ fprintf(stderr,">E %s : bad valence or maxcoord\n",id);
+ exit(1);
+ }
+
+ *ps = s;
+
+ addelement(inputname,name,valence,maxcoord);
+}
+
+#define SWELEMENT(c,id) if (sw==c) {processEswitch(&arg,id);}
+
+/*******************************************************************/
+
+int
+main(int argc, char *argv[])
+{
+ int argnum,i,j;
+ boolean badargs,Gswitch,mswitch,Oswitch,eswitch,notriples;
+ boolean oswitch,Bswitch,cswitch,Dswitch;
+ char *extra1,*extra2,*formula,*arg,sw,*outfilename;
+ long res,mod;
+ int mine,maxe,maxd,maxc;
+ long eminval,emaxval;
+ double t1,t2;
+ long badlist[BADLISTS];
+ int badlen,outf;
+
+ HELP;
+
+ nauty_check(WORDSIZE,1,1,NAUTYVERSIONID);
+
+ maxindex = 0;
+ for (i = 0; i < MAXELEMENTS; ++i)
+ if (element[i].inputname == NULL) break;
+ else if (element[i].index < maxindex && !ISHYDROGEN(i))
+ maxindex = element[i].index;
+ numelements = i;
+
+#ifdef SURGEPLUGIN_INIT
+ SURGEPLUGIN_INIT;
+#endif
+
+ argnum = 0;
+ badargs = verbose = Gswitch = mswitch = FALSE;
+ uswitch = eswitch = notriples = smiles = FALSE;
+ oswitch = gzip = alphabetic = Bswitch = FALSE;
+ tswitch = fswitch = pswitch = bipartite = FALSE;
+ cswitch = planar = xswitch = Dswitch = FALSE;
+ Oswitch = Cswitch = FALSE; outlevel = 4;
+ extra1 = extra2 = formula = NULL;
+ bad1 = bad2 = bad3 = bad4 = bad5 = bad6 = bad7 = bad8 = bad9 = FALSE;
+
+ for (j = 1; !badargs && j < argc; ++j)
+ {
+ arg = argv[j];
+ if (arg[0] == '-' && arg[1] != '\0')
+ {
+ ++arg;
+ while (*arg != '\0')
+ {
+ sw = *arg++;
+ if (sw == 'G')
+ {
+ if (Gswitch)
+ gt_abort(">E surge: -G is only allowed once\n");
+ Gswitch = TRUE;
+ extra1 = arg;
+ break;
+ }
+ else if (sw == 'o')
+ {
+ if (oswitch)
+ gt_abort(">E surge : -o is only allowed once\n");
+ oswitch = TRUE;
+ outfilename = arg;
+ break;
+ }
+ else SWRANGE('m',"/",mswitch,res,mod,"surge -m")
+ else SWINT('O',Oswitch,outlevel,"surge -O")
+ else SWINT('c',cswitch,maxc,"surge -c")
+ else SWINT('d',Dswitch,maxd,"surge -d")
+ else SWBOOLEAN('u',uswitch)
+ else SWBOOLEAN('v',verbose)
+ else SWBOOLEAN('T',notriples)
+ else SWBOOLEAN('S',smiles)
+ else SWBOOLEAN('F',SDFoutput)
+ else SWBOOLEAN('z',gzip)
+ else SWBOOLEAN('A',alphabetic)
+ else SWBOOLEAN('b',bipartite)
+ else SWBOOLEAN('P',planar)
+ else SWBOOLEAN('R',Rswitch)
+ else SWBOOLEAN('x',xswitch)
+ else SWSEQUENCEMIN('B',",",Bswitch,badlist,1,BADLISTS,badlen,"surge -B")
+ else SWRANGE('e',":-",eswitch,eminval,emaxval,"surge -e")
+ else SWRANGE('t',":-",tswitch,min3cycles,max3cycles,"surge -t")
+ else SWRANGE('f',":-",fswitch,min4cycles,max4cycles,"surge -f")
+ else SWRANGE('p',":-",pswitch,min5cycles,max5cycles,"surge -p")
+ else SWRANGE('h',":-",hswitch,min6cycles,max6cycles,"surge -h")
+ else SWRANGE('C',":-",Cswitch,minCrings,maxCrings,"surge -C")
+ else SWELEMENT('E',"surge -E")
+#ifdef SURGEPLUGIN_SWITCHES
+ else SURGEPLUGIN_SWITCHES
+#endif
+ else badargs = TRUE;
+
+ if (Bswitch)
+ {
+ for (i = 0; i < badlen; ++i)
+ {
+ if (badlist[i] < 1 || badlist[i] > BADLISTS)
+ gt_abort(">E surge : invalid bad list number\n");
+ if (badlist[i] == 1) bad1 = TRUE;
+ else if (badlist[i] == 2) bad2 = TRUE;
+ else if (badlist[i] == 3) bad3 = TRUE;
+ else if (badlist[i] == 4) bad4 = TRUE;
+ else if (badlist[i] == 5) bad5 = TRUE;
+ else if (badlist[i] == 6) bad6 = TRUE;
+ else if (badlist[i] == 7) bad7 = TRUE;
+ else if (badlist[i] == 8) bad8 = TRUE;
+ else if (badlist[i] == 9) bad9 = TRUE;
+ /* Don't forget initialization if you add more */
+ }
+ Bswitch = FALSE;
+ }
+ }
+ }
+ else
+ {
+ ++argnum;
+ if (argnum == 1) formula = arg;
+ else badargs = TRUE;
+ }
+ }
+
+ if (badargs)
+ {
+ fprintf(stderr,">E Usage: %s\n",USAGE EXTRAUSAGE);
+ GETHELP;
+ exit(1);
+ }
+
+ if (Oswitch && (outlevel <= 0 || outlevel >= 5))
+ gt_abort(">E surge : unknown value for -O\n");
+
+ if (!cswitch) maxc = 4;
+ if (!Dswitch) maxd = 4;
+
+#ifndef ZLIB
+ if (gzip)
+ gt_abort(">E surge : -z is only allowed if zlib is compiled in\n");
+#endif
+
+ outf = (alphabetic==TRUE) + (smiles==TRUE)
+ + (SDFoutput==TRUE) + (uswitch==TRUE);
+
+ if (outf > 1)
+ gt_abort(">E surge : -A,-S,-F,-u are incompatible\n");
+ if (outf == 0 && !Oswitch) uswitch = TRUE;
+
+ if (uswitch) gzip = FALSE;
+
+ if (!oswitch || (oswitch && strcmp(outfilename,"-") == 0))
+ outfilename = "stdout";
+
+ if (bad5) bad6 = FALSE; /* bad6 is a subset of bad5 */
+ if (notriples) bad1 = FALSE;
+ if (tswitch && fswitch && max3cycles+max4cycles <= 1)
+ bad9 = FALSE;
+
+ needrings = (bad1 || bad2 || bad3 || bad4 || bad6 || Cswitch);
+
+ if (fswitch && max4cycles < 6) bad7 = FALSE;
+
+ if (tswitch && max3cycles < 3) bad8 = FALSE;
+ if (fswitch && max4cycles < 2) bad8 = FALSE;
+ if (pswitch && max5cycles == 0) bad8 = FALSE;
+ if (hswitch && max6cycles < 3) bad4 = FALSE;
+
+ if (gzip)
+ {
+#ifdef ZLIB
+ if (strcmp(outfilename,"stdout") == 0)
+ gzoutfile = gzdopen(fileno(stdout),"wb");
+ else
+ gzoutfile = gzopen(outfilename,"wb");
+ if (!gzoutfile)
+ gt_abort(">E surge : unable to open compressed stream\n");
+ gzbuffer(gzoutfile,1<<16); /* Remove this line if gzbuffer()
+ is not found; it means you have a old version of zlib. */
+#endif
+ }
+ else
+ {
+ if (strcmp(outfilename,"stdout") == 0)
+ outfile = stdout;
+ else
+ outfile = fopen(outfilename,"w");
+ if (!outfile)
+ gt_abort(">E surge : can't open output file\n");
+ }
+
+ maxbond = (notriples ? 1 : 2);
+
+ if (!Oswitch) outlevel = 4;
+
+ if (mswitch)
+ {
+ if (res < 0 || res >= mod)
+ gt_abort(">E surge : -mres/mod needs 0 <= res < mod\n");
+ }
+ else
+ {
+ res = 0;
+ mod = 1;
+ }
+
+ if (badargs || argnum != 1)
+ {
+ fprintf(stderr,">E Usage: %s\n",USAGE);
+ GETHELP;
+ exit(1);
+ }
+
+ if (eswitch)
+ {
+ mine = (int)eminval;
+ maxe = (int)emaxval;
+ }
+ else
+ {
+ mine = 0;
+ maxe = NOLIMIT;
+ }
+
+ carbonindex = elementindex("C");
+ decode_formula(formula,&nv,&mine,&maxe,&maxd,&maxc);
+
+ t1 = CPUTIME;
+ start_geng(nv,maxd,maxc,mine,maxe,extra1,res,mod);
+#ifdef ZLIB
+ if (gzip)
+ if (gzclose(gzoutfile) != Z_OK)
+ gt_abort(">E surge : error on closing compressed stream\n");
+#endif
+ t2 = CPUTIME;
+
+ if (vgroup) free(vgroup); /* Make valgrind happy */
+ if (egroup) free(egroup);
+
+ if (verbose)
+ {
+ fprintf(stderr,">G geng made %lld graphs, %lld accepted\n",
+ gengout,genggood);
+ if (outlevel > 1)
+ fprintf(stderr,">V vcolg %lld nontrivial groups, max size"
+ " %ld, made %lld graphs\n",vcolgnontriv,maxvgroup,vcolgout);
+ if (outlevel > 2)
+ fprintf(stderr,">M multig %lld nontrivial groups, max size"
+ " %ld, made %lld graphs\n",multignontriv,maxegroup,multigout);
+ }
+
+ if (needrings) fprintf(stderr,"Max rings = %d\n",maxrings);
+ if (Rswitch) fprintf(stderr,"Max 2 mod 4 cycles = %d\n",maxcycles);
+
+#ifdef SURGEPLUGIN_SUMMARY
+ SURGEPLUGIN_SUMMARY
+#endif
+
+ if (Rswitch)
+ fprintf(stderr,">Z %s %llu -> %llu -> %llu -> %llu in %.2f sec\n",
+ (uswitch ? "generated" : "wrote"),
+ gengout,vcolgout,molnum,multigout,t2-t1);
+ else
+ fprintf(stderr,">Z %s %llu -> %llu -> %llu in %.2f sec\n",
+ (uswitch ? "generated" : "wrote"),
+ gengout,vcolgout,multigout,t2-t1);
+
+ return 0;
+}