/*
  Generated by atdts from type definitions in 'trace.atd'.

  Type-safe translations from/to JSON

  For each type 'Foo', there is a pair of functions:
  - 'writeFoo': convert a 'Foo' value into a JSON-compatible value.
  - 'readFoo': convert a JSON-compatible value into a TypeScript value
    of type 'Foo'.
*/

// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-nocheck
/* tslint:disable */
/* eslint-disable */

export type Item = {
  kind: Kind[];
  goal_id: number /*int*/;
  runtime_id: number /*int*/;
  step: number /*int*/;
  name: string;
  payload: string[];
}

export type Kind =
| { kind: 'Start' }
| { kind: 'Info' }
| { kind: 'Stop'; value: Stop }

export type Stop = {
  cause: string;
  time: number;
}

export type RawTrace = Item[]

export type Trace = Card[]

export type Card = {
  step_id: StepId;
  runtime_id: RuntimeId;
  step: Step;
  color: Color;
}

export type Color =
| { kind: 'Red' }
| { kind: 'Green' }
| { kind: 'Grey' }
| { kind: 'YellowGreen' }
| { kind: 'YellowRed' }

export type Step =
| { kind: 'Inference'; value: Inference }
| { kind: 'Findall'; value: Findall }
| { kind: 'Cut'; value: Cut }
| { kind: 'Suspend'; value: Suspend }
| { kind: 'Resume'; value: Resume }
| { kind: 'CHR'; value: Chr }
| { kind: 'Init'; value: Goal }

export type Inference = {
  current_goal_id: GoalId;
  current_goal_text: GoalText;
  current_goal_predicate: string;
  failed_attempts: Attempt[];
  successful_attempts: SuccessfulAttempt[];
  more_successful_attempts: StepId[];
  more_failing_attempts: StepId[];
  stack: Stack;
}

export type Attempt = {
  rule: Rule;
  events: Event[];
}

export type Rule =
| { kind: 'BuiltinRule'; value: BuiltinName }
| { kind: 'UserRule'; value: UserRule }

export type UserRule = {
  rule_text: RuleText;
  rule_loc: Location;
}

export type BuiltinName =
| { kind: 'Logic'; value: string }
| { kind: 'FFI'; value: string }

export type Location =
| { kind: 'File'; value: FileLocation }
| { kind: 'Context'; value: StepId }

export type FileLocation = {
  filename: string;
  line: number /*int*/;
  column: number /*int*/;
  character: number /*int*/;
}

export type Event =
| { kind: 'Assign'; value: string }
| { kind: 'Fail'; value: string }
| { kind: 'ResumeGoal'; value: GoalId[] }

export type RuleText = string

export type SuccessfulAttempt = {
  attempt: Attempt;
  siblings: Goal[];
  siblings_aggregated_outcome: Outcome;
}

export type Goal = {
  goal_text: string;
  goal_id: GoalId;
}

export type Outcome =
| { kind: 'Fail' }
| { kind: 'Success' }

export type Stack = Frame[]

export type Frame = {
  rule: Rule;
  step_id: StepId;
  runtime_id: RuntimeId;
}

export type GoalId = number /*int*/

export type StepId = number /*int*/

export type RuntimeId = number /*int*/

export type GoalText = string

export type Findall = {
  findall_goal_id: GoalId;
  findall_goal_text: GoalText;
  findall_cards: Trace;
  findall_solution_text: SolutionText;
  findall_stack: Stack;
}

export type SolutionText = string[]

export type Suspend = {
  suspend_goal_id: GoalId;
  suspend_goal_text: GoalText;
  suspend_sibling: Goal;
  suspend_stack: Stack;
}

export type Cut = {
  cut_goal_id: GoalId;
  cut_victims: Cutted[];
}

export type Cutted = {
  cut_branch_for_goal: Goal;
  cut_branch: UserRule;
}

export type Resume = Goal[]

export type Chr = {
  chr_failed_attempts: ChrAttempt[];
  chr_successful_attempts: SuccessfulChrAttempt[];
  chr_store_before: Goal[];
  chr_store_after: Goal[];
}

export type ChrAttempt = {
  chr_text: ChrText;
  chr_loc: FileLocation;
  chr_condition_cards: Trace;
}

export type SuccessfulChrAttempt = {
  chr_attempt: ChrAttempt;
  chr_removed_goals: GoalId[];
  chr_new_goals: Goal[];
}

export type ChrText = string

export function writeItem(x: Item, context: any = x): any {
  return {
    'kind': _atd_write_required_field('Item', 'kind', _atd_write_array(writeKind), x.kind, x),
    'goal_id': _atd_write_required_field('Item', 'goal_id', _atd_write_int, x.goal_id, x),
    'runtime_id': _atd_write_required_field('Item', 'runtime_id', _atd_write_int, x.runtime_id, x),
    'step': _atd_write_required_field('Item', 'step', _atd_write_int, x.step, x),
    'name': _atd_write_required_field('Item', 'name', _atd_write_string, x.name, x),
    'payload': _atd_write_required_field('Item', 'payload', _atd_write_array(_atd_write_string), x.payload, x),
  };
}

