text:
"""

-----------------------------------------

**AA210A Compressible Flow Formulae**

-----------------------------------------

Material from "AA210A Course Reader: Fundamentals of Compressible Flow"

by Prof. Brian J. Cantwell - https://web.stanford.edu/~cantwell/

|**Function Name**| **Function Description**                   |**Eqn. No.**|
|:----------------|:-------------------------------------------|:-----------|
|           gamma | Specific Heat Ratio                        | (2.98)     |
|             C_p | Heat Capacity at Constant Pressure, J/kg-K | (2.98)     |
|             C_v | Heat Capacity at Constant Volume, J/kg-K   | (2.98)     |
|        spdofsnd | Speed of Sound, m/s                        | (2.112)    |
|             PtP | Stagnation/Static Pressure Ratio           | (5.85)     |
|             TtT | Stagnation/Static Temperature Ratio        | (5.77)     |
|   **NormShock** | **Normal Shock Jump Relations**            | **⤓⤓⤓**  |
|     `Mach2Norm` | `Normal Shock Exit Mach`                   | `(9.60)`   |
|      `T2T1Norm` | `Normal Shock Static Temperature Ratio`    | `(9.73)`   |
|      `P2P1Norm` | `Normal Shock Static Pressure Ratio`       | `(9.56)`   |
|    `Pt2Pt1Norm` | `Normal Shock Stagnation Pressure Ratio`   | `(9.66)`   |
|      `r2r1Norm` | `Normal Shock Density Ratio`               | `(9.97)`   |
|        FindMach | Find Mach Number from Parameter Ratios     |            |
|    **OblShock** | **Oblique Shock Jump Relations**           | **⤓⤓⤓**  |
|       `deflang` | `Oblique Shock Flow Turn Angle θ (deg)`    | `(12.15)`  |
|    `deflangsol` | `Oblique Shock Angle Solver`               | `(12.15)`  |
|      `Mach2Obl` | `Oblique Shock Exit Mach`                  | `(12.7)`   |
|       `T2T1Obl` | `Oblique Shock Static Temperature Ratio`   | `(12.7)`   |
|       `P2P1Obl` | `Oblique Shock Static Pressure Ratio`      | `(12.7)`   |
|     `Pt2Pt1Obl` | `Oblique Shock Stagnation Pressure Ratio`  | `(12.8)`   |
|       `r2r1Obl` | `Oblique Shock Density Ratio`              | `(12.7)`   |
|      **aratio** | **A*/A = f(M)**                            |**(10.16)** |
|     `aratiosol` | `A*/A = f(M) Solver`                       | `(10.16)`  |
|           Fanno | Fanno Line Relations                       | (11.31)    |
|        Rayleigh | Rayleigh Line Relations                    | (11.47)    |
|           PMang | Prandtl-Meyer Expansion Angle ω (deg)      | (12.38)    |
|        PMangsol | Prandtl-Meyer Expansion Angle Solver       | (12.38)    |
|   ShockTubeP4P1 | Shock Tube Initial Pressure Ratio          | (13.64)    |
|     ShockTubeUp | Shock Tube Piston Velocity Up              | (13.59)    |
|      ChokedCond | Pressure Ratio for Choked Flow Pt/Pa       | (10.26)    |
|      NozzleMach | Fully Expanded Nozzle Exit Mach Number     | (10.25)    |
|         scaleht | Atmospheric Scale Height, m                | (2.119)    |
|         shockRe | Shock Reynolds Number (ρUδ)/μ              | (9.96)     |
|       CJDetMach | Chapman-Jouget Detonation Mach Number      | (11.61)    |

Type \"AA210AFnList()\" or \"?AA210A_Functions\" to see function list again

Type \"?<function_name>\" to see individual function documentation

"""
module AA210A_Functions

export spdofsnd,
PtP,TtT,
NormShock,Mach2Norm,T2T1Norm,P2P1Norm,Pt2Pt1Norm,r2r1Norm,
FindMach,
OblShock,deflang,T2T1Obl,P2P1Obl,Pt2Pt1Obl,r2r1Obl,Mach2Obl,deflangsol,
aratio,aratiosol,
Fanno,Rayleigh,
PMang,
PMangsol,
ShockTubeP4P1,ShockTubeMs,ShockTubeUp, #ShockTubeMrs,
gamma,C_p,C_v,
ChokedCond,NozzleMach,
scaleht,
shockRe,
CJDetMach,
AA210AFnList

begin #local scope to avoid naming variables used elsewhere

local fnnames = ["Function Name|Function Description|Eqn. Nos."]
#name format: fnname(args)|description|eqn number

#~~~~~~~~~~Constants~~~~~~~~~~

# println("Ru = 8314.46: Universal Gas Constant, J/kmol-K")
# push!(fnnames,"Ru = 8314.46|Universal Gas Constant, J/kmol-K|")
# Ru = 8314.46 #Universal Gas Constant, J/kmol K

# println("g0 = 9.80665: Std. Gravitational Acceleration, m/s²")
# push!(fnnames,"g0 = 9.80665|Std. Gravitational Accel., m/s²|")
# g0 = 9.80665 #Std. Gravitational Acceleration

# push!(fnnames,"||")

#~~~~~~~~~~Functions~~~~~~~~~~

push!(fnnames,"gamma|Specific Heat Ratio|(2.98)")
"""

### Syntax
    gamma(dof)
    gamma()

### Description
_Eqn. 2.98_

Calculates the specific heat ratio of an ideal gas with total number of molecular degrees of freedom 'dof'. Default corresponds to Air. Typical values for 'dof' are 5 for diatomic gases and 3 for monatomic gases (Section A.8).

### Arguments
'dof::Integer': Molecular degrees of freedom of gas (Default: 5)

### See Also
C_p, spdofsnd, scaleht

"""
function gamma(dof::Integer) #Specific Heat Ratio (eq 2.98)
  gamma = (dof+2)/dof
  return gamma
end
function gamma() #Alternative Method for generic case (Air, diatomic gases)
  gamma(5)
end

push!(fnnames,"C_p|Heat Capacity at Constant Pressure, J/kg-K|(2.98)")
"""

### Syntax
    C_p(dof, Mw)
    C_p()

### Description
_Eqn. 2.98_

Calculates the specific heat capacity Cp of an ideal gas with total number of molecular degrees of freedom 'dof' and molecular weight 'Mw'. Default corresponds to Air. Typical values for dof are 5 for diatomic gases and 3 for monatomic gases (Section A.8).

### Arguments
'dof::Integer': Molecular degrees of freedom of gas (Default: 5)

'Mw::Real': Molecular weight, amu or kg/mol (Default: 28.97)

### See Also
gamma, C_v, spdofsnd, scaleht

"""
function C_p(dof::Real, Mw::Real) #Heat Capacity at constant pressure (eq 2.98)
  Ru = 8314.46 #Universal Gas Constant, J/kmol K
  Cp = ((dof + 2)/2)*(Ru/Mw)
  return Cp
end
function C_p() #Alternative Method for generic case (Air)
  C_p(5,28.97)
end

push!(fnnames,"C_v|Heat Capacity at Constant Volume, J/kg-K|(2.98)")
"""

### Syntax
    C_v(dof, Mw)
    C_v()

### Description
_Eqn. 2.98_

Calculates the specific heat capacity Cv of an ideal gas with total number of molecular degrees of freedom 'dof' and molecular weight 'Mw'. Default corresponds to Air. Typical values for dof are 5 for diatomic gases and 3 for monatomic gases (Section A.8).

### Arguments
'dof::Integer': Molecular degrees of freedom of gas (Default: 5)

'Mw::Real': Molecular weight, amu or kg/mol (Default: 28.97)

### See Also
gamma, C_p, spdofsnd, scaleht

"""
function C_v(dof::Real,Mw::Real) #Heat Capacity at constant volume (eq 2.98)
  Ru = 8314.46 #Universal Gas Constant, J/kmol K
  Cv = (dof*Ru)/(2*Mw)
  return Cv
end
function C_v() #Alternative Method for generic case (Air)
  C_v(5,28.97)
end

push!(fnnames,"spdofsnd|Speed of Sound, m/s|(2.112)")
"""

### Syntax
    spdofsnd(gam,Mw,T)
    spdofsnd(T)

### Description
_Eqn. 2.112_

Calculates the speed of sound of an ideal gas with specific heat ratio 'gam' and molecular weight 'Mw' at the temperature 'T'. Default 'gam' corresponds to Air.

### Arguments
'gam::Real': Specific Heat Ratio (Default: 7/5)

'Mw::Real': Molecular Weight, amu or kg/mol (Default: 28.97)

'T::Real': Static Temperature, K

### See Also
scaleht

"""
function spdofsnd(gamma::Real,Mw::Real,Temp::Real) #Ideal Gas Speed of Sound
  Ru = 8314.46 #Universal Gas Constant, J/kmol K
  sos = sqrt((gamma*Ru*Temp)/Mw)
  return sos
end
function spdofsnd(Temp::Real) #Alternative Method for generic case (Air)
  spdofsnd(1.4,28.97,Temp)
end

push!(fnnames,"PtP|Stagnation/Static Pressure Ratio|(5.85)")
"""

### Syntax
    PtP(gam,M)
    PtP(M)

### Description
_Eqn. 5.85_

Calculates the ratio of stagnation pressure to static pressure in a flow of an ideal gas with specific heat ratio 'gam' at Mach Number 'M'. Default 'gam' corresponds to Air.

### Arguments
'gam::Real': Specific Heat Ratio (Default: 7/5)

'M::Real': Mach Number

### See Also
TtT, P2P1Norm, Pt2Pt1Norm, T2T1Norm

"""
function PtP(gamma::Real,Mach::Real) #eq 9.62
  expo = gamma/(gamma-1)
  PtP = TtT(gamma,Mach)^expo
  return PtP
end
function PtP(Mach::Real) #Alternative Method for generic case (Air/diatomic)
  PtP(1.4,Mach)
end

push!(fnnames,"TtT|Stagnation/Static Temperature Ratio|(5.77)")
"""

### Syntax
    TtT(gam,M)
    TtT(M)

### Description
_Eqn. 5.77_

Calculates the ratio of stagnation temperature to static temperature in a flow of an ideal gas with specific heat ratio 'gam' at Mach Number 'M'. Default 'gam' corresponds to Air.

### Arguments
'gam::Real': Specific Heat Ratio (Default: 7/5)

'M::Real': Mach Number

### See Also
PtP, T2T1Norm, P2P1Norm, Pt2Pt1Norm

"""
function TtT(gamma::Real,Mach::Real) #eq 5.77
  TtT = 1 + (gamma-1)/2*Mach^2
  return TtT
end
function TtT(Mach::Real) #Alternative Method for generic case (Air/diatomic)
  TtT(1.4,Mach)
end

push!(fnnames,"NormShock|Normal Shock Jump Relations|(9.56-97)")
"""

### Syntax
    NormShock(which,gam,M)
    NormShock(which,M)

### Description
Calculates the normal shock jump relation selected by 'which' for an ideal gas with specific heat ratio 'gam' at shock Mach Number 'M'. Default 'gam' corresponds to Air.

### Arguments
'which::String': Shock Jump Relation Selection
- "M" -> Mach Number immediately after shock, 'M2' _Eqn. 9.60_
- "T" -> Static temperature ratio across shock, 'T2/T1' _Eqn. 9.73_
- "P" -> Static pressure ratio across shock, 'P2/P1' _Eqn. 9.56_
- "Pt" -> Stagnation pressure ratio across shock, 'Pt2/Pt1' _Eqn. 9.66_
- "r" -> Density ratio across shock, 'r2/r1' _Eqn. 9.97_
'gam::Real': Specific Heat Ratio (Default: 7/5)

'M::Real': Shock Mach Number (Mach Number immediately before shock)

### See Also
OblShock, Mach2Norm, T2T1Norm, P2P1Norm, Pt2Pt1Norm, r2r1Norm

"""
function NormShock(ver::String,gamma::Real,Mach::Real) #Consolidated Normal Shock Jump Relations
  if ver == "M"
    Mach2Norm(gamma,Mach)
  elseif ver == "T"
    T2T1Norm(gamma,Mach)
  elseif ver == "P"
    P2P1Norm(gamma,Mach)
  elseif ver == "Pt"
    Pt2Pt1Norm(gamma,Mach)
  elseif ver == "r"
    r2r1Norm(gamma,Mach)
  end
end
function NormShock(ver::String,Mach::Real) #Alternative Method for generic case (Air/diatomic)
  NormShock(ver,1.4,Mach)
end

push!(fnnames,"Mach2Norm|*Normal Shock Exit Mach|(9.60)")
"""

### Syntax
    Mach2Norm(gam,M)

### Description
_Eqn. 9.60_

Calculates the mach number immediately after a normal shock of Mach Number 'M' in an ideal gas with specific heat ratio 'gam'.

### Arguments
'gam::Real': Specific Heat Ratio

'M::Real': Shock Mach Number (Mach Number immediately before shock)

### See Also
NormShock, Mach2Obl, T2T1Norm, P2P1Norm, Pt2Pt1Norm, r2r1Norm

"""
function Mach2Norm(gamma::Real,Mach::Real) #eq 9.60
  numer = 1 + (gamma-1)/2*Mach^2
  denom = gamma*Mach^2 - (gamma-1)/2
  mach2 = sqrt(numer/denom)
  return mach2
end

push!(fnnames,"T2T1Norm|*Normal Shock Static Temperature Ratio|(9.73)")
"""

### Syntax
    T2T1Norm(gam,M)

### Description
_Eqn. 9.73_

Calculates the static temperature ratio across a normal shock of Mach Number 'M' in an ideal gas with specific heat ratio 'gam'.

### Arguments
'gam::Real': Specific Heat Ratio

'M::Real': Shock Mach Number (Mach Number immediately before shock)

### See Also
NormShock, T2T1Obl, Mach2Norm, P2P1Norm, Pt2Pt1Norm, r2r1Norm

"""
function T2T1Norm(gamma::Real,Mach::Real) #eq 9.73
  numer = (1 + (gamma-1)/2*Mach^2)*(gamma*Mach^2 - (gamma-1)/2)
  denom = ((gamma+1)/2)^2*Mach^2
  T2T1 = numer/denom
  return T2T1
end

