text:
using ArchGDAL
using Test
include("../src/hilbert.jl")

coarse_geo = [-180.0, 0.0083333333333333, 0.0, 89.99999999999929, 0.0, -0.0083333333333333]
coarse_width_height = (43200, 21600)
fine_geo = [29.5734769457, 0.00027777777999999997, 0.0, 4.22813426181, 0.0, -0.00027777778]
fine_width_height = (19536, 20540)

origin, transform = geo_to_transform(coarse_geo)

xy1 = geo_to_xy_corner(origin, transform, 1, 1)
@test xy1[1] ≈ -180
@test xy1[2] ≈ 90
xy2 = geo_to_xy_corner(origin, transform, coarse_width_height[1] + 1, coarse_width_height[2] + 1)
@test xy2[1] ≈ 180
@test xy2[2] ≈ -90

xy3 = geo_to_xy_corner(origin, transform, 24, 36)
xy4 = geo_to_xy_center(origin, transform, 24, 36)
@test xy4[1] ≈ xy3[1] + coarse_geo[2] / 2
@test xy4[2] ≈ xy3[2] + coarse_geo[6] / 2 # Note that the offset is negative, -0.008333.

coarse_pg = PixelGrid((1, 1), coarse_width_height, geo_to_transform(coarse_geo)...)
xyb1 = xy_bounds(coarse_pg)
@test xyb1[1][1] ≈ -180
@test xyb1[1][2] ≈ 90
@test xyb1[2][1] ≈ 180
@test xyb1[2][2] ≈ -90

xy5 = geo_to_xy_center(origin, transform, 24, 36)
ij1 = pixel_containing(origin, transform, xy5)
@test ij1[1] == 24
@test ij1[2] == 36

xyb2 = xy_bounds(coarse_pg)
# Nudge the corner just a hair so it isn't right on the boundary.
xyb3 = ([xyb2[1][1], xyb2[1][2]], [xyb2[2][1] - 1e-5, xyb2[2][2] + 1e-5])
ij_corners = ij_cover_rect(origin, transform, xyb3)
@test ij_corners[1][1] == 1
@test ij_corners[1][2] == 1
@test ij_corners[2][1] == coarse_width_height[1]
@test ij_corners[2][1] == coarse_width_height[1]

bci_trial = [
   (([1, 1], [10, 10]), (200, 200), ((1, 1), (1, 1))),
   (((1, 1), (200, 200)), (200, 200), ((1, 1), (1, 1))),
   (((1, 1), (201, 250)), (200, 300), ((1, 1), (2, 1))),
   (((1, 1), (200, 301)), (200, 300), ((1, 1), (1, 2))),
   (((204, 1), (250, 601)), (200, 300), ((2, 1), (2, 3))),
   (((5, 50), (5, 50)), (2, 10), ((3, 5), (3, 5))),
   (((4, 50), (5, 50)), (2, 10), ((2, 5), (3, 5))),
   (((5, 40), (5, 50)), (2, 10), ((3, 4), (3, 5))),
   (((5, 50), (6, 50)), (2, 10), ((3, 5), (3, 5))),
   (((5, 50), (7, 50)), (2, 10), ((3, 5), (4, 5))),
]
for (ij_rect, bs, result) in bci_trial[8:8]
   ij = (Int.(ij_rect[1]), Int.(ij_rect[2]))
   bl1 = block_cover_ij_rect(ij, Int.(bs))
   @test bl1 == result
end

rectish = [
   ((1, 1), ((0, 2), (2, 0)), true),
   ((3, 1), ((0, 4), (2, 0)), false),
   ((1, 3), ((0, 4), (2, 0)), true),
   ((-1, 1), ((0, 4), (2, 0)), false),
   ((1, -1), ((0, 4), (2, 0)), false),
   ((-1, -1), ((0, 4), (2, 0)), false),
   ((3, 5), ((0, 4), (2, 0)), false),
   ((1.5, 3.5), ((1, 4), (2, 3)), true),
   ((.5, 3.5), ((1, 4), (2, 3)), false),
   ((1.5, 2.5), ((1, 4), (2, 3)), false),
]
for (pt, rect, result) in rectish
   @test pointinrect(pt, rect) == result
end

boundsrect = ([-6.0, 12.0], [-5.0, 11.0])
pt = [-4.9, 11.9]
@test !pointinrect(pt, boundsrect)

# Here's our stub setup.
# Coarse grid from ul=(-10, 16) to lr=(10, 0) in xy
# Give it w=20, h=16.
# Fine grid from ul=(-5, 12) to lr=(5, 4) in xy
# Give it w=10*5=50, h=8*5=40, so 5x the resolution of the coarse.
function make_coarse(A)
   StubBand{Int32}(
      A,
      [6, 2],  # blocksize
      Float64[-10, 16] , # origin
      Float64[1 0.0; 0.0 -1],  # pixel size
   )
end
coarse_band = make_coarse(fill(Int32(200), 20, 16))
function make_fine(A)
   StubBand{Float64}(
      A,
      [5, 3],  # blocksize
      Float64[-5, 12] , # origin
      Float64[.2 0.0; 0.0 -.2],  # pixel size
   )
end
fine_band = make_fine(fill(2.5, 50, 40))

coarse_pg = pixelgrid(coarse_band)
fine_pg = pixelgrid(fine_band)
# This should be the center of the pixel.
pc1 = pixel_containing(coarse_pg.origin, coarse_pg.transform, [-4.5, 11.5])
@test pc1 == [6, 5]
pc2 = pixel_containing(coarse_pg.origin, coarse_pg.transform, [-4.001, 11.001])
@test pc2 == [6, 5]
r1 = ij_cover_rect(coarse_pg.origin, coarse_pg.transform, ([-5, 12], [-4, 11]))
@test r1[1] == [6, 5]
@test r1[2][1] <= 7  # The cover rect can bring in a neighboring pixel.
@test r1[2][2] <= 6
r2 = ij_cover_rect(coarse_pg.origin, coarse_pg.transform, ([-4.999, 11.99], [4.999, 4.01]))
@test r2 == ([6, 5], [15, 12])
large_crop = crop_to(coarse_pg, xy_bounds(fine_pg))
@test large_crop.ul == (6, 5)

sb1 = load_single_block(coarse_band, coarse_pg, (1, 1))
sb2 = load_single_block(coarse_band, coarse_pg, (3, 3))
@test sb2.A[13, 5] == 200
# This is past the last block in x. There are 4 blocks.
@test_throws AssertionError sb3 = load_single_block(coarse_band, coarse_pg, (5, 3))

# Create an array with unique values we can check.
AA = Int32[encode_hilbert(x[1], x[2]) for x in CartesianIndices((20,16))]
copya = copy(AA)
cb2 = make_coarse(AA)
dg1 = load_data_grid(cb2, coarse_pg)
@test size(dg1.A) == (6 * 4, 16)  # Must be integral blocks to cover the domain.
@test dg1.A == copya
tune_band(fine_band, coarse_band, typemax(Int64))
@test fine_band.A[10, 10] ≈ 200 / 25

# Now try some nonzero structure.
A2 = fill(-floatmax(Float64), 50, 40)
fb2 = make_fine(A2)
tune_band(fb2, coarse_band, typemax(Int64))
@test all(fb2.A .== -floatmax(Float64))

# Now try some nonzero structure.
A3 = fill(-floatmax(Float64), 50, 40)
A3[1:5, 1:5] .= 1  # first 1x1 all filled.
A3[3:3, 7:7] .= 1  # Only one nonzero in this 1x1.
A3[6:7, 6:7] .= 1  # Four nonzero here.
fb3 = make_fine(A3)
@test sum(fb3.A .> 0) == 25 + 1 + 4
tune_band(fb3, coarse_band, 2)

@test all(fb3.A[1:5, 1:5] .≈ 200 / 25)
@test all(fb3.A[3:3, 7:7] .≈ 100)  # This will hit the max.
@test all(fb3.A[6:7, 6:7].≈ 200 / 4)

## A live data test.
hrsl_path = "~/data/inputs/HRSL/uganda2018/hrsl_uga_pop.tif"
landscan_path = "~/data/inputs/landscan/LandScan Global 2018/lspop2018/w001001.adf"
hrsl_exists = isfile(expanduser(hrsl_path))
landscan_exists = isfile(expanduser(landscan_path))

if hrsl_exists && landscan_exists
   out_path = "hrsl_ls_adjusted.tif"
   if ispath(out_path)
      rm(out_path)
   end
   assignweights(hrsl_path, landscan_path, out_path; max_pixels = 1)
   @test ispath(out_path)
   @test stat(out_path).size > 1210320848
else
   println("Could not find HRSL or landscan")
   println("$(hrsl_path)=$(hrsl_exists)")
   println("$(landscan_path)=$(landscan_exists)")
end


text:
"""
`Colorant{T,N}` is the abstract super-type of all types in ColorTypes,
and refers to both (opaque) colors and colors-with-transparency (alpha
channel) information.  `T` is the element type (extractable with
`eltype`) and `N` is the number of *meaningful* entries (extractable
with `length`), i.e., the number of arguments you would supply to the
constructor.
"""
Compat.@compat abstract type Colorant{T,N} end

# Colors (without transparency)
"""
`Color{T,N}` is the abstract supertype for a color (or
grayscale) with no transparency.
"""
Compat.@compat abstract type Color{T, N} <: Colorant{T,N} end

"""
`AbstractRGB{T}` is an abstract supertype for red/green/blue color types that
can be constructed as `C(r, g, b)` and for which the elements can be
extracted as `red(c)`, `green(c)`, `blue(c)`. You should *not* make
assumptions about internal storage order, the number of fields, or the
representation. One `AbstractRGB` color-type, `RGB24`, is not
parametric and does not have fields named `r`, `g`, `b`.
"""
Compat.@compat abstract type AbstractRGB{T}      <: Color{T,3} end

# Types with transparency
"""
`TransparentColor{C,T,N}` is the abstract type for any
color-with-transparency.  The `C` parameter refers to the type of the
pure color (without transparency) and can be extracted with
`color_type`. `T` is the element type of both `C` and the `alpha`
channel, and `N` has the same meaning as in `Colorant` (and is 1 larger
than the corresponding color type).

All transparent types should support two modes of construction:

    P(color, alpha)
    P(component1, component2, component3, alpha) (assuming a 3-component color)

For a `Colorant` `c`, the color component can be extracted with
`color(c)`, and the alpha channel with `alpha(c)`. Note that types
such as `ARGB32` do not have a field named `alpha`.

Most concrete types, like `RGB`, have both `ARGB` and `RGBA`
transparent analogs.  These two indicate different internal storage
order (see `AlphaColor` and `ColorAlpha`, and the `alphacolor` and
`coloralpha` functions).
"""
Compat.@compat abstract type TransparentColor{C<:Color,T,N} <: Colorant{T,N} end

"""
`AlphaColor` is an abstract supertype for types like `ARGB`, where the
alpha channel comes first in the internal storage order. **Note** that
the constructor order is still `(color, alpha)`.
"""
Compat.@compat abstract type AlphaColor{C,T,N} <: TransparentColor{C,T,N} end

"""
`ColorAlpha` is an abstract supertype for types like `RGBA`, where the
alpha channel comes last in the internal storage order.
"""
Compat.@compat abstract type ColorAlpha{C,T,N} <: TransparentColor{C,T,N} end

# These are types we'll dispatch on. Not exported.
Compat.@compat AbstractGray{T}                    = Color{T,1}
Compat.@compat Color3{T}                          = Color{T,3}
Compat.@compat TransparentGray{C<:AbstractGray,T} = TransparentColor{C,T,2}
Compat.@compat Transparent3{C<:Color3,T}          = TransparentColor{C,T,4}
Compat.@compat TransparentRGB{C<:AbstractRGB,T}   = TransparentColor{C,T,4}
Compat.@compat ColorantNormed{T<:Normed,N}        = Colorant{T,N}

"""
`RGB` is the standard Red-Green-Blue (sRGB) colorspace.  Values of the
individual color channels range from 0 (black) to 1 (saturated). If
you want "Integer" storage types (e.g., 255 for full color), use `N0f8(1)`
instead (see FixedPointNumbers).
"""
immutable RGB{T<:Fractional} <: AbstractRGB{T}
    r::T # Red [0,1]
    g::T # Green [0,1]
    b::T # Blue [0,1]

    (::Type{RGB{T}}){T}(r::T, g::T, b::T) = new{T}(r, g, b)
    # T might be a Normed, and so some inputs will result in an
    # error. Try to make it a nice error.
    function (::Type{RGB{T}}){T}(r::Real, g::Real, b::Real)
        checkval(T, r, g, b)
        new{T}(_rem(r,T), _rem(g,T), _rem(b,T))
    end
end
# For types that support Fractional, we need this to avoid a
# StackOverflow. For color types that only support AbstractFloat, this
# is handled by @make_constructors.
RGB{T<:Fractional}(r::T, g::T, b::T) = RGB{T}(r, g, b)

"""
`BGR` is a variant of `RGB` with the opposite storage order.  Note
that the constructor is still called in the order `BGR(r, g, b)`.
This storage order is noteworthy because on little-endian machines,
`BGRA` (with transparency) can be `reinterpret`ed to the `UInt32`
color format used by libraries such as Cairo and OpenGL.
"""
immutable BGR{T<:Fractional} <: AbstractRGB{T}
    b::T
    g::T
    r::T

    (::Type{BGR{T}}){T}(r::T, g::T, b::T) = new{T}(b, g, r)
    function (::Type{BGR{T}}){T}(r::Real, g::Real, b::Real)
        checkval(T, r, g, b)
        new{T}(_rem(b,T), _rem(g,T), _rem(r,T))
    end
end
BGR{T<:Fractional}(r::T, g::T, b::T) = BGR{T}(r, g, b)

