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; +}