text:
#=
    NEMO: Next Energy Modeling system for Optimization.
    https://github.com/sei-international/NemoMod.jl

    Copyright © 2021: Stockholm Environment Institute U.S.

    File description: NEMO functions for manipulating the structure of scenario databases.
=#

"""
    createnemodb(path::String;
    defaultvals::Dict{String, Float64} = Dict{String, Float64}(),
    foreignkeys::Bool = false)

Creates an empty NEMO scenario database in SQLite. If the specified database already exists, drops and
recreates NEMO tables in the database.

# Arguments
- `path::String`: Full path to the scenario database, including the file name.
- `defaultvals::Dict{String, Float64} = Dict{String, Float64}()`: Dictionary of parameter table names and default values for `val` column.
- `foreignkeys::Bool = false`: Indicates whether to create foreign keys within the database.
"""
function createnemodb(path::String; defaultvals::Dict{String, Float64} = Dict{String, Float64}(),
    foreignkeys::Bool = false)
    # Open SQLite database
    local db::SQLite.DB = SQLite.DB(path)
    logmsg("Opened SQLite database at " * path * ".")

    # Drop any default parameter views (function encloses operation in a separate SQLite transaction)
    dropdefaultviews(db::SQLite.DB)

    # BEGIN: DDL operations in an SQLite transaction.
    SQLite.DBInterface.execute(db, "BEGIN")

    # BEGIN: Drop any existing NEMO tables.
    SQLite.DBInterface.execute(db,"drop table if exists AccumulatedAnnualDemand")
    SQLite.DBInterface.execute(db,"drop table if exists AnnualEmissionLimit")
    SQLite.DBInterface.execute(db,"drop table if exists AnnualExogenousEmission")
    SQLite.DBInterface.execute(db,"drop table if exists AvailabilityFactor")
    SQLite.DBInterface.execute(db,"drop table if exists CapacityFactor")
    SQLite.DBInterface.execute(db,"drop table if exists CapacityOfOneTechnologyUnit")
    SQLite.DBInterface.execute(db,"drop table if exists CapacityToActivityUnit")
    SQLite.DBInterface.execute(db,"drop table if exists CapitalCost")
    SQLite.DBInterface.execute(db,"drop table if exists CapitalCostStorage")
    SQLite.DBInterface.execute(db,"drop table if exists Conversionld")
    SQLite.DBInterface.execute(db,"drop table if exists Conversionlh")
    SQLite.DBInterface.execute(db,"drop table if exists Conversionls")
    SQLite.DBInterface.execute(db,"drop table if exists DaySplit")
    SQLite.DBInterface.execute(db,"drop table if exists DaysInDayType")
    SQLite.DBInterface.execute(db,"drop table if exists DepreciationMethod")
    SQLite.DBInterface.execute(db,"drop table if exists DiscountRate")
    SQLite.DBInterface.execute(db,"drop table if exists DiscountRateStorage")
    SQLite.DBInterface.execute(db,"drop table if exists DiscountRateTechnology")
    SQLite.DBInterface.execute(db,"drop table if exists EmissionActivityRatio")
    SQLite.DBInterface.execute(db,"drop table if exists EmissionsPenalty")
    SQLite.DBInterface.execute(db,"drop table if exists FixedCost")
    SQLite.DBInterface.execute(db,"drop table if exists InputActivityRatio")
    SQLite.DBInterface.execute(db,"drop table if exists InterestRateStorage")
    SQLite.DBInterface.execute(db,"drop table if exists InterestRateTechnology")
    SQLite.DBInterface.execute(db,"drop table if exists LTsGroup")
    SQLite.DBInterface.execute(db,"drop table if exists MinStorageCharge")
    SQLite.DBInterface.execute(db,"drop table if exists MinimumUtilization")
    SQLite.DBInterface.execute(db,"drop table if exists ModelPeriodEmissionLimit")
    SQLite.DBInterface.execute(db,"drop table if exists ModelPeriodExogenousEmission")
    SQLite.DBInterface.execute(db,"drop table if exists NodalDistributionDemand")
    SQLite.DBInterface.execute(db,"drop table if exists NodalDistributionTechnologyCapacity")
    SQLite.DBInterface.execute(db,"drop table if exists NodalDistributionStorageCapacity")
    SQLite.DBInterface.execute(db,"drop table if exists OperationalLife")
    SQLite.DBInterface.execute(db,"drop table if exists OperationalLifeStorage")
    SQLite.DBInterface.execute(db,"drop table if exists OutputActivityRatio")
    SQLite.DBInterface.execute(db,"drop table if exists RampRate")
    SQLite.DBInterface.execute(db,"drop table if exists RampingReset")
    SQLite.DBInterface.execute(db,"drop table if exists REMinProductionTarget")
    SQLite.DBInterface.execute(db,"drop table if exists RETagFuel")
    SQLite.DBInterface.execute(db,"drop table if exists RETagTechnology")
    SQLite.DBInterface.execute(db,"drop table if exists ReserveMargin")
    SQLite.DBInterface.execute(db,"drop table if exists ReserveMarginTagFuel")
    SQLite.DBInterface.execute(db,"drop table if exists ReserveMarginTagTechnology")
    SQLite.DBInterface.execute(db,"drop table if exists ResidualCapacity")
    SQLite.DBInterface.execute(db,"drop table if exists ResidualStorageCapacity")
    SQLite.DBInterface.execute(db,"drop table if exists SpecifiedAnnualDemand")
    SQLite.DBInterface.execute(db,"drop table if exists SpecifiedDemandProfile")
    SQLite.DBInterface.execute(db,"drop table if exists StorageFullLoadHours")
    SQLite.DBInterface.execute(db,"drop table if exists StorageLevelStart")
    SQLite.DBInterface.execute(db,"drop table if exists StorageMaxChargeRate")
    SQLite.DBInterface.execute(db,"drop table if exists StorageMaxDischargeRate")
    SQLite.DBInterface.execute(db,"drop table if exists TechWithCapacityNeededToMeetPeakTS")
    SQLite.DBInterface.execute(db,"drop table if exists TechnologyFromStorage")
    SQLite.DBInterface.execute(db,"drop table if exists TechnologyToStorage")
    SQLite.DBInterface.execute(db,"drop table if exists TotalAnnualMaxCapacity")
    SQLite.DBInterface.execute(db,"drop table if exists TotalAnnualMaxCapacityStorage")
    SQLite.DBInterface.execute(db,"drop table if exists TotalAnnualMaxCapacityInvestment")
    SQLite.DBInterface.execute(db,"drop table if exists TotalAnnualMaxCapacityInvestmentStorage")
    SQLite.DBInterface.execute(db,"drop table if exists TotalAnnualMinCapacity")
    SQLite.DBInterface.execute(db,"drop table if exists TotalAnnualMinCapacityStorage")
    SQLite.DBInterface.execute(db,"drop table if exists TotalAnnualMinCapacityInvestment")
    SQLite.DBInterface.execute(db,"drop table if exists TotalAnnualMinCapacityInvestmentStorage")
    SQLite.DBInterface.execute(db,"drop table if exists TotalTechnologyAnnualActivityLowerLimit")
    SQLite.DBInterface.execute(db,"drop table if exists TotalTechnologyAnnualActivityUpperLimit")
    SQLite.DBInterface.execute(db,"drop table if exists TotalTechnologyModelPeriodActivityLowerLimit")
    SQLite.DBInterface.execute(db,"drop table if exists TotalTechnologyModelPeriodActivityUpperLimit")
    SQLite.DBInterface.execute(db,"drop table if exists TransmissionCapacityToActivityUnit")
    SQLite.DBInterface.execute(db,"drop table if exists TransmissionLine")
    SQLite.DBInterface.execute(db,"drop table if exists TransmissionModelingEnabled")
    SQLite.DBInterface.execute(db,"drop table if exists TradeRoute")
    SQLite.DBInterface.execute(db,"drop table if exists VariableCost")
    SQLite.DBInterface.execute(db,"drop table if exists YearSplit")

    SQLite.DBInterface.execute(db,"drop table if exists DefaultParams")

    SQLite.DBInterface.execute(db,"drop table if exists DAILYTIMEBRACKET")
    SQLite.DBInterface.execute(db,"drop table if exists DAYTYPE")
    SQLite.DBInterface.execute(db,"drop table if exists REGION")
    SQLite.DBInterface.execute(db,"drop table if exists YEAR")
    SQLite.DBInterface.execute(db,"drop table if exists TECHNOLOGY")
    SQLite.DBInterface.execute(db,"drop table if exists TIMESLICE")
    SQLite.DBInterface.execute(db,"drop table if exists SEASON")
    SQLite.DBInterface.execute(db,"drop table if exists STORAGE")
    SQLite.DBInterface.execute(db,"drop table if exists MODE_OF_OPERATION")
    SQLite.DBInterface.execute(db,"drop table if exists EMISSION")
    SQLite.DBInterface.execute(db,"drop table if exists FUEL")
    SQLite.DBInterface.execute(db,"drop table if exists TSGROUP1")
    SQLite.DBInterface.execute(db,"drop table if exists TSGROUP2")
    SQLite.DBInterface.execute(db,"drop table if exists NODE")

    SQLite.DBInterface.execute(db,"drop table if exists Version")
    # END: Drop any existing NEMO tables.

    # BEGIN: Add new NEMO tables.
    # Version table is for NEMO data dictionary version
    #   - 2: Added STORAGE.netzeroyear, STORAGE.netzerotg1, and STORAGE.netzerotg2
    #   - 3: Added TransmissionLine.efficiency
    #   - 4: Added TransmissionCapacityToActivityUnit.r
    #   - 5: Added RampRate and RampingReset
    #   - 6: Added MinimumUtilization, DiscountRateStorage, DiscountRateTechnology, TransmissionLine.discountrate
    #   - 7: Removed DiscountRateStorage, DiscountRateTechnology, TransmissionLine.discountrate. Added InterestRateStorage, InterestRateTechnology, TransmissionLine.interestrate
    SQLite.DBInterface.execute(db, "CREATE TABLE `Version` (`version` INTEGER, PRIMARY KEY(`version`))")
    SQLite.DBInterface.execute(db, "INSERT INTO Version VALUES(7)")

    # No default values for sets/dimensions
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `EMISSION` ( `val` TEXT NOT NULL UNIQUE, `desc` TEXT, PRIMARY KEY(`val`) )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `FUEL` ( `val` TEXT NOT NULL UNIQUE, `desc` TEXT, PRIMARY KEY(`val`) )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `MODE_OF_OPERATION` (`val` TEXT NOT NULL UNIQUE, `desc` TEXT, PRIMARY KEY(`val`))")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `REGION` (`val` TEXT NOT NULL UNIQUE, `desc` TEXT, PRIMARY KEY(`val`))")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `STORAGE` ( `val` TEXT NOT NULL UNIQUE, `desc` TEXT, `netzeroyear` INTEGER NOT NULL DEFAULT 1, `netzerotg1` INTEGER NOT NULL DEFAULT 0, `netzerotg2` INTEGER NOT NULL DEFAULT 0, PRIMARY KEY(`val`) )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `TECHNOLOGY` (`val` TEXT NOT NULL UNIQUE, `desc` TEXT, PRIMARY KEY(`val`))")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `TIMESLICE` (`val` TEXT NOT NULL UNIQUE, `desc` TEXT, PRIMARY KEY(`val`))")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `TSGROUP1` (`name` TEXT, `desc` TEXT, `order` INTEGER NOT NULL UNIQUE, `multiplier` REAL NOT NULL DEFAULT 1, PRIMARY KEY(`name`))")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `TSGROUP2` (`name` TEXT, `desc` TEXT, `order` INTEGER NOT NULL UNIQUE, `multiplier` REAL NOT NULL DEFAULT 1, PRIMARY KEY(`name`))")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `YEAR` (`val` TEXT NOT NULL UNIQUE, `desc` TEXT, PRIMARY KEY(`val`))")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `NODE` ( `val` TEXT, `desc` TEXT, `r` TEXT, PRIMARY KEY(`val`)" * (foreignkeys ? ", FOREIGN KEY(`r`) REFERENCES `REGION`(`val`)" : "") * " )")

    # Parameter default table
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `DefaultParams` ( `id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, `tablename` TEXT NOT NULL, `val` REAL NOT NULL );")
    SQLite.DBInterface.execute(db, "CREATE UNIQUE INDEX `DefaultParams_tablename_unique` ON `DefaultParams` (`tablename`);")  # Needed for LEAP compatibility

    # Parameter tables
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `YearSplit` ( `id` INTEGER NOT NULL UNIQUE, `l` TEXT, `y` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`y`) REFERENCES `YEAR`(`val`), FOREIGN KEY(`l`) REFERENCES `TIMESLICE`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `VariableCost` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `t` TEXT, `m` TEXT, `y` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`t`) REFERENCES `TECHNOLOGY`(`val`), FOREIGN KEY(`m`) REFERENCES `MODE_OF_OPERATION`(`val`), FOREIGN KEY(`y`) REFERENCES `YEAR`(`val`), FOREIGN KEY(`r`) REFERENCES `REGION`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `TransmissionModelingEnabled` ( `id` INTEGER, `r` TEXT, `f` TEXT, `y` TEXT, `type` INTEGER DEFAULT 1, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`r`) REFERENCES `REGION`(`val`), FOREIGN KEY(`f`) REFERENCES `FUEL`(`val`), FOREIGN KEY(`y`) REFERENCES `YEAR`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `TransmissionLine` ( `id` TEXT, `n1` TEXT, `n2` TEXT, `f` TEXT, `maxflow` REAL, `reactance` REAL, `yconstruction` INTEGER, `capitalcost` REAL, `fixedcost` REAL, `variablecost` REAL, `operationallife` INTEGER, `efficiency` REAL, `interestrate` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`n2`) REFERENCES `NODE`(`val`), FOREIGN KEY(`n1`) REFERENCES `NODE`(`val`), FOREIGN KEY(`f`) REFERENCES `FUEL`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `TransmissionCapacityToActivityUnit` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `f` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`r`) REFERENCES `REGION`(`val`), FOREIGN KEY(`f`) REFERENCES `FUEL`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `TradeRoute` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `rr` TEXT, `f` TEXT, `y` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`r`) REFERENCES `REGION`(`val`), FOREIGN KEY(`rr`) REFERENCES `REGION`(`val`), FOREIGN KEY(`y`) REFERENCES `YEAR`(`val`), FOREIGN KEY(`f`) REFERENCES `FUEL`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `TotalTechnologyModelPeriodActivityUpperLimit` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `t` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`t`) REFERENCES `TECHNOLOGY`(`val`), FOREIGN KEY(`r`) REFERENCES `REGION`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `TotalTechnologyModelPeriodActivityLowerLimit` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `t` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`t`) REFERENCES `TECHNOLOGY`(`val`), FOREIGN KEY(`r`) REFERENCES `REGION`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `TotalTechnologyAnnualActivityUpperLimit` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `t` TEXT, `y` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`y`) REFERENCES `YEAR`(`val`), FOREIGN KEY(`t`) REFERENCES `TECHNOLOGY`(`val`), FOREIGN KEY(`r`) REFERENCES `REGION`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `TotalTechnologyAnnualActivityLowerLimit` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `t` TEXT, `y` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`y`) REFERENCES `YEAR`(`val`), FOREIGN KEY(`t`) REFERENCES `TECHNOLOGY`(`val`), FOREIGN KEY(`r`) REFERENCES `REGION`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `TotalAnnualMinCapacityInvestment` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `t` TEXT, `y` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`y`) REFERENCES `YEAR`(`val`), FOREIGN KEY(`r`) REFERENCES `REGION`(`val`), FOREIGN KEY(`t`) REFERENCES `TECHNOLOGY`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `TotalAnnualMinCapacityInvestmentStorage` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `s` TEXT, `y` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`y`) REFERENCES `YEAR`(`val`), FOREIGN KEY(`r`) REFERENCES `REGION`(`val`), FOREIGN KEY(`s`) REFERENCES `STORAGE`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `TotalAnnualMinCapacity` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `t` TEXT, `y` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`t`) REFERENCES `TECHNOLOGY`(`val`), FOREIGN KEY(`r`) REFERENCES `REGION`(`val`), FOREIGN KEY(`y`) REFERENCES `YEAR`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `TotalAnnualMinCapacityStorage` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `s` TEXT, `y` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`s`) REFERENCES `STORAGE`(`val`), FOREIGN KEY(`r`) REFERENCES `REGION`(`val`), FOREIGN KEY(`y`) REFERENCES `YEAR`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `TotalAnnualMaxCapacityInvestment` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `t` TEXT, `y` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`y`) REFERENCES `YEAR`(`val`), FOREIGN KEY(`r`) REFERENCES `REGION`(`val`), FOREIGN KEY(`t`) REFERENCES `TECHNOLOGY`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `TotalAnnualMaxCapacityInvestmentStorage` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `s` TEXT, `y` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`y`) REFERENCES `YEAR`(`val`), FOREIGN KEY(`r`) REFERENCES `REGION`(`val`), FOREIGN KEY(`s`) REFERENCES `STORAGE`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `TotalAnnualMaxCapacity` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `t` TEXT, `y` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`t`) REFERENCES `TECHNOLOGY`(`val`), FOREIGN KEY(`r`) REFERENCES `REGION`(`val`), FOREIGN KEY(`y`) REFERENCES `YEAR`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `TotalAnnualMaxCapacityStorage` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `s` TEXT, `y` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`s`) REFERENCES `STORAGE`(`val`), FOREIGN KEY(`r`) REFERENCES `REGION`(`val`), FOREIGN KEY(`y`) REFERENCES `YEAR`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `TechnologyToStorage` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `t` TEXT, `s` TEXT, `m` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`t`) REFERENCES `TECHNOLOGY`(`val`), FOREIGN KEY(`m`) REFERENCES `MODE_OF_OPERATION`(`val`), FOREIGN KEY(`s`) REFERENCES `STORAGE`(`val`), FOREIGN KEY(`r`) REFERENCES `REGION`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `TechnologyFromStorage` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `t` TEXT, `s` TEXT, `m` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`r`) REFERENCES `REGION`(`val`), FOREIGN KEY(`m`) REFERENCES `MODE_OF_OPERATION`(`val`), FOREIGN KEY(`s`) REFERENCES `STORAGE`(`val`), FOREIGN KEY(`t`) REFERENCES `TECHNOLOGY`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `StorageMaxDischargeRate` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `s` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`r`) REFERENCES `REGION`(`val`), FOREIGN KEY(`s`) REFERENCES `STORAGE`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `StorageMaxChargeRate` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `s` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`r`) REFERENCES `REGION`(`val`), FOREIGN KEY(`s`) REFERENCES `STORAGE`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `StorageLevelStart` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `s` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`r`) REFERENCES `REGION`(`val`), FOREIGN KEY(`s`) REFERENCES `STORAGE`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `StorageFullLoadHours` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `s` TEXT, `y` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`r`) REFERENCES `REGION`(`val`), FOREIGN KEY(`s`) REFERENCES `STORAGE`(`val`), FOREIGN KEY(`y`) REFERENCES `YEAR`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `SpecifiedDemandProfile` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `f` TEXT, `l` TEXT, `y` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`f`) REFERENCES `FUEL`(`val`), FOREIGN KEY(`l`) REFERENCES `TIMESLICE`(`val`), FOREIGN KEY(`y`) REFERENCES `YEAR`(`val`), FOREIGN KEY(`r`) REFERENCES `REGION`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `SpecifiedAnnualDemand` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `f` TEXT, `y` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`r`) REFERENCES `REGION`(`val`), FOREIGN KEY(`y`) REFERENCES `YEAR`(`val`), FOREIGN KEY(`f`) REFERENCES `FUEL`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `ResidualStorageCapacity` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `s` TEXT, `y` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`r`) REFERENCES `REGION`(`val`), FOREIGN KEY(`s`) REFERENCES `STORAGE`(`val`), FOREIGN KEY(`y`) REFERENCES `YEAR`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `ResidualCapacity` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `t` TEXT, `y` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`r`) REFERENCES `REGION`(`val`), FOREIGN KEY(`t`) REFERENCES `TECHNOLOGY`(`val`), FOREIGN KEY(`y`) REFERENCES `YEAR`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `ReserveMarginTagTechnology` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `t` TEXT, `y` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`r`) REFERENCES `REGION`(`val`), FOREIGN KEY(`y`) REFERENCES `YEAR`(`val`), FOREIGN KEY(`t`) REFERENCES `TECHNOLOGY`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `ReserveMarginTagFuel` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `f` TEXT, `y` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`f`) REFERENCES `FUEL`(`val`), FOREIGN KEY(`r`) REFERENCES `REGION`(`val`), FOREIGN KEY(`y`) REFERENCES `YEAR`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `ReserveMargin` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `y` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`r`) REFERENCES `REGION`(`val`), FOREIGN KEY(`y`) REFERENCES `YEAR`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `RETagTechnology` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `t` TEXT, `y` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`r`) REFERENCES `REGION`(`val`), FOREIGN KEY(`y`) REFERENCES `YEAR`(`val`), FOREIGN KEY(`t`) REFERENCES `TECHNOLOGY`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `RETagFuel` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `f` TEXT, `y` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`y`) REFERENCES `YEAR`(`val`), FOREIGN KEY(`f`) REFERENCES `FUEL`(`val`), FOREIGN KEY(`r`) REFERENCES `REGION`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `REMinProductionTarget` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `y` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`y`) REFERENCES `YEAR`(`val`), FOREIGN KEY(`r`) REFERENCES `REGION`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `RampingReset` (`id` INTEGER NOT NULL UNIQUE, `r` TEXT, `val` INTEGER, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`r`) REFERENCES `REGION`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `RampRate` (`id` INTEGER NOT NULL UNIQUE, `r` TEXT, `t` TEXT, `y` TEXT, `l` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`r`) REFERENCES `REGION`(`val`), FOREIGN KEY(`t`) REFERENCES `TECHNOLOGY`(`val`), FOREIGN KEY(`y`) REFERENCES `YEAR`(`val`), FOREIGN KEY(`l`) REFERENCES `TIMESLICE`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `OutputActivityRatio` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `t` TEXT, `f` TEXT, `m` TEXT, `y` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`m`) REFERENCES `MODE_OF_OPERATION`(`val`), FOREIGN KEY(`t`) REFERENCES `TECHNOLOGY`(`val`), FOREIGN KEY(`f`) REFERENCES `FUEL`(`val`), FOREIGN KEY(`r`) REFERENCES `REGION`(`val`), FOREIGN KEY(`y`) REFERENCES `YEAR`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `OperationalLifeStorage` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `s` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`s`) REFERENCES `STORAGE`(`val`), FOREIGN KEY(`r`) REFERENCES `REGION`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `OperationalLife` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `t` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`t`) REFERENCES `TECHNOLOGY`(`val`), FOREIGN KEY(`r`) REFERENCES `REGION`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `NodalDistributionTechnologyCapacity` ( `id` INTEGER, `n` TEXT, `t` TEXT, `y` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`t`) REFERENCES `TECHNOLOGY`(`val`), FOREIGN KEY(`y`) REFERENCES `YEAR`(`val`), FOREIGN KEY(`n`) REFERENCES `NODE`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `NodalDistributionStorageCapacity` ( `id` INTEGER, `n` TEXT, `s` TEXT, `y` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`n`) REFERENCES `NODE`(`val`), FOREIGN KEY(`s`) REFERENCES `STORAGE`(`val`), FOREIGN KEY(`y`) REFERENCES `YEAR`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `NodalDistributionDemand` ( `id` INTEGER, `n` TEXT, `f` TEXT, `y` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`f`) REFERENCES `FUEL`(`val`), FOREIGN KEY(`y`) REFERENCES `YEAR`(`val`), FOREIGN KEY(`n`) REFERENCES `NODE`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `ModelPeriodExogenousEmission` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `e` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`e`) REFERENCES `EMISSION`(`val`), FOREIGN KEY(`r`) REFERENCES `REGION`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `ModelPeriodEmissionLimit` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `e` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`e`) REFERENCES `EMISSION`(`val`), FOREIGN KEY(`r`) REFERENCES `REGION`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `MinimumUtilization` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `t` TEXT, `l` TEXT, `y` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`y`) REFERENCES `YEAR`(`val`), FOREIGN KEY(`r`) REFERENCES `REGION`(`val`), FOREIGN KEY(`t`) REFERENCES `TECHNOLOGY`(`val`), FOREIGN KEY(`l`) REFERENCES `TIMESLICE`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `MinStorageCharge` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `s` TEXT, `y` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`r`) REFERENCES `REGION`(`val`), FOREIGN KEY(`s`) REFERENCES `STORAGE`(`val`), FOREIGN KEY(`y`) REFERENCES `YEAR`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `LTsGroup` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, `l` TEXT UNIQUE,	`lorder` INTEGER, `tg2` TEXT, `tg1` TEXT" * (foreignkeys ? ", FOREIGN KEY(`l`) REFERENCES `TIMESLICE`(`val`), FOREIGN KEY(`tg2`) REFERENCES `TSGROUP2`(`name`), FOREIGN KEY(`tg1`) REFERENCES `TSGROUP1`(`name`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `InterestRateTechnology` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `t` TEXT, `y` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`t`) REFERENCES `TECHNOLOGY`(`val`), FOREIGN KEY(`r`) REFERENCES `REGION`(`val`), FOREIGN KEY(`y`) REFERENCES `YEAR`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `InterestRateStorage` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `s` TEXT, `y` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`s`) REFERENCES `STORAGE`(`val`), FOREIGN KEY(`r`) REFERENCES `REGION`(`val`), FOREIGN KEY(`y`) REFERENCES `YEAR`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `InputActivityRatio` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `t` TEXT, `f` TEXT, `m` TEXT, `y` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`y`) REFERENCES `YEAR`(`val`), FOREIGN KEY(`m`) REFERENCES `MODE_OF_OPERATION`(`val`), FOREIGN KEY(`f`) REFERENCES `FUEL`(`val`), FOREIGN KEY(`t`) REFERENCES `TECHNOLOGY`(`val`), FOREIGN KEY(`r`) REFERENCES `REGION`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `FixedCost` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `t` TEXT, `y` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`t`) REFERENCES `TECHNOLOGY`(`val`), FOREIGN KEY(`r`) REFERENCES `REGION`(`val`), FOREIGN KEY(`y`) REFERENCES `YEAR`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `EmissionsPenalty` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `e` TEXT, `y` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`r`) REFERENCES `REGION`(`val`), FOREIGN KEY(`y`) REFERENCES `YEAR`(`val`), FOREIGN KEY(`e`) REFERENCES `EMISSION`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `EmissionActivityRatio` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `t` TEXT, `e` TEXT, `m` TEXT, `y` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`r`) REFERENCES `REGION`(`val`), FOREIGN KEY(`t`) REFERENCES `TECHNOLOGY`(`val`), FOREIGN KEY(`y`) REFERENCES `YEAR`(`val`), FOREIGN KEY(`m`) REFERENCES `MODE_OF_OPERATION`(`val`), FOREIGN KEY(`e`) REFERENCES `EMISSION`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `DiscountRate` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`r`) REFERENCES `REGION`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `DepreciationMethod` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`r`) REFERENCES `REGION`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `CapitalCostStorage` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `s` TEXT, `y` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`s`) REFERENCES `STORAGE`(`val`), FOREIGN KEY(`r`) REFERENCES `REGION`(`val`), FOREIGN KEY(`y`) REFERENCES `YEAR`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `CapitalCost` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `t` TEXT, `y` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`t`) REFERENCES `TECHNOLOGY`(`val`), FOREIGN KEY(`y`) REFERENCES `YEAR`(`val`), FOREIGN KEY(`r`) REFERENCES `REGION`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `CapacityToActivityUnit` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `t` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`t`) REFERENCES `TECHNOLOGY`(`val`), FOREIGN KEY(`r`) REFERENCES `REGION`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `CapacityOfOneTechnologyUnit` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `t` TEXT, `y` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`r`) REFERENCES `REGION`(`val`), FOREIGN KEY(`y`) REFERENCES `YEAR`(`val`), FOREIGN KEY(`t`) REFERENCES `TECHNOLOGY`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `CapacityFactor` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `t` TEXT, `l` TEXT, `y` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`y`) REFERENCES `YEAR`(`val`), FOREIGN KEY(`r`) REFERENCES `REGION`(`val`), FOREIGN KEY(`t`) REFERENCES `TECHNOLOGY`(`val`), FOREIGN KEY(`l`) REFERENCES `TIMESLICE`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `AvailabilityFactor` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `t` TEXT, `y` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`y`) REFERENCES `YEAR`(`val`), FOREIGN KEY(`r`) REFERENCES `REGION`(`val`), FOREIGN KEY(`t`) REFERENCES `TECHNOLOGY`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `AnnualExogenousEmission` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `e` TEXT, `y` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`r`) REFERENCES `REGION`(`val`), FOREIGN KEY(`y`) REFERENCES `YEAR`(`val`), FOREIGN KEY(`e`) REFERENCES `EMISSION`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `AnnualEmissionLimit` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `e` TEXT, `y` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`y`) REFERENCES `YEAR`(`val`), FOREIGN KEY(`e`) REFERENCES `EMISSION`(`val`), FOREIGN KEY(`r`) REFERENCES `REGION`(`val`)" : "") * " )")
    SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `AccumulatedAnnualDemand` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `f` TEXT, `y` TEXT, `val` REAL, PRIMARY KEY(`id`)" * (foreignkeys ? ", FOREIGN KEY(`f`) REFERENCES `FUEL`(`val`), FOREIGN KEY(`y`) REFERENCES `YEAR`(`val`), FOREIGN KEY(`r`) REFERENCES `REGION`(`val`)" : "") * " )")
    # END: Add new NEMO tables.

    # BEGIN: Write default values to DefaultParams.
    for (k, v) in defaultvals
        SQLite.DBInterface.execute(db, "INSERT INTO DefaultParams (tablename, val) values('" * k * "', " * string(v) * ")")
    end
    # END: Write default values to DefaultParams.

    SQLite.DBInterface.execute(db, "COMMIT")
    SQLite.DBInterface.execute(db, "VACUUM")

    logmsg("Added NEMO structure to SQLite database at " * path * ".")
    # END: DDL operations in an SQLite transaction.

    # Return configured database
    return db
end  # createnemodb(path::String, defaultvals::Dict{String, Float64} = Dict{String, Float64}())

# Upgrades NEMO database from version 2 to version 3 by adding TransmissionLine.efficiency.
function db_v2_to_v3(db::SQLite.DB; quiet::Bool = false)
    # BEGIN: Wrap database operations in try-catch block to allow rollback on error.
    try
        # BEGIN: SQLite transaction.
        SQLite.DBInterface.execute(db, "BEGIN")

        qry::DataFrame = SQLite.DBInterface.execute(db, "PRAGMA table_info('TransmissionLine')") |> DataFrame
        updatetl::Bool = !in("efficiency", qry.name)

        if updatetl
            SQLite.DBInterface.execute(db, "alter table TransmissionLine rename to TransmissionLine_old")
            SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `TransmissionLine` ( `id` TEXT, `n1` TEXT, `n2` TEXT, `f` TEXT, `maxflow` REAL, `reactance` REAL, `yconstruction` INTEGER, `capitalcost` REAL, `fixedcost` REAL, `variablecost` REAL, `operationallife` INTEGER, `efficiency` REAL, PRIMARY KEY(`id`) )")
            SQLite.DBInterface.execute(db, "insert into TransmissionLine select id, n1, n2, f, maxflow, reactance, yconstruction, capitalcost, fixedcost, variablecost, operationallife, 1.0 from TransmissionLine_old")
            SQLite.DBInterface.execute(db, "drop table TransmissionLine_old")
            SQLite.DBInterface.execute(db, "update version set version = 3")
        end

        SQLite.DBInterface.execute(db, "COMMIT")
        # END: SQLite transaction.

        updatetl && logmsg("Upgraded database to version 3.", quiet)
    catch
        # Rollback transaction and rethrow error
        SQLite.DBInterface.execute(db, "ROLLBACK")
        rethrow()
    end
    # END: Wrap database operations in try-catch block to allow rollback on error.
end  # db_v2_to_v3(db::SQLite.DB; quiet::Bool = false)

# Upgrades NEMO database from version 3 to version 4 by adding TransmissionCapacityToActivityUnit.r.
function db_v3_to_v4(db::SQLite.DB; quiet::Bool = false)
    # BEGIN: Wrap database operations in try-catch block to allow rollback on error.
    try
        # BEGIN: SQLite transaction.
        SQLite.DBInterface.execute(db, "BEGIN")

        qry::DataFrame = SQLite.DBInterface.execute(db, "PRAGMA table_info('TransmissionCapacityToActivityUnit')") |> DataFrame
        updatetcta::Bool = !in("r", qry.name)

        if updatetcta
            SQLite.DBInterface.execute(db, "alter table TransmissionCapacityToActivityUnit rename to TransmissionCapacityToActivityUnit_old")
            SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `TransmissionCapacityToActivityUnit` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `f` TEXT, `val` REAL, PRIMARY KEY(`id`) )")
            SQLite.DBInterface.execute(db, "insert into TransmissionCapacityToActivityUnit select tcta.id, r.val, tcta.f, tcta.val from TransmissionCapacityToActivityUnit_old tcta, REGION r")
            SQLite.DBInterface.execute(db, "drop table TransmissionCapacityToActivityUnit_old")
            SQLite.DBInterface.execute(db, "update version set version = 4")
        end

        SQLite.DBInterface.execute(db, "COMMIT")
        # END: SQLite transaction.

        updatetcta && logmsg("Upgraded database to version 4.", quiet)
    catch
        # Rollback transaction and rethrow error
        SQLite.DBInterface.execute(db, "ROLLBACK")
        rethrow()
    end
    # END: Wrap database operations in try-catch block to allow rollback on error.
end  # db_v3_to_v4(db::SQLite.DB; quiet::Bool = false)

# Upgrades NEMO database from version 4 to version 5 by adding RampRate and RampingReset.
function db_v4_to_v5(db::SQLite.DB; quiet::Bool = false)
    # BEGIN: Wrap database operations in try-catch block to allow rollback on error.
    try
        # BEGIN: SQLite transaction.
        SQLite.DBInterface.execute(db, "BEGIN")

        qry::SQLite.Query = SQLite.DBInterface.execute(db, "PRAGMA table_info('RampRate')")

        if SQLite.done(qry)
            SQLite.DBInterface.execute(db, "CREATE TABLE `RampRate` (`id` INTEGER NOT NULL UNIQUE, `r` TEXT, `t` TEXT, `y` TEXT, `l` TEXT, `val` REAL, PRIMARY KEY(`id`))")
        end

        qry = SQLite.DBInterface.execute(db, "PRAGMA table_info('RampingReset')")

        if SQLite.done(qry)
            SQLite.DBInterface.execute(db, "CREATE TABLE `RampingReset` (`id` INTEGER NOT NULL UNIQUE, `r` TEXT, `val` INTEGER, PRIMARY KEY(`id`))")
        end

        SQLite.DBInterface.execute(db, "update version set version = 5")

        SQLite.DBInterface.execute(db, "COMMIT")
        # END: SQLite transaction.

        logmsg("Upgraded database to version 5.", quiet)
    catch
        # Rollback transaction and rethrow error
        SQLite.DBInterface.execute(db, "ROLLBACK")
        rethrow()
    end
    # END: Wrap database operations in try-catch block to allow rollback on error.
end  # db_v4_to_v5(db::SQLite.DB; quiet::Bool = false)

# Upgrades NEMO database from version 5 to version 6 by adding MinimumUtilization, DiscountRateStorage, DiscountRateTechnology, TransmissionLine.discountrate.
function db_v5_to_v6(db::SQLite.DB; quiet::Bool = false)
    # BEGIN: Wrap database operations in try-catch block to allow rollback on error.
    try
        # BEGIN: SQLite transaction.
        SQLite.DBInterface.execute(db, "BEGIN")

        qry::SQLite.Query = SQLite.DBInterface.execute(db, "PRAGMA table_info('MinimumUtilization')")

        if SQLite.done(qry)
            SQLite.DBInterface.execute(db, "CREATE TABLE `MinimumUtilization` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `t` TEXT, `l` TEXT, `y` TEXT, `val` REAL, PRIMARY KEY(`id`))")
        end

        qry = SQLite.DBInterface.execute(db, "PRAGMA table_info('DiscountRateStorage')")

        if SQLite.done(qry)
            SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `DiscountRateStorage` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `s` TEXT, `val` REAL, PRIMARY KEY(`id`))")
        end

        qry = SQLite.DBInterface.execute(db, "PRAGMA table_info('DiscountRateTechnology')")

        if SQLite.done(qry)
            SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `DiscountRateTechnology` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `t` TEXT, `val` REAL, PRIMARY KEY(`id`))")
        end

        qrydf::DataFrame = SQLite.DBInterface.execute(db, "PRAGMA table_info('TransmissionLine')") |> DataFrame
        updatetl::Bool = !in("discountrate", qrydf.name)

        if updatetl
            SQLite.DBInterface.execute(db, "alter table TransmissionLine rename to TransmissionLine_old")
            SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `TransmissionLine` ( `id` TEXT, `n1` TEXT, `n2` TEXT, `f` TEXT, `maxflow` REAL, `reactance` REAL, `yconstruction` INTEGER, `capitalcost` REAL, `fixedcost` REAL, `variablecost` REAL, `operationallife` INTEGER, `efficiency` REAL, `discountrate` REAL, PRIMARY KEY(`id`))")
            SQLite.DBInterface.execute(db, "insert into TransmissionLine select id, n1, n2, f, maxflow, reactance, yconstruction, capitalcost, fixedcost, variablecost, operationallife, efficiency, null from TransmissionLine_old")
            SQLite.DBInterface.execute(db, "drop table TransmissionLine_old")
        end

        SQLite.DBInterface.execute(db, "update version set version = 6")

        SQLite.DBInterface.execute(db, "COMMIT")
        # END: SQLite transaction.

        logmsg("Upgraded database to version 6.", quiet)
    catch
        # Rollback transaction and rethrow error
        SQLite.DBInterface.execute(db, "ROLLBACK")
        rethrow()
    end
    # END: Wrap database operations in try-catch block to allow rollback on error.
end  # db_v5_to_v6(db::SQLite.DB; quiet::Bool = false)

# Upgrades NEMO database from version 6 to version 7 by: removing DiscountRateStorage, DiscountRateTechnology, TransmissionLine.discountrate; and adding InterestRateStorage, InterestRateTechnology, TransmissionLine.interestrate
function db_v6_to_v7(db::SQLite.DB; quiet::Bool = false)
    # BEGIN: Wrap database operations in try-catch block to allow rollback on error.
    try
        # BEGIN: SQLite transaction.
        SQLite.DBInterface.execute(db, "BEGIN")

        SQLite.DBInterface.execute(db,"drop view if exists DiscountRateStorage_def")
        SQLite.DBInterface.execute(db,"drop table if exists DiscountRateStorage")
        SQLite.DBInterface.execute(db,"drop view if exists DiscountRateTechnology_def")
        SQLite.DBInterface.execute(db,"drop table if exists DiscountRateTechnology")

        qry::SQLite.Query = SQLite.DBInterface.execute(db, "PRAGMA table_info('InterestRateStorage')")

        if SQLite.done(qry)
            SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `InterestRateStorage` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `s` TEXT, `y` TEXT, `val` REAL, PRIMARY KEY(`id`))")
        end

        qry = SQLite.DBInterface.execute(db, "PRAGMA table_info('InterestRateTechnology')")

        if SQLite.done(qry)
            SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `InterestRateTechnology` ( `id` INTEGER NOT NULL UNIQUE, `r` TEXT, `t` TEXT, `y` TEXT, `val` REAL, PRIMARY KEY(`id`))")
        end

        SQLite.DBInterface.execute(db, "alter table TransmissionLine rename to TransmissionLine_old")
        SQLite.DBInterface.execute(db, "CREATE TABLE IF NOT EXISTS `TransmissionLine` ( `id` TEXT, `n1` TEXT, `n2` TEXT, `f` TEXT, `maxflow` REAL, `reactance` REAL, `yconstruction` INTEGER, `capitalcost` REAL, `fixedcost` REAL, `variablecost` REAL, `operationallife` INTEGER, `efficiency` REAL, `interestrate` REAL, PRIMARY KEY(`id`))")
        SQLite.DBInterface.execute(db, "insert into TransmissionLine select id, n1, n2, f, maxflow, reactance, yconstruction, capitalcost, fixedcost, variablecost, operationallife, efficiency, null from TransmissionLine_old")
        SQLite.DBInterface.execute(db, "drop table TransmissionLine_old")

        SQLite.DBInterface.execute(db, "update version set version = 7")

        SQLite.DBInterface.execute(db, "COMMIT")
        # END: SQLite transaction.

        logmsg("Upgraded database to version 7.", quiet)
    catch
        # Rollback transaction and rethrow error
        SQLite.DBInterface.execute(db, "ROLLBACK")
        rethrow()
    end
    # END: Wrap database operations in try-catch block to allow rollback on error.
end  # db_v6_to_v7(db::SQLite.DB; quiet::Bool = false)

"""
    create_temp_tables(db::SQLite.DB)

Creates temporary tables needed when NEMO is calculating a scenario. These tables are not created as SQLite temporary tables
    in order to make them simultaneously visible to multiple Julia processes."""
function create_temp_tables(db::SQLite.DB)
    try
        # BEGIN: SQLite transaction.
        SQLite.DBInterface.execute(db, "BEGIN")

        SQLite.DBInterface.execute(db, "DROP TABLE IF EXISTS nodalstorage")

        # nodalstorage is used as a filter in nodal and non-nodal storage modeling
        SQLite.DBInterface.execute(db, "create table nodalstorage as
        select distinct n.r as r, nsc.n as n, nsc.s as s, nsc.y as y, nsc.val as val
        from NodalDistributionStorageCapacity_def nsc, node n,
            NodalDistributionTechnologyCapacity_def ntc, TransmissionModelingEnabled tme,
            (select r, t, f, m, y from OutputActivityRatio_def
            where val <> 0
            union
            select r, t, f, m, y from InputActivityRatio_def
            where val <> 0) ar,
            (select r, t, s, m from TechnologyFromStorage_def where val = 1
            union
            select r, t, s, m from TechnologyToStorage_def where val = 1) ts
        where nsc.val > 0
        and n.val = nsc.n
        and ntc.val > 0 and ntc.n = nsc.n and ntc.t = ar.t and ntc.y = nsc.y
        and tme.r = n.r and tme.f = ar.f and tme.y = nsc.y
        and ar.r = n.r and ar.y = nsc.y
        and ts.r = n.r and ts.t = ntc.t and ts.s = nsc.s and ts.m = ar.m")

        SQLite.DBInterface.execute(db, "COMMIT")
        # END: SQLite transaction.
    catch
        # Rollback transaction and rethrow error
        SQLite.DBInterface.execute(db, "ROLLBACK")
        rethrow()
    end
end  # create_temp_tables(db::SQLite.DB)

"""
    drop_temp_tables(db::SQLite.DB)

Drops temporary tables created in `create_temp_tables()`."""
function drop_temp_tables(db::SQLite.DB)
    # BEGIN: SQLite transaction.
    SQLite.DBInterface.execute(db, "BEGIN")

    SQLite.DBInterface.execute(db, "DROP TABLE IF EXISTS nodalstorage")

    SQLite.DBInterface.execute(db, "COMMIT")
    # END: SQLite transaction.
end  # drop_temp_tables(db::SQLite.DB)

"""
    create_other_nemo_indices(db::SQLite.DB)

Creates miscellaneous indices needed by NEMO."""
function create_other_nemo_indices(db::SQLite.DB)
    try
        # BEGIN: SQLite transaction.
        SQLite.DBInterface.execute(db, "BEGIN")

        SQLite.DBInterface.execute(db, "CREATE UNIQUE INDEX IF NOT EXISTS `TransmissionModelingEnabled_fks_unique` ON `TransmissionModelingEnabled` ( `r`, `f`, `y` )")

        SQLite.DBInterface.execute(db, "COMMIT")
        # END: SQLite transaction.
    catch
        # Rollback transaction and rethrow error
        SQLite.DBInterface.execute(db, "ROLLBACK")
        rethrow()
    end
end  # create_other_nemo_indices(db::SQLite.DB)

"""
    createviewwithdefaults(db::SQLite.DB, tables::Array{String,1})

For each parameter table identified in `tables`, creates a view that explicitly shows the default value
for the `val` column, if one is available. View name = [table name]  * "_def". Table names are case-
sensitive."""
function createviewwithdefaults(db::SQLite.DB, tables::Array{String,1})
    try
        # BEGIN: SQLite transaction.
        SQLite.DBInterface.execute(db, "BEGIN")

        for t in tables
            local hasdefault::Bool = false  # Indicates whether val column in t has a default value
            local defaultval  # Default value for val column in t, if column has a default value
            local pks::Array{String,1} = Array{String,1}()  # Names of foreign key fields in t
            local defaultqry::DataFrame  # Query of DefaultParams table for t's default value
            local createviewstmt::String  # Create view SQL statement that will be executed for t

            # Delete view if existing already
            SQLite.DBInterface.execute(db,"drop view if exists " * t * "_def")

            # BEGIN: Determine foreign key fields in t.
            for row in SQLite.DBInterface.execute(db, "PRAGMA table_info('" * t *"')")
                local rowname::String = row[:name]  # Value in name field for row

                if rowname == "id" || rowname == "val"
                    continue
                else
                    push!(pks,rowname)
                end
            end
            # END: Determine foreign key fields in t.

            # BEGIN: Determine default value for t.
            defaultqry = SQLite.DBInterface.execute(db, "select * from DefaultParams where tablename = '" * t * "'") |> DataFrame

            if size(defaultqry)[1] >= 1
                defaultval = defaultqry[!, :val][1]

                # Some special handling here to avoid creating large views where they're not necessary, given NEMO's logic
                if (t == "OutputActivityRatio" || t == "InputActivityRatio") && defaultval == 0.0
                    # OutputActivityRatio and InputActivityRatio are only used when != 0, so can ignore a default value of 0
                    hasdefault = false
                else
                    hasdefault = true
                end
            end
            # END: Determine default value for t.

            # BEGIN: Create unique index on t for foreign key fields.
            # This step significantly improves performance for many queries on new view for t
            SQLite.DBInterface.execute(db,"drop index if exists " * t * "_fks_unique")
            SQLite.DBInterface.execute(db,"create unique index " * t * "_fks_unique on " * t * " (" * join(pks, ",") * ")")
            # END: Create unique index on t for foreign key fields.

            if hasdefault
                # Join to foreign key tables with default specified
                local outerfieldsclause::String = ""  # Dynamic fields clause for create view outer select
                local innerfieldsclause::String = ""  # Dynamic fields clause for create view inner select
                local fromclause::String = ""  # Dynamic from clause for create view inner select
                local leftjoinclause::String = ""  # Dynamic left join criteria for create view inner select

                for f in pks
                    outerfieldsclause = outerfieldsclause * f * ", "
                    innerfieldsclause = innerfieldsclause * f * "_tab.val as " * f * ", "
                    fromclause = fromclause * translatesetabb(f) * " as " * f * "_tab, "
                    leftjoinclause = leftjoinclause * "t." * f * " = " * f * "_tab.val and "
                end

                innerfieldsclause = innerfieldsclause * "t.val as val "
                fromclause = fromclause[1:end-2] * " "
                leftjoinclause = leftjoinclause[1:end-4]

                createviewstmt = "create view " * t * "_def as select " * outerfieldsclause * "ifnull(val," * string(defaultval) * ") as val from (select " * innerfieldsclause * "from " * fromclause * "left join " * t * " t on " * leftjoinclause * ")"
            else
                # No default value, so view with defaults is a simple copy of t
                createviewstmt = "create view " * t * "_def as select * from " * t
            end

            # println("createviewstmt = " * createviewstmt)
            SQLite.DBInterface.execute(db, createviewstmt)
        end

        SQLite.DBInterface.execute(db, "COMMIT")
        # END: SQLite transaction.
    catch
        # Rollback transaction and rethrow error
        SQLite.DBInterface.execute(db, "ROLLBACK")
        rethrow()
    end
end  # createviewwithdefaults(db::SQLite.DB, tables::Array{String,1})

"""
    setparamdefault(db::SQLite.DB, table::String, val::Float64)

Sets the default value for a parameter table in a NEMO scenario database.

# Arguments
- `db::SQLite.DB`: Scenario database containing the parameter table.
- `table::String`: Table name (case-sensitive).
- `val::Float64`: Parameter value (must be a floating-point number).
"""
function setparamdefault(db::SQLite.DB, table::String, val::Float64)
    # Add value to DefaultParams table - INSERT OR REPLACE syntax replaces any existing value
    SQLite.DBInterface.execute(db, "INSERT OR REPLACE INTO DefaultParams (tablename, val) values('" * table * "', " * string(val) * ")")

    # Update view showing default values for table
    createviewwithdefaults(db, [table])
    logmsg("Updated default value for parameter " * table * ". New default = " * string(val) * ".")
end  # setparamdefault(db::SQLite.DB, table::String, val::Float64)

"""
    dropdefaultviews(db::SQLite.DB)

Drops all views in `db` whose name ends with ""_def""."""
function dropdefaultviews(db::SQLite.DB)
    try
        # BEGIN: SQLite transaction.
        SQLite.DBInterface.execute(db, "BEGIN")

        for row in SQLite.DBInterface.execute(db, "select name from sqlite_master where type = 'view'")
            if endswith(row[:name], "_def")
                SQLite.DBInterface.execute(db, "DROP VIEW " * row[:name])
            end
        end

        SQLite.DBInterface.execute(db, "COMMIT")
        SQLite.DBInterface.execute(db, "VACUUM")
        # END: SQLite transaction.
    catch
        # Rollback transaction and rethrow error
        SQLite.DBInterface.execute(db, "ROLLBACK")
        rethrow()
    end
end  # dropdefaultviews(db::SQLite.DB)

"""
    dropresulttables(db::SQLite.DB, quiet::Bool = true)

Drops all tables in `db` whose name begins with ""v"" or ""sqlite_stat"" (both case-sensitive).
    The `quiet` parameter determines whether most status messages are suppressed.
"""
function dropresulttables(db::SQLite.DB, quiet::Bool = true)
    # BEGIN: Wrap database operations in try-catch block to allow rollback on error.
    try
        # Begin SQLite transaction
        SQLite.DBInterface.execute(db, "BEGIN")

        for tname in SQLite.tables(db).name
            if tname[1:1] == "v" || (length(tname) >= 11 && tname[1:11] == "sqlite_stat")
                SQLite.DBInterface.execute(db, "drop table " * tname)
                logmsg("Dropped table " * tname * ".", quiet)
            end
        end

        # Complete SQLite transaction
        SQLite.DBInterface.execute(db, "COMMIT")
    catch
        # Rollback db transaction
        SQLite.DBInterface.execute(db, "ROLLBACK")

        # Proceed with normal Julia error sequence
        rethrow()
    end
    # END: Wrap database operations in try-catch block to allow rollback on error.
end  # dropresulttables(db::SQLite.DB)


text:
#
# Julia interface to BASICLU
#

module BasicLU

using LinearAlgebra
using Printf
using SparseArrays
using basiclu_jll

const cint = Int64
const cdbl = Cdouble
const cint_ptr = Ptr{cint}
const cdbl_ptr = Ptr{cdbl}
const cvec = Array{cdbl,1}
const spvector = SparseVector{cdbl,cint}
const spmatrix = SparseMatrixCSC{cdbl,cint}

# =============================================================================
# exported BASICLU functions
# =============================================================================

export BLU, initialize, factorize, get_factors, solve, solve4update, update, maxvolume, maxvolume_basis

# =============================================================================
# BASICLU defines
# =============================================================================

# size of istore, xstore
const BASICLU_SIZE_ISTORE_1 = 1024
const BASICLU_SIZE_ISTORE_M = 21
const BASICLU_SIZE_XSTORE_1 = 1024
const BASICLU_SIZE_XSTORE_M = 4

# status codes
const BASICLU_OK = 0
const BASICLU_REALLOCATE = 1
const BASICLU_WARNING_singular_matrix = 2
const BASICLU_ERROR_invalid_store = -1
const BASICLU_ERROR_invalid_call = -2
const BASICLU_ERROR_argument_missing = -3
const BASICLU_ERROR_invalid_argument = -4
const BASICLU_ERROR_maximum_updates = -5
const BASICLU_ERROR_singular_update = -6

# user parameters in xstore (offset by 1)
const BASICLU_MEMORYL = 2
const BASICLU_MEMORYU = 3
const BASICLU_MEMORYW = 4
const BASICLU_DROP_TOLERANCE = 5
const BASICLU_ABS_PIVOT_TOLERANCE = 6
const BASICLU_REL_PIVOT_TOLERANCE = 7
const BASICLU_BIAS_NONZEROS = 8
const BASICLU_MAXN_SEARCH_PIVOT = 9
const BASICLU_PAD = 10
const BASICLU_STRETCH = 11
const BASICLU_COMPRESSION_THRESHOLD = 12
const BASICLU_SPARSE_THRESHOLD = 13
const BASICLU_REMOVE_COLUMNS = 14
const BASICLU_SEARCH_ROWS = 15

# user readable from xstore (offset by 1)
const BASICLU_DIM = 65
const BASICLU_STATUS = 66
const BASICLU_ADD_MEMORYL = 67
const BASICLU_ADD_MEMORYU = 68
const BASICLU_ADD_MEMORYW = 69

const BASICLU_NUPDATE = 71
const BASICLU_NFORREST = 72
const BASICLU_NFACTORIZE = 73
const BASICLU_NUPDATE_TOTAL = 74
const BASICLU_NFORREST_TOTAL = 75
const BASICLU_NSYMPERM_TOTAL = 76
const BASICLU_LNZ = 77
const BASICLU_UNZ = 78
const BASICLU_RNZ = 79
const BASICLU_MIN_PIVOT = 80
const BASICLU_MAX_PIVOT = 81
const BASICLU_UPDATE_COST = 82
const BASICLU_TIME_FACTORIZE = 83
const BASICLU_TIME_SOLVE = 84
const BASICLU_TIME_UPDATE = 85
const BASICLU_TIME_FACTORIZE_TOTAL = 86
const BASICLU_TIME_SOLVE_TOTAL = 87
const BASICLU_TIME_UPDATE_TOTAL = 88
const BASICLU_LFLOPS = 89
const BASICLU_UFLOPS = 90
const BASICLU_RFLOPS = 91
const BASICLU_CONDEST_L = 92
const BASICLU_CONDEST_U = 93
const BASICLU_NORM_L = 95
const BASICLU_NORM_U = 96
const BASICLU_NORMEST_LINV = 97
const BASICLU_NORMEST_UINV = 98
const BASICLU_MATRIX_ONENORM = 99
const BASICLU_MATRIX_INFNORM = 100
const BASICLU_RESIDUAL_TEST = 112

const BASICLU_MATRIX_NZ = 101
const BASICLU_RANK = 102
const BASICLU_BUMP_SIZE = 103
const BASICLU_BUMP_NZ = 104
const BASICLU_NSEARCH_PIVOT = 105
const BASICLU_NEXPAND = 106
const BASICLU_NGARBAGE = 107
const BASICLU_FACTOR_FLOPS = 108
const BASICLU_TIME_SINGLETONS = 109
const BASICLU_TIME_SEARCH_PIVOT = 110
const BASICLU_TIME_ELIM_PIVOT = 111

const BASICLU_PIVOT_ERROR = 121

# parameters for Julia driver function
const realloc_factor = 1.5

# =============================================================================
# type BLU
# =============================================================================
"""
    mutable struct BLU

Hold BASICLU object. You can access @xstore to set parameters and get output
values from BASICLU routines.
"""
mutable struct BLU
    istore::Array{cint,1}
    xstore::Array{cdbl,1}
    Li::Array{cint,1}
    Lx::Array{cdbl,1}
    Ui::Array{cint,1}
    Ux::Array{cdbl,1}
    Wi::Array{cint,1}
    Wx::Array{cdbl,1}
    work0::Array{cdbl,1}        # workspace zeroed
    iwork::Array{cint,1}        # workspace uninitialized
    function BLU(m::cint)
        isize = BASICLU_SIZE_ISTORE_1 + BASICLU_SIZE_ISTORE_M * m
        xsize = BASICLU_SIZE_XSTORE_1 + BASICLU_SIZE_XSTORE_M * m
        fsize = m               # initial length of Li, Lx, Ui, Ux, Wi, Wx
        istore = Array{cint}(undef,isize)
        xstore = Array{cdbl}(undef,xsize)
        Li = Array{cint}(undef,fsize)
        Lx = Array{cdbl}(undef,fsize)
        Ui = Array{cint}(undef,fsize)
        Ux = Array{cdbl}(undef,fsize)
        Wi = Array{cint}(undef,fsize)
        Wx = Array{cdbl}(undef,fsize)
        work0 = zeros(cdbl, m)
        iwork = Array{cint}(undef,m)
        err = ccall((:basiclu_initialize, libbasiclu), cint,
                    (cint, cint_ptr, cdbl_ptr), m, istore, xstore)
        if err != BASICLU_OK
            msg = @sprintf("basiclu_initialize() status code: %d", err)
            error(msg)
        end
        xstore[BASICLU_MEMORYL] = fsize
        xstore[BASICLU_MEMORYU] = fsize
        xstore[BASICLU_MEMORYW] = fsize
        return new(istore, xstore, Li, Lx, Ui, Ux, Wi, Wx, work0, iwork)
    end
end

# =============================================================================
# realloc
# =============================================================================

function realloc(this::BLU)
        addmemL = convert(cint, this.xstore[BASICLU_ADD_MEMORYL])
        addmemU = convert(cint, this.xstore[BASICLU_ADD_MEMORYU])
        addmemW = convert(cint, this.xstore[BASICLU_ADD_MEMORYW])
        if addmemL > 0
            memsize = length(this.Li) + addmemL
            memsize = ceil(realloc_factor*memsize)
            memsize = convert(cint, memsize)
            @assert memsize >= length(this.Li) + addmemL
            resize!(this.Li, memsize)
            resize!(this.Lx, memsize)
            this.xstore[BASICLU_MEMORYL] = memsize
        end
        if addmemU > 0
            memsize = length(this.Ui) + addmemU
            memsize = ceil(realloc_factor*memsize)
            memsize = convert(cint, memsize)
            @assert memsize >= length(this.Ui) + addmemU
            resize!(this.Ui, memsize)
            resize!(this.Ux, memsize)
            this.xstore[BASICLU_MEMORYU] = memsize
        end
        if addmemW > 0
            memsize = length(this.Wi) + addmemW
            memsize = ceil(realloc_factor*memsize)
            memsize = convert(cint, memsize)
            @assert memsize >= length(this.Wi) + addmemW
            resize!(this.Wi, memsize)
            resize!(this.Wx, memsize)
            this.xstore[BASICLU_MEMORYW] = memsize
        end
end

# =============================================================================
# gather_work
# =============================================================================

function gather_work(this::BLU, nz::cint)
    m = convert(cint, this.xstore[BASICLU_DIM])
    nzind = this.iwork[1:nz] .+ 1
    sort!(nzind)
    lhs = spvector(m, nzind, this.work0[nzind])
    this.work0[nzind] .= 0
    return lhs
end

# =============================================================================
# initialize
# =============================================================================
"""
    initialize(m)

Return a new BASICLU object for matrices of dimension m.
"""
function initialize(m::cint)
    return BLU(m)
end

# =============================================================================
# factorize
# =============================================================================

import LinearAlgebra.factorize

"""
    factorize(this, B)

Load sparse matrix into BASICLU object and factorize it. @B must be square
and have the same dimension for which @this was initialized.

Return status code.
"""
function factorize(this::BLU, B::spmatrix)
    m = convert(cint, this.xstore[BASICLU_DIM])
    nrow, ncol = size(B)
    @assert nrow == ncol
    @assert nrow == m
    Bp = B.colptr.-1
    Bi = B.rowval.-1
    Bx = B.nzval                # don't need a copy
    c0ntinue = 0
    err = BASICLU_OK
    while true
        err = ccall((:basiclu_factorize, libbasiclu), cint,
                    (cint_ptr, cdbl_ptr,
                     cint_ptr, cdbl_ptr, cint_ptr, cdbl_ptr, cint_ptr, cdbl_ptr,
                     cint_ptr, cint_ptr, cint_ptr, cdbl_ptr, cint),
                    this.istore, this.xstore,
                    this.Li, this.Lx, this.Ui, this.Ux, this.Wi, this.Wx,
                    Bp, pointer(Bp, 2), Bi, Bx, c0ntinue)
        if err != BASICLU_REALLOCATE break; end
        realloc(this)
        c0ntinue = 1
    end
    return err
end

# =============================================================================
# get_factors
# =============================================================================
"""
    get_factors(this)

Export LU factors after fresh factorization.

# Example
```
m = 100
B = spdiagm(-1 => ones(m-1), 0 => 4*ones(m), 1 => ones(m-1))
this = BasicLU.initialize(m)
BasicLU.factorize(this, B)
(L,U,p,q) = BasicLU.get_factors(this)
norm(L*U-B[p,q], Inf)
1.1102230246251565e-16
```
"""
function get_factors(this::BLU)
    m = convert(cint, this.xstore[BASICLU_DIM])
    Lnz = convert(cint, this.xstore[BASICLU_LNZ])
    Unz = convert(cint, this.xstore[BASICLU_UNZ])
    rowperm = Array{cint}(undef,m)
    colperm = Array{cint}(undef,m)
    Lp = Array{cint}(undef,m+1)
    Li = Array{cint}(undef,Lnz+m)
    Lx = Array{cdbl}(undef,Lnz+m)
    Up = Array{cint}(undef,m+1)
    Ui = Array{cint}(undef,Unz+m)
    Ux = Array{cdbl}(undef,Unz+m)
    err = ccall((:basiclu_get_factors, libbasiclu), cint,
                (cint_ptr, cdbl_ptr,
                 cint_ptr, cdbl_ptr, cint_ptr, cdbl_ptr, cint_ptr, cdbl_ptr,
                 cint_ptr, cint_ptr,
                 cint_ptr, cint_ptr, cdbl_ptr,
                 cint_ptr, cint_ptr, cdbl_ptr),
                this.istore, this.xstore,
                this.Li, this.Lx, this.Ui, this.Ux, this.Wi, this.Wx,
                rowperm, colperm, Lp, Li, Lx, Up, Ui, Ux)
    if err != BASICLU_OK
        msg = @sprintf("basiclu_get_factors() status code: %d", err)
        error(msg)
    end
    rowperm .+= 1
    colperm .+= 1
    L = spmatrix(m, m, Lp.+1, Li.+1, Lx)
    U = spmatrix(m, m, Up.+1, Ui.+1, Ux)
    @assert istril(L)
    @assert istriu(U)
    return (L, U, rowperm, colperm)
end

# =============================================================================
# solve
# =============================================================================
"""
    solve(this, rhs, trans)

Solve linear system with factorized matrix.

@rhs must be a dense or sparse vector. The solution is returned sparse when @rhs
is sparse and dense otherwise.

@trans must be 't' or 'T' for transposed solve, and any other character for
forward solve.
"""
function solve(this::BLU, rhs::cvec, trans::Char) # dense solve
    m = convert(cint, this.xstore[BASICLU_DIM])
    @assert length(rhs) == m
    lhs = cvec(undef,m)
    err = ccall((:basiclu_solve_dense, libbasiclu), cint,
                (cint_ptr, cdbl_ptr,
                 cint_ptr, cdbl_ptr, cint_ptr, cdbl_ptr, cint_ptr, cdbl_ptr,
                 cdbl_ptr, cdbl_ptr, Cchar),
                this.istore, this.xstore,
                this.Li, this.Lx, this.Ui, this.Ux, this.Wi, this.Wx,
                rhs, lhs, trans)
    if err != BASICLU_OK
        msg = @sprintf("basiclu_solve_dense() status code: %d", err)
        error(msg)
    end
    return lhs
end

function solve(this::BLU, rhs::spvector, trans::Char) # sparse solve
    m = convert(cint, this.xstore[BASICLU_DIM])
    @assert length(rhs) == m
    nzlhs = Ref{cint}(0)
    err = ccall((:basiclu_solve_sparse, libbasiclu), cint,
                (cint_ptr, cdbl_ptr,
                 cint_ptr, cdbl_ptr, cint_ptr, cdbl_ptr, cint_ptr, cdbl_ptr,
                 cint, cint_ptr, cdbl_ptr,
                 Ref{cint}, cint_ptr, cdbl_ptr, Cchar),
                this.istore, this.xstore,
                this.Li, this.Lx, this.Ui, this.Ux, this.Wi, this.Wx,
                nnz(rhs), rhs.nzind.-1, rhs.nzval,
                nzlhs, this.iwork, this.work0, trans)
    nzlhs = nzlhs[]
    lhs = gather_work(this, nzlhs)
    if err != BASICLU_OK
        msg = @sprintf("basiclu_solve_sparse() status code: %d", err)
        error(msg)
    end
    return lhs
end

# =============================================================================
# solve4update
# =============================================================================
"""
    solve4update(this, rhs, getsolution=false)

Solve linear system in preparation to update the factorization.

@rhs must be a sparse vector or a column index. When @rhs is a vector, then it
is the column to be inserted into the factorized matrix. When @rhs is an index,
than it is the column of the factorized matrix to be replaced.

@getsolution indicates if the solution is to be returned, or only the update is
to be prepared.
"""
function solve4update(this::BLU, rhs, getsolution::Bool=false)
    if typeof(rhs) != spvector && !(length(rhs) == 1 && isinteger(rhs))
        error("rhs must be a sparse vector or a column index")
    end
    m = convert(cint, this.xstore[BASICLU_DIM])
    trans = typeof(rhs) != spvector
    if trans
        @assert 1 <= rhs && rhs <= m
    else
        @assert length(rhs) == m
    end
    nzrhs = trans ? 0 : nnz(rhs)
    irhs = trans ? Ref{cint}(rhs-1) : rhs.nzind.-1
    xrhs = trans ? C_NULL : rhs.nzval
    nzlhs = getsolution ? Ref{cint}(0) : C_NULL
    err = BASICLU_OK
    while true
        err = ccall((:basiclu_solve_for_update, libbasiclu), cint,
                    (cint_ptr, cdbl_ptr,
                     cint_ptr, cdbl_ptr, cint_ptr, cdbl_ptr, cint_ptr, cdbl_ptr,
                     cint, cint_ptr, cdbl_ptr,
                     cint_ptr, cint_ptr, cdbl_ptr, Cchar),
                    this.istore, this.xstore,
                    this.Li, this.Lx, this.Ui, this.Ux, this.Wi, this.Wx,
                    nzrhs, irhs, xrhs,
                    nzlhs, this.iwork, this.work0, trans ? 'T' : 'N')
        if err != BASICLU_REALLOCATE break; end
        realloc(this)
    end
    lhs = nothing
    if getsolution
        nzlhs = nzlhs[]
        lhs = gather_work(this, nzlhs)
    end
    if err != BASICLU_OK
        msg = @sprintf("basiclu_solve_for_update() status code: %d", err)
        error(msg)
    end
    return lhs
end

# =============================================================================
# update
# =============================================================================
"""
    update(this, xtbl)

Update the factorization after a column modification. The column position and
the new column must have been set in previous calls to @solve4update.

@xtbl is the pivot element in the simplex tableau. Used only to compute the
pivot error.

Return estimated error of the new pivot element.
"""
function update(this::BLU, xtbl::cdbl)
    err = BASICLU_OK
    while true
        err = ccall((:basiclu_update, libbasiclu), cint,
                    (cint_ptr, cdbl_ptr,
                     cint_ptr, cdbl_ptr, cint_ptr, cdbl_ptr, cint_ptr, cdbl_ptr,
                     cdbl),
                    this.istore, this.xstore,
                    this.Li, this.Lx, this.Ui, this.Ux, this.Wi, this.Wx,
                    xtbl)
        if err != BASICLU_REALLOCATE break; end
        realloc(this)
    end
    if err != BASICLU_OK
        msg = @sprintf("basiclu_update() status code: %d", err)
        error(msg)
    end
    return this.xstore[BASICLU_PIVOT_ERROR]
end

# =============================================================================
# high level interface for maxvolume()
# =============================================================================

mutable struct basiclu_object
    istore::Ptr{cint}
    xstore::Ptr{cdbl}
    Li::Ptr{cint}
    Ui::Ptr{cint}
    Wi::Ptr{cint}
    Lx::Ptr{cdbl}
    Ux::Ptr{cdbl}
    Wx::Ptr{cdbl}
    lhs::Ptr{cdbl}
    ilhs::Ptr{cint}
    nzlhs::cint
    realloc_factor::cdbl
    function basiclu_object(m::cint)
        obj = new()
        err = ccall((:basiclu_obj_initialize, libbasiclu), cint,
                    (Ptr{basiclu_object}, cint),
                    pointer_from_objref(obj), m)
        if err != BASICLU_OK
            msg = @sprintf("basiclu_obj_initialize() status code: %d", err)
            error(msg)
        end
        finalizer(obj) do x
            ccall((:basiclu_obj_free, libbasiclu), Cvoid,
                  (Ptr{basiclu_object},), pointer_from_objref(x))
        end
    end
end

function get_dim(obj::basiclu_object)
    ccall((:basiclu_obj_get_dim, libbasiclu), cint,
          (Ptr{basiclu_object},), pointer_from_objref(obj))
end

function maxvolume(obj::basiclu_object, A::spmatrix, basis::Array{cint,1},
                   volumetol::cdbl=2.0)
    m, n = size(A)
    if m != get_dim(obj)
        error("Number of rows of A does not match dimension of basiclu object")
    end
    if length(basis) != get_dim(obj)
        error("Basis has incorrect length")
    end
    isbasic = zeros(cint, n)
    isbasic[basis] .= 1
    if sum(isbasic) != m
        error("Duplicate index in basis")
    end
    cbasis = basis.-1
    Ap = A.colptr.-1
    Ai = A.rowval.-1
    Ax = A.nzval                # don't need a copy
    p_nupdate = Ref{cint}(0)
    err = ccall((:basiclu_obj_maxvolume, libbasiclu), cint,
                (Ptr{basiclu_object}, cint, Ptr{cint}, Ptr{cint}, Ptr{cdbl},
                 Ptr{cint}, Ptr{cint}, cdbl, Ptr{cint}),
                pointer_from_objref(obj), n, Ap, Ai, Ax, cbasis, isbasic,
                volumetol, p_nupdate)
    basis[:] = cbasis.+1
    if err != BASICLU_OK
        msg = @sprintf("basiclu_obj_maxvolume() status code: %d", err)
        error(msg)
    end
    return p_nupdate[]
end

"""
    basis, obj = maxvolume_basis(A::spmatrix)

    Returns a set of column indices for the matrix AI = [A sparse(I, m, m)] such
    that AI[:,basis] is nonsingular and the number of slack columns in the basis
    is minimum (this is the row rank deficiency of A).
"""
function maxvolume_basis(A::spmatrix; volumetol::cdbl=2.0, lindeptol::cdbl=1e-8,
                         maxpass::cint=2, verbose::Bool=false)
    m, n = size(A)
    rowmax = maximum(abs.(A), dims=2)[:]
    rowmax = max.(rowmax, 1.)
    AI = [A spdiagm(0 => lindeptol.*rowmax)]
    basis = collect(n+1:n+m)
    obj = basiclu_object(m)
    for pass = 1:maxpass
        nupdate = maxvolume(obj, AI, basis, volumetol)
        verbose && @printf("pass %d: %d updates\n", pass, nupdate)
        if nupdate == 0 break; end
    end
    return basis, obj
end

end

ext: jl


text:
# AUTO GENERATED FILE - DO NOT EDIT

export dugc_pagemenu

"""
    dugc_pagemenu(;kwargs...)

A PageMenu component.

Keyword arguments:
- `id` (String; optional): The ID used to identify this component in Dash callbacks.
- `dummy` (String; optional)
- `dummy2` (String; optional)
- `loading_state` (optional): Object that holds the loading state object coming from dash-renderer. loading_state has the following type: lists containing elements 'is_loading', 'prop_name', 'component_name'.
Those elements have the following types:
  - `is_loading` (Bool; optional): Determines if the component is loading or not
  - `prop_name` (String; optional): Holds which property is loading
  - `component_name` (String; optional): Holds the name of the component that is loading
"""
function dugc_pagemenu(; kwargs...)
        available_props = Symbol[:id, :dummy, :dummy2, :loading_state]
        wild_props = Symbol[]
        return Component("dugc_pagemenu", "PageMenu", "dash_user_guide_components", available_props, wild_props; kwargs...)
end

max_stars_repo_name: joelostblom/dash-docs

max_issues_repo_name: joelostblom/dash-docs

max_forks_repo_name: joelostblom/dash-docs


text:
using Ripserer
using Test

using Ripserer: Chain, ChainElement, ReducedMatrix, bulk_insert!

for F in (Mod{2}, Mod{3}, Mod{257}, Rational{Int})
    @testset "ReducedMatrix with $F" begin
        I = Simplex{2,Int,Int}
        S = Simplex{3,Int,Int}

        columns = I.([3, 10, 6, 8], 1)
        elements = ChainElement{I,F}.(columns)

        @testset "a fresh ReducedMatrix is empty" begin
            @test begin
                @inferred ReducedMatrix{I,F,S}()
                @inferred ReducedMatrix{S,F,I}()
                true
            end

            matrix = ReducedMatrix{I,F,S}()
            @test length(matrix) == 0
            for col in Iterators.flatten((columns, elements))
                @test isempty(matrix[col])
            end
        end

        @testset "`setindex!` adds a column and changes length" begin
            matrix = ReducedMatrix{I,F,S}()
            vals1 = Chain{F,S}([S(1, 1), S(2, 1), S(3, 1), S(4, 1)])

            matrix[columns[1]] = vals1
            @test length(matrix) == 1
            @test matrix[elements[1]] == vals1

            for col in Iterators.flatten((columns[2:end], elements[2:end]))
                @test isempty(matrix[col])
            end

            vals2 = Chain{F,S}([S(1, 1), S(2, 1)])
            matrix[columns[2]] = vals2

            @test length(matrix) == 2
            @test matrix[elements[2]] == vals2

            for col in Iterators.flatten((columns[3:end], elements[3:end]))
                @test !haskey(matrix, col)
            end
        end

        @testset "`bulk_insert!`" begin
            matrix = ReducedMatrix{I,F,S}()
            bulk_insert!(matrix, ())
            @test length(matrix) == 0

            pairs = [
                (S(1, 1), I(2, 2)),
                (S(3, 3), I(4, 4)),
                (S(5, 5), I(6, 6)),
                (S(7, 7), I(8, 8)),
            ]
            bulk_insert!(matrix, pairs)

            for (σ, τ) in pairs
                @test matrix[τ] == Chain{F,S}([σ])
            end
            @test length(matrix) == 4
        end
    end
end


text:
################################################################################
# Iterative Best Response Example
################################################################################
# using Algames
using StaticArrays
using LinearAlgebra

T = Float64

# Define the dynamics of the system
p = 3 # Number of players
d = 2
model = DoubleIntegratorGame(p=p, d=d) # game with 3 players with unicycle dynamics
n = model.n
m = model.m

# Define the horizon of the problem
N = 20 # N time steps
dt = 0.1 # each step lasts 0.1 second
probsize = ProblemSize(N,model) # Structure holding the relevant sizes of the problem

# Define the objective of each player
# We use a LQR cost
Q = [Diagonal(50*ones(SVector{model.ni[i],T})) for i=1:p] # Quadratic state cost
R = [Diagonal(0.01*ones(SVector{model.mi[i],T})) for i=1:p] # Quadratic control cost
# Desrired state
xf = [SVector{model.ni[1],T}([ 0, 0*0.0,0,0]),
      SVector{model.ni[1],T}([ 0, 0*0.2,0,0]),
	  SVector{model.ni[1],T}([ 0, 0*0.4,0,0]),
	  SVector{model.ni[1],T}([ 0, 0*0.6,0,0]),
	  SVector{model.ni[1],T}([ 0, 0*0.8,0,0]),
	  SVector{model.ni[1],T}([ 0, 0*1.0,0,0]),
	  SVector{model.ni[1],T}([ 0, 0*1.2,0,0]),
	  SVector{model.ni[1],T}([ 0, 0*1.4,0,0]),
	  SVector{model.ni[1],T}([ 0, 0*1.6,0,0]),
	  SVector{model.ni[1],T}([ 0, 0*1.8,0,0]),
      ]
xf = xf[1:p]
# Desired control
uf = [zeros(SVector{model.mi[i],T}) for i=1:p]
# Objectives of the game
game_obj = GameObjective(Q,R,xf,uf,N,model)
radius = 3.0*ones(p)
μ = 2.0*ones(p)
add_collision_cost!(game_obj, radius, μ)

# Define the constraints that each player must respect
game_con = GameConstraintValues(probsize)
# # Add collision avoidance
radius = 0.25
add_collision_avoidance!(game_con, radius)

# Define the initial state of the system
x0 = [
	[ 0.00,  0.0,  0.0,  0.0],
	[ 0.05,  0.2,  0.0,  0.0],
	[ 0.10,  0.4,  0.0,  0.0],
	[ 0.15,  0.6,  0.0,  0.0],
	[ 0.20,  0.8,  0.0,  0.0],
	[ 0.25,  1.0,  0.0,  0.0],
	[ 0.30,  1.2,  0.0,  0.0],
	[ 0.35,  1.4,  0.0,  0.0],
	[ 0.40,  1.6,  0.0,  0.0],
	[ 0.45,  1.8,  0.0,  0.0],
	]
θ = range(0.0, length=2, stop=2π)
rad = 0.5
x0 = [[(rad+θ/10)*cos(θ), (rad+θ/10)*sin(θ), 0, 0] for θ in range(0.0, length=p, stop=2π*(1-1/p))]
x0 = SVector{model.n,T}(reshape(vcat([x0[i]' for i=1:p]...), model.n))

# Define the Options of the solver
opts_alg = Options(Δ_min=1e-9, inner_print=false)
opts_ibr = Options(dual_reset=false, outer_iter=10, inner_iter=25, Δ_min=1e-9, inner_print=false)
# opts_ibr = Options(dual_reset=false, outer_iter=7, inner_iter=20, Δ_min=1e-5, inner_print=false)
# ibr_opts = IBROptions(Δ_min=1e-9, ibr_iter=100, ordering=shuffle!([1,2,3,4,5,6,7,8,9,10]))
ibr_opts = IBROptions(Δ_min=1e-9, ibr_iter=15, ordering=[1,2,3,4,5,6,7,8,9,10], live_plotting=true)
ibr_opts = IBROptions(Δ_min=1e-9, ibr_iter=100, ordering=[2,1,3,4,5,6,7,8,9,10], live_plotting=false)

# Define the game problem
prob0_alg = GameProblem(N,dt,x0,model,opts_alg,game_obj,game_con)
prob0_ibr = GameProblem(N,dt,x0,model,opts_ibr,game_obj,game_con)
prob_alg = deepcopy(prob0_alg)
prob_ibr = deepcopy(prob0_ibr)

# cost.(prob_ibr.game_obj.obj)
# game_obj.obj

# Solve the problem
@elapsed newton_solve!(prob_alg)
@elapsed ibr_newton_solve!(prob_ibr, ibr_opts=ibr_opts)

residual!(prob_ibr, prob_ibr.pdtraj)
norm(prob_ibr.core.res, 1)/length(prob_ibr.core.res)
residual!(prob_alg, prob_alg.pdtraj)
norm(prob_alg.core.res, 1)/length(prob_alg.core.res)
prob_ibr.stats

plt = plot()
plot!(plt, cumsum(prob_alg.stats.t_elap), log.(10, prob_alg.stats.res))
plot!(plt, cumsum(prob_alg.stats.t_elap), log.(10, [v.max for v in prob_alg.stats.dyn_vio]))
plot!(plt, cumsum(prob_alg.stats.t_elap), log.(10, [v.max for v in prob_alg.stats.sta_vio]))
plot!(plt, cumsum(prob_ibr.stats.t_elap), log.(10, prob_ibr.stats.res))
plot!(plt, cumsum(prob_ibr.stats.t_elap), log.(10, [v.max for v in prob_ibr.stats.dyn_vio]))
plot!(plt, cumsum(prob_ibr.stats.t_elap), log.(10, [v.max for v in prob_ibr.stats.sta_vio]))

# Visualize the Results
plot(prob_alg.model, prob_alg.pdtraj.pr)
plot!(prob_ibr.model, prob_ibr.pdtraj.pr)
plot(prob_alg.stats)
plot!(prob_ibr.stats)
const Algames = Main
using Plots

# IBR
[prob_ibr.game_con.state_conval[1][1].λ[k][1] for k=1:N-1][end]
[prob_ibr.game_con.state_conval[1][2].λ[k][1] for k=1:N-1][end]

[prob_ibr.game_con.state_conval[2][1].λ[k][1] for k=1:N-1][end]
[prob_ibr.game_con.state_conval[2][2].λ[k][1] for k=1:N-1][end]

[prob_ibr.game_con.state_conval[3][1].λ[k][1] for k=1:N-1][end]
[prob_ibr.game_con.state_conval[3][2].λ[k][1] for k=1:N-1][end]

# Algames
[prob_alg.game_con.state_conval[1][1].λ[k][1] for k=1:N-1][end]
[prob_alg.game_con.state_conval[1][2].λ[k][1] for k=1:N-1][end]

[prob_alg.game_con.state_conval[2][1].λ[k][1] for k=1:N-1][end]
[prob_alg.game_con.state_conval[2][2].λ[k][1] for k=1:N-1][end]

[prob_alg.game_con.state_conval[3][1].λ[k][1] for k=1:N-1][end]
[prob_alg.game_con.state_conval[3][2].λ[k][1] for k=1:N-1][end]

#Conclusions
	# - if there is no coupling at all (dynamics, cost, state constraints)
	# IBR marginally better, but should be the same since we exploit sparsity using a sparse linear solver.

	# - if there is cost coupling only
	# IBR converges to a different local NE
	# the solution depends on the players' ordering
	# the solution does not reflect the symmetry of information between players
	# some players having cost large advantages over

	# - if there is state constraints coupling (when state constraint are active)
	# IBR converges to a different local NNE
	# the solution depends on the players' ordering
	# the solution does not reflect the symmetry of information between players
	# some players having large cost advantages over
	# converges to a solution which is also a Stackelberg equilibrium for 2 players,
	# and chain-Stackelberg for multi player scenarios.
	# really hard time converging the solutions are oscillating in practice


text:
module Representation
    export Organism, Population
    using Random, Distributions

    # Struct that represents a single organism
    mutable struct Organism
        gauss_kernels::Array{Tuple{Float64, Float64, Float64}, 1}
        σ::Array{Float64, 1}
        α::Array{Float64, 1}
        fitness::Union{Float64, Missing}
        hit_ratio::Union{Int64, Missing}
    end
    # Struct that represents a population as a list of organisms
    mutable struct Population
        members::Array{Organism, 1}
    end

    # Generate N organisms, therefore a population
    function generate_population(population_size, n_kernels, initial_σ)
        organism_list = Organism[]
        for i in range(1, population_size)
            kernels = Tuple{Float64, Float64, Float64}[]
            for k in range(1, n_kernels)
                weight, c, γ = generate_kernel_params(initial_σ)
                push!(kernels, tuple(weight, c, γ))
            end
            σ = rand(Normal(0.0, 1.0), 1)[1]
            this_organism = Organism(kernels,
                                    repeat([σ], (n_kernels * length(kernels[1]))),
                                    repeat([0.0], (n_kernels * length(kernels[1]))),
                                    missing,
                                    missing)
            push!(organism_list, this_organism)
        end
        #c = rand(range(0.817, step=0.001, length=floor(Int, (1.0-0.817)/0.001) + 1))
        population = Population(organism_list)
        return population
    end

    # Function to generate a single kernel
    function generate_kernel_params(σ)
        normal_d = Normal(0.0, σ)
        weight = rand(normal_d, 1)[1]
        c = rand(normal_d, 1)[1]
        γ = rand(normal_d, 1)[1]
        return weight, c, γ
    end

end  # module Representation


text:
include(joinpath("..", "common", "utils.jl"))
using SparseArrays: spzeros
using Logging: @info
using Printf: @sprintf
using LinearAlgebra: rank, eigvals

⊗ = kron

function build_operator((Nh, Nu), K; T = Float64, α = 1)

  # Polynomial orders of u and h
  @assert Nu ≥ Nh

  I_KK = sparse(I, K, K)

  # Create the element operators
  ξh, ωh = lglpoints(T, Nh)

  # Create the element operators
  ξu, ωu = lglpoints(T, Nu)

  # Get the scatter matrix
  Qh = scatter_matrix(Nh, K)
  Qu = scatter_matrix(Nu, K)

  # domain
  (x0, x1) = (T(0), T(1))

  # cell size
  Δx = (x1 - x0) / K

  xhcg = [x0 .+ Δx * ((ξh[1:end-1] .+ 1) / 2 .+ (0:(K-1))')[:]; x1]
  xucg = [x0 .+ Δx * ((ξu[1:end-1] .+ 1) / 2 .+ (0:(K-1))')[:]; x1]

  # Jacobian determinant
  J = Δx / 2

  # Form the grid mass matrices
  Wh = J * I_KK ⊗ Diagonal(ωh)
  Wu = J * I_KK ⊗ Diagonal(ωu)

  # CG mass matrices
  Mh = Qh' * Wh * Qh
  Mu = Qu' * Wu * Qu

  # CG stiffness matrix
  Ihu = interpolationmatrix(ξh, ξu)
  Dhh = spectralderivative(ξh) / J
  Dhu = Ihu * Dhh
  S = Qu' * Wu * (I_KK ⊗ Dhu) * Qh

  Nph = length(xhcg)
  Npu = length(xucg)

  eu1 = sparse([1], [Npu], [1], 1, Npu)
  eu0 = sparse([1], [1], [1], 1, Npu)

  Sfull = [spzeros(Nph, Nph) -S'
           S -α * eu0' * eu0 - α * eu1' * eu1]

  Mfull = [Mh spzeros(Nph, Npu)
           spzeros(Npu, Nph) Mu]
  Afull = Mfull \ Sfull

  # Return everything as a named tuple
  return (M = Mfull, S = Sfull, A = Afull, xh = xhcg, xu = xucg)
end

function test_operator((Nh, Nu), Ks; final_time = 3, T = Float64)
  @info @sprintf """
  (Nh, Nu)   = (%d, %d)
  num levels = %d
  """ Nh Nu length(Ks)

  # Create an exact solution
  # f(x) = exp(sin( T(4) * π * x))
  # g(x) = exp(sin(-T(4) * π * x))
  f(x) = sin( T(4) * π * x)
  g(x) = sin(-T(4) * π * x)

  u_ex(x, t) = (f(x + t) - g(x - t)) / 2
  h_ex(x, t) = (f(x + t) + g(x - t)) / 2

  # Storage for the errors
  ϵ = zeros(T, length(Ks))

  # Loop over the levels
  for (l, K) in enumerate(Ks)
    Op = build_operator((Nh, Nu), K; T = T)

    # build the initial condition
    q0 = [h_ex.(Op.xh, 0); u_ex.(Op.xu, 0)]

    # use exponential time integration because I am lazy
    qf = exp(Matrix(Op.A) * final_time) * q0

    # Calculate the error
    e = qf - [h_ex.(Op.xh, final_time); u_ex.(Op.xu, final_time)]
    ϵ[l] = sqrt(e' * Op.M * e)

    # Display some data
    if l > 1
      rate = ((log(ϵ[l]) - log(ϵ[l-1])) / (log(Ks[l-1]) - log(Ks[l])))
      @info @sprintf """
      level = %d
      K     = %d
      error = %.2e
      rate  = %.2e
      """ l K ϵ[l] rate
    else
      @info @sprintf """
      level = %d
      K     = %d
      error = %.2e
      """ l K ϵ[l]
    end
  end
end

function mode_check((Nh, Nu), K)
  Op = build_operator((Nh, Nu), K)
  @info @sprintf """
  (Nh, Nu)             = (%d, %d)
  number of elements   = %d
  number of zero modes = %d
  """ Nh Nu K size(Op.A, 1) - rank(Op.A)
end

using LinearAlgebra: eigen, norm
using PGFPlotsX
Op = build_operator((5,5), 20; α = 1)
ev = eigen(Matrix(Op.A))

# p = sortperm(abs.(ev.values))
# v1 = ev.vectors[:, p[1]]
vals = ev.values
xmin = min(-1e-10, minimum(real.(vals)))
xmax = max( 1ex-10, maximum(real.(vals)))
@pgf a = Axis(
              {
               xmin = xmin,
               xmax = xmax,
               # ymax = 40,
               ymin = 0,
               xlabel = "\$\\textrm{Re}(\\lambda)\$",
               ylabel = "\$\\textrm{Im}(\\lambda)\$",
              }
             );
@pgf push!(a, Plot(
                   {
                    only_marks,
                    color = "red",
                    mark = "x",
                   },
                   Table(real.(vals), imag.(vals)))
          );
mx = ceil(Int, maximum(imag.(vals/π)))
@pgf push!(a, Plot(
                   {
                    only_marks,
                    color = "blue",
                    mark = "o",
                   },
                   Table(zeros(2mx + 1), π*(-mx:mx)))
          );
pgfsave("spectra.pdf", a)

vals = ev.values[findall(real.(ev.values) .> -0.1)]
vals = vals[findall(imag.(vals) .≥ 0)]
p = sortperm(imag.(vals[findall(imag.(vals) .≥ 0)]))
vals = vals[p]
@pgf a = Axis(
              {
              }
             );
@pgf push!(a, Plot(
                   {
                    only_marks,
                    color = "red",
                    mark = "x",
                   },
                   Table(1:length(vals), imag.(vals)))
          );
@pgf push!(a, Plot(
                   {
                    only_marks,
                    color = "black",
                    mark = "x",
                   },
                   Table(1:length(vals), -1000*real.(vals) .+ π*(0:length(vals)-1)))
          );
@pgf push!(a, Plot(
                   {
                    only_marks,
                    color = "blue",
                    mark = "o",
                   },
                   Table(1:length(vals), π*(0:length(vals)-1)))
          );
pgfsave("spectra.pdf", a)


text:
execution_handle(::ExecutionPolicy) = C_NULL
delete_handle(::ExecutionPolicy) = C_NULL
getf_handle(::ExecutionPolicy) = C_NULL
getx_handle(::ExecutionPolicy) = C_NULL

module Execution

using Libdl
using POLO
using POLO: ProxGradient, ExecutionPolicy, AbstractLoss, AbstractLogger, AbstractTermination, init_wrapper, boost_wrapper, step_wrapper, smooth_wrapper, prox_wrapper
using Parameters
import POLO: initialize!, execution_handle, delete_handle, getf_handle, getx_handle, getx

include("serial.jl")
include("multithread.jl")
include("parameter_server.jl")

end


text:
function getdepth(str)
  str = string(str)
  depth = match(r"(UInt\d+)", str)
  if depth == nothing
    println("Error: failed to determine depth from ", str)
    exit(-1)
  end
  if depth[1] == "UInt8"
    rslt = UInt8
  elseif depth[1] == "UInt16"
    rslt = UInt16
  else
    println("Can't handle color depth ", depth[1])
    exit(-1)
  end
  return rslt
end

using Images, ImageCore
args = ARGS
if length(args) < 2
  println("Need at least one input file and one output file.")
  exit(-1)
end

prepped = false
final = nothing
what = nothing

while length(args) > 1
    filen = shift!(args)
    img = load(filen)
    if img == nothing
      println("Image failed to load.")
      exit(-1)
    end
    println("Loaded ", filen)
    if ~ prepped
        what = getdepth(typeof(img))
        final = view(reinterpret(what, img), 1:3, :, :)
        prepped = true
        print("Set base image\n")
    else
        final = max(final, view(reinterpret(what, img), 1:3, :, :))
    end
end

if what == UInt16
    saveit = colorview(RGB{N0f16}, final[1:3, :, :])
elseif what == UInt8
    saveit = colorview(RGB{N0f8}, final[1:3, :, :])
end

filen = shift!(args)
save(filen, saveit)

max_stars_repo_name: FatherGeoffHorton/AstroTools

max_issues_repo_name: FatherGeoffHorton/AstroTools

max_forks_repo_name: FatherGeoffHorton/AstroTools


text:
# Command line wrapper for external sort
#
# Generate a random file, sort it and print the result to screen.
#
# Usage: julia main.jl N M
#
# N is the number of random 4-byte integers to sort.
# M is the number of integers of in-memory capacity to do sorting.

include("es.jl")
include("helper.jl")

if length(ARGS) == 2
    N = parse(Int32, ARGS[1])
    M = parse(Int32, ARGS[2])
end

create_file(N)
external_sort(M)
read_file2screen("sorted-sample.bin")

max_stars_repo_name: rgaiacs/external-sort

max_issues_repo_name: rgaiacs/external-sort

max_forks_repo_name: rgaiacs/external-sort