"""
`RGB1` is a variant of `RGB` which has a padding element inserted at
the beginning. In some applications it may have useful
memory-alignment properties.

Like all other AbstractRGB objects, the constructor is still called
`RGB1(r, g, b)`.
"""
immutable RGB1{T<:Fractional} <: AbstractRGB{T}
    alphadummy::T
    r::T
    g::T
    b::T

    (::Type{RGB1{T}}){T}(r::T, g::T, b::T) = new{T}(one(T), r, g, b)
    function (::Type{RGB1{T}}){T}(r::Real, g::Real, b::Real)
        checkval(T, r, g, b)
        new{T}(one(T), _rem(r,T), _rem(g,T), _rem(b,T))
    end
end
RGB1{T<:Fractional}(r::T, g::T, b::T) = RGB1{T}(r, g, b)

"""
`RGB4` is a variant of `RGB` which has a padding element inserted at
the end. In some applications it may have useful
memory-alignment properties.

Like all other AbstractRGB objects, the constructor is still called
`RGB4(r, g, b)`.
"""
immutable RGB4{T<:Fractional} <: AbstractRGB{T}
    r::T
    g::T
    b::T
    alphadummy::T

    (::Type{RGB4{T}}){T}(r::T, g::T, b::T) = new{T}(r, g, b, one(T))
    function (::Type{RGB4{T}}){T}(r::Real, g::Real, b::Real)
        checkval(T, r, g, b)
        new{T}(_rem(r,T), _rem(g,T), _rem(b,T), one(T))
    end
end
RGB4{T<:Fractional}(r::T, g::T, b::T) = RGB4{T}(r, g, b)

"`HSV` is the Hue-Saturation-Value colorspace."
immutable HSV{T<:AbstractFloat} <: Color{T,3}
    h::T # Hue in [0,360)
    s::T # Saturation in [0,1]
    v::T # Value in [0,1]
end

"`HSB` (Hue-Saturation-Brightness) is an alias for `HSV`."
const HSB = HSV

"`HSL` is the Hue-Saturation-Lightness colorspace."
immutable HSL{T<:AbstractFloat} <: Color{T,3}
    h::T # Hue in [0,360)
    s::T # Saturation in [0,1]
    l::T # Lightness in [0,1]
end

"`HSI` is the Hue-Saturation-Intensity colorspace."
immutable HSI{T<:AbstractFloat} <: Color{T,3}
    h::T
    s::T
    i::T
end

"""
`XYZ` is the CIE 1931 XYZ colorspace. It is a linear colorspace,
meaning that mathematical operations such as addition, subtraction,
and scaling make "colorimetric sense" in this colorspace.
"""
immutable XYZ{T<:AbstractFloat} <: Color{T,3}
    x::T
    y::T
    z::T
end

"`xyY` is the CIE 1931 xyY (chromaticity + luminance) space"
immutable xyY{T<:AbstractFloat} <: Color{T,3}
    x::T
    y::T
    Y::T
end

"`Lab` is the CIELAB colorspace."
immutable Lab{T<:AbstractFloat} <: Color{T,3}
    l::T # Luminance in approximately [0,100]
    a::T # Red/Green
    b::T # Blue/Yellow
end

"`LCHab` is the Luminance-Chroma-Hue, Polar-Lab colorspace"
immutable LCHab{T<:AbstractFloat} <: Color{T,3}
    l::T # Luminance in [0,100]
    c::T # Chroma
    h::T # Hue in [0,360)
end

"`Luv` is the CIELUV colorspace"
immutable Luv{T<:AbstractFloat} <: Color{T,3}
    l::T # Luminance
    u::T # Red/Green
    v::T # Blue/Yellow
end

"`LCHuv` is the Luminance-Chroma-Hue, Polar-Luv colorspace"
immutable LCHuv{T<:AbstractFloat} <: Color{T,3}
    l::T # Luminance
    c::T # Chroma
    h::T # Hue
end

"`DIN99` is the (L99, a99, b99) adaptation of CIELAB"
immutable DIN99{T<:AbstractFloat} <: Color{T,3}
    l::T # L99
    a::T # a99
    b::T # b99
end

"`DIN99d` is the (L99d, a99d, b99d) improvement on DIN99"
immutable DIN99d{T<:AbstractFloat} <: Color{T,3}
    l::T # L99d
    a::T # a99d
    b::T # b99d
end

"`DIN99o` is the (L99o, a99o, b99o) adaptation of CIELAB"
immutable DIN99o{T<:AbstractFloat} <: Color{T,3}
    l::T # L99o
    a::T # a99o
    b::T # b99o
end

"""
`LMS` is the Long-Medium-Short colorspace based on activation of the
three cone photoreceptors.  Like `XYZ`, this is a linear color space.
"""
immutable LMS{T<:AbstractFloat} <: Color{T,3}
    l::T # Long
    m::T # Medium
    s::T # Short
end

"`YIQ` is a color encoding, for example used in NTSC transmission."
immutable YIQ{T<:AbstractFloat} <: Color{T,3}
    y::T
    i::T
    q::T
end

"`YCbCr` is the Y'CbCr color encoding often used in digital photography or video"
immutable YCbCr{T<:AbstractFloat} <: Color{T,3}
    y::T
    cb::T
    cr::T
end

"""
`RGB24` uses a `UInt32` representation of color, 0xAARRGGBB, where
R=red, G=green, B=blue and A is irrelevant. This format is often used
by external libraries such as Cairo.

`RGB24` colors do not have fields named `r`, `g`, `b`, but you can
still extract the individual components with `red(c)`, `green(c)`,
`blue(c)`.  You can construct them directly from a `UInt32`, or as
`RGB(r, g, b)`.
"""
immutable RGB24 <: AbstractRGB{N0f8}
    color::UInt32

    RGB24(col::UInt32, ::Type{Val{true}}) = new(col)
end
# The Val{true} constructor solves a consistency problem, one that's
# most obvious for Gray24: Gray24(x) interprets x as a value between 0
# and 1; since a UInt32 can have value 0 or 1 (among other
# possibilities), having Gray24(x::UInt32) mean something entirely
# different (interpreting the UInt32 as a bit pattern rather than a
# value) would be inconsistent, confusing, and error-prone. Because we
# can construct an RGB24 from a grayscale value (just repeating the
# value for all 3 color channels), the same problem extends to all the
# "24/32" color types.

# However, some constructors "build" RGB24s as a UInt32 bit pattern,
# so we need some way of turning a UInt32 directly into an RGB24.
# This is the role of the Val{true} constructor. This is an internal
# implementation detail, and user code should not use it directly:
# use reinterpret(RGB24, x::UInt32) instead. (The Val{true}
# constructor is used to implement reinterpret, see traits.jl.)
RGB24() = reinterpret(RGB24, UInt32(0))
_RGB24(r::UInt8, g::UInt8, b::UInt8) = reinterpret(RGB24, UInt32(r)<<16 | UInt32(g)<<8 | UInt32(b))
RGB24(r::N0f8, g::N0f8, b::N0f8) = _RGB24(reinterpret(r), reinterpret(g), reinterpret(b))
function RGB24(r, g, b)
    checkval(N0f8, r, g, b)
    RGB24(_rem(r,N0f8), _rem(g,N0f8), _rem(b,N0f8))
end
@deprecate RGB24(x::UInt32) reinterpret(RGB24, x)

"""
`ARGB32` uses a `UInt32` representation of color, 0xAARRGGBB, where
R=red, G=green, B=blue and A is the alpha channel. This format is
often used by external libraries such as Cairo.  On a little-endian
machine, this type has the exact same storage format as `BGRA{N0f8}`.

`ARGB32` colors do not have fields named `alpha`, `r`, `g`, `b`, but
you can still extract the individual components with `alpha(c)`,
`red(c)`, `green(c)`, `blue(c)`.  You can construct them directly from
a `UInt32`, or as `ARGB32(r, g, b, alpha)`.
"""
immutable ARGB32 <: AlphaColor{RGB24, N0f8, 4}
    color::UInt32

    ARGB32(col::UInt32, ::Type{Val{true}}) = new(col)
end
ARGB32() = reinterpret(ARGB32, UInt32(0xff)<<24)
_ARGB32(r::UInt8, g::UInt8, b::UInt8, alpha::UInt8) = reinterpret(ARGB32, UInt32(alpha)<<24 | UInt32(r)<<16 | UInt32(g)<<8 | UInt32(b))
ARGB32(r::N0f8, g::N0f8, b::N0f8, alpha::N0f8 = N0f8(1)) = _ARGB32(reinterpret(r), reinterpret(g), reinterpret(b), reinterpret(alpha))
ARGB32(r, g, b, alpha = 1) = ARGB32(N0f8(r), N0f8(g), N0f8(b), N0f8(alpha))
ARGB32{T}(c::AbstractRGB{T}, alpha = alpha(c)) = ARGB32(red(c), green(c), blue(c), alpha)
@deprecate ARGB32(x::UInt32) reinterpret(ARGB32, x)

"""
`Gray` is a grayscale object. You can extract its value with `gray(c)`.
"""
immutable Gray{T<:Union{Fractional,Bool}} <: AbstractGray{T}
    val::T

    (::Type{Gray{T}}){T}(val::T) = new{T}(val)
    function (::Type{Gray{T}}){T}(val::Real)
        checkval(T, val)
        new{T}(_rem(val,T))
    end
end
Gray{T<:Union{Fractional,Bool}}(val::T) = Gray{T}(val)

"""
`Gray24` uses a `UInt32` representation of color, 0xAAIIIIII, where
I=intensity (grayscale value) and A is irrelevant. Each II pair is
assumed to be the same.  This format is often used by external
libraries such as Cairo.

You can extract the single gray value with `gray(c)`.  You can
construct them directly from a `UInt32`, or as `Gray24(i)`. Note that
`i` is interpreted on a scale from 0 (black) to 1 (white).
"""
immutable Gray24 <: AbstractGray{N0f8}
    color::UInt32

    Gray24(c::UInt32, ::Type{Val{true}}) = new(c)
end
Gray24() = reinterpret(Gray24, UInt32(0))
_Gray24(val::UInt8) = (g = UInt32(val); reinterpret(Gray24, g<<16 | g<<8 | g))
Gray24(val::N0f8) = _Gray24(reinterpret(val))
function Gray24(val::Real)
    checkval(N0f8, val)
    Gray24(val%N0f8)
end
@deprecate Gray24(x::UInt32) reinterpret(Gray24, x)

"""
`AGray32` uses a `UInt32` representation of color, 0xAAIIIIII, where
I=intensity (grayscale value) and A=alpha. Each II pair is
assumed to be the same.  This format is often used by external
libraries such as Cairo.

You can extract the single gray value with `gray(c)` and the alpha as
`alpha(c)`.  You can construct them directly from a `UInt32`, or as
`AGray32(i,alpha)`. Note that `i` and `alpha` are interpreted on a
scale from 0 (black) to 1 (white).
"""
immutable AGray32 <: AlphaColor{Gray24, N0f8, 2}
    color::UInt32

    AGray32(c::UInt32, ::Type{Val{true}}) = new(c)
end
AGray32() = reinterpret(AGray32, 0xff000000)
_AGray32(val::UInt8, alpha::UInt8 = 0xff) = (g = UInt32(val); reinterpret(AGray32, UInt32(alpha)<<24 | g<<16 | g<<8 | g))
AGray32(val::N0f8, alpha::N0f8 = N0f8(1)) = _AGray32(reinterpret(val), reinterpret(alpha))
function AGray32(val::Real, alpha = 1)
    checkval(N0f8, val, alpha)
    AGray32(val%N0f8, alpha%N0f8)
end
function AGray32(g::Gray24, alpha = 1)
    checkval(N0f8, alpha)
    reinterpret(AGray32, UInt32(reinterpret(_rem(alpha,N0f8)))<<24 | g.color)
end
AGray32(g::AbstractGray, alpha = 1) = AGray32(gray(g), alpha)
@deprecate AGray32(x::UInt32) reinterpret(AGray32, x)

# Generated code:
#   - more constructors for colors
#   - TransparentColor definitions (e.g., ARGB), exports, and constructors
#   - coloralpha(::Color) and alphacolor(::Color) traits for corresponding types

# Note: with the exceptions of `alphacolor` and `coloralpha`, all
# traits in the rest of this file are intended just for internal use

const color3types = map(s->getfield(ColorTypes,s),
  filter(names(ColorTypes, false)) do s
    isdefined(ColorTypes, s) || return false
    t = getfield(ColorTypes, s)
    isa(t, Type) && t <: Colorant && !isabstract(t) && length(fieldnames(t))>1
  end)
# The above should have filtered out every non-DataType that's not also a
# wrapped UnionAll-wrapped DataType. By avoiding the explicit UnionAll check
# here, we remain compatible with pre-0.6 julia.
const parametric3 = filter(x->!isa(x, DataType) || !isempty(x.parameters), color3types)

# Provide the field names in the order expected by the constructor
colorfields{C<:Color}(::Type{C}) = (fieldnames(C)...)
colorfields{C<:RGB1}(::Type{C}) = (:r, :g, :b)
colorfields{C<:RGB4}(::Type{C}) = (:r, :g, :b)
colorfields{C<:BGR }(::Type{C}) = (:r, :g, :b)
colorfields{P<:TransparentColor}(::Type{P}) = tuple(colorfields(color_type(P))..., :alpha)
colorfields(c::Colorant) = colorfields(typeof(c))

# Generate convenience constructors for a type
macro make_constructors(C, fields, elty)
    # elty = default element type when supplied with Integer arguments
    fields = fields.args
    Tfields = Expr[:($f::T) for f in fields]
    realfields = Expr[:($f::Real) for f in fields]
    zfields = zeros(Int, length(fields))
    esc(quote
        # More constructors for the non-alpha version
        $C{T<:Integer}($(Tfields...)) = $C{$elty}($(fields...))
        $C($(realfields...)) = $C(promote($(fields...))...)
        $C() = $C{$elty}($(zfields...))
    end)
end