push!(fnnames,"P2P1Norm|*Normal Shock Static Pressure Ratio|(9.56)")
"""

### Syntax
    P2P1Norm(gam,M)

### Description
_Eqn. 9.56_

Calculates the static pressure ratio across a normal shock of Mach Number 'M' in an ideal gas with specific heat ratio 'gam'.

### Arguments
'gam::Real': Specific Heat Ratio

'M::Real': Shock Mach Number (Mach Number immediately before shock)

### See Also
NormShock, P2P1Obl, Mach2Norm, T2T1Norm, Pt2Pt1Norm, r2r1Norm

"""
function P2P1Norm(gamma::Real,Mach::Real) #Static Pressure across normal shock (eq. 9.56)
  Pratio = ((2*gamma)/(gamma-1)*Mach^2 - 1)/((gamma+1)/(gamma-1))
  return Pratio
end

push!(fnnames,"Pt2Pt1Norm|*Normal Shock Stagnation Pressure Ratio|(9.66)")
"""

### Syntax
    Pt2Pt1Norm(gam,M)

### Description
_Eqn. 9.66_

Calculates the stagnation pressure ratio across a normal shock of Mach Number 'M' in an ideal gas with specific heat ratio 'gam'.

### Arguments
'gam::Real': Specific Heat Ratio

'M::Real': Shock Mach Number (Mach Number immediately before shock)

### See Also
NormShock, Pt2Pt1Obl, P2P1Norm, Mach2Norm, T2T1Norm, r2r1Norm

"""
function Pt2Pt1Norm(gamma::Real,Mach::Real) #eq 9.66
  numer1 = (gamma+1)/(gamma-1)
  denom1 = (2*gamma)/(gamma-1)*Mach^2 - 1
  part1 = (numer1/denom1)^(1/(gamma-1))
  numer2 = (gamma+1)/2*Mach^2
  denom2 = 1 + (gamma-1)/2*Mach^2
  part2 = (numer2/denom2)^(gamma/(gamma-1))
  Pt2Pt1 = part1*part2
  return Pt2Pt1
end

push!(fnnames,"r2r1Norm|*Normal Shock Density Ratio|(9.97)")
"""

### Syntax
    r2r1Norm(gam,M)

### Description
_Eqn. 9.97_

Calculates the density ratio across a normal shock of Mach Number 'M' in an ideal gas with specific heat ratio 'gam'.

### Arguments
'gam::Real': Specific Heat Ratio

'M::Real': Shock Mach Number (Mach Number immediately before shock)

### See Also
NormShock, r2r1Obl, Pt2Pt1Norm, P2P1Norm, Mach2Norm, T2T1Norm

"""
function r2r1Norm(gamma,Mach) #eq 9.97
  r2r1 = ((gamma+1)*Mach^2)/((gamma-1)*Mach^2 + 2)
  return r2r1
end

push!(fnnames,"FindMach|Find Mach Number from Parameter Ratios|")
"""

### Syntax
    FindMach(which,gam,input)
    FindMach(which,input)

### Description
Calculates the Mach Number of a given shock or flow from the shock jump or stagnation condition relation selected by 'which' for an ideal gas with specific heat ratio 'gam'. Default 'gam' corresponds to Air.

### Arguments
'which::String': Flow Relation Input Selection
- "PtP" -> Stagnation to static pressure ratio, 'Pt/P' _Eqn. 5.85_
- "TtT" -> Stagnation to static temperature ratio, 'Tt/T' _Eqn. 5.77_
- "M" -> Mach Number immediately after shock, 'M2' _Eqn. 9.60_
- "T" -> Static temperature ratio across shock, 'T2/T1' _Eqn. 9.73_
- "P" -> Static pressure ratio across shock, 'P2/P1' _Eqn. 9.56_
- "Pt" -> Stagnation pressure ratio across shock, 'Pt2/Pt1' _Eqn. 9.66_
- "r" -> Density ratio across shock, 'r2/r1' _Eqn. 9.97_
'gam::Real': Specific Heat Ratio (Default: 7/5)

'M::Real': Shock Mach Number (Mach Number immediately before shock)

### See Also
PtP, TtT, NormShock

"""
function FindMach(which,gamma,input)
  Mguess = (1+sqrt(5))/2
  if which == "PtP"
    return (2/(gamma-1)*(input^((gamma-1)/gamma) - 1))^(1/2) #Eq 5.85
  elseif which == "TtT"
    return (2/(gamma-1)*(input - 1))^(1/2) #Eq 5.77
  elseif which == "P"
    return (1/(2*gamma)*(input*(gamma+1) + gamma - 1))^(1/2) #Eq 9.56
  elseif which == "Pt"
    FindMach(which,gamma,input,Mguess)
  elseif which == "T"
    FindMach(which,gamma,input,Mguess)
  elseif which == "r"
    FindMach(which,gamma,input,Mguess)
  elseif which == "M"
    FindMach(which,gamma,input,Mguess)
  end
end
function FindMach(which,input)
  FindMach(which,1.4,input)
end
function FindMach(which,gamma,input,Mguess)
  if which == "Pt"
    curval = NormShock("Pt",gamma,Mguess)
    if abs(curval - input)/input < 1e-5
      return round(Mguess,digits=4)
    elseif curval < input
      FindMach(which,gamma,input,0.9*Mguess)
    elseif curval > input
      FindMach(which,gamma,input,1.1*Mguess)
    end
  elseif which == "T"
    curval = NormShock("T",gamma,Mguess)
    if abs(curval - input)/input < 1e-5
      return round(Mguess,digits=4)
    elseif curval > input
      FindMach(which,gamma,input,0.9*Mguess)
    elseif curval < input
      FindMach(which,gamma,input,1.1*Mguess)
    end
  elseif which == "r"
    curval = NormShock("r",gamma,Mguess)
    if abs(curval - input)/input < 1e-5
      return round(Mguess,digits=4)
    elseif curval > input
      FindMach(which,gamma,input,0.9*Mguess)
    elseif curval < input
      FindMach(which,gamma,input,1.1*Mguess)
    end
  elseif which == "M"
    curval = NormShock("M",gamma,Mguess)
    if abs(curval - input)/input < 1e-5
      return round(Mguess,digits=4)
    elseif curval < input
      FindMach(which,gamma,input,0.9*Mguess)
    elseif curval > input
      FindMach(which,gamma,input,1.1*Mguess)
    end
  end
end

push!(fnnames,"OblShock|Oblique Shock Jump Relations|(12.7-15)")
"""

### Syntax
    OblShock(which,M,beta)
    OblShock(which,M,theta)
    OblShock(which,gam,M,beta)
    OblShock(which,gam,M,theta)
    OblShock(which,gam,M,beta,theta)

### Description
Calculates the oblique shock jump relation selected by 'which' for an ideal gas with specific heat ratio 'gam' at shock Mach Number 'M', shock angle 'beta', and flow turn angle 'theta'. Default 'gam' corresponds to Air.

### Arguments
'which::String': Shock Jump Relation Selection
- "theta"/"θ" -> Flow turn angle through shock, 'theta' _Eqn. 12.15_
- "T" -> Static temperature ratio across shock, 'T2/T1' _Eqn. 12.7_
- "P" -> Static pressure ratio across shock, 'P2/P1' _Eqn. 12.7_
- "Pt" -> Stagnation pressure ratio across shock, 'Pt2/Pt1' _Eqn. 12.7_
- "r" -> Density ratio across shock, 'r2/r1' _Eqn. 12.7_
- "betaS"/"βS" -> Strong shock solution for shock angle, 'beta' _Eqn. 12.15_
  - Requires Syntax: OblShock(which,gam,M,theta)
- "betaW"/"βW" -> Weak shock solution for shock angle, 'beta' _Eqn. 12.15_
  - Requires Syntax: OblShock(which,gam,M,theta)
- "M" -> Mach number immediately after shock, 'M2' _Eqn. 12.7_
  - Requires Syntax: OblShock(which,gam,M,beta,theta)
'gam::Real': Specific Heat Ratio (Default: 7/5)

'M::Real': Shock Mach Number (Mach Number immediately before shock)

'beta::Real': Shock angle to incoming flow, degrees

'theta::Real': Flow turn angle through shock, degrees

### See Also
NormShock, Mach2Obl, T2T1Obl, P2P1Obl, Pt2Pt1Obl, r2r1Obl, deflang, deflangsol

"""
function OblShock(which::String,gamma::Real,Mach::Real,beta::Real)
  theta = beta
  if which == "theta" || which == "θ"
    deflang(gamma,Mach,beta)
  elseif which == "T"
    T2T1Obl(gamma,Mach,beta)
  elseif which == "P"
    P2P1Obl(gamma,Mach,beta)
  elseif which == "Pt"
    Pt2Pt1Obl(gamma,Mach,beta)
  elseif which == "r"
    r2r1Obl(gamma,Mach,beta)
  elseif which == "betaS" || which == "βS"
    deflangsol("strong",gamma,Mach,theta)
  elseif which == "betaW" || which == "βW"
    deflangsol("weak",gamma,Mach,theta)
  end
end
function OblShock(which::String,gamma::Real,Mach::Real,beta::Real,theta::Real)
  if which == "M"
    Mach2Obl(gamma,Mach,beta,theta)
  end
end
function OblShock(which::String,Mach::Real,beta::Real)
  OblShock(which,1.4,Mach,beta)
end

push!(fnnames,"deflang|**Oblique Shock Flow Turn Angle θ (deg)|(12.15)")
"""

### Syntax
    deflang(gam,M,beta)

### Description
_Eqn. 12.15_

Calculates the flow turn angle through an oblique shock of Mach Number 'M' and angle 'beta' in an ideal gas with specific heat ratio 'gam'.

### Arguments
'gam::Real': Specific Heat Ratio

'M::Real': Shock Mach Number (Mach Number immediately before shock)

'beta::Real': Shock angle to incoming flow, degrees

### See Also
OblShock, deflangsol, Mach2Obl, T2T1Obl, P2P1Obl, Pt2Pt1Obl, r2r1Obl

"""
function deflang(gamma::Real,Mach::Real,shockang::Real)
  numer = Mach^2*sind(shockang)^2 - 1
  denom = 1 + (gamma+1)/2*Mach^2 - Mach^2*sind(shockang)^2
  rhs = cotd(shockang)*(numer/denom)
  theta = atand(rhs)
  return theta
end
#for wolfram alpha atand(cotd(b)*((M^2*sind(b)^2 - 1)/(1 + (gam+1)/2*M^2 - M^2*sind(b)^2)))

push!(fnnames,"deflangsol|**Oblique Shock Angle Solver|(12.15)")
"""

### Syntax
    deflangsol(which,gam,M,beta_guess,theta)
    deflangsol(which,gam,M,theta)

### Description
_Eqn. 12.15_

Calculates the angle of an oblique shock with a flow turn angle 'theta' relative to an incoming flow of Mach Number 'M' in an ideal gas with specific heat ratio 'gam'.

### Arguments
'which::String': Weak or strong shock solution
- "strong" -> strong shock solution
- "weak" -> weak shock solution
'gam::Real': Specific Heat Ratio

'M::Real': Shock Mach Number (Mach Number immediately before shock)

'beta_guess::Real': Guess for shock angle to incoming flow, degrees

'theta::Real': Flow turn angle through shock, degrees

### See Also
OblShock, deflang, Mach2Obl, T2T1Obl, P2P1Obl, Pt2Pt1Obl, r2r1Obl

"""
function deflangsol(which::String,gamma::Real,Mach::Real,bguess::Real,knownang::Real)
  curval = deflang(gamma,Mach,bguess)
  if which == "weak"
    if abs(curval - knownang)/knownang < 1e-4
      return round(bguess,digits=2)
    elseif curval > knownang
      deflangsol(which,gamma,Mach,0.9*bguess,knownang)
    elseif curval < knownang
      deflangsol(which,gamma,Mach,1.1*bguess,knownang)
    end
  elseif which == "strong"
    if abs(curval - knownang)/knownang < 1e-4
      return round(bguess,digits=2)
    elseif curval < knownang
      deflangsol(which,gamma,Mach,0.9*bguess,knownang)
    elseif curval > knownang
      deflangsol(which,gamma,Mach,1.1*bguess,knownang)
    end
  end
end
function deflangsol(which::String,gamma::Real,Mach::Real,knownang::Real) #Alternative Method to dircumvent initial guess
  if which == "weak"
    deflangsol(which,gamma,Mach,10,knownang)
  elseif which == "strong"
    deflangsol(which,gamma,Mach,90,knownang)
  end
end

push!(fnnames,"Mach2Obl|**Oblique Shock Exit Mach|(12.7)")
"""

### Syntax
    Mach2Obl(gam,M,beta,theta)

### Description
_Eqn. 12.7_

Calculates the Mach Number immediately after an oblique shock of Mach Number 'M', shock angle 'beta', and flow turn angle 'theta' in an ideal gas with specific heat ratio 'gam'.

### Arguments
'gam::Real': Specific Heat Ratio

'M::Real': Shock Mach Number (Mach Number immediately before shock)

'beta::Real': Shock angle to incoming flow, degrees

'theta::Real': Flow turn angle through shock, degrees

### See Also
OblShock, Mach2Norm, deflang, deflangsol, T2T1Obl, P2P1Obl, Pt2Pt1Obl, r2r1Obl

"""
function Mach2Obl(gamma::Real,Mach::Real,beta::Real,theta::Real)
  numer = (gamma-1)*Mach^2*sind(beta)^2 + 2
  denom = 2*gamma*Mach^2*sind(beta)^2 - (gamma-1)
  frac = numer/denom
  M2 = sqrt(frac/sind(beta-theta)^2)
  return M2
end

push!(fnnames,"T2T1Obl|**Oblique Shock Static Temperature Ratio|(12.7)")
"""

### Syntax
    T2T1Obl(gam,M,beta)

### Description
_Eqn. 12.7_

Calculates the static temperature ratio across an oblique shock of Mach Number 'M' and shock angle 'beta' in an ideal gas with specific heat ratio 'gam'.

### Arguments
'gam::Real': Specific Heat Ratio

'M::Real': Shock Mach Number (Mach Number immediately before shock)

'beta::Real': Shock angle to incoming flow, degrees

### See Also
OblShock, T2T1Norm, deflang, deflangsol, Mach2Obl, P2P1Obl, Pt2Pt1Obl, r2r1Obl

"""
function T2T1Obl(gamma::Real,Mach::Real,beta::Real)
  msin = Mach^2*sind(beta)^2
  numer = (2*gamma*msin - (gamma-1)) * ((gamma-1)*msin + 2)
  denom = (gamma+1)^2*msin
  T2T1 = numer/denom
  return T2T1
end

