\documentclass[12pt, a4paper, footexclude, headexclude]{scrartcl}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Packages
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\usepackage[margin=0.5in]{geometry}

\usepackage[pdftex]{hyperref}
\usepackage{multicol}

\usepackage{xinttools}
\usepackage[nomessages]{fp}
\usepackage{tabularx}
\usepackage{tcolorbox}

\setlength{\parindent}{0pt}

\usepackage[pdftex]{insdljs}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Parameter
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\def\fieldWidth{9}
\def\fieldHeight{9}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Javascript
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\begin{insDLJS}[]{JSDateiname}{Solo Noble}

const GAME_DESC = "This game consists of a field of cells. On each of these but one a marble\
is placed. The goal is to reduce the marble count to one while placing this last marble\
on the one cell which is empty at the beginning of the game.\
A marble can be moved only by jumping over exactly one other marble onto an empty cell.\
The jumped over marble is removed and it is not allowed to jump diagonal.";

% Outside variables
const FIELD_WIDTH = AFMakeNumber(\fieldWidth);
const FIELD_HEIGHT = AFMakeNumber(\fieldHeight);
const SCREEN_TEXT_WIDTH = 300;

% Constants
const ANIMATION_SPEED = 33;
const ANIMATION_OFFSET = 0.05;
% Index of the animation cell x and y
const ANIM_POS = FIELD_WIDTH - 1;
% Index of the cell that counts as a win
const W_POS_X = [4, 4, 4, 4, 4];
const W_POS_Y = [4, 2, 4, 4, 4];
% For empty cells
const NONE = -1;
const EMPTY = 0;
const FULL = 1;

% Globals
% Animation interval variable for jumping
var run;
var timestep = 0.0;
% The game field
var complField = new Array();
% Saves the last clicked cell
var currentCell = null;
% The amount of taken cells
var currentMarbleCount = 0;
% The index determining which field should be played
var fieldIdx = 0;
var colorEnabled = true;
% Animation variable for solution
var runSol;
% Saving current solving step
var exeIndex = 0;
% The array of moves to execute/apply to the field
var exeMoves = [];

%%%%%%%%%% Field arrays %%%%%%%%%%
const FIELDS = [
    [ % English
        [NONE, NONE,  NONE,  NONE,  NONE,  NONE,  NONE,  NONE, NONE],
        [NONE, NONE,  NONE,  FULL,  FULL,  FULL,  NONE,  NONE, NONE],
        [NONE, NONE,  NONE,  FULL,  FULL,  FULL,  NONE,  NONE, NONE],
        [NONE, FULL,  FULL,  FULL,  FULL,  FULL,  FULL,  FULL, NONE],
        [NONE, FULL,  FULL,  FULL, EMPTY,  FULL,  FULL,  FULL, NONE],
        [NONE, FULL,  FULL,  FULL,  FULL,  FULL,  FULL,  FULL, NONE],
        [NONE, NONE,  NONE,  FULL,  FULL,  FULL,  NONE,  NONE, NONE],
        [NONE, NONE,  NONE,  FULL,  FULL,  FULL,  NONE,  NONE, NONE],
        [NONE, NONE,  NONE,  NONE,  NONE,  NONE,  NONE,  NONE, NONE]
    ],
    [ % European
        [NONE, NONE,  NONE,  NONE,  NONE,  NONE,  NONE,  NONE, NONE],
        [NONE, NONE,  NONE,  FULL,  FULL,  FULL,  NONE,  NONE, NONE],
        [NONE, NONE,  FULL,  FULL,  FULL,  FULL,  FULL,  NONE, NONE],
        [NONE, FULL,  FULL,  FULL, EMPTY,  FULL,  FULL,  FULL, NONE],
        [NONE, FULL,  FULL,  FULL,  FULL,  FULL,  FULL,  FULL, NONE],
        [NONE, FULL,  FULL,  FULL,  FULL,  FULL,  FULL,  FULL, NONE],
        [NONE, NONE,  FULL,  FULL,  FULL,  FULL,  FULL,  NONE, NONE],
        [NONE, NONE,  NONE,  FULL,  FULL,  FULL,  NONE,  NONE, NONE],
        [NONE, NONE,  NONE,  NONE,  NONE,  NONE,  NONE,  NONE, NONE]
    ],
    [ % German
        [NONE, NONE,  NONE,  FULL,  FULL,  FULL,  NONE,  NONE, NONE],
        [NONE, NONE,  NONE,  FULL,  FULL,  FULL,  NONE,  NONE, NONE],
        [NONE, NONE,  NONE,  FULL,  FULL,  FULL,  NONE,  NONE, NONE],
        [FULL, FULL,  FULL,  FULL,  FULL,  FULL,  FULL,  FULL, FULL],
        [FULL, FULL,  FULL,  FULL, EMPTY,  FULL,  FULL,  FULL, FULL],
        [FULL, FULL,  FULL,  FULL,  FULL,  FULL,  FULL,  FULL, FULL],
        [NONE, NONE,  NONE,  FULL,  FULL,  FULL,  NONE,  NONE, NONE],
        [NONE, NONE,  NONE,  FULL,  FULL,  FULL,  NONE,  NONE, NONE],
        [NONE, NONE,  NONE,  FULL,  FULL,  FULL,  NONE,  NONE, NONE]
    ],
    [ % Asymmetrical
        [NONE, NONE,  NONE,  FULL,  FULL,  FULL,  NONE,  NONE, NONE],
        [NONE, NONE,  NONE,  FULL,  FULL,  FULL,  NONE,  NONE, NONE],
        [NONE, NONE,  NONE,  FULL,  FULL,  FULL,  NONE,  NONE, NONE],
        [NONE, FULL,  FULL,  FULL,  FULL,  FULL,  FULL,  FULL, FULL],
        [NONE, FULL,  FULL,  FULL, EMPTY,  FULL,  FULL,  FULL, FULL],
        [NONE, FULL,  FULL,  FULL,  FULL,  FULL,  FULL,  FULL, FULL],
        [NONE, NONE,  NONE,  FULL,  FULL,  FULL,  NONE,  NONE, NONE],
        [NONE, NONE,  NONE,  FULL,  FULL,  FULL,  NONE,  NONE, NONE],
        [NONE, NONE,  NONE,  NONE,  NONE,  NONE,  NONE,  NONE, NONE]
    ],
    [ % Diamond
        [NONE, NONE,  NONE,  NONE,  FULL,  NONE,  NONE,  NONE, NONE],
        [NONE, NONE,  NONE,  FULL,  FULL,  FULL,  NONE,  NONE, NONE],
        [NONE, NONE,  FULL,  FULL,  FULL,  FULL,  FULL,  NONE, NONE],
        [NONE, FULL,  FULL,  FULL,  FULL,  FULL,  FULL,  FULL, NONE],
        [NONE, FULL,  FULL,  FULL, EMPTY,  FULL,  FULL,  FULL, NONE],
        [NONE, FULL,  FULL,  FULL,  FULL,  FULL,  FULL,  FULL, NONE],
        [NONE, NONE,  FULL,  FULL,  FULL,  FULL,  FULL,  NONE, NONE],
        [NONE, NONE,  NONE,  FULL,  FULL,  FULL,  NONE,  NONE, NONE],
        [NONE, NONE,  NONE,  NONE,  FULL,  NONE,  NONE,  NONE, NONE]
    ]
]

%%%%%%%%%% Solutions %%%%%%%%%%
var solMoves = [
    [ % English
        [6, 4, 4, 4], [5, 6, 5, 4], [7, 5, 5, 5], [7, 3, 7, 5], [5, 4, 5, 6],
        [5, 7, 5, 5], [5, 3, 7, 3], [5, 1, 5, 3], [4, 5, 6, 5], [7, 5, 5, 5],
        [4, 7, 4, 5], [4, 5, 6, 5], [4, 3, 4, 5], [4, 1, 4, 3], [4, 3, 6, 3],
        [7, 3, 5, 3], [3, 5, 5, 5], [6, 5, 4, 5], [3, 7, 3, 5], [3, 4, 3, 6],
        [2, 3, 4, 3], [3, 1, 3, 3], [1, 5, 3, 5], [4, 5, 2, 5], [1, 3, 1, 5],
        [1, 5, 3, 5], [3, 6, 3, 4], [3, 4, 3, 2], [5, 3, 3, 3], [3, 2, 3, 4],
        [2, 4, 4, 4]
    ],
    [ % European
        [2, 3, 4, 3], [3, 1, 3, 3], [2, 5, 2, 3], [4, 5, 2, 5], [4, 3, 4, 5],
        [5, 2, 3, 2], [2, 3, 4, 3], [5, 1, 3, 1], [5, 5, 3, 5], [5, 4, 5, 2],
        [4, 7, 4, 5], [2, 2, 4, 2], [7, 5, 5, 5], [2, 6, 2, 4], [7, 3, 5, 3],
        [5, 2, 3, 2], [3, 1, 3, 3], [7, 4, 5, 4], [4, 5, 2, 5], [3, 7, 3, 5],
        [4, 3, 2, 3], [2, 5, 4, 5], [6, 6, 4, 6], [1, 3, 3, 3], [3, 3, 3, 5],
        [5, 4, 5, 6], [4, 5, 2, 5], [1, 4, 3, 4], [5, 7, 5, 5], [1, 5, 3, 5],
        [3, 4, 3, 6], [3, 6, 5, 6], [5, 6, 5, 4], [5, 4, 5, 2], [6, 2, 4, 2]
    ],
    [ % German
        [4, 2, 4, 4], [2, 3, 4, 3], [3, 5, 3, 3], [3, 2, 3, 4], [3, 0, 3, 2],
        [1, 5, 3, 5], [3, 5, 3, 3], [3, 3, 3, 1], [0, 3, 2, 3], [2, 3, 2, 5],
        [5, 5, 3, 5], [3, 5, 1, 5], [3, 7, 3, 5], [5, 6, 3, 6], [5, 8, 5, 6],
        [7, 5, 5, 5], [5, 5, 5, 7], [3, 8, 5, 8], [5, 8, 5, 6], [6, 3, 6, 5],
        [8, 3, 6, 3], [5, 3, 7, 3], [8, 5, 8, 3], [8, 3, 6, 3], [0, 5, 2, 5],
        [2, 5, 4, 5], [5, 0, 3, 0], [3, 0, 3, 2], [5, 1, 3, 1], [3, 1, 3, 3],
        [3, 3, 5, 3], [5, 3, 7, 3], [7, 3, 7, 5], [7, 5, 5, 5], [5, 5, 3, 5],
        [3, 5, 3, 7], [3, 7, 5, 7], [5, 7, 5, 5], [5, 5, 5, 3], [5, 2, 5, 4],
        [5, 4, 3, 4], [0, 4, 2, 4], [2, 4, 4, 4]
    ],
    [ % Asymmetrical
        [4, 2, 4, 4], [4, 0, 4, 2], [6, 3, 4, 3], [8, 3, 6, 3], [8, 5, 8, 3],
        [7, 5, 7, 3], [5, 4, 7, 4], [5, 5, 7, 5], [7, 3, 5, 3], [7, 5, 7, 3],
        [8, 3, 6, 3], [5, 2, 5, 4], [3, 3, 5, 3], [6, 3, 4, 3], [4, 4, 6, 4],
        [2, 4, 4, 4], [1, 3, 3, 3], [3, 2, 3, 4], [3, 0, 3, 2], [4, 4, 2, 4],
        [1, 4, 3, 4], [3, 5, 3, 3], [5, 0, 5, 2], [3, 7, 3, 5], [3, 5, 5, 5],
        [1, 5, 3, 5], [5, 7, 3, 7], [3, 2, 3, 4], [3, 4, 3, 6], [3, 7, 3, 5],
        [5, 6, 3, 6], [3, 6, 3, 4], [4, 2, 4, 4], [3, 4, 5, 4], [5, 5, 5, 3],
        [5, 2, 5, 4], [6, 4, 4, 4]
    ],
    [ % Diamond http://recmath.org/pegsolitaire/gif/Hermarys23Move.gif
        [4, 2, 4, 4], [4, 5, 4, 3], [4, 7, 4, 5], [6, 2, 4, 2], [6, 6, 4, 6],
        [5, 4, 5, 2], [4, 5, 4, 7], [5, 1, 5, 3], [2, 6, 4, 6], [3, 4, 3, 6],
        [3, 7, 3, 5], [3, 2, 3, 4], [3, 4, 3, 6], [3, 6, 5, 6], [5, 6, 5, 4],
        [5, 4, 5, 2], [5, 2, 3, 2], [1, 5, 3, 5], [7, 5, 5, 5], [7, 3, 5, 3],
        [3, 1, 3, 3], [4, 0, 4, 2], [1, 4, 3, 4], [3, 4, 3, 2], [3, 2, 5, 2],
        [5, 2, 5, 4], [5, 4, 5, 6], [1, 3, 3, 3], [5, 7, 5, 5], [4, 8, 4, 6],
        [7, 4, 5, 4], [5, 4, 5, 6], [5, 6, 3, 6], [3, 6, 3, 4], [3, 4, 3, 2],
        [2, 2, 4, 2], [4, 2, 4, 4]
    ]
];

% Symbols
const MARBLE_SYMBOL = String.fromCharCode(0x25CF);
const EMPTY_SYMBOL = String.fromCharCode(0x25CB);

% How many marbles need to be placed
const MARBLE_COUNTS = [
    32, 36, 44, 38, 40
]

% Color data
var colors = new Array();

const HIGHLIGHT_COLOR = [ "RGB", 0.984, 0.800, 0.176 ];

%%%%%%%%%%
% Helper %
%%%%%%%%%%
/**
 * Returns a random number between 0 and the param.
 * @param {Number} max The upperbound
 */
function getRandomInt(max) {
    return Math.floor(Math.random() * max);
}

/**
 * Converts an HSL color value to RGB. Conversion formula
 * adapted from http://en.wikipedia.org/wiki/HSL_color_space.
 * Assumes h, s, and l are contained in the set [0, 1] and
 * returns r, g, and b in the set [0, 1].
 * https://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion
 *
 * @param {number} h The hue
 * @param {number} s The saturation
 * @param {number} l The lightness
 * @return {Array} The RGB representation
 */