# Generate transparent versions
macro make_alpha(C, acol, cola, fields, constrfields, ub, elty)
    # ub = upper-bound on T in C{T}
    # elty = default element type when supplied with Integer arguments
    fields = fields.args
    constrfields = constrfields.args
    N = length(fields)+1
    Tfields       = Expr[:($f::T)    for f in fields]
    Tconstrfields = Expr[:($f::T)    for f in constrfields]
    realfields    = Expr[:($f::Real) for f in constrfields]
    cfields       = Expr[:(c.$f)     for f in constrfields]
    cinnerfields  = Expr[:(c.$f)     for f in fields]
    remfields     = Expr[:(_rem($f,T)) for f in fields]
    zfields       = zeros(Int, length(fields))
    Tconstr = Expr(:<:, :T, ub)
    exportexpr = Expr(:export, acol, cola)
    esc(quote
        immutable $acol{$Tconstr} <: AlphaColor{$C{T}, T, $N}
            alpha::T
            $(Tfields...)

            (::Type{$acol{T}}){T}($(Tconstrfields...), alpha::T=one(T)) = new{T}(alpha, $(fields...))
            function (::Type{$acol{T}}){T}($(realfields...), alpha::Real=one(T))
                checkval(T, $(fields...), alpha)
                new{T}(_rem(alpha,T), $(remfields...))
            end
            (::Type{$acol{T}}){T}(c::$C, alpha::Real=one(T)) = $acol{T}($(cfields...), alpha)
        end
        immutable $cola{$Tconstr} <: ColorAlpha{$C{T}, T, $N}
            $(Tfields...)
            alpha::T

            (::Type{$cola{T}}){T}($(Tconstrfields...), alpha::T=one(T)) = new{T}($(fields...), alpha)
            function (::Type{$cola{T}}){T}($(realfields...), alpha::Real=one(T))
                checkval(T, $(fields...), alpha)
                new{T}($(remfields...), _rem(alpha,T))
            end
            (::Type{$cola{T}}){T}(c::$C, alpha::Real=one(T)) = $cola{T}($(cfields...), alpha)
        end
        $exportexpr
        alphacolor{C<:$C}(::Type{C}) = $acol
        coloralpha{C<:$C}(::Type{C}) = $cola

        # More constructors for the alpha versions
        $acol{T<:Integer}($(Tconstrfields...), alpha::T=1) = $acol{$elty}($(fields...), alpha)
        $acol(c::$C, alpha::Real=one(eltype(c))) = $acol{eltype(c)}(c, alpha)
        function $acol($(constrfields...))
            p = promote($(constrfields...))
            T = typeof(p[1])
            $acol{T}(p...)
        end
        function $acol($(constrfields...), alpha::Real)
            p = promote($(constrfields...), alpha)
            T = typeof(p[1])
            $acol{T}(p...)
        end
        $acol(c::Color, alpha::Real) = $acol($C(c), alpha)
        $acol() = $acol{$elty}($(zfields...))

        $cola{T<:Integer}($(Tconstrfields...), alpha::T=1) = $cola{$elty}($(fields...), alpha)
        $cola(c::$C, alpha::Real=one(eltype(c))) = $cola{eltype(c)}(c, alpha)
        function $cola($(constrfields...))
            p = promote($(constrfields...))
            T = typeof(p[1])
            $cola{T}(p...)
        end
        function $cola($(constrfields...), alpha::Real)
            p = promote($(constrfields...), alpha)
            T = typeof(p[1])
            $cola{T}(p...)
        end
        $cola(c::Color, alpha::Real) = $cola($C(c), alpha)
        $cola() = $cola{$elty}($(zfields...))
    end)
end

eltype_default{C<:AbstractRGB  }(::Type{C}) = N0f8
eltype_default{C<:AbstractGray }(::Type{C}) = N0f8
eltype_default{C<:Color  }(::Type{C}) = Float32
eltype_default{P<:Colorant        }(::Type{P}) = eltype_default(color_type(P))

# Upper bound on element type for each color type
eltype_ub{P<:Colorant        }(::Type{P}) = eltype_ub(eltype_default(P))
eltype_ub{T<:FixedPoint   }(::Type{T}) = Fractional
eltype_ub{T<:AbstractFloat}(::Type{T}) = AbstractFloat

ctypes = union(setdiff(parametric3, [RGB1,RGB4]), [Gray])

# the arg list for C below should be identical to ctypes above.
for (C, acol, cola) in [(DIN99d, :ADIN99d, :DIN99dA),
                        (DIN99o, :ADIN99o, :DIN99oA),
                        (DIN99, :ADIN99, :DIN99A),
                        (HSI, :AHSI, :HSIA),
                        (HSL, :AHSL, :HSLA),
                        (HSV, :AHSV, :HSVA),
                        (LCHab, :ALCHab, :LCHabA),
                        (LCHuv, :ALCHuv, :LCHuvA),
                        (LMS, :ALMS, :LMSA),
                        (Lab, :ALab, :LabA),
                        (Luv, :ALuv, :LuvA),
                        (XYZ, :AXYZ, :XYZA),
                        (YCbCr, :AYCbCr, :YCbCrA),
                        (YIQ, :AYIQ, :YIQA),
                        (xyY, :AxyY, :xyYA),
                        (BGR, :ABGR, :BGRA),
                        (RGB, :ARGB, :RGBA),
                        (Gray, :AGray, :GrayA)]
    fn  = Expr(:tuple, fieldnames(C)...)
    cfn = Expr(:tuple, colorfields(C)...)
    elty = eltype_default(C)
    ub   = eltype_ub(C)
    Csym = isdefined(Core, :UnionAll) ? Base.datatype_name(Base.unwrap_unionall(C)) : C.name.name
    @eval @make_constructors $Csym $fn $elty
    @eval @make_alpha $Csym $acol $cola $fn $cfn $ub $elty
end

# RGB1 and RGB4 require special handling because of the alphadummy field
@eval @make_constructors RGB1 (r,g,b) $N0f8
@eval @make_constructors RGB4 (r,g,b) $N0f8
alphacolor{C<:Gray24}(::Type{C}) = AGray32
alphacolor{C<:RGB24}(::Type{C}) = ARGB32
alphacolor{C<:RGB1}(::Type{C}) = ARGB
alphacolor{C<:RGB4}(::Type{C}) = ARGB
coloralpha{C<:RGB1}(::Type{C}) = RGBA
coloralpha{C<:RGB4}(::Type{C}) = RGBA

"""
`alphacolor(RGB)` returns `ARGB`, i.e., the corresponding transparent
color type with storage order (alpha, color).
""" alphacolor

"""
`coloralpha(RGB)` returns `RGBA`, i.e., the corresponding transparent
color type with storage order (color, alpha).
""" coloralpha

### Validating the inputs for Normed constructors

# Throw helpful errors in case of trouble. The inlining here is
# carefully designed to reduce the impact on runtime performance, and
# we also avoid splatting.

@inline function isok{T<:Normed}(::Type{T}, x)
    Δ = eps(T)/2 # as long as the number rounds to a valid number, that's OK
    (-Δ <= x) & (x < typemax(T)+Δ)
end

@inline checkval{T<:Normed}(::Type{T}, a) = isok(T, a) || throw_colorerror(T, a)

@inline function checkval{T<:Normed}(::Type{T}, a, b)
    isok(T, a) & isok(T, b) || throw_colorerror(T, a, b)
end
@inline function checkval{T<:Normed}(::Type{T}, a, b, c)
    isok(T, a) & isok(T, b) & isok(T, c) || throw_colorerror(T, a, b, c)
end
@inline function checkval{T<:Normed}(::Type{T}, a, b, c, d)
    isok(T, a) & isok(T, b) & isok(T, c) & isok(T, d) || throw_colorerror(T, a, b, c, d)
end

checkval{T<:Normed}(::Type{T}, a::T) = nothing
checkval{T<:Normed}(::Type{T}, a::T, b::T) = nothing
checkval{T<:Normed}(::Type{T}, a::T, b::T, c::T) = nothing
checkval{T<:Normed}(::Type{T}, a::T, b::T, c::T, d::T) = nothing

checkval{T}(::Type{T}, a) = nothing
checkval{T}(::Type{T}, a, b) = nothing
checkval{T}(::Type{T}, a, b, c) = nothing
checkval{T}(::Type{T}, a, b, c, d) = nothing

@noinline throw_colorerror{T}(::Type{T}, g) = throw_colorerror(T, (g,))
@noinline throw_colorerror{T}(::Type{T}, g, a) = throw_colorerror(T, (g,a))
@noinline throw_colorerror{T}(::Type{T}, r, g, b) = throw_colorerror(T, (r, g, b))
@noinline throw_colorerror{T}(::Type{T}, r, g, b, a) = throw_colorerror(T, (r, g, b, a))

function throw_colorerror_{T<:Normed}(::Type{T}, values)
    io = IOBuffer()
    showcompact(io, typemin(T)); Tmin = String(take!(io))
    showcompact(io, typemax(T)); Tmax = String(take!(io))
    bitstring = sizeof(T) == 1 ? "an 8-bit" : "a $(8*sizeof(T))-bit"
    throw(ArgumentError("""
element type $T is $bitstring type representing $(2^(8*sizeof(T))) values from $Tmin to $Tmax,
  but the values $values do not lie within this range.
  See the READMEs for FixedPointNumbers and ColorTypes for more information."""))
end

function throw_colorerror(::Type{N0f8}, values::Tuple{Vararg{Integer}})
    # Let's try to read the user's mind
    if all(x->0<=x<=255, values)
        if length(values) == 1
            vstr = "$(values[1]) is an integer"
            Tstr = "Gray"
        else
            vstr = "$values are integers"
            if length(values) == 2
                Tstr = "AGray"
            elseif length(values) == 3
                Tstr = "RGB"
            else
                Tstr = "RGBA"
            end
        end
        args = join(map(v->"$v/255", values), ',')
        throw(ArgumentError("""
$vstr in the range 0-255, but integer inputs are encoded with the N0f8
  type, an 8-bit type representing 256 discrete values between 0 and 1.
  Consider dividing your input values by 255, for example: $Tstr{N0f8}($args)
  See the READMEs for FixedPointNumbers and ColorTypes for more information."""))
    end
    throw_colorerror_(N0f8, values)
end

function throw_colorerror{T<:Normed}(::Type{T}, values::Tuple)
    throw_colorerror_(T, values)
end

_rem{T<:Normed}(x,::Type{T}) = x % T
_rem{T}(x, ::Type{T})        = x

ext: jl


text:
include("./common.jl")

using BinaryBuilder
using BinaryBuilder: BinaryBuilderBase
Core.eval(BinaryBuilderBase, :(bootstrap_list = [:rootfs, :platform_support]))