export function readItem(x: any, context: any = x): Item {
  return {
    kind: _atd_read_required_field('Item', 'kind', _atd_read_array(readKind), x['kind'], x),
    goal_id: _atd_read_required_field('Item', 'goal_id', _atd_read_int, x['goal_id'], x),
    runtime_id: _atd_read_required_field('Item', 'runtime_id', _atd_read_int, x['runtime_id'], x),
    step: _atd_read_required_field('Item', 'step', _atd_read_int, x['step'], x),
    name: _atd_read_required_field('Item', 'name', _atd_read_string, x['name'], x),
    payload: _atd_read_required_field('Item', 'payload', _atd_read_array(_atd_read_string), x['payload'], x),
  };
}

export function writeKind(x: Kind, context: any = x): any {
  switch (x.kind) {
    case 'Start':
      return 'Start'
    case 'Info':
      return 'Info'
    case 'Stop':
      return ['Stop', writeStop(x.value, x)]
  }
}

export function readKind(x: any, context: any = x): Kind {
  if (typeof x === 'string') {
    switch (x) {
      case 'Start':
        return { kind: 'Start' }
      case 'Info':
        return { kind: 'Info' }
      default:
        _atd_bad_json('Kind', x, context)
        throw new Error('impossible')
    }
  }
  else {
    _atd_check_json_tuple(2, x, context)
    switch (x[0]) {
      case 'Stop':
        return { kind: 'Stop', value: readStop(x[1], x) }
      default:
        _atd_bad_json('Kind', x, context)
        throw new Error('impossible')
    }
  }
}

export function writeStop(x: Stop, context: any = x): any {
  return {
    'cause': _atd_write_required_field('Stop', 'cause', _atd_write_string, x.cause, x),
    'time': _atd_write_required_field('Stop', 'time', _atd_write_float, x.time, x),
  };
}

export function readStop(x: any, context: any = x): Stop {
  return {
    cause: _atd_read_required_field('Stop', 'cause', _atd_read_string, x['cause'], x),
    time: _atd_read_required_field('Stop', 'time', _atd_read_float, x['time'], x),
  };
}

export function writeRawTrace(x: RawTrace, context: any = x): any {
  return _atd_write_array(writeItem)(x, context);
}

export function readRawTrace(x: any, context: any = x): RawTrace {
  return _atd_read_array(readItem)(x, context);
}

export function writeTrace(x: Trace, context: any = x): any {
  return _atd_write_array(writeCard)(x, context);
}

export function readTrace(x: any, context: any = x): Trace {
  return _atd_read_array(readCard)(x, context);
}

export function writeCard(x: Card, context: any = x): any {
  return {
    'step_id': _atd_write_required_field('Card', 'step_id', writeStepId, x.step_id, x),
    'runtime_id': _atd_write_required_field('Card', 'runtime_id', writeRuntimeId, x.runtime_id, x),
    'step': _atd_write_required_field('Card', 'step', writeStep, x.step, x),
    'color': _atd_write_required_field('Card', 'color', writeColor, x.color, x),
  };
}

export function readCard(x: any, context: any = x): Card {
  return {
    step_id: _atd_read_required_field('Card', 'step_id', readStepId, x['step_id'], x),
    runtime_id: _atd_read_required_field('Card', 'runtime_id', readRuntimeId, x['runtime_id'], x),
    step: _atd_read_required_field('Card', 'step', readStep, x['step'], x),
    color: _atd_read_required_field('Card', 'color', readColor, x['color'], x),
  };
}

export function writeColor(x: Color, context: any = x): any {
  switch (x.kind) {
    case 'Red':
      return 'Red'
    case 'Green':
      return 'Green'
    case 'Grey':
      return 'Grey'
    case 'YellowGreen':
      return 'YellowGreen'
    case 'YellowRed':
      return 'YellowRed'
  }
}

export function readColor(x: any, context: any = x): Color {
  switch (x) {
    case 'Red':
      return { kind: 'Red' }
    case 'Green':
      return { kind: 'Green' }
    case 'Grey':
      return { kind: 'Grey' }
    case 'YellowGreen':
      return { kind: 'YellowGreen' }
    case 'YellowRed':
      return { kind: 'YellowRed' }
    default:
      _atd_bad_json('Color', x, context)
      throw new Error('impossible')
  }
}

export function writeStep(x: Step, context: any = x): any {
  switch (x.kind) {
    case 'Inference':
      return ['Inference', writeInference(x.value, x)]
    case 'Findall':
      return ['Findall', writeFindall(x.value, x)]
    case 'Cut':
      return ['Cut', writeCut(x.value, x)]
    case 'Suspend':
      return ['Suspend', writeSuspend(x.value, x)]
    case 'Resume':
      return ['Resume', writeResume(x.value, x)]
    case 'CHR':
      return ['CHR', writeChr(x.value, x)]
    case 'Init':
      return ['Init', writeGoal(x.value, x)]
  }
}