push!(fnnames,"P2P1Obl|**Oblique Shock Static Pressure Ratio|(12.7)")
"""

### Syntax
    P2P1Obl(gam,M,beta)

### Description
_Eqn. 12.7_

Calculates the static pressure ratio across an oblique shock of Mach Number 'M' and shock angle 'beta' in an ideal gas with specific heat ratio 'gam'.

### Arguments
'gam::Real': Specific Heat Ratio

'M::Real': Shock Mach Number (Mach Number immediately before shock)

'beta::Real': Shock angle to incoming flow, degrees

### See Also
OblShock, P2P1Norm, deflang, deflangsol, Mach2Obl, T2T1Obl, Pt2Pt1Obl, r2r1Obl

"""
function P2P1Obl(gamma::Real,Mach::Real,beta::Real)
  numer = 2*gamma*Mach^2*sind(beta)^2 - (gamma-1)
  denom = gamma+1
  P2P1 = numer/denom
  return P2P1
end

push!(fnnames,"Pt2Pt1Obl|**Oblique Shock Stagnation Pressure Ratio|(12.8)")
"""

### Syntax
    Pt2Pt1Obl(gam,M,beta)

### Description
_Eqn. 12.8_

Calculates the stagnation pressure ratio across an oblique shock of Mach Number 'M' and shock angle 'beta' in an ideal gas with specific heat ratio 'gam'.

### Arguments
'gam::Real': Specific Heat Ratio

'M::Real': Shock Mach Number (Mach Number immediately before shock)

'beta::Real': Shock angle to incoming flow, degrees

### See Also
OblShock, Pt2Pt1Norm, deflang, deflangsol, Mach2Obl, T2T1Obl, P2P1Obl, r2r1Obl

"""
function Pt2Pt1Obl(gamma::Real,Mach::Real,beta::Real)
  numer1 = (gamma+1)*Mach^2*sind(beta)^2
  denom1 = (gamma-1)*Mach^2*sind(beta)^2 + 2
  part1 = (numer1/denom1)^(gamma/(gamma-1))
  numer2 = gamma+1
  denom2 = 2*gamma*Mach^2*sind(beta)^2 - (gamma-1)
  part2 = (numer2/denom2)^(1/(gamma-1))
  Pt2Pt1 = part1*part2
  return Pt2Pt1
end

push!(fnnames,"r2r1Obl|**Oblique Shock Density Ratio|(12.7)")
"""

### Syntax
    r2r1Obl(gam,M,beta)

### Description
_Eqn. 12.7_

Calculates the density ratio across an oblique shock of Mach Number 'M' and shock angle 'beta' in an ideal gas with specific heat ratio 'gam'.

### Arguments
'gam::Real': Specific Heat Ratio

'M::Real': Shock Mach Number (Mach Number immediately before shock)

'beta::Real': Shock angle to incoming flow, degrees

### See Also
OblShock, r2r1Norm, deflang, deflangsol, Mach2Obl, T2T1Obl, P2P1Obl, Pt2Pt1Obl

"""
function r2r1Obl(gamma::Real,Mach::Real,beta::Real)
  numer = (gamma+1)*Mach^2*sind(beta)^2
  denom = (gamma-1)*Mach^2*sind(beta)^2 + 2
  r2r1 = numer/denom
  return r2r1
end

push!(fnnames,"aratio|A*/A = f(M)|(10.16)")
"""

### Syntax
    aratio(gam,M)
    aratio(M)
    aratio(which,gam,A*/A)
    aratio(which,A*/A)

### Description
_Eqn. 10.16_

Also known as f(M). Calculates the area ratio between a theoretical choke point and a point at Mach Number 'M' in a flow of an ideal gas with specific heat ratio 'gam'. When used with 'which' and 'A*/A' inputs, will return the Mach Number of the given flow. Default 'gam' corresponds to Air.

### Arguments
'gam::Real': Specific Heat Ratio (Default: 7/5)

'M::Real': Mach Number

'which::String':
- "sup" -> Supersonic Mach Number solution for given A*/A
- "sub" -> Subsoic Mach Number solution for given A*/A

'A*/A::Real': Area ratio or f(M) from which to derive a Mach Number

### See Also
aratiosol, Fanno, Rayleigh, spdofsnd, NormShock, OblShock

"""
function aratio(gamma::Real,Mach::Real) #eq 10.16
  expo = (gamma+1)/(2*(gamma-1))
  numer = Mach*((gamma+1)/2)^expo
  denom = (1 + (gamma-1)/2*Mach^2)^expo
  aratio = numer/denom
  return aratio
end
function aratio(Mach::Real) #Alternative Method for generic case (Air/diatomic)
  aratio(1.4,Mach)
end
# push!(fnnames,"aratio(which,A*/A)|A*/A = f(M) Solver|(10.16)")
function aratio(which::String,gamma::Real,AstarA::Real) #solver
  aratiosol(which,gamma,AstarA,(1+sqrt(5))/2) #arbitrary guess value
end
function aratio(which::String,AstarA::Real) #Alternative Solver Method for generic case (Air/diatomic)
  aratiosol(which,1.4,AstarA,(1+sqrt(5))/2)
end

push!(fnnames,"aratiosol|A*/A = f(M) Solver (included in aratio)|(10.16)")
"""

### Syntax
    aratiosol(which,gam,A*/A,Mguess)

### Description
_Eqn. 10.16_

Calculates the Mach Number of a flow of an ideal gas with specific heat ratio 'gam' from the A*/A, or f(M) of the given flow.

### Arguments
'which::String':
- "sup" -> Supersonic Mach Number solution for given A*/A
- "sub" -> Subsoic Mach Number solution for given A*/A
'gam::Real': Specific Heat Ratio

'A*/A::Real': Area ratio or f(M) from which to derive a Mach Number

'Mguess::Real': Guess for Mach Number (does not affect solution)

### See Also
aratio, Fanno, Rayleigh, spdofsnd, NormShock, OblShock

"""
function aratiosol(which::String,gamma::Real,AstarA::Real,Mguess::Real)
  global curval = aratio(gamma,Mguess)
  if which == "sup"
    if abs(curval - AstarA)/AstarA < 1e-4
      return round(Mguess,digits=4)
    elseif curval < AstarA
      aratiosol(which,gamma,AstarA,0.9*Mguess)
    elseif curval > AstarA
      aratiosol(which,gamma,AstarA,1.1*Mguess)
    end
  elseif which == "sub"
    if abs(curval - AstarA)/AstarA < 1e-4
      return round(Mguess,digits=4)
    elseif curval > AstarA
      aratiosol(which,gamma,AstarA,0.9*Mguess)
    elseif curval < AstarA
      aratiosol(which,gamma,AstarA,1.1*Mguess)
    end
  end
end

push!(fnnames,"Fanno|Fanno Line Relations|(11.31)")
"""

### Syntax
    Fanno(which,gam,M)
    Fanno(which,M)

### Description
Calculates the Fanno Line Flow relation (1D, adiabatic, constant area flow with friction) selected by 'which' for an ideal gas with specific heat ratio 'gam' at Mach Number 'M'. Default 'gam' corresponds to Air.

### Arguments
'which::String': Fanno Line Relation Selection
- "U" -> Velocity ratio, 'U/U*' _Eqn. 11.31_
- "T" -> Static temperature ratio, 'T*/T' _Eqn. 11.31_
- "P" -> Static pressure ratio, 'P*/P' _Eqn. 11.31_
- "Pt" -> Stagnation pressure ratio, 'Pt*/Pt' _Eqn. 11.31_
- "r" -> Density ratio, 'r*/r' _Eqn. 11.31_
- "CfLD" -> '4CfLmax/D' _Eqn. 11.28_
'gam::Real': Specific Heat Ratio (Default: 7/5)

'M::Real': Mach Number

### See Also
Rayleigh, aratio, NormShock, OblShock

"""
function Fanno(ver::String,gamma::Real,Mach::Real) #eq 11.31
  part1 = 1 + (gamma-1)/2*Mach^2
  part2 = (gamma+1)/2
  base = part1/part2

  if ver == "U" || ver == "r"
    out = sqrt(Mach^2/base)
  elseif ver == "T"
    out = base
  elseif ver == "P"
    out = Mach*sqrt(base)
  elseif ver == "Pt"
    out = aratio(gamma,Mach)
  elseif ver == "CfLD"
    out = ((1 - Mach^2)/(gamma*Mach^2)) + ((gamma+1)/(2*gamma)) * log(Mach^2/base)
  end
  return out
end
function Fanno(ver::String,Mach::Real) #Alternative Method for generic case (Air/diatomic)
  Fanno(ver,1.4,Mach)
end

push!(fnnames,"Rayleigh|Rayleigh Line Relations|(11.47)")
"""

### Syntax
    Rayleigh(which,gam,M)
    Rayleigh(which,M)

### Description
Calculates the Rayleigh Line Flow relation (1D, frictionless, constant area flow with heat transfer) selected by 'which' for an ideal gas with specific heat ratio 'gam' at Mach Number 'M'. Default 'gam' corresponds to Air.

### Arguments
'which::String': Rayleigh Line Relation Selection
- "Tt" -> Stagnation temperature ratio, 'Tt*/Tt' _Eqn. 11.43_
- "U" -> Velocity ratio, 'U/U*' _Eqn. 11.47_
- "T" -> Static temperature ratio, 'T*/T' _Eqn. 11.47_
- "P" -> Static pressure ratio, 'P*/P' _Eqn. 11.47_
- "Pt" -> Stagnation pressure ratio, 'Pt*/Pt' _Eqn. 11.47_
- "r" -> Density ratio, 'r*/r' _Eqn. 11.47_
'gam::Real': Specific Heat Ratio (Default: 7/5)

'M::Real': Mach Number

### See Also
Fanno, aratio, NormShock, OblShock

"""
function Rayleigh(ver::String,gamma::Real,Mach::Real) #eq 11.47
  part1 = 1 + (gamma-1)/2*Mach^2
  base = (1+gamma*Mach^2)/(gamma+1)

  if ver == "U" || ver == "r"
    out = Mach^2/base
  elseif ver == "T"
    out = (base/Mach)^2
  elseif ver == "P"
    out = base
  elseif ver == "Pt"
    out = base*((gamma+1)/(2*part1))^(gamma/(gamma-1))
  elseif ver == "Tt"
    out = (base*(1+gamma*Mach^2))/(2*Mach^2*part1)
  end
  return out
end
function Rayleigh(ver::String,Mach::Real) #Alternative Method for generic case (Air/diatomic)
  Rayleigh(ver,1.4,Mach)
end

push!(fnnames,"PMang|Prandtl-Meyer Expansion Angle ω (deg)|(12.38)")
"""

### Syntax
    PMang(gam,M)
    PMang(M)

### Description
_Eqn. 12.38_

Calculates the Prandtl-Meyer expansion angle 'ω' (degrees) required to reach Mach Number 'M' in a flow of an ideal gas with specific heat ratio 'gam'. Default 'gam' corresponds to Air.

### Arguments
'gam::Real': Specific Heat Ratio (Default: 7/5)

'M::Real': Mach Number

### See Also
PMangsol, aratio, Fanno, Rayleigh, spdofsnd, NormShock, OblShock

"""
function PMang(gamma::Real,Mach::Real)
  frac = ((gamma+1)/(gamma-1))
  part1 = sqrt(Mach^2 - 1)
  PM = sqrt(frac) * atan(sqrt(1/frac)*part1) - atan(part1)
  return PM*180/pi
end
function PMang(Mach::Real) #Alternative Method for generic case (Air/Diatomic)
  PMang(1.4,Mach)
end
#for wolfram alpha sqrt((1.4+1)/(1.4-1)) * atan(sqrt((1.4-1)/(1.4+1))*sqrt(M^2 - 1)) - atan(sqrt(M^2 - 1))

push!(fnnames,"PMangsol|Prandtl-Meyer Expansion Angle Solver|(12.38)")
"""

### Syntax
    PMangsol(gam,Mguess,ω)
    PMangsol(gam,ω)
    PMangsol(ω)

### Description
_Eqn. 12.38_

Calculates the Mach Number reached by expanding a Mach 1 flow through the Prandtl-Meyer expansion angle 'ω' (degrees) in a flow of an ideal gas with specific heat ratio 'gam'. Default 'gam' corresponds to Air.

### Arguments
'gam::Real': Specific Heat Ratio (Default: 7/5)

'Mguess::Real': Mach Number guess

'ω::Real': Prandtl-Meyer Expansion Angle, degrees

### See Also
PMang, aratio, Fanno, Rayleigh, spdofsnd, NormShock, OblShock

"""
function PMangsol(gamma::Real,Mguess::Real,knownang::Real)
  curval = PMang(gamma,Mguess)
  if abs(curval - knownang)/knownang < 1e-5
    return round(Mguess,digits=4)
  elseif curval > knownang
    PMangsol(gamma,0.9*Mguess,knownang)
  elseif curval < knownang
    PMangsol(gamma,1.1*Mguess,knownang)
  end
end
function PMangsol(gamma::Real,knownang::Real) #Alternative Method to circumvent guess
  PMangsol(gamma,10,knownang)
end
function PMangsol(knownang::Real) #Alternative Method for generic case (Air/Diatomic)
  PMangsol(1.4,10,knownang)
end

push!(fnnames,"ShockTubeP4P1|Shock Tube Initial Pressure Ratio|(13.64)")
"""

### Syntax
    ShockTubeP4P1(gam1,gam4,T1,T4,Mw1,Mw4,P2/P1)

### Description
_Eqn. 13.64_

Calculates the initial pressure ratio between the driver and driven sections of a shock tube from the specific heat ratios, molecular weights, and initial static temperatures of both gases, and the pressure ratio ("strength") of the initial shock.

### Arguments
'gam1::Real': Specific Heat Ratio of driven gas

'gam4::Real': Specific Heat Ratio of driver gas

'T1::Real': Initial static temperature of driven gas, K

'T4::Real': Initial static temperature of driver gas, K

'Mw1::Real': Molecular weight of driven gas, amu or kg/mol

'Mw4::Real': Molecular weight of driver gas, amu or kg/mol

'P2/P1::Real': Static pressure ratio across initial shock (shock strength)

### See Also
NormShock, spdofsnd

"""
function ShockTubeP4P1(gam1::Real,gam4::Real,T1::Real,T4::Real,Mw1::Real,Mw4::Real,P2P1::Real)
  numer = (gam4-1)*(spdofsnd(gam1,Mw1,T1)/spdofsnd(gam4,Mw4,T4))*(P2P1-1)
  denom = (4*gam1^2+2*gam1*(gam1+1)*(P2P1-1))^(1/2)
  expo = -(2*gam4/(gam4-1))
  P4P1 = P2P1*(1-numer/denom)^expo
  return P4P1