function gcc_sources(gcc_version::VersionNumber, compiler_target::Platform; kwargs...)
    # Since we can build a variety of GCC versions, track them and their hashes here.
    # We download GCC, MPFR, MPC, ISL and GMP.
    gcc_version_sources = Dict(
        v"4.8.5" => [
            ArchiveSource("https://mirrors.kernel.org/gnu/gcc/gcc-4.8.5/gcc-4.8.5.tar.bz2",
                          "22fb1e7e0f68a63cee631d85b20461d1ea6bda162f03096350e38c8d427ecf23"),
            ArchiveSource("https://mirrors.kernel.org/gnu/mpfr/mpfr-2.4.2.tar.xz",
                          "d7271bbfbc9ddf387d3919df8318cd7192c67b232919bfa1cb3202d07843da1b"),
            ArchiveSource("https://gcc.gnu.org/pub/gcc/infrastructure/mpc-0.8.1.tar.gz",
                          "e664603757251fd8a352848276497a4c79b7f8b21fd8aedd5cc0598a38fee3e4"),
            ArchiveSource("https://mirrors.kernel.org/gnu/gmp/gmp-4.3.2.tar.bz2",
                          "936162c0312886c21581002b79932829aa048cfaf9937c6265aeaa14f1cd1775"),
        ],
        v"5.2.0" => [
            ArchiveSource("https://mirrors.kernel.org/gnu/gcc/gcc-5.2.0/gcc-5.2.0.tar.bz2",
                          "5f835b04b5f7dd4f4d2dc96190ec1621b8d89f2dc6f638f9f8bc1b1014ba8cad"),
            ArchiveSource("https://mirrors.kernel.org/gnu/mpfr/mpfr-2.4.2.tar.xz",
                          "d7271bbfbc9ddf387d3919df8318cd7192c67b232919bfa1cb3202d07843da1b"),
            ArchiveSource("https://gcc.gnu.org/pub/gcc/infrastructure/mpc-0.8.1.tar.gz",
                          "e664603757251fd8a352848276497a4c79b7f8b21fd8aedd5cc0598a38fee3e4"),
            ArchiveSource("https://mirrors.kernel.org/gnu/gmp/gmp-4.3.2.tar.bz2",
                          "936162c0312886c21581002b79932829aa048cfaf9937c6265aeaa14f1cd1775"),
            ArchiveSource("https://gcc.gnu.org/pub/gcc/infrastructure/isl-0.14.tar.bz2",
                          "7e3c02ff52f8540f6a85534f54158968417fd676001651c8289c705bd0228f36"),
        ],
        v"6.1.0" => [
            ArchiveSource("https://mirrors.kernel.org/gnu/gcc/gcc-6.1.0/gcc-6.1.0.tar.bz2",
                          "09c4c85cabebb971b1de732a0219609f93fc0af5f86f6e437fd8d7f832f1a351"),
            ArchiveSource("https://mirrors.kernel.org/gnu/mpfr/mpfr-2.4.2.tar.xz",
                          "d7271bbfbc9ddf387d3919df8318cd7192c67b232919bfa1cb3202d07843da1b"),
            ArchiveSource("https://gcc.gnu.org/pub/gcc/infrastructure/mpc-0.8.1.tar.gz",
                          "e664603757251fd8a352848276497a4c79b7f8b21fd8aedd5cc0598a38fee3e4"),
            ArchiveSource("https://mirrors.kernel.org/gnu/gmp/gmp-4.3.2.tar.bz2",
                          "936162c0312886c21581002b79932829aa048cfaf9937c6265aeaa14f1cd1775"),
            ArchiveSource("https://gcc.gnu.org/pub/gcc/infrastructure/isl-0.15.tar.bz2",
                          "8ceebbf4d9a81afa2b4449113cee4b7cb14a687d7a549a963deb5e2a41458b6b"),
        ],
        v"7.1.0" => [
            ArchiveSource("https://mirrors.kernel.org/gnu/gcc/gcc-7.1.0/gcc-7.1.0.tar.bz2",
                          "8a8136c235f64c6fef69cac0d73a46a1a09bb250776a050aec8f9fc880bebc17"),
            ArchiveSource("https://mirrors.kernel.org/gnu/mpfr/mpfr-3.1.4.tar.xz",
                          "761413b16d749c53e2bfd2b1dfaa3b027b0e793e404b90b5fbaeef60af6517f5"),
            ArchiveSource("https://mirrors.kernel.org/gnu/mpc/mpc-1.0.3.tar.gz",
                          "617decc6ea09889fb08ede330917a00b16809b8db88c29c31bfbb49cbf88ecc3"),
            ArchiveSource("https://gcc.gnu.org/pub/gcc/infrastructure/isl-0.16.1.tar.bz2",
                          "412538bb65c799ac98e17e8cfcdacbb257a57362acfaaff254b0fcae970126d2"),
            ArchiveSource("https://mirrors.kernel.org/gnu/gmp/gmp-6.1.0.tar.xz",
                          "68dadacce515b0f8a54f510edf07c1b636492bcdb8e8d54c56eb216225d16989"),
        ],
        v"8.1.0" => [
            ArchiveSource("https://mirrors.kernel.org/gnu/gcc/gcc-8.1.0/gcc-8.1.0.tar.xz",
                          "1d1866f992626e61349a1ccd0b8d5253816222cdc13390dcfaa74b093aa2b153"),
            ArchiveSource("https://mirrors.kernel.org/gnu/mpfr/mpfr-4.0.1.tar.xz",
                          "67874a60826303ee2fb6affc6dc0ddd3e749e9bfcb4c8655e3953d0458a6e16e"),
            ArchiveSource("https://mirrors.kernel.org/gnu/mpc/mpc-1.1.0.tar.gz",
                          "6985c538143c1208dcb1ac42cedad6ff52e267b47e5f970183a3e75125b43c2e"),
            ArchiveSource("https://gcc.gnu.org/pub/gcc/infrastructure/isl-0.18.tar.bz2",
                          "6b8b0fd7f81d0a957beb3679c81bbb34ccc7568d5682844d8924424a0dadcb1b"),
            ArchiveSource("https://mirrors.kernel.org/gnu/gmp/gmp-6.1.2.tar.xz",
                          "87b565e89a9a684fe4ebeeddb8399dce2599f9c9049854ca8c0dfbdea0e21912"),
        ],
        v"9.1.0" => [
            ArchiveSource("https://mirrors.kernel.org/gnu/gcc/gcc-9.1.0/gcc-9.1.0.tar.xz",
                          "79a66834e96a6050d8fe78db2c3b32fb285b230b855d0a66288235bc04b327a0"),
            ArchiveSource("https://mirrors.kernel.org/gnu/mpfr/mpfr-4.0.2.tar.xz",
                          "1d3be708604eae0e42d578ba93b390c2a145f17743a744d8f3f8c2ad5855a38a"),
            ArchiveSource("https://mirrors.kernel.org/gnu/mpc/mpc-1.1.0.tar.gz",
                          "6985c538143c1208dcb1ac42cedad6ff52e267b47e5f970183a3e75125b43c2e"),
            ArchiveSource("https://gcc.gnu.org/pub/gcc/infrastructure/isl-0.18.tar.bz2",
                          "6b8b0fd7f81d0a957beb3679c81bbb34ccc7568d5682844d8924424a0dadcb1b"),
            ArchiveSource("https://mirrors.kernel.org/gnu/gmp/gmp-6.1.2.tar.xz",
                          "87b565e89a9a684fe4ebeeddb8399dce2599f9c9049854ca8c0dfbdea0e21912"),
        ],
        v"11.0.0-iains" => [
            GitSource("https://github.com/iains/gcc-darwin-arm64.git",
                  "03d8ff79b7a2d408db953667f81f76c0b8da26f0"),
            ArchiveSource("https://mirrors.kernel.org/gnu/mpfr/mpfr-4.0.1.tar.xz",
                          "67874a60826303ee2fb6affc6dc0ddd3e749e9bfcb4c8655e3953d0458a6e16e"),
            ArchiveSource("https://mirrors.kernel.org/gnu/mpc/mpc-1.1.0.tar.gz",
                          "6985c538143c1208dcb1ac42cedad6ff52e267b47e5f970183a3e75125b43c2e"),
            ArchiveSource("https://gcc.gnu.org/pub/gcc/infrastructure/isl-0.18.tar.bz2",
                          "6b8b0fd7f81d0a957beb3679c81bbb34ccc7568d5682844d8924424a0dadcb1b"),
            ArchiveSource("https://mirrors.kernel.org/gnu/gmp/gmp-6.1.2.tar.xz",
                          "87b565e89a9a684fe4ebeeddb8399dce2599f9c9049854ca8c0dfbdea0e21912"),
        ]
    )

    # Map from GCC version and platform -> binutils sources
    if isa(compiler_target, MacOS)
        # MacOS doesn't actually use binutils, it uses cctools
        binutils_sources = [
            GitSource("https://github.com/tpoechtrager/apple-libtapi.git",
                      "a66284251b46d591ee4a0cb4cf561b92a0c138d8"),
            GitSource("https://github.com/tpoechtrager/cctools-port.git",
                      "a2e02aad90a98ac034b8d0286496450d136ebfcd"),
        ]
    else
        # Different versions of GCC should be pared with different versions of Binutils
        binutils_gcc_version_mapping = Dict(
            v"4.8.5" => v"2.24",
            v"5.2.0" => v"2.25.1",
            v"6.1.0" => v"2.26",
            v"7.1.0" => v"2.27",
            v"8.1.0" => v"2.31",
            v"9.1.0" => v"2.33.1",
        )

        # Everyone else uses GNU Binutils, but we have to version carefully.
        binutils_version_sources = Dict(
            v"2.24" => [
                ArchiveSource("https://ftp.gnu.org/gnu/binutils/binutils-2.24.tar.bz2",
                              "e5e8c5be9664e7f7f96e0d09919110ab5ad597794f5b1809871177a0f0f14137"),
            ],
            v"2.25.1" => [
                ArchiveSource("https://ftp.gnu.org/gnu/binutils/binutils-2.25.1.tar.bz2",
                              "b5b14added7d78a8d1ca70b5cb75fef57ce2197264f4f5835326b0df22ac9f22"),
            ],
            v"2.26" => [
                ArchiveSource("https://ftp.gnu.org/gnu/binutils/binutils-2.26.tar.bz2",
                              "c2ace41809542f5237afc7e3b8f32bb92bc7bc53c6232a84463c423b0714ecd9"),
            ],
            v"2.27" => [
                ArchiveSource("https://ftp.gnu.org/gnu/binutils/binutils-2.27.tar.bz2",
                              "369737ce51587f92466041a97ab7d2358c6d9e1b6490b3940eb09fb0a9a6ac88"),
            ],
            v"2.31" => [
                ArchiveSource("https://ftp.gnu.org/gnu/binutils/binutils-2.31.tar.bz2",
                              "2c49536b1ca6b8900531b9e34f211a81caf9bf85b1a71f82b81ae32fcd8ffe19"),
            ],
            v"2.33.1" => [
                ArchiveSource("https://ftp.gnu.org/gnu/binutils/binutils-2.33.1.tar.xz",
                              "ab66fc2d1c3ec0359b8e08843c9f33b63e8707efdff5e4cc5c200eae24722cbf"),
            ],
        )
        binutils_version = binutils_gcc_version_mapping[gcc_version]
        binutils_sources = binutils_version_sources[binutils_version]
    end

    if Sys.islinux(compiler_target) && libc(compiler_target) == "glibc"
        # Depending on our architecture, we choose different versions of glibc
        if arch(compiler_target) in ["x86_64", "i686"]
            libc_sources = [
                ArchiveSource("https://mirrors.kernel.org/gnu/glibc/glibc-2.12.2.tar.xz",
                              "0eb4fdf7301a59d3822194f20a2782858955291dd93be264b8b8d4d56f87203f"),
            ]
        elseif arch(compiler_target) in ["armv7l", "aarch64"]
            libc_sources = [
                ArchiveSource("https://mirrors.kernel.org/gnu/glibc/glibc-2.19.tar.xz",
                              "2d3997f588401ea095a0b27227b1d50cdfdd416236f6567b564549d3b46ea2a2"),
            ]
        elseif arch(compiler_target) in ["powerpc64le"]
            libc_sources = [
                ArchiveSource("https://mirrors.kernel.org/gnu/glibc/glibc-2.17.tar.xz",
                              "6914e337401e0e0ade23694e1b2c52a5f09e4eda3270c67e7c3ba93a89b5b23e"),
            ]
        else
            error("Unknown arch for glibc for compiler target $(compiler_target)")
        end
    elseif Sys.islinux(compiler_target) && libc(compiler_target) == "musl"
        libc_sources = [
            ArchiveSource("https://www.musl-libc.org/releases/musl-1.1.19.tar.gz",
                          "db59a8578226b98373f5b27e61f0dd29ad2456f4aa9cec587ba8c24508e4c1d9"),
        ]
    elseif Sys.isapple(copmiler_target)
        if gcc_version == v"11.0.0-iains"
            libc_sources = [
                DirectorySource(joinpath(@__DIR__, "DarwinSDKs"))
            ]
        else
            libc_sources = [
                ArchiveSource("https://github.com/phracker/MacOSX-SDKs/releases/download/10.15/MacOSX10.12.sdk.tar.xz",
                          "6852728af94399193599a55d00ae9c4a900925b6431534a3816496b354926774"),
            ]
        end
    elseif Sys.isfreebsd(compiler_target)
        libc_sources = [
            ArchiveSource("https://download.freebsd.org/ftp/releases/amd64/11.4-RELEASE/base.txz",
                          "3bac8257bdd5e5b071f7b80cc591ebecd01b9314ca7839a2903096cbf82169f9"),
        ]
    elseif Sys.iswindows(compiler_target)
        libc_sources = [
            ArchiveSource("https://sourceforge.net/projects/mingw-w64/files/mingw-w64/mingw-w64-release/mingw-w64-v7.0.0.tar.bz2",
                          "aa20dfff3596f08a7f427aab74315a6cb80c2b086b4a107ed35af02f9496b628"),
        ]
    else
        error("Unknown libc mapping for platform $(compiler_target)")
    end

    # We bundle together GCC, Binutils and libc.
    return [
        gcc_version_sources[gcc_version]...,
        binutils_sources...,
        libc_sources...,
        DirectorySource("./bundled"; follow_symlinks=true),
    ]
end