export function readStep(x: any, context: any = x): Step {
  _atd_check_json_tuple(2, x, context)
  switch (x[0]) {
    case 'Inference':
      return { kind: 'Inference', value: readInference(x[1], x) }
    case 'Findall':
      return { kind: 'Findall', value: readFindall(x[1], x) }
    case 'Cut':
      return { kind: 'Cut', value: readCut(x[1], x) }
    case 'Suspend':
      return { kind: 'Suspend', value: readSuspend(x[1], x) }
    case 'Resume':
      return { kind: 'Resume', value: readResume(x[1], x) }
    case 'CHR':
      return { kind: 'CHR', value: readChr(x[1], x) }
    case 'Init':
      return { kind: 'Init', value: readGoal(x[1], x) }
    default:
      _atd_bad_json('Step', x, context)
      throw new Error('impossible')
  }
}

export function writeInference(x: Inference, context: any = x): any {
  return {
    'current_goal_id': _atd_write_required_field('Inference', 'current_goal_id', writeGoalId, x.current_goal_id, x),
    'current_goal_text': _atd_write_required_field('Inference', 'current_goal_text', writeGoalText, x.current_goal_text, x),
    'current_goal_predicate': _atd_write_required_field('Inference', 'current_goal_predicate', _atd_write_string, x.current_goal_predicate, x),
    'failed_attempts': _atd_write_required_field('Inference', 'failed_attempts', _atd_write_array(writeAttempt), x.failed_attempts, x),
    'successful_attempts': _atd_write_required_field('Inference', 'successful_attempts', _atd_write_array(writeSuccessfulAttempt), x.successful_attempts, x),
    'more_successful_attempts': _atd_write_required_field('Inference', 'more_successful_attempts', _atd_write_array(writeStepId), x.more_successful_attempts, x),
    'more_failing_attempts': _atd_write_field_with_default(_atd_write_array(writeStepId), [], x.more_failing_attempts, x),
    'stack': _atd_write_required_field('Inference', 'stack', writeStack, x.stack, x),
  };
}

export function readInference(x: any, context: any = x): Inference {
  return {
    current_goal_id: _atd_read_required_field('Inference', 'current_goal_id', readGoalId, x['current_goal_id'], x),
    current_goal_text: _atd_read_required_field('Inference', 'current_goal_text', readGoalText, x['current_goal_text'], x),
    current_goal_predicate: _atd_read_required_field('Inference', 'current_goal_predicate', _atd_read_string, x['current_goal_predicate'], x),
    failed_attempts: _atd_read_required_field('Inference', 'failed_attempts', _atd_read_array(readAttempt), x['failed_attempts'], x),
    successful_attempts: _atd_read_required_field('Inference', 'successful_attempts', _atd_read_array(readSuccessfulAttempt), x['successful_attempts'], x),
    more_successful_attempts: _atd_read_required_field('Inference', 'more_successful_attempts', _atd_read_array(readStepId), x['more_successful_attempts'], x),
    more_failing_attempts: _atd_read_field_with_default(_atd_read_array(readStepId), [], x['more_failing_attempts'], x),
    stack: _atd_read_required_field('Inference', 'stack', readStack, x['stack'], x),
  };
}

export function writeAttempt(x: Attempt, context: any = x): any {
  return {
    'rule': _atd_write_required_field('Attempt', 'rule', writeRule, x.rule, x),
    'events': _atd_write_required_field('Attempt', 'events', _atd_write_array(writeEvent), x.events, x),
  };
}

export function readAttempt(x: any, context: any = x): Attempt {
  return {
    rule: _atd_read_required_field('Attempt', 'rule', readRule, x['rule'], x),
    events: _atd_read_required_field('Attempt', 'events', _atd_read_array(readEvent), x['events'], x),
  };
}

export function writeRule(x: Rule, context: any = x): any {
  switch (x.kind) {
    case 'BuiltinRule':
      return ['BuiltinRule', writeBuiltinName(x.value, x)]
    case 'UserRule':
      return ['UserRule', writeUserRule(x.value, x)]
  }
}

export function readRule(x: any, context: any = x): Rule {
  _atd_check_json_tuple(2, x, context)
  switch (x[0]) {
    case 'BuiltinRule':
      return { kind: 'BuiltinRule', value: readBuiltinName(x[1], x) }
    case 'UserRule':
      return { kind: 'UserRule', value: readUserRule(x[1], x) }
    default:
      _atd_bad_json('Rule', x, context)
      throw new Error('impossible')
  }
}

export function writeUserRule(x: UserRule, context: any = x): any {
  return {
    'rule_text': _atd_write_required_field('UserRule', 'rule_text', writeRuleText, x.rule_text, x),
    'rule_loc': _atd_write_required_field('UserRule', 'rule_loc', writeLocation, x.rule_loc, x),
  };
}

export function readUserRule(x: any, context: any = x): UserRule {
  return {
    rule_text: _atd_read_required_field('UserRule', 'rule_text', readRuleText, x['rule_text'], x),
    rule_loc: _atd_read_required_field('UserRule', 'rule_loc', readLocation, x['rule_loc'], x),
  };
}

export function writeBuiltinName(x: BuiltinName, context: any = x): any {
  switch (x.kind) {
    case 'Logic':
      return ['Logic', _atd_write_string(x.value, x)]
    case 'FFI':
      return ['FFI', _atd_write_string(x.value, x)]
  }
}