end

push!(fnnames,"ShockTubeUp|Shock Tube Piston Velocity Up|(13.59)")
"""

### Syntax
    ShockTubeUp(gam1,M1,Mw1,T1)

### Description
_Eqn. 13.59_

Calculates the piston (contact surface) velocity of a shock tube from the specific heat ratio, molecular weight, and initial static temperature of the driven gas, and the Mach Number of the initial shock.

### Arguments
'gam1::Real': Specific Heat Ratio of driven gas

'M1::Real': Mach Number of initial shock

'Mw1::Real': Molecular weight of driven gas, amu or kg/mol

'T1::Real': Initial static temperature of driven gas, K

### See Also
NormShock, spdofsnd

"""
function ShockTubeUp(gamma::Real,Mach::Real,Mw1::Real,Temp::Real)
  Up = spdofsnd(gamma,Mw1,Temp)*((Mach^2-1)/((gamma+1)/2*Mach))
  return Up
end

# push!(fnnames,"ShockTubeMrs(gam1,Up,Mw1,T2,Mguess)|Shock Tube Reflected Shock Mach Number|(13.59)")
# function ShockTubeMrs(gamma::Real,Up::Real,Mw1::Real,Temp::Real,Mguess::Real)
#   curval = ShockTubeUp(gamma,Mguess,Mw1,Temp)
#   if abs(curval - Up)/Up < 1e-5
#     return round(Mguess,digits=4)
#   elseif curval > Up
#     ShockTubeMrs(gamma,Up,Mw1,Temp,0.9*Mguess)
#   elseif curval < Up
#     ShockTubeMrs(gamma,Up,Mw1,Temp,1.1*Mguess)
#   end
# end

push!(fnnames,"ChokedCond|Pressure Ratio for Choked Flow Pt/Pa|(10.26)")
"""

### Syntax
    ChokedCond(gam)
    ChokedCond()

### Description
_Eqn. 10.26_

Calculates the ratio of upstream stagnation pressure to ambient static pressure across a throat required to create choked flow in an ideal gas with specific heat ratio 'gam'. Default 'gam' corresponds to Air.

### Arguments
'gam::Real': Specific Heat Ratio (Default: 7/5)

### See Also
NozzleMach, gamma, PtP

"""
function ChokedCond(gamma::Real)
  PtPa = ((gamma+1)/2)^(gamma/(gamma-1))
  return PtPa
end
function ChokedCond() #Alternative Method for generic case (Air/diatomic)
  ChokedCond(1.4)
end

push!(fnnames,"NozzleMach|Fully Expanded Nozzle Exit Mach Number|(10.25)")
"""

### Syntax
    ChokedCond(gam,Pt/Pa)
    ChokedCond(Pt/Pa)

### Description
_Eqn. 10.25_

Calculates the Mach Number at the exit of a fully expanded (Pe = Po) converging-diverging nozzle discharging an ideal gas with specific heat ratio 'gam' from the ratio of upstream stagnation pressure to ambient static pressure 'Pt/Pa'. Default 'gam' corresponds to Air.

### Arguments
'gam::Real': Specific Heat Ratio (Default: 7/5)

'Pt/Pa::Real': Ratio of upstream stagnation pressure to ambient static pressure

### See Also
ChokedCond, gamma, PtP

"""
function NozzleMach(gamma::Real,PtPa::Real)
  Me = ((2/(gamma-1)) * (PtPa^((gamma-1)/gamma) - 1))^(1/2)
  return Me
end
function NozzleMach(PtPa::Real) #Alternative Method for generic case (Air/diatomic)
  NozzleMach(1.4,PtPa)
end

push!(fnnames,"scaleht|Atmospheric Scale Height, m|(2.119)")
"""

### Syntax
    scaleht(Mw,T)
    scaleht()

### Description
_Eqn. 2.119_

Calculates the scale height of an atmosphere composed of an ideal gas with molecular weight 'Mw' and atmosphere base temperature 'T'. Defaults correspond to the Earth's atmosphere.

### Arguments
'gam::Real': Specific Heat Ratio (Default: 7/5)

'T::Real': Static temperature at base of atmosphere (Default: 292.15)

### See Also
spdofsnd, gamma

"""
function scaleht(Mw::Real,Temp::Real) #Scale Height of atmoshpere (eq 2.119)
  Ru = 8314.46 #Universal Gas Constant, J/kmol K
  g0 = 9.80665 #Std. Gravitational Acceleration
  H = (Ru*Temp)/(Mw*g0)
  return H
end
function scaleht()
  scaleht(28.97,292.15) #Alternative Method for generic case (Earth)
end

push!(fnnames,"shockRe|Shock Reynolds Number (ρ*U*δ)/μ|(9.96)")
"""

### Syntax
    shockRe(gam,M1,Pr,μᵥ/μ)

### Description
_Eqn. 9.96_

Calculates the shock Reynolds number, (ρ*U*δ)/μ, of a shock of Mach Number 'M' where its thickness δ is the characteristic length, in terms of the specific heat ratio 'gam', Prandtl Number 'Pr', and ratio of bulk to shear (dynamic) viscosity 'μᵥ/μ' of the gas in which it propagates. Shock thickness can be estimated from the shock Reynolds number using the average values of ρ, U, and μ through the shock.

### Arguments
'gam::Real': Specific Heat Ratio

'M1::Real': Shock Mach Number (Mach Number immediately before shock)

'Pr::Real': Prandtl Number

'μᵥ/μ::Real': Ratio of bulk to shear (dynamic) viscosity

### See Also
NormShock, spdofsnd

"""
function shockRe(gamma::Real,Mach::Real,Pr::Real,muvr::Real) #eq 9.96
  numer1 = 2*gamma*(gamma-1)*(4.0/3.0+muvr)*Mach^2*(1/(r2r1Norm(gamma,Mach))-1)^2
  numer2 = (4*(gamma/Pr)*(T2T1Norm(gamma,Mach)-1)^2)/(T2T1Norm(gamma,Mach)+1)
  denom1 = (T2T1Norm(gamma,Mach) + 1)
  denom2 = log(T2T1Norm(gamma,Mach)*(1/r2r1Norm(gamma,Mach))^(gamma-1))
  Re = (numer1 + numer2)/(denom1*denom2)
  return Re
end

push!(fnnames,"CJDetMach|Chapman-Jouget Detonation Mach Number|(11.61)")
"""

### Syntax
    CJDetMach(gam,Δht,Cp,T1)

### Description
_Eqn. 11.61_

Calculates the Mach Number of the shock at the front of a Chapman-Jouget detonation wave from the stagnation enthalpy change produced by the detonation wave 'Δht' and the specific heat ratio 'gam', the specific heat 'Cp', and the static temperature 'T1' of the ambient gas.

### Arguments
'gam::Real': Specific Heat Ratio of ambient gas

'Δht::Real': Stagnation enthalpy change through detonation wave

'Cp::Real': Specific heat capacity of ambient gas

'T1::Real': Static temperature of ambient gas

### See Also
NormShock, spdofsnd, gamma, C_p

"""
function CJDetMach(gamma::Real,dht::Real,Cp_::Real,Temp::Real)
  part1 = 1+(gamma+1)*dht/(Cp_*Temp)
  M1 = (part1 + (part1^2 - 1)^(1/2))^(1/2)
  return M1
end

#~~~~~~~~~~Assemble Documentation From Script Content~~~~~~~~~~
fnnames = [split(fnnames[i],"|") for i=1:length(fnnames)]

local maxl = [maximum([length(fnnames[j][i]) for j=1:length(fnnames)]) for i=1:3]

insert!(fnnames,1,[lpad("",maxl[i],"-") for i=1:3])
insert!(fnnames,3,[lpad("",maxl[i],"-") for i=1:3])
push!(fnnames,[lpad("",maxl[i],"-") for i=1:3])

global aa210docs = [] #for on-demand function reference

for i=1:length(fnnames)
  for j=1:3
    if j==1
      fnnames[i][j] = lpad(fnnames[i][j],maxl[j])
    else
      fnnames[i][j] = rpad(fnnames[i][j],maxl[j])
    end
  end
  push!(aa210docs," "*join(fnnames[i]," | "))
end

# push!(aa210docs, "\n Function Arguments:")

# local pad = 9 #padding width for arg names

#add arg help lines to docstring
# push!(aa210docs, "\n"*lpad("dof: ",pad)*"degrees of freedom of gas molecule (monatomic: 3, diatomic: 5)")
# push!(aa210docs, lpad("Mw: ",pad)*"molar mass of gas, g/mol")
# push!(aa210docs, lpad("gam: ",pad)*"γ, specific heat ratio of gas")
# push!(aa210docs, lpad("T: ",pad)*"temperature of gas (atmosphere base temp for scale height)")
# push!(aa210docs, lpad("M (M1): ",pad)*"mach number of flow (before shock)")
# push!(aa210docs, lpad("Pr: ",pad)*"Prandtl Number of gas")
# push!(aa210docs, lpad("μᵥ/μ: ",pad)*"ratio of bulk viscosity to dynamic viscosity")

# push!(aa210docs, lpad("which: ",pad)*"flow relation selection")
# push!(aa210docs, lpad(" ",pad+2)*"\"U\" -> U/U*, ρ*/ρ (Rayleigh & Fanno Only)")
# push!(aa210docs, lpad(" ",pad+2)*"\"M\" -> Shock Exit Mach Number M2 (NormShock & OblShock Only)")
# push!(aa210docs, lpad(" ",pad+2)*"\"T\" -> T*/T (Rayleigh & Fanno), T2/T1 (NormShock & OblShock)")
# push!(aa210docs, lpad(" ",pad+2)*"\"P\" -> P*/P (Rayleigh & Fanno), P2/P1 (NormShock & OblShock)")
# push!(aa210docs, lpad(" ",pad+2)*"\"Pt\" -> Pt*/Pt (Rayleigh & Fanno), Pt2/Pt1 (NormShock & OblShock)")
# push!(aa210docs, lpad(" ",pad+2)*"\"CfLD\" -> 4CfLmax/D (Fanno only)")
# push!(aa210docs, lpad(" ",pad+2)*"\"Tt\" -> Tt*/Tt (Rayleigh Only)")
# push!(aa210docs, lpad(" ",pad+2)*"\"r\" -> ρ2/ρ1 (NormShock & OblShock Only)")
# push!(aa210docs, lpad(" ",pad+2)*"\"sub\" -> subsonic solution (aratiosol Only)")
# push!(aa210docs, lpad(" ",pad+2)*"\"sup\" -> supersonic solution (aratiosol Only)")
# push!(aa210docs, lpad(" ",pad+2)*"\"strong\" -> strong shock solution (deflangsol Only)")
# push!(aa210docs, lpad(" ",pad+2)*"\"weak\" -> weak/normal shock solution (deflangsol Only)")
# push!(aa210docs, lpad(" ",pad+2)*"\"thetaS\"/\"θS\" -> strong shock solution (OblShock Only)")
# push!(aa210docs, lpad(" ",pad+2)*"\"thetaW\"/\"θW\" -> weak/normal shock solution (OblShock Only)")

# push!(aa210docs, lpad("beta: ",pad)*"oblique shock angle to oncoming flow (degrees)")
# push!(aa210docs, lpad("theta: ",pad)*"flow turn angle thru oblique shock (degrees)")
# push!(aa210docs, lpad("Δht: ",pad)*"change in stagnation enthalpy through detonation")

push!(aa210docs, "\nFunctions with descriptions preceded by an asterisk are included within NormShock")

push!(aa210docs, "\nFunctions with descriptions preceded by two asterisks are included within OblShock")

push!(aa210docs, "\nType \"AA210AFnList()\" or \"?AA210A_Functions\" to see function list again")

push!(aa210docs, "\nType \"?<function_name>\" to see individual function documentation")

insert!(aa210docs,1,"\n\n AA210A Compressible Flow Formulae\n\n Material from \"AA210A Course Reader - Fundamentals of Compressible Flow\"\n by Prof. Brian J. Cantwell - https://web.stanford.edu/~cantwell/\n")

end #close "begin" local variable scope

aa210docscomp = join(aa210docs,"\n") #compile docstring from array

"""

Call AA210AFnList() for function list

"""
function AA210AFnList() #function to print docstring on demand
  println(aa210docscomp)
end

end #module end

#Created by Jeff Robinson - jbrobin@stanford.edu


text:
#############################################################
# Constraining values
############################################################
"""
    constrain_kde_distribution(uv::AbstractUncertainScalarKDE,
        constraint::SamplingConstraint)

Return the TruncatedUncertainScalarKDE resulting from applying `constraint`
to `uv`.
"""
function constrain_kde_distribution(uv::AbstractUncertainScalarKDE,
        constraint::SamplingConstraint)
    range_subset, pdf_subset, idx_min, idx_max = truncate(uv, constraint)

    TruncatedUncertainScalarKDE(
        UnivariateKDE(range_subset, pdf_subset),
        uv.values,
        range_subset,
        Weights(pdf_subset)
        )
end

constrain_kde_distribution(uv::AbstractUncertainScalarKDE, constraint::NoConstraint) = uv

#############################################################
# Uncertain values represented by theoretical distributions
# with parameters fitted to empirical data
############################################################

"""
    constrain_theoretical_fitted_distribution(
        uv::TheoreticalFittedUncertainScalar,
        truncated_dist::Distribution)

Return a constrained (truncated) version of an uncertain value represented
by a theoretical distribution with parameters fitted to empirical data.
"""
function constrain_theoretical_fitted_distribution(
        uv::TheoreticalFittedUncertainScalar,
        truncated_dist::Distribution)

    ConstrainedUncertainScalarTheoreticalFit(FittedDistribution(truncated_dist), uv.values)
end

#############################################################
# Uncertain values represented by theoretical distributions
############################################################

"""
    constrain_theoretical_distribution(
        uv::AbstractUncertainOneParameterScalarValue,
        truncated_dist::Distribution)

Return a constrained (truncated) version of an uncertain value represented
by a one-parameter theoretical distribution.
"""
function constrain_theoretical_distribution(
        uv::AbstractUncertainOneParameterScalarValue,
        truncated_dist::Distribution)

    # Get parameters of original distribution
    p1 = fieldnames(typeof(uv))[2]

    ConstrainedUncertainScalarValueOneParameter(
        truncated_dist,
        getfield(uv, p1)
    )
end