function hslToRgb(h, s, l) {
    var r, g, b;

    % achromatic
    if (s == 0) {
        r = g = b = l;
    } else {
        var hue2rgb = function hue2rgb(p, q, t) {
            if (t < 0) t += 1.0;
            if (t > 1) t -= 1.0;
            if (t < 1.0/6.0) return p + (q - p) * 6.0 * t;
            if (t < 1.0/2.0) return q;
            if (t < 2.0/3.0) return p + (q - p) * (2.0/3.0 - t) * 6.0;
            return p;
        }

        var q = l < 0.5 ? l * (1.0 + s) : l + s - l * s;
        var p = 2.0 * l - q;
        r = hue2rgb(p, q, h + 1.0/3.0);
        g = hue2rgb(p, q, h);
        b = hue2rgb(p, q, h - 1.0/3.0);
    }

    return [ "RGB", r, g, b ];
}

/**
 * Generates the specified amount of colors and pushes
 * them to the colors array.
 * For generating the colors are selected using hsl
 * and afterwards transformed to rgb.
 *
 * @param {number} amount The amount of colors to generate
 * @param {number} saturation The saturation
 * @param {number} lightness The lightness
 */
function generateColors(saturation, lightness, amount) {
    var huedelta = 1.0 / amount;

    for (var j = 0; j < amount; j++) {
        var hue = j * huedelta;
        colors.push(hslToRgb(hue, saturation, lightness));
    }
}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Initialisation and Restart %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
/**
 * Initialise the default game state.
 */
function initialise() {
    % Init the GUI
    initialiseButtons();
    % Get the value of the choise menu
    fieldIdx = this.getField("cmLayout").currentValueIndices;
    currentMarbleCount = MARBLE_COUNTS[fieldIdx];

    % Create the field using the FIELDS array data
    var idx = 0;
    for (var row = 0; row < FIELD_HEIGHT; row++) {
        var tmp = new Array();
        for (var column = 0; column < FIELD_WIDTH; column++) {
            var colorIdx = -1;
            var cell = FIELDS[fieldIdx][row][column];
            if (cell == FULL) {
                colorIdx = idx;
                idx++;
            }
            tmp.push([cell, colorIdx]);
        }
        complField.push(tmp);
    }

    % Hide all cells that are not part of the field
    hideCells();
    % Generate a color for each marble
    generateColors(1.0, 0.5, currentMarbleCount);

    drawAll();
}

/**
 * Initialise all buttons from the GUI.
 * For example add callbacks and set the correct colors.
 */
function initialiseButtons() {
    var field;
    % Disables the rectangle around a button after the click
    app.focusRect = false;

    this.getField("cbColor").checkThisBox(0);

    % Assign each button the coresponding function and set the highlights
    this.getField("restart").setAction("MouseUp", "restart();");
    this.getField("restart").highlight="outline";

    this.getField("showSol").setAction("MouseUp", "showSolution();");
    this.getField("showSol").highlight="outline";

    this.getField("import").setAction("MouseUp", "importField();");
    this.getField("export").setAction("MouseUp", "exportField();");

    this.getField("exmoves").setAction("MouseUp", "executeMoves();");

    this.getField("cmLayout").currentValueIndices = 0;

    % Each field needs to be connected to an input function
    var funcName = "fieldClicked";
    for (var x = 0; x < FIELD_WIDTH; x++) {
        for (var y = 0; y < FIELD_HEIGHT; y++) {
            var callbackName = funcName + "(" + x.toString() + "," + y.toString() + ");";
            field = this.getField("pos" + x + "-" + y);
            field.setAction("MouseDown", callbackName);
            field.textSize = SCREEN_TEXT_WIDTH / FIELD_WIDTH;
            field.alignment = "center";
        }
    }

    % Info field setup
    this.getField("info").textFont = font.Times;

    % Tooltip setup
    field = this.getField("tooltipbtn");
    field.setAction("MouseDown", "tooltip(true);");
    field.setAction("MouseUp", "tooltip(false);");
    field.highlight="none";

    printInfoText("Init");
}

/**
 * Restart the game and clear everything
 */
function restart() {
    printInfoText("Restart");

    % Stop showing solution if in process
    if (exeIndex != 0) {
        app.clearInterval(runSol);
        exeIndex = 0;
    }

    currentCell = null;

    fieldIdx = this.getField("cmLayout").currentValueIndices;
    currentMarbleCount = MARBLE_COUNTS[fieldIdx];

    % Create the field choosing a random symbol for each field
    var colorIdx = 0;
    for (var row = 0; row < FIELD_HEIGHT; row++) {
        for (var column = 0; column < FIELD_WIDTH; column++) {
            var cell = FIELDS[fieldIdx][row][column];
            complField[row][column][0] = cell;
            if (cell == FULL) {
                complField[row][column][1] = colorIdx;
                colorIdx++;
            }
        }
    }

    % Disable left over highlights
    for (var row = 0; row < FIELD_HEIGHT; row++) {
        for (var column = 0; column < FIELD_WIDTH; column++) {
            highlight(column, row, false);
        }
    }

    % Hide all cells that are not part of the field
    hideCells();

    colorEnabled = this.getField("cbColor").isBoxChecked(0);
    if (colorEnabled) {
        % Generate a color for each marble
        colors = [];
        generateColors(1.0, 0.5, currentMarbleCount);
    }

    drawAll();
}

/**
 * Enable or disable the tooltip
 *
 * @param {Boolean} on Enable or disable tooltip
 */
function tooltip(on) {
    if (on) {
        % Move the tooltip field to the center of the screen
        % and adjust the size so the text can be displayed
        var field = this.getField("tooltiptxt");
        field.delay = true;
        field.value = GAME_DESC;

        field.multiline = true;
        field.textSize = 16;
        field.fillColor = color.gray;
        % hight 842, width 596
        % upper-left x, upper-left y, lower-right x and lower-right y
        field.rect = [128, 601, 468, 401];

        field.delay = false;
    } else {
        % Remove the field such that it is not visible
        var field = this.getField("tooltiptxt");
        field.delay = true;
        field.value = "";

        field.multiline = false;
        field.fillColor = color.gray;
        field.rect = [0, 0, 0, 0];

        field.delay = false;
    }
}

%%%%%%%%%%%%%
% Gamelogic %
%%%%%%%%%%%%%
/**
 * Save and highlight the last field the user clicked on.
 * Selection is only removed after clicking on the same field.
 *
 * @param {Number} x The x position.
 * @param {Number} y The y position.
 */
function fieldClicked(x, y, ignore=false) {
    this.getField("exporttxt").setFocus();

    if (timestep != 0.0) {
        printInfoText("Please wait for animation.");
        return;
    }

    % Select cell if none selected
    if (currentCell === null) {
        if (complField[y][x][0] == FULL) {
            currentCell = [x, y];
            highlight(x, y, true);
        } else {
            printInfoText("Cell empty and not selectable.");
        }
    } % Deselect cell
    else if (currentCell[0] == x && currentCell[1] == y) {
        currentCell = null;
        highlight(x, y, false);
    } else {
        % If a cell if selected the new click is interpreted as the target cell
        % Calculate wether it is a valid target
        var diffX = currentCell[0] - x;
        var diffY = currentCell[1] - y;
        var absX = Math.abs(diffX);
        var absY = Math.abs(diffY);

        if (absX == 2 && absY == 0 || absX == 0 && absY == 2) {
            if (complField[y][x][0] == EMPTY) {
                var x2 = x + diffX / 2;
                var y2 = y + diffY / 2;
                % Actually do stuff
                if (complField[y2][x2][0] == FULL) {
                    highlight(currentCell[0], currentCell[1], false);
                    jump(x, y);
                    currentCell = null;
                } else {
                    printInfoText("No cell to jump over.");
                }
            } else {
                printInfoText("Cell occupied, select other cell.");
            }
        } else {
            printInfoText("Not in range, select other cell.");
        }
    }
}

/**
 * Place the currentCell content into the new cell
 * and remove the cell content inbetween.
 *
 * @param {Number} x1 The x position.
 * @param {Number} y1 The y position.
 */
function jump(x1, y1) {
    currentMarbleCount--;

    var x2 = currentCell[0];
    var y2 = currentCell[1];

    % Calculate cell in between
    var diffX = x2 - x1;
    var diffY = y2 - y1;
    var x3 = x1 + diffX / 2;
    var y3 = y1 + diffY / 2;

    complField[y3][x3][0] = EMPTY;
    complField[y2][x2][0] = EMPTY;
    complField[y1][x1][0] = FULL;
    % Rewrite color information
    complField[y1][x1][1] = complField[y2][x2][1];
    complField[y3][x3][1] = -1;
    complField[y2][x2][1] = -1;

    % Prepare the animation cell
    complField[ANIM_POS][ANIM_POS][0] = FULL;
    complField[ANIM_POS][ANIM_POS][1] = complField[y1][x1][1];
    draw(ANIM_POS, ANIM_POS);

    % Start animation
    var callback = "jumpAnimation(" + x1 + "," + y1 + "," + x2 + "," + y2 + "," + x3 + "," + y3 + ")";
    run = app.setInterval(callback, ANIMATION_SPEED);
}

/**
 * Function for an animated cell. The cell is moved between two
 * given positions by linear interpolation.
 * The startposition and the center position are redrawn at
 * appropriate times.
 *
 * @param {Number} x1 The target x position.
 * @param {Number} y1 The target y position.
 * @param {Number} x2 The source x position.
 * @param {Number} y2 The source y position.
 * @param {Number} x3 The center x position.
 * @param {Number} y3 The center y position.
 */
function jumpAnimation(x1, y1, x2, y2, x3, y3) {
    % Get animation cell
    var field = this.getField("pos" + ANIM_POS + "-" + ANIM_POS);

    % Stop animation and reset if timelimit is reached
    if (timestep >= 1.0) {
        timestep = 0.0;
        draw(x1, y1);
        field.hidden = true;
        complField[ANIM_POS][ANIM_POS][0] = NONE;
        if (getAllMoves().length > 0) {
            printInfoText("Jump successfull!");
        } else {
            var lastPos = complField[W_POS_Y[fieldIdx]][W_POS_X[fieldIdx]][0] == FULL;
            if (currentMarbleCount == 1 && lastPos) {
                printInfoText("Game over, you won!");
            } else {
                printInfoText("Game over, no moves left!");
            }
        }
        app.clearInterval(run);
        return;
    }
    % If precisely at the center of the line draw pos 3 (remove marble)
    if (timestep >= 0.5 && timestep <= 0.5 + ANIMATION_OFFSET) {
        draw(x3, y3);
    } % Draw (remove) the start marble in the beginning
    else if (timestep >= 0.0 && timestep <= 0.0 + ANIMATION_OFFSET) {
        draw(x2, y2);
    }

    % Show the animation cell
    field.hidden = false;

    % Get the cells for the rectangle
    var src = this.getField("pos" + x2 + "-" + y2);
    var tgt = this.getField("pos" + x1 + "-" + y1);
    % Move the rectangle
    var rect = [0, 0, 0, 0];
    for (var i = 0; i < 4; i++) {
        rect[i] = tgt.rect[i] * timestep + src.rect[i] * (1.0 - timestep);
    }
    field.rect = rect;

    timestep += ANIMATION_OFFSET;
}

%%%%%%%%%%%%%%%%%
% Show Solution %
%%%%%%%%%%%%%%%%%
/**
 * Show a predefined solution for the start field.
 * Checking all possible moves like here
 * https://ece.uwaterloo.ca/~dwharder/aads/Algorithms/Backtracking/Peg_solitaire/
 * does not work for performance reasons.
 */
function showSolution() {
    % Prevent double execution due to multiple clicks on the gui
    if (exeIndex != 0) {
        printInfoText("Solution already running!");
        return;
    }
    % Restart to ensure all marbles being there
    restart();

    var fieldIdx = this.getField("cmLayout").currentValueIndices;
    exeMoves = solMoves[fieldIdx];
    % Start animation
    var callback = "animateMoves()";
    runSol = app.setInterval(callback, ANIMATION_SPEED * (1.1 / ANIMATION_OFFSET));

    drawAll();
}

/**
 * Apply a specified sequence of moves to the field.
 * The moves need to be given via the GUI and need to be
 * in character-notation or matrix-notation (m1:n1-m2:n2).
 * It is possible to give any number of moves.
 */
function executeMoves() {
    % Reset focus since the button would yield the txt window inoperative
    this.getField("exmovestxt").setFocus();
    % Prevent double execution due to multiple clicks on the gui
    if (exeIndex != 0) {
        printInfoText("Moves are already executed!");
        return;
    }

    % Convert move string
    var moveSequence = this.getField("exmovestxt").value;
    exeMoves = stringToSolution(moveSequence);
    % Check for error
    if (exeMoves[0][0] == 0) {
        printInfoText("Moves not parseable!");
        return;
    }

    var callback = "animateMoves()";
    runSol = app.setInterval(callback, ANIMATION_SPEED * (1.1 / ANIMATION_OFFSET));

    drawAll();
}

/**
 * Animates the current move (exeIndex) from the exeMoves array.
 * The same function is called as if the cells are would be clicked on.
 * Clears the intervall after applying all steps.
 */
function animateMoves() {
    % Security measure to ensure no execution while move animation is running
    if (timestep != 0.0) {
        return;
    }
    % Stop the animation
    if (exeIndex >= exeMoves.length) {
        exeIndex = 0;
        app.clearInterval(runSol);
        return;
    }
    % Execute move by simulate a click on the cells
    fieldClicked(exeMoves[exeIndex][0], exeMoves[exeIndex][1]);
    fieldClicked(exeMoves[exeIndex][2], exeMoves[exeIndex][3]);
    % Reset the cell state if the move was not executable
    currentCell = null;
    highlight(exeMoves[exeIndex][0], exeMoves[exeIndex][1], false);
    highlight(exeMoves[exeIndex][2], exeMoves[exeIndex][3], false);
    exeIndex++;
}

/**
 * Converts a given string into a sequence of array positions.
 * The string needs to refere to the cells via character notation.
 * Obacht: Does only work for english and european layout!
 * @param {String} moveSequence The sequence of moves to convert.
 * @return {Array} An array of fromTo positions or [[0]] if not parseable.
 */