function gcc_script(compiler_target::Platform)
    script = raw"""
    cd ${WORKSPACE}/srcdir
    COMPILER_TARGET=${target}
    HOST_TARGET=${MACHTYPE}

    # Install `gcc` from `apk`, which we'll use to bootstrap ourselves a BETTER `gcc`
    apk add build-base gettext-dev gcc-objc clang

    # We like to refer to things with their full triplets, so symlink our host tools
    # (which have no prefix) to have the machtype prefix.
    for tool in gcc g++ ar as ld lipo ranlib nm strip objcopy objdump readelf; do
        if [[ -f /usr/bin/${tool} ]]; then
            ln -s /usr/bin/${tool} /usr/bin/${HOST_TARGET}-${tool}
        fi
    done

    # Default sysroot
    sysroot="${prefix}/${COMPILER_TARGET}/sys-root"
    cp -ra "/opt/${COMPILER_TARGET}/${COMPILER_TARGET}" "${prefix}/${COMPILER_TARGET}"

    # Some things need /lib64, others just need /lib
    case ${COMPILER_TARGET} in
        x86_64*)
            LIB64=lib64
            ;;
        aarch64*)
            LIB64=lib64
            ;;
        ppc64*)
            LIB64=lib64
            ;;
        *)
            LIB64=lib
            ;;
    esac

    # Update configure scripts for all projects
    update_configure_scripts

    # Force everything to default to cross compiling!
    for f in $(find . -name configure); do
        sed -i.bak -e 's&cross_compiling=no&cross_compiling=yes&g' "${f}"
        sed -i.bak -e 's&is_cross_compiler=no&is_cross_compiler=yes&g' "${f}"
    done

    # Allow us to run our binutils stuff as soon as it's ready
    export PATH=${prefix}/bin:$PATH

    # Initialize GCC_CONF_ARGS
    GCC_CONF_ARGS=""

    ## Architecture-dependent arguments
    # On arm*hf targets, pass --with-float=hard explicitly, and choose a default arch.
    if [[ "${COMPILER_TARGET}" == arm*hf ]]; then
        # We choose the armv6 arch by default for compatibility
        GCC_CONF_ARGS="${GCC_CONF_ARGS} --with-float=hard --with-arch=armv6 --with-fpu=vfp"
    elif [[ "${COMPILER_TARGET}" == x86_64* ]]; then
        GCC_CONF_ARGS="${GCC_CONF_ARGS} --with-arch=x86-64"
    elif [[ "${COMPILER_TARGET}" == i686* ]]; then
        GCC_CONF_ARGS="${GCC_CONF_ARGS} --with-arch=pentium4"
    fi

    # On musl targets, disable a bunch of things we don't want
    if [[ "${COMPILER_TARGET}" == *-musl* ]]; then
        GCC_CONF_ARGS="${GCC_CONF_ARGS} --disable-libssp --disable-libmpx --disable-libmudflap"
        GCC_CONF_ARGS="${GCC_CONF_ARGS} --disable-libsanitizer --disable-symvers"
        export libat_cv_have_ifunc=no
        export ac_cv_have_decl__builtin_ffs=yes

        musl_arch()
        {
            case "${COMPILER_TARGET}" in
                i686*)
                    echo i386 ;;
                arm*)
                    echo armhf ;;
                *)
                    echo ${COMPILER_TARGET%%-*} ;;
            esac
        }

    elif [[ "${COMPILER_TARGET}" == *-mingw* ]]; then
        # On mingw, we need to explicitly set the windres code page to 1, otherwise windres segfaults
        export CPPFLAGS="${CPPFLAGS} -DCP_ACP=1"

    elif [[ "${COMPILER_TARGET}" == *-darwin* ]]; then
        # Use llvm archive tools to dodge binutils bugs
        export LD_FOR_TARGET=${prefix}/bin/${COMPILER_TARGET}-ld
        export AR_FOR_TARGET=${prefix}/bin/llvm-ar
        export NM_FOR_TARGET=${prefix}/bin/llvm-nm
        export RANLIB_FOR_TARGET=${prefix}/bin/llvm-ranlib

        # GCC build doesn't pay attention to DSYMUTIL or DSYMUTIL_FOR_TARGET, tsk tsk
        mkdir -p ${prefix}/bin
        ln -s llvm-dsymutil ${prefix}/bin/dsymutil

        # GCC build needs a little exdtra help finding our binutils
        GCC_CONF_ARGS="${GCC_CONF_ARGS} --with-ld=${prefix}/bin/${COMPILER_TARGET}-ld"
        GCC_CONF_ARGS="${GCC_CONF_ARGS} --with-as=${prefix}/bin/${COMPILER_TARGET}-as"

        # GCC doesn't turn LTO on by default for some reason.
        GCC_CONF_ARGS="${GCC_CONF_ARGS} --enable-lto --enable-plugin"

        # On darwin, cilk doesn't build on 5.X-7.X.  :(
        export enable_libcilkrts=no
    fi

    # Link dependent packages into gcc build root:
    cd $WORKSPACE/srcdir/gcc-*/
    for proj in mpfr mpc isl gmp; do
        if [[ -d $(echo ../${proj}-*) ]]; then
            mv ../${proj}-* ${proj}
        fi
    done

    # Do not run fixincludes except on Darwin
    if [[ ${COMPILER_TARGET} != *-darwin* ]]; then
        sed -i 's@\./fixinc\.sh@-c true@' gcc/Makefile.in
    fi

    # Apply all gcc patches
    for p in ${WORKSPACE}/srcdir/patches/gcc*.patch; do
        atomic_patch -p1 "${p}" || true
    done

    # If we're on MacOS, we need to install cctools first, separately.
    if [[ ${COMPILER_TARGET} == *-darwin* ]]; then
        cd ${WORKSPACE}/srcdir/apple-libtapi

        mkdir -p ${WORKSPACE}/srcdir/apple-libtapi/build
        cd ${WORKSPACE}/srcdir/apple-libtapi/build

        export TAPIDIR=${WORKSPACE}/srcdir/apple-libtapi

        # Install libtapi
        cmake ../src/llvm \
            -DCMAKE_CXX_FLAGS="-I${TAPIDIR}/src/llvm/projects/clang/include -I${TAPIDIR}/build/projects/clang/include" \
            -DLLVM_INCLUDE_TESTS=OFF \
            -DCMAKE_BUILD_TYPE=RELEASE \
            -DCMAKE_INSTALL_PREFIX=${prefix}
        make -j${nproc} VERBOSE=1 clangBasic
        make -j${nproc} VERBOSE=1
        make install

        # Install cctools
        mkdir -p ${WORKSPACE}/srcdir/cctools_build
        cd ${WORKSPACE}/srcdir/cctools_build
        CC=/usr/bin/clang CXX=/usr/bin/clang++ LDFLAGS=-L/usr/lib ${WORKSPACE}/srcdir/cctools-port/cctools/configure \
            --prefix=${prefix} \
            --target=${COMPILER_TARGET} \
            --host=${MACHTYPE} \
            --with-libtapi=${prefix}
        make -j${nproc}
        make install

    # Otherwise, we need to install binutils first
    else
        # We also need to build binutils
        cd ${WORKSPACE}/srcdir/binutils-*

        # Patch to make `dlltool` use deterministic mode when building static libraries
        atomic_patch -p1 $WORKSPACE/srcdir/patches/binutils_deterministic_dlltool.patch

        # Patch for building binutils 2.30+ against FreeBSD
        atomic_patch -p1 $WORKSPACE/srcdir/patches/binutils_freebsd_symbol_versioning.patch || true

        ./configure --prefix=${prefix} \
            --target=${COMPILER_TARGET} \
            --host=${MACHTYPE} \
            --with-sysroot="${sysroot}" \
            --enable-multilib \
            --program-prefix="${COMPILER_TARGET}-" \
            --disable-werror \
            --enable-deterministic-archives

        make -j${nproc}
        make install
    fi

    # GCC won't build (crti.o: no such file or directory) unless these directories exist.
    # They can be empty though.
    mkdir -p ${sysroot}/lib ${sysroot}/usr/lib

    # Build bootstrap compiler in a separate directory
    mkdir -p $WORKSPACE/srcdir/gcc_stage1
    cd $WORKSPACE/srcdir/gcc_stage1

    # Since this stage1 compiler is just going to be used to
    # bootstrap, compile without many optimizations, which reduces
    # overall build time
    $WORKSPACE/srcdir/gcc-*/configure \
        --prefix="${prefix}" \
        --target="${COMPILER_TARGET}" \
        --host="${MACHTYPE}" \
        --build="${MACHTYPE}" \
        --disable-multilib \
        --disable-werror \
        --disable-decimal-float \
        --disable-threads \
        --disable-libatomic \
        --disable-libffi \
        --disable-libitm \
        --disable-libmudflap \
        --disable-libssp \
        --disable-libsanitizer \
        --disable-libgomp \
        --disable-libquadmath \
        --disable-libstdcxx \
        --without-headers \
        --disable-bootstrap \
        --enable-host-shared \
        --with-sysroot="${sysroot}" \
        --program-prefix="${COMPILER_TARGET}-" \
        --enable-languages="c" \
        ${GCC_CONF_ARGS}

    make -j${nproc} all-gcc
    make -j${nproc} install-gcc

    unset CFLAGS
    unset CXXFLAGS

    # This is needed for any glibc older than 2.14, which includes the following commit
    # https://sourceware.org/git/?p=glibc.git;a=commit;h=95f5a9a866695da4e038aa4e6ccbbfd5d9cf63b7
    ln -vs libgcc.a $(${COMPILER_TARGET}-gcc -print-libgcc-file-name | sed 's/libgcc/&_eh/') || true

    # Build libc (stage 1)
    if [[ ${COMPILER_TARGET} == *-gnu* ]]; then
        # patch glibc
        cd ${WORKSPACE}/srcdir/glibc-*
        # patch glibc to keep around libgcc_s_resume on arm
        # ref: https://sourceware.org/ml/libc-alpha/2014-05/msg00573.html
        atomic_patch -p1 $WORKSPACE/srcdir/patches/glibc_arm_gcc_fix.patch || true

        # patch glibc's stupid gcc/make version checks (we don't require these,
        # as if it doesn't apply cleanly, it's probably fine).  We also keep them
        # separate, as some glibc versions require one or not the other.  :(
        atomic_patch -p1 $WORKSPACE/srcdir/patches/glibc_gcc_version.patch || true
        atomic_patch -p1 $WORKSPACE/srcdir/patches/glibc_make_version.patch || true

        # patch older glibc's 32-bit assembly to withstand __i686 definition of
        # newer GCC's.  ref: http://comments.gmane.org/gmane.comp.lib.glibc.user/758
        atomic_patch -p1 $WORKSPACE/srcdir/patches/glibc_i686_asm.patch || true

        # Patch glibc's sunrpc cross generator to work with musl
        # See https://sourceware.org/bugzilla/show_bug.cgi?id=21604
        atomic_patch -p0 $WORKSPACE/srcdir/patches/glibc_sunrpc.patch || true

        # patch for building old glibc on newer binutils
        # These patches don't apply on those versions of glibc where they
        # are not needed, but that's ok.
        atomic_patch -p0 $WORKSPACE/srcdir/patches/glibc_nocommon.patch || true
        atomic_patch -p0 $WORKSPACE/srcdir/patches/glibc_regexp_nocommon.patch || true

        # patch for avoiding linking in musl libs for a glibc-linked binary
        atomic_patch -p1 $WORKSPACE/srcdir/patches/glibc_musl_rejection.patch || true
        atomic_patch -p1 $WORKSPACE/srcdir/patches/glibc_musl_rejection_old.patch || true

        # Patch for building glibc 2.25-2.30 on aarch64
        atomic_patch -p1 $WORKSPACE/srcdir/patches/glibc_aarch64_relocation.patch || true

        # Patches for building glibc 2.17 on ppc64le
        for p in ${WORKSPACE}/srcdir/patches/glibc-ppc64le-*.patch; do
            atomic_patch -p1 ${p} || true;
        done

        # Various configure overrides
        GLIBC_CONFIGURE_OVERRIDES=( libc_cv_forced_unwind=yes libc_cv_c_cleanup=yes )

        # We have problems with libssp on ppc64le
        if [[ ${COMPILER_TARGET} == powerpc64le-* ]]; then
            GLIBC_CONFIGURE_OVERRIDES+=( libc_cv_ssp=no libc_cv_ssp_strong=no )
        fi

        # Configure glibc
        mkdir ${WORKSPACE}/srcdir/glibc_build
        cd ${WORKSPACE}/srcdir/glibc_build
        ${WORKSPACE}/srcdir/glibc-*/configure \
            --prefix=/usr \
            --host=${COMPILER_TARGET} \
            --build=${HOST_TARGET} \
            --with-headers="${sysroot}/usr/include" \
            --disable-multilib \
            --disable-werror \
            ${GLIBC_CONFIGURE_OVERRIDES[@]}

        # Install headers
        mkdir -p ${prefix}/${COMPILER_TARGET}/include/gnu
        touch ${prefix}/${COMPILER_TARGET}/include/gnu/stubs.h
        mkdir -p ${sysroot}/usr/include/bits
        touch ${sysroot}/usr/include/bits/stdio_lim.h
        make install_root=${sysroot} install-bootstrap-headers=yes install-headers

        # Install CSU
        make csu/subdir_lib -j${nproc}
        mkdir -p ${sysroot}/usr/${LIB64}
        install csu/crt1.o csu/crti.o csu/crtn.o ${sysroot}/usr/${LIB64}
        ${COMPILER_TARGET}-gcc -nostdlib -nostartfiles -shared -x c /dev/null -o ${sysroot}/usr/${LIB64}/libc.so

    elif [[ ${COMPILER_TARGET} == *-musl* ]]; then
        # Configure musl
        mkdir -p ${WORKSPACE}/srcdir/musl_build
        cd ${WORKSPACE}/srcdir/musl_build
        LDFLAGS="-Wl,-soname,libc.musl-$(musl_arch).so.1" ${WORKSPACE}/srcdir/musl-*/configure \
            --prefix=/usr \
            --host=${COMPILER_TARGET} \
            --with-headers="${sysroot}/usr/include" \
            --with-binutils=${prefix}/bin \
            --disable-multilib \
            --disable-werror \
            --enable-optimize \
            --enable-debug \
            CROSS_COMPILE="${COMPILER_TARGET}-"

        # Install headers
        make install-headers DESTDIR=${sysroot}

        # Make CRT
        make lib/{crt1,crti,crtn}.o
        mkdir -p ${sysroot}/usr/lib
        install lib/crt1.o lib/crti.o lib/crtn.o ${sysroot}/usr/lib
        ${COMPILER_TARGET}-gcc -nostdlib -nostartfiles -shared -x c /dev/null -o ${sysroot}/usr/lib/libc.so

    elif [[ ${COMPILER_TARGET} == *-mingw* ]]; then
        # Build CRT
        mkdir -p $WORKSPACE/srcdir/mingw_crt_build
        cd $WORKSPACE/srcdir/mingw_crt_build
        MINGW_CONF_ARGS=""

        # If we're building a 32-bit build of mingw, add `--disable-lib64`
        if [[ "${COMPILER_TARGET}" == i686-* ]]; then
            MINGW_CONF_ARGS="${MINGW_CONF_ARGS} --disable-lib64"
        else
            MINGW_CONF_ARGS="${MINGW_CONF_ARGS} --disable-lib32"
        fi

        ${WORKSPACE}/srcdir/mingw-*/mingw-w64-crt/configure \
            --prefix=/ \
            --host=${COMPILER_TARGET} \
            --with-sysroot=${sysroot} \
            ${MINGW_CONF_ARGS}
        make -j${nproc}
        make install DESTDIR=${sysroot}

    elif [[ ${COMPILER_TARGET} == *-darwin* ]]; then
        # Install Darwin libc
        cd ${WORKSPACE}/srcdir/MacOSX*.sdk
        mkdir -p "${sysroot}"
        rsync -a usr "${sysroot}/"

        # Clean out libssl and libcrypto, as we never want to link against those old versions included with MacOS
        rm -rfv ${sysroot}/usr/lib/libssl.*
        rm -rfv ${sysroot}/usr/lib/libcrypto.*

    elif [[ ${COMPILER_TARGET} == *freebsd* ]]; then
        cd ${WORKSPACE}/srcdir
        # We're going to clean out vestiges of libgcc_s and friends,
        # because we're going to compile our own from scratch
        for lib in gcc_s ssp; do
            find usr/ -name lib${lib}.\* -delete
            find lib/ -name lib${lib}.\* -delete
        done

        mkdir -p "${sysroot}/usr/lib"
        mv usr/lib/* "${sysroot}/usr/lib/"
        mv lib/* "${sysroot}/lib/"

        # Many symlinks exist that point to `../../lib/libfoo.so`.
        # We need them to point to just `libfoo.so`. :P
        for f in $(find "${prefix}/${COMPILER_TARGET}" -xtype l); do
            link_target="$(readlink "$f")"
            if [[ -n $(echo "${link_target}" | grep "^../../lib") ]]; then
                ln -vsf "${link_target#../../lib/}" "${f}"
            fi
        done
    fi

    # Back to GCC-land, install libgcc
    cd ${WORKSPACE}/srcdir/gcc_stage1
    make all-target-libgcc -j ${nproc}
    make install-target-libgcc

    # Finish off libc
    if [[ ${COMPILER_TARGET} == *-gnu* ]]; then
        cd ${WORKSPACE}/srcdir/glibc_build
        make -j${nproc}
        make install install_root=${sysroot}

    elif [[ ${COMPILER_TARGET} == *-musl* ]]; then
        # Re-configure musl to pick up our newly-built libgcc
        cd ${WORKSPACE}/srcdir/musl_build
        rm -rf *

        LDFLAGS="-Wl,-soname,libc.musl-$(musl_arch).so.1" ${WORKSPACE}/srcdir/musl-*/configure \
            --prefix=/usr \
            --host=${COMPILER_TARGET} \
            --with-headers="${sysroot}/usr/include" \
            --with-binutils=${prefix}/bin \
            --disable-multilib \
            --disable-werror \
            --enable-optimize \
            --enable-debug \
            CROSS_COMPILE="${COMPILER_TARGET}-"

        make -j${nproc} DESTDIR=${sysroot}
        rm -f ${sysroot}/usr/lib/libc.so
        make install DESTDIR=${sysroot}

        # Fix broken symlink
        ln -fsv ../usr/lib/libc.so ${sysroot}/lib/ld-musl-$(musl_arch).so.1

    elif [[ ${COMPILER_TARGET} == *-mingw* ]]; then
        cd $WORKSPACE/srcdir/mingw_crt_build

        # Build winpthreads
        mkdir -p $WORKSPACE/srcdir/mingw_winpthreads_build
        cd $WORKSPACE/srcdir/mingw_winpthreads_build
        ${WORKSPACE}/srcdir/mingw-*/mingw-w64-libraries/winpthreads/configure \
            --prefix=/ \
            --host=${COMPILER_TARGET} \
            --enable-static \
            --enable-shared

        make -j${nproc}
        make install DESTDIR=${sysroot}
    fi

    # Back to GCC-land, build brand new full compiler
    mkdir -p $WORKSPACE/srcdir/gcc_build
    cd $WORKSPACE/srcdir/gcc_build

    ## Platform-dependent arguments
    if [[ "$COMPILER_TARGET" == *-darwin* ]]; then
        GCC_CONF_ARGS="${GCC_CONF_ARGS} --enable-languages=c,c++,fortran,objc,obj-c++"

    elif [[ "${COMPILER_TARGET}" == *linux* ]]; then
        GCC_CONF_ARGS="${GCC_CONF_ARGS} --enable-languages=c,c++,fortran,objc,obj-c++"

    elif [[ "${COMPILER_TARGET}" == *freebsd* ]]; then
        GCC_CONF_ARGS="${GCC_CONF_ARGS} --enable-languages=c,c++,fortran"

    # On mingw32 override native system header directories
    elif [[ "${COMPILER_TARGET}" == *mingw* ]]; then
        GCC_CONF_ARGS="${GCC_CONF_ARGS} --enable-languages=c,c++,fortran"
        GCC_CONF_ARGS="${GCC_CONF_ARGS} --with-native-system-header-dir=/include"

        # On mingw, we need to explicitly enable openmp
        GCC_CONF_ARGS="${GCC_CONF_ARGS} --enable-libgomp"

        # We also need to symlink our lib directory specially
        ln -s sys-root/lib ${prefix}/${COMPILER_TARGET}/lib
    fi

    # GCC won't build (crti.o: no such file or directory) unless these directories exist.
    # They can be empty though.
    mkdir -p ${prefix}/${COMPILER_TARGET}/sys-root/{lib,usr/lib}

    # We have to be really assertive with `CC`, `CC_FOR_BUILD` and `CC_FOR_TARGET`
    # here, as if we don't, building for x86_64-linux-musl itself can fail, due to
    # it getting confused between the compiler we just built and the host compiler.
    CC=/usr/bin/gcc CC_FOR_BUILD=/usr/bin/gcc CC_FOR_TARGET=${prefix}/bin/${COMPILER_TARGET}-gcc $WORKSPACE/srcdir/gcc-*/configure \
        --prefix="${prefix}" \
        --target="${COMPILER_TARGET}" \
        --host="${MACHTYPE}" \
        --build="${MACHTYPE}" \
        --disable-multilib \
        --disable-werror \
        --enable-shared \
        --enable-host-shared \
        --enable-threads=posix \
        --with-sysroot="${sysroot}" \
        --program-prefix="${COMPILER_TARGET}-" \
        --disable-bootstrap \
        ${GCC_CONF_ARGS}

    ## Build, build, build!
    make -j ${nproc}
    make install

    # Remove misleading libtool archives
    rm -f ${prefix}/${COMPILER_TARGET}/lib*/*.la
    """

    return script