"""
    constrain_theoretical_distribution(
        uv::AbstractUncertainTwoParameterScalarValue,
        truncated_dist::Distribution)

Return a constrained (truncated) version of an uncertain value represented
by a two-parameter theoretical distribution.
"""
function constrain_theoretical_distribution(
        uv::AbstractUncertainTwoParameterScalarValue,
        truncated_dist::Distribution)

    # Get parameters of original distribution
    p1, p2 = (fieldnames(typeof(uv))[2:3]...,)

    ConstrainedUncertainScalarValueTwoParameter(
        truncated_dist,
        getfield(uv, p1),
        getfield(uv, p2)
    )
end

"""
    constrain_theoretical_distribution(
        uv::AbstractUncertainTwoParameterScalarValue,
        truncated_dist::Distribution)

Return a constrained (truncated) version of an uncertain value represented
by a two-parameter theoretical distribution.
"""
function constrain_theoretical_distribution(
        uv::AbstractUncertainThreeParameterScalarValue,
        truncated_dist::Distribution)

    # Get parameters of original distribution
    p1, p2, p3 = (fieldnames(typeof(uv))[2:4]...,)

    ConstrainedUncertainScalarValueThreeParameter(
        truncated_dist,
        getfield(uv, p1),
        getfield(uv, p2),
        getfield(uv, p3)
    )
end

#############################################################
# Truncate uncertain values
############################################################
"""
	constrain(uv::AbstractUncertainValue, constraint::SamplingConstraint)

Apply the `constraint` and truncate the support of the distribution
furnishing the uncertain value `uv`. Returns a constrained uncertain value.
"""
constrain(uv::AbstractUncertainValue, constraint::SamplingConstraint)

"""
    constrain(uv::TheoreticalDistributionScalarValue,
        constraint::SamplingConstraint) -> TheoreticalDistributionScalarValue

Apply the `constraint` and truncate the support of an uncertain value `uv`
furnished by a theoretical distribution.
"""
function constrain(uv::TheoreticalDistributionScalarValue,
        constraint::SamplingConstraint)
    constrain_theoretical_distribution(uv, truncate(uv, constraint))
end

"""
    constrain(uv::TheoreticalFittedUncertainScalar,
        constraint::SamplingConstraint) -> ConstrainedUncertainScalarTheoreticalFit

Apply the `constraint` and truncate the support of an uncertain value `uv`
furnished by a theoretical distribution where parameters are fitted to empirical
data.
"""
function constrain(uv::TheoreticalFittedUncertainScalar,
            constraint::SamplingConstraint)

    truncated_dist = truncate(uv, constraint)
    constrain_theoretical_fitted_distribution(uv, truncated_dist)
end

"""
    constrain(uv::AbstractUncertainScalarKDE,
        constraint::SamplingConstraint) -> TruncatedUncertainScalarKDE

Apply the `constraint` and truncate the support of an uncertain value `uv`
represented by a kernel density estimate.
"""
function constrain(uv::AbstractUncertainScalarKDE,
            constraint::SamplingConstraint)
    constrain_kde_distribution(uv, constraint)
end

constrain(uvals::Vector{AbstractUncertainValue}, constraint::SamplingConstraint) =
    [constrain(uval, constraint) for uval in uvals]

function constrain(uvals::Vector{AbstractUncertainValue},
                constraints::Vector{SamplingConstraint})
    [constrain(uvals[i], constraints[i]) for i = 1:length(uvals)]
end

export
ConstrainedUncertainScalarValueTwoParameter,
ConstrainedUncertainScalarValueThreeParameter,
truncate,
constrain

ext: jl


text:
import HTTP, JSON2, Sockets

include("controllers/agent.jl")
include("informer.jl")

"""
    Router{}

Router proxies requests to downstream agent solutions.
"""
mutable struct Router
    agentinformer::Informer
end

function deploy(router::Router)
end

function run(router::Router)
    const AGENT_ROUTER = HTTP.Router()
    HTTP.@register(AGENT_ROUTER, "POST", "/process", processhandler)
    HTTP.@register(AGENT_ROUTER, "POST", "/error", errorhandler)
    HTTP.@register(AGENT_ROUTER, "GET", "/stats", statshandler)

    HTTP.serve(AGENT_ROUTER, Sockets.localhost, 8080)
end

function processproxyhandler(req::HTTP.Request)::HTTP.Response
    input = Dict{String, Any}()
    input = JSON2.read(IOBuffer(HTTP.payload(req)), input)
    # TODO: validate input off openapi spec

    # send to one or all downstream solutions
    # Reinforcement learning

    output = Dict{String, Any}()

    # TODO: validate output off openapi spec
    return HTTP.Response(200, JSON2.write(output))
end

function errorproxyhandler(req::HTTP.Request)::HTTP.Response
    error = Dict{String, Any}()
    error = JSON2.read(IOBuffer(HTTP.payload(req)), error)

    # process error

    return HTTP.Response(200)
end

mutable struct Stats
    agents::Dict{string, Agent}
    kv::Dict{string, Any}
end

function statsproxyhandler(req::HTTP.Request)::HTTP.Response
    # get stats

    stats = Stats(Dict{string, Agent}(), Dict{String, Any}())
    return HTTP.Response(200, JSON2.write(stats))
end

max_stars_repo_name: pbarker/kubemeta

max_issues_repo_name: pbarker/kubemeta

max_forks_repo_name: pbarker/kubemeta


text:
check() = a & 0xffff == b & 0xffff
# Part 1
a, b = 116, 299
n = 0
update(a, b) = (a * 16807, b * 48271) .% 2147483647
for _ in 1:40000000
    a, b = update(a, b)
    n += check()
end
println(n)

# Part 2
a, b = 116, 299
n = 0
for _ in 1:5000000
    while true
        a = (a * 16807) % 2147483647
        a % 4 == 0 && break
    end
    while true
        b = (b * 48271) % 2147483647
        b % 8 == 0 && break
    end
    n += check()
end
println(n)


text:
# This file includes testset for sources.

@testset "ClockTestSet" begin
    @info "Running ClockTestSet ..."

    # Clock construction
    clk1 = Clock(0., 1., 10.)
    clk2 = Clock(0., 1, 10)
    clk3 = Clock(0, 1, 10)
    @test eltype(clk1.t) == Float64
    @test eltype(clk2.t) == Float64
    @test eltype(clk3.t) == Int

    # Check Clock defaults.
    clk = clk1
    @test clk.t == 0.
    @test clk.dt == 1.
    @test clk.tf == 10.
    @test typeof(clk.generator) == Channel{Float64}
    @test clk.generator.sz_max == 0
    @test !ispaused(clk)
    @test !isrunning(clk)

    # Set Clock
    set!(clk)
    @test isrunning(clk)

    # Taking values from clk
    clk = Clock(0., 1., 10.)
    set!(clk)
    @test [take!(clk) for i in 0 : 10] == collect(Float64, 0:10)
    @test isoutoftime(clk)

    # Pausing Clock
    clk = set!(Clock(0., 1, 10))
    @test take!(clk) == 0
    @test take!(clk) == 1.
    pause!(clk)
    for i = 1 : 10
        @test take!(clk) == 1.
    end

    @info "Done ClockTestSet."
end  # testset

@testset "GeneratorsTestSet" begin
    @info "Running GeneratorsTestSet ..."
    # FunctionGenerator construction
    gen = SinewaveGenerator()
    @test typeof(gen.trigger) == Inpin{Float64}
    @test typeof(gen.handshake) == Outpin{Bool}
    @test !hasfield(typeof(gen), :input)
    @test typeof(gen.output) == Outport{Outpin{Float64}}

    @def_source struct  Mygen{OP, RO} <: AbstractSource
        output::OP = Outport(2)
        readout::RO = t -> [sin(t), cos(t)]
    end
    gen = Mygen()
    @test length(gen.output) == 2

    # Driving FunctionGenerator
    gen = Mygen(readout = t -> t, output=Outport(1))
    trg = Outpin()
    hnd = Inpin{Bool}()
    ip = Inport()
    connect!(gen.output, ip)
    connect!(trg, gen.trigger)
    connect!(gen.handshake, hnd)
    task = launch(gen)
    task2 = @async while true
        all(take!(ip) .=== NaN) && break
    end
    for t in 1. : 10.
        put!(trg, t)
        take!(hnd)
    end
    @test content(ip[1].link.buffer) == collect(1:10)
    @test !istaskdone(task)
    put!(trg, NaN)
    @test istaskdone(task)
    put!(gen.output, [NaN])
    @test istaskdone(task2)

    # Construction of other generators
    sinegen = SinewaveGenerator()
    dampedsinegen = DampedSinewaveGenerator()
    sqauregen = SquarewaveGenerator()
    trigen = TriangularwaveGenerator()
    congen = ConstantGenerator()
    rampgen = RampGenerator()
    stepgen = StepGenerator()
    expgen = ExponentialGenerator()
    dampedexpgen = DampedExponentialGenerator()

    # Mutaton of generators
    @test_throws Exception sinegen.amplitude = 5.
    @test_throws Exception sqauregen.high = 10.

    # Test redefining new source types.
    @test_throws Exception @eval @def_source struct Mygen{RO,OP}
        readout::RO = t -> sin(t)
        output::OP = Outport()
    end

    @test_throws Exception @eval @def_source struct Mygen{RO,OP} <: SomeDummyType
        readout::RO = t -> sin(t)
        output::OP = Outport()
    end

    @info "Done GeneratorsTestSet ..."
end  # testset


text:
# try
#     @eval using Revise
#     # Turn on Revise's automatic-evaluation behavior
#     Revise.async_steal_repl_backend()
# catch err
#     @warn "Could not load Revise."
# end

atreplinit() do repl
    @async try
        sleep(0.1)
        @eval using Revise
        @async Revise.wait_steal_repl_backend()
    catch
        @warn("Could not load Revise.")
    end
end

push!(LOAD_PATH, "$(homedir())/Modules/")

try
    using OhMyREPL
    OhMyREPL.enable_autocomplete_brackets(false)
catch err
    @warn "Could not load OhMyREPL."
end

max_stars_repo_name: awellis/dotfiles

max_issues_repo_name: awellis/dotfiles

max_forks_repo_name: awellis/dotfiles


text:
""" Return the geopotential depth at `i, j, k` at cell centers. """
@inline Dᵃᵃᶜ(i, j, k, grid) = @inbounds -grid.zC[k]

""" Return the geopotential depth at `i, j, k` at cell z-interfaces. """
@inline Dᵃᵃᶠ(i, j, k, grid) = @inbounds -grid.zF[k]

# Dispatch shenanigans
@inline θ_and_sᴬ(i, j, k, θ::AbstractArray, sᴬ::AbstractArray) = @inbounds θ[i, j, k], sᴬ[i, j, k]
@inline θ_and_sᴬ(i, j, k, θ::Number,        sᴬ::AbstractArray) = @inbounds θ, sᴬ[i, j, k]
@inline θ_and_sᴬ(i, j, k, θ::AbstractArray, sᴬ::Number)        = @inbounds θ[i, j, k], sᴬ
@inline θ_and_sᴬ(i, j, k, θ::Number,        sᴬ::Number)        = @inbounds θ, sᴬ

# Basic functionality
@inline ρ′(i, j, k, grid, eos, θ, sᴬ) = @inbounds ρ′(θ_and_sᴬ(i, j, k, θ, sᴬ)..., Dᵃᵃᶜ(i, j, k, grid), eos)

@inline thermal_expansionᶜᶜᶜ(i, j, k, grid, eos, θ, sᴬ) = @inbounds thermal_expansion(θ_and_sᴬ(i, j, k, θ, sᴬ)..., Dᵃᵃᶜ(i, j, k, grid), eos)
@inline thermal_expansionᶠᶜᶜ(i, j, k, grid, eos, θ, sᴬ) = @inbounds thermal_expansion(ℑxᶠᵃᵃ(i, j, k, grid, θ), ℑxᶠᵃᵃ(i, j, k, grid, sᴬ), Dᵃᵃᶜ(i, j, k, grid), eos)
@inline thermal_expansionᶜᶠᶜ(i, j, k, grid, eos, θ, sᴬ) = @inbounds thermal_expansion(ℑyᵃᶠᵃ(i, j, k, grid, θ), ℑyᵃᶠᵃ(i, j, k, grid, sᴬ), Dᵃᵃᶜ(i, j, k, grid), eos)
@inline thermal_expansionᶜᶜᶠ(i, j, k, grid, eos, θ, sᴬ) = @inbounds thermal_expansion(ℑzᵃᵃᶠ(i, j, k, grid, θ), ℑzᵃᵃᶠ(i, j, k, grid, sᴬ), Dᵃᵃᶠ(i, j, k, grid), eos)

@inline haline_contractionᶜᶜᶜ(i, j, k, grid, eos, θ, sᴬ) = @inbounds haline_contraction(θ_and_sᴬ(i, j, k, θ, sᴬ)..., Dᵃᵃᶜ(i, j, k, grid), eos)
@inline haline_contractionᶠᶜᶜ(i, j, k, grid, eos, θ, sᴬ) = @inbounds haline_contraction(ℑxᶠᵃᵃ(i, j, k, grid, θ), ℑxᶠᵃᵃ(i, j, k, grid, sᴬ), Dᵃᵃᶜ(i, j, k, grid), eos)
@inline haline_contractionᶜᶠᶜ(i, j, k, grid, eos, θ, sᴬ) = @inbounds haline_contraction(ℑyᵃᶠᵃ(i, j, k, grid, θ), ℑyᵃᶠᵃ(i, j, k, grid, sᴬ), Dᵃᵃᶜ(i, j, k, grid), eos)
@inline haline_contractionᶜᶜᶠ(i, j, k, grid, eos, θ, sᴬ) = @inbounds haline_contraction(ℑzᵃᵃᶠ(i, j, k, grid, θ), ℑzᵃᵃᶠ(i, j, k, grid, sᴬ), Dᵃᵃᶠ(i, j, k, grid), eos)


text:
# TableHDU & ASCIITableHDU methods