function stringToSolution(moveSequence) {
    var toIdx = function toIdx(cell) {
        % The char notation table
        var charRep = [
            [0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 'a', 'b', 'c', 0, 0, 0],
            [0, 0, 'y', 'd', 'e', 'f', 'z', 0, 0],
            [0, 'g', 'h', 'i', 'j', 'k', 'l', 'm', 0],
            [0, 'n', 'o', 'p', 'x', 'P', 'O', 'N', 0],
            [0, 'M', 'L', 'K', 'J', 'I', 'H', 'G', 0],
            [0, 0, 'Z', 'F', 'E', 'D', 'Y', 0, 0],
            [0, 0, 0, 'C', 'B', 'A', 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0, 0, 0]
        ];
        % Search for the currently wanted char in the table
        % and return its coordinates
        for (var y = 0; y < charRep.length; y++) {
            for (var x = 0; x < charRep[y].length; x++) {
                if (cell == charRep[y][x]) {
                    return [x, y];
                }
            }
        }
        % Indicate error if not found
        return [0];
    }

    var result = [];
    var moves = moveSequence.split(/[,\/]/);

    % Check for notation type
    if (moves[0][0] >= '0' && moves[0][0] <= '9') {
        for (var x = 0; x < moves.length; x++) {
            % Get both positions
            var pos = moves[x].split("-");
            % Indicate error
            if (pos.length != 2) {
                return [[0]];
            }
            % Get coordinates
            var from = pos[0].split(":");
            var to = pos[1].split(":");
            % Indicate error
            if (from.length != 2 || to.length != 2) {
                return [[0]];
            }

            % Care input x, y are swapped
            result.push([
                parseInt(from[1], 10) + 1,
                parseInt(from[0], 10) + 1,
                parseInt(to[1], 10) + 1,
                parseInt(to[0], 10) + 1
            ]);
        }
    } else {
        for (var i = 0; i < moves.length; i++) {
            var from = toIdx(moves[i][0]);
            var to = toIdx(moves[i][1]);
            % Indicate error if not found
            if (from[0] == 0 || to[0] == 0 || moves[i].length != 2) {
                return [[0]];
            }
            % Create a fromTo move array
            result.push([from[0], from[1], to[0], to[1]]);
        }
    }

    return result;
}

/**
 * Iterate all cells and check if there is a marble.
 * If there is calculate all valid moves for this marble.
 * @return A list of valid moves.
 */
function getAllMoves() {
    % Get movesToTry for all marbles
    var movesToTry = [];
    for (var x = 0; x < FIELD_WIDTH; x++) {
        for (var y = 0; y < FIELD_HEIGHT; y++) {
            if (complField[y][x][0] == FULL) {
                movesToTry = movesToTry.concat(getMoves([x, y]));
            }
        }
    }

    return movesToTry;
}

/**
 * Get all moves for the given position.
 * This is tested by checking in all directions,
 * if the cell is empty and there is a cell to jump over.
 * @param {Array} marble The position to check.
 */
function getMoves(marble) {
    var x = marble[0];
    var y = marble[1];
    var moves = [];

    % Check each of the four directions + out of bounds
    if (x + 2 < FIELD_WIDTH && complField[y][x + 2][0] == EMPTY && complField[y][x + 1][0] == FULL) {
        moves.push([x, y, x + 2, y]);
    }
    if (x - 2 >= 0 && complField[y][x - 2][0] == EMPTY && complField[y][x - 1][0] == FULL) {
        moves.push([x, y, x - 2, y]);
    }
    if (y + 2 < FIELD_HEIGHT && complField[y + 2][x][0] == EMPTY && complField[y + 1][x][0] == FULL) {
        moves.push([x, y, x, y + 2]);
    }
    if (y - 2 >= 0 && complField[y - 2][x][0] == EMPTY && complField[y - 1][x][0] == FULL) {
        moves.push([x, y, x, y - 2]);
    }
    return moves;
}

%%%%%%%%%%%%%%%%%%%%%
% Import and export %
%%%%%%%%%%%%%%%%%%%%%
/**
 * Print a unique number to the gui as field representation.
 */
function exportField() {
    % Reset focus since the button would yield the txt window inoperative
    this.getField("exporttxt").setFocus();
    if (timestep != 0.0) {
        printInfoText("Can not export while jumping!");
        return;
    }
    this.getField("exporttxt").value = getUniqueCombination();
    printInfoText("Exported!");
}

/**
 * Get a number as unique field representation.
 * Each bit representing one cell of the field.
 * @return {Number} A representor for the field.
 */
function getUniqueCombination() {
    var arrayNum = 0;
    for (var x = 0; x < FIELD_WIDTH; x++) {
        for (var y = 0; y < FIELD_HEIGHT; y++) {
            if (complField[y][x][0] == NONE) {
                continue;
            }
            % Shift operation do not work - to many bits needed
            arrayNum *= 2;
            if (complField[y][x][0] == FULL) {
                arrayNum += 1;
            }
        }
    }
    return arrayNum;
}

/**
 * Read the input from the gui to generate a field from a
 * number representation.
 * OBACHT: Different layouts are not compatible.
 */
function importField() {
    % Stop showing solution if in process
    if (exeIndex != 0) {
        app.clearInterval(runSol);
        exeIndex = 0;
    }

    % Reset focus since the button would yield the txt window inoperative
    this.getField("importtxt").setFocus();
    var fieldIdx = this.getField("cmLayout").currentValueIndices;
    var combination = this.getField("importtxt").value;
    if (MARBLE_COUNTS[fieldIdx] < cellCount(combination)) {
        printInfoText("Input not compatible with current selected layout.");
        return;
    }
    toField(combination);
    drawAll();
    printInfoText("Imported!");
}

/**
 * Uses the given field to set the cells of the displayed field.
 * @param {Number} combination A representation of the field.
 */
function toField(combination) {
    var res = combination;
    % Iterate from back to front because of leading zeros
    for (var x = FIELD_WIDTH - 1; x >= 0; x--) {
        for (var y = FIELD_HEIGHT - 1; y >= 0 ; y--) {
            if (complField[y][x][0] == NONE) {
                continue;
            }

            var val = res \% 2;
            if (val == 1) {
                complField[y][x][0] = FULL;
                res -= 1;
            } else {
                complField[y][x][0] = EMPTY;
            }
            res = res / 2;
        }
    }
}

/**
 * Get the amount of used cells that are in this field.
 * For this the amount of ones of the binary form are counted.
 * @param {Number} combination A number represenation of the field.
 * @return {Number} The amount of used cells.
 */
function cellCount(combination) {
    var count = 0;
    % Get one bit after the other
    while (combination > 0) {
        var val = combination \% 2;
        if (val == 1) {
            count++;
            combination -= 1;
        }
        combination = combination / 2;
    }
    return count;
}

%%%%%%%%%%%%%%%%%%%%%
% Draw and coloring %
%%%%%%%%%%%%%%%%%%%%%
/**
 * Hide all cells that are of type NONE.
 */
function hideCells() {
    var field;
    for (var x = 0; x < FIELD_WIDTH; x++) {
        for (var y = 0; y < FIELD_HEIGHT; y++) {
            field = this.getField("pos" + x + "-" + y);
            if (complField[y][x][0] == NONE) {
                field.hidden = true;
            } else {
                field.hidden = false;
            }
        }
    }
}

/**
 * Draw all fields.
 */
function drawAll() {
    for (var x = 0; x < FIELD_WIDTH; x++) {
        for (var y = 0; y < FIELD_HEIGHT; y++) {
            draw(x, y);
        }
    }
}

/**
 * Since only one field is updated with each input,
 * only this field needs to be redrawn.
 *
 * @param {Number} x The x position.
 * @param {Number} y The y position.
 */
function draw(x, y) {
    var field = this.getField("pos" + x + "-" + y);
    field.delay = true;

    var val = complField[y][x][0];
    if (val == FULL) {
        field.value = MARBLE_SYMBOL;
        if (colorEnabled) {
            field.textColor = colors[complField[y][x][1]];
        } else {
            field.textColor = color.black;
        }
    } else {
        field.value = EMPTY_SYMBOL;
        field.textColor = color.black;
    }
    field.delay = false;
}

/**
 * Enables or disables the highlight for
 * all cells in the field.
 *
 * @param {Boolean} enable Enables or disables highlight.
 */
function highlightAll(enable) {
    for (var row = 0; row < FIELD_HEIGHT; row++) {
        for (var column = 0; column < FIELD_WIDTH; column++) {
            highlight(column, row, enable);
        }
    }
}

/**
 * Enables or disables the highlight of the given cell.
 *
 * @param {Number} x The x position.
 * @param {Number} y The y position.
 * @param {Boolean} enable If true enables the highlight
 * @param {Boolean} bomb If true enables the bombhighlight
 */
function highlight(x, y, enable) {
    var field = this.getField("pos" + x + "-" + y);
    field.delay = true;
    % Highlight the current taken move
    if (enable) {
        field.borderColor = HIGHLIGHT_COLOR;
    } else {
        field.borderColor = color.transparent;
    }
    field.delay = false;
}

/**
 * Print the current points of the player.
 */
function updatePoints() {
    var field = this.getField("pointstxt");
    field.delay = true;
    field.value = points;
    field.delay = false;
}

/**
 * Print information about the gamestate.
 */
function printInfoText(text) {
    var field = this.getField("info");
    field.delay = true;
    field.value = text;
    field.delay = false;
}

\end{insDLJS}