end

# The products that we will ensure are always built
function gcc_products(;kwargs...)
    products = Product[
        # Normally, we would want to provide these, but since we are claiming an incorrect
        # platform (e.g. we claim this is for `compiler_target` but it's really for `host_platform`,
        # which we do because we need the appropriate `platofrm_support`) it doesn't find them.
        # We just build with no products for now.
        #ExecutableProduct("\${target}-gcc", :gcc),
        #ExecutableProduct("\${target}-g++", :gxx),
    ]
end

function build_and_upload_gcc(version, ARGS=ARGS)
    name = "GCCBootstrap"
    compiler_target = platform_key_abi(ARGS[end])
    if isa(compiler_target, UnknownPlatform)
        error("This is not a typical build_tarballs.jl!  Must provide exactly one platform as the last argument!")
    end
    deleteat!(ARGS, length(ARGS))

    sources = gcc_sources(version, compiler_target)
    script = gcc_script(compiler_target)
    products = gcc_products()

    # Build the tarballs, and possibly a `build.jl` as well.
    ndARGS, deploy, deploy_target = find_deploy_arg(ARGS)
    build_info = build_tarballs(ndARGS, name, version, sources, script, [compiler_target], products, []; skip_audit=true)
    build_info = Dict(host_platform => first(values(build_info)))

    # Upload the artifacts (if requested)
    if deploy
        upload_and_insert_shards(deploy_target, name, version, build_info; target=compiler_target)
    end
    return build_info
end

max_stars_repo_name: devmotion/Yggdrasil

max_issues_repo_name: devmotion/Yggdrasil

max_forks_repo_name: devmotion/Yggdrasil


text: er(n, m, k) = reshape(rand(Base.OneTo(n), m*k), k, m)

max_stars_repo_name: LilithHafner/FBD

max_issues_repo_name: LilithHafner/FBD

max_forks_repo_name: LilithHafner/FBD


text:
mutable struct DataPoint{T,E}
    value::T
    start_time_unix_nano::UInt
    time_unix_nano::UInt
    exemplar_reservoir::E
    lock::ReentrantLock
end

function DataPoint{T}(exemplar_reservoir = nothing) where {T}
    t = UInt(time() * 10^9)
    DataPoint(zero(T), t, t, exemplar_reservoir, ReentrantLock())
end

DataPoint(v, s, t, e) = DataPoint(v, s, t, e, ReentrantLock())

function _add_to_datapoint!(p::DataPoint, v, t)
    lock(p.lock) do
        p.value += v
        p.time_unix_nano = t
    end
end

function _update_to_datapoint!(p::DataPoint, v, t)
    p.value = v
    p.time_unix_nano = t
end


text:
"""

Construct a csv file containing data for all instances
"""

@everywhere ROOT = pwd()
@everywhere include(joinpath(ROOT,"..","src_PowSysMod", "PowSysMod_body.jl"))
@everywhere include("para_fct.jl"))

# NOTE: Launch in console using 'julia -p nbparathreads compute_GOC_all_scenario.jl'
# where the nb of cores is likely a good value for nbparathreads

instances_path = joinpath("instances", "GOC")
folders = readdir(instances_path)

##empty dictionaries which will contain data to export in csv
instances_nb_scenario_data = Dict{String,Int64}()
instances_nb_contingency_data = Dict{String,Int64}()
instances_nb_bus_data = Dict{String,Int64}()
instances_nb_links_data = Dict{String,Int64}()
instances_nb_violated_constraints = Dict{String, Float64}()
instances_nb_cc_Qgen_active = Dict{String, Float64}()

##loop on instances to get data
epsilon = 1e-3
for folder in folders
    instances_nb_scenario_data[folder] = get_nb_scenarios(instances_path,folder)
    scenario = readdir(joinpath(instances_path, folder))[1]
    instances_nb_contingency_data[folder] = get_nb_contingencies(folder,scenario)
    instances_nb_bus_data[folder] = get_nb_bus(folder, scenario)
    instances_nb_links_data[folder] = get_nb_links(folder,scenario)
    instances_nb_cc_Qgen_active[folder] = mean(get_nb_cc_reactive_power_active(folder, scenario, epsilon) for scenario in readdir(joinpath(instances_path, folder)) if scenario !="scorepara.csv")
    #instances_nb_violated_constraints[folder] = mean(get_feasibility(folder, scenario, epsilon) for scenario in readdir(joinpath(instances_path, folder)))
end
##create csv file
touch("GOCdata.csv")
f = open("GOCdata.csv", "w")
write(f, "Instance;Nb_scenarios;Nb_contingencies;Nb_bus;Nb_links;Mean nb of violated constraints by GOC solution at $(epsilon);Mean nb of reactive Qgen contingency constraints at $(epsilon)\n")
for (folder, nb_scenarios) in instances_nb_scenario_data
    nb_cont = instances_nb_contingency_data[folder]
    nb_bus = instances_nb_bus_data[folder]
    nb_links = instances_nb_links_data[folder]
    # nb_vc = instances_nb_violated_constraints[folder]
    nb_vc = "NONE"
    nb_cc_act = instances_nb_cc_Qgen_active[folder]
    write(f, "$(folder);$(nb_scenarios);$(nb_cont);$(nb_bus);$(nb_links);$(nb_vc);$(nb_cc_act)\n")
end
close(f)

##functions to get specific data
"""
    get_nb_scenarios(instances_path,folder)

Returns the number of scenarios for an instance `folder` in `instances_path`.
"""
function get_nb_scenarios(instances_path,folder)
    folder_path = joinpath(instances_path, folder)
    scenarios = sort(filter(x->!ismatch(r"\.", x), readdir(folder_path)), by=x->parse(split(x, "_")[2]))
    nb_scenarios = length(scenarios)
    return nb_scenarios
end

"""
    get_nb_bus(folder, scenario)

Returns the number of bus for a `scenario` in instance `folder`.
"""
function get_nb_bus(folder, scenario)
    power_data = getpowerdata([folder, scenario])
    try
        nb_bus =  Int(power_data["totalbus"])
        return nb_bus
    catch
        warn("power_data coming from .mat file does not have key totalbus")
        return 0.0
    end
end

"""
    get_nb_contingencies(folder,scenario)

Returns the number of contingencies for a `scenario` in instance `folder`.
"""
function get_nb_contingencies(folder,scenario)
    contingency_data = getcontingencydata([folder, scenario])
    nb_contingency = size(contingency_data,1)
    return nb_contingency
end

"""
    get_nb_links(folder, scenario)

Returns the number of links for a `scenario` in instance `folder`.
"""
function get_nb_links(folder, scenario)
    power_data = getpowerdata([folder, scenario])
    try
        nb_links =  Int(power_data["totalbranch"])
        return nb_links
    catch
        warn("power_data coming from .mat file does not have key totalbranch")
        return 0.0
    end
end

"""
    get_feasibility(folder, scenario, epsilon)

Returns the number of violated constraints at `epsilon` (except balance constraints) for a `scenario` in instance `folder`.
"""
function get_feasibility(folder, scenario, epsilon)
    try
        violations = test_feasibility_GOC([folder, scenario],epsilon)
        nb_violated_constraints = 0
        for (scenario, dict_infeas) in violations
            nb_violated_constraints += length(dict_infeas)
        end
        return nb_violated_constraints

    catch
        return -1.0
    end
end

"""
    get_nb_cc_reactive_power_active(folder, scenario,epsilon)

Returns the number of active constraints of type contingency Qgen coupling constraints at `epsilon` for a `scenario` in instance `folder`.
"""
function get_nb_cc_reactive_power_active(folder, scenario,epsilon)
    try
        return nb_active_constraints_in_scenario_cc_Qgen([folder, scenario], epsilon)
    catch
        println([folder, scenario])
        return -1.0
    end
end

max_stars_repo_name: Gimaju/GOC

max_issues_repo_name: Gimaju/GOC

max_forks_repo_name: Gimaju/GOC


text:
using Clang
using Clang.LibClang
using Test

@testset "c basic" begin
    # parse file
    trans_unit = parse_header(joinpath(@__DIR__, "c", "cbasic.h"),
                              flags = CXTranslationUnit_DetailedPreprocessingRecord |
                                      CXTranslationUnit_SkipFunctionBodies)
    ctx = DefaultContext()
    push!(ctx.trans_units, trans_unit)
    # get root cursor
    root_cursor = getcursor(trans_unit)
    # search the first macro defination "#define CONST 1"
    cursorCONST = search(root_cursor, x->kind(x)==CXCursor_MacroDefinition && name(x) == "CONST")
    toksCONST = tokenize(cursorCONST[1])
    @test kind(toksCONST[1]) == CXToken_Identifier
    @test kind(toksCONST[2]) == CXToken_Literal
    @test toksCONST[2].text == "1"
    # search the second macro defination "#define CONSTADD CONST + 2"
    cursorCONSTADD = search(root_cursor, x->kind(x)==CXCursor_MacroDefinition && name(x) == "CONSTADD")
    toksCONSTADD = tokenize(cursorCONSTADD[1])
    @test toksCONSTADD[1].text == "CONSTADD"
    @test toksCONSTADD[2].text == "CONST"
    @test toksCONSTADD[3].text == "+"
    @test toksCONSTADD[4].text == "2"

    # function arguments
    func1 = search(root_cursor, "func1")[1]
    @test argnum(func1) == 4
    func1_args = function_args(func1) # TODO should return a structure or namedtuple
    @test map(spelling, func1_args) == ["a","b","c","d"]
    @test endswith(filename(func1), joinpath("c", "cbasic.h"))
    @test spelling(trans_unit) == filename(func1)

    # function constant array arguments should be converted to common Ptr
    func_arg = search(root_cursor, "func_constarr_arg")[1]
    wrap!(ctx, func_arg)
    expr = :(function func_constarr_arg(x)
                 ccall((:func_constarr_arg, libxxx), Cint, (Ptr{Cdouble},), x)
             end)
    Base.remove_linenums!(expr)
    @test ctx.api_buffer[1] == expr