# Table type conversions:
#
# fits_tform_letter(::DataType) -> Char
#     Given Julia array eltype, what FITS table letter code should be used
#     when defining a table column? For example, to store an array of UInt32,
#     use the TFORM letter 'V'.
# CFITSIO_COLTYPE[::Int] -> DataType
#     Given return code from fits_get_eqcoltype(), what type of Julia array
#     should be constructed? For example, for 'V' columns,
#     fits_get_eqcoltype() returns 40. This function maps that code back to
#     UInt32. This also illustrates why we can't simply use the normal CFITSIO
#     datatype mapping: 40 would map to Culong, which is a 64-bit unsigned
#     integer on 64-bit UNIX platforms.
const CFITSIO_COLTYPE = Dict{Int, DataType}()
for (T, tform, code) in ((UInt8,       'B',  11),
                         (Int8,        'S',  12),
                         (Bool,        'L',  14),
                         (String,      'A',  16),
                         (UInt16,      'U',  20),
                         (Int16,       'I',  21),
                         (UInt32,      'V',  40),
                         (Int32,       'J',  41),
                         (Int64,       'K',  81),
                         (Float32,     'E',  42),
                         (Float64,     'D',  82),
                         (Complex64,   'C',  83),
                         (Complex128,  'M', 163))
    @eval fits_tform_char(::Type{$T}) = $tform
    CFITSIO_COLTYPE[code] = T
end
const FITSTableScalar = Union{UInt8, Int8, Bool, UInt16, Int16, UInt32,
                              Int32, Int64, Float32, Float64, Complex64,
                              Complex128}

# Helper function for reading information about a (binary) table column
# Returns: (eltype, rowsize, isvariable)
function fits_get_col_info(f::FITSFile, colnum::Integer)
    eqtypecode, repeat, width = fits_get_eqcoltype(f, colnum)
    isvariable = eqtypecode < 0
    eqtypecode = abs(eqtypecode)

    (eqtypecode == 1) && error("BitArray ('X') columns not yet supported")

    T = CFITSIO_COLTYPE[eqtypecode]

    if isvariable
        if T !== String
            T = Vector{T}
        end
        rowsize = Int[]
    else
        if T === String
            # for strings, cfitsio only considers it to be a vector column if
            # width != repeat, even if tdim is multi-valued.
            if repeat == width
                rowsize = Int[]
            else
                tdim = fits_read_tdim(f, colnum)
                # if tdim isn't multi-valued, ignore it (we know it *is* a
                # vector column). If it is multi-valued, prefer it to repeat
                # width.
                if length(tdim) == 1
                    rowsize = [div(repeat, width)]
                else
                    rowsize = tdim[2:end]
                end
            end
        else
            if repeat == 1
                rowsize = Int[]
            else
                rowsize = fits_read_tdim(f, colnum)
            end
        end
    end

    return T, rowsize, isvariable
end

# Parse max length from tform for a variable column
function var_col_maxlen(tform::String)
    maxlen = -1
    i = search(tform, '(')
    if i > 0
        j = search(tform, ')', i)
        if j > 0
            try maxlen = parseint(tform[i+1:j-1]) end
        end
    end
    return maxlen
end

# Helper function for getting fits tdim shape for given array
fits_tdim(A::Array) = (ndims(A) == 1) ? [1] : [size(A, i) for i=1:ndims(A)-1]
function fits_tdim(A::Array{String})
    n = ndims(A)
    tdim = Vector{Int}(n)
    tdim[1] = maximum(length, A)
    for i=2:n
        tdim[n] = size(A, n-1)
    end
    tdim
end

# Helper function for getting fits tform string for given table type
# and data array.
fits_tform{T}(::Type{TableHDU}, A::Array{T}) = "$(prod(fits_tdim(A)))$(fits_tform_char(T))"

# For string arrays with 2+ dimensions, write tform as rAw. Otherwise,
# cfitsio doesn't recognize that multiple strings should be written to
# a single row, even if TDIM is set to 2+ dimensions.
fits_tform(::Type{TableHDU}, A::Vector{String}) = "$(maximum(length, A))A"
fits_tform(::Type{TableHDU}, A::Array{String}) = "$(prod(fits_tdim(A)))A$(maximum(length, A))"

# variable length columns
fits_tform_v{T<:FITSTableScalar}(::Type{TableHDU}, A::Vector{Vector{T}}) = "1P$(fits_tform_char(T))($(maximum(length(A))))"
fits_tform_v(::Type{TableHDU}, A::Vector{String}) = "1PA($(maximum(length(A))))"
fits_tform_v(::Type{TableHDU}, A::Vector{Vector}) = error("column data must be a leaf type: e.g., Vector{Vector{Int}}, not Vector{Vector{T}}.")
fits_tform_v(::Type{TableHDU}, ::Any) = error("variable length columns only supported for arrays of arrays and arrays of String")
fits_tform_v(::Type{ASCIITableHDU}, ::Any) = error("variable length columns not supported in ASCII tables")

fits_tform(::Type{ASCIITableHDU}, ::Vector{Int16}) = "I7"
fits_tform(::Type{ASCIITableHDU}, ::Vector{Int32}) = "I12"
fits_tform(::Type{ASCIITableHDU}, ::Vector{Float32}) = "E26.17"
fits_tform(::Type{ASCIITableHDU}, ::Vector{Float64}) = "E26.17"
fits_tform(::Type{ASCIITableHDU}, A::Vector{String}) = "A$(maximum(length, A))"
fits_tform(::Type{ASCIITableHDU}, A::Vector) = error("unsupported type: $(eltype(A))")
fits_tform(::Type{ASCIITableHDU}, A::Array) = error("only 1-d arrays supported: dimensions are $(size(A))")

# for passing to fits_create_tbl.
table_type_code(::Type{ASCIITableHDU}) = Cint(1)
table_type_code(::Type{TableHDU}) = Cint(2)

function fits_read_table_header!(hdu::TableHDU, ncols, nrows,
                                 colnames_in, coltforms_in, status)
    # fits_read_btblhdrll (Can pass NULL for return fields not needed.)
    ccall(("ffghbnll", libcfitsio), Cint,
          (Ptr{Void}, Cint,  # Inputs: fitsfile, maxdim
           Ref{Int64}, Ptr{Cint}, Ptr{Ptr{UInt8}},  # nrows, tfields, ttype
           Ptr{Ptr{UInt8}}, Ptr{Ptr{UInt8}}, Ptr{UInt8},  # tform,tunit,extname
           Ptr{Clong}, Ref{Cint}),  # pcount, status
          hdu.fitsfile.ptr, ncols, nrows, C_NULL, colnames_in, coltforms_in,
          C_NULL, C_NULL, C_NULL, status)
end

function fits_read_table_header!(hdu::ASCIITableHDU, ncols, nrows,
                                 colnames_in, coltforms_in, status)
    # fits_read_atblhdrll (Can pass NULL for return fields not needed)
    ccall(("ffghtbll", libcfitsio), Cint,
          (Ptr{Void}, Cint,  # Inputs: fitsfile, maxdim
           Ptr{Int64}, Ref{Int64}, Ptr{Cint},  # rowlen, nrows, tfields
           Ptr{Ptr{UInt8}}, Ptr{Clong}, Ptr{Ptr{UInt8}},  # ttype, tbcol, tform
           Ptr{Ptr{UInt8}}, Ptr{UInt8}, Ref{Cint}),  # tunit, extname, status
          hdu.fitsfile.ptr, ncols, C_NULL, nrows, C_NULL, colnames_in,
          C_NULL, coltforms_in, C_NULL, C_NULL, status)
end

function columns_names_tforms(hdu::Union{ASCIITableHDU,TableHDU})
    fits_assert_open(hdu.fitsfile)
    fits_movabs_hdu(hdu.fitsfile, hdu.ext)
    ncols = fits_get_num_cols(hdu.fitsfile)

    # allocate return arrays for column names & types
    colnames_in  = [Vector{UInt8}(70) for i=1:ncols]
    coltforms_in = [Vector{UInt8}(70) for i=1:ncols]
    nrows = Ref{Int64}()
    status = Ref{Cint}(0)

    fits_read_table_header!(hdu, ncols, nrows, colnames_in, coltforms_in, status)
    fits_assert_ok(status[])

    # parse out results
    colnames = [unsafe_string(pointer(item)) for item in colnames_in]
    coltforms = [unsafe_string(pointer(item)) for item in coltforms_in]
    return colnames, coltforms, ncols, nrows
end

"""
    colnames(hdu) -> Vector{String}

Return the names of columns in a table HDU.
"""
colnames(hdu::Union{ASCIITableHDU,TableHDU}) = columns_names_tforms(hdu)[1]

function show(io::IO, hdu::TableHDU)
    colnames, coltforms, ncols, nrows = columns_names_tforms(hdu)
    # get some more information for all the columns
    coltypes    = Vector{String}(ncols)
    colrowsizes = Vector{String}(ncols)
    showlegend = false
    for i in 1:ncols
        T, rowsize, isvariable = fits_get_col_info(hdu.fitsfile, i)
        coltypes[i] = repr(T)
        colrowsizes[i] = length(rowsize) > 0 ? repr(tuple(rowsize...)) : ""
        if isvariable
            coltypes[i] *= "*"
            showlegend = true
        end
    end

    print(io, """
    File: $(fits_file_name(hdu.fitsfile))
    HDU: $(hdu.ext)$(fits_get_ext_info_string(hdu.fitsfile))
    Type: Table
    Rows: $(nrows[])
    Columns: """)
    show_ascii_table(
        io, ["Name", "Size", "Type", "TFORM"],
        Vector{String}[colnames, colrowsizes, coltypes, coltforms],
        2, 9)
    if showlegend
        print(io, "\n\n         (*) = variable-length column")
    end
end

function show(io::IO, hdu::ASCIITableHDU)
    colnames, coltforms, ncols, nrows = columns_names_tforms(hdu)
    # Get additional info
    coltypes = Vector{String}(ncols)
    for i in 1:ncols
        eqtypecode, repeat, width = fits_get_eqcoltype(hdu.fitsfile, i)
        T = CFITSIO_COLTYPE[eqtypecode]
        coltypes[i] = repr(T)
    end

    print(io, """
    File: $(fits_file_name(hdu.fitsfile))
    HDU: $(hdu.ext)$(fits_get_ext_info_string(hdu.fitsfile))
    Type: ASCIITable
    Rows: $(nrows[])
    Columns: """)
    show_ascii_table(io, ["Name", "Type", "TFORM"],
                     Vector{String}[colnames, coltypes, coltforms], 2, 9)
end

# Write a variable length array column of numbers
# (separate implementation from normal fits_write_col function because
#  we must make separate calls to `fits_write_col` for each row.)
function fits_write_var_col{T}(f::FITSFile, colnum::Integer,
                               data::Vector{Vector{T}})
    for i=1:length(data)
        fits_write_col(f, colnum, i, 1, data[i])
    end
end

function fits_write_var_col(f::FITSFile, colnum::Integer,
                            data::Vector{String})
    for el in data; fits_assert_isascii(el); end
    status = Ref{Cint}(0)
    buffer = Ref{Ptr{UInt8}}()  # holds the address of the current row
    for i=1:length(data)
        buffer[] = pointer(data[i])

        # Note that when writing to a variable ASCII column, the
        # ‘firstelem’ and ‘nelements’ parameter values in the
        # fits_write_col routine are ignored and the number of
        # characters to write is simply determined by the length of
        # the input null-terminated character string.
        ccall((:ffpcls, libcfitsio), Cint,
              (Ptr{Void}, Cint, Int64, Int64, Int64, Ref{Ptr{UInt8}},
               Ref{Cint}),
              f.ptr, colnum, i, 1, length(data[i]), buffer, status)
        fits_assert_ok(status[])
    end
end

# Add a new TableHDU to a FITS object
function write_internal(f::FITS, colnames::Vector{String},
                        coldata::Vector, hdutype, name, ver, header, units,
                        varcols)
    fits_assert_open(f.fitsfile)
    for el in colnames; fits_assert_isascii(el); end

    # move to last HDU; table will be added after the CHDU
    nhdus = Int(fits_get_num_hdus(f.fitsfile))
    (nhdus > 1) && fits_movabs_hdu(f.fitsfile, nhdus)

    ncols = length(colnames)
    ttype = [pointer(name) for name in colnames]

    # determine which columns are requested to be variable-length
    isvarcol = zeros(Bool, ncols)
    if !isa(varcols, Void)
        for i=1:ncols
            isvarcol[i] = (i in varcols) || (colnames[i] in varcols)
        end
    end

    # create an array of tform strings (which we will create pointers to)
    tform_str = Vector{String}(ncols)
    for i in 1:ncols
        if isvarcol[i]
            tform_str[i] = fits_tform_v(hdutype, coldata[i])
        else
            tform_str[i] = fits_tform(hdutype, coldata[i])
        end
    end
    tform = [pointer(s) for s in tform_str]

    # get units
    if isa(units, Void)
        tunit = C_NULL
    else
        tunit = Ptr{UInt8}[(haskey(units, n) ? pointer(units[n]) : C_NULL)
                           for n in colnames]
    end

    # extension name
    name_ptr = (isa(name, Void) ? Ptr{UInt8}(C_NULL) :
                   pointer(name))

    status = Ref{Cint}(0)
    ccall(("ffcrtb", libcfitsio), Cint,
          (Ptr{Void}, Cint, Int64, Cint, Ptr{Ptr{UInt8}}, Ptr{Ptr{UInt8}},
           Ptr{Ptr{UInt8}}, Ptr{UInt8}, Ref{Cint}),
          f.fitsfile.ptr, table_type_code(hdutype), 0, ncols,  # 0 = nrows
          ttype, tform, tunit, name_ptr, status)
    fits_assert_ok(status[])

    # For binary tables, write tdim info
    if hdutype === TableHDU
        for (i, a) in enumerate(coldata)
            isvarcol[i] || fits_write_tdim(f.fitsfile, i, fits_tdim(a))
        end
    end

    if isa(header, FITSHeader)
        fits_write_header(f.fitsfile, header, true)
    end
    if isa(ver, Integer)
        fits_update_key(f.fitsfile, "EXTVER", ver)
    end

    for (i, a) in enumerate(coldata)
        if isvarcol[i]
            fits_write_var_col(f.fitsfile, i, a)
        else
            fits_write_col(f.fitsfile, i, 1, 1, a)
        end
    end
    nothing
end

"""
    write(f::FITS, colnames, coldata; hdutype=TableHDU, name=nothing, ver=nothing, header=nothing, units=nothing, varcols=nothing)

Same as `write(f::FITS, data::Dict; ...)` but providing column names
and column data as a separate arrays. This is useful for specifying
the order of the columns. Column names must be `Vector{String}`
and column data must be a vector of arrays.
"""
function write(f::FITS, colnames::Vector{String}, coldata::Vector;
               units=nothing, header=nothing, hdutype=TableHDU,
               name=nothing, ver=nothing, varcols=nothing)
    if length(colnames) != length(coldata)
        error("length of colnames and length of coldata must match")
    end
    write_internal(f, colnames, coldata, hdutype, name, ver, header,
                   units, varcols)