export function readBuiltinName(x: any, context: any = x): BuiltinName {
  _atd_check_json_tuple(2, x, context)
  switch (x[0]) {
    case 'Logic':
      return { kind: 'Logic', value: _atd_read_string(x[1], x) }
    case 'FFI':
      return { kind: 'FFI', value: _atd_read_string(x[1], x) }
    default:
      _atd_bad_json('BuiltinName', x, context)
      throw new Error('impossible')
  }
}

export function writeLocation(x: Location, context: any = x): any {
  switch (x.kind) {
    case 'File':
      return ['File', writeFileLocation(x.value, x)]
    case 'Context':
      return ['Context', writeStepId(x.value, x)]
  }
}

export function readLocation(x: any, context: any = x): Location {
  _atd_check_json_tuple(2, x, context)
  switch (x[0]) {
    case 'File':
      return { kind: 'File', value: readFileLocation(x[1], x) }
    case 'Context':
      return { kind: 'Context', value: readStepId(x[1], x) }
    default:
      _atd_bad_json('Location', x, context)
      throw new Error('impossible')
  }
}

export function writeFileLocation(x: FileLocation, context: any = x): any {
  return {
    'filename': _atd_write_required_field('FileLocation', 'filename', _atd_write_string, x.filename, x),
    'line': _atd_write_required_field('FileLocation', 'line', _atd_write_int, x.line, x),
    'column': _atd_write_required_field('FileLocation', 'column', _atd_write_int, x.column, x),
    'character': _atd_write_required_field('FileLocation', 'character', _atd_write_int, x.character, x),
  };
}

export function readFileLocation(x: any, context: any = x): FileLocation {
  return {
    filename: _atd_read_required_field('FileLocation', 'filename', _atd_read_string, x['filename'], x),
    line: _atd_read_required_field('FileLocation', 'line', _atd_read_int, x['line'], x),
    column: _atd_read_required_field('FileLocation', 'column', _atd_read_int, x['column'], x),
    character: _atd_read_required_field('FileLocation', 'character', _atd_read_int, x['character'], x),
  };
}

export function writeEvent(x: Event, context: any = x): any {
  switch (x.kind) {
    case 'Assign':
      return ['Assign', _atd_write_string(x.value, x)]
    case 'Fail':
      return ['Fail', _atd_write_string(x.value, x)]
    case 'ResumeGoal':
      return ['ResumeGoal', _atd_write_array(writeGoalId)(x.value, x)]
  }
}

export function readEvent(x: any, context: any = x): Event {
  _atd_check_json_tuple(2, x, context)
  switch (x[0]) {
    case 'Assign':
      return { kind: 'Assign', value: _atd_read_string(x[1], x) }
    case 'Fail':
      return { kind: 'Fail', value: _atd_read_string(x[1], x) }
    case 'ResumeGoal':
      return { kind: 'ResumeGoal', value: _atd_read_array(readGoalId)(x[1], x) }
    default:
      _atd_bad_json('Event', x, context)
      throw new Error('impossible')
  }
}

export function writeRuleText(x: RuleText, context: any = x): any {
  return _atd_write_string(x, context);
}

export function readRuleText(x: any, context: any = x): RuleText {
  return _atd_read_string(x, context);
}

export function writeSuccessfulAttempt(x: SuccessfulAttempt, context: any = x): any {
  return {
    'attempt': _atd_write_required_field('SuccessfulAttempt', 'attempt', writeAttempt, x.attempt, x),
    'siblings': _atd_write_required_field('SuccessfulAttempt', 'siblings', _atd_write_array(writeGoal), x.siblings, x),
    'siblings_aggregated_outcome': _atd_write_required_field('SuccessfulAttempt', 'siblings_aggregated_outcome', writeOutcome, x.siblings_aggregated_outcome, x),
  };
}

export function readSuccessfulAttempt(x: any, context: any = x): SuccessfulAttempt {
  return {
    attempt: _atd_read_required_field('SuccessfulAttempt', 'attempt', readAttempt, x['attempt'], x),
    siblings: _atd_read_required_field('SuccessfulAttempt', 'siblings', _atd_read_array(readGoal), x['siblings'], x),
    siblings_aggregated_outcome: _atd_read_required_field('SuccessfulAttempt', 'siblings_aggregated_outcome', readOutcome, x['siblings_aggregated_outcome'], x),
  };
}

export function writeGoal(x: Goal, context: any = x): any {
  return {
    'goal_text': _atd_write_required_field('Goal', 'goal_text', _atd_write_string, x.goal_text, x),
    'goal_id': _atd_write_required_field('Goal', 'goal_id', writeGoalId, x.goal_id, x),
  };
}

export function readGoal(x: any, context: any = x): Goal {
  return {
    goal_text: _atd_read_required_field('Goal', 'goal_text', _atd_read_string, x['goal_text'], x),
    goal_id: _atd_read_required_field('Goal', 'goal_id', readGoalId, x['goal_id'], x),
  };
}

export function writeOutcome(x: Outcome, context: any = x): any {
  switch (x.kind) {
    case 'Fail':
      return 'Fail'
    case 'Success':
      return 'Success'
  }
}