end


text: compute(n::Int)::Int = binomial(2n, n)

max_stars_repo_name: Dynortice/Project-Euler

max_issues_repo_name: Dynortice/Project-Euler

max_forks_repo_name: Dynortice/Project-Euler


text:
module_path = splitdir(@__FILE__)[1]            # Path to this file

# NOTE: In Julia v0.7, JLD throws the error "Cannot `convert` an object of type
# getfield(JLD, Symbol("##JLD.AssociativeWrapper" unless, imported outside of
# WFVisual through the following line. I'm not sure, but I think it's because
# JLD must be imported before GeometricTools to be able to read Grid JLDs.
# Also, notice that `generate_windfarm(...)` takes an abnormal long time to run
# in v0.7.
import JLD

# Load WFVisual module
import WFVisual
wfv=WFVisual

# Load GeometricTools: https://github.com/byuflowlab/GeometricTools.jl
import GeometricTools
gt=GeometricTools

# Data path with geometry JLDs
if Int(VERSION.major)==0 && Int(VERSION.minor)==6   # Case of Julia v0.6
    data_path = joinpath(module_path, "../data/")
else                                                # Case of Julia v0.7
    data_path = joinpath(module_path, "../datav07/")
end

# Create save path where to store vtks
save_path = "temps/windfarm06/"         # Save path of this example
gt.create_path(save_path, true)

# --------------------- WIND FARM LAYOUT ---------------------------------------
turbine_x = [  909.98349606,  1005.0174903 ,   900.40238835,  -479.57607866,
                 937.92551703,   461.20472344,   123.06165965, -1073.3529325 ,
                -698.76200523, -1083.53094471,  1365.9338773 ,   204.10557045,
                 306.99725222,  -585.35767689,    10.9706543 ,   543.54927959,
                 -20.50212678, -1313.07762466, -1327.00852816,   137.06316521,
                1378.99410072,  -645.66250771,  -535.7876181 ,  -100.66538419,
               -1331.29278587]

turbine_y = [  500.59889721,  -974.65186327,    76.51586507,  1315.29789208,
                1039.37304649, -1321.85187113,  -300.57817461,  -765.82650713,
               -1213.14956771,   886.54973742,  -306.96866239,   194.51410928,
                 735.69027357,    81.95252777,  -892.14222889,  -930.41798152,
                 970.9117144 ,   398.75277565,    -3.52236011,  1393.27798468,
                 241.61586052,  -791.92492366,   919.26560593, -1396.37638078,
                -433.19849727]
turbine_z = [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,
                0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.]

hub_height = [ 100.,  100.,  100.,  100.,  100.,  100.,  100.,  100.,  100.,
                100.,  100.,  100.,  100.,  100.,  100.,  100.,  100.,  100.,
                100.,  100.,  100.,  100.,  100.,  100.,  100.]

rotor_diameter = [ 100.,  100.,  100.,  100.,  100.,  100.,  100.,  100.,  100.,
                100.,  100.,  100.,  100.,  100.,  100.,  100.,  100.,  100.,
                100.,  100.,  100.,  100.,  100.,  100.,  100.]

nBlades = [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
               3, 3]

wind_direction = 228.0

yaw = [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,
        0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.] - wind_direction

# --------------------- PERIMETER AND FLUID DOMAIN -----------------------------
NDIVSx = 70              # Cells in the parametric x-direction
NDIVSy = 70              # Cells in the parametric y-direction
NDIVSz = 70              # Cells in the geometric z-direction

# Dummy perimeter
Rper = norm(maximum.([turbine_x, turbine_y]) - minimum.([turbine_x, turbine_y]))/2*3/4
perimeter_points = Rper*[ [cos(a), sin(a), 0] for a in linspace(0, 2*pi, 179)]

# Dummy wake function
wake(X) = 1.0*[cos(wind_direction*pi/180), sin(wind_direction*pi/180), 0]

# --------------------- GENERATE WIND FARM -------------------------------------

wfv.generate_windfarm(rotor_diameter, hub_height, nBlades,
                                turbine_x, turbine_y, turbine_z,
                                yaw,
                                perimeter_points, wake;
                                NDIVSx=NDIVSx, NDIVSy=NDIVSy, NDIVSz=NDIVSz,
                                save_path=save_path, spl_s=0.01,
                                data_path=data_path);

nothing

max_issues_repo_name: byuflowlab/wind-farm-utilities

max_forks_repo_name: byuflowlab/wind-farm-utilities


text:
module BottleNeckFlow

# using RecipesBase
using Makie
using Logging

export
    Simulation,
    CanalConfig, δ, SimParam,
    GridBox,
    AbstractGrid,
    UGrid,
    VGrid,
    PGrid,
    VortGrid,
    DivGrid,
    getxs,
    getys,
    poiseuille,
    poiseuille!,
    fillghost!,
    noslip,
    naturalbound,
    inbump,
    step_euler!,
    gauss_seidel!,
    residual,
    step_poisson!,
    poisson_project!,
    runsim!

include("constants.jl")
include("canal.jl")
include("mesh.jl")
include("simulation.jl")
include("poiseuille.jl")
include("schemes.jl")
include("diagnostic.jl")
include("run.jl")

include("recipes.jl")

end


text:
"""
    Clause{N<:Real}

Type that represents a disjunction of linear constraints.

### Fields

- ``atoms`` -- a vector of linear constraints
"""
struct Clause{N<:Real}
    atoms::AbstractVector{LinearConstraint{N}}
end
# constructor from a single atom
Clause(atom::LinearConstraint{N}) where {N<:Real} = Clause{N}([atom])

"""
    LinearConstraintProperty{N<:Real} <: Property

Type that represents a property consisting of a Boolean function of linear
constraints.
The function is given in CNF (conjunctive normal form), i.e., a conjunction of
disjunctions of linear constraints.

### Fields

- ``clauses`` -- a vector of `Clause` objects
"""
struct LinearConstraintProperty{N<:Real} <: Property
    clauses::AbstractVector{Clause{N}}
end
# constructor from a single clause
LinearConstraintProperty(clause::Clause{N}) where {N<:Real} =
    LinearConstraintProperty{N}([clause])
# constructor from a single constraint
LinearConstraintProperty(linConst::LinearConstraint{N}) where {N<:Real} =
    LinearConstraintProperty{N}([Clause(linConst)])
# constructor from a single constraint in raw form ax+b
LinearConstraintProperty(linComb::AbstractVector{N}, bound::N) where {N<:Real} =
    LinearConstraintProperty{N}([Clause(LinearConstraint{N}(linComb, bound))])

"""
    inout_map_property(prop::LinearConstraintProperty{N},
                       partition::AbstractVector{<:AbstractVector{Int}},
                       blocks::AbstractVector{Int},
                       n::Int
                      )::LinearConstraintProperty{N} where {N<:Real}

Map a `LinearConstraintProperty` to the dimensions of analyzed blocks.

### Input

- `prop`      -- property
- `partition` -- block partition; elements are start and end indices of a block
- `blocks`    -- list of all output block indices in the partition
- `n`         -- total number of input dimensions

### Output

A new property of reduced dimension.
"""
function inout_map_property(prop::LinearConstraintProperty{N},
                            partition::AbstractVector{<:AbstractVector{Int}},
                            blocks::AbstractVector{Int},
                            n::Int
                           )::LinearConstraintProperty{N} where {N<:Real}
    # sanity check: do not project away non-zero dimensions
    function check_projection(a, proj)
        p = 1
        for i in 1:length(a)
            if p <= length(proj) && i == proj[p]
                # dimension is not projected away
                p += 1
            elseif a[i] != 0
                # dimension is projected away; entry is non-zero
                return false
            end
        end
        return true
    end

    @assert dim(prop.clauses[1].atoms[1]) == n "the property has dimension $(dim(prop.clauses[1].atoms[1])) but should have dimension $n"

    proj = projection_map(partition, blocks)

    # create modified property
    clauses = Vector{Clause{N}}(length(prop.clauses))
    for (ic, c) in enumerate(prop.clauses)
        atoms = Vector{LinearConstraint{N}}(length(c.atoms))
        for (ia, atom) in enumerate(c.atoms)
            @assert check_projection(atom.a, proj) "blocks incompatible with property"
            atoms[ia] = LinearConstraint{N}(atom.a[proj], atom.b)
        end
        clauses[ic] = Clause(atoms)
    end
    return LinearConstraintProperty(clauses)
end

"""
    check_property(set::LazySet, prop::LinearConstraintProperty)::Bool

Checks whether a convex set satisfies a property of linear constraints.

### Input

- ``set``  -- convex set
- ``prop`` -- property of linear constraints

### Output

`true` iff the convex set satisfies the property of linear constraints.
"""
@inline function check_property(set::LazySet, prop::LinearConstraintProperty
                               )::Bool
    @assert (length(prop.clauses) > 0) "empty properties are not allowed"
    for clause in prop.clauses
        @assert (length(clause.atoms) > 0) "empty clauses are not allowed"
        clause_sat = false
        for atom in clause.atoms
            if ρ(atom.a, set) < atom.b
                clause_sat = true
                break
            end
        end
        if !clause_sat
            return false
        end
    end
    return true
end


text:
function hfun_bar(vname)
  val = Meta.parse(vname[1])
  return round(sqrt(val), digits=2)
end

function hfun_m1fill(vname)
  var = vname[1]
  return pagevar("index", var)
end

function lx_baz(com, _)
  # keep this first line
  brace_content = Franklin.content(com.braces[1]) # input string
  # do whatever you want here
  return uppercase(brace_content)
end

function hfun_timestamp_now()
    return string(Dates.now()) * "+00:00"
end