end

"""
    write(f::FITS, data::Dict; hdutype=TableHDU, name=nothing, ver=nothing, header=nothing, units=nothing, varcols=nothing)

Create a new table extension and write data to it. If the FITS file is
currently empty then a dummy primary array will be created before
appending the table extension to it. `data` should be a dictionary
with String keys (giving the column names) and Array values
(giving data to write to each column). The following types are
supported in binary tables: `UInt8`, `Int8`, `UInt16`, `Int16`,
`UInt32`, `Int32`, `Int64`, `Float32`, `Float64`, `Complex{Float32}`,
`Complex{Float64}`, `String`, `Bool`.

Optional inputs:

- `hdutype`: Type of table extension to create. Can be either
  `TableHDU` (binary table) or `ASCIITableHDU` (ASCII table).
- `name`: Name of extension.
- `ver`: Version of extension (Int).
- `header`: FITSHeader instance to write to new extension.
- `units`: Dictionary mapping column name to units (as a string).
- `varcols`: An array giving the column names or column indicies to
  write as "variable-length columns".

!!! note "Variable length columns"

    Variable length columns allow a column's row entries to contain
    arrays of different lengths. They can potentially save diskspace
    when the rows of a column vary greatly in length, as the column
    data is all written to a contiguous heap area at the end of the
    table. Only column data of type `Vector{String}` or types
    such as `Vector{Vector{UInt8}}` can be written as variable
    length columns. In the second case, ensure that the column data
    type is a *leaf type*. That is, the type cannot be
    `Vector{Vector{T}}`, which would be an array of arrays having
    potentially non-uniform element types (which would not be writable
    as a FITS table column).
"""
function write(f::FITS, data::Dict{String};
               units=nothing, header=nothing, hdutype=TableHDU,
               name=nothing, ver=nothing, varcols=nothing)
    colnames = collect(keys(data))
    coldata = collect(values(data))
    write_internal(f, colnames, coldata, hdutype, name, ver, header,
                   units, varcols)
end

# Read a variable length array column of numbers
# (separate implementation from normal fits_read_col function because
# the length of each vector must be determined for each row.
function fits_read_var_col{T}(f::FITSFile, colnum::Integer,
                              data::Vector{Vector{T}})
    nrows = length(data)
    for i=1:nrows
        repeat, offset = fits_read_descript(f, colnum, i)
        data[i] = Vector{T}(repeat)
        fits_read_col(f, colnum, i, 1, data[i])
    end
end

# Read a variable length array column of strings
# (Must be separate implementation from normal fits_read_col function because
# the length of each string must be determined for each row.)
function fits_read_var_col(f::FITSFile, colnum::Integer, data::Vector{String})
    status = Ref{Cint}(0)
    bufptr = Ref{Ptr{UInt8}}()  # holds a pointer to the current row buffer
    for i=1:length(data)
        repeat, offset = fits_read_descript(f, colnum, i)
        buffer = Vector{UInt8}(repeat)
        bufptr[] = pointer(buffer)
        ccall((:ffgcvs, libcfitsio), Cint,
              (Ptr{Void}, Cint, Int64, Int64, Int64,
               Ptr{UInt8}, Ref{Ptr{UInt8}}, Ptr{Cint}, Ref{Cint}),
              f.ptr, colnum, i, 1, repeat, " ", bufptr, C_NULL, status)
        fits_assert_ok(status[])

        # Create string out of the buffer, terminating at null characters
        zeropos = search(buffer, 0x00)
        data[i] = (zeropos >= 1) ? String(buffer[1:(zeropos-1)]) :
                                   String(buffer)
    end
end

# Read a table column
function read(hdu::ASCIITableHDU, colname::String)
    fits_assert_open(hdu.fitsfile)
    fits_movabs_hdu(hdu.fitsfile, hdu.ext)

    nrows = fits_get_num_rows(hdu.fitsfile)
    colnum = fits_get_colnum(hdu.fitsfile, colname)

    typecode, repcnt, width = fits_get_eqcoltype(hdu.fitsfile, colnum)
    T = CFITSIO_COLTYPE[typecode]

    result = Vector{T}(nrows)
    fits_read_col(hdu.fitsfile, colnum, 1, 1, result)

    return result
end

"""
    read(hdu, colname)

Read a column as an array from the given table HDU.

The column name may contain wild card characters (`*`, `?`, or
`#`). The `*` wild card character matches any sequence of
characters (including zero characters) and the `?` character
matches any single character. The `#` wildcard will match any
consecutive string of decimal digits (0-9). The string must match a
unique column.
"""
function read(hdu::TableHDU, colname::String)
    fits_assert_open(hdu.fitsfile)
    fits_movabs_hdu(hdu.fitsfile, hdu.ext)

    nrows = fits_get_num_rows(hdu.fitsfile)
    colnum = fits_get_colnum(hdu.fitsfile, colname)

    T, rowsize, isvariable = fits_get_col_info(hdu.fitsfile, colnum)

    result = Array{T}(rowsize..., nrows)

    if isvariable
        fits_read_var_col(hdu.fitsfile, colnum, result)
    else
        fits_read_col(hdu.fitsfile, colnum, 1, 1, result)
    end

    return result
end


text:
export boundproject

function boundproject(x,UB,LB)
    x[x .< LB] = LB
    x[x .> UB] = UB
    return x
end


text:
import Base.Iterators:product

function read_puzzle(filename)

    open(filename) do f
         return parse.(Int, readlines(f))
    end

end

function ex1( puzzle )
    for item in product(puzzle,puzzle)
        if length(unique(item)) == 2
            sum(item) == 2020  && return prod(item)
        end
    end
end

function ex2( puzzle )
    for item in product(puzzle, puzzle, puzzle)
        if length(unique(item)) == 3
            sum(item) == 2020 && return prod(item)
        end
    end
end

puzzle = read_puzzle("input01.txt")
println(ex1(puzzle))
println(ex2(puzzle))

max_stars_repo_name: pnavaro/AdventOfCode

max_issues_repo_name: pnavaro/AdventOfCode

max_forks_repo_name: pnavaro/AdventOfCode


text:
#=
$ mkdir ~/tmp
$ cd ~/tmp
$ git clone https://github.com/avik-pal/Flux.jl.git
$ cd Flux.jl
$ git checkout -b depthwiseconv origin depthwiseconv
replace ~/.julia/packages/Flux/UHjNa with all of them under ~/tmp/Flux.jl
=#

using Flux
using Flux:Chain, DepthwiseConv
using Flux:@treelike

relu6(x) = min(max(zero(x),x), eltype(x)(6))

struct ExpandedConv
    layers::Chain
    stride::Int
end

function ExpandedConv(expand::Int, ch::Pair{<:Integer,<:Integer}; stride=1)
    inch=ch[1]
    outch=ch[2]
    expandedch = inch * expand
    if expand != 1
        chain = Chain(Conv((1,1), inch=>expandedch),
                      BatchNorm(expandedch, relu6),
                      DepthwiseConv((3,3),expandedch,relu6,stride=stride,pad=1),
                      BatchNorm(expandedch, relu6),
                      Conv((1,1),expandedch=>outch),
                      BatchNorm(outch))
    else
        chain = Chain(DepthwiseConv((3,3),expandedch,relu6,stride=stride,pad=1),
                      BatchNorm(expandedch, relu6),
                      Conv((1,1),expandedch=>outch),
                      BatchNorm(outch))
    end
    ExpandedConv(chain, stride)
end

@treelike ExpandedConv

function (ec::ExpandedConv)(x)
    h=ec.layers(x)
    if size(h)==size(x)
        h + x
    else
        h
    end
end

#= test
out=ExpandedConv(3,5=>5)(rand(23,30,5,2))
println(size(out))
=#

struct MobileNetv2
    layers::Chain
end

mv2() = Chain(Conv((3,3),3=>32,stride=2,pad=1),
                    BatchNorm(32,relu6),
                    ExpandedConv(1,32=>16),
                    ExpandedConv(6,16=>24,stride=2),
                    ExpandedConv(6,24=>24),
                    ExpandedConv(6,24=>32,stride=2),
                    ExpandedConv(6,32=>32),
                    ExpandedConv(6,32=>32),
                    ExpandedConv(6,32=>64,stride=2),
                    ExpandedConv(6,64=>64),
                    ExpandedConv(6,64=>64),
                    ExpandedConv(6,64=>64),
                    ExpandedConv(6,64=>96),
                    ExpandedConv(6,96=>96),
                    ExpandedConv(6,96=>96),
                    ExpandedConv(6,96=>160,stride=2),
                    ExpandedConv(6,160=>160),
                    ExpandedConv(6,160=>160),
                    ExpandedConv(6,160=>320),
                    Conv((1,1),320=>120),
                    BatchNorm(120,relu6),
                    MeanPool((7,7)),
                    x -> reshape(x, :, size(x, 4)),
                    Dense(120,101),
                    softmax
                    )

MobileNetv2() = MobileNetv2(mv2())

@treelike MobileNetv2

(mv2::MobileNetv2)(x) = mv2.layers(x)

#=
using Flux
using CuArrays
model = MobileNetv2()
@show size(model(rand(224,224,3,2)))
model = model |> gpu
@show size(model(rand(224,224,3,2)|> gpu))
=#


text:
struct DiagonalStateWrapper{T} <: FiniteBasisState where {T<:DoubleState}
    parent::T
end

# accessor
Base.parent(σ::DiagonalStateWrapper) = σ.parent

# Property Accessors
spacedimension(s::DiagonalStateWrapper) = spacedimension(row(s.parent))
nsites(s::DiagonalStateWrapper) = nsites(row(s.parent))
toint(s::DiagonalStateWrapper) = toint(row(s.parent))
index(s::DiagonalStateWrapper) = index(row(s.parent))
index_to_int(s::DiagonalStateWrapper, id) = index_to_int(row(s.parent), id)
flipped(a::DiagonalStateWrapper, b::DiagonalStateWrapper) =
    flipped(a.parent, b.parent)
@inline eltype(state::DiagonalStateWrapper) = eltype(state.parent)
@inline config(state::DiagonalStateWrapper) = config(state.parent)

zero!(s::DiagonalStateWrapper) = zero!(s.parent)
base_state(s::DiagonalStateWrapper) = s.parent

# Operations
function flipat!(rng::AbstractRNG, s::DiagonalStateWrapper, i)
    old, new = flipat!(rng, row(s.parent), i)
    setat!(col(s.parent), i, new)
    return (old, new)
end

function setat!(s::DiagonalStateWrapper, i, val)
    setat!(row(s.parent), i, val)
    setat!(col(s.parent), i, val)
    return s
end

set_index!(v::DiagonalStateWrapper, i::Integer) = set!(v, index_to_int(v, i))
function set!(s::DiagonalStateWrapper, i)
    set!(row(s.parent), i)
    set!(col(s.parent), i)
    return s
end

function add!(s::DiagonalStateWrapper, i)
    add!(row(s.parent), i)
    add!(col(s.parent), i)
    return s
end

function rand!(rng::AbstractRNG, s::DiagonalStateWrapper)
    rand!(rng, row(s.parent))
    set!(col(s.parent), toint(row(s.parent)))
    return s
end

# -------------- Base.show extension for nice printing -------------- #
Base.show(io::IO, mm::MIME"text/plain", bs::DiagonalStateWrapper) =
    print(io, "Diagonal State: $(bs.parent)\n")

Base.show(io::IO, bs::DiagonalStateWrapper) = print(io, "DiagSt: ", bs.parent)


text:
# Autogenerated wrapper script for x264_jll for armv7l-linux-musleabihf
export libx264, x264

JLLWrappers.@generate_wrapper_header("x264")
JLLWrappers.@declare_library_product(libx264, "libx264.so.163")
JLLWrappers.@declare_executable_product(x264)
function __init__()
    JLLWrappers.@generate_init_header()
    JLLWrappers.@init_library_product(
        libx264,
        "lib/libx264.so",
        RTLD_LAZY | RTLD_DEEPBIND,
    )

    JLLWrappers.@init_executable_product(
        x264,
        "bin/x264",
    )

    JLLWrappers.@generate_init_footer()
end  # __init__()


text:
# vary transition prob, mcts_depth, and number of nodes in network
cond1 = make_condition_dict(num_nets=5,mcts_d=[10],n_bnds=[8,35],seed=5,repeats=25,steps=25,role=:trusted)
cond2 = make_condition_dict(num_nets=3,mcts_d=[1],n_bnds=[8,35],seed=4,repeats=25,steps=25)
cond3 = make_condition_dict(num_nets=2,mcts_d=[2],n_bnds=[8,35],seed=3,repeats=25,steps=25)
cond4 = make_condition_dict(num_nets=3,mcts_d=[3],n_bnds=[8,35],seed=2,repeats=25,steps=25)
cond5 = make_condition_dict(num_nets=2,mcts_d=[4,7],n_bnds=[8,35],seed=1,repeats=25,steps=25)

exp_dict = Dict(:name=>"mturk_fast", :conditions=>Dict(1=>cond1, 2=>cond2, 3=> cond3,4=> cond4,5=> cond5),
                :xQ=>Dict(:inpts=>[:tprob,:N],:epocs=>100,:cmp=>[2,3,4,5],:nn_loc=>"nn_logs"))

fldr = "logs"
for x in keys(exp_dict[:conditions])
    fname = make_useful_fname(exp_dict,x)
    exp_dict[:conditions][x][:fname] = fname
    exp_dict[:conditions][x][:fldr] = fldr
end

max_stars_repo_name: COHRINT/FaMSeC

max_issues_repo_name: COHRINT/FaMSeC

max_forks_repo_name: COHRINT/FaMSeC


text:
using JSON

abstract type AbstractRecorder end

"""
ExperimentRecorder

    A structure to record and write experiment status updates to file.

    Write to JSON file with format:

        "train": {
          [message],
          ...
        },
        "test": {
          [message]
        }

    # Example
    ```julia-repl
    r = ExperimentRecorder([:episode, :score, :steps, :epsilon], "/Users/colinswaney/Desktop/record.txt")
    message = Dict(:episode => 1, :score => 45, :steps => 45, :epsilon => 1.0)
    stamp!(r, message, :train)
    message = Dict(:episode => 2, :score => 24, :steps => 24, :epsilon => 1.0)
    stamp!(r, message, :train)
    message = Dict(:episode => 2, :score => 45)  # this will fail
    stamp!(r, message, :test)
    save(r)
    ```
"""
mutable struct ExperimentRecorder <: AbstractRecorder
    history
    items
    url