export function readOutcome(x: any, context: any = x): Outcome {
  switch (x) {
    case 'Fail':
      return { kind: 'Fail' }
    case 'Success':
      return { kind: 'Success' }
    default:
      _atd_bad_json('Outcome', x, context)
      throw new Error('impossible')
  }
}

export function writeStack(x: Stack, context: any = x): any {
  return _atd_write_array(writeFrame)(x, context);
}

export function readStack(x: any, context: any = x): Stack {
  return _atd_read_array(readFrame)(x, context);
}

export function writeFrame(x: Frame, context: any = x): any {
  return {
    'rule': _atd_write_required_field('Frame', 'rule', writeRule, x.rule, x),
    'step_id': _atd_write_required_field('Frame', 'step_id', writeStepId, x.step_id, x),
    'runtime_id': _atd_write_required_field('Frame', 'runtime_id', writeRuntimeId, x.runtime_id, x),
  };
}

export function readFrame(x: any, context: any = x): Frame {
  return {
    rule: _atd_read_required_field('Frame', 'rule', readRule, x['rule'], x),
    step_id: _atd_read_required_field('Frame', 'step_id', readStepId, x['step_id'], x),
    runtime_id: _atd_read_required_field('Frame', 'runtime_id', readRuntimeId, x['runtime_id'], x),
  };
}

export function writeGoalId(x: GoalId, context: any = x): any {
  return _atd_write_int(x, context);
}

export function readGoalId(x: any, context: any = x): GoalId {
  return _atd_read_int(x, context);
}

export function writeStepId(x: StepId, context: any = x): any {
  return _atd_write_int(x, context);
}

export function readStepId(x: any, context: any = x): StepId {
  return _atd_read_int(x, context);
}

export function writeRuntimeId(x: RuntimeId, context: any = x): any {
  return _atd_write_int(x, context);
}

export function readRuntimeId(x: any, context: any = x): RuntimeId {
  return _atd_read_int(x, context);
}

export function writeGoalText(x: GoalText, context: any = x): any {
  return _atd_write_string(x, context);
}

export function readGoalText(x: any, context: any = x): GoalText {
  return _atd_read_string(x, context);
}

export function writeFindall(x: Findall, context: any = x): any {
  return {
    'findall_goal_id': _atd_write_required_field('Findall', 'findall_goal_id', writeGoalId, x.findall_goal_id, x),
    'findall_goal_text': _atd_write_required_field('Findall', 'findall_goal_text', writeGoalText, x.findall_goal_text, x),
    'findall_cards': _atd_write_required_field('Findall', 'findall_cards', writeTrace, x.findall_cards, x),
    'findall_solution_text': _atd_write_required_field('Findall', 'findall_solution_text', writeSolutionText, x.findall_solution_text, x),
    'findall_stack': _atd_write_required_field('Findall', 'findall_stack', writeStack, x.findall_stack, x),
  };
}

export function readFindall(x: any, context: any = x): Findall {
  return {
    findall_goal_id: _atd_read_required_field('Findall', 'findall_goal_id', readGoalId, x['findall_goal_id'], x),
    findall_goal_text: _atd_read_required_field('Findall', 'findall_goal_text', readGoalText, x['findall_goal_text'], x),
    findall_cards: _atd_read_required_field('Findall', 'findall_cards', readTrace, x['findall_cards'], x),
    findall_solution_text: _atd_read_required_field('Findall', 'findall_solution_text', readSolutionText, x['findall_solution_text'], x),
    findall_stack: _atd_read_required_field('Findall', 'findall_stack', readStack, x['findall_stack'], x),
  };
}

export function writeSolutionText(x: SolutionText, context: any = x): any {
  return _atd_write_array(_atd_write_string)(x, context);
}

export function readSolutionText(x: any, context: any = x): SolutionText {
  return _atd_read_array(_atd_read_string)(x, context);
}

export function writeSuspend(x: Suspend, context: any = x): any {
  return {
    'suspend_goal_id': _atd_write_required_field('Suspend', 'suspend_goal_id', writeGoalId, x.suspend_goal_id, x),
    'suspend_goal_text': _atd_write_required_field('Suspend', 'suspend_goal_text', writeGoalText, x.suspend_goal_text, x),
    'suspend_sibling': _atd_write_required_field('Suspend', 'suspend_sibling', writeGoal, x.suspend_sibling, x),
    'suspend_stack': _atd_write_required_field('Suspend', 'suspend_stack', writeStack, x.suspend_stack, x),
  };
}

export function readSuspend(x: any, context: any = x): Suspend {
  return {
    suspend_goal_id: _atd_read_required_field('Suspend', 'suspend_goal_id', readGoalId, x['suspend_goal_id'], x),
    suspend_goal_text: _atd_read_required_field('Suspend', 'suspend_goal_text', readGoalText, x['suspend_goal_text'], x),
    suspend_sibling: _atd_read_required_field('Suspend', 'suspend_sibling', readGoal, x['suspend_sibling'], x),
    suspend_stack: _atd_read_required_field('Suspend', 'suspend_stack', readStack, x['suspend_stack'], x),
  };
}