"""
    {{blogposts}}

Plug in the list of blog posts contained in the `/posts` folder.
Souce: <https://github.com/abhishalya/abhishalya.github.io>.
"""
@delay function hfun_blogposts()
    today = Dates.today()
    curyear = year(today)
    curmonth = month(today)
    curday = day(today)

    list = readdir("posts")
    filter!(endswith(".md"), list)
    function sorter(p)
        ps  = splitext(p)[1]
        url = "/posts/$ps/"
        surl = strip(url, '/')
        pubdate = pagevar(surl, "published")
        if isnothing(pubdate)
            return Date(Dates.unix2datetime(stat(surl * ".md").ctime))
        end
        return Date(pubdate, dateformat"yyyy-mm-dd")
    end
    sort!(list, by=sorter, rev=true)

    io = IOBuffer()
    write(io, """
""")
    for (i, post) in enumerate(list)
        if post == "index.md"
            continue
        end
        ps = splitext(post)[1]
        write(io, "
")
        url = "/posts/$ps/"
        surl = strip(url, '/')
        title = pagevar(surl, "title")
        pubdate = pagevar(surl, "published")
        description = pagevar(surl, "rss_description")
        if isnothing(pubdate)
            date = "$curyear-$curmonth-$curday"
        else
            date = Date(pubdate, dateformat"yyyy-mm-dd")
        end
        write(io, """$date$title""")
        write(io, """
$description
""")
    end
    write(io, "
")
    return String(take!(io))
end

# Based on https://github.com/tlienart/Franklin.jl/pull/799.
function hfun_rss()
    rss = locvar(:rss)::String
    descr = fd2html(rss; internal=true, nop=true)
    Franklin.set_var!(Franklin.LOCAL_VARS, "rss_description", descr)
    return "
$descr
"
end

"""
    hfun_requiredfill(params::Vector{String})

Return the value for the field, just like `fill`, but throws an assertion error if the value is not given.
"""
function hfun_requiredfill(params::Vector{String})::String
    value = Franklin.hfun_fill(params)
    field = params[1]
    @assert(value != "", "Missing a value for the field $field")
    return value
end

"""
    lx_pluto(com, _)

Embed a Pluto notebook via:
https://github.com/rikhuijzer/PlutoHTML.jl
"""
function lx_pluto(com, _)
    file = string(Franklin.content(com.braces[1]))::String
    path = joinpath("posts", "notebooks", "$file.jl")
    log_path = joinpath("posts", "notebooks", "$file.log")

    return """
        ```julia:pluto
        # hideall

        using PlutoHTML: notebook2html

        path = "$path"
        log_path = "$log_path"
        @assert isfile(path)
        @info "→ evaluating Pluto notebook at (\$path)"
        html = open(log_path, "w") do io
            redirect_stdout(io) do
                html = notebook2html(path)
                return html
            end
        end
        println("\n~~~\n\$html\n~~~\n")
        ```
        \\textoutput{pluto}
        """
end


text:
"""
A module for the function ncon, which does contractions of several tensors.
"""
module NCon
using TensorOperations
using LinearAlgebra: BlasFloat
export ncon

"""
    ncon(L, v; forder=nothing, order=nothing, check_indices=false)

Takes a tuple of tensors L and a tuple of Vector{Integer}s v that specifies
how these tensors form a network, and contracts this network. The return value
is the tensor that is formed as the result of this contraction.

More specifically:
L = (A1, A2, ..., Ap) is a tuple of tensors or a single tensor.

v = (v1, v2, ..., vp) is a tuple of Arrays of indices, each of which
corresponds to one of the tensors in L. For instance, if v1 = (3, 4, -1) and
v2 = (-2, -3, 4), then the second index of A1 and the last index of A2 are
contracted together, because they are both labeled by 4. All positive numbers
label indices that are to be contracted and all negative numbers label indices
that are to remain open. The open indices will be the remaining indices of the
tensor that is formed. The index lists v1, v2, etc. may also be tuples instead
of Arrays, and v may consist of a single index list v1, if there's only one
tensor in the contraction (a trace).

order, if present, contains a tuple or Array of all positive indices - if not
(1, 2, 3, 4, ...) by default. This is the order in which the contractions are
performed. However, whenever an index joining two tensors is about to be
contracted together, ncon contracts at the same time all indices connecting
these two tensors, even if some of them only come up later in order.

forder, if present, contains the final ordering of the uncontracted indices
- if not, (-1, -2, ...) by default.

If check_indices is true (by default it's false) then checks are performed to
make sure the contraction is well-defined. If not, an ArgumentError with a
helpful description of what went wrong is provided.
"""

function ncon(L, v; forder=nothing, order=nothing, check_indices=true)
    # We want to handle the tensors as an Vector{AbstractArray} and v as a
    # Vector{Vector{Number}. If either one is a single AbstractArray or
    # Vector{Integer} then wrap them in a Vector, othewise collect to make sure
    # tuples and other iterables are turned into Vectors.
    L = (isa(L, AbstractArray{T} where T <: Number) ?
         AbstractArray[L] : AbstractArray[L...])
    v = (isa(v, Vector{T} where T <: Number) ?
         [collect(v)] : collect(map(collect, v)))

    order === nothing && (order = create_order(v))
    forder === nothing && (forder = create_forder(v))
    check_indices && do_check_indices(L, v, order, forder)

    # Keep looping until there is only one tensor left. At each iteration
    # either two tensors will be contracted together, or a trace will be taken
    # over the indices of some tensor.
    while length(order) > 0
        tcon = get_tcon(v, order[1])  # tcon = tensors to be contracted

        if length(tcon) == 1  # This contraction is a trace.
            t = tcon[1]
            newA = trace(L[t], v[t])
        else
            t1, t2 = tcon
            newA = con(L[t1], v[t1], L[t2], v[t2])
        end
        # Find the indices icon that were contracted.
        icon = get_icon(v, tcon)
        # Find the indices of the new tensor.
        newv = find_newv(v, tcon, icon)
        push!(L, newA)
        push!(v, newv)
        for i in sort(tcon, rev=true)
            # Delete the contracted tensors and indices from the lists.
            # tcon is reverse sorted so that tensors are removed starting from
            # the end of L, otherwise the order would get messed.
            deleteat!(L, i)
            deleteat!(v, i)
        end
        renew_order!(order, icon)  # Update order
    end
    Alast = multiply_final(L, v, forder)
    return Alast
end

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

""" Identify all unique, positive indices and return them sorted. """
create_order(v) = sort(filter!(x -> x > 0, unique(reduce(vcat, v))))

"""
Identify all unique, negative indices and return them reverse sorted (-1 first).
"""
create_forder(v) = sort(filter!(x -> x < 0, unique(reduce(vcat, v))), rev=true)

"""
Return a Vector of indices for L of the tensors that have n as their leg.
"""
get_tcon(v, n) = findall(x -> in(n, x), v)

"""
Return a Vector of indices that are to be contracted when contractions between
the two tensors numbered in tcon are contracted.
"""
function get_icon(v, tcon)
    if length(tcon) == 1
        # This is a trace.
        inds = v[tcon[1]]
        # Find all the elements that occur twice in inds.
        return filter!(x -> count(==(x), inds) > 1, unique(inds))
    else
        return unique!(reduce(intersect, v[tcon]))
    end
end

"""
Find the list of indices for the new tensor after contraction of indices icon
of the tensors tcon.
"""
find_newv(v, tcon, icon) = filter!(!in(icon), reduce(vcat, v[tcon]))

""" Update order by removing the contracted indices from it. """
renew_order!(order, icon) = filter!(!in(icon), order)

"""
Check that
1) the number of tensors in L matches the number of index lists in v.
2) every tensor is given the right number of indices.
3) order only has positive numbers in it and forder only negative.
4) every contracted index is featured exactly twice and every free index
   exactly once.
5) That every index appears exactly once in either order or forder.
6) the dimensions of the two ends of each contracted index match.
7) 0 is not in v.
"""
function do_check_indices(L, v, order, forder)
    #1)
    if length(L) != length(v)
        msg = "In ncon, the number of tensors ($(length(L)))"*
              " does not match the number of index lists ($(length(v)))."
        throw(ArgumentError(msg))
    end

    #2)
    for (i, (A, inds)) in enumerate(zip(L, v))
        dimcount = ndims(A)
        numinds = length(inds)
        if numinds != dimcount
            msg = "In ncon, length(v[$i])=$(numinds)"*
                  " does not match the numbers of indices of L[$i] ="*
                  " $(dimcount)"
            throw(ArgumentError(msg))
        end
    end

    #3)
    if any(x -> x <= 0, order)
        msg = "In ncon, not all elements in order are positive."
        throw(ArgumentError(msg))
    end
    if any(x -> x >= 0, forder)
        msg = "In ncon, not all elements in forder are negative."
        throw(ArgumentError(msg))
    end

    #4-7)
    v_sum = reduce(vcat, v)
    if 0 in v_sum
        throw(ArgumentError("Zero is not a valid index for ncon"))
    end
    if Set(union(order, forder)) != Set(v_sum)
        msg = "In ncon, the indices in forder and order are not the same as"*
              " the indices in v."*
              "\nforder: $forder\norder: $order\nv: $v"
        throw(ArgumentError(msg))
    end
    # v_pairs provides all valid indices for v, in the same order as the
    # elements of v_sum, e.g.
    # "v_pairs = [[(1,1), (1,2), (1,3), ...], [(2,1), (2,2), (2,3), ...], ...]"
    v_pairs = [(i, j) for (i, s) in enumerate(v) for j in 1:length(s)]
    # For t, o in zip(v_pairs, v_sum), t is the tuple of the number of
    # the tensor and the index and o is the contraction order of that
    # index. We group these tuples by the contraction order.
    order_groups = [[t for (t, o) in zip(v_pairs, v_sum) if o == n]
                     for n in order]
    forder_groups = [[t for (t, fo) in zip(v_pairs, v_sum) if fo == n]
                     for n in forder]
    for (i, o) in enumerate(order_groups)
        if length(o) != 2
            msg = "In ncon, the contracted index"*
                  " $(order[i]) is not featured exactly twice in v."
            throw(ArgumentError(msg))
        else
            A0, ind0 = o[1]
            A1, ind1 = o[2]
            size_A0_ind0 = size(L[A0], ind0)
            size_A1_ind1 = size(L[A1], ind1)
            compatible = (size_A0_ind0 == size_A1_ind1)
            if !compatible
                msg = "In ncon, for the contraction index"*
                      " $(order[i]), the leg $ind0 of tensor $A0"*
                      " (dimension $size_A0_ind0) and leg $ind1 of tensor $A1"*
                      " (dimension $size_A1_ind1) are not compatible."
                throw(ArgumentError(msg))
            end
        end
    end
    for (i, fo) in enumerate(forder_groups)
        if length(fo) != 1
            msg = "In ncon, the free index"*
                  " $(forder[i]) is not featured exactly once in v."
            throw(ArgumentError(msg))
        end
    end

    # All is well if we made it here.
    return true
end

########################################################################
# The following are simple wrappers around TensorOperations functions, #
# but may be replaced with other stuff later.                          #
########################################################################

"""
If vA contains some element repeated twice, the second of these elements is
replaced by m+1. Each time this occurs, m is incremented by one. The result
is a new Array that has no elements repeated twice, and all the new elements
are larger than m.
"""
function change_duplicates(vA::Vector{T}, m=maximum(abs.(vA))) where {T <: Number}
    s = Set{T}()
    for (i, el) in enumerate(vA)
        if el in s
            vA = copy(vA)
            m += one(T)
            vA[i] = m
        else
            push!(s, el)
        end
    end
    return vA
end

""" Contract two tensors. """
function con(A, vA, B, vB)
    # tensorcontract can't handle a case where vA or vB includes a partial
    # trace (a repeated index) that is to be performed later.
    # Work around this by changing the labels if this occurs.
    m = maximum(abs.(vcat(vA, vB)))
    vA = change_duplicates(vA, m)
    m = maximum(abs.(vcat(vA, vB)))
    vB = change_duplicates(vB, m)
    if A isa BlasFloat && B isa BlasFloat
        enable_blas()
    else
        disable_blas()
    end
    res = tensorcontract(A, vA, B, vB)
    return res
end

""" Trace over some of the indices of a tensor. """
function trace(A, inds)
    res = tensortrace(A, inds)
    return res
end

"""
Return the tensor product (no contractions) of the tensors in L, with the
indices permuted to the order specified in forder.
"""
function multiply_final(L, v, forder)
    if length(L) == 1
        Anew = tensorcopy(L[1], v[1], forder)
    else
        lengths = map(length, L)
        while length(L) > 1
            # Get the two tensors in L with smallest number of elements.
            i = argmin(lengths)
            A, vA = L[i], v[i]
            deleteat!(L, i)
            deleteat!(v, i)
            deleteat!(lengths, i)
            j = argmin(lengths)
            B, vB = L[j], v[j]
            deleteat!(L, j)
            deleteat!(v, j)
            deleteat!(lengths, j)
            # Multiply them together and permute their indices at the same
            # time. vnew is all the indices of vA and vB, in the order that
            # they appear in forder.
            vnew = intersect(forder, vcat(vA, vB))
            Anew = tensorproduct(A, vA, B, vB, vnew)
            push!(L, Anew)
            push!(v, vnew)
            push!(lengths, length(Anew))
        end
    end
    return Anew
end

end


text:
abstract type DynamicComponent <: DeviceParameter end

"""
Abstract type for all dynamic injection types

Subtypes must implement these methods:
- [`get_static_injector`](@ref)
- [`set_static_injector!`](@ref)
"""
abstract type DynamicInjection <: Device end

supports_services(d::DynamicInjection) = false

"""
Return the static injector stored with a dynamic injector.
"""
function get_static_injector(device::T) where {T <: DynamicInjection}
    error("get_static_injector must be implemented for type $T")
end

function get_dynamic_components(device::T) where {T <: DynamicInjection}
    return (
        getfield(device, x) for
        (x, y) in zip(fieldnames(T), fieldtypes(T)) if y <: DynamicComponent
    )
end


text:
@testset "SGS" begin
  𝒮 = georef((z=[1.,0.,1.],), [25. 50. 75.; 25. 75. 50.])
  𝒟 = RegularGrid(100,100)
  N = 3

  𝒫₁ = SimulationProblem(𝒮, 𝒟, :z, N)
  𝒫₂ = SimulationProblem(𝒟, :z=>Float64, N)

  solver = SGS(
    :z => (variogram=GaussianVariogram(range=35.),
           neighborhood=BallNeighborhood(10.))
  )

  Random.seed!(2017)
  sol₁ = solve(𝒫₁, solver)
  sol₂ = solve(𝒫₂, solver)

  # basic checks
  reals = sol₁[:z]
  inds = LinearIndices(size(𝒟))
  @test all(reals[i][inds[26,26]] == 1. for i in 1:N)
  @test all(reals[i][inds[51,76]] == 0. for i in 1:N)
  @test all(reals[i][inds[76,51]] == 1. for i in 1:N)
end


text:
# ---
# title: 216. Combination Sum III
# id: problem216
# author: Indigo
# date: 2021-01-21
# difficulty: Medium
# categories: Array, Backtracking
# link: <https://leetcode.com/problems/combination-sum-iii/description/>
# hidden: true
# ---
#
# Find all valid combinations of `k` numbers that sum up to `n` such that the
# following conditions are true:
#
#   * Only numbers `1` through `9` are used.
#   * Each number is used **at most once**.
#
# Return _a list of all possible valid combinations_. The list must not contain
# the same combination twice, and the combinations may be returned in any order.
#
#
#
# **Example 1:**
#
#
#
#     Input: k = 3, n = 7
#     Output: [[1,2,4]]
#     Explanation:
#     1 + 2 + 4 = 7
#     There are no other valid combinations.
#
# **Example 2:**
#
#
#
#     Input: k = 3, n = 9
#     Output: [[1,2,6],[1,3,5],[2,3,4]]
#     Explanation:
#     1 + 2 + 6 = 9
#     1 + 3 + 5 = 9
#     2 + 3 + 4 = 9
#     There are no other valid combinations.
#
#
# **Example 3:**
#
#
#
#     Input: k = 4, n = 1
#     Output: []
#     Explanation: There are no valid combinations. [1,2,1] is not valid because 1 is used twice.
#
#
# **Example 4:**
#
#
#
#     Input: k = 3, n = 2
#     Output: []
#     Explanation: There are no valid combinations.
#
#
# **Example 5:**
#
#
#
#     Input: k = 9, n = 45
#     Output: [[1,2,3,4,5,6,7,8,9]]
#     Explanation:
#     1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 = 45
#     There are no other valid combinations.
#
#
#
#
# **Constraints:**
#
#   * `2 <= k <= 9`
#   * `1 <= n <= 60`
#
#
## @lc code=start
using LeetCode

function combination_sum3(k::Int, n::Int)
    function combination_sum3(k::Int, n::Int, s::Int)::Vector{Vector{Int}}
        if s + k - 1 > 9 || sum(s:(s + k - 1)) > n
            return []
        elseif k == 1 && s == n
            return [[n]]
        end
        choose_s = combination_sum3(k - 1, n - s, s + 1)
        notchoose_s = combination_sum3(k, n, s + 1)
        return [[[s, cs...] for cs in choose_s]..., notchoose_s...]
    end
    return combination_sum3(k, n, 1)
end
## @lc code=end