{"id":"percent-compute","version":"1.0.0","description":"Exact percentage of, ratio, change, increase, decrease and reverse percentage, rounded once at a chosen scale.","supported_operations":["what is 20 percent of 150","percentage increase from 20 to 25","increase a number by 20 percent","price after 15% discount","reverse percentage original price before increase","what percent is 3 of 8","percent change between two amounts","take a percentage off an amount"],"unsupported_operations":["compound interest or growth over several periods","stacked or sequential discounts","tax jurisdiction rules or rate lookups","floating point or scientific notation input"],"semantics":["Decimal strings match ^-?(0|[1-9][0-9]*)(\\.[0-9]+)?$: no plus sign, exponent, leading zeros, leading or trailing point, whitespace or separators. A JSON number where a decimal string is required is invalid_input (wrong type).","A decimal string longer than 62 UTF-16 code units -> limit_exceeded, checked before the grammar. Otherwise a grammar failure, more than 60 digits in total, or more than 40 fraction digits -> invalid_input.","Zero has no sign: \"-0\", \"-0.00\" and any all-zero value are zero and are emitted as \"0\" (canonical) or \"0.00\" (fixed scale), never \"-0\".","Required in every call: mode, scale, rounding. scale is a JSON integer 0..40; rounding is one of the ten modes; mode is one of of, ratio, change, increase, decrease, reverse (any other string -> invalid_input).","Fields by mode: of needs percent and value; ratio needs part and whole; change needs from and to; increase, decrease and reverse need value and percent. A field required by the mode but absent -> invalid_input. A field that belongs to a different mode (for example whole in mode of) -> invalid_input, even if it is a valid decimal. An unknown key or a \"__proto__\" key -> invalid_input.","Formulas on the exact rationals (100 is exact): of = value * percent / 100. ratio = part / whole * 100. change = (to - from) / |from| * 100. increase = value * (100 + percent) / 100. decrease = value * (100 - percent) / 100. reverse = value * 100 / (100 + percent), the original amount before a percent increase produced value.","change divides by the absolute value of from, so a rise is positive whatever the sign of from: from -10 to -5 is +50, from -10 to 5 is +150, from 10 to -10 is -200. from = 0 -> not_computable with details.reason \"division_by_zero\". ratio with whole = 0 -> not_computable \"division_by_zero\", even when part is 0. reverse with percent = -100 -> not_computable \"division_by_zero\", even when value is 0.","percent may be negative or above 100: decrease by 150% of 100 is -50; increase by -100% is 0; reverse of 10 at -200% is -10. increase and decrease are not inverses: reverse (not decrease) undoes an increase, e.g. reverse of 120 at 20% is 100 while decrease of 120 by 20% is 96.","The result is computed exactly and rounded ONCE to scale digits with the given mode; nothing is rounded mid-calculation. ratio 1/3 at scale 2 is \"33.33\" (exact false); ratio 1/4 at scale 0 with half_even is \"25\" exactly. Ties follow the mode: of 0.5% of 1 at scale 2 is 0.005, which half_even gives as \"0.00\" and half_up as \"0.01\".","Result formatting: A result rounded to scale s is written with exactly s fraction digits (no fraction and no point when s is 0) (always fixed scale here). Zero is \"0\" or \"0.00\", never \"-0\", including a negative result that rounds to zero. exact is true when the rounded result equals the true value.","rounding \"unnecessary\" with an inexact result -> not_computable with details.reason \"rounding_necessary\"; with an exact result it succeeds. Domain errors are checked after all field validation; the division_by_zero check precedes the rounding_necessary check.","Rounding modes act on the exact rational result and are: half_even (ties to the even neighbour), half_up (ties away from zero), half_down (ties toward zero), half_ceiling (ties toward +infinity), half_floor (ties toward -infinity), up (away from zero), down (toward zero), ceiling (toward +infinity), floor (toward -infinity), unnecessary (any inexact result -> not_computable with details.reason \"rounding_necessary\").","Validation order (each vector carries a single fault): input not an object; unknown field; missing required field; field types, ranges and decimal-string limits; scale/rounding pairing; then domain errors (not_computable) last."],"limits":{"max_decimal_chars":62,"max_decimal_digits":60,"max_fraction_digits":40,"max_scale":40},"pricing":{"status":"unpriced","charge_usd":null},"input_schema":{"type":"object","additionalProperties":false,"required":["mode","scale","rounding"],"properties":{"mode":{"type":"string","enum":["of","ratio","change","increase","decrease","reverse"],"description":"Which calculation: of, ratio, change, increase, decrease or reverse."},"percent":{"type":"string","minLength":1,"maxLength":62,"pattern":"^-?(0|[1-9][0-9]*)(\\.[0-9]+)?$","description":"Percentage rate (may be negative) for modes of, increase, decrease and reverse."},"value":{"type":"string","minLength":1,"maxLength":62,"pattern":"^-?(0|[1-9][0-9]*)(\\.[0-9]+)?$","description":"Base amount for modes of, increase, decrease and reverse."},"part":{"type":"string","minLength":1,"maxLength":62,"pattern":"^-?(0|[1-9][0-9]*)(\\.[0-9]+)?$","description":"Numerator amount for mode ratio."},"whole":{"type":"string","minLength":1,"maxLength":62,"pattern":"^-?(0|[1-9][0-9]*)(\\.[0-9]+)?$","description":"Denominator amount for mode ratio; zero -> not_computable."},"from":{"type":"string","minLength":1,"maxLength":62,"pattern":"^-?(0|[1-9][0-9]*)(\\.[0-9]+)?$","description":"Starting amount for mode change; zero -> not_computable."},"to":{"type":"string","minLength":1,"maxLength":62,"pattern":"^-?(0|[1-9][0-9]*)(\\.[0-9]+)?$","description":"Ending amount for mode change."},"scale":{"type":"integer","minimum":0,"maximum":40,"description":"Fraction digits of the result."},"rounding":{"type":"string","enum":["half_even","half_up","half_down","half_ceiling","half_floor","up","down","ceiling","floor","unnecessary"],"description":"Rounding mode applied once to the exact result."}}},"output_schema":{"type":"object","additionalProperties":false,"required":["result","exact","mode"],"properties":{"result":{"type":"string","minLength":1,"maxLength":200,"description":"The result at exactly scale fraction digits (no point when scale is 0). For ratio and change it is a percentage number without a percent sign."},"exact":{"type":"boolean","description":"true when the reported result equals the true value."},"mode":{"type":"string","enum":["of","ratio","change","increase","decrease","reverse"]}}},"examples":[{"input":{"mode":"of","percent":"20","value":"150","scale":2,"rounding":"half_even"},"output":{"result":"30.00","exact":true,"mode":"of"}},{"input":{"mode":"reverse","value":"120","percent":"20","scale":2,"rounding":"half_up"},"output":{"result":"100.00","exact":true,"mode":"reverse"}},{"input":{"mode":"change","from":"20","to":"25","scale":1,"rounding":"half_even"},"output":{"result":"25.0","exact":true,"mode":"change"}}],"execute_url":"/v1/tools/percent-compute/versions/1.0.0/execute"}