export function writeCut(x: Cut, context: any = x): any {
  return {
    'cut_goal_id': _atd_write_required_field('Cut', 'cut_goal_id', writeGoalId, x.cut_goal_id, x),
    'cut_victims': _atd_write_required_field('Cut', 'cut_victims', _atd_write_array(writeCutted), x.cut_victims, x),
  };
}

export function readCut(x: any, context: any = x): Cut {
  return {
    cut_goal_id: _atd_read_required_field('Cut', 'cut_goal_id', readGoalId, x['cut_goal_id'], x),
    cut_victims: _atd_read_required_field('Cut', 'cut_victims', _atd_read_array(readCutted), x['cut_victims'], x),
  };
}

export function writeCutted(x: Cutted, context: any = x): any {
  return {
    'cut_branch_for_goal': _atd_write_required_field('Cutted', 'cut_branch_for_goal', writeGoal, x.cut_branch_for_goal, x),
    'cut_branch': _atd_write_required_field('Cutted', 'cut_branch', writeUserRule, x.cut_branch, x),
  };
}

export function readCutted(x: any, context: any = x): Cutted {
  return {
    cut_branch_for_goal: _atd_read_required_field('Cutted', 'cut_branch_for_goal', readGoal, x['cut_branch_for_goal'], x),
    cut_branch: _atd_read_required_field('Cutted', 'cut_branch', readUserRule, x['cut_branch'], x),
  };
}

export function writeResume(x: Resume, context: any = x): any {
  return _atd_write_array(writeGoal)(x, context);
}

export function readResume(x: any, context: any = x): Resume {
  return _atd_read_array(readGoal)(x, context);
}

export function writeChr(x: Chr, context: any = x): any {
  return {
    'chr_failed_attempts': _atd_write_required_field('Chr', 'chr_failed_attempts', _atd_write_array(writeChrAttempt), x.chr_failed_attempts, x),
    'chr_successful_attempts': _atd_write_required_field('Chr', 'chr_successful_attempts', _atd_write_array(writeSuccessfulChrAttempt), x.chr_successful_attempts, x),
    'chr_store_before': _atd_write_required_field('Chr', 'chr_store_before', _atd_write_array(writeGoal), x.chr_store_before, x),
    'chr_store_after': _atd_write_required_field('Chr', 'chr_store_after', _atd_write_array(writeGoal), x.chr_store_after, x),
  };
}

export function readChr(x: any, context: any = x): Chr {
  return {
    chr_failed_attempts: _atd_read_required_field('Chr', 'chr_failed_attempts', _atd_read_array(readChrAttempt), x['chr_failed_attempts'], x),
    chr_successful_attempts: _atd_read_required_field('Chr', 'chr_successful_attempts', _atd_read_array(readSuccessfulChrAttempt), x['chr_successful_attempts'], x),
    chr_store_before: _atd_read_required_field('Chr', 'chr_store_before', _atd_read_array(readGoal), x['chr_store_before'], x),
    chr_store_after: _atd_read_required_field('Chr', 'chr_store_after', _atd_read_array(readGoal), x['chr_store_after'], x),
  };
}

export function writeChrAttempt(x: ChrAttempt, context: any = x): any {
  return {
    'chr_text': _atd_write_required_field('ChrAttempt', 'chr_text', writeChrText, x.chr_text, x),
    'chr_loc': _atd_write_required_field('ChrAttempt', 'chr_loc', writeFileLocation, x.chr_loc, x),
    'chr_condition_cards': _atd_write_required_field('ChrAttempt', 'chr_condition_cards', writeTrace, x.chr_condition_cards, x),
  };
}

export function readChrAttempt(x: any, context: any = x): ChrAttempt {
  return {
    chr_text: _atd_read_required_field('ChrAttempt', 'chr_text', readChrText, x['chr_text'], x),
    chr_loc: _atd_read_required_field('ChrAttempt', 'chr_loc', readFileLocation, x['chr_loc'], x),
    chr_condition_cards: _atd_read_required_field('ChrAttempt', 'chr_condition_cards', readTrace, x['chr_condition_cards'], x),
  };
}

export function writeSuccessfulChrAttempt(x: SuccessfulChrAttempt, context: any = x): any {
  return {
    'chr_attempt': _atd_write_required_field('SuccessfulChrAttempt', 'chr_attempt', writeChrAttempt, x.chr_attempt, x),
    'chr_removed_goals': _atd_write_required_field('SuccessfulChrAttempt', 'chr_removed_goals', _atd_write_array(writeGoalId), x.chr_removed_goals, x),
    'chr_new_goals': _atd_write_required_field('SuccessfulChrAttempt', 'chr_new_goals', _atd_write_array(writeGoal), x.chr_new_goals, x),
  };
}

export function readSuccessfulChrAttempt(x: any, context: any = x): SuccessfulChrAttempt {
  return {
    chr_attempt: _atd_read_required_field('SuccessfulChrAttempt', 'chr_attempt', readChrAttempt, x['chr_attempt'], x),
    chr_removed_goals: _atd_read_required_field('SuccessfulChrAttempt', 'chr_removed_goals', _atd_read_array(readGoalId), x['chr_removed_goals'], x),
    chr_new_goals: _atd_read_required_field('SuccessfulChrAttempt', 'chr_new_goals', _atd_read_array(readGoal), x['chr_new_goals'], x),
  };
}

