{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "https://jisifu.envs.net/lib/equations.schema.json",
  "title": "Equation Visualizer Schema",
  "description": "Schema for lib/equations.json — defines the interactive equation calculators consumed by the EquationVisualizer frontend",
  "type": "object",
  "required": ["equations"],
  "additionalProperties": false,
  "properties": {
    "equations": {
      "type": "array",
      "description": "Array of interactive equation calculators",
      "minItems": 1,
      "uniqueItems": true,
      "items": {
        "$ref": "#/definitions/equation"
      }
    }
  },
  "definitions": {
    "equation": {
      "type": "object",
      "description": "An interactive equation calculator with inputs and computed outputs",
      "required": ["id", "name", "description", "formula", "variables", "outputs"],
      "additionalProperties": false,
      "properties": {
        "id": {
          "type": "string",
          "description": "Unique snake_case identifier for the equation",
          "pattern": "^[a-z][a-z0-9_]*$",
          "examples": ["ohms_law", "shannon_entropy", "loan_payment"]
        },
        "name": {
          "type": "string",
          "description": "Human-readable display name with category suffix",
          "minLength": 1,
          "examples": ["Ohm's Law (Physics)", "Shannon Entropy (Info Theory)"]
        },
        "description": {
          "type": "string",
          "description": "One-line description of what the equation computes",
          "minLength": 1
        },
        "formula": {
          "type": "string",
          "description": "Human-readable mathematical notation (Unicode symbols, not JS)",
          "minLength": 1,
          "examples": ["V = I × R", "H = -p · log₂(p) - (1-p) · log₂(1-p)"]
        },
        "formula_js_code": {
          "type": "string",
          "description": "Optional JS function definition for complex calculations (eval context); empty string if unused",
          "default": ""
        },
        "variables": {
          "type": "array",
          "description": "Slider-adjustable input parameters",
          "minItems": 1,
          "items": {
            "$ref": "#/definitions/variable"
          }
        },
        "outputs": {
          "type": "array",
          "description": "Computed results derived from variable values",
          "minItems": 1,
          "items": {
            "$ref": "#/definitions/output"
          }
        }
      }
    },
    "variable": {
      "type": "object",
      "description": "An input parameter rendered as a labelled range slider",
      "required": ["id", "name", "symbol", "description", "min", "max", "step", "default", "unit"],
      "additionalProperties": false,
      "properties": {
        "id": {
          "type": "string",
          "description": "Unique camelCase identifier referenced in output formulas",
          "pattern": "^[a-zA-Z][a-zA-Z0-9]*$",
          "examples": ["p", "reward", "risk", "sourceVoltage", "pendulumGravity"]
        },
        "name": {
          "type": "string",
          "description": "Display label for the slider",
          "minLength": 1
        },
        "symbol": {
          "type": "string",
          "description": "Mathematical symbol for display (Unicode or ASCII)",
          "minLength": 1,
          "examples": ["I", "R", "v₀", "S/N"]
        },
        "description": {
          "type": "string",
          "description": "Tooltip / help text explaining what the parameter represents",
          "minLength": 1
        },
        "min": {
          "type": "number",
          "description": "Minimum slider value (inclusive)"
        },
        "max": {
          "type": "number",
          "description": "Maximum slider value (inclusive)"
        },
        "step": {
          "type": "number",
          "description": "Stepping increment — should be >= 0 and typically a clean divisor of (max - min)",
          "exclusiveMinimum": 0
        },
        "default": {
          "type": "number",
          "description": "Default / initial slider value; must be between min and max (inclusive)"
        },
        "unit": {
          "type": "string",
          "description": "Unit suffix displayed after the slider value (use empty string for dimensionless)",
          "examples": ["V", "$", "Hz", "m/s²", "kg", "%", ""]
        },
        "calculated": {
          "type": "boolean",
          "description": "If true, this variable is derived from other variables and skipped in the slider UI",
          "default": false
        },
        "calculatedFrom": {
          "type": "string",
          "description": "JS expression for derived variables, referencing other variable IDs; only valid when calculated is true"
        }
      },
      "allOf": [
        {
          "description": "default must be within [min, max] range",
          "$comment": "This is a structural constraint; runtime validation is done in test.php",
          "if": {
            "required": ["default", "min", "max"]
          },
          "then": {
            "not": {
              "properties": {
                "default": {
                  "anyOf": [
                    { "description": "default < min", "$comment": "comparison logic: if default < min then fail" },
                    { "description": "default > max", "$comment": "comparison logic: if default > max then fail" }
                  ]
                }
              }
            }
          }
        },
        {
          "description": "calculatedFrom must be present when calculated is true",
          "if": { "required": ["calculated"], "properties": { "calculated": { "const": true } } },
          "then": { "required": ["calculatedFrom"] }
        },
        {
          "description": "calculatedFrom must be absent when calculated is false or absent",
          "if": {
            "anyOf": [
              { "not": { "required": ["calculated"] } },
              { "required": ["calculated"], "properties": { "calculated": { "not": { "const": true } } } }
            ]
          },
          "then": { "not": { "required": ["calculatedFrom"] } }
        }
      ]
    },
    "output": {
      "type": "object",
      "description": "A computed value shown in the results panel",
      "required": ["id", "name", "formula", "unit", "description"],
      "additionalProperties": false,
      "properties": {
        "id": {
          "type": "string",
          "description": "Unique lowercase identifier for this output (snake_case or camelCase)",
          "pattern": "^[a-z][a-zA-Z0-9_]*$",
          "examples": ["voltage", "entropy", "snr_db", "profitLoss", "capVoltage"]
        },
        "name": {
          "type": "string",
          "description": "Display label for the result (e.g., 'Voltage', 'Channel Capacity')",
          "minLength": 1
        },
        "formula": {
          "type": "string",
          "description": "JavaScript expression computing the output from variable IDs and Math.*. Evaluated by new Function()",
          "minLength": 1,
          "examples": ["current * resistance", "mass * velocity", "sourceFreq * (soundSpeed + observerSpeed) / soundSpeed"]
        },
        "unit": {
          "type": "string",
          "description": "Unit suffix displayed after the computed value (use empty string for dimensionless)",
          "examples": ["V", "$", "bits", "dB", "m", ""]
        },
        "description": {
          "type": "string",
          "description": "Help text explaining what this output represents"
        }
      }
    }
  }
}