end

ExperimentRecorder(items, url) = ExperimentRecorder((train=[], test=[]), items, url)
ExperimentRecorder(url) = ExperimentRecorder([:episode, :score, :steps, :epsilon], url)

function stamp!(r::ExperimentRecorder, message, message_type::Symbol)
    missing_items = setdiff(r.items, collect(keys(message)))
    length(missing_items) > 0 && error("missing items in message: $missing_items")
    push!(r.history[message_type], message)
end

function save(r::ExperimentRecorder)
    open(r.url, "w") do f
        write(f, json(r.history, 2))
    end
end


text:
using WordCloud
words = "天地玄黄宇宙洪荒日月盈昃辰宿列张寒来暑往秋收冬藏闰余成岁律吕调阳云腾致雨露结为霜金生丽水玉出昆冈剑号巨阙珠称夜光果珍李柰菜重芥姜海咸河淡鳞潜羽翔龙师火帝鸟官人皇始制文字乃服衣裳推位让国有虞陶唐吊民伐罪周发殷汤坐朝问道垂拱平章"
words = [string(c) for c in words]
weights = rand(length(words)) .^ 2 .* 100 .+ 30
wc = wordcloud(words, weights)
generate!(wc)


text:
# This file was generated, do not modify it. # hide
Random.seed!(1515)

KMeans = @load KMeans pkg=Clustering
SPCA2 = Pipeline(
    Standardizer(),
    PCA(),
    KMeans(k=3)
)

spca2 = machine(SPCA2, X)
fit!(spca2)

assignments = collect(values(report(spca2).report_given_machine))[3].assignments
mask1 = assignments .== 1
mask2 = assignments .== 2
mask3 = assignments .== 3;

max_issues_repo_name: alan-turing-institute/MLJTutorials

max_forks_repo_name: alan-turing-institute/MLJTutorials


text:
module DevTools

import Registrator, JuliaFormatter, RegistryTools
using PkgTemplates, LibGit2, Pkg, SnoopCompile, Base64
using Base: UUID
import GitHub

export register,
    LYCEUM_PKGTEMPLATE,
    pushrepo,
    namedgenerate,
    incrementversion!,
    pkgdir,
    parsetomls,
    cpinto,
    snooppkg,
    with_sandbox_env,
    register

include("git.jl")
include("misc.jl")
include("packaging.jl")
include("Compat.jl")

end # module


text:
using Documenter, EasyConfig

makedocs(;
    modules=[EasyConfig],
    format=Documenter.HTML(),
    pages=[
        "Home" => "index.md",
    ],
    repo="https://github.com/joshday/EasyConfig.jl/blob/{commit}{path}#L{line}",
    sitename="EasyConfig.jl",
    authors="joshday <emailjoshday@gmail.com>",
    assets=String[],
)

deploydocs(;
    repo="github.com/joshday/EasyConfig.jl",
)


text:
module GaussWrapper

using JavaCall

const BaseRuleFactory = @jimport org.hipparchus.analysis.integration.gauss.BaseRuleFactory
const Class = @jimport java.lang.Class
const GaussIntegrator = @jimport org.hipparchus.analysis.integration.gauss.GaussIntegrator
const GaussIntegratorFactory = @jimport org.hipparchus.analysis.integration.gauss.GaussIntegratorFactory
const HermiteRuleFactory = @jimport org.hipparchus.analysis.integration.gauss.HermiteRuleFactory
const JString = @jimport java.lang.String
const LaguerreRuleFactory = @jimport org.hipparchus.analysis.integration.gauss.LaguerreRuleFactory
const LegendreHighPrecisionRuleFactory = @jimport org.hipparchus.analysis.integration.gauss.LegendreHighPrecisionRuleFactory
const LegendreRuleFactory = @jimport org.hipparchus.analysis.integration.gauss.LegendreRuleFactory
const MathContext = @jimport java.math.MathContext
const Object = @jimport java.lang.Object
const Pair = @jimport org.hipparchus.util.Pair
const SymmetricGaussIntegrator = @jimport org.hipparchus.analysis.integration.gauss.SymmetricGaussIntegrator
const UnivariateFunction = @jimport org.hipparchus.analysis.UnivariateFunction

export BaseRuleFactory
export GaussIntegrator
export GaussIntegratorFactory
export HermiteRuleFactory
export LaguerreRuleFactory
export LegendreHighPrecisionRuleFactory
export LegendreRuleFactory
export SymmetricGaussIntegrator
export equals
export get_class
export get_number_of_points
export get_point
export get_rule
export get_weight
export hash_code
export hermite
export integrate
export laguerre
export legendre
export legendre_high_precision
export notify
export notify_all
export to_string
export wait

include("base_rule_factory.jl")
include("gauss_integrator.jl")
include("gauss_integrator_factory.jl")
include("hermite_rule_factory.jl")
include("laguerre_rule_factory.jl")
include("legendre_high_precision_rule_factory.jl")
include("legendre_rule_factory.jl")
include("symmetric_gauss_integrator.jl")

end


text:
abstract AbstractMixer

type LinearMixer <: AbstractMixer
    c::Float64  # mixing weight
end

LinearMixer(;c=0.25) = LinearMixer(c)

Base.call(mixer::LinearMixer, rho_in::AbstractArray, rho_out::AbstractArray) = (1 - mixer.c)*rho_in + mixer.c*rho_out

type PulayMixer <: AbstractMixer
    n::Int  # number of densities to mix
    beta::Float64  # damping coefficient
    grid  # integration grid
    rho_in::Array{AbstractArray}
    R::Array{AbstractArray}  # residuals
end

PulayMixer(;n=3, beta=0.25, grid=nothing) = PulayMixer(n,beta,grid,[],[])

function Base.call(mixer::PulayMixer, rho_in::AbstractArray, rho_out::AbstractArray)
    push!(mixer.rho_in, rho_in)
    push!(mixer.R, rho_out - rho_in)

    if length(mixer.rho_in) > mixer.n
        shift!(mixer.rho_in)
        shift!(mixer.R)
    end

    n = length(mixer.rho_in)

    B = Array{Float64}(n+1,n+1)
    for i in 1:n
        for j in i:n
            B[i,j] = integrate(mixer.grid, mixer.R[i].*mixer.R[j])
        end
    end

    B[:,end] = -1
    B[end,end] = 0
    B = Symmetric(B)

    c = zeros(1,n+1)
    c[end] = -1

    a = squeeze(c/B,1)  # solve

    rho = zeros(rho_in)
    for i in 1:n
        rho += a[i].*(mixer.rho_in[i] + mixer.beta.*mixer.R[i])
    end

    rho
end

max_stars_repo_name: johncsnyder/Element


text:
# Monte Carlo Simulation of Berry 1994

function Berry1994(R, repNum;
                     βo = 5.00,
                     βx = 2.00,
                     α  = 1.00,
                     γo = 1.00,
                     γx = 0.50,
                     γw = 0.25,
                     σc = 0.25,
                     σw = 0.25,
                     σd = 3.00)

#Let's set up the remaining parameters
J  = 2

# Let's randomly draw observable and unobservable characteristics that affect
# demand: xₘⱼ and ξₘⱼ
xₘⱼ =  randn(R, J, repNum);
ξₘⱼ =  randn(R, J, repNum);

# Let's randomly draw observable and unobservable characteristics that affect
# marginal cost: wₘⱼ and ωₘⱼ
wₘⱼ =  randn(R, J, repNum);
ωₘⱼ =  randn(R, J, repNum);

# Let's compute the marginal cost (cₘⱼ) for each market m and product/firm j
# as Berry does:
cₘⱼ = exp.( γo.*ones(R,J,repNum) .+ γx.*xₘⱼ + σc.*ξₘⱼ .+ γw.*wₘⱼ .+ σw.*ωₘⱼ );

# Let's compute the equilibrium prices (pₘⱼ) and market share (sₘⱼ)
function eq_p_sh( xₘ, ξₘ, cₘ, sₘ)
    p = cₘ .+ ( α.*(1.0.-sₘ)).^(-1);
    v = βo .+ βx.*xₘ .+ σd.*ξₘ .- α.*p;
    F = (exp.(v))./(1.0 .+ sum(exp.(v)));
    return F
end

s₀ = [0.5, 0.5];
sₘⱼ = similar(xₘⱼ);
pₘⱼ = similar(xₘⱼ);

for k = 1:repNum
    for r = 1:R
        sol = nlsolve(y -> eq_p_sh( xₘⱼ[r,:,k], ξₘⱼ[r,:,k], cₘⱼ[r,:,k], y)-y, s₀)

        s_eq = sol.zero

        if ( sol.f_converged == false || sum(s_eq .< 0)>0 || sum(s_eq)>1 )
            s_eq = [NaN , NaN];
        end

        p_eq = cₘⱼ[r,:,k] .+ (α.*(1.0 .- s_eq) ).^(-1);

        sₘⱼ[r,:,k] = s_eq;
        pₘⱼ[r,:,k] = p_eq;

    end
end

@assert sum(isnan.(sₘⱼ)) == 0

# Let's calculate the mean utility of product j: (δₘⱼ)
δₘⱼ = similar(xₘⱼ);
for k = 1:repNum
for r=1:R
    δₘⱼ[r,1,k] = log(sₘⱼ[r,1,k])-log(1-sₘⱼ[r,1,k]-sₘⱼ[r,2,k]);
    δₘⱼ[r,2,k] = log(sₘⱼ[r,2,k])-log(1-sₘⱼ[r,1,k]-sₘⱼ[r,2,k]);
end
end

# Let's compute the OLS estimates
θhatᵒˡˢ = zeros(3,repNum);

for k = 1:repNum
    y = reshape(δₘⱼ[:,:,k],R*J,1);
    X = [ones(R*J,1)  reshape(xₘⱼ[:,:,k],R*J,1) reshape(pₘⱼ[:,:,k],R*J,1) ];
    θhatᵒˡˢ[:,k] = inv(X'*X)*(X'*y);
end

# Taking the average and the standard deviation of the simulations
m_θhatᵒˡˢ  = mean(θhatᵒˡˢ,dims=2);
sd_θhatᵒˡˢ = std(θhatᵒˡˢ,dims=2);

# Let's compute the IV estimates
θhatⁱᵛ = zeros(3,repNum);

aux_xₘⱼ = similar(xₘⱼ);
aux_xₘⱼ[:,1,:] = xₘⱼ[:,2,:];
aux_xₘⱼ[:,2,:] = xₘⱼ[:,1,:];

for k = 1:repNum
    y = reshape(δₘⱼ[:,:,k],R*J,1);
    X = [ones(R*J,1)  reshape(xₘⱼ[:,:,k],R*J,1)  reshape(pₘⱼ[:,:,k],R*J,1) ];
    Z = [ones(R*J,1)  reshape(xₘⱼ[:,:,k],R*J,1)  reshape(wₘⱼ[:,:,k],R*J,1)  reshape(aux_xₘⱼ[:,:,k],R*J,1) ];
    Xhat = Z*inv(Z'*Z)*(Z'*X);
    θhatⁱᵛ[:,k] = inv(Xhat'*Xhat)*(Xhat'*y);
end

# Taking the average and the standard deviation of the simulations
m_θhatⁱᵛ  = mean(θhatⁱᵛ, dims=2);
sd_θhatⁱᵛ = std(θhatⁱᵛ, dims=2);

# Organizing the outputs
θ = [βo ; βx ; -α];
comparison = (θ = θ, m_θhatᵒˡˢ= m_θhatᵒˡˢ,  sd_θhatᵒˡˢ=sd_θhatᵒˡˢ,  m_θhatⁱᵛ= m_θhatⁱᵛ,  sd_θhatⁱᵛ= sd_θhatⁱᵛ);
data = ( xₘⱼ=xₘⱼ, ξₘⱼ=ξₘⱼ, wₘⱼ=wₘⱼ, ωₘⱼ=ωₘⱼ, cₘⱼ=cₘⱼ, sₘⱼ=sₘⱼ, pₘⱼ=pₘⱼ, δₘⱼ=δₘⱼ );
params = (βo = βo, βx = βx, α  = α, γo = γo, γx = γx, γw = γw, σc = σc, σw = σw, σd = σd);
output = (comparison = comparison, data = data, params=params);

return output

end


text:
using PGFPlots
using Test

@assert success(`lualatex -v`)
using NBInclude
@nbinclude(joinpath(dirname(@__FILE__), "..", "doc", "PGFPlots.ipynb"))


text:
using Random
Random.seed!(1234)

using PearsonHash
using Test

@testset "PearsonHash.jl" begin
    @test hash8("Test") == hash8("Test")
    @test hash8("Test") == hashn(UInt8, "Test")
    @test typeof(hashn(UInt32, "Test")) == UInt32
    @test hashn("Test") != hashn("Test"; seed = 0xFF)
end

max_stars_repo_name: darsnack/PearsonHash

max_issues_repo_name: darsnack/PearsonHash

max_forks_repo_name: darsnack/PearsonHash


text:
using Boltzmann

require("data.jl")

function load_fitted()
    artists = load(ARTIST_FILE)["artists"]
    model = load(MODEL_FILE)["model"]
    return model, artists
end

function most_different(w, artists)
    artists[findin(w, minimum(w))], artists[findin(w, maximum(w))]
end

function artname2id(artists)
    Dict([(a, i) for (i, a) in enumerate(artists)])
end

function mk_axis(w, artists)
    wi = [(w[i], i) for i=1:length(w)]
    idxs = Int32[i for (w, i) in sort(wi)]
    return artists[idxs]
end

function basis_artnames(model, artists)
    W = components(model)
    basis = convert(Array{UTF8String, 2}, zeros(UTF8String, size(W)))
    for j=1:size(W, 2)
        basis[:, j] = mk_axis(W[:, j], artists)
    end
    return basis
end

# Usage: recommend(model, artists, ["my favorite artist", "another good guy", ...])
function recommend(model, artists, pref_artists)
    n_vis = size(components(model), 1)
    pref_vec = zeros(Float64, n_vis)
    name2id = artname2id(artists)
    for art in pref_artists
        pref_vec[name2id[art]] = 1.
    end
    rec_vec = generate(model, pref_vec)
    return artists[findin(rec_vec, 1.)]
end

max_stars_repo_name: dfdx/lastfm-rbm

max_issues_repo_name: dfdx/lastfm-rbm

max_forks_repo_name: dfdx/lastfm-rbm