export function writeChrText(x: ChrText, context: any = x): any {
  return _atd_write_string(x, context);
}

export function readChrText(x: any, context: any = x): ChrText {
  return _atd_read_string(x, context);
}


/////////////////////////////////////////////////////////////////////
// Runtime library
/////////////////////////////////////////////////////////////////////

export type Option<T> = null | { value: T }

function _atd_missing_json_field(type_name: string, json_field_name: string) {
    throw new Error(`missing field '${json_field_name}'` +
                    ` in JSON object of type '${type_name}'`)
}

function _atd_missing_ts_field(type_name: string, ts_field_name: string) {
    throw new Error(`missing field '${ts_field_name}'` +
                    ` in TypeScript object of type '${type_name}'`)
}

function _atd_bad_json(expected_type: string, json_value: any, context: any) {
  let value_str = JSON.stringify(json_value)
  if (value_str.length > 200)
    value_str = value_str.substring(0, 200) + '…';

  throw new Error(`incompatible JSON value where` +
                  ` type '${expected_type}' was expected: '${value_str}'.` +
                  ` Occurs in '${JSON.stringify(context)}'.`)
}

function _atd_bad_ts(expected_type: string, ts_value: any, context: any) {
  let value_str = JSON.stringify(ts_value)
  if (value_str.length > 200)
    value_str = value_str.substring(0, 200) + '…';

  throw new Error(`incompatible TypeScript value where` +
                  ` type '${expected_type}' was expected: '${value_str}'.` +
                  ` Occurs in '${JSON.stringify(context)}'.`)
}

function _atd_check_json_tuple(len: number /*int*/, x: any, context: any) {
  if (! Array.isArray(x) || x.length !== len)
    _atd_bad_json('tuple of length ' + len, x, context);
}

function _atd_read_unit(x: any, context: any): null {
  if (x === null)
    return null
  else {
    _atd_bad_json('null', x, context)
    throw new Error('impossible')
  }
}

function _atd_read_bool(x: any, context: any): boolean {
  if (typeof x === 'boolean')
    return x
  else {
    _atd_bad_json('boolean', x, context)
    throw new Error('impossible')
  }
}

function _atd_read_int(x: any, context: any): number /*int*/ {
  if (Number.isInteger(x))
    return x
  else {
    _atd_bad_json('integer', x, context)
    throw new Error('impossible')
  }
}

function _atd_read_float(x: any, context: any): number {
  if (isFinite(x))
    return x
  else {
    _atd_bad_json('number', x, context)
    throw new Error('impossible')
  }
}

function _atd_read_string(x: any, context: any): string {
  if (typeof x === 'string')
    return x
  else {
    _atd_bad_json('string', x, context)
    throw new Error('impossible')
  }
}

function _atd_read_required_field<T>(type_name: string,
                                     field_name: string,
                                     read_elt: (x: any, context: any) => T,
                                     x: any,
                                     context: any): T {
  if (x === undefined) {
    _atd_missing_json_field(type_name, field_name)
    throw new Error('impossible')
  }
  else
    return read_elt(x, context)
}

function _atd_read_optional_field<T>(read_elt: (x: any, context: any) => T,
                                     x: any,
                                     context: any): T {
  if (x === undefined || x === null)
    return x
  else
    return read_elt(x, context)
}

function _atd_read_field_with_default<T>(read_elt: (x: any, context: any) => T,
                                         default_: T,
                                         x: any,
                                         context: any): T {
  if (x === undefined || x === null)
    return default_
  else
    return read_elt(x, context)
}

function _atd_read_option<T>(read_elt: (x: any, context: any) => T):
  (x: any, context: any) => Option<T> {
  function read_option(x: any, context: any): Option<T> {
    if (x === 'None')
      return null
    else {
      _atd_check_json_tuple(2, x, context);
      switch (x[0]) {
        case 'Some':
          return { value: read_elt(x[1], context) }
        default:
          _atd_bad_json('option', x, context)
          throw new Error('impossible')
      }
    }
  }
  return read_option
}

function _atd_read_nullable<T>(read_elt: (x: any, context: any) => T):
  (x: any, context: any) => T | null {
  function read_nullable(x: any, context: any): T | null {
    if (x === null)
      return null
    else
      return read_elt(x, context)
  }
  return read_nullable
}

function _atd_read_array<T>(read_elt: (x: any, context: any) => T):
  (elts: any, context: any) => T[] {
  function read_array(elts: any, context: any): T[] {
    if (Array.isArray(elts))
      return elts.map((x) => read_elt(x, elts))
    else {
      _atd_bad_json('array', elts, context)
      throw new Error('impossible')
    }
  }
  return read_array
}