\OpenAction{/S /JavaScript /JS (initialise();)}
\newdimen\longline
\longline=\textwidth\advance\longline-6em
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Dokument
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\begin{document}

    \begin{Form}

        % Header
        \begin{multicols}{2}
            % Title
            \section*{Solo Noble}
        \columnbreak
            % Tooltip object
            \begin{flushright}%
                \PushButton[name=tooltipbtn, bordercolor=white]{%
                    \begin{tcolorbox}[width=20pt, height=20pt, left=3pt, top=0pt]
                        \centering?\strut{}
                    \end{tcolorbox}%
                }
            \end{flushright}%
        \end{multicols}

        % Calculate the max width and height of a block
        \FPeval{\resultW}{(\fieldWidth * 4 + 3)}
        \def\blocksizeW{\dimexpr (\linewidth-\resultW pt)/\fieldWidth \relax}

        \FPeval{\resultH}{(\fieldHeight * 4 + 3)}
        \def\blocksizeH{\dimexpr (500 pt -\resultH pt)/\fieldHeight \relax}

        % Define the blocksize via the smaller amount
        \ifnum\blocksizeW<\blocksizeH
            \def\blocksize{\blocksizeW}
        \else
            \def\blocksize{\blocksizeH}
        \fi

        % The gamefield
        \begin{tcolorbox}
            \begin{flushright}
                \xintFor* #2 in {\xintSeq{0}{\fieldHeight-1}} \do {%
                    \xintFor* #1 in {\xintSeq{0}{\fieldWidth-1}} \do {%
                        \TextField[%
                            width=\blocksize, height=\blocksize,%
                            bordercolor=, backgroundcolor=, name=pos#1-#2, readonly]{}\hspace{3pt}%
                    }\vspace{3pt}
                    \newline%
                }%
            \end{flushright}%
        \end{tcolorbox}

        %%%%%%%%%%%%%%%%%%%%%%%
        % Commandredifinition %
        % https://www.dickimaw-books.com/latex/admin/html/eforms.shtml
        %%%%%%%%%%%%%%%%%%%%%%%
        \def\DefaultHeightofText{14pt}
        \renewcommand*{\LayoutTextField}[2]{%
            \parbox[c][\DefaultHeightofText]{0.5\linewidth}{#1#2}%
        }

        \renewcommand*{\LayoutCheckField}[2]{#1 #2}
        \renewcommand*{\DefaultWidthofCheckBox}{2ex}
        \renewcommand*{\DefaultHeightofCheckBox}{2ex}
        \renewcommand*{\LayoutCheckField}[2]{%
            \parbox[c][\DefaultHeightofCheckBox]{0.12\linewidth}{#1}\enspace%
            \parbox[c][\DefaultHeightofCheckBox]{\DefaultWidthofCheckBox}{#2}%
        }

        \renewcommand*{\DefaultWidthofChoiceMenu}{2.5ex}
        \renewcommand*{\DefaultHeightofChoiceMenu}{2.04ex}

        %%%%%%%%%%%%%%%%
        % GUI elements %
        %%%%%%%%%%%%%%%%
        \begin{multicols}{2}

            \PushButton[name=restart, bordercolor=, backgroundcolor=, borderwidth=0]{
                \begin{tcolorbox}
                    \centering
                    Restart game\strut{}
                \end{tcolorbox}
            }\vspace{0.1cm}

            \PushButton[name=exmoves, bordercolor=, backgroundcolor=, borderwidth=0]{
                \begin{tcolorbox}
                    \centering
                    Execute moves: \strut{}
                    \TextField[name=exmovestxt, bordercolor=gray, borderwidth=2pt,
                        backgroundcolor=white]{\strut}
                \end{tcolorbox}
            }\vspace{0.1cm}

            \PushButton[name=import, bordercolor=, backgroundcolor=, borderwidth=0]{
                \begin{tcolorbox}
                    \centering
                    Import field: \strut{}
                    \TextField[name=importtxt, bordercolor=gray, borderwidth=2pt,
                        backgroundcolor=white]{\strut}
                \end{tcolorbox}
            }%

            \begin{tcolorbox}
                \centering
                \ChoiceMenu[name=cmLayout, bordercolor=gray, width=2cm, combo]{Layout: }{
                    English (33), European (37), German (45), Asymmetrical (39), Hermary (39)
                }
            \end{tcolorbox}

            \columnbreak%

            \PushButton[name=showSol, bordercolor=, backgroundcolor=, borderwidth=0]{
                \begin{tcolorbox}
                    \centering
                    Restart and show full solution\strut{}
                \end{tcolorbox}
            }

            \begin{tcolorbox}
                \TextField[name=info, width=2\linewidth, bordercolor=, backgroundcolor=, readonly]
                    {\strut}
            \end{tcolorbox}

            \PushButton[name=export, bordercolor=, backgroundcolor=, borderwidth=0]{
                \begin{tcolorbox}
                    \centering
                    Export field: \strut{}
                    \TextField[name=exporttxt,
                        bordercolor=gray, borderwidth=2pt, backgroundcolor=white]{\strut}
                \end{tcolorbox}
            }

            \begin{tcolorbox}
                \CheckBox[name=cbColor, bordercolor=gray, checkboxsymbol=5]{Color}
            \end{tcolorbox}

        \end{multicols}

        \TextField[bordercolor=white, name=tooltiptxt, readonly]{}
    \end{Form}
\end{document}


\chapter{Type theoretic descent}\label{chap:descent}

In this chapter we study homotopy pushouts, which were established as higher inductive types in homotopy type theory in section 6.8 of \cite{hottbook}. From this chapter on, we will assume that universes are closed under homotopy pushouts. This is the last assumption that we will be making in the present work. In particular, we will not assume the existence of higher inductive types with some self-reference in the constructors (e.g.~the propositional truncation).

Our first main result is the descent theorem for homotopy pushouts (\cref{thm:descent,cor:descent_fib}), in which we establish that a cartesian transformation of spans
\begin{equation*}
\begin{tikzcd}
A' \arrow[d]  & S' \arrow[l] \arrow[r] \arrow[d] \arrow[dl,phantom,"\llcorner" very near start] \arrow[dr,phantom,"\lrcorner" very near start] & B' \arrow[d] \\
A & S \arrow[l] \arrow[r] & B
\end{tikzcd}
\end{equation*}
extends uniquely to a cartesian transformation of the pushout squares, i.e. a commuting cube
\begin{equation*}
\begin{tikzcd}
& S' \arrow[dl] \arrow[dr] \arrow[d] \\
A' \arrow[d] & S \arrow[dl] \arrow[dr] & B' \arrow[dl,crossing over] \arrow[d] \\
A \arrow[dr] & A'\sqcup^{S'}B' \arrow[d] \arrow[from=ul,crossing over] & B \arrow[dl] \\
& A\sqcup^S B
\end{tikzcd}
\end{equation*}
of which the vertical sides are pullback squares.

The second main theorem of this chapter, \cref{thm:cartesian_cube}, is an adaption to homotopy type theory of a theorem due to \cite{AnelBiedermanFinsterJoyal}. It is closely related to the descent theorem but can be stated without a universe: for any commuting cube
\begin{equation*}
\begin{tikzcd}
& S' \arrow[dl] \arrow[dr] \arrow[d] \\
A' \arrow[d] & S \arrow[dl] \arrow[dr] & B' \arrow[dl,crossing over] \arrow[d] \\
A \arrow[dr] & X' \arrow[d] \arrow[from=ul,crossing over] & B \arrow[dl] \\
& X
\end{tikzcd}
\end{equation*}
of which the two vertical back squares are pullback squares, the two vertical front squares are pullback squares if and only if the square
\begin{equation*}
\begin{tikzcd}
A' \sqcup^{S'} B' \arrow[r] \arrow[d] & X' \arrow[d] \\
A\sqcup^{S} B \arrow[r] & X.
\end{tikzcd}
\end{equation*}
is a pullback square. Even though this statement does not involve a universe, we use the univalence axiom in our proof that this square being pullback implies that the front two vertical squares of the cube are pullback squares. Function extensionality suffices for the converse direction.

\section{Homotopy pushouts}

\subsection{Pushouts as higher inductive types}

\begin{defn}
A \define{span} $\mathcal{S}$ from $A$ to $B$ is a triple $(S,f,g)$ consisting of a type $S$ and maps $f:S\to A$ and $g:S\to B$. We write $\mathsf{span}(A,B)$ for the type of small spans from $A$ to $B$.
\end{defn}

\begin{defn}
Consider a span $\mathcal{S}\jdeq (S,f,g)$ from $A$ to $B$, and let $X$ be a type. A cocone with vertex $X$ on $\mathcal{S}$ is a triple $(i,j,H)$ consisting of maps $i:A\to X$, $j:B\to X$, and a homotopy $H:i\circ f\htpy j\circ g$ witnessing that the square
\begin{equation*}
\begin{tikzcd}
S \arrow[d,swap,"f"] \arrow[r,"g"] & B \arrow[d,"j"] \\
A \arrow[r,swap,"i"] & X
\end{tikzcd}
\end{equation*}
commutes. We write $\mathsf{cocone}_{\mathcal{S}}(X)$ for the type of cocones with vertex $X$ on $\mathcal{S}$.
\end{defn}

\begin{defn}
Consider a commuting square
\begin{equation*}
\begin{tikzcd}
S \arrow[d,swap,"f"] \arrow[r,"g"] & B \arrow[d,"j"] \\
A \arrow[r,swap,"i"] & X,
\end{tikzcd}
\end{equation*}
with $H:i\circ f\htpy j\circ g$, and let $Y$ be a type. We define the operation
\begin{equation*}
\mathsf{cocone\usc{}map}((i,j,H),Y) \defeq (X\to Y) \to \mathsf{cocone}_{\mathcal{S}}(Y).
\end{equation*}
by $h\mapsto (h\circ i,h\circ j,h\cdot H)$.
\end{defn}

\begin{defn}
A commuting square
\begin{equation*}
\begin{tikzcd}
S \arrow[r,"g"] \arrow[d,swap,"f"] & B \arrow[d,"j"] \\
A \arrow[r,swap,"i"] & X
\end{tikzcd}
\end{equation*}
with $H:i\circ f \htpy j\circ g$ is said to be a \define{(homotopy) pushout square}\index{pushout square} if the cocone $(i,j,H)$ with vertex $X$ on the span $\mathcal{S}\jdeq (S,f,g)$
satisfies the \define{universal property of pushouts}\index{universal property!of pushouts|textbf}, which asserts that the map
\begin{equation*}
\mathsf{cocone\usc{}map}(i,j,H):(X\to Y)\to \mathsf{cocone}(Y)
\end{equation*}
is an equivalence for any type $Y$. Sometimes pushout squares are also called \define{cocartesian squares}\index{cocartesian square|textbf}.
\end{defn}

\begin{defn}
Consider a pushout square
\begin{equation*}
\begin{tikzcd}
S \arrow[r,"g"] \arrow[d,swap,"f"] & B \arrow[d,"j"] \\
A \arrow[r,swap,"i"] & X
\end{tikzcd}
\end{equation*}
with $H:i\circ f \htpy j\circ g$, and consider a cocone $(i',j',H')$ with vertex $Y$ on the same span $\mathcal{S}\jdeq(S,f,g)$. Then the unique map $h:X\to Y$ such that
\begin{equation*}
\mathsf{cocone\usc{}map}((i,j,H),Y,h)= (i',j',H')
\end{equation*}
is called the \define{cogap map} of $(i',j',H')$. We also write $\mathsf{cogap}(i',j',H')$ for the cogap map, and we write
\begin{align*}
\mathsf{left\usc{}comp}(i',j',H') & : i' \htpy \mathsf{cogap}(i',j',H')\circ \inl  \\
\mathsf{right\usc{}comp}(i',j',H') & : j' \htpy \mathsf{cogap}(i',j',H')\circ \inr  \\
\mathsf{coh\usc{}comp}(i',j',H') & : \ct{(\mathsf{left\usc{}comp}(i',j',H')\cdot g)}{H'} \htpy \ct{(\mathsf{cogap}(i',j',H')\cdot \glue)}{(\mathsf{right\usc{}comp}(i',j',H')\cdot f)}.
\end{align*}
for the homotopies determining the uniqueness of $\mathsf{cogap}(i',j',H')$.
\end{defn}

\begin{prp}\label{thm:pushout_up}
Consider a commuting square\index{universal property!of pushouts|textit}
\begin{equation*}
\begin{tikzcd}
S \arrow[r,"g"] \arrow[d,swap,"f"] & B \arrow[d,"j"] \\
A \arrow[r,swap,"i"] & X,
\end{tikzcd}
\end{equation*}
with $H:i\circ f\htpy j\circ g$. The following are equivalent:
\begin{enumerate}
\item The square is a pushout square.
\item The square
\begin{equation*}
\begin{tikzcd}
Y^X \arrow[r,"\blank\circ j"] \arrow[d,swap,"\blank\circ i"] & Y^B \arrow[d,"\blank\circ g"] \\
Y^A \arrow[r,swap,"\blank\circ f"] & Y^S,
\end{tikzcd}
\end{equation*}
which commutes by the homotopy
\begin{equation*}
\lam{h} \mathsf{eq\usc{}htpy}(h\cdot H),
\end{equation*}
is a pullback square, for every type $Y$.
\item For every type family $P$ over $X$, the square
\begin{equation*}
\begin{tikzcd}[column sep=9em]
\prd{x:X}P(x) \arrow[r,"\blank\circ j"] \arrow[d,swap,"\blank\circ i"] & \prd{b:B}P(j(b)) \arrow[d,"\blank\circ g"] \\
\prd{a:A}P(i(a)) \arrow[r,swap,"{\lam{h}{x} \tr_{P}(H(x),h(f(x)))}"] & \prd{x:S}P(j(g(x))),
\end{tikzcd}
\end{equation*}
which commutes by the homotopy
\begin{equation*}
\lam{h}\mathsf{eq\usc{}htpy}(\lam{x}\apd{h}{H(x)})
\end{equation*}
is a pullback square. This property is also called the \define{dependent universal property of pushouts}.
\item The gap map of the square
\begin{equation*}
\begin{tikzcd}
\prd{x:X}P(x) \arrow[r] \arrow[d] & \prd{b:B}P(j(b)) \arrow[d] \\
\prd{a:A}P(i(a)) \arrow[r] & \prd{x:S}P(j(g(x)))
\end{tikzcd}
\end{equation*}
has a section, for any type family $P$ over $X$. This property is also called the \define{induction principle of pushouts}.
\end{enumerate}
\end{prp}

\begin{defn}
From now on we will assume that any span has a pushout, and moreover that universes are closed under pushouts. We will write $A\sqcup^{\mathcal{S}} B$ for the pushout of the span $\mathcal{S}\jdeq(S,f,g)$ from $A$ to $B$. The type $A\sqcup^{\mathcal{S}} B$ comes equipped with a colimiting cocone $(\inl,\inr,\glue)$, as displayed in the pushout square
\begin{equation*}
\begin{tikzcd}
S \arrow[d,swap,"f"] \arrow[r,"g"] & B \arrow[d,"\inr"] \\
A \arrow[r,swap,"\inl"] & A \sqcup^{\mathcal{S}} B.
\end{tikzcd}
\end{equation*}
\end{defn}

\begin{rmk}
We note that if $\mathcal{S}\jdeq (S,f,g)$ is a span of \emph{pointed} types and pointed maps between them, then the pushout $A\sqcup^{\mathcal{S}} B$ of $\mathcal{S}$ is again a pointed type. The cocone $(\inl,\inr,\glue)$ consists of two pointed maps and a pointed homotopy filling the square of pointed maps. Moreover, the pushout $A\sqcup^{\mathcal{S}} B$ satisfies a pointed version of the universal property: for any pointed type $Y$ the square
\begin{equation*}
\begin{tikzcd}
(A\sqcup^{\mathcal{S}} B \to_\ast Y) \arrow[r] \arrow[d] & (B\to_\ast Y) \arrow[d] \\
(A\to_\ast Y) \arrow[r] & (S\to_\ast Y)
\end{tikzcd}
\end{equation*}
is a pullback square.
\end{rmk}

\subsection{Examples of pushouts}

\begin{defn}
Let $X$ be a type. We define the \define{suspension}\index{suspension|textbf} $\susp X$\index{SX@{$\susp X$}|textbf} of $X$ to be the pushout of the span
\begin{equation*}
\begin{tikzcd}
X \arrow[r] \arrow[d] & \unit \arrow[d,"\inr"] \\
\unit \arrow[r,swap,"\inl"] & \susp X
\end{tikzcd}
\end{equation*}
We will write $\north\defeq\inl(\ttt)$ and $\south\defeq\inr(\ttt)$.
\end{defn}

\begin{rmk}By the universal property it follows that the map
\begin{equation*}
(\susp X \to Y) \to \sm{y,y':Y}X\to (y=y')
\end{equation*}
given by $h\mapsto (h(\north),h(\south),h\cdot\glue)$ is an equivalence.

Moreover, if $X$ is a pointed type, then the suspension is considered to be a pointed type with base point $\north$. By the universal property of $\susp X$ it follows that the square
\begin{equation*}
\begin{tikzcd}
(\susp X\to_\ast Y) \arrow[r] \arrow[d] & (\unit \to_\ast Y) \arrow[d] \\
(\unit\to_\ast Y) \arrow[r] & (X\to_\ast Y)
\end{tikzcd}
\end{equation*}
is a pullback square. Since $\unit\to_\ast Y$ is contractible, it follows that
\begin{equation*}
(\susp X\to_\ast Y) \eqvsym \loopspace{X\to_\ast Y} \eqvsym X \to_\ast \loopspace Y
\end{equation*}
\end{rmk}

\begin{defn}
Given a map $f:A\to B$, we define the \define{cofiber}\index{cofiber|textbf} $\mathsf{cof}_f$\index{cofib_f@{$\mathsf{cof}_f$}|textbf} of $f$ as the pushout
\begin{equation*}
\begin{tikzcd}
A \arrow[r,"f"] \arrow[d] & B \arrow[d,"\inr"] \\
\unit \arrow[r,swap,"\inl"] & \mathsf{cof}_f.
\end{tikzcd}
\end{equation*}
The cofiber of a map is sometimes also called the \define{mapping cone}\index{mapping cone|textbf}.
\end{defn}

\begin{defn}
We define the \define{join}\index{join} $\join{X}{Y}$\index{join X Y@{$\join{X}{Y}$}|textbf} of $X$ and $Y$ to be the pushout
\begin{equation*}
\begin{tikzcd}
X\times Y \arrow[r,"\proj 2"] \arrow[d,swap,"\proj 1"] & Y \arrow[d,"\inr"] \\
X \arrow[r,swap,"\inl"] & X \ast Y.
\end{tikzcd}
\end{equation*}
\end{defn}

\begin{defn}
We define the \define{$n$-sphere}\index{n-sphere@{$n$-sphere}|textbf} $\sphere{n}$\index{Sn@{$\sphere{n}$}|textbf} for any $n\geq -1$ by induction on $n$, by taking
\begin{align*}
\sphere{-1} & \defeq \emptyt \\
\sphere{0} & \defeq \bool \\
\sphere{n+1} & \defeq \join{\bool}{\sphere{n}}.
\end{align*}
\end{defn}

\begin{defn}
Suppose $A$ and $B$ are pointed types, with base points $a_0$ and $b_0$, respectively. The \define{(binary) wedge}\index{wedge@(binary) wedge|textbf} $A\vee B$ of $A$ and $B$ is defined as the pushout
\begin{equation*}
\begin{tikzcd}
\bool \arrow[r] \arrow[d] & A+B \arrow[d] \\
\unit \arrow[r] & A\vee B.
\end{tikzcd}
\end{equation*}
\end{defn}

\begin{defn}
Given a type $I$, and a family of pointed types $A$ over $i$, with base points $a_0(i)$. We define the \define{(indexed) wedge}\index{wedge@{(indexed) wedge}|textbf} $\bigvee_{(i:I)}A_i$ as the pushout
\begin{equation*}
\begin{tikzcd}[column sep=huge]
I \arrow[d] \arrow[r,"{\lam{i}(i,a_0(i))}"] & \sm{i:I}A_i \arrow[d] \\
\unit \arrow[r] & \bigvee_{(i:I)} A_i.
\end{tikzcd}
\end{equation*}
\end{defn}

\begin{defn}
Suppose $A$ and $B$ are pointed types. We define the \define{wedge inclusion} $\mathsf{wedge\usc{}in}:A\vee B\to A\times B$ to be the unique map obtained via the universal property of pushouts as indicated in the diagram
\begin{equation*}
\begin{tikzcd}
\unit \arrow[d] \arrow[r] &[1em] B \arrow[d] \arrow[ddr,bend left=15,"{\lam{b}(a_0,b)}"] \\
A \arrow[r] \arrow[drr,bend right=15,swap,"{\lam{a}(a,b_0)}"] & A\vee B \arrow[dr,swap,"\mathsf{wedge\usc{}in}" near start] \\
& & A \times B.
\end{tikzcd}
\end{equation*}
We define the \define{smash product} $A\wedge B$ of $A$ and $B$ as the cofiber of the wedge inclusion, i.e.~as a pushout
\begin{equation*}
\begin{tikzcd}[column sep=large]
A\vee B \arrow[r,"\mathsf{wedge\usc{}in}"] \arrow[d] & A\times B \arrow[d] \\
\unit \arrow[r] & A\wedge B.
\end{tikzcd}
\end{equation*}
\end{defn}

\subsection{Properties of iterated pushouts}
The following corollary is also called the \define{pasting property} of pullbacks.\index{pasting property!of pullbacks|textit}\marginnote{edit}

\begin{cor}\label{thm:pb_pasting}
Consider a commuting diagram of the form
\begin{equation*}
\begin{tikzcd}
A \arrow[r,"k"] \arrow[d,swap,"f"] & B \arrow[r,"l"] \arrow[d,"g"] & C \arrow[d,"h"] \\
X \arrow[r,swap,"i"] & Y \arrow[r,swap,"j"] & Z
\end{tikzcd}
\end{equation*}
with homotopies $H:i\circ f\htpy g\circ k$ and $K:j\circ g\htpy h\circ l$, and the homotopy
\begin{equation*}
\ct{(j\cdot H)}{(K\cdot k)}:j\circ i\circ f\htpy h\circ l\circ k
\end{equation*}
witnessing that the outer rectangle commutes. Furthermore, suppose that the square on the right is a pullback square. Then the following are equivalent:
\begin{samepage}%
\begin{enumerate}
\item The square on the left is a pullback square.
\item The outer rectangle is a pullback square.
\end{enumerate}%
\end{samepage}%
\end{cor}

\begin{proof}
The commutativity of the two squares induces fiberwise transformations
\begin{align*}
& \prd{x:X}\fib{f}{x}\to \fib{g}{i(x)} \\
& \prd{y:Y}\fib{g}{y}\to \fib{h}{j(y)}.
\end{align*}
By the assumption that the square on the right is a pullback square, it follows from \cref{cor:pb_fibequiv} that the fiberwise transformation
\begin{equation*}
\prd{y:Y}\fib{g}{y}\to\fib{h}{j(y)}
\end{equation*}
is a fiberwise equivalence. Therefore it follows from 3-for-2 property of equivalences that the fiberwise transformation
\begin{equation*}
\prd{x:X}\fib{f}{x}\to\fib{g}{i(x)}
\end{equation*}
is a fiberwise equivalence if and only if the fiberwise transformation
\begin{equation*}
\prd{x:X}\fib{f}{x}\to\fib{h}{j(i(x))}
\end{equation*}
is a fiberwise equivalence. Now the claim follows from one more application of \cref{cor:pb_fibequiv}.
\end{proof}

\begin{cor}
Consider a commuting cube
\begin{equation*}
\begin{tikzcd}
& S' \arrow[dl] \arrow[dr] \arrow[d] \\
A' \arrow[d] & S \arrow[dl] \arrow[dr] & B' \arrow[dl,crossing over] \arrow[d] \\
A \arrow[dr] & X' \arrow[d] \arrow[from=ul,crossing over] & B \arrow[dl] \\
& X,
\end{tikzcd}
\end{equation*}
of which the two front squares are pullback squares. Then the back left square is a pullback square if and only if the back right square is a pullback square.
\end{cor}

\begin{prp}\label{thm:pushout_pasting}
Consider the following configuration of commuting squares:\index{pushout!pasting property|textit}\index{pasting property!for pushouts|textit}
\begin{equation*}
\begin{tikzcd}
A \arrow[r,"i"] \arrow[d,swap,"f"] & B \arrow[r,"k"] \arrow[d,swap,"g"] & C \arrow[d,"h"] \\
X \arrow[r,swap,"j"] & Y \arrow[r,swap,"l"] & Z
\end{tikzcd}
\end{equation*}
with homotopies $H:j\circ f\htpy g\circ i$ and $K:l\circ g\htpy h\circ k$, and suppose that the square on the left is a pushout square.
Then the square on the right is a pushout square if and only if the outer rectangle is a pushout square.
\end{prp}

\begin{proof}
Let $T$ be a type. Taking the exponent $T^{(\blank)}$ of the entire diagram of the statement of the theorem, we obtain the following commuting diagram
\begin{equation*}
\begin{tikzcd}
T^Z \arrow[r,"\blank\circ l"] \arrow[d,swap,"\blank\circ h"] & T^Y \arrow[d,swap,"\blank\circ g"] \arrow[r,"\blank\circ j"] & T^X \arrow[d,"\blank\circ f"] \\
T^C \arrow[r,swap,"\blank\circ k"] & T^B \arrow[r,swap,"\blank\circ i"] & T^A.
\end{tikzcd}
\end{equation*}
By the assumption that $Y$ is the pushout of $B\leftarrow A \rightarrow X$, it follows that the square on the right is a pullback square. It follows by \autoref{thm:pb_pasting} that the rectangle on the left is a pullback if and only if the outer rectangle is a pullback. Thus the statement follows by the second characterization in \autoref{thm:pushout_up}.
\end{proof}

\begin{lem}
Consider a map $f:A\to B$. Then the cofiber of the map $\inr:B\to \mathsf{cof}_f$ is equivalent to the suspension $\susp{A}$ of $A$.
\end{lem}

\begin{prp}
Consider a commuting square
\begin{equation*}
\begin{tikzcd}
A \arrow[r,"i"] \arrow[d,swap,"f"] & B \arrow[d,"g"] \\
X \arrow[r,swap,"j"] & Y
\end{tikzcd}
\end{equation*}
and write $\mathsf{cogap}: X\sqcup^A B\to Y$ for the cogap map.
Then the square
\begin{equation*}
\begin{tikzcd}
\mathsf{cof}_f \arrow[d] \arrow[r] & \mathsf{cof}_g \arrow[d] \\
\unit \arrow[r] & \mathsf{cof}_{\mathsf{cogap}}
\end{tikzcd}
\end{equation*}
is a pushout square.
\end{prp}

\section{Descent for pushouts}\label{sec:descent}

\subsection{Type families over pushouts}

\begin{defn}
Consider a commuting square
\begin{equation*}
\begin{tikzcd}
S \arrow[r,"g"] \arrow[d,swap,"f"] & B \arrow[d,"j"] \\
A \arrow[r,swap,"i"] & X.
\end{tikzcd}
\end{equation*}
with $H:i\circ f\htpy j\circ g$, where all types involved are in $\UU$. The type $\mathsf{Desc}(\mathcal{S})$\index{Desc@{$\mathsf{Desc}(\mathcal{S})$}|textbf} of \define{descent data}\index{descent data|textbf} for $X$, is defined to be the type of triples $(P_A,P_B,P_S)$ consisting of
\begin{align*}
P_A & : A \to \UU \\
P_B & : B \to \UU \\
P_S & : \prd{x:S} \eqv{P_A(f(x))}{P_B(g(x))}.
\end{align*}
Furthermore, we define the map\index{desc_fam@{$\mathsf{desc\usc{}fam}_{\mathcal{S}}$}|textbf}
\begin{equation*}
\mathsf{desc\usc{}fam}_{\mathcal{S}}(i,j,H) : (X\to \UU)\to \mathsf{Desc}(\mathcal{S})
\end{equation*}
by $P\mapsto (P\circ i,P\circ j,\lam{x}\mathsf{tr}_P(H(x)))$.
\end{defn}

\begin{prp}\label{thm:desc_fam}
Consider a commuting square
\begin{equation*}
\begin{tikzcd}
S \arrow[r,"g"] \arrow[d,swap,"f"] & B \arrow[d,"j"] \\
A \arrow[r,swap,"i"] & X.
\end{tikzcd}
\end{equation*}
with $H:i\circ f\htpy j\circ g$. If the square is a pushout square, then the function\index{desc_fam@{$\mathsf{desc\usc{}fam}_{\mathcal{S}}$}!is an equivalence|textit}
\begin{equation*}
\mathsf{desc\usc{}fam}_{\mathcal{S}}(i,j,H) : (X\to \UU)\to \mathsf{Desc}(\mathcal{S})
\end{equation*}
is an equivalence.
\end{prp}

\begin{proof}
By the 3-for-2 property of equivalences it suffices to construct an equivalence $\varphi:\mathsf{cocone}_{\mathcal{S}}(\UU)\to\mathsf{Desc}(\mathcal{S})$ such that the triangle
\begin{equation*}
\begin{tikzcd}
& \UU^X \arrow[dl,swap,"{\mathsf{cocone\usc{}map}_{\mathcal{S}}(i,j,H)}"] \arrow[dr,"{\mathsf{desc\usc{}fam}_{\mathcal{S}}(i,j,H)}"] & \phantom{\mathsf{cocone}_{\mathcal{S}}(\UU)} \\
\mathsf{cocone}_{\mathcal{S}}(\UU) \arrow[rr,densely dotted,"\eqvsym","\varphi"'] & & \mathsf{Desc}(\mathcal{S})
\end{tikzcd}
\end{equation*}
commutes.

Since we have equivalences
\begin{equation*}
\mathsf{equiv\usc{}eq}:\eqv{\Big(P_A(f(x))=P_B(g(x))\Big)}{\Big(\eqv{P_A(f(x))}{P_B(g(x))}\Big)}
\end{equation*}
for all $x:S$, we obtain an equivalence on the dependent products
\begin{equation*}
\eqv{\Big(\prd{x:S}P_A(f(x))=P_B(g(x))\Big)}{\Big(\prd{x:S}\eqv{P_A(f(x))}{P_B(g(x))}\Big)}.
\end{equation*}
by post-composing with the equivalences $\mathsf{equiv\usc{}eq}$.
We define $\varphi$ to be the induced map on total spaces. Explicitly, we have
\begin{equation*}
\varphi\defeq \lam{(P_A,P_B,K)}(P_A,P_B,\lam{x}\mathsf{equiv\usc{}eq}(K(x))).
\end{equation*}
Then $\varphi$ is an equivalence by \cref{thm:fib_equiv}, and the triangle commutes because there is a homotopy
\begin{equation*}
\mathsf{equiv\usc{}eq}(\ap{P}{H(x)}) \htpy \mathsf{tr}_P(H(x)). \qedhere
\end{equation*}
\end{proof}

\begin{cor}\label{cor:desc_fam}
Consider descent data $(P_A,P_B,P_S)$ for a pushout square as in \cref{thm:desc_fam}.
Then the type of quadruples $(P,e_A,e_B,e_S)$ consisting of a family $P:X\to\UU$ equipped with fiberwise equivalences
\begin{samepage}
\begin{align*}
e_A & : \prd{a:A}\eqv{P_A(a)}{P(i(a))} \\
e_B & : \prd{b:B}\eqv{P_B(a)}{P(j(b))}
\end{align*}
\end{samepage}%
and a homotopy $e_S$ witnessing that the square
\begin{equation*}
\begin{tikzcd}[column sep=huge]
P_A(f(x)) \arrow[r,"e_A(f(x))"] \arrow[d,swap,"P_S(x)"] & P(i(f(x))) \arrow[d,"\mathsf{tr}_P(H(x))"] \\
P_B(g(x)) \arrow[r,swap,"e_B(g(x))"] & P(j(g(x)))
\end{tikzcd}
\end{equation*}
commutes, is contractible.
\end{cor}

\begin{proof}
The fiber of $\mathsf{desc\usc{}fam}_{\mathcal{S}}(i,j,H)$ map at $(P_A,P_B,P_S)$ is equivalent to the type of quadruples $(P,e_A,e_B,e_S)$ as described in the theorem, which are contractible by \cref{thm:contr_equiv}.
\end{proof}

For the remainder of this subsection we consider a pushout square
\begin{equation*}
\begin{tikzcd}
S \arrow[r,"g"] \arrow[d,swap,"f"] & B \arrow[d,"j"] \\
A \arrow[r,swap,"i"] & X.
\end{tikzcd}
\end{equation*}
with $H:i\circ f\htpy j\circ g$, descent data
\begin{align*}
P_A & : A \to \UU \\
P_B & : B \to \UU \\
P_S & : \prd{x:S} \eqv{P_A(f(x))}{P_B(g(x))},
\end{align*}
and a family $P:X\to\UU$ equipped with
\begin{align*}
e_A & : \prd{a:A}\eqv{P_A(a)}{P(i(a))} \\
e_B & : \prd{b:B}\eqv{P_B(a)}{P(j(b))}
\end{align*}
and a homotopy $e_S$ witnessing that the square
\begin{equation*}
\begin{tikzcd}[column sep=huge]
P_A(f(x)) \arrow[r,"e_A(f(x))"] \arrow[d,swap,"P_S(x)"] & P(i(f(x))) \arrow[d,"\mathsf{tr}_P(H(x))"] \\
P_B(g(x)) \arrow[r,swap,"e_B(g(x))"] & P(j(g(x)))
\end{tikzcd}
\end{equation*}
commutes.

\begin{defn}
We define the commuting square
\begin{equation*}
\begin{tikzcd}[column sep=6em]
\sm{x:S}P_A(f(x)) \arrow[d,swap,"{f'\,\defeq\,\total[f]{\lam{x}\idfunc[P_A(f(x))]}}"] \arrow[r,"{g'\,\defeq\, \total[g]{e_S}}"] & \sm{b:B}P_B(b) \arrow[d,"{j'\,\defeq\, \total[j]{e_B}}"] \\
\sm{a:A}P_A(a) \arrow[r,swap,"{i'\, \defeq\, \total[i]{e_A}}"] & \sm{x:X}P(x)
\end{tikzcd}
\end{equation*}
with the homotopy $H':i'\circ f'\htpy j'\circ g'$ defined as
\begin{equation*}
\lam{(x,y)}\mathsf{eq\usc{}pair}(H(x),e_S(x,y)^{-1}).
\end{equation*}
Furthermore, we will write $\mathcal{S'}$ for the span
\begin{equation*}
\begin{tikzcd}
\sm{a:A}P_A(a) & \sm{x:S}P_A(f(x)) \arrow[l,swap,"{f'}"] \arrow[r,"{g'}"] & \sm{b:B}P_B(b).
\end{tikzcd}
\end{equation*}
\end{defn}

We now state the flattening lemma for pushouts, which should be compared to the flattening lemma for coequalizers, stated in Lemma 6.12.2 of \cite{hottbook}. We note that, using the dependent universal property of pushouts, our proof is substantially shorter.

\begin{lem}[The flattening lemma]\label{lem:flattening}
The commuting square\index{flattening lemma!for pushouts|textit}
\begin{equation*}
\begin{tikzcd}
\sm{x:S}P_A(f(x)) \arrow[d,swap,"{f'}"] \arrow[r,"{g'}"] & \sm{b:B}P_B(b) \arrow[d,"{j'}"] \\
\sm{a:A}P_A(a) \arrow[r,swap,"{i'}"] & \sm{x:X}P(x)
\end{tikzcd}
\end{equation*}
is a pushout square.
\end{lem}

\begin{proof}
Note that we have a commuting cube
\begin{equation*}
\begin{tikzcd}[row sep=large]
& Y^{\sm{x:X}P(x)} \arrow[dl] \arrow[d,"\mathsf{ev\usc{}pair}"] \arrow[dr] \\
Y^{\sm{a:A}P_A(a)} \arrow[d,swap,"\mathsf{ev\usc{}pair}"] & \prd{x:X}Y^{P(x)} \arrow[dl] \arrow[dr] & Y^{\sm{b:B}P_B(b)} \arrow[dl,crossing over] \arrow[d,"\mathsf{ev\usc{}pair}"] \\
\prd{a:A}Y^{P_A(a)} \arrow[dr] & Y^{\sm{x:S}P_A(f(x))} \arrow[from=ul,crossing over] \arrow[d,swap,"\mathsf{ev\usc{}pair}"] & \prd{b:B}Y^{P_B(b)} \arrow[dl] \\
\phantom{\prd{b:B}Y^{P_B(b)}} & \prd{x:S}Y^{P_A(f(x))} & \phantom{\prd{a:A}Y^{P_A(a)}}
\end{tikzcd}
\end{equation*}
for any type $Y$. In this cube, the bottom square is a pullback square by property (iii) of \cref{thm:pushout_up}. The vertical maps (of the form $\mathsf{ev\usc{}pair}$) are equivalences, so it follows that the top square is a pullback square. We conclude that $\sm{x:X}P(x)$ is a pushout.
\end{proof}

\subsection{The descent property for pushouts}

\begin{defn}
Consider a span $\mathcal{S}$ from $A$ to $B$, and a span $\mathcal{S}'$ from $A'$ to $B'$. A \define{cartesian transformation} of spans\index{cartesian transformation!of spans|textbf} from $\mathcal{S}'$ to $\mathcal{S}$ is a tuple
\begin{equation*}
(h_A,h_S,h_B,F,G,p_f,p_g)
\end{equation*}
consisting of maps $h_A:A'\to A$, $h_S:S'\to S$, and $h_B:B'\to B$, as indicated in the diagram
\begin{equation*}
\begin{tikzcd}
A' \arrow[d,swap,"h_A"]  & S' \arrow[l,swap,"{f'}"] \arrow[r,"{g'}"] \arrow[d,swap,"h_S"] & B' \arrow[d,"h_B"] \\
A & S \arrow[l,"f"] \arrow[r,swap,"g"] & B,
\end{tikzcd}
\end{equation*}
with homotopies $F:f\circ h_S\htpy h_A\circ f'$ and $G:g\circ h_S\htpy h_B\circ g'$, satisfying the conditions
\begin{align*}
p_f & : \mathsf{is\usc{}pullback}(h_S,f',F) \\
p_g & : \mathsf{is\usc{}pullback}(h_S,g',G)
\end{align*}
that both squares are pullback squares. We write $\mathsf{cart}(\mathcal{S}',\mathcal{S})$\index{cart(S,S')@{$\mathsf{cart}(\mathcal{S},\mathcal{S}')$}|textbf} for the type of cartesian transformations from $\mathcal{S}'$ to $\mathcal{S}$, and we write
\begin{equation*}
\mathsf{Cart}(\mathcal{S}) \defeq \sm{A',B':\UU}{\mathcal{S}':\mathsf{span}(A',B')}\mathsf{cart}(\mathcal{S}',\mathcal{S}).
\end{equation*}
\end{defn}

Given descent data $(P_A,P_B,P_S)$ on a span $\mathcal{S}$ from $A$ to $B$, we obtain a cartesian transformation
\begin{equation*}
\begin{tikzcd}[column sep=large]
\sm{a:A}P_A(a) \arrow[d,swap,"\proj 1"] & \sm{x:S}P_A(f(x)) \arrow[d,swap,"\proj 1"] \arrow[l,swap,"{\total[f]{\idfunc}}"] \arrow[r,"{\total[g]{P_S}}"] & \sm{b:B}P_B(b) \arrow[d,"\proj 1"] \\
A & S \arrow[l,"f"] \arrow[r,swap,"g"] & B
\end{tikzcd}
\end{equation*}
with the canonical homotopies witnessing that the squares commute. Note that both the left and right commuting squares are pullback squares by \cref{thm:pb_fibequiv}. Thus we obtain an operation
\begin{equation*}
\mathsf{cart\usc{}desc}_{\mathcal{S}}:\mathsf{Desc}(\mathcal{S})\to \mathsf{Cart}(\mathcal{S}).
\end{equation*}

\begin{lem}\label{lem:cart_desc}
For any span $\mathcal{S}$, the operation\index{cart_desc@{$\mathsf{cart\usc{}desc}_{\mathcal{S}}$}|textit}
\begin{equation*}
\mathsf{cart\usc{}desc}_{\mathcal{S}}:\mathsf{Desc}(\mathcal{S})\to \mathsf{Cart}(\mathcal{S})
\end{equation*}
is an equivalence.
\end{lem}

\begin{proof}
Note that by \cref{thm:pb_fibequiv_complete} it follows that the types of triples $(f',F,p_f)$ and $(g',G,p_g)$ are equivalent to the types of fiberwise equivalences
\begin{align*}
& \prd{x:S}\eqv{\fib{h_S}{x}}{\fib{h_A}{f(x)}} \\
& \prd{x:S}\eqv{\fib{h_S}{x}}{\fib{h_B}{g(x)}}
\end{align*}
respectively. Furthermore, by \cref{thm:fam_proj} the types of pairs $(S',h_S)$, $(A',h_A)$, and $(B',h_B)$ are equivalent to the types $S\to \UU$, $A\to \UU$, and $B\to \UU$, respectively. Therefore it follows that the type $\mathsf{Cart}(\mathcal{S})$ is equivalent to the type of tuples $(Q,P_A,\varphi,P_B,P_S)$ consisting of
\begin{align*}
Q & : S\to \UU \\
P_A & : A \to \UU \\
P_B & : B \to \UU \\
\varphi & : \prd{x:S}\eqv{Q(x)}{P_A(f(x))} \\
P_S & : \prd{x:S}\eqv{Q(x)}{P_B(g(x))}.
\end{align*}
However, the type of $\varphi$ is equivalent to the type $P_A\circ f=Q$. Thus we see that the type of pairs $(Q,\varphi)$ is contractible, so our claim follows.
\end{proof}

\begin{defn}
Consider a commuting square
\begin{equation*}
\begin{tikzcd}
S \arrow[r,"g"] \arrow[d,swap,"f"] & B \arrow[d,"j"] \\
A \arrow[r,swap,"i"] & X
\end{tikzcd}
\end{equation*}
with $H:i\circ f\htpy j\circ g$.
We define an operation\index{cart map!{$\mathsf{cart\usc{}map}_{\mathcal{S}}$}|textbf}
\begin{equation*}
\mathsf{cart\usc{}map}_{\mathcal{S}}:{\Big(\sm{X':\UU}X'\to X\Big)}\to \mathsf{Cart}(\mathcal{S}).
\end{equation*}
\end{defn}

\begin{proof}[Construction]
Let $X':\UU$ and $h_X:X'\to X$. Then we define $A'$, $B'$, and $S'$ as the pullbacks
\begin{align*}
A' & \defeq A\times_X X' \\
B' & \defeq B\times_X X' \\
S' & \defeq S\times_A A',
\end{align*}
resulting in a diagram of the form
\begin{equation*}
\begin{tikzcd}
& S' \arrow[dl] \arrow[dr,densely dotted] \arrow[d] \\
A' \arrow[d] & S \arrow[dl] \arrow[dr] & B' \arrow[dl,crossing over] \arrow[d] \\
A \arrow[dr] & X' \arrow[d] \arrow[from=ul,crossing over] & B \arrow[dl] \\
& X
\end{tikzcd}
\end{equation*}
By the universal property of $B'$ it follows that there is a unique map $g':S'\to B'$ making the cube commute.
Moreover, since the two front squares and the back left squares are pullback squares by construction, it follows by \cref{thm:pb_pasting} that also the back right square is a pullback square. Thus we obtain a cartesian transformation of spans.
\end{proof}

The following theorem is analogous to \cref{thm:desc_fam}.

\begin{thm}[The descent theorem for pushouts]\label{thm:descent}\index{descent theorem!for pushouts|textit}
Consider a commuting square
\begin{equation*}
\begin{tikzcd}
S \arrow[r,"g"] \arrow[d,swap,"f"] & B \arrow[d,"j"] \\
A \arrow[r,swap,"i"] & X
\end{tikzcd}
\end{equation*}
with $H:i\circ f\htpy j\circ g$.
If this square is a pushout square, then the operation $\mathsf{cart\usc{}map}_{\mathcal{S}}$\index{cart map!{$\mathsf{cart\usc{}map}_{\mathcal{S}}$}!is an equivalence|textit} is an equivalence
\begin{equation*}
\eqv{\Big(\sm{X':\UU}X'\to X\Big)}{\mathsf{Cart}(\mathcal{S})}
\end{equation*}
\end{thm}

\begin{proof}
It suffices to show that the square
\begin{equation*}
\begin{tikzcd}[column sep=huge]
\UU^X \arrow[r,"{\mathsf{desc\usc{}fam}_{\mathcal{S}}(i,j,H)}"] \arrow[d,swap,"\mathsf{map\usc{}fam}_X"] & \mathsf{Desc}(\mathcal{S}) \arrow[d,"\mathsf{cart\usc{}desc}_{\mathcal{S}}"] \\
\sm{X':\UU}X^{X'} \arrow[r,swap,"\mathsf{cart\usc{}map}_{\mathcal{S}}"] & \mathsf{Cart}(\mathcal{S})
\end{tikzcd}
\end{equation*}
commutes. To see that this suffices, note that the operation $\mathsf{map\usc{}fam}_X$ is an equivalence by \cref{thm:fam_proj}, the operation $\mathsf{desc\usc{}fam}_{\mathcal{S}}(i,j,H)$ is an equivalence by \cref{thm:desc_fam}, and the operation $\mathsf{cart\usc{}desc}_{\mathcal{S}}$ is an equivalence by \cref{lem:cart_desc}.

To see that the square commutes, note that the composite
\begin{equation*}
\mathsf{cart\usc{}map}_{\mathcal{S}}\circ \mathsf{map\usc{}fam}_X
\end{equation*}
takes a family $P:X\to \UU$ to the cartesian transformation of spans
\begin{equation*}
\begin{tikzcd}
A\times_X\tilde{P} \arrow[d,swap,"\pi_1"] & S\times_A\Big(A\times_X\tilde{P}\Big) \arrow[l] \arrow[r] \arrow[d,swap,"\pi_1"] & B\times_X\tilde{P} \arrow[d,"\pi_1"] \\
A & S \arrow[l] \arrow[r] & B,
\end{tikzcd}
\end{equation*}
where $\tilde{P}\defeq\sm{x:X}P(x)$.

The composite
\begin{equation*}
\mathsf{cart\usc{}desc}_{\mathcal{S}}\circ \mathsf{desc\usc{}fam}_X
\end{equation*}
takes a family $P:X\to \UU$ to the cartesian transformation of spans
\begin{equation*}
\begin{tikzcd}
\sm{a:A}P(i(a)) \arrow[d] & \sm{s:S}P(i(f(s))) \arrow[l] \arrow[r] \arrow[d] & \sm{b:B}P(j(b)) \arrow[d] \\
A & S \arrow[l] \arrow[r] & B
\end{tikzcd}
\end{equation*}
These cartesian natural transformations are equal by \cref{thm:pb_fibequiv}.
\end{proof}

Since $\mathsf{cart\usc{}map}_{\mathcal{S}}$ is an equivalence it follows that its fibers are contractible.

\begin{cor}\label{cor:descent_fib}
Consider a diagram of the form
\begin{equation*}
\begin{tikzcd}
& S' \arrow[d,swap,"h_S"] \arrow[dl,swap,"{f'}"] \arrow[dr,"{g'}"] \\
A' \arrow[d,swap,"h_A"] & S \arrow[dl,swap,"f"] \arrow[dr,"g"] & B' \arrow[d,"{h_B}"] \\
A \arrow[dr,swap,"i"] & & B \arrow[dl,"j"] \\
& X
\end{tikzcd}
\end{equation*}
with homotopies
\begin{align*}
F & : f\circ h_S \htpy h_A\circ f' \\
G & : g\circ h_S \htpy h_B\circ g' \\
H & : i\circ f \htpy j\circ g,
\end{align*}
and suppose that the bottom square is a pushout square, and the top squares are pullback squares.
Then the type of tuples $((X',h_X),(i',I,p),(j',J,q),(H',C))$ consisting of
\begin{enumerate}
\item A type $X':\UU$ together with a morphism
\begin{equation*}
h_X : X'\to X,
\end{equation*}
\item A map $i':A'\to X'$, a homotopy $I:i\circ h_A\htpy h_X\circ i'$, and a term $p$ witnessing that the square
\begin{equation*}
\begin{tikzcd}
A' \arrow[d,swap,"h_A"] \arrow[r,"{i'}"] & X' \arrow[d,"h_X"] \\
A \arrow[r,swap,"i"] & X
\end{tikzcd}
\end{equation*}
is a pullback square.
\item A map $j':B'\to X'$, a homotopy $J:j\circ h_B\htpy h_X\circ j'$, and a term $q$ witnessing that the square
\begin{equation*}
\begin{tikzcd}
B' \arrow[d,swap,"h_B"] \arrow[r,"{j'}"] & X' \arrow[d,"h_X"] \\
B \arrow[r,swap,"j"] & X
\end{tikzcd}
\end{equation*}
is a pullback square,
\item A homotopy $H':i'\circ f'\htpy j'\circ g'$, and a homotopy
\begin{equation*}
C : \ct{(i\cdot F)}{(\ct{(I\cdot f')}{(h_X\cdot H')})} \htpy \ct{(H\cdot h_S)}{(\ct{(j\cdot G)}{(J\cdot g')})}
\end{equation*}
witnessing that the cube
\begin{equation*}
\begin{tikzcd}
& S' \arrow[dl] \arrow[dr] \arrow[d] \\
A' \arrow[d] & S \arrow[dl] \arrow[dr] & B' \arrow[dl,crossing over] \arrow[d] \\
A \arrow[dr] & X' \arrow[d] \arrow[from=ul,crossing over] & B \arrow[dl] \\
& X,
\end{tikzcd}
\end{equation*}
commutes,
\end{enumerate}
is contractible.
\end{cor}

The following theorem should be compared to the flattening lemma, \cref{lem:flattening}.\index{flattening lemma!for pushouts}

\begin{thm}\label{cor:descent}
Consider a commuting cube
\begin{equation*}
\begin{tikzcd}
& S' \arrow[dl,swap,"{f'}"] \arrow[dr,"{g'}"] \arrow[d,"h_S"] \\
A' \arrow[d,swap,"h_A"] & S \arrow[dl,swap,"f" near start] \arrow[dr,"g" near start] & B' \arrow[dl,crossing over,"{j'}" near end] \arrow[d,"h_B"] \\
A \arrow[dr,swap,"i"] & X' \arrow[d,"h_X" near start] \arrow[from=ul,crossing over,"{i'}"' near end] & B \arrow[dl,"j"] \\
& X
\end{tikzcd}
\end{equation*}
in which the bottom square is a pushout, and the two vertical squares in the back are pullbacks. Then the following are equivalent:
\begin{enumerate}
\item The two vertical squares in the front are pullback squares.
\item The top square is a pushout square.
\end{enumerate}
\end{thm}

\begin{proof}
By \cref{cor:pb_fibequiv} we have fiberwise equivalences
\begin{align*}
F & : \prd{x:S}\eqv{\fib{h_S}{x}}{\fib{h_A}{f(x)}} \\
G & : \prd{x:S}\eqv{\fib{h_S}{x}}{\fib{h_B}{g(x)}} \\
I & : \prd{a:A}\eqv{\fib{h_A}{a}}{\fib{h_X}{i(a)}} \\
J & : \prd{b:B}\eqv{\fib{h_B}{b}}{\fib{h_X}{j(b)}}.
\end{align*}
Moreover, since the cube commutes we obtain a fiberwise homotopy
\begin{equation*}
K : \prd{x:S} I(f(x))\circ F(x) \htpy J(g(x))\circ G(x).
\end{equation*}
We define the descent data $(P_A,P_B,P_S)$ consisting of $P_A:A\to\UU$, $P_B:B\to\UU$, and $P_S:\prd{x:S}\eqv{P_A(f(x))}{P_B(g(x))}$ by
\begin{align*}
P_A(a) & \defeq \fib{h_A}{a} \\
P_B(b) & \defeq \fib{h_B}{b} \\
P_S(x) & \defeq G(x)\circ F(x)^{-1}.
\end{align*}
We have
\begin{align*}
P & \defeq \fibf{h_X} \\
e_A & \defeq I \\
e_B & \defeq J \\
e_S & \defeq K.
\end{align*}
Now consider the diagram
\begin{equation*}
\begin{tikzcd}
\sm{s:S}\fib{h_S}{s} \arrow[r] \arrow[d] & \sm{s:S}\fib{h_A}{f(s)} \arrow[r] \arrow[d] & \sm{b:B}\fib{h_B}{b} \arrow[d] \\
\sm{a:A}\fib{h_A}{a} \arrow[r] & \sm{a:A}\fib{h_A}{a} \arrow[r] & \sm{x:X}\fib{h_X}{x}
\end{tikzcd}
\end{equation*}
Since the top and bottom map in the left square are equivalences, we obtain that the left square is a pushout square. Moreover, the right square is a pushout by \cref{lem:flattening}. Therefore it follows by \cref{thm:pushout_pasting} that the outer rectangle is a pushout square.

Now consider the commuting cube
\begin{equation*}
\begin{tikzcd}
& \sm{s:S}\fib{h_S}{s} \arrow[dl] \arrow[dr] \arrow[d] \\
\sm{a:A}\fib{h_A}{a} \arrow[d] & S' \arrow[dl] \arrow[dr] & \sm{b:B}\fib{h_B}{b} \arrow[dl,crossing over] \arrow[d] \\
A' \arrow[dr,swap] & \sm{x:X}\fib{h_X}{x} \arrow[d] \arrow[from=ul,crossing over] & B' \arrow[dl] \\
& X'.
\end{tikzcd}
\end{equation*}
We have seen that the top square is a pushout. The vertical maps are all equivalences, so the vertical squares are all pushout squares. Thus it follows from one more application of \cref{thm:pushout_pasting} that the bottom square is a pushout.
\end{proof}

\begin{thm}\label{thm:cartesian_cube}
Consider a commuting cube of types
\begin{equation*}\label{eq:cube}
\begin{tikzcd}
& S' \arrow[dl] \arrow[dr] \arrow[d] \\
A' \arrow[d] & S \arrow[dl] \arrow[dr] & B' \arrow[dl,crossing over] \arrow[d] \\
A \arrow[dr] & X' \arrow[d] \arrow[from=ul,crossing over] & B \arrow[dl] \\
& X,
\end{tikzcd}
\end{equation*}
and suppose the two vertical squares in the back are pullback squares. Then the following are equivalent:
\begin{enumerate}
\item The two vertical squares in the front are pullback squares.
\item The commuting square
\begin{equation*}
\begin{tikzcd}
A' \sqcup^{S'} B' \arrow[r] \arrow[d] & X' \arrow[d] \\
A\sqcup^{S} B \arrow[r] & X
\end{tikzcd}
\end{equation*}
is a pullback square.
\end{enumerate}
\end{thm}

\begin{proof}
To see that (i) implies (ii), it suffices to show that the pullback
\begin{equation*}
(A\sqcup^{S} B)\times_{X}X'
\end{equation*}
has the universal property of the pushout. This follows by the descent theorem, since the vertical squares in the cube
\begin{equation*}
\begin{tikzcd}
& S' \arrow[dl] \arrow[dr] \arrow[d] \\
A' \arrow[d] & S \arrow[dl] \arrow[dr] & B' \arrow[dl,crossing over] \arrow[d] \\
A \arrow[dr] & (A\sqcup^{S} B)\times_{X}X' \arrow[d] \arrow[from=ul,crossing over] & B \arrow[dl] \\
& A\sqcup^{S} B
\end{tikzcd}
\end{equation*}
are pullback squares by \cref{thm:pb_pasting}.

To prove that (ii) implies (i), we note that in the cube
\begin{equation*}
\begin{tikzcd}
& S' \arrow[dl] \arrow[dr] \arrow[d] \\
A' \arrow[d] & S \arrow[dl] \arrow[dr] & B' \arrow[dl,crossing over] \arrow[d] \\
A \arrow[dr] & A'\sqcup^{S'}B' \arrow[d] \arrow[from=ul,crossing over] & B \arrow[dl] \\
& A\sqcup^S B,
\end{tikzcd}
\end{equation*}
the two back squares are pullback squares, and the top and bottom squares are pushout squares. Therefore it follows from \cref{cor:descent} that the two front squares are pullback squares. Now we obtain (i) from the pasting lemma for pushouts.
\end{proof}

\section{Applications of the descent theorem for pushouts}
\sectionmark{Applications of the descent theorem}

\subsection{Fiber sequences}

\begin{defn}
A \define{pointed type} is a pair $(X,x)$ consisting of a type $X$ equipped with a \define{base point} $x:X$. We will write $\UU_\ast$ for the type $\sm{X:\UU}X$ of all pointed types.
\end{defn}

In the following lemma we characterize the identity type of $\UU_\ast$.

\begin{lem}\label{lem:equiv_of_ptdtype}
For any $(A,a),(B,b):\UU_\ast$ we have an equivalence
\begin{equation*}
\eqv{\Big(\pairr{A,a}=\pairr{B,b}\Big)}{\Big(\sm{e:\eqv{A}{B}}e(a)=b\Big)}.
\end{equation*}
\end{lem}

\begin{proof}[Construction]
By \cref{thm:eq_sigma} the type on the left hand side is
equivalent to the type $\sm{p:A=B}\tr_{\universalfam}({p},{a})=b$.
By the univalence axiom, the map
\begin{equation*}
\equiveq_{A,B}:(A=B)\to (\eqv{A}{B})
\end{equation*}
is an equivalence for each $B:\UU$.
Therefore, we have an equivalence of type
\begin{equation*}
\eqv{\Big(\sm{p:A=B}\tr_{\universalfam}({p},{a})=b\Big)}{\Big(\sm{e:\eqv{A}{B}}\tr_{\universalfam}({\eqequiv(e)},{a})=b\Big)}
\end{equation*}
Moreover, by equivalence induction (the analogue of path induction for
equivalences), we can compute the transport:
\begin{equation*}
\tr_{\universalfam}({\eqequiv(e)},{a})=e(a).
\end{equation*}
It follows that $\eqv{(\tr_{\universalfam}({\eqequiv(e)},{a})=b)}
{(e(a)=b)}$.
\end{proof}

\begin{defn}
\begin{enumerate}
\item Let $(X,\ast_X)$ be a pointed type. A \define{pointed family} over $(X,\ast_X)$ consists of a type family $P:X\to \UU$ equipped with a base point $\ast_P:P(\ast_X)$.
\item Let $(P,\ast_P)$ be a pointed family over $(X,\ast_X)$. A \define{pointed section} of $(P,\ast_P)$ consists of a dependent function $f:\prd{x:X}P(x)$ and an identification $p:f(\ast_X)=\ast_P$. We define the \define{pointed $\Pi$-type} to be the type of pointed sections:
\begin{equation*}
\Pi^\ast_{(x:X)}P(x) \defeq \sm{f:\prd{x:X}P(x)}f(\ast_X)=\ast_P
\end{equation*}
In the case of two pointed types $X$ and $Y$, we may also view $Y$ as a pointed family over $X$. In this case we write $X\to_\ast Y$ for the type of pointed functions.
\item Given any two pointed sections $f$ and $g$ of a pointed family $P$ over $X$, we define the type of pointed homotopies
\begin{equation*}
f\htpy_\ast g \defeq \Pi^\ast_{(x:X)} f(x)=g(x),
\end{equation*}
where the family $x\mapsto f(x)=g(x)$ is equipped with the base point $\ct{p}{q^{-1}}$.
\end{enumerate}
\end{defn}

\begin{defn}
\begin{enumerate}
\item For any pointed type $X$, we define the \define{pointed identity function} $\idp_X\defeq (\idfunc[X],\refl{\ast})$.
\item For any two pointed maps $f:X\to_\ast Y$ and $g:Y\to_\ast Z$, we define the \define{pointed composite}
\begin{equation*}
g\mathbin{\circ_\ast} f \defeq (g\circ f,\ct{\ap{g}{p_f}}{p_g}).
\end{equation*}
\end{enumerate}
\end{defn}

\begin{defn}
Let $X$ be a pointed type with base point $x$. We define the \define{loop space} $\loopspace{X,x}$ of $X$ at $x$ to be the pointed type $x=x$ with base point $\refl{x}$.
\end{defn}

\begin{defn}
The loop space operation $\loopspacesym$ is \emph{functorial} in the sense that
\begin{enumerate}
\item For every pointed map $f:X\to_\ast Y$ there is a pointed map
\begin{equation*}
\loopspace{f}:\loopspace{X}\to_\ast \loopspace{Y},
\end{equation*}
defined by $\loopspace{f}(\omega)\defeq \ct{p_f}{\ap{f}{\omega}}{p_f^{-1}}$, which is base point preserving by $\rightinv(p_f)$.
\item For every pointed type $X$ there is a pointed homotopy
\begin{equation*}
\loopspace{\idp_X}\htpy_\ast \idp_{\loopspace{X}}.
\end{equation*}
\item For any two pointed maps $f:X\to_\ast Y$ and $g:Y\to_\ast X$, there is a pointed homotopy witnessing that the triangle
\begin{equation*}
\begin{tikzcd}
& \loopspace{Y} \arrow[dr,"\loopspace{g}"] \\
\loopspace{X} \arrow[rr,swap,"\loopspace{g\circ_\ast f}"] \arrow[ur,"\loopspace{f}"] & & \loopspace{Z}
\end{tikzcd}
\end{equation*}
of pointed types commutes.
\end{enumerate}
\end{defn}

\begin{lem}\label{lem:equiv_of_ptdequiv}
For any $\pairr{e,p},\pairr{f,q}:\sm{e:\eqv{A}{B}}e(a)=b$, we have an equivalence of type
\begin{equation*}
\eqv{\Big(\pairr{e,p}=\pairr{f,q}\Big)}{\Big(\sm{h:e\htpy f} p=\ct{h(a)}{q}\Big)}.
\end{equation*}
\end{lem}

\begin{proof}[Construction]
The type $\pairr{e,p}=\pairr{f,q}$ is equivalent
to the type $\sm{h:e=f}\tr({h},{p})=q$.
Note that by the principle of function extensionality,
the map $\htpyeq:(e=f)\to(e\htpy f)$
is an equivalence. Furthermore, it follows by homotopy induction that for any
$h:e\htpy f$ we have an equivalence of type
\begin{equation*}
\eqv{(\tr({\eqhtpy(h)},{p})=q)}
    {(p= \ct{h(a)}{q})}.\qedhere
\end{equation*}
\end{proof}

\begin{defn}
A \define{fiber sequence} $F\hookrightarrow E \twoheadrightarrow B$ consists of:
\begin{enumerate}
\item Pointed types $F$, $E$, and $B$, with base points $x_0$, $y_0$, and $b_0$ respectively,
\item Base point preserving maps $i:F\to_\ast E$ and $p:E\to_\ast B$, with $\alpha:i(x_0)=y_0$ and $\beta:p(y_0)=b_0$,
\item A pointed homotopy $H:\const_{b_0}\htpy_\ast p\circ_\ast i$ witnessing that the square
\begin{equation*}
\begin{tikzcd}
F \arrow[r,"i"] \arrow[d] & E \arrow[d,"p"] \\
\unit \arrow[r,swap,"\const_{b_0}"] & B,
\end{tikzcd}
\end{equation*}
commutes and is a pullback square.
\end{enumerate}
We will write $\FibSeq$ for the type of all fiber sequences in $\UU$.
\end{defn}

\begin{prp}
The type of all fiber sequences is equivalent to the type
\begin{equation*}
\sm{(B,b):\UU_\ast}{P:B\to\UU}P(b).
\end{equation*}
\end{prp}

\subsection{Fiber sequences obtained by the descent property}

\begin{defn}
Let $f:A\to B$ be a map. The \define{codiagonal}\index{codiagonal}\index{nabla@{$\nabla_f$}} $\nabla_f$ of $f$ is the map obtained from the universal property of the pushout, as indicated in the diagram
\begin{equation*}
\begin{tikzcd}
A \arrow[d,swap,"f"] \arrow[r,"f"] \arrow[dr, phantom, "\ulcorner", very near end] & B \arrow[d,"\inr"] \arrow[ddr,bend left=15,"{\idfunc[B]}"] \\
B \arrow[r,"\inl"] \arrow[drr,bend right=15,swap,"{\idfunc[B]}"] & B\sqcup^{A} B \arrow[dr,densely dotted,near start,swap,"\nabla_f"] \\
& & B
\end{tikzcd}
\end{equation*}
\end{defn}

\begin{prp}
For any map $f:A\to B$ and any $y:B$, there is an equivalence $\eqv{\fib{\nabla_f}{y}}{\susp(\fib{f}{y})}$.
\end{prp}

\begin{proof}
For any $b:B$ we have the commuting cube
\begin{equation*}
\begin{tikzcd}
& \fib{f}{b} \arrow[dl] \arrow[d] \arrow[dr] \\
\unit \arrow[d] & A \arrow[dl] \arrow[dr] & \unit \arrow[dl,crossing over] \arrow[d] \\
B \arrow[dr] & \unit \arrow[from=ul,crossing over] \arrow[d,swap,"b"] & B \arrow[dl] \\
& B
\end{tikzcd}
\end{equation*}
of which the vertical sides are pullback squares. Hence we obtain the pullback square
\begin{equation*}
\begin{tikzcd}
\susp{\fib{f}{b}} \arrow[r] \arrow[d] & \unit \arrow[d,"b"] \\
B\sqcup^{A} B \arrow[r] & B
\end{tikzcd}
\end{equation*}
from \cref{thm:cartesian_cube}, from which the claim follows.
\end{proof}

\begin{defn}
Consider two maps $f:A\to B$ and $g:C\to D$.
The \define{pushout-product}\index{pushout-product}
\begin{equation*}
f\square g : (A\times D)\sqcup^{A\times C} (B\times C)\to B\times D
\end{equation*}
of $f$ and $g$ is defined by the universal property of the pushout as the unique map rendering the diagram
\begin{equation*}
\begin{tikzcd}
A\times C \arrow[r,"{f\times \idfunc[C]}"] \arrow[d,swap,"{\idfunc[A]\times g}"] & B\times C \arrow[d,"\inr"] \arrow[ddr,bend left=15,"{\idfunc[B]\times g}"] \\
A\times D \arrow[r,"\inl"] \arrow[drr,bend right=15,swap,"{f\times\idfunc[D]}"] & (A\times D)\sqcup^{A\times C} (B\times C) \arrow[dr,densely dotted,swap,near start,"f\square g"] \\
& & B\times D
\end{tikzcd}
\end{equation*}
commutative.
\end{defn}

\begin{prp}
For any two maps $f:A\to B$ and $g:C\to D$, and any $(b,d):B\times D$, there is an equivalence
\begin{equation*}
\eqv{\fib{f\square g}{b,d}}{\join{\fib{f}{b}}{\fib{g}{d}}}.
\end{equation*}
\end{prp}

\begin{proof}
Let $b:B$ and $d:D$. Then we have the commuting cube
\begin{equation*}
\begin{tikzcd}
& \fib{f}{b}\times \fib{g}{d} \arrow[dl] \arrow[d] \arrow[dr] \\
\fib{f}{b} \arrow[d] & A\times C \arrow[dl] \arrow[dr] & \fib{g}{d} \arrow[dl,crossing over] \arrow[d] \\
A\times D \arrow[dr] & \unit \arrow[from=ul,crossing over] \arrow[d] & B\times C \arrow[dl] \\
& B\times D
\end{tikzcd}
\end{equation*}
of which the vertical sides are pullback squares. Hence the claim follows from \cref{thm:cartesian_cube}.
\end{proof}

\begin{defn}\label{defn:fib_join}
Let $f:A\to X$ and $g:B\to X$ be maps into $X$. We define the \define{fiberwise join} $\join[X]{A}{B}$ and the \define{join}\footnote{\emph{Warning}: By $\join{f}{g}$ we do \emph{not} mean the functorial action of the
join, applied to $(f,g)$.} $\join{f}{g}:\join[X]{A}{B}\to X$ of
$f$ and $g$, as indicated in the following diagram:
\begin{equation*}
\begin{tikzcd}
A\times_X B \arrow[r,"\pi_2"] \arrow[d,swap,"\pi_1"] \arrow[dr, phantom, "{\ulcorner}", at end] & B \arrow[d,"\inr"] \arrow[ddr,bend left=15,"g"] \\
A \arrow[r,swap,"\inl"] \arrow[drr,bend right=15,swap,"f"] & \join[X]{A}{B} \arrow[dr,densely dotted,swap,near start,"\join{f}{g}" xshift=1ex] \\
& & X.
\end{tikzcd}
\end{equation*}
\end{defn}

\begin{thm}\label{defn:join-fiber}
Let $f:A\to X$ and $g:B\to X$ be maps into $X$, and let $x:X$. Then there is
an equivalence
\begin{equation*}
\eqv{\fib{\join{f}{g}}{x}}{\join{\fib{f}{x}}{\fib{g}{x}}}.
\end{equation*}
\end{thm}

\begin{proof}
We have the following commuting cube
\begin{equation*}
\begin{tikzcd}
& \fib{f}{x}\times\fib{g}{x} \arrow[dl] \arrow[d] \arrow[dr] \\
\fib{f}{x} \arrow[d] & A\times_X B \arrow[dl] \arrow[dr] & \fib{g}{x} \arrow[dl,crossing over] \arrow[d] \\
A \arrow[dr] & \unit \arrow[d] \arrow[from=ul,crossing over] & B \arrow[dl] \\
& X
\end{tikzcd}
\end{equation*}
in which the vertical squares are pullback squares. Therefore it follows by \cref{thm:cartesian_cube} that the square
\begin{equation*}
\begin{tikzcd}
\join{\fib{f}{x}}{\fib{g}{x}} \arrow[r] \arrow[d] & \unit \arrow[d] \\
\join[X]{A}{B} \arrow[r] & X
\end{tikzcd}
\end{equation*}
is a pullback square.
\end{proof}

\begin{rmk}
The join operation on maps with a common codomain is associative up to homotopy (this was formalized by Brunerie, see Proposition 1.8.6 of \cite{BruneriePhD}), and it is a commutative operation on the generalized elements of a type $X$. Furthermore, the unique map of type $\emptyt\to X$ is a unit for the join operation.
\end{rmk}

\begin{defn}
Let $A$ and $B$ be pointed types with base points $a_0:A$ and $b_0:B$. The \define{wedge inclusion}\index{wedge inclusion} is defined as follows by the universal property of the wedge:
\begin{equation*}
\begin{tikzcd}[column sep=huge]
\unit \arrow[r] \arrow[d] & B \arrow[d,"\inr"] \arrow[ddr,bend left=15,"{\lam{b}(a_0,b)}"] \\
A \arrow[r,"\inl"] \arrow[drr,bend right=15,swap,"{\lam{a}(a,b_0)}"] & A\vee B \arrow[dr,densely dotted,swap,"{\mathsf{wedge\usc{}in}_{A,B}}"{near start,xshift=1ex}] \\
& & A\times B
\end{tikzcd}
\end{equation*}
\end{defn}

\begin{prp}
There is a fiber sequence
\begin{equation*}
\join{\loopspace{A}}{\loopspace{B}}\hookrightarrow A\vee B\twoheadrightarrow A\times B.
\end{equation*}
\end{prp}

\begin{proof}
We have the commuting cube
\begin{equation*}
\begin{tikzcd}
& \loopspace{B}\times\loopspace{A} \arrow[dl] \arrow[d] \arrow[dr] \\
\loopspace{B} \arrow[d,swap,"\mathsf{const}_a"] & \unit \arrow[dl] \arrow[dr] & \loopspace{A} \arrow[dl,crossing over] \arrow[d,"\mathsf{const}_b"] \\
A \arrow[dr] & \unit \arrow[from=ul,crossing over] \arrow[d] & B \arrow[dl] \\
& A\times B
\end{tikzcd}
\end{equation*}
of which the vertical sides are pullback squares. Hence the claim follows from \cref{thm:cartesian_cube}.
\end{proof}

\begin{defn}
Consider a pointed type $X$. We define the map $\mathsf{fold}:X\vee X\to X$ by the universal property of the wedge as indicated in the diagram
\begin{equation*}
\begin{tikzcd}
\unit \arrow[d,swap,"x_0"] \arrow[r,"x_0"] \arrow[dr, phantom, "\ulcorner", very near end] & X \arrow[d,"\inr"] \arrow[ddr,bend left=15,"{\idfunc[X]}"] \\
X \arrow[r,"\inl"] \arrow[drr,bend right=15,swap,"{\idfunc[X]}"] & X\vee X \arrow[dr,densely dotted,near start,swap,"\mathsf{fold}"] \\
& & X.
\end{tikzcd}
\end{equation*}
\end{defn}

\begin{prp}
There is a fiber sequence
\begin{equation*}
\susp\loopspace{X} \hookrightarrow X\vee X \twoheadrightarrow X.
\end{equation*}
\end{prp}

\begin{proof}
We have the commuting cube
\begin{equation*}
\begin{tikzcd}
& \loopspace{X} \arrow[dl] \arrow[d] \arrow[dr] \\
\unit \arrow[d] & \unit \arrow[dl] \arrow[dr] & \unit \arrow[dl,crossing over] \arrow[d] \\
X \arrow[dr] & \unit \arrow[from=ul,crossing over] \arrow[d] & X \arrow[dl] \\
& X
\end{tikzcd}
\end{equation*}
of which the vertical sides are pullback squares. Hence the claim follows from \cref{thm:cartesian_cube}.
\end{proof}

\begin{rmk}
As a corollary, there are fiber sequences
\begin{align*}
\sphere{1} \hookrightarrow \rprojective{\infty}\vee \rprojective{\infty} & \twoheadrightarrow \rprojective{\infty} \\
\sphere{2} \hookrightarrow \cprojective{\infty}\vee \cprojective{\infty} & \twoheadrightarrow \cprojective{\infty}.
\end{align*}
Here we take $\rprojective{\infty}\defeq K(\Z/2,1)$ and $\cprojective{\infty}\defeq K(\Z,2)$, where the Eilenberg-Mac Lane space $K(G,n)$ is defined in \cite{FinsterLicata}.
\end{rmk}

\begin{cor}
There is a fiber sequence
\begin{equation*}
(\susp\loopspace{X})^{\vee n} \hookrightarrow X^{\vee (n+1)} \twoheadrightarrow X.
\end{equation*}
\end{cor}

\begin{defn}\label{defn:coh_hspace}
A \define{coherent H-space} consists of a type $X$ equipped with a unit $1:X$, a multiplication operation $\mu:X \to (X \to X)$ such that the function $\mu(x,\blank)$ and $\mu(\blank,y)$ are equivalences for each $x:X$ and $y:X$, respectively, and \emph{coherent} unit laws
\begin{align*}
\mathsf{right\usc{}unit} & : \prd{x: X} \mu(x,1)= x \\
\mathsf{left\usc{}unit} & : \prd{y:X} \mu(1,y) = y \\
\mathsf{coh\usc{}unit} & : \mathsf{left\usc{}unit}(1)=\mathsf{right\usc{}unit}(1).
\end{align*}
\end{defn}

The following theorem is also known as the Hopf-construction.

\begin{thm}\label{thm:hopf_construction}
For any coherent H-space $X$ there is a fiber sequence
\begin{equation*}
X \hookrightarrow \join{X}{X} \twoheadrightarrow \susp X.
\end{equation*}
The map $\eta_X:\join{X}{X}\to \susp X$ is called the \define{Hopf fibration} for $X$.
\end{thm}

\begin{proof}
We have the commuting cube
\begin{equation*}
\begin{tikzcd}
& X \times X \arrow[dl,swap,"\pi_1"] \arrow[d,swap,"\mu"] \arrow[dr,"\pi_2"] \\
X \arrow[d] & X \arrow[dl] \arrow[dr] & X \arrow[dl,crossing over] \arrow[d] \\
\unit \arrow[dr] & \join{X}{X} \arrow[from=ul,crossing over] \arrow[d,densely dotted] & \unit \arrow[dl] \\
& \susp X,
\end{tikzcd}
\end{equation*}
where the front map is obtained by the universal property of the pushout.

In this cube, the two vertical squares in the back are pullback squares by \cref{thm:pb_fibequiv}, since $\mu(\blank,y)$ and $\mu(x,\blank)$ are equivalences for every $x:X$ and $y:X$, respectively. Since the bottom and top squares are pushout squares it follows by \cref{thm:cartesian_cube} that the front two squares are pullback squares.
\end{proof}

\begin{rmk}
The classical Hopf fibration
\begin{equation*}
\begin{tikzcd}
\sphere{1} \arrow[r,hookrightarrow] & \sphere{3} \arrow[r,->>] & \sphere{2}
\end{tikzcd}
\end{equation*}
is now obtained from the fact that $\join{\sphere{1}}{\sphere{1}}\eqvsym \sphere{3}$, which was established in \cite{BruneriePhD}. Indeed, the circle is a coherent H-space.
\end{rmk}