function _atd_read_assoc_array_into_map<K, V>(
    read_key: (key: any, context: any) => K,
    read_value: (value: any, context: any) => V
  ): (x: any, context: any) => Map<K, V> {
  function read_assoc(elts: any, context: any): Map<K, V> {
    if (Array.isArray(elts)) {
      const res = new Map<K, V>([])
      for (const x of elts) {
        if (Array.isArray(x) && x.length === 2)
          res.set(read_key(x[0], x), read_value(x[1], x))
        else {
          _atd_bad_json('pair', x, elts)
          throw new Error('impossible')
        }
      }
      return res
    }
    else {
      _atd_bad_json('array', elts, context)
      throw new Error('impossible')
    }
  }
  return read_assoc
}

function _atd_read_assoc_object_into_map<T>(
    read_value: (value: any, context: any) => T
  ): (x: any, context: any) => Map<string, T> {
  function read_assoc(elts: any, context: any): Map<string, T> {
    if (typeof elts === 'object') {
      const res = new Map<string, T>([])
      for (const [key, value] of Object.entries(elts))
        res.set(key, read_value(value, elts))
      return res
    }
    else {
      _atd_bad_json('object', elts, context)
      throw new Error('impossible')
    }
  }
  return read_assoc
}

function _atd_read_assoc_object_into_array<T>(
    read_value: (value: any, context: any) => T
  ): (x: any, context: any) => [string, T][] {
  function read_assoc(elts: any, context: any): [string, T][] {
    if (typeof elts === 'object') {
      const res: [string, T][] = []
      for (const [key, value] of Object.entries(elts))
        res.push([key, read_value(value, elts)])
      return res
    }
    else {
      _atd_bad_json('object', elts, context)
      throw new Error('impossible')
    }
  }
  return read_assoc
}

function _atd_write_unit(x: any, context: any) {
  if (x === null)
    return x
  else {
    _atd_bad_ts('null', x, context)
    throw new Error('impossible')
  }
}

function _atd_write_bool(x: any, context: any): boolean {
  if (typeof x === 'boolean')
    return x
  else {
    _atd_bad_ts('boolean', x, context)
    throw new Error('impossible')
  }
}

function _atd_write_int(x: any, context: any): number /*int*/ {
  if (Number.isInteger(x))
    return x
  else {
    _atd_bad_ts('integer', x, context)
    throw new Error('impossible')
  }
}

function _atd_write_float(x: any, context: any): number {
  if (isFinite(x))
    return x
  else {
    _atd_bad_ts('number', x, context)
    throw new Error('impossible')
  }
}

function _atd_write_string(x: any, context: any): string {
  if (typeof x === 'string')
    return x
  else {
    _atd_bad_ts('string', x, context)
    throw new Error('impossible')
  }
}

function _atd_write_option<T>(write_elt: (x: T, context: any) => any):
   (elts: Option<T>, context: any) => any {
  function write_option(x: Option<T>, context: any): any {
    if (x === null)
      return 'None'
    else
      return ['Some', write_elt(x.value, context)]
  }
  return write_option
}

function _atd_write_nullable<T>(write_elt: (x: T, context: any) => any):
  (x: T | null, context: any) => any {
  function write_option(x: T | null, context: any): any {
    if (x === null)
      return null
    else
      return write_elt(x, context)
  }
  return write_option
}

function _atd_write_array<T>(write_elt: (elt: T, context: any) => any):
  (elts: T[], context: any) => any {
  return ((elts: T[], context: any): any =>
    elts.map((x) => write_elt(x, elts))
  )
}

function _atd_write_assoc_map_to_array<K, V>(
    write_key: (key: K, context: any) => any,
    write_value: (value: V, context: any) => any
  ): (elts: Map<K, V>, context: any) => any {
  function write_assoc(elts: Map<K, V>, context: any): any {
    const res: any = []
    elts.forEach((value: V, key: K) =>
      res.push([write_key(key, elts), write_value(value, elts)])
    )
    return res
  }
  return write_assoc
}

function _atd_write_assoc_map_to_object<T>(
    write_value: (value: T, context: any) => any
  ): (elts: Map<string, T>, context: any) => any {
  function write_assoc(elts: Map<string, T>, context: any): any {
    const res: any = {}
    elts.forEach((value: T, key: string) =>
      res[key] = write_value(value, elts)
    )
    return res
  }
  return write_assoc
}

function _atd_write_assoc_array_to_object<T>(
    write_value: (value: T, context: any) => any
  ): (elts: [string, T][], context: any) => any {
  function write_assoc(elts: [string, T][], context: any): any {
    const res: any = {}
    for (const [key, value] of elts)
      res[key] = write_value(value, elts)
    return res
  }
  return write_assoc
}

function _atd_write_required_field<T>(type_name: string,
                                      field_name: string,
                                      write_elt: (x: T, context: any) => any,
                                      x: T,
                                      context: any): any {
  if (x === undefined) {
    _atd_missing_ts_field(type_name, field_name)
    throw new Error('impossible')
  }
  else
    return write_elt(x, context)
}

function _atd_write_optional_field<T>(write_elt: (x: T, context: any) => any,
                                      x: T | undefined,
                                      context: any): any {
  if (x === undefined || x === null)
    return x
  else
    return write_elt(x, context)
}

function _atd_write_field_with_default<T>(
  write_elt: (x: T, context: any) => any,
  default_: T,
  x: T,
  context: any
): T {
  const value = (x === undefined || x === null) ? default_ : x
  return write_elt(value, context)
}

