/* This file is generated by "mtxrun --script "mtx-wtoc.lua" from the metapost cweb files. */


# include "mpconfig.h"
# include "mp.h"
# include "mpmath.h"
# include "mpmathdouble.h"
# include "mpmathbinary.h"
# include "mpmathdecimal.h"
# include "mpmathposit.h"
# include "mpstrings.h"


# define  default_banner "This is MPLIB for LuaMetaTeX, version 3.14"
# define  odd(A) (abs(A) % 2 == 1)
# define  loc mp->cur_input.loc_field
# define  inf_t          mp->math->md_inf_t
# define  negative_inf_t mp->math->md_negative_inf_t
# define  arc_tol_k                 mp->math->md_arc_tol_k
# define  coef_bound_k              mp->math->md_coef_bound_k
# define  coef_bound_minus_1        mp->math->md_coef_bound_minus_1
# define  sqrt_8_e_k                mp->math->md_sqrt_8_e_k
# define  twelve_ln_2_k             mp->math->md_twelve_ln_2_k
# define  twelvebits_3              mp->math->md_twelvebits_3
# define  one_k                     mp->math->md_one_k
# define  epsilon_t                 mp->math->md_epsilon_t
# define  unity_t                   mp->math->md_unity_t
# define  zero_t                    mp->math->md_zero_t
# define  two_t                     mp->math->md_two_t
# define  three_t                   mp->math->md_three_t
# define  half_unit_t               mp->math->md_half_unit_t
# define  three_quarter_unit_t      mp->math->md_three_quarter_unit_t
# define  twentysixbits_sqrt2_t     mp->math->md_twentysixbits_sqrt2_t
# define  twentyeightbits_d_t       mp->math->md_twentyeightbits_d_t
# define  twentysevenbits_sqrt2_d_t mp->math->md_twentysevenbits_sqrt2_d_t
# define  warning_limit_t           mp->math->md_warning_limit_t
# define  precision_default         mp->math->md_precision_default
# define  precision_min             mp->math->md_precision_min
# define  precision_max             mp->math->md_precision_max
# define  fraction_one_t            mp->math->md_fraction_one_t
# define  fraction_half_t           mp->math->md_fraction_half_t
# define  fraction_three_t          mp->math->md_fraction_three_t
# define  fraction_four_t           mp->math->md_fraction_four_t
# define  one_eighty_deg_t          mp->math->md_one_eighty_deg_t
# define  negative_one_eighty_deg_t mp->math->md_negative_one_eighty_deg_t
# define  three_sixty_deg_t         mp->math->md_three_sixty_deg_t
# define  max_quarterword    0x3FFF
# define  max_halfword    0xFFFFFFF
# define  max_num_token_nodes    8000
# define  max_num_pair_nodes     1000
# define  max_num_knot_nodes     1000
# define  max_num_value_nodes    1000
# define  max_num_symbolic_nodes 1000
# define  mp_link(A)      (A)->link
# define  mp_type(A)      (A)->type
# define  mp_name_type(A) (A)->name_type
# define  mp_set_link(A,B) (A)->link = (mp_node) (B)
# define  mp_max_command_code       mp_stop
# define  mp_max_pre_command        mp_etex_command
# define  mp_min_command            (mp_defined_macro_command+1)
# define  mp_max_statement_command  mp_type_name_command
# define  mp_min_primary_command    mp_type_name_command
# define  mp_min_suffix_token       mp_internal_command
# define  mp_max_suffix_token       mp_numeric_command
# define  mp_max_primary_command    mp_plus_or_minus_command
# define  mp_min_tertiary_command   mp_plus_or_minus_command
# define  mp_max_tertiary_command   mp_tertiary_binary_command
# define  mp_min_expression_command mp_left_brace_command
# define  mp_max_expression_command mp_equals_command
# define  mp_min_secondary_command  mp_and_command
# define  mp_max_secondary_command  mp_secondary_binary_command
# define  mp_end_of_statement       (cur_cmd>mp_comma_command)
# define  unknown_tag 1
# define  mp_min_of_operation mp_substring_operation
# define  max_given_internal mp_restore_clip_color_internal
# define  set_text(A) { \
    (A)->text = (B) ; \
}
# define  set_eq_type(A,B) { \
    (A)->type = (B) ; \
}
# define  set_eq_property(A,B) { \
    (A)->property = (B) ; \
}
# define  set_equiv(A,B) { \
    (A)->v.data.node = NULL ; \
    (A)->v.data.indep.serial = (B); \
}
# define  set_equiv_node(A,B) { \
    (A)->v.data.node = (B) ; \
    (A)->v.data.indep.serial = 0; \
}
# define  set_equiv_sym(A,B) { \
    (A)->v.data.node = (mp_node) (B); \
    (A)->v.data.indep.serial = 0; \
}
# define  mp_id_lookup(A,B,C,D) mp_do_id_lookup((A), mp->symbols, (B), (C), (D))
# define  mp_set_value_sym(A,B)    do_set_value_sym   (mp, (mp_token_node) (A), (B))
# define  mp_set_value_number(A,B) do_set_value_number(mp, (mp_token_node) (A), &(B))
# define  mp_set_value_node(A,B)   do_set_value_node  (mp, (mp_token_node) (A), (B))
# define  mp_set_value_str(A,B)    do_set_value_str   (mp, (mp_token_node) (A), (B))
# define  mp_set_value_knot(A,B)   do_set_value_knot  (mp, (mp_token_node) (A), (B))
# define  mp_get_ref_count(A)   mp_get_indep_value(A)
# define  mp_set_ref_count(A,B) mp_set_indep_value(A,B)
# define  mp_add_mac_ref(A)     mp_set_ref_count((A), mp_get_ref_count((A))+1)
# define  mp_decr_mac_ref(A)    mp_set_ref_count((A), mp_get_ref_count((A))-1)
# define  mp_get_attribute_head(A)     mp_do_get_attribute_head(mp, (mp_value_node) (A))
# define  mp_set_attribute_head(A,B)   mp_do_set_attribute_head(mp, (mp_value_node) (A),(mp_node) (B))
# define  mp_get_subscr_head(A)   mp_do_get_subscr_head(mp,(mp_value_node) (A))
# define  mp_set_subscr_head(A,B) mp_do_set_subscr_head(mp,(mp_value_node) (A),(mp_node) (B))
# define  mp_collective_subscript (void *)0
# define  mp_subscript(A)         ((mp_value_node)(A))->subscript
# define  mp_x_part(A) ((mp_pair_node) (A))->x_part
# define  mp_y_part(A) ((mp_pair_node) (A))->y_part
# define  mp_tx_part(A) ((mp_transform_node) (A))->tx_part
# define  mp_ty_part(A) ((mp_transform_node) (A))->ty_part
# define  mp_xx_part(A) ((mp_transform_node) (A))->xx_part
# define  mp_xy_part(A) ((mp_transform_node) (A))->xy_part
# define  mp_yx_part(A) ((mp_transform_node) (A))->yx_part
# define  mp_yy_part(A) ((mp_transform_node) (A))->yy_part
# define  mp_red_part(A)     ((mp_color_node) (A))->red_part
# define  mp_green_part(A)   ((mp_color_node) (A))->green_part
# define  mp_blue_part(A)    ((mp_color_node) (A))->blue_part
# define  mp_grey_part(A)    ((mp_color_node) (A))->grey_part
# define  mp_cyan_part(A)    ((mp_color_node) (A))->cyan_part
# define  mp_magenta_part(A) ((mp_color_node) (A))->magenta_part
# define  mp_yellow_part(A)  ((mp_color_node) (A))->yellow_part
# define  mp_black_part(A)   ((mp_color_node) (A))->black_part
# define  mp_next_knot(A)  (A)->next
# define  mp_left_type(A)  (A)->left_type
# define  mp_right_type(A) (A)->right_type
# define  mp_prev_knot(A)  (A)->prev
# define  mp_knot_info(A)  (A)->info
# define  mp_originator(A) (A)->originator
# define  mp_knotstate(A)  (A)->state
# define  mp_minx mp->bbmin[mp_x_code]
# define  mp_maxx mp->bbmax[mp_x_code]
# define  mp_miny mp->bbmin[mp_y_code]
# define  mp_maxy mp->bbmax[mp_y_code]
# define  one_third_inf_t mp->math->md_one_third_inf_t
# define  mp_copy_pen(mp,A) mp_make_pen(mp, mp_copy_path(mp, (A)),0)
# define  mp_pen_is_elliptical(A) ((A)==mp_next_knot((A)))
# define  new_number(A)                           mp->math->md_allocate(mp, &(A), mp_scaled_type)
# define  new_fraction(A)                         mp->math->md_allocate(mp, &(A), mp_fraction_type)
# define  new_angle(A)                            mp->math->md_allocate(mp, &(A), mp_angle_type)
# define  new_number_clone(A,B)                   mp->math->md_allocate_clone(mp, &(A), mp_scaled_type, &(B))
# define  new_fraction_clone(A,B)                 mp->math->md_allocate_clone(mp, &(A), mp_fraction_type, &(B))
# define  new_angle_clone(A,B)                    mp->math->md_allocate_clone(mp, &(A), mp_angle_type, &(B))
# define  new_number_from_double(mp,A,B)          mp->math->md_allocate_double(mp, &(A), B)
# define  new_number_abs(A,B)                     mp->math->md_allocate_abs(mp, &(A), mp_scaled_type, &(B))
# define  free_number(A)                          mp->math->md_free(mp, &(A))
# define  set_precision()                         mp->math->md_set_precision(mp)
# define  free_math()                             mp->math->md_free_math(mp)
# define  scan_numeric_token(A)                   mp->math->md_scan_numeric(mp,A)
# define  scan_fractional_token(A)                mp->math->md_scan_fractional(mp,A)
# define  set_number_from_of_the_way(A,t,B,C)     mp->math->md_from_oftheway(mp,&(A),&(t),&(B),&(C))
# define  set_number_from_int(A,B)                mp->math->md_from_int(&(A),B)
# define  set_number_from_scaled(A,B)             mp->math->md_from_scaled(&(A),B)
# define  set_number_from_boolean(A,B)            mp->math->md_from_boolean(&(A),B)
# define  set_number_from_double(A,B)             mp->math->md_from_double(&(A),B)
# define  set_number_from_addition(A,B,C)         mp->math->md_from_addition(&(A),&(B),&(C))
# define  set_number_half_from_addition(A,B,C)    mp->math->md_half_from_addition(&(A),&(B),&(C))
# define  set_number_from_subtraction(A,B,C)      mp->math->md_from_subtraction(&(A),&(B),&(C))
# define  set_number_half_from_subtraction(A,B,C) mp->math->md_half_from_subtraction(&(A),&(B),&(C))
# define  set_number_from_div(A,B,C)              mp->math->md_from_div(&(A),&(B),&(C))
# define  set_number_from_mul(A,B,C)              mp->math->md_from_mul(&(A),&(B),&(C))
# define  number_int_div(A,C)                     mp->math->md_from_int_div(&(A),&(A),C)
# define  set_number_from_int_mul(A,B,C)          mp->math->md_from_int_mul(&(A),&(B),C)
# define  set_number_to_unity(A)                  mp->math->md_clone(&(A), &unity_t)
# define  set_number_to_zero(A)                   mp->math->md_clone(&(A), &zero_t)
# define  set_number_to_inf(A)                    mp->math->md_clone(&(A), &inf_t)
# define  set_number_to_negative_inf(A)           mp->math->md_clone(&(A), &negative_inf_t)
# define  old_set_number_to_neg_inf(A)	           do { set_number_to_inf(A); number_negate(A); } while (0)
# define  init_randoms(A)                         mp->math->md_init_randoms(mp,A)
# define  print_number(A)                         mp->math->md_print(mp,&(A))
# define  number_tostring(A)                      mp->math->md_tostring(mp,&(A))
# define  make_scaled(R,A,B)                      mp->math->md_make_scaled(mp,&(R),&(A),&(B))
# define  take_scaled(R,A,B)                      mp->math->md_take_scaled(mp,&(R),&(A),&(B))
# define  make_fraction(R,A,B)                    mp->math->md_make_fraction(mp,&(R),&(A),&(B))
# define  take_fraction(R,A,B)                    mp->math->md_take_fraction(mp,&(R),&(A),&(B))
# define  pyth_add(R,A,B)                         mp->math->md_pyth_add(mp,&(R),&(A),&(B))
# define  pyth_sub(R,A,B)                         mp->math->md_pyth_sub(mp,&(R),&(A),&(B))
# define  power_of(R,A,B)                         mp->math->md_power_of(mp,&(R),&(A),&(B))
# define  n_arg(R,A,B)                            mp->math->md_n_arg(mp,&(R),&(A),&(B))
# define  m_log(R,A)                              mp->math->md_m_log(mp,&(R),&(A))
# define  m_exp(R,A)                              mp->math->md_m_exp(mp,&(R),&(A))
# define  m_unif_rand(R,A)                        mp->math->md_m_unif_rand(mp,&(R),&(A))
# define  m_norm_rand(R)                          mp->math->md_m_norm_rand(mp,&(R))
# define  velocity(R,A,B,C,D,E)                   mp->math->md_velocity(mp,&(R),&(A),&(B),&(C),&(D),&(E))
# define  ab_vs_cd(A,B,C,D)                       mp->math->md_ab_vs_cd(&(A),&(B),&(C),&(D))
# define  crossing_point(R,A,B,C)                 mp->math->md_crossing_point(mp,&(R),&(A),&(B),&(C))
# define  n_sin_cos(A,S,C)                        mp->math->md_sin_cos(mp,&(A),&(S),&(C))
# define  square_rt(A,S)                          mp->math->md_sqrt(mp,&(A),&(S))
# define  slow_add(R,A,B)                         mp->math->md_slow_add(mp,&(R),&(A),&(B))
# define  round_unscaled(A)                       mp->math->md_round_unscaled(&(A))
# define  floor_scaled(A)                         mp->math->md_floor_scaled(&(A))
# define  fraction_to_round_scaled(A)             mp->math->md_fraction_to_round_scaled(&(A))
# define  number_to_int(A)                        mp->math->md_to_int(&(A))
# define  number_to_boolean(A)                    mp->math->md_to_boolean(&(A))
# define  number_to_scaled(A)                     mp->math->md_to_scaled(&(A))
# define  number_to_double(A)                     mp->math->md_to_double(&(A))
# define  number_negate(A)                        mp->math->md_negate(&(A))
# define  number_add(A,B)                         mp->math->md_add(&(A),&(B))
# define  number_subtract(A,B)                    mp->math->md_subtract(&(A),&(B))
# define  number_half(A)                          mp->math->md_half(&(A))
# define  number_double(A)                        mp->math->md_do_double(&(A))
# define  number_add_scaled(A,B)                  mp->math->md_add_scaled(&(A),B)
# define  number_multiply_int(A,B)                mp->math->md_multiply_int(&(A),B)
# define  number_divide_int(A,B)                  mp->math->md_divide_int(&(A),B)
# define  number_abs(A)                           mp->math->md_abs(&(A))
# define  number_modulo(A,B)                      mp->math->md_modulo(&(A),&(B))
# define  number_nonequalabs(A,B)                 mp->math->md_nonequalabs(&(A),&(B))
# define  number_odd(A)                           mp->math->md_odd(&(A))
# define  number_equal(A,B)                       mp->math->md_equal(&(A),&(B))
# define  number_greater(A,B)                     mp->math->md_greater(&(A),&(B))
# define  number_less(A,B)                        mp->math->md_less(&(A),&(B))
# define  number_clone(A,B)                       mp->math->md_clone(&(A),&(B))
# define  number_negated_clone(A,B)               mp->math->md_negated_clone(&(A),&(B))
# define  number_abs_clone(A,B)                   mp->math->md_abs_clone(&(A),&(B))
# define  number_swap(A,B)                        mp->math->md_swap(&(A),&(B));
# define  convert_scaled_to_angle(A)              mp->math->md_scaled_to_angle(&(A));
# define  convert_angle_to_scaled(A)              mp->math->md_angle_to_scaled(&(A));
# define  convert_fraction_to_scaled(A)           mp->math->md_fraction_to_scaled(&(A));
# define  convert_scaled_to_fraction(A)           mp->math->md_scaled_to_fraction(&(A));
# define  number_zero(A)           number_equal(A, zero_t)
# define  number_infinite(A)       number_equal(A, inf_t)
# define  number_unity(A)          number_equal(A, unity_t)
# define  number_negative(A)       number_less(A, zero_t)
# define  number_nonnegative(A)    (! number_negative(A))
# define  number_positive(A)       number_greater(A, zero_t)
# define  number_nonpositive(A)    (! number_positive(A))
# define  number_nonzero(A)        (! number_zero(A))
# define  number_greaterequal(A,B) (! number_less(A,B))
# define  number_lessequal(A,B)    (! number_greater(A,B))
# define  mp_path_ptr(A)       (A)->path
# define  mp_pen_ptr(A)        (A)->pen
# define  mp_dash_ptr(A)       ((mp_shape_node) (A))->dash
# define  mp_line_cap(A)       ((mp_shape_node) (A))->linecap
# define  mp_line_join(A)      ((mp_shape_node) (A))->linejoin
# define  mp_miterlimit(A)     ((mp_shape_node) (A))->miterlimit
# define  mp_set_linecap(A,B)  ((mp_shape_node) (A))->linecap = (unsigned char) (B)
# define  mp_set_linejoin(A,B) ((mp_shape_node) (A))->linejoin = (unsigned char) (B)
# define  mp_pre_script(A)     ((mp_shape_node) (A))->pre_script
# define  mp_post_script(A)    ((mp_shape_node) (A))->post_script
# define  mp_color_model(A)    ((mp_shape_node) (A))->color_model
# define  mp_stacking(A)       ((mp_shape_node) (A))->stacking
# define  mp_pen_type(A)       ((mp_shape_node) (A))->pen_type
# define  mp_cyan_color(A)     ((mp_shape_node) (A))->cyan
# define  mp_magenta_color(A)  ((mp_shape_node) (A))->magenta
# define  mp_yellow_color(A)   ((mp_shape_node) (A))->yellow
# define  mp_black_color(A)    ((mp_shape_node) (A))->black
# define  mp_red_color(A)      ((mp_shape_node) (A))->red
# define  mp_green_color(A)    ((mp_shape_node) (A))->green
# define  mp_blue_color(A)     ((mp_shape_node) (A))->blue
# define  mp_gray_color(A)     ((mp_shape_node) (A))->grey
# define  mp_grey_color(A)     ((mp_shape_node) (A))->grey
# define  mp_has_color(A)  ((A)->type <  mp_start_clip_node_type)
# define  mp_has_script(A) ((A)->type <= mp_start_bounds_node_type)
# define  mp_has_pen(A)    ((A)->type <= mp_stroked_node_type)
# define  mp_is_start_or_stop(A) ((A)->type >= mp_start_clip_node_type)
# define  mp_is_stop(A)          ((A)->type >= mp_stop_clip_node_type)
# define  mp_get_dash_list(A)   (mp_dash_node) (((mp_dash_node) (A))->link)
# define  mp_set_dash_list(A,B) ((mp_dash_node) (A))->link = (mp_node) ((B))
# define  mp_bblast(A)    ((mp_edge_header_node) (A))->bblast
# define  mp_edge_list(A) ((mp_edge_header_node) (A))->list
# define  mp_obj_tail(A)       ((mp_edge_header_node) (A))->obj_tail
# define  mp_edge_ref_count(A) ((mp_edge_header_node) (A))->ref_count
# define  mp_add_edge_ref(mp,A) mp_edge_ref_count((A)) += 1
# define  mp_delete_edge_ref(mp,A) { \
    if (mp_edge_ref_count((A)) == 0) { \
        mp_toss_edges(mp, (mp_edge_header_node) (A)); \
    } else { \
        mp_edge_ref_count((A)) -= 1; \
    } \
}
# define  mp_dash_info(A) ((mp_dash_node) (A))->dash_info
# define  zero_off 0
# define  near_zero_angle_k mp->math->md_near_zero_angle_t
# define  stack_1(A)   mp->bisect_stack[(A)]
# define  stack_2(A)   mp->bisect_stack[(A)+1]
# define  stack_3(A)   mp->bisect_stack[(A)+2]
# define  stack_min(A) mp->bisect_stack[(A)+3]
# define  stack_max(A) mp->bisect_stack[(A)+4]
# define  int_packets  20
# define  u_packet(A) ((A)- 5)
# define  v_packet(A) ((A)-10)
# define  x_packet(A) ((A)-15)
# define  y_packet(A) ((A)-20)
# define  l_packets (mp->bisect_ptr-int_packets)
# define  r_packets mp->bisect_ptr
# define  ul_packet u_packet(l_packets)
# define  vl_packet v_packet(l_packets)
# define  xl_packet x_packet(l_packets)
# define  yl_packet y_packet(l_packets)
# define  ur_packet u_packet(r_packets)
# define  vr_packet v_packet(r_packets)
# define  xr_packet x_packet(r_packets)
# define  yr_packet y_packet(r_packets)
# define  u1l stack_1(ul_packet)
# define  u2l stack_2(ul_packet)
# define  u3l stack_3(ul_packet)
# define  v1l stack_1(vl_packet)
# define  v2l stack_2(vl_packet)
# define  v3l stack_3(vl_packet)
# define  x1l stack_1(xl_packet)
# define  x2l stack_2(xl_packet)
# define  x3l stack_3(xl_packet)
# define  y1l stack_1(yl_packet)
# define  y2l stack_2(yl_packet)
# define  y3l stack_3(yl_packet)
# define  u1r stack_1(ur_packet)
# define  u2r stack_2(ur_packet)
# define  u3r stack_3(ur_packet)
# define  v1r stack_1(vr_packet)
# define  v2r stack_2(vr_packet)
# define  v3r stack_3(vr_packet)
# define  x1r stack_1(xr_packet)
# define  x2r stack_2(xr_packet)
# define  x3r stack_3(xr_packet)
# define  y1r stack_1(yr_packet)
# define  y2r stack_2(yr_packet)
# define  y3r stack_3(yr_packet)
# define  stack_dx  mp->bisect_stack[mp->bisect_ptr]
# define  stack_dy  mp->bisect_stack[mp->bisect_ptr+1]
# define  stack_tol mp->bisect_stack[mp->bisect_ptr+2]
# define  stack_uv  mp->bisect_stack[mp->bisect_ptr+3]
# define  stack_xy  mp->bisect_stack[mp->bisect_ptr+4]
# define  int_increment (int_packets+int_packets+5)
# define  max_patience 5000
# define  half(A) ((A)/2)
# define  intersection_run_shift 8
# define  mp_get_indep_scale(A)   ((mp_value_node) (A))->data.indep.scale
# define  mp_set_indep_scale(A,B) ((mp_value_node) (A))->data.indep.scale = (B)
# define  mp_get_indep_value(A)   ((mp_value_node) (A))->data.indep.serial
# define  mp_set_indep_value(A,B) ((mp_value_node) (A))->data.indep.serial = (B)
# define  mp_get_dep_value(A)   ((mp_value_node) (A))->data.n
# define  mp_get_dep_list(A)    ((mp_value_node) (A))->attr_head
# define  mp_get_prev_dep(A)    ((mp_value_node) (A))->subscr_head
# define  mp_get_dep_info(A)    do_get_dep_info(mp, (A))
# define  mp_set_dep_value(A,B) do_set_dep_value(mp,(A),&(B))
# define  mp_set_dep_list(A,B)  ((mp_value_node) (A))->attr_head = (mp_node) (B)
# define  mp_set_prev_dep(A,B)  ((mp_value_node) (A))->subscr_head = (mp_node) (B)
# define  mp_set_dep_info(A,B)  ((mp_value_node) (A))->parent = (mp_node) (B)
# define  independent_needing_fix 0
# define  fraction_threshold_k      mp->math->md_fraction_threshold_t
# define  half_fraction_threshold_k mp->math->md_half_fraction_threshold_t
# define  scaled_threshold_k        mp->math->md_scaled_threshold_t
# define  half_scaled_threshold_k   mp->math->md_half_scaled_threshold_t
# define  p_over_v_threshold_k mp->math->md_p_over_v_threshold_t
# define  independent_being_fixed 1
# define  two_to_the(A) (1<<(unsigned)(A))
# define  cur_cmd               mp->cur_mod_->command
# define  cur_mod               number_to_scaled(mp->cur_mod_->data.n)
# define  cur_mod_number        mp->cur_mod_->data.n
# define  cur_mod_node          mp->cur_mod_->data.node
# define  cur_mod_str           mp->cur_mod_->data.str
# define  cur_sym               mp->cur_mod_->data.sym
# define  cur_sym_mod           mp->cur_mod_->name_type
# define  set_cur_cmd(A)        mp->cur_mod_->command = (A)
# define  set_cur_mod(A)        set_number_from_scaled(mp->cur_mod_->data.n, (A))
# define  set_cur_mod_number(A) number_clone(mp->cur_mod_->data.n, (A))
# define  set_cur_mod_node(A)   mp->cur_mod_->data.node = (A)
# define  set_cur_mod_str(A)    mp->cur_mod_->data.str = (A)
# define  set_cur_sym(A)        mp->cur_mod_->data.sym = (A)
# define  set_cur_sym_mod(A)    mp->cur_mod_->name_type = (A)
# define  iindex mp->cur_input.index_field
# define  start  mp->cur_input.start_field
# define  limit  mp->cur_input.limit_field
# define  name   mp->cur_input.name_field
# define  is_term    (mp_string) 0
# define  is_read    (mp_string) 1
# define  is_scantok (mp_string) 2
# define  max_spec_src is_scantok
# define  terminal_input (name == is_term)
# define  cur_file       mp->input_file[iindex]
# define  line           mp->line_stack[iindex]
# define  nloc   mp->cur_input.nloc_field
# define  nstart mp->cur_input.nstart_field
# define  token_type  iindex
# define  token_state (iindex<=mp_macro_text)
# define  file_state  (iindex>mp_macro_text)
# define  param_start limit
# define  get_t_next(mp) do { \
    mp_get_next(mp); \
    if (cur_cmd <= mp_max_pre_command) { \
        mp_t_next(mp); \
    } \
} while (0)
# define  mp_if_line_field(A) ((mp_if_node) (A))->if_line_field
# define  MP_VOID             (mp_node) (1)
# define  MP_PROGRESSION_FLAG (mp_node) (2)
# define  cur_exp_value_boolean number_to_int(mp->cur_exp.data.n)
# define  cur_exp_value_number  mp->cur_exp.data.n
# define  cur_exp_node          mp->cur_exp.data.node
# define  cur_exp_str           mp->cur_exp.data.str
# define  cur_exp_knot          mp->cur_exp.data.p
# define  min_tension three_quarter_unit_t
# define  mp_floor(a)  ((a) >= 0 ? (int) (a) : -(int) (-(a)))
# define  bezier_error (720*(256*256*16))+1
# define  mp_sign(v)   ((v) > 0 ? 1 : ((v)<0 ? -1 : 0 ))
# define  mp_out(A)    (double)((A)/16)
# define  p_nextnext mp_next_knot(mp_next_knot(p))
# define  p_next mp_next_knot(p)
# define  equation_threshold_k mp->math->md_equation_threshold_t
# define  make_cp_a_colored_object(cp,p) do { \
    cp = p; \
    while (cp != NULL) { \
        if (mp_has_color(cp)) { \
            break; \
        } else { \
            cp = cp->link; \
        } \
    } \
} while (0)
# define  set_color_val(A,B) do { \
    if (number_negative(A)) { \
        set_number_to_zero(A); \
    } else if (number_greater(A,unity_t)) { \
        set_number_to_unity(A); \
    } else { \
        number_clone(A, (B)); \
    } \
} while (0)
# define  message_code     0
# define  err_message_code 1
# define  err_help_code    2
# define  max_integer 0x7FFFFFFF
# define  gr_next_knot(A)      (A)->next
# define  gr_originator(A)     (A)->originator
# define  mp_knotstate(A)      (A)->state
# define  gr_type(A)           (A)->type
# define  gr_link(A)           (A)->next
# define  gr_color_model(A)    (A)->color_model
# define  gr_red_val(A)        (A)->color.red
# define  gr_green_val(A)      (A)->color.green
# define  gr_blue_val(A)       (A)->color.blue
# define  gr_cyan_val(A)       (A)->color.cyan
# define  gr_magenta_val(A)    (A)->color.magenta
# define  gr_yellow_val(A)     (A)->color.yellow
# define  gr_black_val(A)      (A)->color.black
# define  gr_grey_val(A)       (A)->color.gray
# define  gr_path_ptr(A)       (A)->path
# define  gr_htap_ptr(A)       (A)->htap
# define  gr_pen_ptr(A)        (A)->pen
# define  gr_linejoin_val(A)   (A)->linejoin
# define  gr_linecap_val(A)    (A)->linecap
# define  gr_stacking_val(A)   (A)->stacking
# define  gr_miterlimit_val(A) (A)->miterlimit
# define  gr_pre_script(A)     (A)->pre_script
# define  gr_post_script(A)    (A)->post_script
# define  gr_pre_length(A)     (A)->pre_length
# define  gr_post_length(A)    (A)->post_length
# define  gr_dash_ptr(A)       (A)->dash
# define  mp_gr_export_color(q,p) \
if (mp_color_model(p) == mp_uninitialized_model) { \
    gr_color_model(q) = (unsigned char) (number_to_scaled(internal_value(mp_default_color_model_internal))/65536); \
    gr_cyan_val(q)    = 0; \
    gr_magenta_val(q) = 0; \
    gr_yellow_val(q)  = 0; \
    gr_black_val(q)   = gr_color_model(q) == mp_cmyk_model ? (number_to_scaled(unity_t)/65536.0) : 0; \
} else { \
    gr_color_model(q) = (unsigned char) mp_color_model(p); \
    gr_cyan_val(q)    = number_to_double(p->cyan); \
    gr_magenta_val(q) = number_to_double(p->magenta); \
    gr_yellow_val(q)  = number_to_double(p->yellow); \
    gr_black_val(q)   = number_to_double(p->black); \
}
# define  mp_gr_export_scripts(q,p) \
if (mp_pre_script (p)) { \
    gr_pre_script(q) = mp_strndup((const char *) mp_pre_script(p)->str, mp_pre_script(p)->len); \
    gr_pre_length(q) = mp_pre_script(p)->len; \
} \
if (mp_post_script(p)) { \
    gr_post_script(q) = mp_strndup((const char *) mp_post_script(p)->str, mp_post_script(p)->len); \
    gr_post_length(q) = mp_post_script(p)->len; \
}
 MP_options *mp_options    (void);
 MP          mp_initialize (MP_options * opt);
 static char *mp_find_file    (MP mp, const char *fname, const char *fmode, int ftype);
 static void *mp_open_file    (MP mp, const char *fname, const char *fmode, int ftype);
 static char *mp_read_file    (MP mp, void *f, size_t * size);
 static void  mp_close_file   (MP mp, void *f);
 static void  mp_write_file   (MP mp, void *f, const char *s);
 static char *mp_run_script   (MP mp, const char *str, size_t len, int n);
 static void  mp_run_internal (MP mp, int action, int n, int type, const char *iname);
 static void  mp_run_logger   (MP mp, int target, const char *s, size_t l);
 static int   mp_run_overload (MP mp, int property, const char *, int);
 static void  mp_run_error    (MP mp, const char *, const char *, int);
 static void  mp_run_warning  (MP mp, const char *);
 static char *mp_make_text    (MP mp, const char *str, size_t len, int mode);
 static void mp_print_str    (MP mp, const char *s);
 static void mp_print_nl     (MP mp, const char *s);
 static void mp_print_fmt    (MP mp, const char *s, ...);
 static void mp_print_ln     (MP mp);
 static void mp_print_chr    (MP mp, unsigned char k);
 static void mp_print_mp_str (MP mp, mp_string s);
 static void mp_print_nl     (MP mp, const char *s);
 static void mp_print_two    (MP mp, mp_number *x, mp_number *y);
 static void mp_print_nl_only (MP mp);
 static void mp_print_int (MP mp, int n);
 static void mp_get_next           (MP mp);
 static void mp_begin_file_reading (MP mp);
 static void mp_free_node          (MP mp, mp_node p, size_t siz);
 static void mp_free_symbolic_node (MP mp, mp_node p);
 static void mp_free_value_node    (MP mp, mp_node p);
 static void mp_print_type (MP mp, int t);
 static void mp_fix_date_and_time (MP mp);
 static void mp_begin_diagnostic (MP mp);
 static void mp_end_diagnostic   (MP mp, int blank_line);
 static void mp_print_diagnostic (MP mp, const char *s, const char *t, int nuline);
 static int   mp_compare_symbols_entry (void *p, const void *pa, const void *pb);
 static void *mp_copy_symbols_entry    (const void *p);
 static void *mp_delete_symbols_entry  (void *p);
 static mp_sym new_symbols_entry (MP mp, unsigned char *nam, size_t len);
 inline static void do_set_value_sym    (MP mp, mp_token_node A, mp_sym B);
 inline static void do_set_value_number (MP mp, mp_token_node A, mp_number *B);
 inline static void do_set_value_str    (MP mp, mp_token_node A, mp_string B);
 inline static void do_set_value_node   (MP mp, mp_token_node A, mp_node B);
 inline static void do_set_value_knot   (MP mp, mp_token_node A, mp_knot B);
 static void mp_free_token_node (MP mp, mp_node p);
 static void mp_flush_token_list (MP mp, mp_node p);
 static void mp_show_token_list       (MP mp, mp_node p, mp_node q);
 static void mp_show_token_list_space (MP mp, mp_node p, mp_node q);
 static void mp_print_capsule (MP mp, mp_node p);
 static mp_node mp_do_get_subscr_head (MP mp, mp_value_node A);
 static mp_node mp_do_get_attribute_head   (MP mp, mp_value_node A);
 static void    mp_do_set_attribute_head   (MP mp, mp_value_node A, mp_node d);
 static void    mp_do_set_subscr_head (MP mp, mp_value_node A, mp_node d);
 static mp_node mp_new_value_node (MP mp);
 static void mp_print_variable_name (MP mp, mp_node p);
 static void mp_flush_cur_exp (MP mp, mp_value v);
 static void mp_flush_below_variable (MP mp, mp_node p);
 static void mp_pr_path (MP mp, mp_knot h);
 static void mp_print_path (MP mp, mp_knot h, const char *s, int nuline);
 static mp_knot mp_new_knot (MP mp);
 static mp_gr_knot mp_gr_new_knot (MP mp);
 static void mp_toss_knot_list (MP mp, mp_knot p);
 static void mp_toss_knot      (MP mp, mp_knot p);
 static void mp_free_knot      (MP mp, mp_knot p);
 static void mp_reallocate_paths (MP mp, int l);
 static void mp_solve_choices (MP mp, mp_knot p, mp_knot q, int n);
 static void mp_reduce_angle (MP mp, mp_number *a);
 static void mp_curl_ratio (MP mp, mp_number *ret, mp_number *gamma, mp_number *a_tension, mp_number *b_tension);
 static void mp_set_controls (MP mp, mp_knot p, mp_knot q, int k);
 static void mp_solve_rising_cubic (MP mp, mp_number *ret, mp_number *a_orig, mp_number *b_orig, mp_number *c_orig, mp_number *x_orig);
 static void mp_pr_pen (MP mp, mp_knot h);
 static void mp_print_pen (MP mp, mp_knot h, const char *s, int nuline);
 static mp_knot mp_convex_hull (MP mp, mp_knot h);
 void mp_simplify_path (MP mp, mp_knot h);
 static void mp_move_knot (MP mp, mp_knot p, mp_knot q);
 static void mp_sqrt_det (MP mp, mp_number *ret, mp_number *a_orig, mp_number *b_orig, mp_number *c_orig, mp_number *d_orig);
 static void                mp_flush_dash_list (MP mp, mp_edge_header_node h);
 static mp_edge_header_node mp_toss_gr_object  (MP mp, mp_node p);
 static void                mp_toss_edges      (MP mp, mp_edge_header_node h);
 static mp_edge_header_node mp_copy_objects (MP mp, mp_node p, mp_node q);
 static void mp_print_edges (MP mp, mp_node h, const char *s, int nuline);
 static void mp_print_obj_color (MP mp, mp_node p);
 static void mp_dash_offset (MP mp, mp_number *x, mp_dash_node h);
 static void mp_x_retrace_error (MP mp);
 static void mp_set_bbox (MP mp, mp_edge_header_node h, int top_level);
 static void    mp_split_cubic      (MP mp, mp_knot p, mp_number *t);
 static mp_knot mp_split_cubic_knot (MP mp, mp_knot p, mp_number *t);
 static void mp_remove_cubic (MP mp, mp_knot p);
 static mp_knot mp_pen_walk (MP mp, mp_knot w, int k);
 static void mp_fin_offset_prep (MP mp, mp_knot p, mp_knot w, mp_number *x0, mp_number *x1, mp_number *x2, mp_number *y0, mp_number *y1, mp_number *y2, int rise, int turn_amt);
 static int mp_get_turn_amt (MP mp, mp_knot w, mp_number *dx, mp_number *dy, int ccw);
 static mp_knot mp_insert_knot (MP mp, mp_knot q, mp_number *x, mp_number *y);
 static void mp_set_min_max (MP mp, int v);
 static void mp_new_indep (MP mp, mp_node p);
 inline static mp_node do_get_dep_info  (MP mp, mp_value_node p);
 inline static void    do_set_dep_value (MP mp, mp_value_node p, mp_number *q);
 static void mp_free_dep_node (MP mp, mp_value_node p);
 static void mp_print_dependency (MP mp, mp_value_node p, int t);
 static mp_value_node mp_p_plus_fq (MP mp, mp_value_node p, mp_number *f, mp_value_node q, mp_variable_type t, mp_variable_type tt);
 static mp_value_node mp_p_over_v (MP mp, mp_value_node p, mp_number *v, int t0, int t1);
 static void mp_val_too_big (MP mp, mp_number *x);
 static void mp_make_known (MP mp, mp_value_node p, mp_value_node q);
 static void mp_fix_dependencies (MP mp);
 static void mp_ring_delete (MP mp, mp_node p);
 static void mp_exclaim_redundant_equation (MP mp);
 static const char *mp_cmd_mod_string (MP mp, int c, int m);
 static void        mp_print_cmd_mod  (MP mp, int c, int m);
 static void mp_reallocate_input_stack (MP mp, int newsize);
 static int mp_true_line (MP mp);
 static void mp_push_input (MP mp);
 static void mp_pop_input (MP mp);
 static void mp_back_input (MP mp);
 static void mp_back_error (MP mp, const char *msg, const char *hlp) ;
 static void mp_runaway (MP mp);
 static void mp_firm_up_the_line (MP mp);
 static int mp_move_to_next_line (MP mp);
 static void mp_t_next (MP mp);
 static void mp_scan_primary     (MP mp);
 static void mp_scan_secondary   (MP mp);
 static void mp_scan_tertiary    (MP mp);
 static void mp_scan_expression  (MP mp);
 static void mp_scan_suffix      (MP mp);
 static void mp_pass_text        (MP mp);
 static void mp_conditional      (MP mp);
 static void mp_start_input      (MP mp);
 static void mp_begin_iteration  (MP mp);
 static void mp_resume_iteration (MP mp);
 static void mp_stop_iteration   (MP mp);
 static void check_script_result (MP mp, char *s);
 static void mp_get_x_next (MP mp);
 static void mp_macro_call (MP mp, mp_node def_ref, mp_node arg_list, mp_sym macro_name);
 static void mp_print_macro_name (MP mp, mp_node a, mp_sym n);
 static void mp_print_arg (MP mp, mp_node q, int n, int b, int bb);
 static void mp_scan_text_arg (MP mp, mp_sym l_delim, mp_sym r_delim);
 static void mp_push_condition_stack (MP mp);
 static void mp_pop_condition_stack  (MP mp);
 static void mp_begin_name (MP mp);
 static int  mp_more_name  (MP mp, unsigned char c);
 static void mp_end_name   (MP mp);
 static void mp_open_write_file (MP mp, char *s, int n);
 static void mp_set_cur_exp_knot          (MP mp, mp_knot n);
 static void mp_set_cur_exp_node          (MP mp, mp_node n);
 static void mp_set_cur_exp_value_boolean (MP mp, int b);
 static void mp_set_cur_exp_value_scaled  (MP mp, int s);
 static void mp_set_cur_exp_value_number  (MP mp, mp_number *n);
 static void mp_set_cur_exp_str           (MP mp, mp_string s);
 static mp_node mp_stash_cur_exp (MP mp);
 static void mp_print_dp        (MP mp, int t, mp_value_node p, int verbosity);
 static void mp_unstash_cur_exp (MP mp, mp_node p);
 static void mp_print_exp       (MP mp, mp_node p, int verbosity);
 static void mp_print_big_node  (MP mp, mp_node p, int verbosity);
 static void mp_disp_err (MP mp, mp_node p);
 static void mp_recycle_value (MP mp, mp_node p);
 static void mp_recycle_independent_value (MP mp, mp_node p);
 static void mp_show_transformed_dependency (MP mp, mp_number *v, mp_variable_type t, mp_node p);
 static void mp_known_pair (MP mp);
 static void do_boolean_error (MP mp);
 static void push_of_path_result (MP mp, int what, mp_knot p, mp_number i, mp_number n);
 static mp_knot mp_simple_knot(MP mp, mp_number *x, mp_number *y);
 static mp_knot mp_complex_knot(MP mp, mp_knot o);
 static int mp_pict_color_type (MP mp, int c);
 static void mp_bad_color_part (MP mp, int c);
 static mp_edge_header_node mp_scale_edges (MP mp, mp_number *se_sf, mp_edge_header_node se_pic);
 static void mp_path_length (MP mp, mp_number *n);
 static void mp_path_no_length (MP mp, mp_number *n);
 static void mp_pair_value (MP mp, mp_number *x, mp_number *y);
 static void mp_do_type_declaration (MP mp);
 static void mp_do_max_knot_pool (MP mp);
 static void mp_do_random_seed (MP mp);
 static void mp_do_protection (MP mp);
 static void mp_do_property (MP mp);
 static void mp_def_delims (MP mp);
 static void mp_do_statement (MP mp);
 static void mp_do_interim   (MP mp);
 static void mp_do_let (MP mp);
 static void mp_do_show (MP mp);
 static void mp_disp_token (MP mp);
 static void mp_do_show_token (MP mp);
 static void mp_do_show_stats (MP mp);
 static void mp_disp_var (MP mp, mp_node p);
 static void mp_do_show_var (MP mp);
 static void mp_do_show_dependencies (MP mp);
 static void mp_do_show_whatever (MP mp);
 static void mp_scan_with_list (MP mp, mp_node p, mp_node pp);
 static mp_edge_header_node mp_find_edges_var (MP mp, mp_node t);
 static mp_node mp_start_draw_cmd (MP mp, int sep);
 static void mp_do_bounds (MP mp);
 static void mp_do_add_to (MP mp);
 static void mp_ship_out (MP mp, mp_node h);

 static void mp_do_ship_out (MP mp);
 static void mp_do_message    (MP mp);
 static void mp_no_string_err (MP mp, const char *s);
 static void mp_do_write        (MP mp);
 static void mp_do_write_string (MP mp, mp_string t);

 static void mp_make_eq (MP mp, mp_node lhs);

 static void mp_do_equation (MP mp);
 static void mp_do_assignment (MP mp);
 static void mp_try_eq (MP mp, mp_node l, mp_node r);
 static mp_node mp_scan_declared_variable (MP mp);
 static void mp_check_delimiter (MP mp, mp_sym l_delim, mp_sym r_delim);
 static void mp_do_new_internal (MP mp);
 static void mp_shipout_backend (MP mp, void *h);
 static void mp_close_files               (MP mp);
 static void mp_close_files_and_terminate (MP mp);
 static void mp_final_cleanup (MP mp);
 static void mp_init_prim     (MP mp);
 static void mp_init_tab      (MP mp);

void mp_jump_out (MP mp)
{
    if (mp->internal != NULL && mp->history < mp_system_error_stop) {
        mp_close_files_and_terminate(mp);
    }
    longjmp(*(mp->jump_buf), 1);
}
void mp_warn (MP mp, const char *msg)
{
    int selector = mp->selector;
    mp_normalize_selector(mp);
    mp_print_nl(mp, "Warning: ");
    mp_print_str(mp, msg);
    mp_print_ln(mp);
    mp->selector = selector;
}
void mp_fatal_error (MP mp, const char *s)
{
    mp_normalize_selector(mp);
    if (mp->interaction == mp_error_stop_mode) {
        mp->interaction = mp_scroll_mode;
    }
    mp_error(mp, "Emergency stop", s);
    mp->history = mp_fatal_error_stop;
    mp_jump_out(mp);
}
void mp_confusion (MP mp, const char *s)
{
    char msg[256];
    const char *hlp = NULL;
    mp_normalize_selector(mp);
    if (mp->history < mp_error_message_issued) {
        mp_snprintf(msg, 256, "This can't happen (%s)", s);
        hlp =
            "I'm broken. Please show this to someone who can fix can fix it and try\n"
            "again";
    } else {
        hlp =
            "One of your faux pas seems to have wounded me deeply ... in fact, I'm barely\n"
            "conscious. Please fix it and try again.";
        mp_snprintf(msg, 256, "I can't go on meeting you like this");
    }
    if (mp->interaction == mp_error_stop_mode) {
        mp->interaction = mp_scroll_mode;
    }
    mp_error(mp, msg, hlp);
    mp->history=mp_fatal_error_stop;
    mp_jump_out(mp);
}

MP_options *mp_options (void)
{
    MP_options *opt = mp_memory_clear_allocate(sizeof(MP_options));
    return opt;
}

static MP mp_do_new (jmp_buf *buf)
{
    MP mp = mp_memory_clear_allocate(sizeof(MP_instance));
    if (mp == NULL) {
        mp_memory_free(buf);
        return NULL;
    } else {
        mp->jump_buf = buf;
        return mp;
    }
}

static void mp_free (MP mp)
{
    mp_memory_free(mp->banner);
    mp_memory_free(mp->buffer);
    mp_dealloc_strings(mp);
    for (int i = 0; i < 55; i++) {
        free_number(mp->randoms[i]);
    }
    while (mp->value_nodes) {
        mp_node p = mp->value_nodes;
        mp->value_nodes = p->link;
        mp_free_node(mp, p, sizeof(mp_value_node_data));
    }
    while (mp->symbolic_nodes) {
        mp_node p = mp->symbolic_nodes;
        mp->symbolic_nodes = p->link;
        mp_free_node(mp, p, sizeof(mp_node_data));
    }
    while (mp->pair_nodes) {
        mp_node p = mp->pair_nodes;
        mp->pair_nodes = p->link;
        mp_free_node(mp, p, sizeof(mp_pair_node_data));
    }
    while (mp->token_nodes) {
        mp_node p = mp->token_nodes;
        mp->token_nodes = p->link;
        mp_free_node(mp, p, sizeof(mp_node_data));
    }
    while (mp->knot_nodes) {
        mp_knot p = mp->knot_nodes;
        mp->knot_nodes = p->next;
        mp_free_knot(mp, p);
    }
    if (mp->symbols != NULL) {
        avl_destroy (mp->symbols);
    }
    if (mp->frozen_symbols != NULL) {
        avl_destroy (mp->frozen_symbols);
    }
    for (int k = 0; k<mp->path_size; k++) {
        free_number(mp->delta_x[k]);
        free_number(mp->delta_y[k]);
        free_number(mp->delta[k]);
        free_number(mp->psi[k]);
    }
    mp_memory_free(mp->delta_x);
    mp_memory_free(mp->delta_y);
    mp_memory_free(mp->delta);
    mp_memory_free(mp->psi);
    for (int k = 0; k < mp->path_size; k++) {
        free_number(mp->theta[k]);
        free_number(mp->uu[k]);
        free_number(mp->vv[k]);
        free_number(mp->ww[k]);
    }
    mp_memory_free(mp->theta);
    mp_memory_free(mp->uu);
    mp_memory_free(mp->vv);
    mp_memory_free(mp->ww);
    free_number(mp->st);
    free_number(mp->ct);
    free_number(mp->sf);
    free_number(mp->cf);
    for (int i = 0; i <= mp_y_code; i++) {
        free_number(mp->bbmin[i]);
        free_number(mp->bbmax[i]);
    }
    for (int k = 0; k <= 7; k++) {
        free_number(mp->half_cos[k]);
        free_number(mp->d_cos[k]);
    }
    free_number(mp->cur_x);
    free_number(mp->cur_y);
    for (int i=0; i<bistack_size + 1; i++) {
        free_number(mp->bisect_stack[i]);
    }
    mp_memory_free(mp->bisect_stack);
    free_number(mp->cur_t);
    free_number(mp->cur_tt);
    free_number(mp->max_t);
    free_number(mp->delx);
    free_number(mp->dely);
    free_number(mp->appr_t);
    free_number(mp->appr_tt);
    mp_memory_free(mp->input_stack);
    mp_memory_free(mp->input_file);
    mp_memory_free(mp->line_stack);
    mp_memory_free(mp->param_stack);
    mp_memory_free(mp->cur_name);
    mp_memory_free(mp->job_name);
    for (int i = 0; i < mp_proto_dependent_type + 1; i++) {
        free_number(mp->max_c[i]);
    }
    for (int k = 0; k <= mp->max_internal; k++) {
        free_number(mp->internal[k].v.data.n);
        mp_memory_free(internal_name(k));
    }
    mp_memory_free(mp->internal);
    mp_close_files(mp);
    if (mp->rd_fname != NULL) {
        mp_memory_free(mp->rd_file);
        mp_memory_free(mp->rd_fname);
        mp->rd_file = NULL;
        mp->rd_fname = NULL;
    }
    if (mp->wr_fname != NULL) {
        mp_memory_free(mp->wr_file);
        mp_memory_free(mp->wr_fname);
        mp->wr_file = NULL;
        mp->wr_fname = NULL;
    }
    mp_memory_free(mp->term_in);
    mp->term_in = NULL;

    mp_memory_free(mp->jump_buf);
    mp_free_symbolic_node(mp, mp->spec_head);
    mp_free_symbolic_node(mp, mp->temp_head);
    mp_free_symbolic_node(mp, mp->hold_head);
    mp_free_value_node(mp, mp->end_attr);
    mp_free_node(mp, (mp_node) mp->null_dash, sizeof(mp_dash_node_data));
    mp_free_dep_node(mp, mp->dep_head);
    mp_free_symbolic_node(mp, mp->cur_mod_);
    mp_free_value_node(mp, mp->bad_vardef);
    free_number(mp->cur_exp.data.n);
    mp_free_value_node(mp, mp->temp_val);
    free_number(mp->txx);
    free_number(mp->txy);
    free_number(mp->tyx);
    free_number(mp->tyy);
    free_number(mp->tx);
    free_number(mp->ty);
    mp_free_value_node(mp, mp->inf_val);
    mp_free_value_node(mp, mp->zero_val);

    free_math();
    mp_memory_free(mp);
}

static void mp_do_initialize (MP mp)
{
    mp->int_ptr = max_given_internal;
    for (int k = '0'; k <= '9'; k++) {
        mp->char_class[k] = mp_digit_class;
    }
    for (int k = 'A'; k <= 'Z'; k++) {
        mp->char_class[k] = mp_letter_class;
    }
    for (int k = 'a'; k <= 'z'; k++) {
        mp->char_class[k] = mp_letter_class;
    }
    mp->char_class['.']  = mp_period_class;
    mp->char_class[' ']  = mp_space_class;
    mp->char_class['%']  = mp_percent_class;
    mp->char_class['"']  = mp_string_class;
    mp->char_class[',']  = mp_comma_class;
    mp->char_class[';']  = mp_semicolon_class;
    mp->char_class['(']  = mp_left_parenthesis_class;
    mp->char_class[')']  = mp_right_parenthesis_class;
    mp->char_class['_']  = mp_letter_class;
    mp->char_class['<']  = 10;
    mp->char_class['=']  = 10;
    mp->char_class['>']  = 10;
    mp->char_class[':']  = 10;
    mp->char_class['|']  = 10;
    mp->char_class['`']  = 11;
    mp->char_class['\''] = 11;
    mp->char_class['+']  = 12;
    mp->char_class['-']  = 12;
    mp->char_class['/']  = 13;
    mp->char_class['*']  = 13;
    mp->char_class['\\'] = 13;
    mp->char_class['^']  = 13;
    mp->char_class['!']  = 14;
    mp->char_class['?']  = 14;
    mp->char_class['#']  = mp_suffix_class;
    mp->char_class['&']  = mp_suffix_class;
    mp->char_class['@'] = mp_suffix_class;
    mp->char_class['$']  = mp_suffix_class;
    mp->char_class['^']  = 16;
    mp->char_class['~']  = 16;
    mp->char_class['[']  = mp_left_bracket_class;
    mp->char_class[']']  = mp_right_bracket_class;
    mp->char_class['{']  = mp_brace_class;
    mp->char_class['}']  = mp_brace_class;
    for (int k = 0; k < ' '; k++) {
        mp->char_class[k] = mp_invalid_class;
    }
    mp->char_class['\r'] = mp_space_class;
    mp->char_class['\n'] = mp_space_class;
    mp->char_class['\t'] = mp_space_class;
    mp->char_class['\f'] = mp_space_class;
    for (int k = 127; k <= 255; k++) {
        mp->char_class[k] = mp->utf8_mode ? mp_letter_class : mp_invalid_class;
    }
    if (mp->text_mode) {
        mp->char_class[2] = mp_string_class;
    }
    mp->save_ptr = NULL;
    for (int k = 0; k <= 7; k++) {
        new_fraction(mp->half_cos[k]);
        new_fraction(mp->d_cos[k]);
    }
    number_clone(mp->half_cos[0], fraction_half_t);
    number_clone(mp->half_cos[1], twentysixbits_sqrt2_t);
    number_clone(mp->half_cos[2], zero_t);
    number_clone(mp->d_cos[0],    twentyeightbits_d_t);
    number_clone(mp->d_cos[1],    twentysevenbits_sqrt2_d_t);
    number_clone(mp->d_cos[2],    zero_t);
    for (int k = 3; k <= 4; k++) {
        number_negated_clone(mp->half_cos[k], mp->half_cos[4 - k]);
        number_negated_clone(mp->d_cos[k], mp->d_cos[4 - k]);
    }
    for (int k = 5; k <= 7; k++) {
        number_clone(mp->half_cos[k], mp->half_cos[8 - k]);
        number_clone(mp->d_cos[k], mp->d_cos[8 - k]);
    }
    mp->spec_p1 = NULL;
    mp->spec_p2 = NULL;
    mp->fix_needed  = 0;
    mp->watch_coefs = 1;
    mp->expand_depth = 10000;
    mp->cond_ptr = NULL;
    mp->if_limit = mp_no_if_code;
    mp->cur_if   = 0;
    mp->if_line  = 0;
    mp->loop_ptr = NULL;
    mp->cur_name = mp_strdup("");
    memset(&mp->cur_exp.data, 0, sizeof(mp_value));
    new_number(mp->cur_exp.data.n);
    mp->var_flag = 0;
    mp->eof_line = mp_rtsl (mp, "\0", 1);
    mp->eof_line->refs = MAX_STR_REF;
    mp->eof_file = mp_rtsl (mp, "%", 1);
    mp->eof_file->refs = MAX_STR_REF;
    mp->every_job_sym = NULL;
    mp->long_help_seen = 0;
    mp->ten_pow[0] = 1;
    for (int i = 1; i <= 9; i++) {
        mp->ten_pow[i] = 10 * mp->ten_pow[i - 1];
    }
}

MP mp_initialize (MP_options * opt)
{
    MP mp;
    jmp_buf *buf = mp_memory_allocate(sizeof(jmp_buf));
    if (buf == NULL || setjmp(*buf) != 0) {
        return NULL;
    }
    mp = mp_do_new(buf);
    if (mp == NULL) {
        return NULL;
    }
    if (opt->job_name == NULL || ! *(opt->job_name)) {
        return NULL;
    }
    mp->job_name   = mp_strdup(opt->job_name);
    mp->userdata   = opt->userdata;
    mp->extensions = opt->extensions;
    mp->find_file       = mp_find_file;
    mp->open_file       = mp_open_file;
    mp->close_file      = mp_close_file;
    mp->write_file      = mp_write_file;
    mp->read_file       = mp_read_file;
    mp->run_script      = mp_run_script;
    mp->run_internal    = mp_run_internal;
    mp->run_logger      = mp_run_logger;
    mp->run_overload    = mp_run_overload;
    mp->run_error       = mp_run_error;
    mp->run_warning     = mp_run_warning;
    mp->make_text       = mp_make_text;
    mp->shipout_backend = mp_shipout_backend;
    mp->find_file_id    = 0;
    mp->run_script_id   = 0;
    mp->run_logger_id   = 0;
    mp->run_error_id    = 0;
    mp->run_warning_id  = 0;
    mp->run_overload_id = 0;
    mp->make_text_id    = 0;
    mp->open_file_id    = 0;


    mp->find_file       = opt->find_file       ? opt->find_file       : mp_find_file      ;
    mp->open_file       = opt->open_file       ? opt->open_file       : mp_open_file      ;
    mp->read_file       = opt->read_file       ? opt->read_file       : mp_read_file      ;
    mp->close_file      = opt->close_file      ? opt->close_file      : mp_close_file     ;
    mp->write_file      = opt->write_file      ? opt->write_file      : mp_write_file     ;
    mp->shipout_backend = opt->shipout_backend ? opt->shipout_backend : mp_shipout_backend;
    mp->run_script      = opt->run_script      ? opt->run_script      : mp_run_script     ;
    mp->run_internal    = opt->run_internal    ? opt->run_internal    : mp_run_internal   ;
    mp->run_logger      = opt->run_logger      ? opt->run_logger      : mp_run_logger     ;
    mp->run_overload    = opt->run_overload    ? opt->run_overload    : mp_run_overload   ;
    mp->run_error       = opt->run_error       ? opt->run_error       : mp_run_error      ;
    mp->run_warning     = opt->run_warning     ? opt->run_warning     : mp_run_warning    ;
    mp->make_text       = opt->make_text       ? opt->make_text       : mp_make_text      ;

    mp->find_file_id    = opt->find_file_id;
    mp->run_script_id   = opt->run_script_id;
    mp->run_internal_id = opt->run_internal_id;
    mp->run_logger_id   = opt->run_logger_id;
    mp->run_overload_id = opt->run_overload_id;
    mp->run_error_id    = opt->run_error_id;
    mp->run_warning_id  = opt->run_warning_id;
    mp->make_text_id    = opt->make_text_id;
    mp->open_file_id    = opt->open_file_id;

    if (opt->banner && *(opt->banner)) {
        mp->banner = mp_strdup(opt->banner);
    } else {
        mp->banner = mp_strdup(default_banner);
    }
    switch (opt->math_mode) {
        case mp_math_scaled_mode:
            mp->math = mp_initialize_scaled_math(mp);
            break;
        case mp_math_decimal_mode:
            mp->math = mp_initialize_decimal_math(mp);
            break;
        case mp_math_binary_mode:
            mp->math = mp_initialize_binary_math(mp);
            break;
        case mp_math_posit_mode:
            mp->math = mp_initialize_posit_math(mp);
            break;
        default:
            mp->math = mp_initialize_double_math(mp);
            break;
    }
    mp->param_size      = 4;
    mp->max_in_open     = 0;
    mp->halt_on_error   = opt->halt_on_error ? 1 : 0;
    mp->utf8_mode       = opt->utf8_mode     ? 1 : 0;
    mp->text_mode       = opt->text_mode     ? 1 : 0;
    mp->show_mode       = opt->show_mode     ? 1 : 0;
    mp->buf_size = 200;
    mp->buffer   = mp_memory_allocate((size_t) (mp->buf_size + 1) * sizeof(unsigned char));
    mp_initialize_strings(mp);
    mp->interaction = opt->interaction;
    if (mp->interaction == mp_unspecified_mode || mp->interaction > mp_silent_mode) {
        mp->interaction = mp_error_stop_mode;
    }
    if (mp->interaction < mp_unspecified_mode) {
        mp->interaction = mp_batch_mode;
    }
    mp->use_err_help = 0;
    mp->finished = 0;
    mp->arith_error = 0;
    mp->random_seed = opt->random_seed;
    for (int i = 0; i < 55; i++) {
        new_fraction(mp->randoms[i]);
    }
    mp->math_mode = opt->math_mode;
    mp->token_nodes        = NULL;
    mp->num_token_nodes    = 0;
    mp->pair_nodes         = NULL;
    mp->num_pair_nodes     = 0;
    mp->knot_nodes         = NULL;
    mp->max_knot_nodes     = max_num_knot_nodes;
    mp->num_knot_nodes     = 0;
    mp->value_nodes        = NULL;
    mp->num_value_nodes    = 0;
    mp->symbolic_nodes     = NULL;
    mp->num_symbolic_nodes = 0;

    mp->max_internal = 1000 + max_given_internal;
    mp->internal     = mp_memory_allocate((size_t) (mp->max_internal + 1) * sizeof(mp_internal));
    memset(mp->internal, 0, (size_t) (mp->max_internal + 1) * sizeof(mp_internal));
    for (int i = 1; i <= mp->max_internal; i++) {
        new_number(mp->internal[i].v.data.n);
    }
    for (int i = 1; i <= max_given_internal; i++) {
        set_internal_type(i, mp_known_type);
    }
    set_internal_type(mp_number_system_internal, mp_string_type);
    set_internal_type(mp_job_name_internal, mp_string_type);
    mp->symbols = avl_create(mp_compare_symbols_entry, mp_copy_symbols_entry, mp_delete_symbols_entry, mp_memory_allocate, mp_memory_free, NULL);
    mp->frozen_symbols = avl_create(mp_compare_symbols_entry, mp_copy_symbols_entry, mp_delete_symbols_entry, mp_memory_allocate, mp_memory_free, NULL);
    mp->bisect_stack = mp_memory_allocate((size_t) (bistack_size + 1) * sizeof(mp_number));
    for (int i=0; i<bistack_size + 1; i++) {
        new_number(mp->bisect_stack[i]);
    }
    mp->stack_size = 16;
    mp->input_stack = mp_memory_allocate((size_t) (mp->stack_size + 1) * sizeof(mp_in_state_record));
    mp_reallocate_input_stack(mp, mp_file_bottom_text + 4);
    mp->param_stack = mp_memory_allocate((size_t) (mp->param_size + 1) * sizeof(mp_node));
    mp->max_read_files  = 8;
    mp->rd_file         = mp_memory_allocate((size_t) (mp->max_read_files + 1) * sizeof(void *));
    mp->rd_fname        = mp_memory_allocate((size_t) (mp->max_read_files + 1) * sizeof(char *));
    mp->max_write_files = 8;
    mp->wr_file         = mp_memory_allocate((size_t) (mp->max_write_files + 1) * sizeof(void *));
    mp->wr_fname        = mp_memory_allocate((size_t) (mp->max_write_files + 1) * sizeof(char *));
    memset(mp->rd_fname, 0, sizeof(char *) * (mp->max_read_files  + 1));
    memset(mp->wr_fname, 0, sizeof(char *) * (mp->max_write_files + 1));

    mp_reallocate_paths(mp, 1000);
    mp->history = mp_fatal_error_stop;
    mp_do_initialize(mp);
    mp_init_tab(mp);
    switch (opt->math_mode) {
        case mp_math_scaled_mode:
            set_internal_string(mp_number_system_internal, mp_intern(mp, "scaled"));
            break;
        case mp_math_decimal_mode:
            set_internal_string(mp_number_system_internal, mp_intern(mp, "decimal"));
            break;
        case mp_math_posit_mode:
            set_internal_string(mp_number_system_internal, mp_intern(mp, "posit"));
            break;
        case mp_math_binary_mode:
            set_internal_string(mp_number_system_internal, mp_intern(mp, "binary"));
            break;
        default:
            set_internal_string(mp_number_system_internal, mp_intern(mp, "double"));
            break;
    }
    mp_init_prim(mp);
    mp_fix_date_and_time(mp);
    mp->history = mp_spotless;
    set_precision();
    if (mp->job_name != NULL) {
        if (internal_string(mp_job_name_internal) != 0) {
            delete_str_ref(internal_string(mp_job_name_internal));
        }
        set_internal_string(mp_job_name_internal, mp_rts(mp, mp->job_name));
    }
    return mp;
}

int   mp_status   (MP mp) { return mp->history; }
int   mp_finished (MP mp) { return mp->finished; }
void *mp_userdata (MP mp) { return mp->userdata; }

static char *mp_find_file (MP mp, const char *fname, const char *fmode, int ftype)
{
    (void) mp; (void) fname; (void) fmode; (void) ftype;
    mp_fatal_error(mp, "no 'find_file' callback set");
    return NULL;
}

static char *mp_run_script (MP mp, const char *str, size_t len, int n)
{
    (void) mp; (void) str; (void) len; (void) n;
    mp_fatal_error(mp, "no 'run_script' callback set");
    return NULL;
}

void mp_run_internal (MP mp, int action, int n, int type, const char *iname)
{
    (void) mp; (void) action; (void) n; (void) type; (void) iname;
    mp_fatal_error(mp, "no 'run_internal' callback set");
}

static void mp_run_logger (MP mp, int target, const char *s, size_t l)
{
    (void) mp; (void) target; (void) s; (void) l;
    mp_fatal_error(mp, "no 'run_logger' callback set");
}

static int mp_run_overload (MP mp, int property, const char *str, int mode)
{
    (void) mp; (void) property; (void) str; (void) mode;
    mp_fatal_error(mp, "no 'run_overload' callback set");
    return 0;
}

static void mp_check_overload (MP mp, mp_sym p)
{
    if (number_nonzero(internal_value(mp_overloadmode_internal))) {
        if (mp->run_overload(mp, p->property, (const char *) p->text->str, number_to_int(internal_value(mp_overloadmode_internal)))) {
            p->property = 0;
        } else {
        }
    } else {
        p->property = 0;
    }
}

static void mp_run_error (MP mp, const char *msg, const char *hlp, int interaction)
{
    (void) mp; (void) msg; (void) hlp; (void) interaction;
    mp_fatal_error(mp, "no 'run_error' callback set");
}

static void mp_run_warning (MP mp, const char *msg)
{
    (void) mp; (void) msg;
    mp_fatal_error(mp, "no 'run_warning' callback set");
}

static char *mp_make_text (MP mp, const char *str, size_t len, int mode)
{
    (void) mp; (void) mode; (void) str; (void) len;
    mp_fatal_error(mp, "no 'make_text' callback set");
    return NULL;
}

static void *mp_open_file (MP mp, const char *fname, const char *fmode, int ftype)
{
    (void) mp; (void) fname; (void) fmode; (void) ftype;
    mp_fatal_error(mp, "no 'open_file' callback set");
    return NULL;
}

static int mp_do_open_file (MP mp, void **f, int ftype, const char *mode)
{
    char *s = (mp->find_file)(mp, mp->name_of_file, mode, ftype);
    if (s != NULL) {
        mp_memory_free(mp->name_of_file);
        mp->name_of_file = mp_strdup(s);

        lmt_memory_free(s);
        *f = (mp->open_file)(mp, mp->name_of_file, mode, ftype);
    } else {
        *f = NULL;
    }
    return (*f ? 1 : 0);
}

static int mp_open_in (MP mp, void **f, int ftype)
{
    return mp_do_open_file(mp, f, ftype, "r");
}

static int mp_open_out (MP mp, void **f, int ftype)
{
    return mp_do_open_file(mp, f, ftype, "w");
}

static char *mp_read_file (MP mp, void *f, size_t *size)
{
    (void) mp; (void) f; (void) size;
    mp_fatal_error(mp, "no 'read_file' callback set");
    return NULL;
}

static void mp_write_file (MP mp, void *f, const char *s)
{
    (void) mp; (void) f; (void) s;
    mp_fatal_error(mp, "no 'read_file' callback set");
}

static void mp_close_file (MP mp, void *f)
{
    (void) mp; (void) f;
    mp_fatal_error(mp, "no 'close_file' callback set");
}

static void mp_reallocate_buffer (MP mp, size_t l)
{
    if (l > max_halfword) {
        mp_confusion(mp, "buffer size");
    } else {
        unsigned char *buffer = mp_memory_allocate((size_t) (l + 1) * sizeof(unsigned char));
        memcpy(buffer, mp->buffer, (mp->buf_size + 1));
        mp_memory_free(mp->buffer);
        mp->buffer = buffer;
        mp->buf_size = l;
    }
}

static int mp_input_ln (MP mp, void *f)
{
    char *s;
    size_t size = 0;
    mp->last = mp->first;
    s = (mp->read_file)(mp, f, &size);
    if (s == NULL) {
        return 0;
    } else if (size > 0) {
        mp->last = mp->first + size;
        if (mp->last >= mp->max_buf_stack) {
            mp->max_buf_stack = mp->last + 1;
            while (mp->max_buf_stack > mp->buf_size) {
                mp_reallocate_buffer(mp, (mp->buf_size + (mp->buf_size >> 2)));
            }
        }
        memcpy((mp->buffer + mp->first), s, size);
    }
    lmt_memory_free(s);
    return 1;
}

static void mp_print_ln (MP mp)
{
    switch (mp->selector) {
        case mp_term_and_log_selector:
            mp_log_cr(mp_both_logging_target);
            mp->term_offset = 0;
            mp->file_offset = 0;
            break;
        case mp_log_only_selector:
            mp_log_cr(mp_file_logging_target);
            mp->file_offset = 0;
            break;
        case mp_term_only_selector:
            mp_log_cr(mp_term_logging_target);
            mp->term_offset = 0;
            break;
        case mp_no_print_selector:
        case mp_new_string_selector:
            break;
        default:
            mp_fputs("\n", mp->wr_file[mp->selector - mp_first_file_selector]);
    }
}

static void mp_print_chr (MP mp, unsigned char s)
{
    switch (mp->selector) {
        case mp_term_and_log_selector:
            mp_log_chr(mp_both_logging_target, s);
            mp->term_offset = 1;
            mp->file_offset = 1;
            break;
        case mp_log_only_selector:
            mp_log_chr(mp_file_logging_target, s);
            mp->file_offset = 1;
            break;
        case mp_term_only_selector:
            mp_log_chr(mp_term_logging_target, s);
            mp->term_offset = 1;
            break;
        case mp_no_print_selector:
            break;
        case mp_new_string_selector:
            mp_str_room(mp, 1);
            mp_append_char(mp, s);
            break;
        default:
            {
                unsigned char ss[2] = { s, 0 };
                mp_fputs((char *) ss, mp->wr_file[mp->selector - mp_first_file_selector]);
            }
    }
}

void mp_print_e_chr (MP mp, unsigned char s)
{
    mp_print_chr(mp, s);
}

static void mp_do_print (MP mp, const char *s, size_t len)
{
    if (len == 0) {
        return;
    } else if (mp->selector == mp_new_string_selector) {
        mp_str_room(mp, (int) len);
        memcpy((mp->cur_string + mp->cur_length), s, len);
        mp->cur_length += len;
    } else {
        switch (mp->selector) {
            case mp_term_and_log_selector:
                mp_log_mpstr(mp_both_logging_target, s, (int) len);
                mp->term_offset = 1;
                mp->file_offset = 1;
                break;
            case mp_log_only_selector:
                mp_log_mpstr(mp_file_logging_target, s, (int) len);
                mp->file_offset = 1;
                break;
            case mp_term_only_selector:
                mp_log_mpstr(mp_term_logging_target, s, (int) len);
                mp->term_offset = 1;
                break;
            case mp_no_print_selector:
                break;
            case mp_new_string_selector:
                mp_str_room(mp, (int) len);
                mp_append_str(mp, s);
                break;
            default:
                mp_fputs(s, mp->wr_file[mp->selector - mp_first_file_selector]);
                break;
        }
    }
}

static void mp_print_str (MP mp, const char *s)
{
    mp_do_print(mp, s, strlen(s));
}

void mp_print_e_str (MP mp, const char *s)
{
    mp_print_str(mp,s);
}

static void mp_print_fmt (MP mp, const char *s, ...)
{
    va_list ap;
    char pval[256];
    va_start(ap, s);
    vsnprintf(pval, 256, s, ap);
    mp_do_print(mp, pval, strlen(pval));
    va_end(ap);
}

static void mp_print_mp_str (MP mp, mp_string s)
{
    mp_do_print(mp, (const char *) s->str, s->len);
}

static void mp_print_nl_only (MP mp)
{
    switch (mp->selector) {
        case mp_term_and_log_selector:
            if (mp->file_offset > 0) {
                mp_log_cr(mp_file_logging_target);
                mp->file_offset = 0;
            }
            if (mp->term_offset > 0) {
                mp_log_cr(mp_term_logging_target);
                mp->term_offset = 0;
            }
            break;
        case mp_log_only_selector:
            if (mp->file_offset > 0) {
                mp_log_cr(mp_file_logging_target);
                mp->file_offset = 0;
            }
            break;
        case mp_term_only_selector:
            if (mp->term_offset > 0) {
                mp_log_cr(mp_term_logging_target);
                mp->term_offset = 0;
            }
            break;
        case mp_no_print_selector:
        case mp_new_string_selector:
            break;
    }
}

static void mp_print_nl (MP mp, const char *s)
{
    mp_print_nl_only(mp);
    mp_print_str(mp, s);
}

static void mp_print_int (MP mp, int n)
{
    char s[12];
    mp_snprintf(s, 12, "%d", (int) n);
    mp_print_str(mp, s);
}

void mp_error (MP mp, const char *msg, const char *hlp)
{
    int selector = mp->selector;
    mp_normalize_selector(mp);
    mp->run_error(mp, msg, hlp, mp->interaction);
    if (mp->history < mp_error_message_issued) {
        mp->history = mp_error_message_issued;
    }
    if (mp->halt_on_error) {
        mp->history = mp_fatal_error_stop;
        mp_jump_out(mp);
    }
    ++mp->error_count;
    if (mp->error_count == 100) {
        mp_print_nl(mp, "(That makes 100 errors; please try again.)");
        mp->history = mp_fatal_error_stop;
        mp_jump_out(mp);
    }
    mp->selector = selector;
}

void mp_normalize_selector (MP mp)
{
    mp->selector = mp->interaction == mp_batch_mode ? mp_log_only_selector : mp_term_and_log_selector;
}

static void check_arith (MP mp)
{
    if (mp->arith_error) {
        mp_error(
            mp,
            "Arithmetic overflow",
            "Uh, oh. A little while ago one of the quantities that I was computing got too\n"
            "large, so I'm afraid your answers will be somewhat askew. You'll probably have to\n"
            "adopt different tactics next time. But I shall try to carry on anyway."
        );
        mp->arith_error = 0;
    }
}

static void mp_print_two (MP mp, mp_number *x, mp_number *y)
{
    mp_print_chr(mp, '(');
    print_number(*x);
    mp_print_chr(mp, ',');
    print_number(*y);
    mp_print_chr(mp, ')');
}

void mp_new_randoms (MP mp)
{
    mp_number x;
    new_number(x);
    for (int k = 0; k <= 23; k++) {
        set_number_from_subtraction(x, mp->randoms[k], mp->randoms[k + 31]);
        if (number_negative(x)) {
            number_add(x, fraction_one_t);
        }
        number_clone(mp->randoms[k], x);
    }
    for (int k = 24; k <= 54; k++) {
        set_number_from_subtraction(x, mp->randoms[k], mp->randoms[k - 24]);
        if (number_negative(x)) {
            number_add(x, fraction_one_t);
        }
        number_clone(mp->randoms[k], x);
    }
    free_number(x);
    mp->j_random = 54;
}

void *mp_allocate_node (MP mp, size_t size)
{
    void *p = mp_memory_allocate(size);
    ((mp_node) p)->link = NULL;
    ((mp_node) p)->hasnumber = 0;
    mp->var_used += size;
    if (mp->var_used > mp->var_used_max) {
        mp->var_used_max = mp->var_used;
    }
    return p;
}

void *mp_allocate_dash (MP mp)
{
    void *p = mp_memory_allocate(sizeof(mp_dash_object));
    mp->var_used += sizeof(mp_dash_object);
    if (mp->var_used > mp->var_used_max) {
        mp->var_used_max = mp->var_used;
    }
    return p;
}

void *mp_memory_allocate (size_t size)
{
    void *w = lmt_memory_malloc(size);
    if (! w) {
        printf("mplib ran out of memory, case 1");
        exit(EXIT_FAILURE);
    }
    return w;
}

void *mp_memory_clear_allocate (size_t size)
{
    void *w = lmt_memory_calloc(1, size);
    if (! w) {
        printf("mplib ran out of memory, case 2");
        exit(EXIT_FAILURE);
    }
    return w;
}

void *mp_memory_reallocate (void *p, size_t size)
{
    void *w = lmt_memory_realloc(p, size);
    if (! w) {
        printf("mplib ran out of memory, case 3");
        exit(EXIT_FAILURE);
    }
    return w;
}

void mp_memory_free (void *p)
{
    lmt_memory_free(p);
}

# define mp_get_sym_info(A)   mp_get_indep_value(A)
# define mp_set_sym_info(A,B) mp_set_indep_value(A, (B))
# define mp_get_sym_sym(A)    (A)->data.sym
# define mp_set_sym_sym(A,B)  (A)->data.sym = (mp_sym)(B)

static mp_node mp_new_symbolic_node (MP mp)
{
    mp_symbolic_node p;
    if (mp->symbolic_nodes) {
        p = (mp_symbolic_node) mp->symbolic_nodes;
        mp->symbolic_nodes = p->link;
        mp->num_symbolic_nodes--;
        p->link = NULL;
    } else {
        p = mp_allocate_node(mp, sizeof(mp_node_data));
        new_number(p->data.n);
        p->hasnumber = 1;
    }
    p->type = mp_symbol_node_type;
    p->name_type = mp_normal_operation;
    return (mp_node) p;
}

static void mp_free_node (MP mp, mp_node p, size_t siz)
{
    if (p) {
        mp->var_used -= siz;
        if (mp->math_mode > mp_math_double_mode) {
            if (p->hasnumber >= 1 && is_number(((mp_symbolic_node) p)->data.n)) {
                free_number(((mp_symbolic_node) p)->data.n);
            }
            if (p->hasnumber == 2 && is_number(((mp_value_node) p)->subscript)) {
                free_number(((mp_value_node) p)->subscript);
            }
            if (p->type == mp_dash_node_type) {
                free_number(((mp_dash_node) p)->start_x);
                free_number(((mp_dash_node) p)->stop_x);
                free_number(((mp_dash_node) p)->dash_y);
            }
        }
        mp_memory_free(p);
    }
}

static void mp_free_symbolic_node (MP mp, mp_node p)
{
    if (p) {
        if (mp->num_symbolic_nodes < max_num_symbolic_nodes) {
            p->link = mp->symbolic_nodes;
            mp->symbolic_nodes = p;
            mp->num_symbolic_nodes++;
        } else {
            mp->var_used -= sizeof(mp_node_data);
            mp_memory_free(p);
        }
    }
}

static void mp_free_value_node (MP mp, mp_node p)
{
    if (p) {
        if (mp->num_value_nodes < max_num_value_nodes) {
            p->link = mp->value_nodes;
            mp->value_nodes = p;
            mp->num_value_nodes++;
        } else {
            mp->var_used -= sizeof(mp_value_node_data);
            if (mp->math_mode > mp_math_double_mode) {
                free_number(((mp_value_node) p)->data.n);
                free_number(((mp_value_node) p)->subscript);
            }
            mp_memory_free(p);
        }
    }
}

static void mp_flush_node_list (MP mp, mp_node p)
{
    while (p != NULL) {
        mp_node q = p;
        p = p->link;
        if (q->type != mp_symbol_node_type) {
            mp_free_token_node(mp, q);
        } else {
            mp_free_symbolic_node(mp, q);
        }
    }
}

static const char *mp_type_string(int t)
{
    const char *s = NULL;
    switch (t) {
        case mp_undefined_type:         s = "undefined";         break;
        case mp_vacuous_type:           s = "vacuous";           break;
        case mp_boolean_type:           s = "boolean";           break;
        case mp_unknown_boolean_type:   s = "unknown boolean";   break;
        case mp_string_type:            s = "string";            break;
        case mp_unknown_string_type:    s = "unknown string";    break;
        case mp_pen_type:               s = "pen";               break;
        case mp_unknown_pen_type:       s = "unknown pen";       break;
        case mp_nep_type:               s = "pen";               break;
        case mp_unknown_nep_type:       s = "unknown pen";       break;
        case mp_path_type:              s = "path";              break;
        case mp_unknown_path_type:      s = "unknown path";      break;
        case mp_picture_type:           s = "picture";           break;
        case mp_unknown_picture_type:   s = "unknown picture";   break;
        case mp_transform_type:         s = "transform";         break;
        case mp_color_type:             s = "color";             break;
        case mp_cmykcolor_type:         s = "cmykcolor";         break;
        case mp_pair_type:              s = "pair";              break;
        case mp_known_type:             s = "known numeric";     break;
        case mp_dependent_type:         s = "dependent";         break;
        case mp_proto_dependent_type:   s = "proto-dependent";   break;
        case mp_numeric_type:           s = "numeric";           break;
        case mp_independent_type:       s = "independent";       break;
        case mp_token_list_type:        s = "token list";        break;
        case mp_structured_type:        s = "mp_structured";     break;
        case mp_unsuffixed_macro_type:  s = "unsuffixed macro";  break;
        case mp_suffixed_macro_type:    s = "suffixed macro";    break;
        case mp_symbol_node_type:       s = "symbol node";       break;
        case mp_token_node_type:        s = "token node";        break;
        case mp_value_node_type:        s = "value node";        break;
        case mp_attribute_node_type:    s = "attribute node";    break;
        case mp_subscript_node_type:    s = "subscript node";    break;
        case mp_pair_node_type:         s = "pair node";         break;
        case mp_transform_node_type:    s = "transform node";    break;
        case mp_color_node_type:        s = "color node";        break;
        case mp_cmykcolor_node_type:    s = "cmykcolor node";    break;
        case mp_fill_node_type:         s = "fill node";         break;
        case mp_stroked_node_type:      s = "stroked node";      break;
        case mp_start_clip_node_type:   s = "start clip node";   break;
        case mp_start_group_node_type:  s = "start group node";  break;
        case mp_start_bounds_node_type: s = "start bounds node"; break;
        case mp_stop_clip_node_type:    s = "stop clip node";    break;
        case mp_stop_group_node_type:   s = "stop group node";   break;
        case mp_stop_bounds_node_type:  s = "stop bounds node";  break;
        case mp_dash_node_type:         s = "dash node";         break;
        case mp_dep_node_type:          s = "dependency node";   break;
        case mp_if_node_type:           s = "if node";           break;
        case mp_edge_header_node_type:  s = "edge header node";  break;
        default:
            {
                char ss[256];
                mp_snprintf(ss, 256, "<unknown type %d>", t);
                s = mp_strdup(ss);
            }
            break;
    }
    return s;
}

void mp_print_type (MP mp, int t)
{
    if (t >= 0 && t <= mp_edge_header_node_type) {
        mp_print_str(mp, mp_type_string(t));
    } else {
        mp_print_str(mp, "unknown");
    }
}

static const char *mp_op_string (int c)
{
    if (c <= mp_numeric_type) {
        return mp_type_string(c);
    } else {
        switch (c) {
            case mp_true_operation             : return "true";
            case mp_false_operation            : return "false";
            case mp_null_picture_operation     : return "nullpicture";
            case mp_null_pen_operation         : return "nullpen";
            case mp_read_string_operation      : return "readstring";
            case mp_pen_circle_operation       : return "pencircle";
            case mp_normal_deviate_operation   : return "normaldeviate";
            case mp_read_from_operation        : return "readfrom";
            case mp_close_from_operation       : return "closefrom";
            case mp_odd_operation              : return "odd";
            case mp_known_operation            : return "known";
            case mp_unknown_operation          : return "unknown";
            case mp_not_operation              : return "not";
            case mp_decimal_operation          : return "decimal";
            case mp_reverse_operation          : return "reverse";
            case mp_uncycle_operation          : return "uncycle";
            case mp_make_path_operation        : return "makepath";
            case mp_make_pen_operation         : return "makepen";
            case mp_make_nep_operation         : return "makenep";
            case mp_convexed_operation         : return "convexed";
            case mp_uncontrolled_operation     : return "uncontrolled";
            case mp_oct_operation              : return "oct";
            case mp_hex_operation              : return "hex";
            case mp_ASCII_operation            : return "ASCII";
            case mp_char_operation             : return "char";
            case mp_length_operation           : return "length";
            case mp_no_length_operation        : return "nolength";
            case mp_turning_operation          : return "turningnumber";
            case mp_x_part_operation           : return "xpart";
            case mp_y_part_operation           : return "ypart";
            case mp_xx_part_operation          : return "xxpart";
            case mp_xy_part_operation          : return "xypart";
            case mp_yx_part_operation          : return "yxpart";
            case mp_yy_part_operation          : return "yypart";
            case mp_red_part_operation         : return "redpart";
            case mp_green_part_operation       : return "greenpart";
            case mp_blue_part_operation        : return "bluepart";
            case mp_cyan_part_operation        : return "cyanpart";
            case mp_magenta_part_operation     : return "magentapart";
            case mp_yellow_part_operation      : return "yellowpart";
            case mp_black_part_operation       : return "blackpart";
            case mp_grey_part_operation        : return "greypart";
            case mp_color_model_operation      : return "colormodel";
            case mp_prescript_part_operation   : return "prescriptpart";
            case mp_postscript_part_operation  : return "postscriptpart";
            case mp_stacking_part_operation    : return "stackingpart";
            case mp_path_part_operation        : return "pathpart";
            case mp_pen_part_operation         : return "penpart";
            case mp_dash_part_operation        : return "dashpart";
            case mp_sqrt_operation             : return "sqrt";
            case mp_m_exp_operation            : return "mexp";
            case mp_m_log_operation            : return "mlog";
            case mp_sin_d_operation            : return "sind";
            case mp_cos_d_operation            : return "cosd";
            case mp_floor_operation            : return "floor";
            case mp_uniform_deviate_operation  : return "uniformdeviate";
            case mp_ll_corner_operation        : return "llcorner";
            case mp_lr_corner_operation        : return "lrcorner";
            case mp_ul_corner_operation        : return "ulcorner";
            case mp_ur_corner_operation        : return "urcorner";
            case mp_center_of_operation        : return "centerof";
            case mp_center_of_mass_operation   : return "centerofmass";
            case mp_corners_operation          : return "corners";
            case mp_x_range_operation          : return "xrange";
            case mp_y_range_operation          : return "yrange";
            case mp_delta_point_operation      : return "deltapoint";
            case mp_delta_precontrol_operation : return "deltaprecontrol";
            case mp_delta_postcontrol_operation: return "deltapostcontrol";
            case mp_delta_direction_operation  : return "deltadirection";
            case mp_arc_length_operation       : return "arclength";
            case mp_angle_operation            : return "angle";
            case mp_cycle_operation            : return "cycle";
            case mp_no_cycle_operation         : return "nocycle";
            case mp_filled_operation           : return "filled";
            case mp_stroked_operation          : return "stroked";
            case mp_clipped_operation          : return "clipped";
            case mp_grouped_operation          : return "grouped";
            case mp_bounded_operation          : return "bounded";
            case mp_plus_operation             : return "+";
            case mp_minus_operation            : return "-";
            case mp_times_operation            : return "*";
            case mp_over_operation             : return "/";
            case mp_power_operation            : return "^";
            case mp_pythag_add_operation       : return "++";
            case mp_pythag_sub_operation       : return "+-+";
            case mp_or_operation               : return "or";
            case mp_and_operation              : return "and";
            case mp_less_than_operation        : return "<";
            case mp_less_or_equal_operation    : return "<=";
            case mp_greater_than_operation     : return ">";
            case mp_greater_or_equal_operation : return ">=";
            case mp_equal_operation            : return "=";
            case mp_unequal_operation          : return "<>";
            case mp_concatenate_operation      : return "&";
            case mp_just_append_operation      : return "&&";
            case mp_tolerant_concat_operation  : return "&&&";
            case mp_tolerant_append_operation  : return "&&&&";
            case mp_rotated_operation          : return "rotated";
            case mp_slanted_operation          : return "slanted";
            case mp_scaled_operation           : return "scaled";
            case mp_shifted_operation          : return "shifted";
            case mp_transformed_operation      : return "transformed";
            case mp_x_scaled_operation         : return "xscaled";
            case mp_y_scaled_operation         : return "yscaled";
            case mp_z_scaled_operation         : return "zscaled";
            case mp_intertimes_operation       : return "intersectiontimes";
            case mp_intertimes_list_operation  : return "intersectiontimeslist";
            case mp_substring_operation        : return "substring";
            case mp_subpath_operation          : return "subpath";
            case mp_direction_time_operation   : return "directiontime";
            case mp_point_operation            : return "point";
            case mp_precontrol_operation       : return "precontrol";
            case mp_postcontrol_operation      : return "postcontrol";
            case mp_direction_operation        : return "direction";
            case mp_path_point_operation       : return "pathpoint";
            case mp_path_precontrol_operation  : return "pathprecontrol";
            case mp_path_postcontrol_operation : return "pathpostcontrol";
            case mp_path_direction_operation   : return "pathdirection";
            case mp_path_state_operation       : return "pathstate";
            case mp_path_index_operation       : return "pathindex";
            case mp_path_lastindex_operation   : return "pathlastindex";
            case mp_path_length_operation      : return "pathlength";
            case mp_path_first_operation       : return "pathfirst";
            case mp_path_last_operation        : return "pathlast";
            case mp_pen_offset_operation       : return "penoffset";
            case mp_arc_time_operation         : return "arctime";
            case mp_arc_point_operation        : return "arcpoint";
            case mp_arc_point_list_operation   : return "arcpointlist";
            case mp_subarc_length_operation    : return "subarclength";
            case mp_version_operation          : return "mpversion";
            case mp_envelope_operation         : return "envelope";
            case mp_boundingpath_operation     : return "boundingpath";

            case mp_pen_type_operation         : return "pen";
            case mp_nep_type_operation         : return "nep";
            case mp_path_type_operation        : return "path";
            case mp_picture_type_operation     : return "picture";
            case mp_transform_type_operation   : return "transform";
            case mp_color_type_operation       : return "color";
            case mp_cmykcolor_type_operation   : return "cmykcolor";
            case mp_pair_type_operation        : return "pair";
            case mp_numeric_type_operation     : return "numeric";

            default                            : return "..";
        }
    }
}
static void mp_print_op (MP mp, int c)
{
    mp_print_str(mp, mp_op_string(c));
}

static void mp_fix_date_and_time (MP mp)
{
    time_t aclock = time ((time_t *) 0);
    struct tm *tmptr = localtime (&aclock);
    number_clone(internal_value(mp_time_internal), unity_t);
    number_multiply_int(internal_value(mp_time_internal), (tmptr->tm_hour * 60 + tmptr->tm_min));
    number_clone(internal_value(mp_hour_internal), unity_t);
    number_multiply_int(internal_value(mp_hour_internal), (tmptr->tm_hour));
    number_clone(internal_value(mp_minute_internal), unity_t);
    number_multiply_int(internal_value(mp_minute_internal), (tmptr->tm_min));
    number_clone(internal_value(mp_day_internal), unity_t);
    number_multiply_int(internal_value(mp_day_internal), (tmptr->tm_mday));
    number_clone(internal_value(mp_month_internal), unity_t);
    number_multiply_int(internal_value(mp_month_internal), (tmptr->tm_mon + 1));
    number_clone(internal_value(mp_year_internal), unity_t);
    number_multiply_int(internal_value(mp_year_internal), (tmptr->tm_year + 1900));
}

static void mp_begin_diagnostic (MP mp)
{
    mp->old_selector = mp->selector;
    if (number_nonpositive(internal_value(mp_tracing_online_internal)) && (mp->selector == mp_term_and_log_selector)) {
        mp->selector = mp_log_only_selector;
        if (mp->history == mp_spotless) {
            mp->history = mp_warning_issued;
        }
    }
}

void mp_end_diagnostic (MP mp, int blank_line)
{
    mp_print_nl(mp, "");
    if (blank_line) {
        mp_print_ln(mp);
    }
    mp->selector = mp->old_selector;
}

static void mp_print_diagnostic (MP mp, const char *s, const char *t, int nuline)
{
    mp_begin_diagnostic(mp);
    if (nuline) {
        mp_print_nl(mp, s);
    } else {
        mp_print_str(mp, s);
    }
    mp_print_str(mp, " at line ");
    mp_print_int(mp, mp_true_line(mp));
    mp_print_str(mp, t);
    mp_print_chr(mp, ':');
}

# define text(A)        (A)->text
# define eq_type(A)     (A)->type
# define eq_property(A) (A)->property
# define equiv(A)       (A)->v.data.indep.serial
# define equiv_node(A)  (A)->v.data.node
# define equiv_sym(A)   (mp_sym)(A)->v.data.node

static int mp_compare_symbols_entry (void *p, const void *pa, const void *pb)
{
    const mp_symbol_entry *a = (const mp_symbol_entry *) pa;
    const mp_symbol_entry *b = (const mp_symbol_entry *) pb;
    (void) p;
    if (a->text->len != b->text->len) {
        return (a->text->len > b->text->len ? 1 : -1);
    }
    return strncmp ((const char *) a->text->str, (const char *) b->text->str, a->text->len);
}

static void *mp_copy_symbols_entry (const void *p)
{

    mp_symbol_entry *fp = (mp_symbol_entry *) p;
    MP mp = (MP)fp->parent;
    mp_sym ff = mp_memory_allocate(sizeof(mp_symbol_entry));
    if (ff == NULL) {
        return NULL;
    }
    ff->text = mp_aux_copy_strings_entry(fp->text);
    if (ff->text == NULL) {
        return NULL;
    }
    ff->v = fp->v;
    ff->type = fp->type;
    ff->property = fp->property;
    ff->parent = mp;
    new_number_clone(ff->v.data.n, fp->v.data.n);
    return ff;
}

static void *mp_delete_symbols_entry (void *p)
{
    mp_sym ff = (mp_sym) p;
    MP mp = (MP) ff->parent;
    free_number(ff->v.data.n);
    mp_memory_free(ff->text->str);
    mp_memory_free(ff->text);
    mp_memory_free(ff);
    return NULL;
}

static mp_sym new_symbols_entry (MP mp, unsigned char *nam, size_t len)
{
    mp_sym ff = mp_memory_clear_allocate(sizeof(mp_symbol_entry));
    ff->parent = mp;
    ff->text = mp_memory_allocate(sizeof(mp_lstring));
    ff->text->str = nam;
    ff->text->len = len;
    ff->type = mp_tag_command;
    ff->v.type = mp_known_type;
    new_number(ff->v.data.n);
    return ff;
}

static mp_sym mp_do_id_lookup (MP mp, avl_tree symbols, char *j, size_t l, int insert_new)
{
    mp_sym str;
    mp->id_lookup_test->text->str = (unsigned char *) j;
    mp->id_lookup_test->text->len = l;
    str = (mp_sym) avl_find(mp->id_lookup_test, symbols);
    if (str == NULL && insert_new) {
        unsigned char *nam = (unsigned char *) mp_strndup(j, l);
        mp_sym s = new_symbols_entry(mp, nam, l);
        mp->st_count++;
        avl_ins(s, symbols, avl_false);
        str = (mp_sym) avl_find(s, symbols);
        mp_delete_symbols_entry(s);
    }
    return str;
}

int mp_initialize_symbol_traverse (MP mp)
{
    mp->symbol_iterator = avl_iterator_new(mp->symbols, AVL_ITERATOR_INI_PRE);
    return (mp->symbol_iterator != NULL);
}

void mp_kill_symbol_traverse (MP mp)
{
    avl_iterator_kill(mp->symbol_iterator);
}

void *mp_fetch_symbol_traverse (MP mp)
{
    return avl_iterator_next(mp->symbol_iterator);
}

void *mp_fetch_symbol (MP mp, char *s)
{
    return mp_id_lookup(mp, s, strlen(s), 0);
}

static void mp_primitive (MP mp, const char *ss, int c, int o)
{

    set_cur_sym(mp_id_lookup(mp, (char *) ss, strlen(ss), 1));
    set_eq_type(cur_sym, c);
    set_eq_property(cur_sym, 0x1);
    set_equiv(cur_sym, o);
}

static mp_sym mp_frozen_primitive (MP mp, const char *ss, int c, int o)
{

    mp_sym str = mp_do_id_lookup(mp, mp->frozen_symbols, (char *) ss, strlen(ss), 1);
    str->type = c;
    str->property = 0x1;
    str->v.data.indep.serial = o;
    return str;
}

static int mp_is_frozen (MP mp, mp_sym sym)
{
    mp_sym temp = mp_do_id_lookup(mp, mp->frozen_symbols, (char *) sym->text->str, sym->text->len, 0);
    if (temp == mp->frozen_inaccessible) {
        return 0;
    } else {
        return (temp == sym);
    }
}

# define mp_get_value_sym(A)    ((mp_token_node) (A))->data.sym
# define mp_get_value_number(A) ((mp_token_node) (A))->data.n
# define mp_get_value_node(A)   ((mp_token_node) (A))->data.node
# define mp_get_value_str(A)    ((mp_token_node) (A))->data.str
# define mp_get_value_knot(A)   ((mp_token_node) (A))->data.p

inline static void do_set_value_sym (MP mp, mp_token_node A, mp_sym B)
{
    (void) mp;
    A->data.sym=(B);
}

inline static void do_set_value_number (MP mp, mp_token_node A, mp_number *B)
{
    (void) mp;
    A->data.p = NULL;
    A->data.str = NULL;
    A->data.node = NULL;
    number_clone(A->data.n, *B);
}

inline static void do_set_value_str (MP mp, mp_token_node A, mp_string B)
{
    (void) mp;
    A->data.p = NULL;
    A->data.str = (B);
    add_str_ref((B));
    A->data.node = NULL;
    number_clone(A->data.n, zero_t);
}

inline static void do_set_value_node (MP mp, mp_token_node A, mp_node B)
{
    (void) mp;
    A->data.p = NULL;
    A->data.str = NULL;
    A->data.node = B;
    number_clone(A->data.n, zero_t);
}

inline static void do_set_value_knot (MP mp, mp_token_node A, mp_knot B)
{
    (void) mp;
    A->data.p = (B);
    A->data.str = NULL;
    A->data.node = NULL;
    number_clone(A->data.n, zero_t);
}

static mp_node mp_new_token_node (MP mp)
{
    mp_node p;
    if (mp->token_nodes) {
        p = mp->token_nodes;
        mp->token_nodes = p->link;
        mp->num_token_nodes--;
        p->link = NULL;
    } else {
        p = mp_allocate_node(mp, sizeof(mp_node_data));
        new_number(p->data.n);
        p->hasnumber = 1;
    }
    p->type = mp_token_node_type;
    return (mp_node) p;
}

static void mp_free_token_node (MP mp, mp_node p)
{
    if (p) {
        if (mp->num_token_nodes < max_num_token_nodes) {
            p->link = mp->token_nodes;
            mp->token_nodes = p;
            mp->num_token_nodes++;
        } else {
            mp->var_used -= sizeof(mp_node_data);
            if (mp->math_mode > mp_math_double_mode) {
                free_number(((mp_value_node) p)->data.n);
            }
            mp_memory_free(p);
        }
    }
}

static mp_node mp_new_num_tok (MP mp, mp_number *v)
{
    mp_node p = mp_new_token_node(mp);
    mp_set_value_number(p, *v);
    p->type = mp_known_type;
    p->name_type = mp_token_operation;
    return p;
}

static void mp_flush_token_list (MP mp, mp_node p)
{
    while (p != NULL) {
        mp_node q = p;
        p = p->link;
        switch (q->type) {
            case mp_symbol_node_type:
                mp_free_symbolic_node(mp, q);
                continue;
            case mp_vacuous_type:
            case mp_boolean_type:
            case mp_known_type:
                break;
            case mp_string_type:
                delete_str_ref(mp_get_value_str(q));
                break;
            case mp_unknown_boolean_type:
            case mp_unknown_string_type:
            case mp_unknown_pen_type:
            case mp_unknown_nep_type:
            case mp_unknown_path_type:
            case mp_unknown_picture_type:
            case mp_pen_type:
            case mp_nep_type:
            case mp_path_type:
            case mp_picture_type:
            case mp_pair_type:
            case mp_color_type:
            case mp_cmykcolor_type:
            case mp_transform_type:
            case mp_dependent_type:
            case mp_proto_dependent_type:
            case mp_independent_type:
                mp_recycle_value(mp, q);
                break;
            default:
                mp_confusion(mp, "token");
        }
        mp_free_token_node(mp, q);
    }
}

void mp_show_token_list (MP mp, mp_node p, mp_node q)
{
    int cclass = mp_percent_class;
    (void) q;
    while (p != NULL) {
        int c = mp_letter_class;
        if (p->type != mp_symbol_node_type) {
            if (p->name_type == mp_token_operation) {
                if (p->type == mp_known_type) {
                    if (cclass == mp_digit_class) {
                        mp_print_chr(mp, ' ');
                    }
                    if (number_negative(mp_get_value_number(p))) {
                        if (cclass == mp_left_bracket_class) {
                            mp_print_chr(mp, ' ');
                        }
                        mp_print_chr(mp, '[');
                        print_number(mp_get_value_number(p));
                        mp_print_chr(mp, ']');
                        c = mp_right_bracket_class;
                    } else {
                        print_number(mp_get_value_number(p));
                        c = mp_digit_class;
                    }
                } else if (p->type == mp_string_type) {
                    mp_print_chr(mp, '"');
                    mp_print_mp_str(mp, mp_get_value_str(p));
                    mp_print_chr(mp, '"');
                    c = mp_string_class;
                } else {
                    mp_print_str(mp, " BAD");
                }
            } else if ((p->name_type != mp_capsule_operation) || (p->type < mp_vacuous_type) || (p->type > mp_independent_type)) {
                mp_print_str(mp, " BAD");
            } else {
                mp_print_capsule(mp, p);
                c = mp_right_parenthesis_class;
            }
        } else if (p->name_type == mp_expr_operation || p->name_type == mp_suffix_operation || p->name_type == mp_text_operation) {
            int r = mp_get_sym_info(p);
            if (p->name_type == mp_expr_operation) {
                mp_print_str(mp, "(EXPR");
            } else if (p->name_type == mp_suffix_operation) {
                mp_print_str(mp, "(SUFFIX");
            } else {
                mp_print_str(mp, "(TEXT");
            }
            mp_print_int(mp, r);
            mp_print_chr(mp, ')');
            c = mp_right_parenthesis_class;
        } else {
            mp_sym sr = mp_get_sym_sym(p);
            if (sr == mp_collective_subscript) {
                if (cclass == mp_left_bracket_class) {
                    mp_print_chr(mp, ' ');
                }
                mp_print_str(mp, "[]");
                c = mp_right_bracket_class;
            } else {
                mp_string rr = text(sr);
                if (rr == NULL || rr->str == NULL) {
                    mp_print_str(mp, " NONEXISTENT");
                } else {
                    c = mp->char_class[(rr->str[0])];
                    if (c == cclass) {
                        switch (c) {
                            case mp_letter_class:
                                mp_print_chr(mp, '.');
                                break;
                            case mp_comma_class:
                            case mp_semicolon_class:
                            case mp_left_parenthesis_class:
                            case mp_right_parenthesis_class:
                                break;
                            default:
                                mp_print_chr(mp, ' ');
                                break;
                            }
                    }
                    mp_print_mp_str(mp, rr);
                }
            }
        }
        cclass = c;
        p = p->link;
    }
    return;
}

void mp_show_token_list_space (MP mp, mp_node p, mp_node q)
{
    (void) q;
    while (p != NULL) {
        if (p->type != mp_symbol_node_type) {
            if (p->name_type == mp_token_operation) {
                if (p->type == mp_known_type) {
                    if (number_negative(mp_get_value_number(p))) {
                        mp_print_str(mp, "[ ");
                        print_number(mp_get_value_number(p));
                        mp_print_str(mp, " ]");
                    } else {
                        print_number(mp_get_value_number(p));
                    }
                } else if (p->type == mp_string_type) {
                    mp_print_chr(mp, '"');
                    mp_print_mp_str(mp, mp_get_value_str(p));
                    mp_print_chr(mp, '"');
                } else {
                    mp_print_str(mp, "BAD");
                }
            } else if ((p->name_type != mp_capsule_operation) || (p->type < mp_vacuous_type) || (p->type > mp_independent_type)) {
                mp_print_str(mp, "BAD");
            } else {
                mp_print_capsule(mp, p);
            }
        } else if (p->name_type == mp_expr_operation || p->name_type == mp_suffix_operation || p->name_type == mp_text_operation) {
            int r = mp_get_sym_info(p);
            if (p->name_type == mp_expr_operation) {
                mp_print_str(mp, "(EXPR ");
            } else if (p->name_type == mp_suffix_operation) {
                mp_print_str(mp, "(SUFFIX ");
            } else {
                mp_print_str(mp, "(TEXT ");
            }
            mp_print_int(mp, r);
            mp_print_chr(mp, ')');
        } else {
            mp_sym sr = mp_get_sym_sym(p);
            if (sr == mp_collective_subscript) {
                mp_print_str(mp, "[]");
            } else {
                mp_string rr = text(sr);
                if (rr == NULL || rr->str == NULL) {
                    mp_print_str(mp, "NONEXISTENT");
                } else {
                    mp_print_mp_str(mp, rr);
                }
            }
        }
        p = p->link;
        if (p) {
            mp_print_chr(mp, ' ');
        }
    }
    return;
}

static void mp_delete_mac_ref (MP mp, mp_node p)
{
    if (mp_get_ref_count(p) == 0) {
        mp_flush_token_list(mp, p);
    } else {
        mp_decr_mac_ref(p);
    }
}

static void mp_show_macro (MP mp, mp_node p, mp_node q)
{
    p = p->link;
    while (p->name_type != mp_macro_operation) {
        mp_node r = p->link;
        p->link = NULL;
        mp_show_token_list(mp, p, NULL);
        p->link = r;
        p = r;
    }
    switch (mp_get_sym_info(p)) {
        case mp_general_macro:
            mp_print_str(mp, "-> ");
            break;
        case mp_primary_macro:
        case mp_secondary_macro:
        case mp_tertiary_macro:
            mp_print_str(mp, "<");
            mp_print_cmd_mod(mp, mp_parameter_commmand, mp_get_sym_info(p));
            mp_print_str(mp, "> -> ");
            break;
        case mp_expr_macro:
            mp_print_str(mp, "<expr> -> ");
            break;
        case mp_of_macro:
            mp_print_str(mp, "<expr> of <primary> -> ");
            break;
        case mp_suffix_macro:
            mp_print_str(mp, "<suffix> -> ");
            break;
        case mp_text_macro:
            mp_print_str(mp, "<text> -> ");
            break;
    }
    mp_show_token_list(mp, p->link, q);
}

static mp_node mp_do_get_attribute_head (MP mp, mp_value_node A)
{
    (void) mp;
    return A->attr_head;
}

static mp_node mp_do_get_subscr_head (MP mp, mp_value_node A)
{
    (void) mp;
    return A->subscr_head;
}

static void mp_do_set_attribute_head (MP mp, mp_value_node A, mp_node d)
{
    (void) mp;
    A->attr_head = d;
}

static void mp_do_set_subscr_head (MP mp, mp_value_node A, mp_node d)
{
    (void) mp;
    A->subscr_head = d;
}

static mp_node mp_new_value_node (MP mp)
{
    mp_value_node p;
    if (mp->value_nodes) {
        p = (mp_value_node) mp->value_nodes;
        mp->value_nodes = p->link;
        mp->num_value_nodes--;
        p->link = NULL;
    } else {
        p = mp_allocate_node(mp, sizeof(mp_value_node_data));
        new_number(p->data.n);
        new_number(p->subscript);
        p->hasnumber = 2;
    }
    p->type = mp_value_node_type;
    return (mp_node) p;
}

# define mp_get_hashloc(A)   ((mp_value_node)(A))->hashloc_
# define mp_set_hashloc(A,B) ((mp_value_node)(A))->hashloc_ = B
# define mp_get_parent(A)    ((mp_value_node)(A))->parent
# define mp_set_parent(A,B)  ((mp_value_node)(A))->parent = B

static mp_value_node mp_get_attribute_node (MP mp)
{
    mp_value_node p = (mp_value_node) mp_new_value_node(mp);
    p->type = mp_attribute_node_type;
    return p;
}

static mp_value_node mp_get_subscr_node (MP mp)
{
    mp_value_node p = (mp_value_node) mp_new_value_node(mp);
    p->type = mp_subscript_node_type;
    return p;
}

static mp_node mp_get_pair_node (MP mp)
{
    mp_node p;
    if (mp->pair_nodes) {
        p = mp->pair_nodes;
        mp->pair_nodes = p->link;
        mp->num_pair_nodes--;
        p->link = NULL;
    } else {
        p = mp_allocate_node(mp, sizeof(mp_pair_node_data));
    }
    p->type = mp_pair_node_type;
    return (mp_node) p;
}

static void mp_free_pair_node (MP mp, mp_node p)
{
    if (p) {
        if (mp->num_pair_nodes < max_num_pair_nodes) {
            p->link = mp->pair_nodes;
            mp->pair_nodes = p;
            mp->num_pair_nodes++;
        } else {
            mp->var_used -= sizeof(mp_pair_node_data);
            mp_memory_free(p);
        }
    }
}

static void mp_init_pair_node (MP mp, mp_node p)
{
    mp_node q;
    p->type = mp_pair_type;
    q = mp_get_pair_node(mp);
    mp_y_part(q) = mp_new_value_node(mp);
    mp_new_indep(mp, mp_y_part(q));
    mp_y_part(q)->name_type = mp_y_part_operation;
    mp_y_part(q)->link = p;
    mp_x_part(q) = mp_new_value_node(mp);
    mp_new_indep(mp, mp_x_part(q));
    mp_x_part(q)->name_type = mp_x_part_operation;
    mp_x_part(q)->link = p;
    mp_set_value_node(p, q);
}

static mp_node mp_get_transform_node (MP mp)
{
    mp_transform_node p = (mp_transform_node) mp_allocate_node(mp, sizeof(mp_transform_node_data));
    p->type = mp_transform_node_type;
    return (mp_node) p;
}

static void mp_init_transform_node (MP mp, mp_node p)
{
    mp_node q;
    p->type = mp_transform_type;
    q = mp_get_transform_node(mp);
    mp_yy_part(q) = mp_new_value_node(mp);
    mp_new_indep(mp, mp_yy_part(q));
    mp_yy_part(q)->name_type = mp_yy_part_operation;
    mp_yy_part(q)->link = p;
    mp_yx_part(q) = mp_new_value_node(mp);
    mp_new_indep(mp, mp_yx_part(q));
    mp_yx_part(q)->name_type = mp_yx_part_operation;
    mp_yx_part(q)->link = p;
    mp_xy_part(q) = mp_new_value_node(mp);
    mp_new_indep(mp, mp_xy_part(q));
    mp_xy_part(q)->name_type = mp_xy_part_operation;
    mp_xy_part(q)->link = p;
    mp_xx_part(q) = mp_new_value_node(mp);
    mp_new_indep(mp, mp_xx_part(q));
    mp_xx_part(q)->name_type = mp_xx_part_operation;
    mp_xx_part(q)->link = p;
    mp_ty_part(q) = mp_new_value_node(mp);
    mp_new_indep(mp, mp_ty_part(q));
    mp_ty_part(q)->name_type = mp_y_part_operation;
    mp_ty_part(q)->link = p;
    mp_tx_part(q) = mp_new_value_node(mp);
    mp_new_indep(mp, mp_tx_part(q));
    mp_tx_part(q)->name_type = mp_x_part_operation;
    mp_tx_part(q)->link = p;
    mp_set_value_node(p, q);
}

static void mp_init_color_node (MP mp, mp_node p, int type)
{
    mp_node q = (mp_node) mp_allocate_node(mp, sizeof(mp_color_node_data));
    p->type = type;
    switch (type) {
        case mp_color_type:
            q->type = mp_color_node_type;
            mp_red_part(q) = mp_new_value_node(mp);
            mp_new_indep(mp, mp_red_part(q));
            mp_red_part(q)->name_type = mp_red_part_operation;
            mp_red_part(q)->link = p;
            mp_green_part(q) = mp_new_value_node(mp);
            mp_new_indep(mp, mp_green_part(q));
            mp_green_part(q)->name_type = mp_green_part_operation;
            mp_green_part(q)->link = p;
            mp_blue_part(q) = mp_new_value_node(mp);
            mp_new_indep(mp, mp_blue_part(q));
            mp_blue_part(q)->name_type = mp_blue_part_operation;
            mp_blue_part(q)->link = p;
            break;
        case mp_cmykcolor_type:
            q->type = mp_cmykcolor_node_type;
            mp_cyan_part(q) = mp_new_value_node(mp);
            mp_new_indep(mp, mp_cyan_part(q));
            mp_cyan_part(q)->name_type = mp_cyan_part_operation;
            mp_cyan_part(q)->link = p;
            mp_magenta_part(q) = mp_new_value_node(mp);
            mp_new_indep(mp, mp_magenta_part(q));
            mp_magenta_part(q)->name_type = mp_magenta_part_operation;
            mp_magenta_part(q)->link = p;
            mp_yellow_part(q) = mp_new_value_node(mp);
            mp_new_indep(mp, mp_yellow_part(q));
            mp_yellow_part(q)->name_type = mp_yellow_part_operation;
            mp_yellow_part(q)->link = p;
            mp_black_part(q) = mp_new_value_node(mp);
            mp_new_indep(mp, mp_black_part(q));
            mp_black_part(q)->name_type = mp_black_part_operation;
            mp_black_part(q)->link = p;
            break;
    }
    mp_set_value_node(p, q);
}

static mp_node mp_id_transform (MP mp)
{
    mp_node q;
    mp_node p = mp_new_value_node(mp);
    p->name_type = mp_capsule_operation;
    mp_set_value_number(p, zero_t);
    mp_init_transform_node(mp, p);
    q = mp_get_value_node(p);
    mp_tx_part(q)->type = mp_known_type;
    mp_set_value_number(mp_tx_part(q), zero_t);
    mp_ty_part(q)->type = mp_known_type;
    mp_set_value_number(mp_ty_part(q), zero_t);
    mp_xy_part(q)->type = mp_known_type;
    mp_set_value_number(mp_xy_part(q), zero_t);
    mp_yx_part(q)->type = mp_known_type;
    mp_set_value_number(mp_yx_part(q), zero_t);
    mp_xx_part(q)->type = mp_known_type;
    mp_set_value_number(mp_xx_part(q), unity_t);
    mp_yy_part(q)->type = mp_known_type;
    mp_set_value_number(mp_yy_part(q), unity_t);
    return p;
}

static void mp_new_root (MP mp, mp_sym x)
{
    mp_node p = mp_new_value_node(mp);
    p->type = mp_undefined_type;
    p->name_type = mp_root_operation;
    mp_set_value_sym(p, x);
    set_equiv_node(x, p);
}

void mp_print_variable_name (MP mp, mp_node p)
{
    mp_node q = NULL;
    mp_node r = NULL;
    while (p->name_type >= mp_x_part_operation) {
        switch (p->name_type) {
            case mp_x_part_operation      : mp_print_str(mp, "xpart ");         break;
            case mp_y_part_operation      : mp_print_str(mp, "ypart ");         break;
            case mp_xx_part_operation     : mp_print_str(mp, "xxpart ");        break;
            case mp_xy_part_operation     : mp_print_str(mp, "xypart ");        break;
            case mp_yx_part_operation     : mp_print_str(mp, "yxpart ");        break;
            case mp_yy_part_operation     : mp_print_str(mp, "yypart ");        break;
            case mp_red_part_operation    : mp_print_str(mp, "redpart ");       break;
            case mp_green_part_operation  : mp_print_str(mp, "greenpart ");     break;
            case mp_blue_part_operation   : mp_print_str(mp, "bluepart ");      break;
            case mp_cyan_part_operation   : mp_print_str(mp, "cyanpart ");      break;
            case mp_magenta_part_operation: mp_print_str(mp, "magentapart ");   break;
            case mp_yellow_part_operation : mp_print_str(mp, "yellowpart ");    break;
            case mp_black_part_operation  : mp_print_str(mp, "blackpart ");     break;
            case mp_grey_part_operation   : mp_print_str(mp, "greypart ");      break;
            case mp_capsule_operation     : mp_print_fmt(mp, "%%CAPSULE%p", p); return;
            default                       :                                     break;
        }
        p = p->link;
    }
    while (p->name_type > mp_saved_root_operation) {
        if (p->name_type == mp_subscript_operation) {
            r = mp_new_num_tok(mp, &(mp_subscript(p)));
            do {
                p = p->link;
            } while (p->name_type != mp_attribute_operation);
        } else if (p->name_type == mp_structured_root_operation) {
            p = p->link;
            goto FOUND;
        } else if (p->name_type != mp_attribute_operation) {
            mp_confusion(mp, "variable");
            return;
        } else {
            r = mp_new_symbolic_node(mp);
            mp_set_sym_sym(r, mp_get_hashloc(p));
        }
        mp_set_link(r, q);
        q = r;
    FOUND:
        p = mp_get_parent((mp_value_node) p);
    }
    r = mp_new_symbolic_node(mp);
    mp_set_sym_sym(r, mp_get_value_sym(p));
    r->link = q;
    if (p->name_type == mp_saved_root_operation) {
        mp_print_str(mp, "(SAVED)");
    }
    mp_show_token_list(mp, r, NULL);
    mp_flush_token_list(mp, r);
}

static int mp_interesting (MP mp, mp_node p)
{
    if (number_positive(internal_value(mp_tracing_capsules_internal))) {
        return 1;
    } else {
        mp_name_type_type t = p->name_type;
        if (t >= mp_x_part_operation && t != mp_capsule_operation) {
            mp_node tt = mp_get_value_node(p->link);
            switch (t) {
                case mp_x_part_operation:       t = mp_x_part      (tt)->name_type; break;
                case mp_y_part_operation:       t = mp_y_part      (tt)->name_type; break;
                case mp_xx_part_operation:      t = mp_xx_part     (tt)->name_type; break;
                case mp_xy_part_operation:      t = mp_xy_part     (tt)->name_type; break;
                case mp_yx_part_operation:      t = mp_yx_part     (tt)->name_type; break;
                case mp_yy_part_operation:      t = mp_yy_part     (tt)->name_type; break;
                case mp_red_part_operation:     t = mp_red_part    (tt)->name_type; break;
                case mp_green_part_operation:   t = mp_green_part  (tt)->name_type; break;
                case mp_blue_part_operation:    t = mp_blue_part   (tt)->name_type; break;
                case mp_cyan_part_operation:    t = mp_cyan_part   (tt)->name_type; break;
                case mp_magenta_part_operation: t = mp_magenta_part(tt)->name_type; break;
                case mp_yellow_part_operation:  t = mp_yellow_part (tt)->name_type; break;
                case mp_black_part_operation:   t = mp_black_part  (tt)->name_type; break;
                case mp_grey_part_operation:    t = mp_grey_part   (tt)->name_type; break;
                default:                                                            break;
            }
        }
        return (t != mp_capsule_operation);
    }
}

static mp_node mp_new_structure (MP mp, mp_node p)
{
    mp_node r = NULL;
    switch (p->name_type) {
        case mp_root_operation:
            {
                mp_sym q = mp_get_value_sym(p);
                r = mp_new_value_node(mp);
                set_equiv_node(q, r);
            }
            break;
        case mp_subscript_operation:
            {
                mp_node q_new;
                mp_node q = p;
                do {
                    q = q->link;
                } while (q->name_type != mp_attribute_operation);
                q = mp_get_parent((mp_value_node) q);
                r = mp->temp_head;
                mp_set_link(r, mp_get_subscr_head(q));
                do {
                    q_new = r;
                    r = r->link;
                } while (r != p);
                r = (mp_node) mp_get_subscr_node(mp);
                if (q_new == mp->temp_head) {
                    mp_set_subscr_head(q, r);
                } else {
                    mp_set_link(q_new, r);
                }
                number_clone(mp_subscript(r), mp_subscript(p));
            }
            break;
        case mp_attribute_operation:
            {
                mp_value_node rr;
                mp_node q = mp_get_parent((mp_value_node) p);
                r = mp_get_attribute_head(q);
                do {
                    q = r;
                    r = r->link;
                } while (r != p);
                rr = mp_get_attribute_node(mp);
                r = (mp_node) rr;
                mp_set_link(q, rr);
                mp_set_hashloc(rr, mp_get_hashloc(p));
                mp_set_parent(rr, mp_get_parent((mp_value_node) p));
                if (mp_get_hashloc(p) == mp_collective_subscript) {
                    q = mp->temp_head;
                    mp_set_link(q, mp_get_subscr_head(mp_get_parent((mp_value_node) p)));
                    while (q->link != p) {
                        q = q->link;
                    }
                    if (q == mp->temp_head) {
                        mp_set_subscr_head(mp_get_parent((mp_value_node) p), (mp_node) rr);
                    } else {
                        mp_set_link(q, rr);
                    }
                }
            }
            break;
        default:
            mp_confusion(mp, "structure");
            break;
    }
    if (r) {
        mp_value_node q;
        mp_set_link(r, p->link);
        mp_set_value_sym(r, mp_get_value_sym(p));
        r->type = mp_structured_type;
        r->name_type = p->name_type;
        mp_set_attribute_head(r, p);
        p->name_type = mp_structured_root_operation;
        q = mp_get_attribute_node(mp);
        mp_set_link(p, q);
        mp_set_subscr_head(r, (mp_node) q);
        mp_set_parent(q, r);
        q->type = mp_undefined_type;
        q->name_type = mp_attribute_operation;
        mp_set_link(q, mp->end_attr);
        mp_set_hashloc(q, mp_collective_subscript);
    }
    return r;
}

static mp_node mp_find_variable (MP mp, mp_node t)
{
    mp_sym p_sym = mp_get_sym_sym(t);

    if (eq_type(p_sym) != mp_tag_command) {
        return NULL;
    } else {
        mp_node p, q, r, s;
        mp_node pp, qq, rr, ss;
        t = t->link;
        if (equiv_node(p_sym) == NULL) {
            mp_new_root (mp, p_sym);
        }
        p = equiv_node(p_sym);
        pp = p;
        while (t != NULL) {
            if (pp->type != mp_structured_type) {
                if (pp->type > mp_structured_type) {
                    return NULL;
                } else {
                    ss = mp_new_structure(mp, pp);
                    if (p == pp) {
                        p = ss;
                    }
                    pp = ss;
                }
            }
            if (p->type != mp_structured_type) {
                p = mp_new_structure(mp, p);
            }
            if (t->type != mp_symbol_node_type) {
                mp_number nn, save_subscript;
                new_number_clone(nn, mp_get_value_number(t));
                pp = mp_get_attribute_head(pp)->link;
                q = mp_get_attribute_head(p)->link;
                new_number_clone(save_subscript, mp_subscript(q));
                set_number_to_inf(mp_subscript(q));
                s = mp->temp_head;
                mp_set_link(s, mp_get_subscr_head(p));
                do {
                    r = s;
                    s = s->link;
                } while (number_greater(nn, mp_subscript(s)));
                if (number_equal(nn, mp_subscript(s))) {
                    p = s;
                } else {
                    mp_value_node p1 = mp_get_subscr_node(mp);
                    if (r == mp->temp_head) {
                        mp_set_subscr_head(p, (mp_node) p1);
                    } else {
                        mp_set_link(r, p1);
                    }
                    mp_set_link(p1, s);
                    number_clone(mp_subscript(p1), nn);
                    p1->name_type = mp_subscript_operation;
                    p1->type = mp_undefined_type;
                    p = (mp_node) p1;
                }
                number_clone(mp_subscript(q), save_subscript);
                free_number(save_subscript);
                free_number(nn);
            } else {
                mp_sym nn1 = mp_get_sym_sym(t);
                ss = mp_get_attribute_head(pp);
                do {
                    rr = ss;
                    ss = ss->link;
                } while (nn1 > mp_get_hashloc(ss));
                if (nn1 < mp_get_hashloc(ss)) {
                    qq = (mp_node) mp_get_attribute_node(mp);
                    mp_set_link(rr, qq);
                    mp_set_link(qq, ss);
                    mp_set_hashloc(qq, nn1);
                    qq->name_type = mp_attribute_operation;
                    qq->type = mp_undefined_type;
                    mp_set_parent((mp_value_node) qq, pp);
                    ss = qq;
                }
                if (p == pp) {
                    p = ss;
                    pp = ss;
                } else {
                    pp = ss;
                    s = mp_get_attribute_head(p);
                    do {
                        r = s;
                        s = s->link;
                    } while (nn1 > mp_get_hashloc(s));
                    if (nn1 == mp_get_hashloc(s)) {
                        p = s;
                    } else {
                        q = (mp_node) mp_get_attribute_node(mp);
                        mp_set_link(r, q);
                        mp_set_link(q, s);
                        mp_set_hashloc(q, nn1);
                        q->name_type = mp_attribute_operation;
                        q->type = mp_undefined_type;
                        mp_set_parent((mp_value_node) q, p);
                        p = q;
                    }
                }
            }
            t = t->link;
        }
        if (pp->type >= mp_structured_type) {
            if (pp->type == mp_structured_type) {
                pp = mp_get_attribute_head(pp);
            } else {
                return NULL;
            }
        }
        if (p->type == mp_structured_type) {
            p = mp_get_attribute_head(p);
        }
        if (p->type == mp_undefined_type) {
            if (pp->type == mp_undefined_type) {
                pp->type = mp_numeric_type;
                mp_set_value_number(pp, zero_t);
            }
            p->type = pp->type;
            mp_set_value_number(p, zero_t);
        }
        return p;
    }
}

static void mp_flush_variable (MP mp, mp_node p, mp_node t, int discard_suffixes)
{
    while (t != NULL) {
        if (p->type != mp_structured_type) {
            return;
        } else {
            mp_sym n = mp_get_sym_sym(t);
            t = t->link;
            if (n == mp_collective_subscript) {
                mp_node q = mp_get_subscr_head(p);
                mp_node r = NULL;
                while (q->name_type == mp_subscript_operation) {
                    mp_flush_variable(mp, q, t, discard_suffixes);
                    if (t != NULL) {
                        r = q;
                    } else if (q->type == mp_structured_type) {
                        r = q;
                    } else {
                        if (r == NULL) {
                            mp_set_subscr_head(p, q->link);
                        } else {
                            mp_set_link(r, q->link);
                        }
                        mp_free_value_node(mp, q);
                    }
                    q = r == NULL ? mp_get_subscr_head(p) : r->link;
                }
            }
            p = mp_get_attribute_head(p);
            do {
                p = p->link;
            } while (mp_get_hashloc(p) < n);
            if (mp_get_hashloc(p) != n) {
                return;
            }
        }
    }
    if (discard_suffixes) {
        mp_flush_below_variable(mp, p);
    } else {
        if (p->type == mp_structured_type) {
            p = mp_get_attribute_head(p);
        }
        mp_recycle_value(mp, p);
    }
}

void mp_flush_below_variable (MP mp, mp_node p)
{
    if (p->type != mp_structured_type) {
        mp_recycle_value(mp, p);
    } else {
        mp_node r;
        mp_node q = mp_get_subscr_head(p);
        while (q->name_type == mp_subscript_operation) {
            mp_flush_below_variable(mp, q);
            r = q;
            q = q->link;
            mp_free_value_node(mp, r);
        }
        r = mp_get_attribute_head(p);
        q = r->link;
        mp_recycle_value(mp, r);
        mp_free_value_node(mp, r);
        do {
            mp_flush_below_variable(mp, q);
            r = q;
            q = q->link;
            mp_free_value_node(mp, r);
        } while (q != mp->end_attr);
        p->type = mp_undefined_type;
    }
}

static int mp_und_type (MP mp, mp_node p)
{
    (void) mp;
    switch (p->type) {
        case mp_vacuous_type:
            return mp_undefined_type;
        case mp_boolean_type:
        case mp_unknown_boolean_type:
            return mp_unknown_boolean_type;
        case mp_string_type:
        case mp_unknown_string_type:
            return mp_unknown_string_type;
        case mp_pen_type:
        case mp_unknown_pen_type:
            return mp_unknown_pen_type;
        case mp_nep_type:
        case mp_unknown_nep_type:
            return mp_unknown_nep_type;
        case mp_path_type:
        case mp_unknown_path_type:
            return mp_unknown_path_type;
        case mp_picture_type:
        case mp_unknown_picture_type:
            return mp_unknown_picture_type;
        case mp_transform_type:
        case mp_color_type:
        case mp_cmykcolor_type:
        case mp_pair_type:
        case mp_numeric_type:
            return p->type;
        case mp_known_type:
        case mp_dependent_type:
        case mp_proto_dependent_type:
        case mp_independent_type:
            return mp_numeric_type;
        default:
            return 0;
    }
}

static void mp_clear_symbol (MP mp, mp_sym p, int saving)
{
    mp_node q = equiv_node(p);
    if (eq_property(p) > 0) {
        mp_check_overload(mp, p);
    }
    switch (eq_type(p)) {
        case mp_defined_macro_command:
        case mp_primary_def_command:
        case mp_secondary_def_command:
        case mp_tertiary_def_command:
            if (!saving) {
                mp_delete_mac_ref(mp, q);
            }
            break;
        case mp_tag_command:
            if (q != NULL) {
                if (saving) {
                    q->name_type = mp_saved_root_operation;
                } else {
                    mp_flush_below_variable(mp, q);
                    mp_free_value_node(mp, q);
                }
            }
            break;
        default:
            break;
    }
    set_equiv(p, mp->frozen_undefined->v.data.indep.serial);
    set_eq_type(p, mp->frozen_undefined->type);
}

static void mp_save_boundary (MP mp)
{
    mp_save_data *p = mp_memory_allocate(sizeof(mp_save_data));
    p->type = 0;
    p->link = mp->save_ptr;
    mp->save_ptr = p;
}

static void mp_save_variable (MP mp, mp_sym q)
{
    if (mp->save_ptr != NULL) {
        mp_save_data *p = mp_memory_allocate(sizeof(mp_save_data));
        p->type = mp_normal_operation;
        p->link = mp->save_ptr;
        p->value.v.data.indep.scale = eq_type(q);
        p->value.v.data.indep.serial = equiv(q);
        p->value.v.data.node = equiv_node(q);
        p->value.v.data.p = (mp_knot)q;
        mp->save_ptr = p;
    }
    mp_clear_symbol(mp, q, (mp->save_ptr != NULL));
}

static void mp_unsave_variable (MP mp)
{
    mp_sym q = (mp_sym)mp->save_ptr->value.v.data.p;
    if (number_positive(internal_value(mp_tracing_restores_internal))) {
        mp_begin_diagnostic(mp);
        mp_print_nl(mp, "{restoring ");
        mp_print_mp_str(mp,text(q));
        mp_print_chr(mp, '}');
        mp_end_diagnostic(mp, 0);
    }
    mp_clear_symbol(mp, q, 0);
    set_eq_type(q, mp->save_ptr->value.v.data.indep.scale);
    set_equiv(q,mp->save_ptr->value.v.data.indep.serial);
    q->v.data.node = mp->save_ptr->value.v.data.node;

    if (eq_type(q) == mp_tag_command) {
        mp_node pp = q->v.data.node;
        if (pp != NULL) {
            pp->name_type = mp_root_operation;
        }
    }
}

static void mp_save_internal (MP mp, int q)
{
    if (mp->save_ptr != NULL) {
        mp_save_data *p = mp_memory_allocate(sizeof(mp_save_data));
        p->type = mp_internal_operation;
        p->link = mp->save_ptr;
        p->value = mp->internal[q];
        p->value.v.data.indep.serial = q;
        if (internal_run(q) == 1) {
            mp->run_internal(mp, 1, q, internal_type(q), internal_name(q));
        }
        new_number_clone(p->value.v.data.n, mp->internal[q].v.data.n);
        mp->save_ptr = p;
    }
}

static void mp_unsave_internal (MP mp)
{
    int q = mp->save_ptr->value.v.data.indep.serial;
    mp_internal saved = mp->save_ptr->value;
    if (number_positive(internal_value(mp_tracing_restores_internal))) {
        mp_begin_diagnostic(mp);
        mp_print_nl(mp, "{restoring ");
        mp_print_str(mp, internal_name(q));
        mp_print_chr(mp, '=');
        switch (internal_type(q)) {
            case mp_known_type:
            case mp_numeric_type:
                print_number(saved.v.data.n);
                break;
            case mp_boolean_type:
                mp_print_str(mp, number_to_boolean(saved.v.data.n) == mp_true_operation ? "true" : "false");
                break;
            case mp_string_type:
                {
                    char *s = mp_str(mp, saved.v.data.str);
                    mp_print_str(mp, s);
                    break;
                }
            default:
                mp_confusion(mp, "internal restore");
                break;
        }
        mp_print_chr(mp, '}');
        mp_end_diagnostic(mp, 0);
    }
    free_number(mp->internal[q].v.data.n);
    if (internal_run(q) == 1) {
        mp->run_internal(mp, 2, q, internal_type(q), internal_name(q));
    }
    mp->internal[q] = saved;
}

static void mp_unsave (MP mp)
{
    mp_save_data *p;
    while (mp->save_ptr->type != 0) {
        if (mp->save_ptr->type == mp_internal_operation) {
            mp_unsave_internal(mp);
        } else {
            mp_unsave_variable(mp);
        }
        p = mp->save_ptr->link;
        mp_memory_free(mp->save_ptr);
        mp->save_ptr = p;
    }
    p = mp->save_ptr->link;
    mp_memory_free(mp->save_ptr);
    mp->save_ptr = p;
}

void mp_pr_path (MP mp, mp_knot h)
{
    mp_knot p = h;
    do {
        mp_knot q = mp_next_knot(p);
        if ((p == NULL) || (q == NULL)) {
            mp_print_nl(mp, "???");
            return;
        } else {
            mp_print_two(mp, &(p->x_coord), &(p->y_coord));
            switch (mp_knotstate(p)) {
                case mp_begin_knot:
                    mp_print_str(mp, " {begin}");
                    break;
                case mp_end_knot:
                    mp_print_str(mp, " {end}");
                    break;
            }
            switch (mp_right_type(p)) {
                case mp_endpoint_knot:
                    {
                        if (mp_left_type(p) == mp_open_knot) {
                            mp_print_str(mp, " {open?}");
                        }
                        if ((mp_left_type(q) != mp_endpoint_knot) || (q != h)) {
                            q = NULL;
                        }
                        goto DONE1;
                    }
                    break;
                case mp_explicit_knot:
                    {
                        mp_print_str(mp, " .. controls ");
                        mp_print_two(mp, &(p->right_x), &(p->right_y));
                        mp_print_str(mp, " and ");
                        if (mp_left_type(q) != mp_explicit_knot) {
                            mp_print_str(mp, "??");
                        } else {
                            mp_print_two(mp, &(q->left_x), &(q->left_y));
                        }
                        goto DONE1;
                    }
                    break;
                case mp_open_knot:
                    {
                        if ((mp_left_type(p) != mp_explicit_knot) && (mp_left_type(p) != mp_open_knot)) {
                            mp_print_str(mp, " {open?}");
                        }
                    }
                    break;
                case mp_curl_knot:
                case mp_given_knot:
                    {
                        if (mp_left_type(p) == mp_open_knot) {
                            mp_print_str(mp, " ??");
                        }
                        if (mp_right_type(p) == mp_curl_knot) {
                            mp_print_str(mp, " {curl");
                            print_number(p->right_curl);
                        } else {
                            mp_number n_sin, n_cos;
                            new_fraction(n_sin);
                            new_fraction(n_cos);
                            n_sin_cos(p->right_given, n_cos, n_sin);
                            mp_print_str(mp, " {");
                            print_number(n_cos);
                            mp_print_chr(mp, ',');
                            print_number(n_sin);
                            free_number(n_sin);
                            free_number(n_cos);
                        }
                        mp_print_str(mp, "} ");
                    }
                    break;
                default:
                    {
                        mp_print_str(mp, "???");
                    }
                    break;
            }
            if (mp_left_type(q) <= mp_explicit_knot) {
                mp_print_str(mp, " .. control ?");
            } else if ((! number_equal(p->right_tension, unity_t)) || (! number_equal(q->left_tension, unity_t))) {
                mp_number v1;
                mp_print_str(mp, " .. tension");
                if (number_negative(p->right_tension)) {
                    mp_print_str(mp, " atleast");
                }
                new_number_abs(v1, p->right_tension);
                print_number(v1);
                if (! number_equal(p->right_tension, q->left_tension)) {
                    mp_print_str(mp, " and");
                    if (number_negative(q->left_tension)) {
                        mp_print_str(mp, " atleast");
                    }
                    number_abs_clone(v1, p->left_tension);
                    print_number(v1);
                }
                free_number(v1);
            }

          DONE1:
            p = q;
            if (p && ((p != h) || (mp_left_type(h) != mp_endpoint_knot))) {
                mp_number n_sin, n_cos;
                new_fraction(n_sin);
                new_fraction(n_cos);
                mp_print_nl(mp, " .. ");
                if (mp_left_type(p) == mp_given_knot) {
                    n_sin_cos(p->left_given, n_cos, n_sin);
                    mp_print_str(mp, "{");
                    print_number(n_cos);
                    mp_print_chr(mp, ',');
                    print_number(n_sin);
                    mp_print_chr(mp, '}');
                } else if (mp_left_type(p) == mp_curl_knot) {
                    mp_print_str(mp, "{curl ");
                    print_number(p->left_curl);
                    mp_print_chr(mp, '}');
                }
                free_number(n_sin);
                free_number(n_cos);
            }
        }
    } while (p != h);
    if (mp_left_type(h) != mp_endpoint_knot) {
        mp_print_str(mp, " cycle");
    }
}

void mp_print_path (MP mp, mp_knot h, const char *s, int nuline)
{
    mp_print_diagnostic(mp, "Path", s, nuline);
    mp_print_ln(mp);
    mp_pr_path(mp, h);
    mp_end_diagnostic(mp, 1);
}

static mp_knot mp_new_knot (MP mp)
{
    mp_knot q;
    if (mp->knot_nodes) {
        q = mp->knot_nodes;
        mp->knot_nodes = q->next;
        mp->num_knot_nodes--;
    } else {
        q = mp_memory_clear_allocate(sizeof(struct mp_knot_data));
    }
    new_number(q->x_coord);
    new_number(q->y_coord);
    new_number(q->left_x);
    new_number(q->left_y);
    new_number(q->right_x);
    new_number(q->right_y);
    mp_knotstate(q) = mp_regular_knot;
    mp_originator(q) = mp_regular_knot;
    return q;
}

static mp_gr_knot mp_gr_new_knot (MP mp)
{
    mp_gr_knot q = mp_memory_allocate(sizeof(struct mp_gr_knot_data));
    (void) mp;
    return q;
}

static mp_knot mp_copy_knot (MP mp, mp_knot p)
{
    mp_knot q;
    if (mp->knot_nodes) {
        q = mp->knot_nodes;
        mp->knot_nodes = q->next;
        mp->num_knot_nodes--;
    } else {
        q = mp_memory_allocate(sizeof(struct mp_knot_data));
    }
    memcpy(q, p, sizeof(struct mp_knot_data));
    if (mp->math_mode > mp_math_double_mode) {
        new_number_clone(q->x_coord, p->x_coord);
        new_number_clone(q->y_coord, p->y_coord);
        new_number_clone(q->left_x, p->left_x);
        new_number_clone(q->left_y, p->left_y);
        new_number_clone(q->right_x, p->right_x);
        new_number_clone(q->right_y, p->right_y);
    }
    mp_prev_knot(q) = NULL;
    mp_next_knot(q) = NULL;
    return q;
}

static mp_gr_knot mp_export_knot (MP mp, mp_knot p)
{
    mp_gr_knot q = mp_gr_new_knot(mp);
    q->x_coord    = number_to_double(p->x_coord);
    q->y_coord    = number_to_double(p->y_coord);
    q->left_x     = number_to_double(p->left_x);
    q->left_y     = number_to_double(p->left_y);
    q->right_x    = number_to_double(p->right_x);
    q->right_y    = number_to_double(p->right_y);
    q->left_type  = p->left_type;
    q->right_type = p->right_type;
    q->info       = p->info;
    q->originator = p->originator;
    q->state      = p->state;
    q->prev       = NULL;
    q->next       = NULL;
    return q;
}

static mp_knot mp_copy_path (MP mp, mp_knot p)
{
    if (p == NULL) {
        return NULL;
    } else {
        mp_knot q = mp_copy_knot(mp, p);
        mp_knot qq = q;
        mp_knot pp = mp_next_knot(p);
        while (pp != p) {
            mp_knot k = mp_copy_knot(mp, pp);
            mp_next_knot(qq) = k;
            mp_prev_knot(k) = qq;
            qq = mp_next_knot(qq);
            pp = mp_next_knot(pp);
        }
        mp_next_knot(qq) = q;
        mp_prev_knot(q) = qq;
        return q;
    }
}

static mp_gr_knot mp_export_path (MP mp, mp_knot p)
{
    if (p == NULL) {
        return NULL;
    } else {
        mp_gr_knot q = mp_export_knot(mp, p);
        mp_gr_knot qq = q;
        mp_knot pp = mp_next_knot(p);
        while (pp != p) {
            mp_gr_knot k = mp_export_knot(mp, pp);
            mp_prev_knot(k) = qq;
            mp_next_knot(qq) = k;
            qq = k;
            pp = mp_next_knot(pp);
        }
        mp_prev_knot(q) = qq;
        mp_next_knot(qq) = q;
        return q;
    }
}

static mp_gr_knot mp_export_knot_list (MP mp, mp_knot p)
{
    if (p == NULL) {
        return NULL;
    } else {
        mp_gr_knot q = mp_export_path(mp, p);
        return q;
    }
}

static mp_knot mp_htap_ypoc (MP mp, mp_knot p)
{
    mp_knot q = mp_new_knot(mp);
    mp_knot qq = q;
    mp_knot pp = p;
    while (1) {
        mp_right_type(qq) = mp_left_type(pp);
        mp_left_type(qq) = mp_right_type(pp);
        number_clone(qq->x_coord, pp->x_coord);
        number_clone(qq->y_coord, pp->y_coord);
        number_clone(qq->right_x, pp->left_x);
        number_clone(qq->right_y, pp->left_y);
        number_clone(qq->left_x, pp->right_x);
        number_clone(qq->left_y, pp->right_y);
        mp_originator(qq) = mp_originator(pp);
        mp_knotstate(qq) = mp_knotstate(pp);
        if (mp_next_knot(pp) == p) {
            mp_prev_knot(qq) = q;
            mp_next_knot(q) = qq;
            mp->path_tail = pp;
            return q;
        } else {
            mp_knot rr = mp_new_knot(mp);
            mp_prev_knot(qq) = rr;
            mp_next_knot(rr) = qq;
            qq = rr;
            pp = mp_next_knot(pp);
        }
    }
}

static void mp_free_knot (MP mp, mp_knot q)
{
    if (mp->math_mode > mp_math_double_mode) {
        free_number(q->x_coord);
        free_number(q->y_coord);
        free_number(q->left_x);
        free_number(q->left_y);
        free_number(q->right_x);
        free_number(q->right_y);
    }
    mp_memory_free(q);
}

static void mp_toss_knot (MP mp, mp_knot q)
{
    if (mp->num_knot_nodes < mp->max_knot_nodes) {
        mp_next_knot(q) = mp->knot_nodes;
        mp->knot_nodes = q;
        mp->num_knot_nodes++;
        if (mp->math_mode > mp_math_double_mode) {
            free_number(q->x_coord);
            free_number(q->y_coord);
            free_number(q->left_x);
            free_number(q->left_y);
            free_number(q->right_x);
            free_number(q->right_y);
        }
    } else {
        mp_free_knot(mp, q);
    }
}

static void mp_toss_knot_list (MP mp, mp_knot p)
{
    if (p == NULL) {
        return;
    } else {
        mp_knot q = p;
        do {
            mp_knot r = mp_next_knot(q);
            mp_toss_knot(mp, q);
            q = r;
        } while (q != p);
    }
}

void mp_make_choices  (MP mp, mp_knot knots)
{
    mp_knot h;
    mp_knot p, q;
    int k, n;
    mp_knot s, t;

    check_arith(mp);
    if (number_positive(internal_value(mp_tracing_choices_internal))) {
        mp_print_path(mp, knots, ", before choices", 1);
    }
    p = knots;
    do {
        q = mp_next_knot(p);
        if (number_equal(p->x_coord, q->x_coord) && number_equal(p->y_coord, q->y_coord) && mp_right_type(p) > mp_explicit_knot) {
            mp_right_type(p) = mp_explicit_knot;
            if (mp_left_type(p) == mp_open_knot) {
                mp_left_type(p) = mp_curl_knot;
                set_number_to_unity(p->left_curl);
            }
            mp_left_type(q) = mp_explicit_knot;
            if (mp_right_type(q) == mp_open_knot) {
                mp_right_type(q) = mp_curl_knot;
                set_number_to_unity(q->right_curl);
            }
            number_clone(p->right_x, p->x_coord);
            number_clone(q->left_x, p->x_coord);
            number_clone(p->right_y, p->y_coord);
            number_clone(q->left_y, p->y_coord);
        }
        p = q;
    } while (p != knots);

    h = knots;
    while (1) {
        if (mp_left_type(h) != mp_open_knot) {
            break;
        } else if (mp_right_type(h) != mp_open_knot) {
            break;
        } else {
            h = mp_next_knot(h);
            if (h == knots) {
                mp_left_type(h) = mp_end_cycle_knot;
                break;
            }
        }
    }
    p = h;
    do {
        q = mp_next_knot(p);
        if (mp_right_type(p) >= mp_given_knot) {
            while ((mp_left_type(q) == mp_open_knot) && (mp_right_type(q) == mp_open_knot)) {
                q = mp_next_knot(q);
            }
            {

                mp_number sine, cosine;
                mp_number arg1, arg2, r1, r2;
                mp_number delx, dely;
                new_fraction(sine);
                new_fraction(cosine);
                new_number(arg1);
                new_number(arg2);
                new_fraction(r1);
                new_fraction(r2);
                new_number(delx);
                new_number(dely);

                {
                  RESTART:
                    k = 0;
                    s = p;
                    n = mp->path_size;
                    do {
                        t = mp_next_knot(s);
                        set_number_from_subtraction(mp->delta_x[k], t->x_coord, s->x_coord);
                        set_number_from_subtraction(mp->delta_y[k], t->y_coord, s->y_coord);
                        pyth_add(mp->delta[k], mp->delta_x[k], mp->delta_y[k]);
                        if (k > 0) {
                            make_fraction(r1, mp->delta_y[k - 1], mp->delta[k - 1]);
                            number_clone(sine, r1);
                            make_fraction(r2, mp->delta_x[k - 1], mp->delta[k - 1]);
                            number_clone(cosine, r2);
                            take_fraction(r1, mp->delta_x[k], cosine);
                            take_fraction(r2, mp->delta_y[k], sine);
                            set_number_from_addition(arg1, r1, r2);
                            take_fraction(r1, mp->delta_y[k], cosine);
                            take_fraction(r2, mp->delta_x[k], sine);
                            set_number_from_subtraction(arg2, r1, r2);
                            n_arg(mp->psi[k], arg1, arg2 );
                        }
                        ++k;
                        s = t;
                        if (k == mp->path_size) {
                            mp_reallocate_paths(mp, mp->path_size + (mp->path_size / 4));
                            goto RESTART;
                        } else if (s == q) {
                            n = k;
                        }
                    } while (! ((k >= n) && (mp_left_type(s) != mp_end_cycle_knot)));
                    if (k == n) {
                        set_number_to_zero(mp->psi[k]);
                    } else {
                        number_clone(mp->psi[k], mp->psi[1]);
                    }
                }
                {
                    if (mp_left_type(q) == mp_open_knot) {
                        set_number_from_subtraction(delx, q->right_x, q->x_coord);
                        set_number_from_subtraction(dely, q->right_y, q->y_coord);
                        if (number_zero(delx) && number_zero(dely)) {
                            mp_left_type(q) = mp_curl_knot;
                            set_number_to_unity(q->left_curl);
                        } else {
                            mp_left_type(q) = mp_given_knot;
                            n_arg(q->left_given, delx, dely);
                        }
                    }
                    if ((mp_right_type(p) == mp_open_knot) && (mp_left_type(p) == mp_explicit_knot)) {
                        set_number_from_subtraction(delx, p->x_coord, p->left_x);
                        set_number_from_subtraction(dely, p->y_coord, p->left_y);
                        if (number_zero(delx) && number_zero(dely)) {
                            mp_right_type(p) = mp_curl_knot;
                            set_number_to_unity(p->right_curl);
                        } else {
                            mp_right_type(p) = mp_given_knot;
                            n_arg(p->right_given, delx, dely);
                        }
                    }
                }
                free_number(sine);
                free_number(cosine);
                free_number(arg1);
                free_number(arg2);
                free_number(r1);
                free_number(r2);
                free_number(delx);
                free_number(dely);

                mp_solve_choices(mp, p, q, n);
            }
        } else if (mp_right_type(p) == mp_endpoint_knot) {
            number_clone(p->right_x, p->x_coord);
            number_clone(p->right_y, p->y_coord);
            number_clone(q->left_x, q->x_coord);
            number_clone(q->left_y, q->y_coord);
        }
        p = q;
    } while (p != h);
    if (number_positive(internal_value(mp_tracing_choices_internal))) {
        mp_print_path(mp, knots, ", after choices", 1);
    }
    if (mp->arith_error) {
        mp_back_error(
            mp,
            "Some number got too big",
            "The path that I just computed is out of range. So it will probably look funny.\n"
            "Proceed, for a laugh."
        );
        mp_get_x_next(mp);
        mp->arith_error = 0;
    }
}

void mp_reallocate_paths (MP mp, int l)
{
    mp->delta_x = mp_memory_reallocate(mp->delta_x, (size_t) (l + 1) * sizeof(mp_number));
    mp->delta_y = mp_memory_reallocate(mp->delta_y, (size_t) (l + 1) * sizeof(mp_number));
    mp->delta = mp_memory_reallocate(mp->delta, (size_t) (l + 1) * sizeof(mp_number));
    mp->psi = mp_memory_reallocate(mp->psi, (size_t) (l + 1) * sizeof(mp_number));
    mp->theta = mp_memory_reallocate(mp->theta, (size_t) (l + 1) * sizeof(mp_number));
    mp->uu = mp_memory_reallocate(mp->uu, (size_t) (l + 1) * sizeof(mp_number));
    mp->vv = mp_memory_reallocate(mp->vv, (size_t) (l + 1) * sizeof(mp_number));
    mp->ww = mp_memory_reallocate(mp->ww, (size_t) (l + 1) * sizeof(mp_number));
    for (int k = mp->path_size; k<l; k++) {
        new_number(mp->delta_x[k]);
        new_number(mp->delta_y[k]);
        new_number(mp->delta[k]);
        new_angle(mp->psi[k]);
        new_angle(mp->theta[k]);
        new_fraction(mp->uu[k]);
        new_angle(mp->vv[k]);
        new_fraction(mp->ww[k]);
    }
    mp->path_size = l;
}

void mp_solve_choices (MP mp, mp_knot p, mp_knot q, int n)
{
    int k = 0;
    mp_knot r = 0;
    mp_knot s = p;
    mp_number ff;
    new_fraction(ff);
    while (1) {
        mp_knot t = mp_next_knot(s);
        if (k == 0) {
            switch (mp_right_type(s)) {
                case mp_given_knot:
                    if (mp_left_type(t) == mp_given_knot) {
                        {
                            mp_number arg1;
                            mp_number narg;
                            new_angle(narg);
                            n_arg(narg, mp->delta_x[0], mp->delta_y[0]);
                            new_number(arg1);
                            set_number_from_subtraction(arg1, p->right_given, narg);
                            n_sin_cos(arg1, mp->ct, mp->st);
                            set_number_from_subtraction(arg1, q->left_given, narg);
                            n_sin_cos(arg1, mp->cf, mp->sf);
                            number_negate(mp->sf);
                            mp_set_controls (mp, p, q, 0);
                            free_number(narg);
                            free_number(arg1);
                            free_number(ff);
                            return;
                        }
                    } else {
                        {
                            mp_number narg;
                            new_angle(narg);
                            n_arg(narg, mp->delta_x[0], mp->delta_y[0]);
                            set_number_from_subtraction(mp->vv[0], s->right_given, narg);
                            free_number(narg);
                            mp_reduce_angle(mp, &mp->vv[0]);
                            set_number_to_zero(mp->uu[0]);
                            set_number_to_zero(mp->ww[0]);
                        }
                    }
                    break;
                case mp_curl_knot:
                    if (mp_left_type(t) == mp_curl_knot) {
                        {
                            mp_number lt, rt;
                            mp_right_type(p) = mp_explicit_knot;
                            mp_left_type(q) = mp_explicit_knot;
                            new_number_abs(lt, q->left_tension);
                            new_number_abs(rt, p->right_tension);
                            if (number_unity(rt)) {
                                mp_number arg2;
                                new_number(arg2);
                                if (number_nonnegative(mp->delta_x[0])) {
                                    set_number_from_addition(arg2, mp->delta_x[0], epsilon_t);
                                } else {
                                    set_number_from_subtraction(arg2, mp->delta_x[0], epsilon_t);
                                }
                                number_int_div(arg2, 3);
                                set_number_from_addition(p->right_x, p->x_coord, arg2);
                                if (number_nonnegative(mp->delta_y[0])) {
                                    set_number_from_addition(arg2, mp->delta_y[0], epsilon_t);
                                } else {
                                    set_number_from_subtraction(arg2, mp->delta_y[0], epsilon_t);
                                }
                                number_int_div(arg2, 3);
                                set_number_from_addition(p->right_y, p->y_coord, arg2);
                                free_number(arg2);
                            } else {
                                mp_number arg2, r1;
                                new_fraction(r1);
                                new_number_clone(arg2, rt);
                                number_multiply_int(arg2, 3);
                                make_fraction(ff, unity_t, arg2);
                                free_number(arg2);
                                take_fraction(r1, mp->delta_x[0], ff);
                                set_number_from_addition(p->right_x, p->x_coord, r1);
                                take_fraction(r1, mp->delta_y[0], ff);
                                set_number_from_addition(p->right_y, p->y_coord, r1);
                            }
                            if (number_unity(lt)) {
                                mp_number arg2;
                                new_number(arg2);
                                if (number_nonnegative(mp->delta_x[0])) {
                                    set_number_from_addition(arg2, mp->delta_x[0], epsilon_t);
                                } else {
                                    set_number_from_subtraction(arg2, mp->delta_x[0], epsilon_t);
                                }
                                number_int_div(arg2, 3);
                                set_number_from_subtraction(q->left_x, q->x_coord, arg2);
                                if (number_nonnegative(mp->delta_y[0])) {
                                    set_number_from_addition(arg2, mp->delta_y[0], epsilon_t);
                                } else {
                                    set_number_from_subtraction(arg2, mp->delta_y[0], epsilon_t);
                                }
                                number_int_div(arg2, 3);
                                set_number_from_subtraction(q->left_y, q->y_coord, arg2);
                                free_number(arg2);
                            } else {
                                mp_number arg2, r1;
                                new_fraction(r1);
                                new_number_clone(arg2, lt);
                                number_multiply_int(arg2, 3);
                                make_fraction(ff, unity_t, arg2);
                                free_number(arg2);
                                take_fraction(r1, mp->delta_x[0], ff);
                                set_number_from_subtraction(q->left_x, q->x_coord, r1);
                                take_fraction(r1, mp->delta_y[0], ff);
                                set_number_from_subtraction(q->left_y, q->y_coord, r1);
                                free_number(r1);
                            }
                            free_number(ff);
                            free_number(lt);
                            free_number(rt);
                            return;
                        }
                    } else {
                        {
                            mp_number lt, rt, cc;
                            new_number_clone(cc, s->right_curl);
                            new_number_abs(lt, t->left_tension);
                            new_number_abs(rt, s->right_tension);
                            if (number_unity(rt) && number_unity(lt)) {
                                mp_number arg1, arg2;
                                new_number_clone(arg1, cc);
                                new_number_clone(arg2, cc);
                                number_double(arg1);
                                number_add(arg1, unity_t);
                                number_add(arg2, two_t);
                                make_fraction(mp->uu[0], arg1, arg2);
                                free_number(arg1);
                                free_number(arg2);
                            } else {
                                mp_curl_ratio(mp, &mp->uu[0], &cc, &rt, &lt);
                            }
                            take_fraction(mp->vv[0], mp->psi[1], mp->uu[0]);
                            number_negate(mp->vv[0]);
                            set_number_to_zero(mp->ww[0]);
                            free_number(rt);
                            free_number(lt);
                            free_number(cc);
                        }
                    }
                    break;
                case mp_open_knot:
                    set_number_to_zero(mp->uu[0]);
                    set_number_to_zero(mp->vv[0]);
                    number_clone(mp->ww[0], fraction_one_t);
                    break;
            }
        } else {
            switch (mp_left_type(s)) {
                case mp_end_cycle_knot:
                case mp_open_knot:
                    {
                        mp_number aa, bb, cc, acc;
                        mp_number dd, ee;
                        new_fraction(aa);
                        new_fraction(bb);
                        new_fraction(cc);
                        new_fraction(acc);
                        new_number(dd);
                        new_number(ee);
                        {
                            mp_number absval;
                            new_number_abs(absval, r->right_tension);
                            if (number_equal(absval, unity_t)) {
                                number_clone(aa, fraction_half_t);
                                number_clone(dd, mp->delta[k]);
                                number_double(dd);
                            } else {
                                mp_number arg1, arg2, ret;
                                new_number(arg1);
                                new_number_abs(arg2, r->right_tension);
                                number_multiply_int(arg2, 3);
                                number_subtract(arg2, unity_t);
                                make_fraction(aa, unity_t, arg2);
                                number_abs_clone(arg2, r->right_tension);
                                new_fraction(ret);
                                make_fraction(ret, unity_t, arg2);
                                set_number_from_subtraction(arg1, fraction_three_t, ret);
                                take_fraction(arg2, mp->delta[k], arg1);
                                number_clone(dd, arg2);
                                free_number(ret);
                                free_number(arg1);
                                free_number(arg2);
                            }
                            number_abs_clone(absval, t->left_tension);
                            if (number_equal(absval, unity_t)) {
                                number_clone(bb, fraction_half_t);
                                number_clone(ee, mp->delta[k - 1]);
                                number_double(ee);
                            } else {
                                mp_number arg1, arg2, ret;
                                new_number(arg1);
                                new_number_abs(arg2, t->left_tension);
                                number_multiply_int(arg2, 3);
                                number_subtract(arg2, unity_t);
                                make_fraction(bb, unity_t, arg2);
                                number_abs_clone(arg2, t->left_tension);
                                new_fraction(ret);
                                make_fraction(ret, unity_t, arg2);
                                set_number_from_subtraction(arg1, fraction_three_t, ret);
                                take_fraction(ee, mp->delta[k - 1], arg1);
                                free_number(ret);
                                free_number(arg1);
                                free_number(arg2);
                            }
                            free_number(absval);
                        }
                        {
                            mp_number r1;
                            new_number(r1);
                            take_fraction(r1, mp->uu[k - 1], aa);
                            set_number_from_subtraction(cc, fraction_one_t, r1);
                            free_number(r1);
                        }
                        {
                            mp_number rt, lt;
                            mp_number arg2;
                            new_number_clone(arg2, dd);
                            take_fraction(dd, arg2, cc);
                            new_number_abs(lt, s->left_tension);
                            new_number_abs(rt, s->right_tension);
                            if (! number_equal(lt, rt)) {
                                mp_number r1;
                                new_number(r1);
                                if (number_less(lt, rt)) {
                                    make_fraction(r1, lt, rt);
                                    take_fraction(ff, r1, r1);
                                    number_clone(r1, dd);
                                    take_fraction(dd, r1, ff);
                                } else {
                                    make_fraction(r1, rt, lt);
                                    take_fraction(ff, r1, r1);
                                    number_clone(r1, ee);
                                    take_fraction(ee, r1, ff);
                                }
                                free_number(r1);
                            }
                            free_number(rt);
                            free_number(lt);
                            set_number_from_addition(arg2, dd, ee);
                            make_fraction(ff, ee, arg2);
                            free_number(arg2);
                        }
                        take_fraction(mp->uu[k], ff, bb);
                        take_fraction(acc, mp->psi[k + 1], mp->uu[k]);
                        number_negate(acc);
                        if (mp_right_type(r) == mp_curl_knot) {
                            mp_number r1, arg2;
                            new_fraction(r1);
                            new_number(arg2);
                            set_number_from_subtraction(arg2, fraction_one_t, ff);
                            take_fraction(r1, mp->psi[1], arg2);
                            set_number_to_zero(mp->ww[k]);
                            set_number_from_subtraction(mp->vv[k], acc, r1);
                            free_number(r1);
                            free_number(arg2);
                        } else {
                            mp_number arg1, r1;
                            new_fraction(r1);
                            new_number(arg1);
                            set_number_from_subtraction(arg1, fraction_one_t, ff);
                            make_fraction(ff, arg1, cc);
                            free_number(arg1);
                            take_fraction(r1, mp->psi[k], ff);
                            number_subtract(acc, r1);
                            number_clone(r1, ff);
                            take_fraction(ff, r1, aa);
                            take_fraction(r1, mp->vv[k - 1], ff);
                            set_number_from_subtraction(mp->vv[k], acc, r1 );
                            if (number_zero(mp->ww[k - 1])) {
                                set_number_to_zero(mp->ww[k]);
                            } else {
                                take_fraction(mp->ww[k], mp->ww[k - 1], ff);
                                number_negate(mp->ww[k]);
                            }
                            free_number(r1);
                        }
                        if (mp_left_type(s) == mp_end_cycle_knot) {
                            mp_number arg2, r1;
                            new_number(arg2);
                            new_number(r1);
                            set_number_to_zero(aa);
                            number_clone(bb, fraction_one_t);
                            do {
                                --k;
                                if (k == 0) {
                                    k = n;
                                }
                                take_fraction(r1, aa, mp->uu[k]);
                                set_number_from_subtraction(aa, mp->vv[k], r1);
                                take_fraction(r1, bb, mp->uu[k]);
                                set_number_from_subtraction(bb, mp->ww[k], r1);
                            } while (k != n);
                            set_number_from_subtraction(arg2, fraction_one_t, bb);
                            make_fraction(r1, aa, arg2);
                            number_clone(aa, r1);
                            number_clone(mp->theta[n], aa);
                            number_clone(mp->vv[0], aa);
                            for (k = 1; k < n; k++) {
                                take_fraction(r1, aa, mp->ww[k]);
                                number_add(mp->vv[k], r1);
                            }
                            free_number(arg2);
                            free_number(r1);
                            free_number(aa);
                            free_number(bb);
                            free_number(cc);
                            free_number(acc);
                            free_number(dd);
                            free_number(ee);
                            goto FOUND;
                        }
                        free_number(aa);
                        free_number(bb);
                        free_number(cc);
                        free_number(acc);
                        free_number(dd);
                        free_number(ee);
                    }
                    break;
                case mp_curl_knot:
                    {
                        mp_number lt, rt, cc;
                        new_number_clone(cc, s->left_curl);
                        new_number_abs(lt, s->left_tension);
                        new_number_abs(rt, r->right_tension);
                        if (number_unity(rt) && number_unity(lt)) {
                            mp_number arg1, arg2;
                            new_number_clone(arg1, cc);
                            new_number_clone(arg2, cc);
                            number_double(arg1);
                            number_add(arg1, unity_t);
                            number_add(arg2, two_t);
                            make_fraction(ff, arg1, arg2);
                            free_number(arg1);
                            free_number(arg2);
                        } else {
                            mp_curl_ratio(mp, &ff, &cc, &lt, &rt);
                        }
                        {
                            mp_number arg1, arg2, r1;
                            new_fraction(r1);
                            new_fraction(arg1);
                            new_number(arg2);
                            take_fraction(arg1, mp->vv[n - 1], ff);
                            take_fraction(r1, ff, mp->uu[n - 1]);
                            set_number_from_subtraction(arg2, fraction_one_t, r1);
                            make_fraction(mp->theta[n], arg1, arg2);
                            number_negate(mp->theta[n]);
                            free_number(r1);
                            free_number(arg1);
                            free_number(arg2);
                        }
                        free_number(rt);
                        free_number(lt);
                        free_number(cc);
                        goto FOUND;
                    }
                    break;
                case mp_given_knot:
                    {
                        mp_number narg;
                        new_angle(narg);
                        n_arg(narg, mp->delta_x[n - 1], mp->delta_y[n - 1]);
                        set_number_from_subtraction(mp->theta[n], s->left_given, narg);
                        free_number(narg);
                        mp_reduce_angle(mp, &mp->theta[n]);
                        goto FOUND;
                    }
                    break;
            }
        }
        r = s;
        s = t;
        ++k;
    }
FOUND:
    {
        mp_number r1;
        new_number(r1);
        for (k = n - 1; k >= 0; k--) {
            take_fraction(r1, mp->theta[k + 1], mp->uu[k]);
            set_number_from_subtraction(mp->theta[k], mp->vv[k], r1);
        }
        free_number(r1);
    }
    s = p;
    k = 0;
    {
        mp_number arg;
        new_number(arg);
        do {
            mp_knot t = mp_next_knot(s);
            n_sin_cos(mp->theta[k], mp->ct, mp->st);
            number_negated_clone(arg, mp->psi[k + 1]);
            number_subtract(arg, mp->theta[k + 1]);
            n_sin_cos(arg, mp->cf, mp->sf);
            mp_set_controls (mp, s, t, k);
            ++k;
            s = t;
        } while (k != n);
        free_number(arg);
    }
    free_number(ff);
}

static void mp_reduce_angle (MP mp, mp_number *a)
{
    mp_number abs_a;
    new_number_abs(abs_a, *a);
    if (number_greater(abs_a, one_eighty_deg_t)) {
        if (number_positive(*a)) {
            number_subtract(*a, three_sixty_deg_t);
        } else {
            number_add(*a, three_sixty_deg_t);
        }
    }
    free_number(abs_a);
}

void mp_curl_ratio (MP mp, mp_number *ret, mp_number *gamma_orig, mp_number *a_tension, mp_number *b_tension)
{
    mp_number alpha, beta, gamma, num, denom, ff;
    mp_number arg1;
    new_number(arg1);
    new_fraction(alpha);
    new_fraction(beta);
    new_fraction(gamma);
    new_fraction(ff);
    new_fraction(denom);
    new_fraction(num);
    make_fraction(alpha, unity_t, *a_tension);
    make_fraction(beta, unity_t, *b_tension);
    number_clone(gamma, *gamma_orig);
    if (number_lessequal(alpha, beta)) {
        make_fraction(ff, alpha, beta);
        number_clone(arg1, ff);
        take_fraction(ff, arg1, arg1);
        number_clone(arg1, gamma);
        take_fraction(gamma, arg1, ff);
        convert_fraction_to_scaled(beta);
        take_fraction(denom, gamma, alpha);
        number_add(denom, three_t);
    } else {
        make_fraction(ff, beta, alpha);
        number_clone(arg1, ff);
        take_fraction(ff, arg1, arg1);
        take_fraction(arg1, beta, ff);
        convert_fraction_to_scaled(arg1);
        number_clone(beta, arg1);
        take_fraction(denom, gamma, alpha);
        set_number_from_div(arg1, ff, twelvebits_3);
        number_add(denom, arg1);
    }
    number_subtract(denom, beta);
    set_number_from_subtraction(arg1, fraction_three_t, alpha);
    take_fraction(num, gamma, arg1);
    number_add(num, beta);
    number_clone(arg1, denom);
    number_double(arg1);
    number_double(arg1);
    if (number_greaterequal(num, arg1)) {
        number_clone(*ret, fraction_four_t);
    } else {
        make_fraction(*ret, num, denom);
    }
    free_number(alpha);
    free_number(beta);
    free_number(gamma);
    free_number(num);
    free_number(denom);
    free_number(ff);
    free_number(arg1);
}

void mp_set_controls (MP mp, mp_knot p, mp_knot q, int k)
{
    mp_number rr, ss;
    mp_number lt, rt;
    mp_number sine;
    mp_number tmp;
    mp_number r1, r2;
    new_number(tmp);
    new_number(r1);
    new_number(r2);
    new_number_abs(lt, q->left_tension);
    new_number_abs(rt, p->right_tension);
    new_fraction(sine);
    new_fraction(rr);
    new_fraction(ss);
    velocity(rr, mp->st, mp->ct, mp->sf, mp->cf, rt);
    velocity(ss, mp->sf, mp->cf, mp->st, mp->ct, lt);
    if (number_negative(p->right_tension) || number_negative(q->left_tension)) {
        if ((number_nonnegative(mp->st) && number_nonnegative(mp->sf)) || (number_nonpositive(mp->st) && number_nonpositive(mp->sf))) {
            mp_number r1, r2, arg1;
            new_fraction(r1);
            new_fraction(r2);
            new_number_abs(arg1, mp->st);
            take_fraction(r1, arg1, mp->cf);
            number_abs_clone(arg1, mp->sf);
            take_fraction(r2, arg1, mp->ct);
            set_number_from_addition(sine, r1, r2);
            if (number_positive(sine)) {
                set_number_from_addition(arg1, fraction_one_t, unity_t);
                number_clone(r1, sine);
                take_fraction(sine, r1, arg1);
                if (number_negative(p->right_tension)) {
                    number_abs_clone(arg1, mp->sf);
                    if (ab_vs_cd(arg1, fraction_one_t, rr, sine) < 0) {
                        number_abs_clone(arg1, mp->sf);
                        make_fraction(rr, arg1, sine);
                    }
                }
                if (number_negative(q->left_tension)) {
                    number_abs_clone(arg1, mp->st);
                    if (ab_vs_cd(arg1, fraction_one_t, ss, sine) < 0) {
                        number_abs_clone(arg1, mp->st);
                        make_fraction(ss, arg1, sine);
                    }
                }
            }
            free_number(arg1);
            free_number(r1);
            free_number(r2);
        }
    }
    take_fraction(r1, mp->delta_x [k], mp->ct);
    take_fraction(r2, mp->delta_y [k], mp->st);
    number_subtract(r1, r2);
    take_fraction(tmp, r1, rr);
    set_number_from_addition(p->right_x, p->x_coord, tmp);
    take_fraction(r1, mp->delta_y[k], mp->ct);
    take_fraction(r2, mp->delta_x[k], mp->st);
    number_add(r1, r2);
    take_fraction(tmp, r1, rr);
    set_number_from_addition(p->right_y, p->y_coord, tmp);
    take_fraction(r1, mp->delta_x[k], mp->cf);
    take_fraction(r2, mp->delta_y[k], mp->sf);
    number_add(r1, r2);
    take_fraction(tmp, r1, ss);
    set_number_from_subtraction(q->left_x, q->x_coord, tmp);
    take_fraction(r1, mp->delta_y[k], mp->cf);
    take_fraction(r2, mp->delta_x[k], mp->sf);
    number_subtract(r1, r2);
    take_fraction(tmp, r1, ss);
    set_number_from_subtraction(q->left_y, q->y_coord, tmp);
    mp_right_type(p) = mp_explicit_knot;
    mp_left_type(q) = mp_explicit_knot;
    free_number(tmp);
    free_number(r1);
    free_number(r2);
    free_number(lt);
    free_number(rt);
    free_number(rr);
    free_number(ss);
    free_number(sine);
}

# define TOO_LARGE(a) (fabs((a))>4096.0)
# define PI           3.1415926535897932384626433832795028841971

static int out_of_range (MP mp, double a)
{
    mp_number t;
    (void) mp;
    new_number_from_double(mp, t, fabs(a));
    if (number_greaterequal(t, inf_t)) {
        free_number(t);
        return 1;
    } else {
        free_number(t);
        return 0;
    }
}

static int mp_link_knotpair (MP mp, mp_knot p, mp_knot q)
{
    (void) mp;
    if (p == NULL || q == NULL) {
        return 0;
    } else {
        mp_prev_knot(q) = p;
        mp_next_knot(p) = q;
        set_number_from_double(p->right_tension, 1.0);
        if (mp_right_type(p) == mp_endpoint_knot) {
            mp_right_type(p) = mp_open_knot;
        }
        set_number_from_double(q->left_tension, 1.0);
        if (mp_left_type(q) == mp_endpoint_knot) {
            mp_left_type(q) = mp_open_knot;
        }
        return 1;
    }
}

static int mp_link_knotpair_xy (MP mp, mp_knot p, mp_knot q)
{
    (void) mp;
    if (p == NULL || q == NULL) {
        return 0;
    } else {
        mp_prev_knot(q) = p;
        mp_next_knot(p) = q;
        return 1;
    }
}

int mp_close_path_cycle (MP mp, mp_knot p, mp_knot q)
{
    return mp_link_knotpair(mp, p, q);
}

int mp_close_path (MP mp, mp_knot q, mp_knot first)
{
    if (q == NULL || first == NULL) {
        return 0;
    } else {
        mp_prev_knot(first) = q;
        mp_next_knot(q) = first;
        mp_right_type(q) = mp_endpoint_knot;
        set_number_from_double(q->right_tension, 1.0);
        mp_left_type(first) = mp_endpoint_knot;
        set_number_from_double(first->left_tension, 1.0);
        return 1;
    }
}

mp_knot mp_create_knot (MP mp)
{
    mp_knot q = mp_new_knot(mp);
    mp_left_type(q) = mp_endpoint_knot;
    mp_right_type(q) = mp_endpoint_knot;
    return q;
}

int mp_set_knot (MP mp, mp_knot p, double x, double y)
{
    if (p == NULL) {
        return 0;
    } else if (out_of_range(mp, x)) {
        return 0;
    } else if (out_of_range(mp, y)) {
        return 0;
    } else {
        set_number_from_double(p->x_coord, x);
        set_number_from_double(p->y_coord, y);
        return 1;
    }
}

mp_knot mp_append_knot (MP mp, mp_knot p, double x, double y)
{
    mp_knot q = mp_create_knot(mp);
    if (q == NULL) {
        return NULL;
    } else if (! mp_set_knot(mp, q, x, y)) {
        mp_memory_free(q);
        return NULL;
    } else if (p == NULL) {
        return q;
    } else if (mp_link_knotpair(mp, p, q)) {
        return q;
    } else {
        mp_memory_free(q);
        return NULL;
    }
}

mp_knot mp_append_knot_xy (MP mp, mp_knot p, double x, double y)
{
    mp_knot q = mp_create_knot(mp);
    if (q == NULL) {
        return NULL;
    } else if (! mp_set_knot(mp, q, x, y)) {
        mp_memory_free(q);
        return NULL;
    } else if (p == NULL) {
        return q;
    } else if (mp_link_knotpair_xy(mp, p, q)) {
        mp_right_type(p) = mp_explicit_knot;
        mp_left_type(p) = mp_explicit_knot;
        return q;
    } else {
        mp_memory_free(q);
        return NULL;
    }
}

int mp_set_knot_curl (MP mp, mp_knot q, double value)
{
    if (q == NULL) {
        return 0;
    } else if (TOO_LARGE(value)) {
        return 0;
    } else {
        mp_right_type(q) = mp_curl_knot;
        set_number_from_double(q->right_curl, value);
        if (mp_left_type(q) == mp_open_knot) {
            mp_left_type(q) = mp_curl_knot;
            set_number_from_double(q->left_curl, value);
        }
        return 1;
    }
}

int mp_set_knot_left_curl (MP mp, mp_knot q, double value)
{
    if (q == NULL) {
        return 0;
    } else if (TOO_LARGE(value)) {
        return 0;
    } else {
        mp_left_type(q) = mp_curl_knot;
        set_number_from_double(q->left_curl, value);
        if (mp_right_type(q) == mp_open_knot) {
            mp_right_type(q) = mp_curl_knot;
            set_number_from_double(q->right_curl, value);
        }
        return 1;
    }
}

int mp_set_knot_right_curl (MP mp, mp_knot q, double value)
{
    if (q == NULL) {
        return 0;
    } else if (TOO_LARGE(value)) {
        return 0;
    } else {
        mp_right_type(q) = mp_curl_knot;
        set_number_from_double(q->right_curl, value);
        if (mp_left_type(q) == mp_open_knot) {
            mp_left_type(q) = mp_curl_knot;
            set_number_from_double(q->left_curl, value);
        }
        return 1;
    }
}

int mp_set_knot_simple_curl (MP mp, mp_knot q)
{
    if (q == NULL) {
        return 0;
    } else {
        mp_right_type(q) = mp_curl_knot;
        set_number_from_double(q->right_curl, 1.0);
        mp_left_type(q) = mp_curl_knot;
        set_number_from_double(q->left_curl, 1.0);
        return 1;
    }
}

int mp_set_knotpair_curls (MP mp, mp_knot p, mp_knot q, double t1, double t2)
{
    if (p == NULL || q == NULL) {
        return 0;
    } else if (mp_set_knot_curl(mp, p, t1)) {
        return mp_set_knot_curl(mp, q, t2);
    } else {
        return 0;
    }
}

int mp_set_knotpair_tensions (MP mp, mp_knot p, mp_knot q, double t1, double t2)
{
    if (p == NULL || q == NULL) {
        return 0;
    } else if (TOO_LARGE(t1)) {
        return 0;
    } else if (TOO_LARGE(t2)) {
        return 0;
    } else if ((fabs(t1) < 0.75)) {
        return 0;
    } else if ((fabs(t2) < 0.75)) {
        return 0;
    } else {
        set_number_from_double(p->right_tension, t1);
        set_number_from_double(q->left_tension, t2);
        return 1;
    }
}

int mp_set_knot_left_tension (MP mp, mp_knot p, double t1)
{
    if (p == NULL) {
        return 0;
    } else if (TOO_LARGE(t1)) {
        return 0;
    } else if ((fabs(t1) < 0.75)) {
        return 0;
    } else  {
        set_number_from_double(p->left_tension, t1);
        return 1;
    }
}

int mp_set_knot_right_tension (MP mp, mp_knot p, double t1)
{
    if (p == NULL) {
        return 0;
    } else if (TOO_LARGE(t1)) {
        return 0;
    } else if ((fabs(t1) < 0.75)) {
        return 0;
    } else {
        set_number_from_double(p->right_tension, t1);
        return 1;
    }
}

int mp_set_knotpair_controls (MP mp, mp_knot p, mp_knot q, double x1, double y1, double x2, double y2)
{
    if (p == NULL || q == NULL) {
        return 0;
    } else if (out_of_range(mp, x1)) {
        return 0;
    } else if (out_of_range(mp, y1)) {
        return 0;
    } else if (out_of_range(mp, x2)) {
        return 0;
    } else if (out_of_range(mp, y2)) {
        return 0;
    } else {
        mp_right_type(p) = mp_explicit_knot;
        set_number_from_double(p->right_x, x1);
        set_number_from_double(p->right_y, y1);
        mp_left_type(q) = mp_explicit_knot;
        set_number_from_double(q->left_x, x2);
        set_number_from_double(q->left_y, y2);
        return 1;
    }
}

int mp_set_knot_left_control (MP mp, mp_knot p, double x1, double y1)
{
    if (p == NULL) {
        return 0;
    } else if (out_of_range(mp, x1)) {
        return 0;
    } else if (out_of_range(mp, y1)) {
        return 0;
    } else {
        mp_left_type(p) = mp_explicit_knot;
        set_number_from_double(p->left_x, x1);
        set_number_from_double(p->left_y, y1);
        return 1;
    }
}

int mp_set_knot_right_control (MP mp, mp_knot p, double x1, double y1)
{
    if (p == NULL) {
        return 0;
    } else if (out_of_range(mp, x1)) {
        return 0;
    } else if (out_of_range(mp, y1)) {
        return 0;
    } else {
        mp_right_type(p) = mp_explicit_knot;
        set_number_from_double(p->right_x, x1);
        set_number_from_double(p->right_y, y1);
        return 1;
    }
}

int mp_set_knot_direction (MP mp, mp_knot q, double x, double y)
{
    if (q == NULL) {
        return 0;
    } else if (TOO_LARGE(x)) {
        return 0;
    } else if (TOO_LARGE(y)) {
        return 0;
    } else {
        double value = 0;
        if (!(x == 0 && y == 0)) {
            value = atan2(y, x) * (180.0 / PI)  * 16.0;
        }
        mp_right_type(q) = mp_given_knot;
        set_number_from_double(q->right_curl, value);
        if (mp_left_type(q) == mp_open_knot) {
            mp_left_type(q) = mp_given_knot;
            set_number_from_double(q->left_curl, value);
        }
        return 1;
    }
}

int mp_set_knotpair_directions (MP mp, mp_knot p, mp_knot q, double x1, double y1, double x2, double y2)
{
    if (p == NULL || q == NULL) {
        return 0;
    } else if (mp_set_knot_direction(mp,p, x1, y1)) {
        return mp_set_knot_direction(mp,q, x2, y2);
    } else {
        return 0;
    }
}

static int path_needs_fixing(mp_knot source)
{
    mp_knot sourcehead = source;
    do {
        source = source->next;
    } while (source && source != sourcehead);
    if (! source) {
        return 1;
    } else {
        return 0;
    }
}

int mp_solve_path (MP mp, mp_knot first)
{
    if (first == NULL) {
        return 0;
    } else if (path_needs_fixing(first)) {
        return 0;
    } else {
        int saved_arith_error = mp->arith_error;
        int retval = 1;
        jmp_buf *saved_jump_buf = mp->jump_buf;
        mp->jump_buf = mp_memory_allocate(sizeof(jmp_buf));
        if (mp->jump_buf == NULL || setjmp(*(mp->jump_buf)) != 0) {
            return 0;
        } else {
            mp->arith_error = 0;
            mp_make_choices(mp, first);
            if (mp->arith_error) {
                retval = 0;
            }
            mp->arith_error = saved_arith_error;
            mp_memory_free(mp->jump_buf);
            mp->jump_buf = saved_jump_buf;
            return retval;
        }
    }
}

void mp_free_path (MP mp, mp_knot p)
{
    mp_toss_knot_list(mp, p);
}

double mp_number_as_double (MP mp, mp_number n) {
    (void) mp;
    return number_to_double(n);
}

static void mp_eval_cubic (MP mp, mp_number *r, mp_knot p, mp_knot q, int c, mp_number *t)
{
    mp_number x1, x2, x3;
    new_number(x1);
    new_number(x2);
    new_number(x3);
    if (c == mp_x_code) {
        set_number_from_of_the_way(x1, *t, p->x_coord, p->right_x);
        set_number_from_of_the_way(x2, *t, p->right_x, q->left_x);
        set_number_from_of_the_way(x3, *t, q->left_x, q->x_coord);
    } else {
        set_number_from_of_the_way(x1, *t, p->y_coord, p->right_y);
        set_number_from_of_the_way(x2, *t, p->right_y, q->left_y);
        set_number_from_of_the_way(x3, *t, q->left_y, q->y_coord);
    }
    set_number_from_of_the_way(x1, *t, x1, x2);
    set_number_from_of_the_way(x2, *t, x2, x3);
    set_number_from_of_the_way(*r, *t, x1, x2);
    free_number(x1);
    free_number(x2);
    free_number(x3);
}

static void mp_bound_cubic (MP mp, mp_knot p, mp_knot q, int c)
{
    int wavy;
    mp_number del1, del2, del3, del, dmax;
    mp_number t, tt;
    mp_number x;
    new_fraction(t);
    new_fraction(tt);
    if (c == mp_x_code) {
        new_number_clone(x, q->x_coord);
    } else {
        new_number_clone(x, q->y_coord);
    }
    new_number(del1);
    new_number(del2);
    new_number(del3);
    new_number(del);
    new_number(dmax);
    if (number_less(x, mp->bbmin[c])) {
        number_clone(mp->bbmin[c], x);
    }
    if (number_greater(x, mp->bbmax[c])) {
        number_clone(mp->bbmax[c], x);
    }
    wavy = 1;
    if (c == mp_x_code) {
        if (number_lessequal(mp->bbmin[c], p->right_x) && number_lessequal(p->right_x, mp->bbmax[c])) {
            if (number_lessequal(mp->bbmin[c], q->left_x) && number_lessequal(q->left_x, mp->bbmax[c])) {
                wavy = 0;
            }
        }
    } else {
        if (number_lessequal(mp->bbmin[c], p->right_y) && number_lessequal(p->right_y, mp->bbmax[c])) {
            if (number_lessequal(mp->bbmin[c], q->left_y) && number_lessequal(q->left_y, mp->bbmax[c])) {
                wavy = 0;
            }
        }
    }
    if (wavy) {
        if (c == mp_x_code) {
            set_number_from_subtraction(del1, p->right_x, p->x_coord);
            set_number_from_subtraction(del2, q->left_x, p->right_x);
            set_number_from_subtraction(del3, q->x_coord, q->left_x);
        } else {
            set_number_from_subtraction(del1, p->right_y, p->y_coord);
            set_number_from_subtraction(del2, q->left_y, p->right_y);
            set_number_from_subtraction(del3, q->y_coord, q->left_y);
        }
        if (number_nonzero(del1)) {
            number_clone(del, del1);
        } else if (number_nonzero(del2)) {
            number_clone(del, del2);
        } else {
            number_clone(del, del3);
        }
        if (number_nonzero(del)) {
            mp_number absval1;
            new_number(absval1);
            number_abs_clone(dmax, del1);
            number_abs_clone(absval1, del2);
            if (number_greater(absval1, dmax)) {
                number_clone(dmax, absval1);
            }
            number_abs_clone(absval1, del3);
            if (number_greater(absval1, dmax)) {
                number_clone(dmax, absval1);
            }
            while (number_less(dmax, fraction_half_t)) {
                number_double(dmax);
                number_double(del1);
                number_double(del2);
                number_double(del3);
            }
            free_number(absval1);
        }
        if (number_negative(del)) {
            number_negate(del1);
            number_negate(del2);
            number_negate(del3);
        }
        crossing_point(t, del1, del2, del3);
        if (number_less(t, fraction_one_t)) {
            {
                mp_eval_cubic(mp, &x, p, q, c, &t);
                if (number_less(x, mp->bbmin[c])) {
                    number_clone(mp->bbmin[c], x);
                }
                if (number_greater(x, mp->bbmax[c])) {
                    number_clone(mp->bbmax[c], x);
                }
                set_number_from_of_the_way(del2, t, del2, del3);
                if (number_positive(del2)) {
                    set_number_to_zero(del2);
                }
                {
                    mp_number arg2, arg3;
                    new_number(arg2);
                    new_number(arg3);
                    number_negated_clone(arg2, del2);
                    number_negated_clone(arg3, del3);
                    crossing_point(tt, zero_t, arg2, arg3);
                    free_number(arg2);
                    free_number(arg3);
                }
                if (number_less(tt, fraction_one_t)) {
                    mp_number arg;
                    new_number(arg);
                    set_number_from_of_the_way(arg, t, tt, fraction_one_t);
                    mp_eval_cubic(mp, &x, p, q, c, &arg);
                    free_number(arg);
                    if (number_less(x, mp->bbmin[c])) {
                        number_clone(mp->bbmin[c], x);
                    }
                    if (number_greater(x, mp->bbmax[c])) {
                        number_clone(mp->bbmax[c], x);
                    }
                }
            }
        }
    }
    free_number(del3);
    free_number(del2);
    free_number(del1);
    free_number(del);
    free_number(dmax);
    free_number(x);
    free_number(t);
    free_number(tt);
}

static void mp_path_bbox (MP mp, mp_knot h)
{
    mp_knot p = h;
    number_clone(mp_minx, h->x_coord);
    number_clone(mp_miny, h->y_coord);
    number_clone(mp_maxx, mp_minx);
    number_clone(mp_maxy, mp_miny);
    do {
        if (mp_right_type(p) == mp_endpoint_knot) {
            return;
        } else {
            mp_knot q = mp_next_knot(p);
            mp_bound_cubic(mp, p, q, mp_x_code);
            mp_bound_cubic(mp, p, q, mp_y_code);
            p = q;
        }
    } while (p != h);
}

static void mp_path_xbox (MP mp, mp_knot h)
{
    mp_knot p = h;
    number_clone(mp_minx, h->x_coord);
    number_clone(mp_maxx, mp_minx);
    set_number_to_zero(mp_miny);
    set_number_to_zero(mp_maxy);
    do {
        if (mp_right_type(p) == mp_endpoint_knot) {
            return;
        } else {
            mp_knot q = mp_next_knot(p);
            mp_bound_cubic(mp, p, q, mp_x_code);
            p = q;
        }
    } while (p != h);
}

static void mp_path_ybox (MP mp, mp_knot h)
{
    mp_knot p = h;
    set_number_to_zero(mp_minx);
    set_number_to_zero(mp_maxx);
    number_clone(mp_miny, h->y_coord);
    number_clone(mp_maxy, mp_miny);
    do {
        if (mp_right_type(p) == mp_endpoint_knot) {
            return;
        } else {
            mp_knot q = mp_next_knot(p);
            mp_bound_cubic(mp, p, q, mp_y_code);
            p = q;
        }
    } while (p != h);
}

static void mp_arc_test (MP mp,
    mp_number *ret, mp_number *dx0, mp_number *dy0, mp_number *dx1,
    mp_number *dy1, mp_number *dx2, mp_number *dy2, mp_number *v0,
    mp_number *v02, mp_number *v2, mp_number *a_goal, mp_number *tol_orig
)
{
    int simple;
    mp_number dx01, dy01, dx12, dy12, dx02, dy02;
    mp_number v002, v022;
    mp_number arc;
    mp_number arc1;
    mp_number simply;
    mp_number tol;
    new_number(arc );
    new_number(arc1);
    new_number(dx01);
    new_number(dy01);
    new_number(dx12);
    new_number(dy12);
    new_number(dx02);
    new_number(dy02);
    new_number(v002);
    new_number(v022);
    new_number(simply);
    new_number_clone(tol, *tol_orig);
    set_number_half_from_addition(dx01, *dx0, *dx1);
    set_number_half_from_addition(dx12, *dx1, *dx2);
    set_number_half_from_addition(dx02, dx01, dx12);
    set_number_half_from_addition(dy01, *dy0, *dy1);
    set_number_half_from_addition(dy12, *dy1, *dy2);
    set_number_half_from_addition(dy02, dy01, dy12);

    {
        mp_number tmp, arg1, arg2 ;
        new_number(tmp);
        new_number(arg1);
        new_number(arg2);
        set_number_half_from_addition(arg1, *dx0, dx02);
        number_add(arg1, dx01);
        set_number_half_from_addition(arg2, *dy0, dy02);
        number_add(arg2, dy01);
        pyth_add(v002, arg1, arg2);
        set_number_half_from_addition(arg1, dx02, *dx2);
        number_add(arg1, dx12);
        set_number_half_from_addition(arg2, dy02, *dy2);
        number_add(arg2, dy12);
        pyth_add(v022, arg1, arg2);
        free_number(arg1);
        free_number(arg2);
        number_clone(tmp, *v02);
        number_add_scaled(tmp, 2);
        number_half(tmp);
        set_number_half_from_addition(arc1, *v0, tmp);
        number_subtract(arc1, v002);
        number_half(arc1);
        set_number_from_addition(arc1, v002, arc1);
        set_number_half_from_addition(arc, *v2, tmp);
        number_subtract(arc, v022);
        number_half(arc);
        set_number_from_addition(arc, v022, arc);
        set_number_to_inf(tmp);
        number_subtract(tmp,arc1);
        if (number_less(arc, tmp)) {
            free_number(tmp);
            number_add(arc, arc1);
        } else {
            free_number(tmp);
            mp->arith_error = 1;
            if (number_infinite(*a_goal)) {
                set_number_to_inf(*ret);
            } else {
                set_number_to_unity(*ret);
                number_double(*ret);
                number_negate(*ret);
            }
            goto DONE;
        }
    }
    simple = (number_nonnegative(*dx0) && number_nonnegative(*dx1) && number_nonnegative(*dx2))
          || (number_nonpositive(*dx0) && number_nonpositive(*dx1) && number_nonpositive(*dx2));
    if (simple) {
        simple = (number_nonnegative(*dy0) && number_nonnegative(*dy1) && number_nonnegative(*dy2))
              || (number_nonpositive(*dy0) && number_nonpositive(*dy1) && number_nonpositive(*dy2));
    }
    if (!simple) {
        simple = (number_greaterequal(*dx0, *dy0) && number_greaterequal(*dx1, *dy1) && number_greaterequal(*dx2, *dy2))
              || (number_lessequal   (*dx0, *dy0) && number_lessequal   (*dx1, *dy1) && number_lessequal   (*dx2, *dy2));
        if (simple) {
            mp_number neg_dx0, neg_dx1, neg_dx2;
            new_number(neg_dx0);
            new_number(neg_dx1);
            new_number(neg_dx2);
            number_negated_clone(neg_dx0, *dx0);
            number_negated_clone(neg_dx1, *dx1);
            number_negated_clone(neg_dx2, *dx2);
            simple = (number_greaterequal(neg_dx0, *dy0) && number_greaterequal(neg_dx1, *dy1) && number_greaterequal(neg_dx2, *dy2))
                  || (number_lessequal   (neg_dx0, *dy0) && number_lessequal   (neg_dx1, *dy1) && number_lessequal   (neg_dx2, *dy2));
            free_number(neg_dx0);
            free_number(neg_dx1);
            free_number(neg_dx2);
        }
    }
    set_number_half_from_addition(simply, *v0, *v2);
    number_negate(simply);
    number_add(simply, arc);
    number_subtract(simply, *v02);
    number_abs(simply);
    if (simple && number_lessequal(simply, tol)) {
        if (number_less(arc, *a_goal)){
            number_clone(*ret, arc);
        } else {
            mp_number tmp;
            mp_number tmp2;
            mp_number tmp3;
            mp_number tmp4;
            mp_number tmp5;
            new_number_clone(tmp, *v02);
            new_number(tmp2);
            new_number(tmp3);
            new_number(tmp4);
            new_number(tmp5);
            number_add_scaled(tmp, 2);
            number_half(tmp);
            number_half(tmp);
            if (number_lessequal(*a_goal, arc1)) {
                number_clone(tmp2, *v0);
                number_half(tmp2);
                set_number_from_subtraction(tmp3, arc1, tmp2);
                number_subtract(tmp3, tmp);
                mp_solve_rising_cubic(mp, &tmp5, &tmp2, &tmp3, &tmp, a_goal);
                number_half(tmp5);
                set_number_to_unity(tmp3);
                number_subtract(tmp5, tmp3);
                number_subtract(tmp5, tmp3);
                number_clone(*ret, tmp5);
            } else {
                number_clone(tmp2, *v2);
                number_half(tmp2);
                set_number_from_subtraction(tmp3, arc, arc1);
                number_subtract(tmp3, tmp);
                number_subtract(tmp3, tmp2);
                set_number_from_subtraction(tmp4, *a_goal, arc1);
                mp_solve_rising_cubic(mp, &tmp5, &tmp, &tmp3, &tmp2, &tmp4);
                number_half(tmp5);
                set_number_to_unity(tmp2);
                set_number_to_unity(tmp3);
                number_half(tmp2);
                number_subtract(tmp2, tmp3);
                number_subtract(tmp2, tmp3);
                set_number_from_addition(*ret, tmp2, tmp5);
            }
            free_number(tmp);
            free_number(tmp2);
            free_number(tmp3);
            free_number(tmp4);
            free_number(tmp5);
        }
    } else {
        mp_number a_new, a_aux;
        mp_number a, b;
        mp_number half_v02;
        new_number(a_new);
        new_number(a_aux);
        new_number(half_v02);
        set_number_to_inf(a_aux);
        number_subtract(a_aux, *a_goal);
        if (number_greater(*a_goal, a_aux)) {
            set_number_from_subtraction(a_aux, *a_goal, a_aux);
            set_number_to_inf(a_new);
        } else {
            set_number_from_addition(a_new, *a_goal, *a_goal);
            set_number_to_zero(a_aux);
        }
        {
            mp_number half_tol;
            new_number_clone(half_tol, tol);
            number_half(half_tol);
            number_add(tol, half_tol);
            free_number(half_tol);
        }
        number_clone(half_v02, *v02);
        number_half(half_v02);
        new_number(a);
        mp_arc_test(mp, &a, dx0, dy0, &dx01, &dy01, &dx02, &dy02, v0, &v002, &half_v02, &a_new, &tol);
        if (number_negative(a)) {
            set_number_to_unity(*ret);
            number_double(*ret);
            number_subtract(*ret, a);
            number_half(*ret);
            number_negate(*ret);
        } else {
            if (number_greater(a, a_aux)) {
                number_subtract(a_aux, a);
                number_add(a_new, a_aux);
            }
            new_number(b);
            mp_arc_test(mp, &b, &dx02, &dy02, &dx12, &dy12, dx2, dy2, &half_v02, &v022, v2, &a_new, &tol);
            if (number_negative(b)) {
                mp_number tmp ;
                new_number(tmp);
                number_negated_clone(tmp, b);
                number_half(tmp);
                number_negate(tmp);
                number_clone(*ret, tmp);
                set_number_to_unity(tmp);
                number_half(tmp);
                number_subtract(*ret, tmp);
                free_number(tmp);
            } else {
                set_number_from_subtraction(*ret, b, a);
                number_half(*ret);
                set_number_from_addition(*ret, a, *ret);
            }
            free_number(b);
        }
        free_number(half_v02);
        free_number(a_aux);
        free_number(a_new);
        free_number(a);
    }
  DONE:
    free_number(arc);
    free_number(arc1);
    free_number(dx01);
    free_number(dy01);
    free_number(dx12);
    free_number(dy12);
    free_number(dx02);
    free_number(dy02);
    free_number(v002);
    free_number(v022);
    free_number(simply);
    free_number(tol);
}

void mp_solve_rising_cubic (MP mp, mp_number *ret, mp_number *a_orig, mp_number *b_orig, mp_number *c_orig, mp_number *x_orig)
{
    mp_number abc;
    mp_number a, b, c, x;
    mp_number ab, bc, ac;
    mp_number t;
    mp_number xx;
    mp_number neg_x;
    if (number_negative(*a_orig) || number_negative(*c_orig)) {
        mp_confusion(mp, "rising cubic");
    }
    new_number(t);
    new_number(abc);
    new_number_clone(a, *a_orig);
    new_number_clone(b, *b_orig);
    new_number_clone(c, *c_orig);
    new_number_clone(x, *x_orig);
    new_number(ab);
    new_number(bc);
    new_number(ac);
    new_number(xx);
    new_number(neg_x);
    set_number_from_addition(abc, a, b);
    number_add(abc, c);
    if (number_nonpositive(x)) {
        set_number_to_zero(*ret);
    } else if (number_greaterequal(x, abc)) {
        set_number_to_unity(*ret);
    } else {
        number_clone(t, epsilon_t);
        while (number_greater(a, one_third_inf_t) || number_greater(b, one_third_inf_t) || number_greater(c, one_third_inf_t)) {
            number_half(a);
            number_half(b);
            number_half(c);
            number_half(x);
        }
        do {
            number_add(t, t);
            set_number_half_from_addition(ab, a, b);
            set_number_half_from_addition(bc, b, c);
            set_number_half_from_addition(ac, ab, bc);

            number_clone(xx,x);
            number_subtract(xx, a);
            number_subtract(xx, ab);
            number_subtract(xx, ac);
            number_negated_clone(neg_x, x);
            if (number_less(xx, neg_x)) {
                number_double(x);
                number_clone(b, ab);
                number_clone(c, ac);
            } else {
                number_add(x, xx);
                number_clone(a, ac);
                number_clone(b, bc);
                number_add(t, epsilon_t);
            }
        } while (number_less(t, unity_t));
        set_number_from_subtraction(*ret, t, unity_t);
    }
    free_number(abc);
    free_number(t);
    free_number(a);
    free_number(b);
    free_number(c);
    free_number(ab);
    free_number(bc);
    free_number(ac);
    free_number(xx);
    free_number(x);
    free_number(neg_x);
}

static void mp_do_arc_test (MP mp,
    mp_number *ret, mp_number *dx0, mp_number *dy0, mp_number *dx1,
    mp_number *dy1, mp_number *dx2, mp_number *dy2, mp_number *a_goal
)
{
    mp_number v0, v1, v2;
    mp_number v02;
    new_number(v0);
    new_number(v1);
    new_number(v2);
    pyth_add(v0, *dx0, *dy0);
    pyth_add(v1, *dx1, *dy1);
    pyth_add(v2, *dx2, *dy2);
    if ((number_greaterequal(v0, fraction_four_t)) || (number_greaterequal(v1, fraction_four_t)) || (number_greaterequal(v2, fraction_four_t))) {
        mp->arith_error = 1;
        if (number_infinite(*a_goal)) {
            set_number_to_inf(*ret);
        } else {
            set_number_to_unity(*ret);
            number_double(*ret);
            number_negate(*ret);
        }
    } else {
        mp_number arg1, arg2;
        new_number(v02);
        new_number(arg1);
        new_number(arg2);
        set_number_half_from_addition(arg1, *dx0, *dx2);
        number_add(arg1, *dx1);
        set_number_half_from_addition(arg2, *dy0, *dy2);
        number_add(arg2, *dy1);
        pyth_add(v02, arg1, arg2);
        free_number(arg1);
        free_number(arg2);
        mp_arc_test(mp, ret, dx0, dy0, dx1, dy1, dx2, dy2, &v0, &v02, &v2, a_goal, &arc_tol_k);
        free_number(v02);
    }
    free_number(v0);
    free_number(v1);
    free_number(v2);
}

static void mp_get_arc_length (MP mp, mp_number *ret, mp_knot h)
{
    mp_number a;
    mp_number a_tot;
    mp_number arg1, arg2, arg3, arg4, arg5, arg6;
    mp_number arcgoal;
    mp_knot p = h;
    new_number(a_tot);
    new_number(arg1);
    new_number(arg2);
    new_number(arg3);
    new_number(arg4);
    new_number(arg5);
    new_number(arg6);
    new_number(a);
    new_number(arcgoal);
    set_number_to_inf(arcgoal);
    while (mp_right_type(p) != mp_endpoint_knot) {
        mp_knot q = mp_next_knot(p);
        set_number_from_subtraction(arg1, p->right_x, p->x_coord);
        set_number_from_subtraction(arg2, p->right_y, p->y_coord);
        set_number_from_subtraction(arg3, q->left_x,  p->right_x);
        set_number_from_subtraction(arg4, q->left_y,  p->right_y);
        set_number_from_subtraction(arg5, q->x_coord, q->left_x);
        set_number_from_subtraction(arg6, q->y_coord, q->left_y);
        mp_do_arc_test(mp, &a, &arg1, &arg2, &arg3, &arg4, &arg5, &arg6, &arcgoal);
        slow_add(a_tot, a, a_tot);

        if (q == h) {
            break;
        } else {
            p = q;
        }
    }
    free_number(arcgoal);
    free_number(a);
    free_number(arg1);
    free_number(arg2);
    free_number(arg3);
    free_number(arg4);
    free_number(arg5);
    free_number(arg6);
    check_arith(mp);
    number_clone(*ret, a_tot);
    free_number(a_tot);
}

static void mp_get_subarc_length (MP mp, mp_number *ret, mp_knot h, mp_number *first, mp_number *last)
{
    mp_number a;
    mp_number a_tot, a_cnt;
    mp_number arg1, arg2, arg3, arg4, arg5, arg6;
    mp_number arcgoal;
    mp_knot p = h;
    new_number(a_tot);
    new_number(a_cnt);
    new_number(arg1);
    new_number(arg2);
    new_number(arg3);
    new_number(arg4);
    new_number(arg5);
    new_number(arg6);
    new_number(a);
    new_number(arcgoal);
    set_number_to_inf(arcgoal);
    while (mp_right_type(p) != mp_endpoint_knot) {
        mp_knot q = mp_next_knot(p);
        if (number_greaterequal(a_cnt, *last)) {
            break;
        } else if (number_greaterequal(a_cnt, *first)) {
            set_number_from_subtraction(arg1, p->right_x, p->x_coord);
            set_number_from_subtraction(arg2, p->right_y, p->y_coord);
            set_number_from_subtraction(arg3, q->left_x,  p->right_x);
            set_number_from_subtraction(arg4, q->left_y,  p->right_y);
            set_number_from_subtraction(arg5, q->x_coord, q->left_x);
            set_number_from_subtraction(arg6, q->y_coord, q->left_y);
            mp_do_arc_test(mp, &a, &arg1, &arg2, &arg3, &arg4, &arg5, &arg6, &arcgoal);
            slow_add(a_tot, a, a_tot);
        }
        if (q == h) {
            break;
        } else {
            p = q;
            number_add(a_cnt, unity_t);
        }
    }
    free_number(arcgoal);
    free_number(a);
    free_number(arg1);
    free_number(arg2);
    free_number(arg3);
    free_number(arg4);
    free_number(arg5);
    free_number(arg6);
    check_arith(mp);
    number_clone(*ret, a_tot);
    free_number(a_cnt);
    free_number(a_tot);
}

static mp_knot mp_get_arc_time(MP mp, mp_number *ret, mp_knot h, mp_number *arc0_orig, int local)
{
    if (number_negative(*arc0_orig)) {
        if (mp_left_type(h) == mp_endpoint_knot) {
            set_number_to_zero(*ret);
        } else {
            mp_number neg_arc0;
            mp_knot p = mp_htap_ypoc(mp, h);
            new_number(neg_arc0);
            number_negated_clone(neg_arc0, *arc0_orig);
            mp_get_arc_time(mp, ret, p, &neg_arc0, 0);
            number_negate(*ret);
            mp_toss_knot_list(mp, p);
            free_number(neg_arc0);
        }
        check_arith(mp);
    } else {
        mp_knot p, q, k;
        mp_number t_tot;
        mp_number t;
        mp_number arc, arc0;
        mp_number arg1, arg2, arg3, arg4, arg5, arg6;
        new_number(t_tot);
        new_number_clone(arc0, *arc0_orig);
        if (number_infinite(arc0)) {
            number_add_scaled(arc0, -1);
        }
        new_number_clone(arc, arc0);
        p = h;
        k = h;
        new_number(arg1);
        new_number(arg2);
        new_number(arg3);
        new_number(arg4);
        new_number(arg5);
        new_number(arg6);
        new_number(t);
        while ((mp_right_type(p) != mp_endpoint_knot) && number_positive(arc)) {
            k = p;
            q = mp_next_knot(p);
            set_number_from_subtraction(arg1, p->right_x, p->x_coord);
            set_number_from_subtraction(arg2, p->right_y, p->y_coord);
            set_number_from_subtraction(arg3, q->left_x,  p->right_x);
            set_number_from_subtraction(arg4, q->left_y,  p->right_y);
            set_number_from_subtraction(arg5, q->x_coord, q->left_x);
            set_number_from_subtraction(arg6, q->y_coord, q->left_y);
            mp_do_arc_test(mp, &t, &arg1, &arg2, &arg3, &arg4, &arg5, &arg6, &arc);
            if (number_negative(t)) {
                number_add(t_tot, t);
                number_add(t_tot, two_t);
                set_number_to_zero(arc);
            } else {
                number_add(t_tot, unity_t);
                number_subtract(arc, t);
            }
            if (q == h) {
                if (number_positive(arc)) {
                    mp_number n, n1, d1, v1;
                    new_number(n);
                    new_number(n1);
                    new_number(d1);
                    new_number(v1);
                    set_number_from_subtraction(d1, arc0, arc);
                    set_number_from_div(n1, arc, d1);
                    number_clone(n, n1);
                    set_number_from_mul(n1, n1, d1);
                    number_subtract(arc, n1);
                    number_clone(d1, inf_t);
                    number_clone(v1, n);
                    number_add(v1, epsilon_t);
                    set_number_from_div(d1, d1, v1);
                    if (number_greater(t_tot, d1)) {
                        mp->arith_error = 1;
                        check_arith(mp);
                        set_number_to_inf(*ret);
                        free_number(n);
                        free_number(n1);
                        free_number(d1);
                        free_number(v1);
                        goto RETURN;
                    }
                    set_number_from_mul(t_tot, t_tot, v1);
                    free_number(n);
                    free_number(n1);
                    free_number(d1);
                    free_number(v1);
                }
            }
            p = q;
        }
        check_arith(mp);
        if (local) {
            number_add(t, two_t);
            number_clone(*ret, t);
        } else {
            number_clone(*ret, t_tot);
        }
        h = k;
      RETURN:
        free_number(t_tot);
        free_number(t);
        free_number(arc);
        free_number(arc0);
        free_number(arg1);
        free_number(arg2);
        free_number(arg3);
        free_number(arg4);
        free_number(arg5);
        free_number(arg6);
    }
    return h;
}

static mp_knot mp_make_pen (MP mp, mp_knot h, int need_hull)
{
    mp_knot q = h;
    do {
        mp_knot p = q;
        q = mp_next_knot(q);
        mp_prev_knot(q) = p;
    } while (q != h);
    if (need_hull) {
        h = mp_convex_hull(mp, h);
        if (mp_pen_is_elliptical(h)) {
            number_clone(h->left_x, h->x_coord);
            number_clone(h->left_y, h->y_coord);
            number_clone(h->right_x, h->x_coord);
            number_clone(h->right_y, h->y_coord);
        }
    }
    return h;
}

static mp_knot mp_get_pen_circle (MP mp, mp_number *diam)
{
    mp_knot h = mp_new_knot(mp);
    mp_next_knot(h) = h;
    mp_prev_knot(h) = h;
    mp_originator(h) = mp_program_code;
    mp_knotstate(h) = mp_regular_knot;
    set_number_to_zero(h->x_coord);
    set_number_to_zero(h->y_coord);
    number_clone(h->left_x, *diam);
    set_number_to_zero(h->left_y);
    set_number_to_zero(h->right_x);
    number_clone(h->right_y, *diam);
    return h;
}

void mp_pr_pen (MP mp, mp_knot h)
{
    if (mp_pen_is_elliptical(h)) {
        {
            mp_number v1;
            new_number(v1);
            mp_print_str(mp, "pencircle transformed (");
            print_number(h->x_coord);
            mp_print_chr(mp, ',');
            print_number(h->y_coord);
            mp_print_chr(mp, ',');
            set_number_from_subtraction(v1, h->left_x, h->x_coord);
            print_number(v1);
            mp_print_chr(mp, ',');
            set_number_from_subtraction(v1, h->right_x, h->x_coord);
            print_number(v1);
            mp_print_chr(mp, ',');
            set_number_from_subtraction(v1, h->left_y, h->y_coord);
            print_number(v1);
            mp_print_chr(mp, ',');
            set_number_from_subtraction(v1, h->right_y, h->y_coord);
            print_number(v1);
            mp_print_chr(mp, ')');
            free_number(v1);
        }
    } else {
        mp_knot p = h;
        do {
            mp_knot q = mp_next_knot(p);
            mp_print_two(mp, &(p->x_coord), &(p->y_coord));
            mp_print_nl(mp, " .. ");
            if ((q == NULL) || (mp_prev_knot(q) != p)) {
                mp_print_nl(mp, "???");
                return;
            }
            p = q;
        } while (p != h);
        mp_print_str(mp, "cycle");
    }
}

void mp_print_pen (MP mp, mp_knot h, const char *s, int nuline) {
    mp_print_diagnostic(mp, "Pen", s, nuline);
    mp_print_ln(mp);
    mp_pr_pen(mp, h);
    mp_end_diagnostic(mp, 1);
}

static void mp_make_path (MP mp, mp_knot h)
{
    if (mp_pen_is_elliptical(h)) {
        mp_knot p;
        mp_number center_x, center_y;
        mp_number width_x, width_y;
        mp_number height_x, height_y;
        mp_number dx, dy;
        new_number(width_x);
        new_number(width_y);
        new_number(height_x);
        new_number(height_y);
        new_number(dx);
        new_number(dy);
        new_number_clone(center_x, h->x_coord);
        new_number_clone(center_y, h->y_coord);
        set_number_from_subtraction(width_x, h->left_x, center_x);
        set_number_from_subtraction(width_y, h->left_y, center_y);
        set_number_from_subtraction(height_x, h->right_x, center_x);
        set_number_from_subtraction(height_y, h->right_y, center_y);
        p = h;
        for (int k = 0; k <= 7; k++) {

            int kk = (k + 6) % 8;
            mp_number r1, r2;
            new_fraction(r1);
            new_fraction(r2);
            take_fraction(r1, mp->half_cos[k], width_x);
            take_fraction(r2, mp->half_cos[kk], height_x);
            number_add(r1, r2);
            set_number_from_addition(p->x_coord, center_x, r1);
            take_fraction(r1, mp->half_cos[k],  width_y);
            take_fraction(r2, mp->half_cos[kk], height_y);
            number_add(r1, r2);
            set_number_from_addition(p->y_coord, center_y, r1);
            take_fraction(r1, mp->d_cos[kk], width_x);
            take_fraction(r2, mp->d_cos[k], height_x);
            number_negated_clone(dx, r1);
            number_add(dx, r2);
            take_fraction(r1, mp->d_cos[kk], width_y);
            take_fraction(r2, mp->d_cos[k], height_y);
            number_negated_clone(dy, r1);
            number_add(dy, r2);
            set_number_from_addition(p->right_x, p->x_coord, dx);
            set_number_from_addition(p->right_y, p->y_coord, dy);
            set_number_from_subtraction(p->left_x, p->x_coord, dx);
            set_number_from_subtraction(p->left_y, p->y_coord, dy);
            free_number(r1);
            free_number(r2);
            mp_left_type(p) = mp_explicit_knot;
            mp_right_type(p) = mp_explicit_knot;
            mp_originator(p) = mp_program_code;
            mp_knotstate(p) = mp_regular_knot;

            if (k == 7) {
                mp_prev_knot(h) = p;
                mp_next_knot(p) = h;
            } else {
                mp_knot k = mp_new_knot(mp);
                mp_prev_knot(k) = p;
                mp_next_knot(p) = k;
            }
            p = mp_next_knot(p);
        }
        free_number(dx);
        free_number(dy);
        free_number(center_x);
        free_number(center_y);
        free_number(width_x);
        free_number(width_y);
        free_number(height_x);
        free_number(height_y);
    } else {
        mp_knot p = h;
        do {
            mp_left_type(p) = mp_explicit_knot;
            mp_right_type(p) = mp_explicit_knot;
            number_clone(p->left_x, p->x_coord);
            number_clone(p->left_y, p->y_coord);
            number_clone(p->right_x, p->x_coord);
            number_clone(p->right_y, p->y_coord);
            p = mp_next_knot(p);
        } while (p != h);
    }
}

mp_knot mp_convex_hull (MP mp, mp_knot h)
{
    if (mp_pen_is_elliptical(h)) {
        return h;
    } else {
        mp_knot l, r;
        mp_knot p, q;
        mp_knot s;
        mp_number dx, dy;
        new_number(dx);
        new_number(dy);
        l = h;
        p = mp_next_knot(h);
        while (p != h) {
            if (number_lessequal(p->x_coord, l->x_coord) && (number_less(p->x_coord, l->x_coord) || number_less(p->y_coord, l->y_coord))) {
                l = p;
            }
            p = mp_next_knot(p);
        }
        r = h;
        p = mp_next_knot(h);
        while (p != h) {
            if (number_greaterequal(p->x_coord, r->x_coord) && (number_greater(p->x_coord, r->x_coord) || number_greater(p->y_coord, r->y_coord))) {
                r = p;
            }
            p = mp_next_knot(p);
        }
        if (l != r) {
            mp_knot s = mp_next_knot(r);
            {
                mp_number arg1, arg2;
                new_number(arg1);
                new_number(arg2);
                set_number_from_subtraction(dx, r->x_coord, l->x_coord);
                set_number_from_subtraction(dy, r->y_coord, l->y_coord);
                p = mp_next_knot(l);
                while (p != r) {
                    q = mp_next_knot(p);
                    set_number_from_subtraction(arg1, p->y_coord, l->y_coord);
                    set_number_from_subtraction(arg2, p->x_coord, l->x_coord);
                    if (ab_vs_cd(dx, arg1, dy, arg2) > 0) {
                        mp_move_knot(mp, p, r);
                    }
                    p = q;
                }
                free_number(arg1);
                free_number(arg2);
            }
            {
                mp_number arg1, arg2;
                new_number(arg1);
                new_number(arg2);
                p = s;
                while (p != l) {
                    q = mp_next_knot(p);
                    set_number_from_subtraction(arg1, p->y_coord, l->y_coord);
                    set_number_from_subtraction(arg2, p->x_coord, l->x_coord);
                    if (ab_vs_cd(dx, arg1, dy, arg2) < 0) {
                        mp_move_knot(mp, p, l);
                    }
                    p = q;
                }
                free_number(arg1);
                free_number(arg2);
            }
            p = mp_next_knot(l);
            while (p != r) {
                q = mp_prev_knot(p);
                while (number_greater(q->x_coord, p->x_coord)) {
                    q = mp_prev_knot(q);
                }
                while (number_equal(q->x_coord, p->x_coord)) {
                    if (number_greater(q->y_coord, p->y_coord)) {
                        q = mp_prev_knot(q);
                    } else {
                        break;
                    }
                }
                if (q == mp_prev_knot(p)) {
                    p = mp_next_knot(p);
                } else {
                    p = mp_next_knot(p);
                    mp_move_knot(mp, mp_prev_knot(p), q);
                }
            }
            p = mp_next_knot(r);
            while (p != l) {
                q = mp_prev_knot(p);
                while (number_less(q->x_coord, p->x_coord)) {
                    q = mp_prev_knot(q);
                }
                while (number_equal(q->x_coord, p->x_coord)) {
                    if (number_less(q->y_coord, p->y_coord)) {
                        q = mp_prev_knot(q);
                    } else {
                        break;
                    }
                }
                if (q == mp_prev_knot(p)) {
                    p = mp_next_knot(p);
                } else {
                    p = mp_next_knot(p);
                    mp_move_knot(mp, mp_prev_knot(p), q);
                }
            }
        }
        if (l != mp_next_knot(l)) {
            mp_number arg1, arg2;
            new_number(arg1);
            new_number(arg2);
            p = l;
            q = mp_next_knot(l);
            while (1) {
                set_number_from_subtraction(dx, q->x_coord, p->x_coord);
                set_number_from_subtraction(dy, q->y_coord, p->y_coord);
                p = q;
                q = mp_next_knot(q);
                if (p == l) {
                    break;
                } else if (p != r) {
                    set_number_from_subtraction(arg1, q->y_coord, p->y_coord);
                    set_number_from_subtraction(arg2, q->x_coord, p->x_coord);
                    if (ab_vs_cd(dx, arg1, dy, arg2) <= 0) {
                        s = mp_prev_knot(p);
                        mp_memory_free(p);
                        mp_next_knot(s) = q;
                        mp_prev_knot(q) = s;
                        if (s == l) {
                            p = s;
                        } else {
                            p = mp_prev_knot(s);
                            q = s;
                        }
                    }
                }
            }
            free_number(arg1);
            free_number(arg2);
        }
        free_number(dx);
        free_number(dy);
        return l;
    }
}

void mp_simplify_path (MP mp, mp_knot h)
{
    mp_knot p = h;
    (void) mp;
    do {
        p->left_x = p->x_coord;
        p->left_y = p->y_coord;
        p->right_x = p->x_coord;
        p->right_y = p->y_coord;
        p = mp_next_knot(p);
    } while (p != h);
}

void mp_move_knot (MP mp, mp_knot p, mp_knot q)
{
    (void) mp;
    mp_next_knot(mp_prev_knot(p)) = mp_next_knot(p);
    mp_prev_knot(mp_next_knot(p)) = mp_prev_knot(p);
    mp_prev_knot(p) = q;
    mp_next_knot(p) = mp_next_knot(q);
    mp_next_knot(q) = p;
    mp_prev_knot(mp_next_knot(p)) = p;
}

static void mp_find_offset (MP mp, mp_number *x_orig, mp_number *y_orig, mp_knot h)
{
    if (mp_pen_is_elliptical(h)) {
        mp_number xx, yy;
        mp_number wx, wy, hx, hy;
        mp_number d;
        new_fraction(xx);
        new_fraction(yy);
        new_number(wx);
        new_number(wy);
        new_number(hx);
        new_number(hy);
        new_fraction(d);
        if (number_zero(*x_orig) && number_zero(*y_orig)) {
            number_clone(mp->cur_x, h->x_coord);
            number_clone(mp->cur_y, h->y_coord);
        } else {
            mp_number x, y, abs_x, abs_y;
            new_number_clone(x, *x_orig);
            new_number_clone(y, *y_orig);
            set_number_from_subtraction(wx, h->left_x,  h->x_coord);
            set_number_from_subtraction(wy, h->left_y,  h->y_coord);
            set_number_from_subtraction(hx, h->right_x, h->x_coord);
            set_number_from_subtraction(hy, h->right_y, h->y_coord);

            new_number_abs(abs_x, x);
            new_number_abs(abs_y, y);
            while (number_less(abs_x, fraction_half_t) && number_less(abs_y, fraction_half_t)) {
                number_double(x);
                number_double(y);
                number_abs_clone(abs_x, x);
                number_abs_clone(abs_y, y);
            }
            {
                mp_number r1, r2, arg1;
                new_number(arg1);
                new_fraction(r1);
                new_fraction(r2);
                take_fraction(r1, x, hy);
                number_negated_clone(arg1, hx);
                take_fraction(r2, y, arg1);
                number_add(r1, r2);
                number_negate(r1);
                number_clone(yy, r1);
                number_negated_clone(arg1, wy);
                take_fraction(r1, x, arg1);
                take_fraction(r2, y, wx);
                number_add(r1, r2);
                number_clone(xx, r1);
                free_number(arg1);
                free_number(r1);
                free_number(r2);
            }
            pyth_add(d, xx, yy);
            if (number_positive(d)) {
                mp_number ret;
                new_fraction(ret);
                make_fraction(ret, xx, d);
                number_half(ret);
                number_clone(xx, ret);
                make_fraction(ret, yy, d);
                number_half(ret);
                number_clone(yy, ret);
                free_number(ret);
            }
            {
                mp_number r1, r2;
                new_fraction(r1);
                new_fraction(r2);
                take_fraction(r1, xx, wx);
                take_fraction(r2, yy, hx);
                number_add(r1, r2);
                set_number_from_addition(mp->cur_x, h->x_coord, r1);
                take_fraction(r1, xx, wy);
                take_fraction(r2, yy, hy);
                number_add(r1, r2);
                set_number_from_addition(mp->cur_y, h->y_coord, r1);
                free_number(r1);
                free_number(r2);
            }
            free_number(abs_x);
            free_number(abs_y);
            free_number(x);
            free_number(y);
        }
        free_number(xx);
        free_number(yy);
        free_number(wx);
        free_number(wy);
        free_number(hx);
        free_number(hy);
        free_number(d);
    } else {
        mp_knot p, q;
        mp_number arg1, arg2;
        new_number(arg1);
        new_number(arg2);
        q = h;
        do {
            p = q;
            q = mp_next_knot(q);
            set_number_from_subtraction(arg1, q->x_coord, p->x_coord);
            set_number_from_subtraction(arg2, q->y_coord, p->y_coord);
        } while (ab_vs_cd(arg1, *y_orig, arg2, *x_orig) < 0);
        do {
            p = q;
            q = mp_next_knot(q);
            set_number_from_subtraction(arg1, q->x_coord, p->x_coord);
            set_number_from_subtraction(arg2, q->y_coord, p->y_coord);
        } while (ab_vs_cd(arg1, *y_orig, arg2, *x_orig) > 0);
        number_clone(mp->cur_x, p->x_coord);
        number_clone(mp->cur_y, p->y_coord);
        free_number(arg1);
        free_number(arg2);
    }
}

static void mp_pen_bbox (MP mp, mp_knot h)
{
    if (mp_pen_is_elliptical(h)) {
        mp_number arg1, arg2;
        new_number(arg1);
        new_fraction(arg2);
        number_clone(arg2, fraction_one_t);
        mp_find_offset(mp, &arg1, &arg2, h);
        number_clone(mp_maxx, mp->cur_x);
        number_clone(mp_minx, h->x_coord);
        number_double(mp_minx);
        number_subtract(mp_minx, mp->cur_x);
        number_negate(arg2);
        mp_find_offset(mp, &arg2, &arg1, h);
        number_clone(mp_maxy, mp->cur_y);
        number_clone(mp_miny, h->y_coord);
        number_double(mp_miny);
        number_subtract(mp_miny, mp->cur_y);
        free_number(arg1);
        free_number(arg2);
    } else {
        mp_knot p = mp_next_knot(h);
        number_clone(mp_minx, h->x_coord);
        number_clone(mp_maxx, mp_minx);
        number_clone(mp_miny, h->y_coord);
        number_clone(mp_maxy, mp_miny);
        while (p != h) {
            if (number_less(p->x_coord, mp_minx)) {
                number_clone(mp_minx, p->x_coord);
            }
            if (number_less(p->y_coord, mp_miny)) {
                number_clone(mp_miny, p->y_coord);
            }
            if (number_greater(p->x_coord, mp_maxx)) {
                number_clone(mp_maxx, p->x_coord);
            }
            if (number_greater(p->y_coord, mp_maxy)) {
                number_clone(mp_maxy, p->y_coord);
            }
            p = mp_next_knot(p);
        }
    }
}

static mp_node mp_new_shape_node (MP mp, mp_knot p, int type)
{
    mp_shape_node t = mp_allocate_node(mp, sizeof(mp_shape_node_data));
    t->type = type;
    mp_path_ptr(t) = p;
    mp_pen_ptr(t) = NULL;
    mp_dash_ptr(t) = NULL;
    new_number(t->red);
    new_number(t->green);
    new_number(t->blue);
    new_number(t->black);
    new_number(t->miterlimit);
    new_number(t->dashscale);
    set_number_to_unity(t->dashscale);
    mp_color_model(t) = mp_uninitialized_model;
    mp_pen_type(t) = 0;
    mp_pre_script(t) = NULL;
    mp_post_script(t) = NULL;
    if (number_greater(internal_value(mp_linejoin_internal), unity_t)) {
        t->linejoin = mp_beveled_linejoin_code;
    } else if (number_positive(internal_value(mp_linejoin_internal))) {
        t->linejoin = mp_rounded_linejoin_code;
    } else {
        t->linejoin = mp_mitered_linejoin_code;
    }
    t->stacking = round_unscaled(internal_value(mp_stacking_internal));
    if (number_less(internal_value(mp_miterlimit_internal), unity_t)) {
        set_number_to_unity(t->miterlimit);
    } else {
        number_clone(t->miterlimit, internal_value(mp_miterlimit_internal));
    }
    if (number_greater(internal_value(mp_linecap_internal), unity_t)) {
        t->linecap = mp_squared_linecap_code;
    } else if (number_positive(internal_value(mp_linecap_internal))) {
        t->linecap = mp_rounded_linecap_code;
    } else {
        t->linecap = mp_butt_linecap_code;
    }
    return (mp_node) t;
}

static mp_edge_header_node mp_free_shape_node (MP mp, mp_shape_node p)
{
    mp_edge_header_node e = NULL;
    mp_toss_knot_list(mp, mp_path_ptr(p));
    if (mp_pen_ptr(p) != NULL) {
        mp_toss_knot_list(mp, mp_pen_ptr(p));
    }
    if (mp_pre_script(p) != NULL) {
        delete_str_ref(mp_pre_script(p));
    }
    if (mp_post_script(p) != NULL) {
        delete_str_ref(mp_post_script(p));
    }
    e = (mp_edge_header_node) mp_dash_ptr(p);
    free_number(p->red);
    free_number(p->green);
    free_number(p->blue);
    free_number(p->black);
    free_number(p->miterlimit);
    free_number(p->dashscale);
    mp_free_node(mp, (mp_node) p, sizeof(mp_shape_node_data));
    return e ;
}

void mp_sqrt_det (MP mp, mp_number *ret, mp_number *a_orig, mp_number *b_orig, mp_number *c_orig, mp_number *d_orig)
{
    mp_number a, b, c, d;
    mp_number maxabs;
    unsigned s = 64;
    mp_number tmp;
    new_number_clone(a, *a_orig);
    new_number_clone(b, *b_orig);
    new_number_clone(c, *c_orig);
    new_number_clone(d, *d_orig);
    new_number_abs(maxabs, a);
    new_number_abs(tmp, b);
    if (number_greater(tmp, maxabs)) {
        number_clone(maxabs, tmp);
    }
    number_abs_clone(tmp, c);
    if (number_greater(tmp, maxabs)) {
        number_clone(maxabs, tmp);
    }
    number_abs_clone(tmp, d);
    if (number_greater(tmp, maxabs)) {
        number_clone(maxabs, tmp);
    }
    free_number(tmp);
    while ((number_less(maxabs, fraction_one_t)) && (s > 1)) {
        number_double(a);
        number_double(b);
        number_double(c);
        number_double(d);
        number_double(maxabs);
        s = s/2;
    }
    {
        mp_number r1, r2;
        new_fraction(r1);
        new_fraction(r2);
        take_fraction(r1, a, d);
        take_fraction(r2, b, c);
        number_subtract(r1, r2);
        number_abs(r1);
        square_rt(*ret, r1);
        number_multiply_int(*ret, s);
        free_number(r1);
        free_number(r2);
    }
    free_number(a);
    free_number(b);
    free_number(c);
    free_number(d);
    free_number(maxabs);
}

static void mp_get_pen_scale (MP mp, mp_number *ret, mp_knot p)
{
    if (p == NULL) {
        set_number_to_zero(*ret);
    } else {
        mp_number a, b, c, d;
        new_number(a);
        new_number(b);
        new_number(c);
        new_number(d);
        set_number_from_subtraction(a, p->left_x, p->x_coord);
        set_number_from_subtraction(b, p->right_x, p->x_coord);
        set_number_from_subtraction(c, p->left_y,    p->y_coord);
        set_number_from_subtraction(d, p->right_y, p->y_coord);
        mp_sqrt_det(mp, ret, &a, &b, &c, &d);
        free_number(a);
        free_number(b);
        free_number(c);
        free_number(d);
    }
}

static mp_node mp_new_bounds_node (MP mp, mp_knot p, int c)
{
    switch (c) {
        case mp_start_clip_node_type:
        case mp_start_group_node_type:
        case mp_start_bounds_node_type:
            {
                mp_start_node t = (mp_start_node) mp_allocate_node(mp, sizeof(mp_start_node_data));
                t->type = c;
                t->path = p;
                t->stacking = round_unscaled(internal_value(mp_stacking_internal));
                mp_pre_script(t) = NULL;
                mp_post_script(t) = NULL;
                return (mp_node) t;
            }
            break;
        case mp_stop_clip_node_type:
        case mp_stop_group_node_type:
        case mp_stop_bounds_node_type:
            {
                mp_stop_node t = (mp_stop_node) mp_allocate_node(mp, sizeof(mp_stop_node_data));
                t->type = c;
                t->stacking = round_unscaled(internal_value(mp_stacking_internal));
                return (mp_node) t;
            }
            break;
        default:
            break;
    }
    return NULL;
}

static void mp_free_start_node (MP mp, mp_start_node p)
{
    mp_toss_knot_list(mp, mp_path_ptr(p));
    if (mp_pre_script(p) != NULL) {
        delete_str_ref(mp_pre_script(p));
    }
    if (mp_post_script(p) != NULL) {
        delete_str_ref(mp_post_script(p));
    }
    mp_free_node(mp, (mp_node) p, sizeof(mp_start_node_data));
}

static void mp_free_stop_node (MP mp, mp_stop_node p)
{
    mp_free_node(mp, (mp_node) p, sizeof(mp_stop_node_data));
}

static mp_dash_node mp_get_dash_node (MP mp)
{
    mp_dash_node p = (mp_dash_node) mp_allocate_node(mp, sizeof(mp_dash_node_data));
    p->hasnumber = 0;
    new_number(p->start_x);
    new_number(p->stop_x);
    new_number(p->dash_y);
    p->type = mp_dash_node_type;
    return p;
}

static void mp_init_bbox (MP mp, mp_edge_header_node h)
{
    (void) mp;
    mp_bblast(h) = mp_edge_list(h);
    h->bbtype = mp_no_bounds_code;
    set_number_to_inf(h->minx);
    set_number_to_inf(h->miny);
    set_number_to_negative_inf(h->maxx);
    set_number_to_negative_inf(h->maxy);
}

static mp_edge_header_node mp_get_edge_header_node (MP mp)
{
    mp_edge_header_node p = (mp_edge_header_node) mp_allocate_node(mp, sizeof(mp_edge_header_node_data));
    p->type = mp_edge_header_node_type;
    new_number(p->start_x);
    new_number(p->stop_x);
    new_number(p->dash_y);
    new_number(p->minx);
    new_number(p->miny);
    new_number(p->maxx);
    new_number(p->maxy);
    p->list = mp_new_token_node(mp);
    return p;
}

static void mp_init_edges (MP mp, mp_edge_header_node h)
{
  mp_set_dash_list(h, mp->null_dash);
  mp_obj_tail(h) = mp_edge_list(h);
  mp_edge_list(h)->link = NULL;
  mp_edge_ref_count(h) = 0;
  mp_init_bbox(mp, h);
}

void mp_toss_edges (MP mp, mp_edge_header_node h)
{
    mp_node q;
    mp_edge_header_node r;
    mp_flush_dash_list(mp, h);
    q = mp_edge_list(h)->link;
    while (q != NULL) {
        mp_node p = q;
        q = q->link;
        r = mp_toss_gr_object(mp, p);
        if (r != NULL) {
            mp_delete_edge_ref(mp, r);
        }
    }
    free_number(h->start_x);
    free_number(h->stop_x);
    free_number(h->dash_y);
    free_number(h->minx);
    free_number(h->miny);
    free_number(h->maxx);
    free_number(h->maxy);
    mp_free_token_node(mp, h->list);
    mp_free_node(mp, (mp_node) h, sizeof(mp_edge_header_node_data));
}

void mp_flush_dash_list (MP mp, mp_edge_header_node h)
{
    mp_dash_node q = mp_get_dash_list(h);
    while (q != mp->null_dash) {
        mp_dash_node p = q;
        q = (mp_dash_node) q->link;
        mp_free_node(mp, (mp_node) p, sizeof(mp_dash_node_data));
    }
    mp_set_dash_list(h, mp->null_dash);
}

mp_edge_header_node mp_toss_gr_object (MP mp, mp_node p)
{
    switch (p->type) {
        case mp_fill_node_type:
        case mp_stroked_node_type:
            return mp_free_shape_node(mp, (mp_shape_node) p);
        case mp_start_clip_node_type:
        case mp_start_group_node_type:
        case mp_start_bounds_node_type:
            mp_free_start_node(mp, (mp_start_node) p);
            return NULL;
        case mp_stop_clip_node_type:
        case mp_stop_group_node_type:
        case mp_stop_bounds_node_type:
            mp_free_stop_node(mp, (mp_stop_node) p);
            return NULL;
        default:
            return NULL;
    }
}

static mp_edge_header_node mp_private_edges (MP mp, mp_edge_header_node h)
{
    if (mp_edge_ref_count(h) == 0) {
        return h;
    } else {
        mp_edge_header_node hh;
        mp_dash_node p, pp;
        mp_edge_ref_count(h) -= 1;
        hh = (mp_edge_header_node) mp_copy_objects (mp, mp_edge_list(h)->link, NULL);
        pp = (mp_dash_node) hh;
        p = mp_get_dash_list(h);
        while ((p != mp->null_dash)) {
            pp->link = (mp_node) mp_get_dash_node(mp);
            pp = (mp_dash_node) pp->link;
            number_clone(pp->start_x, p->start_x);
            number_clone(pp->stop_x, p->stop_x);
            p = (mp_dash_node) p->link;
        }
        pp->link = (mp_node) mp->null_dash;
        number_clone(hh->dash_y, h->dash_y);

        number_clone(hh->minx, h->minx);
        number_clone(hh->miny, h->miny);
        number_clone(hh->maxx, h->maxx);
        number_clone(hh->maxy, h->maxy);
        hh->bbtype = h->bbtype;
        p = (mp_dash_node) mp_edge_list(h);
        pp = (mp_dash_node) mp_edge_list(hh);
        while ((p != (mp_dash_node) mp_bblast(h))) {
            if (p == NULL) {
                mp_confusion(mp, "boundingbox last");
            } else {
                p = (mp_dash_node) p->link;
                pp = (mp_dash_node) pp->link;
            }
        }
        mp_bblast(hh) = (mp_node) pp;

        return hh;
    }
}

static mp_dash_object *mp_export_dashes (MP mp, mp_shape_node q, mp_number *w)
{
    mp_dash_node h = (mp_dash_node) mp_dash_ptr(q);
    if (h == NULL || mp_get_dash_list(h) == mp->null_dash) {
        return NULL;
    } else {
        mp_dash_object *d;
        mp_dash_node p;
        mp_number scf;
        mp_number dashoff;
        double *dashes = NULL;
        int num_dashes = 1;
        new_number(scf);
        p = mp_get_dash_list(h);
        mp_get_pen_scale(mp, &scf, mp_pen_ptr(q));
        if (number_zero(scf)) {
            if (number_zero(*w)) {
                number_clone(scf, q->dashscale);
            } else {
                free_number(scf);
                return NULL;
            }
        } else {
            mp_number ret;
            new_number(ret);
            make_scaled(ret, *w, scf);
            take_scaled(scf, ret, q->dashscale);
            free_number(ret);
        }
        number_clone(*w, scf);
        d = mp_allocate_dash(mp);
        set_number_from_addition(mp->null_dash->start_x, p->start_x, h->dash_y);
        {
            mp_number ret, arg1;
            new_number(ret);
            new_number(arg1);
            new_number(dashoff);
            while (p != mp->null_dash) {
                dashes = mp_memory_reallocate(dashes, (size_t) (num_dashes + 2) * sizeof(double));
                set_number_from_subtraction(arg1, p->stop_x, p->start_x);
                take_scaled(ret, arg1, scf);
                dashes[(num_dashes - 1)] = number_to_double(ret);
                set_number_from_subtraction(arg1, ((mp_dash_node) p->link)->start_x, p->stop_x);
                take_scaled(ret, arg1, scf);
                dashes[(num_dashes)] = number_to_double(ret);
                dashes[(num_dashes + 1)] = -1.0;
                num_dashes += 2;
                p = (mp_dash_node) p->link;
            }
            d->array = dashes;
            mp_dash_offset(mp, &dashoff, h);
            take_scaled(ret, dashoff, scf);
            d->offset = number_to_double(ret);
            free_number(ret);
            free_number(arg1);
        }
        free_number(dashoff);
        free_number(scf);
        return d;
    }
}

mp_edge_header_node mp_copy_objects (MP mp, mp_node p, mp_node q) {
    mp_node pp;
    int k = 0;
    mp_edge_header_node hh = mp_get_edge_header_node(mp);
    mp_set_dash_list(hh, mp->null_dash);
    mp_edge_ref_count(hh) = 0;
    pp = mp_edge_list(hh);
    while (p != q) {
        {
            switch (p->type) {
                case mp_fill_node_type:
                case mp_stroked_node_type:
                    k = sizeof(mp_shape_node_data);
                    break;
                case mp_start_clip_node_type:
                case mp_start_group_node_type:
                case mp_start_bounds_node_type:
                    k = sizeof(mp_start_node_data);
                    break;
                case mp_stop_clip_node_type:
                case mp_stop_group_node_type:
                case mp_stop_bounds_node_type:
                    k = sizeof(mp_stop_node_data);
                    break;
                default:
                    break;
            }
            pp->link = mp_allocate_node(mp, (size_t) k);
            pp = pp->link;
            memcpy(pp, p, (size_t) k);
            pp->link = NULL;
            switch (p->type) {
                case mp_fill_node_type:
                case mp_stroked_node_type:
                    {
                        mp_shape_node tt = (mp_shape_node) pp;
                        mp_shape_node t = (mp_shape_node) p;
                        new_number_clone(tt->red, t->red);
                        new_number_clone(tt->green, t->green);
                        new_number_clone(tt->blue, t->blue);
                        new_number_clone(tt->black, t->black);
                        new_number_clone(tt->miterlimit, t->miterlimit);
                        new_number_clone(tt->dashscale, t->dashscale);
                        mp_path_ptr(tt) = mp_copy_path(mp, mp_path_ptr(t));
                        if (mp_pre_script(p) != NULL) {
                            add_str_ref(mp_pre_script(p));
                        }
                        if (mp_post_script(p) != NULL) {
                            add_str_ref(mp_post_script(p));
                        }
                        if (mp_pen_ptr(t) != NULL) {
                            mp_pen_ptr(tt) = mp_copy_pen(mp, mp_pen_ptr(t));
                        }
                        if (mp_dash_ptr(p) != NULL) {
                            mp_add_edge_ref(mp, mp_dash_ptr(pp));
                        }
                    }
                    break;
                case mp_start_clip_node_type:
                case mp_start_group_node_type:
                case mp_start_bounds_node_type:
                    {
                        mp_start_node tt = (mp_start_node) pp;
                        mp_start_node t = (mp_start_node) p;
                        mp_path_ptr(tt) = mp_copy_path(mp, mp_path_ptr(t));
                        if (mp_pre_script(p) != NULL) {
                            add_str_ref(mp_pre_script(p));
                        }
                        if (mp_post_script(p) != NULL) {
                            add_str_ref(mp_post_script(p));
                        }
                    }
                    break;
                case mp_stop_clip_node_type:
                case mp_stop_group_node_type:
                case mp_stop_bounds_node_type:
                    break;
                default:
                    break;
            }
            p = p->link;
        }
    }
    mp_obj_tail(hh) = pp;
    pp->link = NULL;
    return hh;
}

static mp_node mp_skip_1component (MP mp, mp_node p)
{
    int lev = 0;
    (void) mp;
    do {
        if (mp_is_start_or_stop (p)) {
            if (mp_is_stop(p)) {
                --lev;
            } else {
                ++lev;
            }
        }
        p = p->link;
    } while (lev != 0);
    return p;
}

void mp_print_edges (MP mp, mp_node h, const char *s, int nuline)
{
    mp_node p = mp_edge_list(h);
    mp_number scf;
    new_number(scf);
    mp_print_diagnostic(mp, "Edge structure", s, nuline);
    while (p->link != NULL) {
        p = p->link;
        mp_print_ln(mp);
        switch (p->type) {
            case mp_fill_node_type:
                mp_print_str(mp, "Filled contour ");
                mp_print_obj_color (mp, p);
                mp_print_chr(mp, ':');
                mp_print_ln(mp);
                mp_pr_path(mp, mp_path_ptr((mp_shape_node) p));
                mp_print_ln(mp);
                if ((mp_pen_ptr((mp_shape_node) p) != NULL)) {
                    switch (((mp_shape_node) p)->linejoin) {
                        case mp_mitered_linejoin_code:
                            mp_print_str(mp, "mitered joins limited ");
                            print_number(((mp_shape_node) p)->miterlimit);
                            break;
                        case mp_rounded_linejoin_code:
                            mp_print_str(mp, "round joins");
                            break;
                        case mp_beveled_linejoin_code:
                            mp_print_str(mp, "beveled joins");
                            break;
                        default:
                            mp_print_str(mp, "?? joins");
                            break;
                    }
                    mp_print_str(mp, " with pen");
                    mp_print_ln(mp);
                    mp_pr_pen(mp, mp_pen_ptr((mp_shape_node) p));
                }
                break;
            case mp_stroked_node_type:
                mp_print_str(mp, "Filled pen stroke ");
                mp_print_obj_color (mp, p);
                mp_print_chr(mp, ':');
                mp_print_ln(mp);
                mp_pr_path(mp, mp_path_ptr((mp_shape_node) p));
                if (mp_dash_ptr(p) != NULL) {
                    mp_dash_node ppd, hhd;
                    int ok_to_dash = mp_pen_is_elliptical(mp_pen_ptr((mp_shape_node) p));
                    mp_print_nl(mp, "dashed (");
                    if (! ok_to_dash) {
                        set_number_to_unity(scf);
                    } else {
                        number_clone(scf, ((mp_shape_node) p)->dashscale);
                    }
                    hhd = (mp_dash_node) mp_dash_ptr(p);
                    ppd = mp_get_dash_list(hhd);
                    if ((ppd == mp->null_dash) || number_negative(hhd->dash_y)) {
                        mp_print_str(mp, " ??");
                    } else {
                        mp_number dashoff;
                        mp_number ret, arg1;
                        new_number(ret);
                        new_number(arg1);
                        new_number(dashoff);
                        set_number_from_addition(mp->null_dash->start_x, ppd->start_x, hhd->dash_y );
                        while (ppd != mp->null_dash) {
                            mp_print_str(mp, "on ");
                            set_number_from_subtraction(arg1, ppd->stop_x, ppd->start_x);
                            take_scaled(ret, arg1, scf);
                            print_number( ret);
                            mp_print_str(mp, " off ");
                            set_number_from_subtraction(arg1, ((mp_dash_node) ppd->link)->start_x, ppd->stop_x);
                            take_scaled(ret, arg1, scf);
                            print_number(ret);
                            ppd = (mp_dash_node) ppd->link;
                            if (ppd != mp->null_dash) {
                                mp_print_chr(mp, ' ');
                            }
                        }
                        mp_print_str(mp, ") shifted ");
                        mp_dash_offset(mp, &dashoff, hhd);
                        take_scaled(ret, dashoff, scf);
                        number_negate(ret);
                        print_number(ret);
                        free_number(dashoff);
                        free_number(ret);
                        free_number(arg1);
                        if (!ok_to_dash || number_zero(hhd->dash_y)) {
                            mp_print_str(mp, " (this will be ignored)");
                        }
                    }
                }
                mp_print_ln(mp);
                switch (((mp_shape_node) p)->linecap) {
                    case mp_butt_linecap_code:
                        mp_print_str(mp, "butt");
                        break;
                    case mp_rounded_linecap_code:
                        mp_print_str(mp, "round");
                        break;
                    case mp_squared_linecap_code:
                        mp_print_str(mp, "square");
                        break;
                    default:
                        mp_print_str(mp, "??");
                        break;
                }
                mp_print_str(mp, " ends, ");
                switch (((mp_shape_node) p)->linejoin) {
                    case mp_mitered_linejoin_code:
                        mp_print_str(mp, "mitered joins limited ");
                        print_number(((mp_shape_node) p)->miterlimit);
                        break;
                    case mp_rounded_linejoin_code:
                        mp_print_str(mp, "round joins");
                        break;
                    case mp_beveled_linejoin_code:
                        mp_print_str(mp, "beveled joins");
                        break;
                    default:
                        mp_print_str(mp, "?? joins");
                        break;
                }

                mp_print_str(mp, " with pen");
                mp_print_ln(mp);
                if (mp_pen_ptr((mp_shape_node) p) == NULL) {
                    mp_print_str(mp, "???");
                } else {
                    mp_pr_pen(mp, mp_pen_ptr((mp_shape_node) p));
                }
                break;
            case mp_start_clip_node_type:
                mp_print_str(mp, "clipping path:");
                goto COMMONSTART;
            case mp_start_group_node_type:
                mp_print_str(mp, "setgroup path:");
                goto COMMONSTART;
            case mp_start_bounds_node_type:
                mp_print_str(mp, "setbounds path:");
              COMMONSTART:
                mp_print_ln(mp);
                mp_pr_path(mp, mp_path_ptr((mp_start_node) p));
                break;
            case mp_stop_clip_node_type:
                mp_print_str(mp, "stop clipping");
                break;
            case mp_stop_group_node_type:
                mp_print_str(mp, "stop group");
                break;
            case mp_stop_bounds_node_type:
                mp_print_str(mp, "end of setbounds");
                break;

            default:
                mp_print_str(mp, "[unknown object type!]");
                break;
        }
    }
    mp_print_nl(mp, "End edges");
    if (p != mp_obj_tail(h)) {
        mp_print_str(mp, "?");
    }
    mp_end_diagnostic(mp, 1);
    free_number(scf);
}

void mp_print_obj_color (MP mp, mp_node p)
{
    mp_shape_node p0 = (mp_shape_node) p;
    switch (mp_color_model(p)) {
        case mp_grey_model:
            if (number_positive(p0->grey)) {
                mp_print_str(mp, "greyed ");
                mp_print_chr(mp, '(');
                print_number(p0->grey);
                mp_print_chr(mp, ')');
            };
            break;
        case mp_cmyk_model:
            if (number_positive(p0->cyan)   || number_positive(p0->magenta)
             || number_positive(p0->yellow) || number_positive(p0->black)) {
                mp_print_str(mp, "processcolored ");
                mp_print_chr(mp, '(');
                print_number(p0->cyan);
                mp_print_chr(mp, ',');
                print_number(p0->magenta);
                mp_print_chr(mp, ',');
                print_number(p0->yellow);
                mp_print_chr(mp, ',');
                print_number(p0->black);
                mp_print_chr(mp, ')');
            };
            break;
        case mp_rgb_model:
            if (number_positive(p0->red) || number_positive(p0->green) || number_positive(p0->blue)) {
                mp_print_str(mp, "colored ");
                mp_print_chr(mp, '(');
                print_number(p0->red);
                mp_print_chr(mp, ',');
                print_number(p0->green);
                mp_print_chr(mp, ',');
                print_number(p0->blue);
                mp_print_chr(mp, ')');
            }
            break;
        default:
            break;
    }
}

void mp_dash_offset (MP mp, mp_number *x, mp_dash_node h)
{
    if (mp_get_dash_list(h) == mp->null_dash || number_negative(h->dash_y)) {
        mp_confusion(mp, "dash offset");
    } else if (number_zero(h->dash_y)) {
        set_number_to_zero(*x);
    } else {
        number_clone(*x, (mp_get_dash_list(h))->start_x);
        number_modulo(*x, h->dash_y);
        number_negate(*x);
        if (number_negative(*x)) {
            number_add(*x, h->dash_y);
        }
    }
}

static mp_edge_header_node mp_make_dashes (MP mp, mp_edge_header_node h)
{
    if (mp_get_dash_list(h) != mp->null_dash) {
        return h;
    } else {
        mp_node p;
        mp_node p0;
        mp_knot pp, qq, rr;
        mp_dash_node d, dd;
        mp_number y0;
        mp_dash_node dln;
        mp_edge_header_node hh;
        mp_node ds;

        new_number(y0);
        p0 = NULL;
        p = mp_edge_list(h)->link;
        while (p != NULL) {
            if (p->type != mp_stroked_node_type) {
                mp_back_error(
                    mp,
                    "Picture is too complicated to use as a dash pattern",
                    "When you say 'dashed p', picture p should not contain any text, filled regions,\n"
                    "or clipping paths. This time it did so I'll just make it a solid line instead."
                );
                mp_get_x_next(mp);
                goto NOT_FOUND;
            }
            pp = mp_path_ptr((mp_shape_node) p);
            if (p0 == NULL) {
                p0 = p;
                number_clone(y0, pp->y_coord);
            }
            if (! number_equal(((mp_shape_node) p)->red,   ((mp_shape_node) p0)->red)
             || ! number_equal(((mp_shape_node) p)->black, ((mp_shape_node) p0)->black)
             || ! number_equal(((mp_shape_node) p)->green, ((mp_shape_node) p0)->green)
             || ! number_equal(((mp_shape_node) p)->blue,  ((mp_shape_node) p0)->blue)
                ) {
                mp_back_error(
                    mp,
                    "Picture is too complicated to use as a dash pattern",
                    "When you say 'dashed p', everything in picture p should be the same color. I\n"
                    "can't handle your color changes so I'll just make it a solid line instead."
                );
                mp_get_x_next(mp);
                goto NOT_FOUND;
            }
            rr = pp;
            if (mp_next_knot(pp) != pp) {
                do {
                    qq = rr;
                    rr = mp_next_knot(rr);
                    {
                        mp_number x0, x1, x2, x3;
                        new_number_clone(x0, qq->x_coord);
                        new_number_clone(x1, qq->right_x);
                        new_number_clone(x2, rr->left_x);
                        new_number_clone(x3, rr->x_coord);
                        if (number_greater(x0, x1) || number_greater(x1, x2) || number_greater(x2, x3)) {
                            if (number_less(x0, x1) || number_less(x1, x2) || number_less(x2, x3)) {
                                mp_number a1, a2, a3, a4;
                                int test;
                                new_number(a1);
                                new_number(a2);
                                new_number(a3);
                                new_number(a4);
                                set_number_from_subtraction(a1, x2, x1);
                                set_number_from_subtraction(a2, x2, x1);
                                set_number_from_subtraction(a3, x1, x0);
                                set_number_from_subtraction(a4, x3, x2);
                                test = ab_vs_cd(a1, a2, a3, a4);
                                free_number(a1);
                                free_number(a2);
                                free_number(a3);
                                free_number(a4);
                                if (test > 0) {
                                    mp_x_retrace_error(mp);
                                    free_number(x0);
                                    free_number(x1);
                                    free_number(x2);
                                    free_number(x3);
                                    goto NOT_FOUND;
                                }
                            }
                        }
                        if (number_greater(pp->x_coord, x0) || number_greater(x0, x3)) {
                            if (number_less(pp->x_coord, x0) || number_less(x0, x3)) {
                                mp_x_retrace_error(mp);
                                free_number(x0);
                                free_number(x1);
                                free_number(x2);
                                free_number(x3);
                                goto NOT_FOUND;
                            }
                        }
                        free_number(x0);
                        free_number(x1);
                        free_number(x2);
                        free_number(x3);
                    }
                } while (mp_right_type(rr) != mp_endpoint_knot);
            }
            d = (mp_dash_node) mp_get_dash_node(mp);
            if (mp_dash_ptr(p) == NULL) {
                mp_dash_info(d) = NULL;
            } else {
                mp_dash_info(d) = p;
            }
            if (number_less(pp->x_coord, rr->x_coord)) {
                number_clone(d->start_x, pp->x_coord);
                number_clone(d->stop_x, rr->x_coord);
            } else {
                number_clone(d->start_x, rr->x_coord);
                number_clone(d->stop_x, pp->x_coord);
            }
            number_clone(mp->null_dash->start_x, d->stop_x);
            dd = (mp_dash_node) h;
            while (number_less(((mp_dash_node) dd->link)->start_x, d->stop_x)) {
                dd = (mp_dash_node) dd->link;
            }
            if ((dd != (mp_dash_node) h) && number_greater(dd->stop_x, d->start_x)) {
                mp_x_retrace_error(mp);
                goto NOT_FOUND;
            }
            d->link = dd->link;
            dd->link = (mp_node) d;

            p = p->link;
        }
        if (mp_get_dash_list(h) == mp->null_dash) {
            goto NOT_FOUND;
        } else {
            {
            mp_number hsf;
            new_number(hsf);
            d = (mp_dash_node) h;
            while (d->link != (mp_node) mp->null_dash) {
                ds = mp_dash_info(d->link);
                if (ds == NULL) {
                    d = (mp_dash_node) d->link;
                } else {
                    hh = (mp_edge_header_node) mp_dash_ptr(ds);
                    number_clone(hsf, ((mp_shape_node) ds)->dashscale);
                    if (hh == NULL) {
                        mp_confusion(mp, "dash pattern");
                        return NULL;
                    } else if (number_zero(((mp_dash_node) hh)->dash_y )) {
                        d = (mp_dash_node) d->link;
                    } else if (mp_get_dash_list (hh) == NULL) {
                        mp_confusion(mp, "dash list");
                        return NULL;
                    } else {
                        mp_number xoff;
                        mp_number dashoff;
                        mp_number r1, r2;
                        new_number(r1);
                        new_number(r2);
                        dln = (mp_dash_node) d->link;
                        dd = mp_get_dash_list(hh);
                        new_number(xoff);
                        new_number(dashoff);
                        mp_dash_offset(mp, &dashoff, (mp_dash_node) hh);
                        take_scaled(r1, hsf, dd->start_x);
                        take_scaled(r2, hsf, dashoff);
                        number_add(r1, r2);
                        set_number_from_subtraction(xoff, dln->start_x, r1);
                        free_number(dashoff);
                        take_scaled(r1, hsf, dd->start_x);
                        take_scaled(r2, hsf, hh->dash_y);
                        set_number_from_addition(mp->null_dash->start_x, r1, r2);
                        number_clone(mp->null_dash->stop_x, mp->null_dash->start_x);
                        {
                            mp_number r1;
                            new_number(r1);
                            take_scaled(r1, hsf, dd->stop_x);
                            number_add(r1, xoff);
                            while (number_less(r1, dln->start_x)) {
                                dd = (mp_dash_node) dd->link;
                                take_scaled(r1, hsf, dd->stop_x);
                                number_add(r1, xoff);
                            }
                            free_number(r1);
                        }
                        while (number_lessequal(dln->start_x, dln->stop_x)) {
                            if (dd == mp->null_dash) {
                                mp_number ret;
                                new_number(ret);
                                dd = mp_get_dash_list(hh);
                                take_scaled(ret, hsf, hh->dash_y);
                                number_add(xoff, ret);
                                free_number(ret);
                            }
                            {
                                mp_number r1;
                                new_number(r1);
                                take_scaled(r1, hsf, dd->start_x);
                                number_add(r1, xoff);
                                if (number_lessequal(r1, dln->stop_x)) {
                                    d->link = (mp_node) mp_get_dash_node(mp);
                                    d = (mp_dash_node) d->link;
                                    d->link = (mp_node) dln;
                                    take_scaled(r1, hsf, dd->start_x );
                                    number_add(r1, xoff);
                                    if (number_greater(dln->start_x, r1)) {
                                        number_clone(d->start_x, dln->start_x);
                                    } else {
                                        number_clone(d->start_x, r1);
                                    }
                                    take_scaled(r1, hsf, dd->stop_x);
                                    number_add(r1, xoff);
                                    if (number_less(dln->stop_x, r1)) {
                                        number_clone(d->stop_x, dln->stop_x );
                                    } else {
                                        number_clone(d->stop_x, r1);
                                    }
                                }
                                free_number(r1);
                            }
                            dd = (mp_dash_node) dd->link;
                            take_scaled(r1, hsf, dd->start_x);
                            set_number_from_addition(dln->start_x , xoff, r1);
                        }
                        free_number(xoff);
                        free_number(r1);
                        free_number(r2);
                        d->link = dln->link;
                        mp_free_node(mp, (mp_node) dln, sizeof(mp_dash_node_data));
                    }
                }
            }
            free_number(hsf);
            }
            d = mp_get_dash_list(h);
            while (d->link != (mp_node) mp->null_dash) {
                d = (mp_dash_node) d->link;
            }
            dd = mp_get_dash_list(h);
            set_number_from_subtraction(h->dash_y, d->stop_x, dd->start_x);
            {
                mp_number absval;
                new_number(absval);
                number_abs_clone(absval, y0);
                if (number_greater(absval, h->dash_y) ) {
                    number_clone(h->dash_y, absval);
                } else if (d != dd) {
                    mp_set_dash_list(h, dd->link);
                    set_number_from_addition(d->stop_x, dd->stop_x, h->dash_y);
                    mp_free_node(mp, (mp_node) dd, sizeof(mp_dash_node_data));
                }
                free_number(absval);
            }
            free_number(y0);
            return h;
        }
      NOT_FOUND:
        free_number(y0);
        mp_flush_dash_list(mp, h);
        mp_delete_edge_ref(mp, h);
        return NULL;
    }
}

void mp_x_retrace_error (MP mp)
{
    mp_back_error(
        mp,
        "Picture is too complicated to use as a dash pattern",
        "When you say 'dashed p', every path in p should be monotone in x and there must\n"
        "be no overlapping. This failed so I'll just make it a solid line instead."
    );
    mp_get_x_next(mp);
}

static void mp_adjust_bbox (MP mp, mp_edge_header_node h)
{
    if (number_less(mp_minx, h->minx)) {
        number_clone(h->minx, mp_minx);
    }
    if (number_less(mp_miny, h->miny)) {
        number_clone(h->miny, mp_miny);
    }
    if (number_greater(mp_maxx, h->maxx)) {
        number_clone(h->maxx, mp_maxx);
    }
    if (number_greater(mp_maxy, h->maxy)) {
        number_clone(h->maxy, mp_maxy);
    }
}

static void mp_box_ends (MP mp, mp_knot p, mp_knot pp, mp_edge_header_node h)
{
    if (mp_right_type(p) != mp_endpoint_knot) {
        mp_number dx, dy;
        mp_number d;
        mp_number z;
        mp_number xx, yy;
        mp_knot q = mp_next_knot(p);
        new_fraction(dx);
        new_fraction(dy);
        new_number(xx);
        new_number(yy);
        new_number(z);
        new_number(d);
        while (1) {
            if (q == mp_next_knot(p)) {
                set_number_from_subtraction(dx, p->x_coord, p->right_x);
                set_number_from_subtraction(dy, p->y_coord, p->right_y);
                if (number_zero(dx) && number_zero(dy)) {
                    set_number_from_subtraction(dx, p->x_coord, q->left_x);
                    set_number_from_subtraction(dy, p->y_coord, q->left_y);
                }
            } else {
                set_number_from_subtraction(dx, p->x_coord, p->left_x);
                set_number_from_subtraction(dy, p->y_coord, p->left_y);
                if (number_zero(dx) && number_zero(dy)) {
                    set_number_from_subtraction(dx, p->x_coord, q->right_x);
                    set_number_from_subtraction(dy, p->y_coord, q->right_y);
                }
            }
            set_number_from_subtraction(dx, p->x_coord, q->x_coord);
            set_number_from_subtraction(dy, p->y_coord, q->y_coord);

            pyth_add(d, dx, dy);
            if (number_positive(d)) {
                mp_number arg1, r;
                new_fraction(r);
                new_number(arg1);
                make_fraction(r, dx, d);
                number_clone(dx, r);
                make_fraction(r, dy, d);
                number_clone(dy, r);
                free_number(r);
                number_negated_clone(arg1, dy);
                mp_find_offset(mp, &arg1, &dx, pp);
                free_number(arg1);
                number_clone(xx, mp->cur_x);
                number_clone(yy, mp->cur_y);

                for (int i = 1; i <= 2; i++) {
                    mp_number r1, r2, arg1;
                    new_number(arg1);
                    new_fraction(r1);
                    new_fraction(r2);
                    mp_find_offset(mp, &dx, &dy, pp);
                    set_number_from_subtraction(arg1, xx, mp->cur_x);
                    take_fraction(r1, arg1, dx);
                    set_number_from_subtraction(arg1, yy, mp->cur_y);
                    take_fraction(r2, arg1, dy);
                    set_number_from_addition(d, r1, r2);
                    if ((number_negative(d) && (i == 1)) || (number_positive(d) && (i == 2))) {
                        mp_confusion(mp, "box ends");
                    }
                    take_fraction(r1, d, dx);
                    set_number_from_addition(z, p->x_coord, mp->cur_x);
                    number_add(z, r1);
                    if (number_less(z, h->minx)) {
                        number_clone(h->minx, z);
                    }
                    if (number_greater(z, h->maxx)) {
                        number_clone(h->maxx, z);
                    }
                    take_fraction(r1, d, dy);
                    set_number_from_addition(z, p->y_coord, mp->cur_y);
                    number_add(z, r1);
                    if (number_less(z, h->miny)) {
                        number_clone(h->miny, z);
                    }
                    if (number_greater(z, h->maxy)) {
                        number_clone(h->maxy, z);
                    }
                    free_number(r1);
                    free_number(r2);
                    free_number(arg1);

                    number_negate(dx);
                    number_negate(dy);
                }
            }
            if (mp_right_type(p) == mp_endpoint_knot) {
                goto DONE;
            } else {
                do {
                    q = p;
                    p = mp_next_knot(p);
                } while (mp_right_type(p) != mp_endpoint_knot);
            }
        }
      DONE:
        free_number(dx);
        free_number(dy);
        free_number(xx);
        free_number(yy);
        free_number(z);
        free_number(d);
    }
}

void mp_set_bbox (MP mp, mp_edge_header_node h, int top_level)
{
    switch (h->bbtype ) {
        case mp_no_bounds_code:
            break;
        case mp_bounds_set_code:
            if (number_positive(internal_value(mp_true_corners_internal))) {
                mp_init_bbox(mp, h);
            }
            break;
        case mp_bounds_unset_code:
            if (number_nonpositive(internal_value(mp_true_corners_internal))) {
                mp_init_bbox(mp, h);
            }
            break;
    }
    while (mp_bblast(h)->link != NULL) {
        mp_node p = mp_bblast(h)->link;
        mp_bblast(h) = p;
        switch (p->type) {
            case mp_stop_clip_node_type:
                if (top_level) {
                    mp_confusion(mp, "clip");
                    break;
                } else {
                    return;
                }
            case mp_start_bounds_node_type:
                if (number_positive(internal_value(mp_true_corners_internal))) {
                    h->bbtype = mp_bounds_unset_code;
                } else {
                    h->bbtype = mp_bounds_set_code;
                    mp_path_bbox(mp, mp_path_ptr((mp_start_node) p));
                    mp_adjust_bbox(mp, h);
                    {
                        int lev = 1;
                        while (lev != 0) {
                            if (p->link == NULL) {
                                mp_confusion(mp, "bounds");
                            } else {
                                p = p->link;
                                if (p->type == mp_start_bounds_node_type) {
                                    ++lev;
                                } else if (p->type == mp_stop_bounds_node_type) {
                                    --lev;
                                }
                            }
                        }
                        mp_bblast(h) = p;
                    }
                }
                break;
            case mp_stop_bounds_node_type:
                if (number_nonpositive (internal_value(mp_true_corners_internal))) {
                    mp_confusion(mp, "bounds");
                }
                break;
            case mp_fill_node_type:
            case mp_stroked_node_type:
                {
                    mp_number x0a, y0a, x1a, y1a;
                    mp_path_bbox(mp, mp_path_ptr((mp_shape_node) p));
                    if (mp_pen_ptr((mp_shape_node) p) != NULL) {
                        new_number_clone(x0a, mp_minx);
                        new_number_clone(y0a, mp_miny);
                        new_number_clone(x1a, mp_maxx);
                        new_number_clone(y1a, mp_maxy);
                        mp_pen_bbox(mp, mp_pen_ptr((mp_shape_node) p));
                        number_add(mp_minx, x0a);
                        number_add(mp_miny, y0a);
                        number_add(mp_maxx, x1a);
                        number_add(mp_maxy, y1a);
                        free_number(x0a);
                        free_number(y0a);
                        free_number(x1a);
                        free_number(y1a);
                    }
                    mp_adjust_bbox(mp, h);
                    if ((mp_left_type(mp_path_ptr((mp_shape_node) p)) == mp_endpoint_knot) && (((mp_shape_node) p)->linecap == 2)) {
                        mp_box_ends(mp, mp_path_ptr((mp_shape_node) p), mp_pen_ptr((mp_shape_node) p), h);
                    }
                }
                break;
            case mp_start_clip_node_type:
                {
                    mp_number sminx, sminy, smaxx, smaxy;
                    mp_number x0a, y0a, x1a, y1a;
                    mp_path_bbox(mp, mp_path_ptr((mp_start_node) p));
                    new_number_clone(x0a, mp_minx);
                    new_number_clone(y0a, mp_miny);
                    new_number_clone(x1a, mp_maxx);
                    new_number_clone(y1a, mp_maxy);
                    new_number_clone(sminx, h->minx);
                    new_number_clone(sminy, h->miny);
                    new_number_clone(smaxx, h->maxx);
                    new_number_clone(smaxy, h->maxy);
                    set_number_to_inf(h->minx);
                    set_number_to_inf(h->miny);
                    set_number_to_negative_inf(h->maxx);
                    set_number_to_negative_inf(h->maxy);
                    mp_set_bbox(mp, h, 0);

                    if (number_less(h->minx, x0a)) {
                        number_clone(h->minx, x0a);
                    }
                    if (number_less(h->miny, y0a)) {
                        number_clone(h->miny, y0a);
                    }
                    if (number_greater(h->maxx, x1a)) {
                        number_clone(h->maxx, x1a);
                    }
                    if (number_greater(h->maxy, y1a)) {
                        number_clone(h->maxy, y1a);
                    }
                    number_clone(mp_minx, sminx);
                    number_clone(mp_miny, sminy);
                    number_clone(mp_maxx, smaxx);
                    number_clone(mp_maxy, smaxy);
                    mp_adjust_bbox(mp, h);
                    free_number(sminx);
                    free_number(sminy);
                    free_number(smaxx);
                    free_number(smaxy);
                    free_number(x0a);
                    free_number(y0a);
                    free_number(x1a);
                    free_number(y1a);
                }
                break;

            default:
                break;
        }
    }
    if (! top_level) {
        mp_confusion(mp, "boundingbox");
    }
}

static mp_knot mp_offset_prep (MP mp, mp_knot c, mp_knot h)
{
    int n;
    mp_knot c0, p, q, q0, r, w, ww;
    int k_needed;
    mp_knot w0;
    mp_number dxin, dyin;
    int turn_amt;
    mp_number max_coef;
    mp_number ss;
    mp_number x0, x1, x2, y0, y1, y2;
    mp_number t0, t1, t2;
    mp_number du, dv, dx, dy;
    mp_number dx0, dy0;
    mp_number x0a, x1a,x2a, y0a, y1a, y2a;
    mp_number t;
    mp_number s;
    mp_number dx_m;
    mp_number dy_m;
    mp_number dxin_m;
    mp_number u0, u1, v0, v1;
    int d_sign;
    new_number(max_coef);
    new_number(dxin);
    new_number(dyin);
    new_number(dx0);
    new_number(dy0);
    new_number(x0);
    new_number(y0);
    new_number(x1);
    new_number(y1);
    new_number(x2);
    new_number(y2);
    new_number(du);
    new_number(dv);
    new_number(dx);
    new_number(dy);
    new_number(x0a);
    new_number(y0a);
    new_number(x1a);
    new_number(y1a);
    new_number(x2a);
    new_number(y2a);
    new_number(t0);
    new_number(t1);
    new_number(t2);
    new_number(u0);
    new_number(u1);
    new_number(v0);
    new_number(v1);
    new_number(dx_m);
    new_number(dy_m);
    new_number(dxin_m);
    new_fraction(ss);
    new_fraction(s);
    new_fraction(t);
    n = 0;
    p = h;
    do {
        ++n;
        p = mp_next_knot(p);
    } while (p != h);

    {
        mp_knot hn = mp_next_knot(h);
        mp_knot hp = mp_prev_knot(h);
        set_number_from_subtraction(dxin, hn->x_coord, hp->x_coord);
        set_number_from_subtraction(dyin, hn->y_coord, hp->y_coord);
        if (number_zero(dxin) && number_zero(dyin)) {
            set_number_from_subtraction(dxin, hp->y_coord, h->y_coord);
            set_number_from_subtraction(dyin, h->x_coord, hp->x_coord);
        }
    }
    w0 = h;

    p = c;
    c0 = c;
    k_needed = 0;
     do {
        q = mp_next_knot(p);
        mp_knot_info(p) = zero_off + k_needed;
        k_needed = 0;
        set_number_from_subtraction(x0, p->right_x, p->x_coord);
        set_number_from_subtraction(x2, q->x_coord, q->left_x);
        set_number_from_subtraction(x1, q->left_x, p->right_x);
        set_number_from_subtraction(y0, p->right_y, p->y_coord);
        set_number_from_subtraction(y2, q->y_coord, q->left_y);
        set_number_from_subtraction(y1, q->left_y, p->right_y);
        {
        	
            mp_number absval;
            new_number_abs(absval, x1);
            number_abs_clone(max_coef, x0);
            if (number_greater(absval, max_coef)) {
                number_clone(max_coef, absval);
            }
            number_abs_clone(absval, x2);
            if (number_greater(absval, max_coef)) {
                number_clone(max_coef, absval);
            }
            number_abs_clone(absval, y0);
            if (number_greater(absval, max_coef)) {
                number_clone(max_coef, absval);
            }
            number_abs_clone(absval, y1);
            if (number_greater(absval, max_coef)) {
                number_clone(max_coef, absval);
            }
            number_abs_clone(absval, y2);
            if (number_greater(absval, max_coef)) {
                number_clone(max_coef, absval);
            }
            free_number(absval);
            if (number_zero(max_coef)) {
                goto NOT_FOUND;
            }
        }
        while (number_less(max_coef, fraction_half_t)) {
            number_double(max_coef);
            number_double(x0);
            number_double(x1);
            number_double(x2);
            number_double(y0);
            number_double(y1);
            number_double(y2);
        }
        number_clone(dx_m, zero_t);
        number_clone(dy_m, zero_t);
        number_clone(dx, x0);
        number_clone(dy, y0);
        if (number_zero(dx) && number_zero(dy)) {
            number_clone(dx, x1);
            number_clone(dy, y1);
            if (number_zero(dx) && number_zero(dy)) {
                number_clone(dx, x2);
                number_clone(dy, y2);
            }
        }
        if (p == c) {
            number_clone(dx0, dx);
            number_clone(dy0, dy);
        }
        {
            turn_amt = mp_get_turn_amt(mp, w0, &dx, &dy, ab_vs_cd(dy, dxin, dx, dyin) >= 0);
            w = mp_pen_walk(mp, w0, turn_amt);
            w0 = w;
            mp_knot_info(p) = mp_knot_info(p) + turn_amt;
        }
        number_clone(dxin, x2);
        number_clone(dyin, y2);
        if (number_zero(dxin) && number_zero(dyin)) {
            number_clone(dxin, x1);
            number_clone(dyin, y1);
            if (number_zero(dxin) && number_zero(dyin)) {
                number_clone(dxin, x0);
                number_clone(dyin, y0);
            }
        }
        {
            int sign = ab_vs_cd(dx, dyin, dxin, dy);
            if (sign < 0) {
                d_sign = -1;
            } else if (sign == 0) {
                d_sign = 0;
            } else {
                d_sign = 1;
            }
        }
        if (d_sign == 0) {
            {
                int t;
                set_number_from_subtraction(u0, q->x_coord, p->x_coord);
                set_number_from_subtraction(u1, q->y_coord, p->y_coord);
                t = ab_vs_cd(dx, u1, u0, dy) + ab_vs_cd(u0, dyin, dxin, u1);

                if (t < 0) {
                    d_sign = -1;
                } else if (t == 0) {
                    d_sign = 0;
                } else {
                    d_sign = 1;
                }
            }
        }
        if (d_sign == 0) {
            if (number_zero(dx)) {
                d_sign = number_positive(dy) ? 1 : -1;
            } else {
                d_sign = number_positive(dx) ? 1 : -1;
            }
        }
        {
            mp_number r1, r2, arg1;
            new_number(arg1);
            new_fraction(r1);
            new_fraction(r2);
            take_fraction(r1, x0, y2);
            take_fraction(r2, x2, y0);
            number_half(r1);
            number_half(r2);
            set_number_from_subtraction(t0, r1, r2);
            set_number_from_addition(arg1, y0, y2);
            take_fraction(r1, x1, arg1);
            set_number_from_addition(arg1, x0, x2);

            take_fraction(r2, y1, arg1);
            number_half(r1);
            number_half(r2);
            set_number_from_subtraction(t1, r1, r2);
            free_number(arg1);
            free_number(r1);
            free_number(r2);
        }
        if (number_zero(t0)) {
            set_number_from_scaled(t0, d_sign);
        }
        if (number_positive(t0)) {
            mp_number arg3;
            new_number(arg3);
            number_negated_clone(arg3, t0);
            crossing_point(t, t0, t1, arg3);
            free_number(arg3);
            set_number_from_of_the_way(u0, t, x0, x1);
            set_number_from_of_the_way(u1, t, x1, x2);
            set_number_from_of_the_way(v0, t, y0, y1);
            set_number_from_of_the_way(v1, t, y1, y2);
        } else {
            mp_number arg1;
            new_number(arg1);
            number_negated_clone(arg1, t0);
            crossing_point(t, arg1, t1, t0);
            free_number(arg1);
            set_number_from_of_the_way(u0, t, x2, x1);
            set_number_from_of_the_way(u1, t, x1, x0);
            set_number_from_of_the_way(v0, t, y2, y1);
            set_number_from_of_the_way(v1, t, y1, y0);
        }
        {
            mp_number tmp1, tmp2, r1, r2, arg1;
            new_fraction(r1);
            new_fraction(r2);
            new_number(arg1);
            new_number(tmp1);
            new_number(tmp2);
            set_number_from_of_the_way(tmp1, t, u0, u1);
            set_number_from_of_the_way(tmp2, t, v0, v1);
            set_number_from_addition(arg1, x0, x2);
            take_fraction(r1, arg1, tmp1);
            set_number_from_addition(arg1, y0, y2);
            take_fraction(r2, arg1, tmp2);
            set_number_from_addition(ss, r1, r2);
            free_number(arg1);
            free_number(r1);
            free_number(r2);
            free_number(tmp1);
            free_number(tmp2);
        }
        turn_amt = mp_get_turn_amt(mp, w, &dxin, &dyin, (d_sign > 0));
        if (number_negative(ss)) {
            turn_amt = turn_amt - d_sign * n;
        }
        ww = mp_prev_knot(w);
        {
            mp_number abs_du, abs_dv;
            new_number(abs_du);
            new_number(abs_dv);
            set_number_from_subtraction(du, ww->x_coord, w->x_coord);
            set_number_from_subtraction(dv, ww->y_coord, w->y_coord);
            number_abs_clone(abs_du, du);
            number_abs_clone(abs_dv, dv);
            if (number_greaterequal(abs_du, abs_dv)) {
                mp_number r1;
                new_fraction(r1);
                make_fraction(s, dv, du);
                take_fraction(r1, x0, s);
                set_number_from_subtraction(t0, r1, y0);
                take_fraction(r1, x1, s);
                set_number_from_subtraction(t1, r1, y1);
                take_fraction(r1, x2, s);
                set_number_from_subtraction(t2, r1, y2);
                if (number_negative(du)) {
                    number_negate(t0);
                    number_negate(t1);
                    number_negate(t2);
                }
                free_number(r1);
            } else {
                mp_number r1;
                new_fraction(r1);
                make_fraction(s, du, dv);
                take_fraction(r1, y0, s);
                set_number_from_subtraction(t0, x0, r1);
                take_fraction(r1, y1, s);
                set_number_from_subtraction(t1, x1, r1);
                take_fraction(r1, y2, s);
                set_number_from_subtraction(t2, x2, r1);
                if (number_negative(dv)) {
                    number_negate(t0);
                    number_negate(t1);
                    number_negate(t2);
                }
                free_number(r1);
            }
            free_number(abs_du);
            free_number(abs_dv);
            if (number_negative(t0)) {
                set_number_to_zero(t0);
            }
        }
        crossing_point(t, t0, t1, t2);
        if (turn_amt >= 0) {
            if (number_negative(t2)) {
                number_clone(t, fraction_one_t);
                number_add_scaled(t, 1);
            } else {
                mp_number tmp, arg1, r1;
                new_fraction(r1);
                new_number(tmp);
                new_number(arg1);
                set_number_from_of_the_way(u0, t, x0, x1);
                set_number_from_of_the_way(u1, t, x1, x2);
                set_number_from_of_the_way(tmp, t, u0, u1);
                number_negated_clone(arg1, du);
                take_fraction(ss, arg1, tmp);
                set_number_from_of_the_way(v0, t, y0, y1);
                set_number_from_of_the_way(v1, t, y1, y2);
                set_number_from_of_the_way(tmp, t, v0, v1);
                number_negated_clone(arg1, dv);
                take_fraction(r1, arg1, tmp);
                number_add(ss, r1);
                free_number(tmp);
                if (number_negative(ss)) {
                    number_clone(t, fraction_one_t);
                    number_add_scaled(t, 1);
                }
                free_number(arg1);
                free_number(r1);
            }
        } else if (number_greater(t, fraction_one_t)) {
            number_clone(t, fraction_one_t);
        }
        if (number_greater(t, fraction_one_t)) {
            mp_fin_offset_prep(mp, p, w, &x0, &x1, &x2, &y0, &y1, &y2, 1, turn_amt);
        } else {
            mp_split_cubic(mp, p, &t);
            r = mp_next_knot(p);
            set_number_from_of_the_way(x1a, t, x0, x1);
            set_number_from_of_the_way(x1, t, x1, x2);
            set_number_from_of_the_way(x2a, t, x1a, x1);
            set_number_from_of_the_way(y1a, t, y0, y1);
            set_number_from_of_the_way(y1, t, y1, y2);
            set_number_from_of_the_way(y2a, t, y1a, y1);
            mp_fin_offset_prep (mp, p, w, &x0, &x1a, &x2a, &y0, &y1a, &y2a, 1, 0);
            number_clone(x0, x2a);
            number_clone(y0, y2a);
            mp_knot_info(r) = zero_off - 1;
            if (turn_amt >= 0) {
                mp_number arg1, arg2, arg3;
                new_number(arg1);
                new_number(arg2);
                new_number(arg3);
                set_number_from_of_the_way(t1, t, t1, t2);
                if (number_positive(t1)) {
                    set_number_to_zero(t1);
                }
                number_negated_clone(arg2, t1);
                number_negated_clone(arg3, t2);
                crossing_point(t, arg1, arg2, arg3);
                free_number(arg1);
                free_number(arg2);
                free_number(arg3);
                if (number_greater(t, fraction_one_t)) {
                    number_clone(t, fraction_one_t);
                }
                mp_split_cubic(mp, r, &t);
                mp_knot_info(mp_next_knot(r)) = zero_off + 1;
                set_number_from_of_the_way(x1a, t, x1, x2);
                set_number_from_of_the_way(x1,  t, x0, x1);
                set_number_from_of_the_way(x0a, t, x1, x1a);
                set_number_from_of_the_way(y1a, t, y1, y2);
                set_number_from_of_the_way(y1,  t, y0, y1);
                set_number_from_of_the_way(y0a, t, y1, y1a);
                mp_fin_offset_prep (mp, mp_next_knot(r), w, &x0a, &x1a, &x2, &y0a, &y1a, &y2, 1, turn_amt);
                number_clone(x2, x0a);
                number_clone(y2, y0a);

                mp_fin_offset_prep(mp, r, ww, &x0, &x1, &x2, &y0, &y1, &y2, -1, 0);
            } else {
                mp_fin_offset_prep(mp, r, ww, &x0, &x1, &x2, &y0, &y1, &y2, -1, (-1 - turn_amt));
            }
        }
        w0 = mp_pen_walk (mp, w0, turn_amt);

      NOT_FOUND:
        q0 = q;
        do {
            r = mp_next_knot(p);
            if (r != p && r != q
             && number_equal(p->x_coord, p->right_x)
             && number_equal(p->y_coord, p->right_y)
             && number_equal(p->x_coord, r->left_x)
             && number_equal(p->y_coord, r->left_y)
             && number_equal(p->x_coord, r->x_coord)
             && number_equal(p->y_coord, r->y_coord)) {
                {
                    k_needed = mp_knot_info(p) - zero_off;
                    if (r == q) {
                        q = p;
                    } else {
                        mp_knot_info(p) = k_needed + mp_knot_info(r);
                        k_needed = 0;
                    }
                    if (r == c) {
                        mp_knot_info(p) = mp_knot_info(c);
                        c = p;
                    }
                    if (r == mp->spec_p1) {
                        mp->spec_p1 = p;
                    }
                    if (r == mp->spec_p2) {
                        mp->spec_p2 = p;
                    }
                    r = p;
                    mp_remove_cubic(mp, p);
                }
            }
            p = r;
        } while (p != q);
        if ((q != q0) && (q != c || c == c0)) {
            q = mp_next_knot(q);
        }
    } while (q != c);
    mp->spec_offset = mp_knot_info(c) - zero_off;
    if (mp_next_knot(c) == c) {
        mp_knot_info(c) = zero_off + n;
    } else {
        mp_knot_info(c) += k_needed;
        while (w0 != h) {
            mp_knot_info(c) += 1;
            w0 = mp_next_knot(w0);
        }
        while (mp_knot_info(c) <= zero_off - n) {
            mp_knot_info(c) += n;
        }
        while (mp_knot_info(c) > zero_off) {
            mp_knot_info(c) -= n;
        }
        ;
        if ((mp_knot_info(c) != zero_off) && ab_vs_cd(dy0, dxin, dx0, dyin) >= 0) {
            mp_knot_info(c) += n;
        }
    }
    free_number(ss);
    free_number(s);
    free_number(dxin);
    free_number(dyin);
    free_number(dx0);
    free_number(dy0);
    free_number(x0);
    free_number(y0);
    free_number(x1);
    free_number(y1);
    free_number(x2);
    free_number(y2);
    free_number(max_coef);
    free_number(du);
    free_number(dv);
    free_number(dx);
    free_number(dy);
    free_number(x0a);
    free_number(y0a);
    free_number(x1a);
    free_number(y1a);
    free_number(x2a);
    free_number(y2a);
    free_number(t0);
    free_number(t1);
    free_number(t2);
    free_number(u0);
    free_number(u1);
    free_number(v0);
    free_number(v1);
    free_number(dx_m);
    free_number(dy_m);
    free_number(dxin_m);
    free_number(t);
    return c;
}

void mp_split_cubic (MP mp, mp_knot p, mp_number *t)
{
    mp_number v;
    mp_knot q = mp_next_knot(p);
    mp_knot r = mp_new_knot(mp);
    mp_prev_knot(r) = p;
    mp_next_knot(p) = r;
    mp_prev_knot(q) = r;
    mp_next_knot(r) = q;
    mp_originator(r) = mp_program_code;
    mp_knotstate(r) = mp_regular_knot;
    mp_left_type(r) = mp_explicit_knot;
    mp_right_type(r) = mp_explicit_knot;
    new_number(v);
    set_number_from_of_the_way(v,          *t, p->right_x, q->left_x);
    set_number_from_of_the_way(p->right_x, *t, p->x_coord, p->right_x);
    set_number_from_of_the_way(q->left_x,  *t, q->left_x, q->x_coord);
    set_number_from_of_the_way(r->left_x,  *t, p->right_x, v);
    set_number_from_of_the_way(r->right_x, *t, v, q->left_x);
    set_number_from_of_the_way(r->x_coord, *t, r->left_x, r->right_x);
    set_number_from_of_the_way(v,          *t, p->right_y, q->left_y);
    set_number_from_of_the_way(p->right_y, *t, p->y_coord, p->right_y);
    set_number_from_of_the_way(q->left_y,  *t, q->left_y, q->y_coord);
    set_number_from_of_the_way(r->left_y,  *t, p->right_y, v);
    set_number_from_of_the_way(r->right_y, *t, v, q->left_y);
    set_number_from_of_the_way(r->y_coord, *t, r->left_y, r->right_y);
    free_number(v);
}

static mp_knot mp_split_cubic_knot (MP mp, mp_knot p, mp_number *t)
{
    mp_number v;
    mp_knot k = mp_new_knot(mp);
    mp_knot r = mp_copy_knot(mp, mp_next_knot(p));
    mp_knot l = mp_copy_knot(mp, p);
    mp_originator(k) = mp_program_code;
    mp_knotstate(k) = mp_regular_knot;
    mp_left_type(k) = mp_explicit_knot;
    mp_right_type(k) = mp_explicit_knot;
    new_number(v);
    set_number_from_of_the_way(v,          *t, l->right_x, r->left_x);
    set_number_from_of_the_way(l->right_x, *t, l->x_coord, l->right_x);
    set_number_from_of_the_way(r->left_x,  *t, r->left_x, r->x_coord);
    set_number_from_of_the_way(k->left_x,  *t, l->right_x, v);
    set_number_from_of_the_way(k->right_x, *t, v, r->left_x);
    set_number_from_of_the_way(k->x_coord, *t, k->left_x, k->right_x);
    set_number_from_of_the_way(v,          *t, l->right_y, r->left_y);
    set_number_from_of_the_way(l->right_y, *t, l->y_coord, l->right_y);
    set_number_from_of_the_way(r->left_y,  *t, r->left_y, r->y_coord);
    set_number_from_of_the_way(k->left_y,  *t, l->right_y, v);
    set_number_from_of_the_way(k->right_y, *t, v, r->left_y);
    set_number_from_of_the_way(k->y_coord, *t, k->left_y, k->right_y);
    free_number(v);
    mp_toss_knot(mp, l);
    mp_toss_knot(mp, r);
    return k;
}

void mp_remove_cubic (MP mp, mp_knot p)
{
    mp_knot q = mp_next_knot(p);
    mp_prev_knot(q) = mp_next_knot(p);
    mp_next_knot(p) = mp_next_knot(q);
    number_clone(p->right_x, q->right_x);
    number_clone(p->right_y, q->right_y);
    mp_toss_knot(mp, q);
}

mp_knot mp_pen_walk (MP mp, mp_knot w, int k)
{
    (void) mp;
    while (k > 0) {
        w = mp_next_knot(w);
        --k;
    }
    while (k < 0) {
        w = mp_prev_knot(w);
        ++k;
    }
    return w;
}

void mp_fin_offset_prep (MP mp, mp_knot p, mp_knot w, mp_number *x0, mp_number *x1, mp_number *x2, mp_number *y0, mp_number *y1, mp_number *y2, int rise, int turn_amt)
{
    mp_number du, dv;
    mp_number t0, t1, t2;
    mp_number t;
    mp_number s;
    mp_number v;
    mp_knot q = mp_next_knot(p);
    new_number(du);
    new_number(dv);
    new_number(v);
    new_number(t0);
    new_number(t1);
    new_number(t2);
    new_fraction(s);
    new_fraction(t);
    while (1) {
        mp_knot ww = rise > 0 ? mp_next_knot(w) : mp_prev_knot(w);
        {
            mp_number abs_du, abs_dv;
            new_number(abs_du);
            new_number(abs_dv);
            set_number_from_subtraction(du, ww->x_coord, w->x_coord);
            set_number_from_subtraction(dv, ww->y_coord, w->y_coord);
            number_abs_clone(abs_du, du);
            number_abs_clone(abs_dv, dv);
            if (number_greaterequal(abs_du, abs_dv)) {
                mp_number r1;
                new_fraction(r1);
                make_fraction(s, dv, du);
                take_fraction(r1, *x0, s);
                set_number_from_subtraction(t0, r1, *y0);
                take_fraction(r1, *x1, s);
                set_number_from_subtraction(t1, r1, *y1);
                take_fraction(r1, *x2, s);
                set_number_from_subtraction(t2, r1, *y2);
                if (number_negative(du)) {
                    number_negate(t0);
                    number_negate(t1);
                    number_negate(t2);
                }
                free_number(r1);
            } else {
                mp_number r1;
                new_fraction(r1);
                make_fraction(s, du, dv);
                take_fraction(r1, *y0, s);
                set_number_from_subtraction(t0, *x0, r1);
                take_fraction(r1, *y1, s);
                set_number_from_subtraction(t1, *x1, r1);
                take_fraction(r1, *y2, s);
                set_number_from_subtraction(t2, *x2, r1);
                if (number_negative(dv)) {
                    number_negate(t0);
                    number_negate(t1);
                    number_negate(t2);
                }
                free_number(r1);
            }
            free_number(abs_du);
            free_number(abs_dv);
            if (number_negative(t0)) {
                set_number_to_zero(t0);
            }
        }
        crossing_point(t, t0, t1, t2);
        if (number_greaterequal(t, fraction_one_t)) {
            if (turn_amt > 0) {
                number_clone(t, fraction_one_t);
            } else {
                goto RETURN;
            }
        }
        {
            mp_split_cubic(mp, p, &t);
            p = mp_next_knot(p);
            mp_knot_info(p) = zero_off + rise;
            --turn_amt;
            set_number_from_of_the_way(v, t, *x0, *x1);
            set_number_from_of_the_way(*x1, t, *x1, *x2);
            set_number_from_of_the_way(*x0, t, v, *x1);
            set_number_from_of_the_way(v, t, *y0, *y1);
            set_number_from_of_the_way(*y1, t, *y1, *y2);
            set_number_from_of_the_way(*y0, t, v, *y1);
            if (turn_amt < 0) {
                mp_number arg1, arg2, arg3;
                new_number(arg1);
                new_number(arg2);
                new_number(arg3);
                set_number_from_of_the_way(t1, t, t1, t2);
                if (number_positive(t1)) {
                    set_number_to_zero(t1);
                }
                number_negated_clone(arg2, t1);
                number_negated_clone(arg3, t2);
                crossing_point(t, arg1, arg2, arg3);
                free_number(arg1);
                free_number(arg2);
                free_number(arg3);
                if (number_greater(t, fraction_one_t)) {
                    number_clone(t, fraction_one_t);
                }
                ++turn_amt;
                if (number_equal(t,fraction_one_t) && (mp_next_knot(p) != q)) {
                    mp_knot_info(mp_next_knot(p)) = mp_knot_info(mp_next_knot(p)) - rise;
                } else {
                    mp_split_cubic(mp, p, &t);
                    mp_knot_info(mp_next_knot(p)) = zero_off - rise;
                    set_number_from_of_the_way(v, t, *x1, *x2);
                    set_number_from_of_the_way(*x1, t, *x0, *x1);
                    set_number_from_of_the_way(*x2, t, *x1, v);
                    set_number_from_of_the_way(v, t, *y1, *y2);
                    set_number_from_of_the_way(*y1, t, *y0, *y1);
                    set_number_from_of_the_way(*y2, t, *y1, v);
                }
            }
        }
        w = ww;
    }
  RETURN:
    free_number(s);
    free_number(t);
    free_number(du);
    free_number(dv);
    free_number(v);
    free_number(t0);
    free_number(t1);
    free_number(t2);
}

int mp_get_turn_amt (MP mp, mp_knot w, mp_number *dx, mp_number *dy, int ccw)
{
    int s = 0;
    mp_number arg1, arg2;
    new_number(arg1);
    new_number(arg2);
    if (ccw) {
        int t;
        mp_knot ww = mp_next_knot(w);
        do {
            set_number_from_subtraction(arg1, ww->x_coord, w->x_coord);
            set_number_from_subtraction(arg2, ww->y_coord, w->y_coord);
            t = ab_vs_cd(*dy, arg1, *dx, arg2);
            if (t < 0) {
                break;
            } else {
                ++s;
                w = ww;
                ww = mp_next_knot(ww);
            }
        } while (t > 0);
    } else {
        mp_knot ww = mp_prev_knot(w);
        set_number_from_subtraction(arg1, w->x_coord, ww->x_coord);
        set_number_from_subtraction(arg2, w->y_coord, ww->y_coord);
        while (ab_vs_cd(*dy, arg1, *dx, arg2) < 0) {
            --s;
            w = ww;
            ww = mp_prev_knot(ww);
            set_number_from_subtraction(arg1, w->x_coord, ww->x_coord);
            set_number_from_subtraction(arg2, w->y_coord, ww->y_coord);
        }
    }
    free_number(arg1);
    free_number(arg2);
    return s;
}

static void mp_print_spec (MP mp, mp_knot cur_spec, mp_knot cur_pen, const char *s)
{
    mp_knot w;
    mp_knot p = cur_spec;
    mp_print_diagnostic(mp, "Envelope spec", s, 1);
    w = mp_pen_walk(mp, cur_pen, mp->spec_offset);
    mp_print_ln(mp);
    mp_print_two(mp, &(cur_spec->x_coord), &(cur_spec->y_coord));
    mp_print_str(mp, " % beginning with offset ");
    mp_print_two(mp, &(w->x_coord), &(w->y_coord));
    do {
        while (1) {
            mp_knot q = mp_next_knot(p);
            mp_print_nl(mp, " .. controls ");
            mp_print_two(mp, &(p->right_x), &(p->right_y));
            mp_print_str(mp, " and ");
            mp_print_two(mp, &(q->left_x), &(q->left_y));
            mp_print_nl(mp, " .. ");
            mp_print_two(mp, &(q->x_coord), &(q->y_coord));

            p = q;
            if ((p == cur_spec) || (mp_knot_info(p) != zero_off)) {
                break;
            }
        }
        if (mp_knot_info(p) != zero_off) {
            w = mp_pen_walk (mp, w, (mp_knot_info(p) - zero_off));
            mp_print_str(mp, " % ");
            if (mp_knot_info(p) > zero_off) {
                mp_print_str(mp, "counter");
            }
            mp_print_str(mp, "clockwise to offset ");
            mp_print_two(mp, &(w->x_coord), &(w->y_coord));
        }
    } while (p != cur_spec);
    mp_print_nl(mp, " & cycle");
    mp_end_diagnostic(mp, 1);
}

static mp_knot mp_make_envelope (MP mp, mp_knot c, mp_knot h, int linejoin, int linecap, mp_number *miterlimit)
{
    mp_knot p, q, r, q0;
    mp_knot w, w0;
    int k, k0;
    mp_number qx, qy;
    mp_number dxin, dyin, dxout, dyout;
    int join_type = 0;
    mp_number tmp;
    mp_number max_ht;
    int kk;
    mp_knot ww;

    new_number(max_ht);
    new_number(tmp);
    new_fraction(dxin);
    new_fraction(dyin);
    new_fraction(dxout);
    new_fraction(dyout);
    mp->spec_p1 = NULL;
    mp->spec_p2 = NULL;
    new_number(qx);
    new_number(qy);
    if (mp_left_type(c) == mp_endpoint_knot) {
        mp->spec_p1 = mp_htap_ypoc(mp, c);
        mp->spec_p2 = mp->path_tail;
        mp_originator(mp->spec_p1) = mp_program_code;
        mp_knotstate(mp->spec_p1) = mp_regular_knot;
        mp_prev_knot(mp->spec_p1) = mp_next_knot(mp->spec_p2);
        mp_next_knot(mp->spec_p2) = mp_next_knot(mp->spec_p1);
        mp_prev_knot(c) = mp->spec_p1;
        mp_next_knot(mp->spec_p1) = c;
        mp_remove_cubic(mp, mp->spec_p1);
        c = mp->spec_p1;
        if (c != mp_next_knot(c)) {
            mp_originator(mp->spec_p2) = mp_program_code;
            mp_knotstate(mp->spec_p2) = mp_regular_knot;
            mp_remove_cubic(mp, mp->spec_p2);
        } else {
            mp_left_type(c) = mp_explicit_knot;
            mp_right_type(c) = mp_explicit_knot;
            number_clone(c->left_x, c->x_coord);
            number_clone(c->left_y, c->y_coord);
            number_clone(c->right_x, c->x_coord);
            number_clone(c->right_y, c->y_coord);
        }
    }
    c = mp_offset_prep (mp, c, h);
    if (number_positive(internal_value(mp_tracing_specs_internal))) {
        mp_print_spec(mp, c, h, "");
    }
    h = mp_pen_walk (mp, h, mp->spec_offset);

    w = h;
    p = c;
    do {
        q = mp_next_knot(p);
        q0 = q;
        number_clone(qx, q->x_coord);
        number_clone(qy, q->y_coord);
        k = mp_knot_info(q);
        k0 = k;
        w0 = w;
        if (k != zero_off) {
            if (k < zero_off) {
                join_type = 2;
            } else {
                if ((q != mp->spec_p1) && (q != mp->spec_p2)) {
                    join_type = linejoin;
                } else if (linecap == mp_squared_linecap_code) {
                    join_type = 3;
                } else {
                    join_type = 2 - linecap;
                }
                if ((join_type == 0) || (join_type == 3)) {
                    set_number_from_subtraction(dxin, q->x_coord, q->left_x);
                    set_number_from_subtraction(dyin, q->y_coord, q->left_y);
                    if (number_zero(dxin) && number_zero(dyin)) {
                        set_number_from_subtraction(dxin, q->x_coord, p->right_x);
                        set_number_from_subtraction(dyin, q->y_coord, p->right_y);
                        if (number_zero(dxin) && number_zero(dyin)) {
                            set_number_from_subtraction(dxin, q->x_coord, p->x_coord);
                            set_number_from_subtraction(dyin, q->y_coord, p->y_coord);
                            if (p != c) {
                                number_add(dxin, w->x_coord);
                                number_add(dyin, w->y_coord);
                            }
                        }
                    }
                    pyth_add(tmp, dxin, dyin);
                    if (number_zero(tmp)) {
                        join_type = 2;
                    } else {
                        mp_number r1;
                        new_fraction(r1);
                        make_fraction(r1, dxin, tmp);
                        number_clone(dxin, r1);
                        make_fraction(r1, dyin, tmp);
                        number_clone(dyin, r1);
                        free_number(r1);
                        set_number_from_subtraction(dxout, q->right_x, q->x_coord);
                        set_number_from_subtraction(dyout, q->right_y, q->y_coord);
                        if (number_zero(dxout) && number_zero(dyout)) {
                            r = mp_next_knot(q);
                            set_number_from_subtraction(dxout, r->left_x, q->x_coord);
                            set_number_from_subtraction(dyout, r->left_y, q->y_coord);
                            if (number_zero(dxout) && number_zero(dyout)) {
                                set_number_from_subtraction(dxout, r->x_coord, q->x_coord);
                                set_number_from_subtraction(dyout, r->y_coord, q->y_coord);
                            }
                        }
                        if (q == c) {
                            number_subtract(dxout, h->x_coord);
                            number_subtract(dyout, h->y_coord);
                        }
                        pyth_add(tmp, dxout, dyout);
                        if (number_zero(tmp)) {
                        } else {
                            mp_number r1;
                            new_fraction(r1);
                            make_fraction(r1, dxout, tmp);
                            number_clone(dxout, r1);
                            make_fraction(r1, dyout, tmp);
                            number_clone(dyout, r1);
                            free_number(r1);
                        }
                    }
                    if (join_type == 0) {
                        mp_number r1, r2;
                        new_fraction(r1);
                        new_fraction(r2);
                        take_fraction(r1, dxin, dxout);
                        take_fraction(r2, dyin, dyout);
                        number_add(r1, r2);
                        number_half(r1);
                        number_add(r1, fraction_half_t);
                        take_fraction(tmp, *miterlimit, r1);
                        if (number_less(tmp, unity_t)) {
                            mp_number ret;
                            new_number(ret);
                            take_scaled(ret, *miterlimit, tmp);
                            if (number_less(ret, unity_t)) {
                                join_type = 2;
                            }
                            free_number(ret);
                        }
                        free_number(r1);
                        free_number(r2);
                    }
                }
            }
        }
        number_add(p->right_x, w->x_coord);
        number_add(p->right_y, w->y_coord);
        number_add(q->left_x,  w->x_coord);
        number_add(q->left_y,  w->y_coord);
        number_add(q->x_coord, w->x_coord);
        number_add(q->y_coord, w->y_coord);
        mp_left_type(q) = mp_explicit_knot;
        mp_right_type(q) = mp_explicit_knot;

        while (k != zero_off) {
            if (k > zero_off) {
                w = mp_next_knot(w);
                --k;
            } else {
                w = mp_prev_knot(w);
                ++k;
            }
            if ((join_type == 1) || (k == zero_off)) {
                mp_number xtot, ytot;
                new_number(xtot);
                new_number(ytot);
                set_number_from_addition(xtot, qx, w->x_coord);
                set_number_from_addition(ytot, qy, w->y_coord);
                q = mp_insert_knot(mp, q, &xtot, &ytot);
                free_number(xtot);
                free_number(ytot);
            }
        }
        if (q != mp_next_knot(p)) {
            p = mp_next_knot(p);
            if ((join_type == 0) || (join_type == 3)) {
                if (join_type == 0) {
                    mp_number det;
                    mp_number absdet;
                    mp_number r1, r2;
                    new_fraction(r1);
                    new_fraction(r2);
                    new_fraction(det);
                    new_fraction(absdet);
                    take_fraction(r1, dyout, dxin);
                    take_fraction(r2, dxout, dyin);
                    set_number_from_subtraction(det, r1, r2);
                    number_abs_clone(absdet, det);
                    if (number_less(absdet, near_zero_angle_k)) {
                        r = NULL;
                    } else {
                        mp_number xtot, ytot, xsub, ysub;
                        new_fraction(xsub);
                        new_fraction(ysub);
                        new_number(xtot);
                        new_number(ytot);
                        set_number_from_subtraction(tmp, q->x_coord, p->x_coord);
                        take_fraction(r1, tmp, dyout);
                        set_number_from_subtraction(tmp, q->y_coord, p->y_coord);
                        take_fraction(r2, tmp, dxout);
                        set_number_from_subtraction(tmp, r1, r2);
                        make_fraction(r1, tmp, det);
                        number_clone(tmp, r1);
                        take_fraction(xsub, tmp, dxin);
                        take_fraction(ysub, tmp, dyin);
                        set_number_from_addition(xtot, p->x_coord, xsub);
                        set_number_from_addition(ytot, p->y_coord, ysub);
                        r = mp_insert_knot(mp, p, &xtot, &ytot);
                        free_number(xtot);
                        free_number(ytot);
                        free_number(xsub);
                        free_number(ysub);
                    }
                    free_number(r1);
                    free_number(r2);
                    free_number(det);
                    free_number(absdet);
                } else {
                    mp_number ht_x, ht_y;
                    mp_number ht_x_abs, ht_y_abs;
                    mp_number xtot, ytot, xsub, ysub;
                    new_fraction(xsub);
                    new_fraction(ysub);
                    new_number(xtot);
                    new_number(ytot);
                    new_fraction(ht_x);
                    new_fraction(ht_y);
                    new_fraction(ht_x_abs);
                    new_fraction(ht_y_abs);
                    set_number_from_subtraction(ht_x, w->y_coord, w0->y_coord);
                    set_number_from_subtraction(ht_y, w0->x_coord, w->x_coord);
                    number_abs_clone(ht_x_abs, ht_x);
                    number_abs_clone(ht_y_abs, ht_y);
                    while (number_less(ht_x_abs, fraction_half_t) && number_less(ht_y_abs, fraction_half_t)) {
                        number_double(ht_x);
                        number_double(ht_y);
                        number_abs_clone(ht_x_abs, ht_x);
                        number_abs_clone(ht_y_abs, ht_y);
                    }
                    set_number_to_zero(max_ht);
                    kk = zero_off;
                    ww = w;
                    while (1) {
                        if (kk > k0) {
                            ww = mp_next_knot(ww);
                            --kk;
                        } else {
                            ww = mp_prev_knot(ww);
                            ++kk;
                        }
                        if (kk == k0) {
                            break;
                        } else {
                            mp_number r1, r2;
                            new_fraction(r1);
                            new_fraction(r2);
                            set_number_from_subtraction(tmp, ww->x_coord, w0->x_coord);
                            take_fraction(r1, tmp, ht_x);
                            set_number_from_subtraction(tmp, ww->y_coord, w0->y_coord);
                            take_fraction(r2, tmp, ht_y);
                            set_number_from_addition(tmp, r1,    r2);
                            free_number(r1);
                            free_number(r2);
                            if (number_greater(tmp, max_ht)) {
                                number_clone(max_ht, tmp);
                            }
                        }
                    }
                    {
                        mp_number    r1 ,r2;
                        new_fraction(r1);
                        new_fraction(r2);
                        take_fraction(r1, dxin, ht_x);
                        take_fraction(r2, dyin, ht_y);
                        number_add(r1, r2);
                        make_fraction(tmp, max_ht, r1);
                        free_number(r1);
                        free_number(r2);
                    }
                    take_fraction(xsub, tmp, dxin);
                    take_fraction(ysub, tmp, dyin);
                    set_number_from_addition(xtot, p->x_coord, xsub);
                    set_number_from_addition(ytot, p->y_coord, ysub);
                    r = mp_insert_knot(mp, p, &xtot, &ytot);
                    {
                        mp_number r1 ,r2;
                        new_fraction(r1);
                        new_fraction(r2);
                        take_fraction(r1, dxout, ht_x);
                        take_fraction(r2, dyout, ht_y);
                        number_add(r1, r2);
                        make_fraction(tmp, max_ht, r1);
                        free_number(r1);
                        free_number(r2);
                    }
                    take_fraction(xsub, tmp, dxout);
                    take_fraction(ysub, tmp, dyout);
                    set_number_from_addition(xtot, q->x_coord, xsub);
                    set_number_from_addition(ytot, q->y_coord, ysub);
                    r = mp_insert_knot(mp, r, &xtot, &ytot);
                    free_number(xsub);
                    free_number(ysub);
                    free_number(xtot);
                    free_number(ytot);
                    free_number(ht_x);
                    free_number(ht_y);
                    free_number(ht_x_abs);
                    free_number(ht_y_abs);
                }
                if (r != NULL) {
                    number_clone(r->right_x, r->x_coord);
                    number_clone(r->right_y, r->y_coord);
                }
            }
        }
        p = q;
    } while (q0 != c);
    free_number(max_ht);
    free_number(tmp);
    free_number(qx);
    free_number(qy);
    free_number(dxin);
    free_number(dyin);
    free_number(dxout);
    free_number(dyout);
    return c;
}

mp_knot mp_insert_knot (MP mp, mp_knot q, mp_number *x, mp_number *y)
{
    mp_knot r = mp_new_knot(mp);
    mp_knot n = mp_next_knot(q);
    mp_next_knot(r) = n;
    mp_prev_knot(n) = r;
    mp_prev_knot(r) = q;
    mp_next_knot(q) = r;
    number_clone(r->right_x, q->right_x);
    number_clone(r->right_y, q->right_y);
    number_clone(r->x_coord, *x);
    number_clone(r->y_coord, *y);
    number_clone(q->right_x, q->x_coord);
    number_clone(q->right_y, q->y_coord);
    number_clone(r->left_x, r->x_coord);
    number_clone(r->left_y, r->y_coord);
    mp_left_type(r) = mp_explicit_knot;
    mp_right_type(r) = mp_explicit_knot;
    mp_originator(r) = mp_program_code;
    mp_knotstate(r) = mp_regular_knot;
    return r;
}

static void mp_find_direction_time (MP mp, mp_number *ret, mp_number *x_orig, mp_number *y_orig, mp_knot h)
{
    mp_number max;
    mp_knot p, q;
    mp_number n;
    mp_number tt;
    mp_number abs_x, abs_y;
    mp_number x1, x2, x3, y1, y2, y3;
    mp_number phi;
    mp_number t;
    mp_number x, y;
    new_number(max);
    new_number(x1);
    new_number(x2);
    new_number(x3);
    new_number(y1);
    new_number(y2);
    new_number(y3);
    new_fraction(t);
    new_angle(phi);
    set_number_to_zero(*ret);
    new_number(x);
    new_number(y);
    new_number(abs_x);
    new_number(abs_y);
    new_number(n);
    new_fraction(tt);
    number_clone(x, *x_orig);
    number_clone(y, *y_orig);
    number_abs_clone(abs_x, *x_orig);
    number_abs_clone(abs_y, *y_orig);
    if (number_less(abs_x, abs_y)) {
        mp_number r1;
        new_fraction(r1);
        make_fraction(r1, x, abs_y);
        number_clone(x, r1);
        free_number(r1);
        if (number_positive(y)) {
            number_clone(y, fraction_one_t);
        } else {
            number_negated_clone(y, fraction_one_t);
        }
    } else if (number_zero(x)) {
        goto FREE;
    } else {
        mp_number r1;
        new_fraction(r1);
        make_fraction(r1, y, abs_x);
        number_clone(y, r1);
        free_number(r1);
        if (number_positive(x)) {
            number_clone(x, fraction_one_t);
        } else {
            number_negated_clone(x, fraction_one_t);
        }
    }
    p = h;
    while (1) {
        if (mp_right_type(p) == mp_endpoint_knot) {
            break;
        } else {
            q = mp_next_knot(p);
            set_number_to_zero(tt);
            {
                mp_number absval;
                new_number(absval);
                set_number_from_subtraction(x1, p->right_x, p->x_coord);
                set_number_from_subtraction(x2, q->left_x,  p->right_x);
                set_number_from_subtraction(x3, q->x_coord, q->left_x);
                set_number_from_subtraction(y1, p->right_y, p->y_coord);
                set_number_from_subtraction(y2, q->left_y,  p->right_y);
                set_number_from_subtraction(y3, q->y_coord, q->left_y);
                number_abs_clone(absval, x2);
                number_abs_clone(max, x1);
                if (number_greater(absval, max)) {
                    number_clone(max, absval);
                }
                number_abs_clone(absval, x3);
                if (number_greater(absval, max)) {
                    number_clone(max, absval);
                }
                number_abs_clone(absval, y1);
                if (number_greater(absval, max)) {
                    number_clone(max, absval);
                }
                number_abs_clone(absval, y2);
                if (number_greater(absval, max)) {
                    number_clone(max, absval);
                }
                number_abs_clone(absval, y3);
                if (number_greater(absval, max)) {
                    number_clone(max, absval);
                }
                free_number(absval);
                if (number_zero(max)) {
                    goto FOUND;
                }
                while (number_less(max, fraction_half_t)) {
                    number_double(max);
                    number_double(x1);
                    number_double(x2);
                    number_double(x3);
                    number_double(y1);
                    number_double(y2);
                    number_double(y3);
                }
                number_clone(t, x1);
                {
                     mp_number r1, r2;
                     new_fraction(r1);
                     new_fraction(r2);
                     take_fraction(r1, x1, x);
                     take_fraction(r2, y1, y);
                     set_number_from_addition(x1, r1, r2);
                     take_fraction(r1, y1, x);
                     take_fraction(r2, t, y);
                     set_number_from_subtraction(y1, r1, r2);
                     number_clone(t, x2);
                     take_fraction(r1, x2, x);
                     take_fraction(r2, y2, y);
                     set_number_from_addition(x2, r1, r2);
                     take_fraction(r1, y2, x);
                     take_fraction(r2, t, y);
                     set_number_from_subtraction(y2, r1, r2);
                     number_clone(t, x3);
                     take_fraction(r1, x3 ,x);
                     take_fraction(r2, y3, y);
                     set_number_from_addition(x3, r1, r2);
                     take_fraction(r1, y3, x);
                     take_fraction(r2, t, y);
                     set_number_from_subtraction(y3, r1, r2);
                     free_number(r1);
                     free_number(r2);
                }
            }
            if (number_zero(y1) && (number_zero(x1) || number_positive(x1))) {
                goto FOUND;
            }
            if (number_positive(n)) {
                mp_number theta;
                mp_number tmp;
                new_angle(theta);
                n_arg(theta, x1, y1);
                new_angle(tmp);
                set_number_from_subtraction(tmp, theta, one_eighty_deg_t);
                if (number_nonnegative(theta) && number_nonpositive(phi) && number_greaterequal(phi, tmp)) {
                    free_number(tmp);
                    free_number(theta);
                    goto FOUND;
                }
                set_number_from_addition(tmp, theta, one_eighty_deg_t);
                if (number_nonpositive(theta) && number_nonnegative(phi) && number_lessequal(phi, tmp)) {
                    free_number(tmp);
                    free_number(theta);
                    goto FOUND;
                }
                free_number(tmp);
                free_number(theta);
                if (p == h) {
                    break;
                }
            }
            if (number_nonzero(x3) || number_nonzero(y3)) {
                n_arg(phi, x3, y3);
            }
            if (number_negative(x1) && number_negative(x2) && number_negative(x3)) {
                goto DONE;
            }
            {
                if (ab_vs_cd(y1, y3, y2, y2) == 0) {
                    {
                        if (ab_vs_cd(y1, y2, zero_t, zero_t) < 0) {
                            mp_number tmp, arg2;
                            new_number(tmp);
                            new_number(arg2);
                            set_number_from_subtraction(arg2, y1, y2);
                            make_fraction(t, y1, arg2);
                            free_number(arg2);
                            set_number_from_of_the_way(x1, t, x1, x2);
                            set_number_from_of_the_way(x2, t, x2, x3);
                            set_number_from_of_the_way(tmp, t, x1, x2);
                            if (number_zero(tmp) || number_positive(tmp)) {
                                free_number(tmp);
                                number_clone(tt, t);
                                fraction_to_round_scaled(tt);
                                goto FOUND;
                            } else {
                                free_number(tmp);
                            }
                        } else if (number_zero(y3)) {
                            if (number_zero(y1)) {
                                {
                                    mp_number arg1, arg2, arg3;
                                    new_number(arg1);
                                    new_number(arg2);
                                    new_number(arg3);
                                    number_negated_clone(arg1, x1);
                                    number_negated_clone(arg2, x2);
                                    number_negated_clone(arg3, x3);
                                    crossing_point(t, arg1, arg2, arg3);
                                    free_number(arg1);
                                    free_number(arg2);
                                    free_number(arg3);
                                    if (number_lessequal(t, fraction_one_t)) {
                                        number_clone(tt, t);
                                        fraction_to_round_scaled(tt);
                                        goto FOUND;
                                    } else if (ab_vs_cd(x1, x3, x2, x2) <= 0) {
                                        mp_number arg2;
                                        new_number(arg2);
                                        set_number_from_subtraction(arg2, x1, x2);
                                        make_fraction(t, x1, arg2);
                                        free_number(arg2);
                                        number_clone(tt, t);
                                        fraction_to_round_scaled(tt);
                                        goto FOUND;
                                    }
                                }
                            } else if (number_zero(x3) || number_positive(x3)) {
                                set_number_to_unity(tt);
                                goto FOUND;
                            }
                        }
                        goto DONE;
                    }
                }
            }
            if (number_zero(y1) || number_negative(y1)) {
                if (number_negative(y1)) {
                    number_negate(y1);
                    number_negate(y2);
                    number_negate(y3);
                } else if (number_positive(y2)) {
                    number_negate(y2);
                    number_negate(y3);
                }
            }
            crossing_point(t, y1, y2, y3);
            if (number_greater(t, fraction_one_t)) {
                goto DONE;
            }
            set_number_from_of_the_way(y2, t, y2, y3);
            set_number_from_of_the_way(x1, t, x1, x2);
            set_number_from_of_the_way(x2, t, x2, x3);
            set_number_from_of_the_way(x1, t, x1, x2);
            if (number_zero(x1) || number_positive(x1)) {
                number_clone(tt, t);
                fraction_to_round_scaled(tt);
                goto FOUND;
            }
            if (number_positive(y2)) {
                set_number_to_zero(y2);
            }
            number_clone(tt, t);
            {
                mp_number arg1, arg2, arg3;
                new_number(arg1);
                new_number(arg2);
                new_number(arg3);
                number_negated_clone(arg2, y2);
                number_negated_clone(arg3, y3);
                crossing_point(t, arg1, arg2, arg3);
                free_number(arg1);
                free_number(arg2);
                free_number(arg3);
            }
            if (number_greater(t, fraction_one_t)) {
                goto DONE;
            } else {
                mp_number tmp;
                new_number(tmp);
                set_number_from_of_the_way(x1, t, x1, x2);
                set_number_from_of_the_way(x2, t, x2, x3);
                set_number_from_of_the_way(tmp, t, x1, x2);
                if (number_nonnegative(tmp)) {
                    free_number(tmp);
                    set_number_from_of_the_way(t, t, tt, fraction_one_t);
                    number_clone(tt, t);
                    fraction_to_round_scaled(tt);
                    goto FOUND;
                }
                free_number(tmp);
            }
            DONE:
            p = q;
            number_add(n, unity_t);
        }
    }
    set_number_to_unity(*ret);
    number_negate(*ret);
    goto FREE;
  FOUND:
    set_number_from_addition(*ret, n, tt);
    goto FREE;
  FREE:
    free_number(x);
    free_number(y);
    free_number(abs_x);
    free_number(abs_y);
    free_number(x1);
    free_number(x2);
    free_number(x3);
    free_number(y1);
    free_number(y2);
    free_number(y3);
    free_number(t);
    free_number(phi);
    free_number(n);
    free_number(max);
    free_number(tt);
}

void mp_set_min_max (MP mp, int v)
{
    if (number_negative(stack_1(v))) {
        if (number_nonnegative (stack_3(v))) {
            if (number_negative(stack_2(v))) {
                set_number_from_addition(stack_min(v), stack_1(v), stack_2(v));
            } else {
                number_clone(stack_min(v), stack_1(v));
            }
            set_number_from_addition(stack_max(v), stack_1(v), stack_2(v));
            number_add(stack_max(v), stack_3(v));
            if (number_negative(stack_max(v))) {
                set_number_to_zero(stack_max(v));
            }
        } else {
            set_number_from_addition(stack_min(v), stack_1(v), stack_2(v));
            number_add(stack_min(v), stack_3(v));
            if (number_greater(stack_min(v), stack_1(v))) {
                number_clone(stack_min(v), stack_1(v));
            }
            set_number_from_addition(stack_max(v), stack_1(v), stack_2(v));
            if (number_negative(stack_max(v))) {
                set_number_to_zero(stack_max(v));
            }
        }
    } else if (number_nonpositive(stack_3(v))) {
        if (number_positive(stack_2(v))) {
            set_number_from_addition(stack_max(v), stack_1(v), stack_2(v));
        } else {
            number_clone(stack_max(v), stack_1(v));
        }
        set_number_from_addition(stack_min(v), stack_1(v), stack_2(v));
        number_add(stack_min(v), stack_3(v));
        if (number_positive(stack_min(v))) {
            set_number_to_zero(stack_min(v));
        }
    } else  {
        set_number_from_addition(stack_max(v), stack_1(v), stack_2(v));
        number_add(stack_max(v), stack_3(v));
        if (number_less(stack_max(v), stack_1(v))) {
            number_clone(stack_max(v), stack_1(v));
        }
        set_number_from_addition(stack_min(v), stack_1(v), stack_2(v));
        if (number_positive(stack_min(v))) {
            set_number_to_zero(stack_min(v));
        }
    }
}

static int mp_cubic_intersection(MP mp, mp_knot p, mp_knot pp, int run)
{
    mp_knot q, qq;
    mp_number x_two_t;
    mp_number x_two_t_low_precision;
    mp->time_to_go = max_patience;
    set_number_from_scaled(mp->max_t, 2);
    new_number_clone(x_two_t, two_t);
    new_number(x_two_t_low_precision);
    number_double(x_two_t);
    number_double(x_two_t);
    set_number_from_double(x_two_t_low_precision, -0.5);
    number_add(x_two_t_low_precision, x_two_t);
    q = mp_next_knot(p);
    qq = mp_next_knot(pp);
    mp->bisect_ptr = int_packets;
    set_number_from_subtraction(u1r, p->right_x, p->x_coord);
    set_number_from_subtraction(u2r, q->left_x, p->right_x);
    set_number_from_subtraction(u3r, q->x_coord, q->left_x);
    mp_set_min_max(mp, ur_packet);
    set_number_from_subtraction(v1r, p->right_y, p->y_coord);
    set_number_from_subtraction(v2r, q->left_y, p->right_y);
    set_number_from_subtraction(v3r, q->y_coord, q->left_y);
    mp_set_min_max(mp, vr_packet);
    set_number_from_subtraction(x1r, pp->right_x, pp->x_coord);
    set_number_from_subtraction(x2r, qq->left_x, pp->right_x);
    set_number_from_subtraction(x3r, qq->x_coord, qq->left_x);
    mp_set_min_max(mp, xr_packet);
    set_number_from_subtraction(y1r, pp->right_y, pp->y_coord);
    set_number_from_subtraction(y2r, qq->left_y, pp->right_y);
    set_number_from_subtraction(y3r, qq->y_coord, qq->left_y);
    mp_set_min_max(mp, yr_packet);
    set_number_from_subtraction(mp->delx, p->x_coord, pp->x_coord);
    set_number_from_subtraction(mp->dely, p->y_coord, pp->y_coord);
    mp->tol = 0;
    mp->uv = r_packets;
    mp->xy = r_packets;
    mp->three_l = 0;
    set_number_from_scaled(mp->cur_t, 1);
    set_number_from_scaled(mp->cur_tt, 1);

  CONTINUE:
    while (1) {
        if (((x_packet (mp->xy))+4)>bistack_size
         || ((u_packet (mp->uv))+4)>bistack_size
         || ((y_packet (mp->xy))+4)>bistack_size
         || ((v_packet (mp->uv))+4)>bistack_size){
            set_number_from_scaled(mp->cur_t,1);
            set_number_from_scaled(mp->cur_tt,1);
            goto NOT_FOUND;
        }
        if (number_greater(mp->max_t, x_two_t)){
            set_number_from_scaled(mp->cur_t,1);
            set_number_from_scaled(mp->cur_tt,1);
            goto NOT_FOUND;
        }
        if (number_to_scaled(mp->delx) - mp->tol <= number_to_scaled(stack_max (x_packet (mp->xy))) - number_to_scaled(stack_min (u_packet (mp->uv)))) {
            if (number_to_scaled(mp->delx) + mp->tol >= number_to_scaled(stack_min (x_packet (mp->xy))) - number_to_scaled(stack_max (u_packet (mp->uv)))) {
                if (number_to_scaled(mp->dely) - mp->tol <= number_to_scaled(stack_max (y_packet (mp->xy))) - number_to_scaled(stack_min (v_packet (mp->uv)))) {
                    if (number_to_scaled(mp->dely) + mp->tol >= number_to_scaled(stack_min (y_packet (mp->xy))) - number_to_scaled(stack_max (v_packet (mp->uv)))) {
                        if (number_to_scaled(mp->cur_t) >= number_to_scaled(mp->max_t)) {
                            if (number_equal(mp->max_t, x_two_t) || number_greater(mp->max_t, x_two_t_low_precision)) {
                                if (run == 1) {
                                    number_divide_int(mp->cur_t,1<<2);
                                    number_divide_int(mp->cur_tt,1<<2);
                                    set_number_from_scaled(mp->cur_t, ((number_to_scaled(mp->cur_t) + 1)/2));
                                    set_number_from_scaled(mp->cur_tt, ((number_to_scaled(mp->cur_tt) + 1)/2));
free_number(x_two_t);
free_number(x_two_t_low_precision);
                                    return 1;
                                } else {
                                    run--;
                                    goto NOT_FOUND;
                                }
                            }
                            number_double(mp->max_t);
                            number_clone(mp->appr_t, mp->cur_t);
                            number_clone(mp->appr_tt, mp->cur_tt);
                        }
                        number_clone(stack_dx, mp->delx);
                        number_clone(stack_dy, mp->dely);
                        set_number_from_scaled(stack_tol, mp->tol);
                        set_number_from_scaled(stack_uv, mp->uv);
                        set_number_from_scaled(stack_xy, mp->xy);
                        mp->bisect_ptr = mp->bisect_ptr + int_increment;
                        number_double(mp->cur_t);
                        number_double(mp->cur_tt);
                        number_clone(u1l, stack_1(u_packet (mp->uv)));
                        number_clone(u3r, stack_3(u_packet (mp->uv)));
                        set_number_half_from_addition(u2l, u1l, stack_2(u_packet(mp->uv)));
                        set_number_half_from_addition(u2r, u3r, stack_2(u_packet(mp->uv)));
                        set_number_half_from_addition(u3l, u2l, u2r);
                        number_clone(u1r, u3l);
                        mp_set_min_max(mp, ul_packet);
                        mp_set_min_max(mp, ur_packet);
                        number_clone(v1l, stack_1(v_packet (mp->uv)));
                        number_clone(v3r, stack_3(v_packet (mp->uv)));
                        set_number_half_from_addition(v2l, v1l, stack_2(v_packet(mp->uv)));
                        set_number_half_from_addition(v2r, v3r, stack_2(v_packet(mp->uv)));
                        set_number_half_from_addition(v3l, v2l, v2r);
                        number_clone(v1r, v3l);
                        mp_set_min_max(mp, vl_packet);
                        mp_set_min_max(mp, vr_packet);
                        number_clone(x1l, stack_1(x_packet (mp->xy)));
                        number_clone(x3r, stack_3(x_packet (mp->xy)));
                        set_number_half_from_addition(x2l, x1l, stack_2(x_packet(mp->xy)));
                        set_number_half_from_addition(x2r, x3r, stack_2(x_packet(mp->xy)));
                        set_number_half_from_addition(x3l, x2l, x2r);
                        number_clone(x1r, x3l);
                        mp_set_min_max(mp, xl_packet);
                        mp_set_min_max(mp, xr_packet);
                        number_clone(y1l, stack_1(y_packet (mp->xy)));
                        number_clone(y3r, stack_3(y_packet (mp->xy)));
                        set_number_half_from_addition(y2l, y1l, stack_2(y_packet(mp->xy)));
                        set_number_half_from_addition(y2r, y3r, stack_2(y_packet(mp->xy)));
                        set_number_half_from_addition(y3l, y2l, y2r);
                        number_clone(y1r, y3l);
                        mp_set_min_max(mp, yl_packet);
                        mp_set_min_max(mp, yr_packet);
                        mp->uv = l_packets;
                        mp->xy = l_packets;
                        number_double(mp->delx);
                        number_double(mp->dely);
                        mp->tol = mp->tol - mp->three_l + (int) mp->tol_step;
                        mp->tol += mp->tol;
                        mp->three_l = mp->three_l + (int) mp->tol_step;

                        goto CONTINUE;
                    }
                }
            }
        }
        if (mp->time_to_go > 0) {
            --mp->time_to_go;
        } else {
            number_divide_int(mp->appr_t, 1<<2);
            number_divide_int(mp->appr_tt, 1<<2);
            while (number_less(mp->appr_t, unity_t)) {
                number_double(mp->appr_t);
                number_double(mp->appr_tt);
            }
            number_clone(mp->cur_t, mp->appr_t);
            number_clone(mp->cur_tt, mp->appr_tt);
free_number(x_two_t);
free_number(x_two_t_low_precision);
            return 2;
        }
      NOT_FOUND:
        if (odd(number_to_scaled(mp->cur_tt))) {

            if (odd(number_to_scaled(mp->cur_t))) {

                set_number_from_scaled(mp->cur_t, half (number_to_scaled(mp->cur_t)));
                set_number_from_scaled(mp->cur_tt, half (number_to_scaled(mp->cur_tt)));
                if (number_to_scaled(mp->cur_t) == 0) {
free_number(x_two_t);
free_number(x_two_t_low_precision);
                    return 3;
                } else {
                    mp->bisect_ptr -= int_increment;
                    mp->three_l -= (int) mp->tol_step;
                    number_clone(mp->delx, stack_dx);
                    number_clone(mp->dely, stack_dy);
                    mp->tol = number_to_scaled(stack_tol);
                    mp->uv = number_to_scaled(stack_uv);
                    mp->xy = number_to_scaled(stack_xy);
                    goto NOT_FOUND;
                }
            } else {
                set_number_from_scaled(mp->cur_t, number_to_scaled(mp->cur_t) + 1);
                number_add(mp->delx, stack_1(u_packet (mp->uv)));
                number_add(mp->delx, stack_2(u_packet (mp->uv)));
                number_add(mp->delx, stack_3(u_packet (mp->uv)));
                number_add(mp->dely, stack_1(v_packet (mp->uv)));
                number_add(mp->dely, stack_2(v_packet (mp->uv)));
                number_add(mp->dely, stack_3(v_packet (mp->uv)));
                mp->uv = mp->uv + int_packets;
                set_number_from_scaled(mp->cur_tt, number_to_scaled(mp->cur_tt) - 1);
                mp->xy = mp->xy - int_packets;
                number_add(mp->delx, stack_1(x_packet (mp->xy)));
                number_add(mp->delx, stack_2(x_packet (mp->xy)));
                number_add(mp->delx, stack_3(x_packet (mp->xy)));
                number_add(mp->dely, stack_1(y_packet (mp->xy)));
                number_add(mp->dely, stack_2(y_packet (mp->xy)));
                number_add(mp->dely, stack_3(y_packet (mp->xy)));
            }
        } else {
            set_number_from_scaled(mp->cur_tt, number_to_scaled(mp->cur_tt) + 1);
            mp->tol = mp->tol + mp->three_l;
            number_subtract(mp->delx, stack_1(x_packet (mp->xy)));
            number_subtract(mp->delx, stack_2(x_packet (mp->xy)));
            number_subtract(mp->delx, stack_3(x_packet (mp->xy)));
            number_subtract(mp->dely, stack_1(y_packet (mp->xy)));
            number_subtract(mp->dely, stack_2(y_packet (mp->xy)));
            number_subtract(mp->dely, stack_3(y_packet (mp->xy)));
            mp->xy = mp->xy + int_packets;
        }
    }
free_number(x_two_t);
free_number(x_two_t_low_precision);
}

static mp_knot mp_path_intersection_add(MP mp, mp_knot list, mp_knot *last, mp_number *t, mp_number *tt)
{
    int a = number_to_scaled(*t) >> intersection_run_shift;
    int aa = number_to_scaled(*tt) >> intersection_run_shift;
    int b =  (list ? number_to_scaled((*last)->x_coord) : -1) >> intersection_run_shift ;
    int bb = (list ? number_to_scaled((*last)->y_coord) : -1) >> intersection_run_shift ;
    if (a == b && aa == bb) {
    } else {
        mp_knot k = mp_new_knot(mp);
        mp_left_type(k) = mp_explicit_knot;
        mp_right_type(k) = mp_explicit_knot;
        number_clone(k->x_coord, *t);
        number_clone(k->y_coord, *tt);
        if (list) {
            mp_prev_knot(k) = *last;
            mp_next_knot(*last) = k;
            mp_prev_knot(list) = k;
            mp_next_knot(k) = list;
        } else {
            list = k;
            mp_prev_knot(k) = k;
            mp_next_knot(k) = k;
        }
        *last = k;
    }
    return list;
}

static mp_knot mp_path_intersection(MP mp, mp_knot h, mp_knot hh, int path, mp_knot *last)
{
    mp_number n, nn;
    int done = 0;
    mp_knot list = NULL;
    mp_knot l = NULL;
    mp_knot ll = NULL;
    if (last) {
        *last = NULL;
    }
    if (mp_right_type(h) == mp_endpoint_knot) {
        number_clone(h->right_x, h->x_coord);
        number_clone(h->left_x, h->x_coord);
        number_clone(h->right_y, h->y_coord);
        number_clone(h->left_y, h->y_coord);
        mp_right_type(h) = mp_explicit_knot;
    }
    if (mp_right_type(hh) == mp_endpoint_knot) {
        number_clone(hh->right_x, hh->x_coord);
        number_clone(hh->left_x, hh->x_coord);
        number_clone(hh->right_y, hh->y_coord);
        number_clone(hh->left_y, hh->y_coord);
        mp_right_type(hh) = mp_explicit_knot;
    }
    new_number(n);
    new_number(nn);
    mp->tol_step = 0;
    do {
        mp_knot p, pp;
        int t = -1;
        int tt = -1;


        number_negated_clone(n, unity_t);
        p = h;
        do {
            if (mp_right_type(p) != mp_endpoint_knot) {


                number_negated_clone(nn, unity_t);
                pp = hh;
                do {
                    if (mp_right_type(pp) != mp_endpoint_knot) {
                        int run = 0;
                        int retrials = 0;
                      RETRY:
                        ++run;
                        mp_cubic_intersection(mp, p, pp, run);
                        if (number_positive(mp->cur_t)) {
                            number_add(mp->cur_t, n);
                            number_add(mp->cur_tt, nn);
                            done = 1;
                            if (path) {
                                list = mp_path_intersection_add(mp, list, last, &(mp->cur_t), &(mp->cur_tt));
                                if (t == number_to_scaled(mp->cur_t) && tt == number_to_scaled(mp->cur_tt)) {
                                    if (retrials == 8) {
                                        break;
                                    } else {
                                        retrials += 1;
                                        goto RETRY;
                                    }
                                } else {
                                    retrials = 0;
                                    t = number_to_scaled(mp->cur_t);
                                    tt = number_to_scaled(mp->cur_tt);
                                    goto RETRY;
                                }
                            } else {
                                goto DONE;
                            }
                        }
                    }
                    number_add(nn, unity_t);
                    ll = pp;
                    pp = mp_next_knot(pp);
if (pp != hh && mp_knotstate(pp) == mp_end_knot) {
    number_add(nn, unity_t);
    ll = pp;
    pp = mp_next_knot(pp);
}
                } while (pp != hh);
            }
            number_add(n, unity_t);
            l = p;
            p = mp_next_knot(p);
if (p != hh && mp_knotstate(p) == mp_end_knot) {
    number_add(n, unity_t);
    l = p;
    p = mp_next_knot(p);
}
        } while (p != h);
        mp->tol_step = mp->tol_step + 3;
        if (done) {
            goto DONE;
        }
    } while (mp->tol_step <= 3);
  DONE:
    if (path && l && ll && number_equal(l->x_coord, ll->x_coord) && number_equal(l->y_coord, ll->y_coord)) {
        list = mp_path_intersection_add(mp, list, last, &n, &nn);
    }
    if (! done) {
        number_negated_clone(mp->cur_t, unity_t);
        number_negated_clone(mp->cur_tt, unity_t);
        if (path && ! list) {
            mp_knot k = mp_new_knot(mp);
            number_clone(k->x_coord, mp->cur_t);
            number_clone(k->y_coord, mp->cur_tt);
            mp_prev_knot(k) = k;
            mp_next_knot(k) = k;
            list = k;
            if (last) {
                *last = k;
            }
        }
    }
    free_number(n);
    free_number(nn);
    return list;
}

static void mp_new_indep (MP mp, mp_node p)
{
    (void) mp;
    if (mp->serial_no >= max_integer) {
        mp_fatal_error(mp, "Variable instance identifiers exhausted");
    }
    p->type = mp_independent_type;
    mp->serial_no = mp->serial_no + 1;
    mp_set_indep_scale(p, 0);
    mp_set_indep_value(p, mp->serial_no);
}

inline static mp_node do_get_dep_info (MP mp, mp_value_node p)
{
    mp_node d;
    (void) mp;
    d = p->parent;
    return d;
}

inline static void do_set_dep_value (MP mp, mp_value_node p, mp_number *q)
{
    number_clone(p->data.n, *q);
    p->attr_head = NULL;
    p->subscr_head = NULL;
}

static mp_value_node mp_get_dep_node (MP mp)
{
    mp_value_node p = (mp_value_node) mp_new_value_node(mp);
    p->type = mp_dep_node_type;
    return p;
}

static void mp_free_dep_node (MP mp, mp_value_node p)
{
    mp_free_value_node(mp, (mp_node) p);
}

void mp_print_dependency (MP mp, mp_value_node p, int t)
{
    mp_number v;
    mp_node q;
    mp_value_node pp = p;
    new_number(v);
    while (1) {
        number_abs_clone(v, mp_get_dep_value(p));
        q = mp_get_dep_info(p);
        if (q == NULL) {
            if (number_nonzero(v) || (p == pp)) {
                if (number_positive(mp_get_dep_value(p)) && p != pp) {
                    mp_print_chr(mp, '+');
                }
                print_number(mp_get_dep_value(p));
            }
            return;
        }
        if (number_negative(mp_get_dep_value(p))) {
            mp_print_chr(mp, '-');
        } else if (p != pp) {
            mp_print_chr(mp, '+');
        }
        if (t == mp_dependent_type) {
            fraction_to_round_scaled(v);
        }
        if (! number_equal(v, unity_t)) {
            print_number(v);
        }
        if (q->type != mp_independent_type) {
            mp_confusion(mp, "dependency");
        } else {
            mp_print_variable_name(mp, q);
            set_number_from_scaled(v, mp_get_indep_scale(q));
            while (number_positive(v)) {
                mp_print_str(mp, "*4");
                number_add_scaled(v, -2);
            }
            p = (mp_value_node) p->link;
        }
    }
}

static void mp_max_coef (MP mp, mp_number *x, mp_value_node p)
{
    mp_number absv;
    new_number(absv);
    set_number_to_zero(*x);
    while (mp_get_dep_info(p) != NULL) {
        number_abs_clone(absv, mp_get_dep_value(p));
        if (number_greater(absv, *x)) {
            number_clone(*x, absv);
        }
        p = (mp_value_node) p->link;
    }
    free_number(absv);
}

static mp_value_node mp_p_plus_fq (MP mp,
    mp_value_node p, mp_number *f,
    mp_value_node q, mp_variable_type t,
    mp_variable_type tt
)
{
    mp_node pp, qq;
    mp_value_node r, s;
    mp_number threshold;
    mp_number half_threshold;
    mp_number v, vv;
    new_number(v);
    new_number(vv);
    if (t == mp_dependent_type) {
        new_number_clone(threshold, fraction_threshold_k);
        new_number_clone(half_threshold, half_fraction_threshold_k);
    } else {
        new_number_clone(threshold, scaled_threshold_k);
        new_number_clone(half_threshold, half_scaled_threshold_k);
    }
    r = (mp_value_node) mp->temp_head;
    pp = mp_get_dep_info(p);
    qq = mp_get_dep_info(q);
    while (1) {
        if (pp == qq) {
            if (pp == NULL) {
                break;
            } else {
                mp_number r1;
                mp_number absv;
                new_fraction(r1);
                new_number(absv);
                if (tt == mp_dependent_type) {
                    take_fraction(r1, *f, mp_get_dep_value(q));
                } else {
                    take_scaled(r1, *f, mp_get_dep_value(q));
                }
                set_number_from_addition(v, mp_get_dep_value(p), r1);
                free_number(r1);
                mp_set_dep_value(p, v);
                s = p;
                p = (mp_value_node) p->link;
                number_abs_clone(absv, v);
                if (number_less(absv, threshold)) {
                    mp_free_dep_node(mp, s);
                } else {
                    if (number_greaterequal(absv, coef_bound_k) && mp->watch_coefs) {
                        qq->type = independent_needing_fix;
                        mp->fix_needed = 1;
                    }
                    mp_set_link(r, s);
                    r = s;
                }
                free_number(absv);
                pp = mp_get_dep_info(p);
                q = (mp_value_node) q->link;
                qq = mp_get_dep_info(q);
            }
        } else {
            if (pp == NULL) {
                set_number_to_negative_inf(v);
            } else if (pp->type == mp_independent_type || (pp->type == independent_needing_fix && mp->fix_needed)) {
                set_number_from_scaled(v, mp_get_indep_value(pp));
            } else {
                number_clone(v, mp_get_value_number(pp));
            }
            if (qq == NULL) {
                set_number_to_negative_inf(vv);
            } else if (qq->type == mp_independent_type || (qq->type == independent_needing_fix && mp->fix_needed)) {
                set_number_from_scaled(vv, mp_get_indep_value(qq));
            } else {
                number_clone(vv, mp_get_value_number(qq));
            }
            if (number_less(v, vv)) {
                mp_number absv;
                {
                    mp_number r1;
                    mp_number arg1, arg2;
                    new_fraction(r1);
                    new_number_clone(arg1, *f);
                    new_number_clone(arg2, mp_get_dep_value(q));
                    if (tt == mp_dependent_type) {
                        take_fraction(r1, arg1, arg2);
                    } else {
                        take_scaled(r1, arg1, arg2);
                    }
                    number_clone(v, r1);
                    free_number(r1);
                    free_number(arg1);
                    free_number(arg2);
                }
                new_number_abs(absv, v);
                if (number_greater(absv, half_threshold)) {
                    s = mp_get_dep_node(mp);
                    mp_set_dep_info(s, qq);
                    mp_set_dep_value(s, v);
                    if (number_greaterequal(absv, coef_bound_k) && mp->watch_coefs) {
                        qq->type = independent_needing_fix;
                        mp->fix_needed = 1;
                    }
                    mp_set_link(r, s);
                    r = s;
                }
                q = (mp_value_node) q->link;
                qq = mp_get_dep_info(q);
                free_number(absv);
            } else {
                mp_set_link(r, p);
                r = p;
                p = (mp_value_node) p->link;
                pp = mp_get_dep_info(p);
            }
        }
    }
    {
        mp_number r1;
        mp_number arg1, arg2;
        new_fraction(r1);
        new_number(arg1);
        new_number(arg2);
        number_clone(arg1, mp_get_dep_value(q));
        number_clone(arg2, *f);
        if (t == mp_dependent_type) {
            take_fraction(r1, arg1, arg2);
        } else {
            take_scaled(r1, arg1, arg2);
        }
        slow_add(arg1, mp_get_dep_value(p), r1);
        mp_set_dep_value(p, arg1);
        free_number(r1);
        free_number(arg1);
        free_number(arg2);
    }
    mp_set_link(r, p);
    mp->dep_final = p;
    free_number(threshold);
    free_number(half_threshold);
    free_number(v);
    free_number(vv);
    return (mp_value_node) mp->temp_head->link;
}

static mp_value_node mp_p_plus_q (MP mp, mp_value_node p, mp_value_node q, mp_variable_type t)
{
    mp_node pp, qq;
    mp_value_node s;
    mp_value_node r;
    mp_number threshold;
    mp_number v, vv;
    new_number(v);
    new_number(vv);
    new_number(threshold);
    if (t == mp_dependent_type) {
        number_clone(threshold, fraction_threshold_k);
    } else {
        number_clone(threshold, scaled_threshold_k);
    }
    r = (mp_value_node) mp->temp_head;
    pp = mp_get_dep_info(p);
    qq = mp_get_dep_info(q);
    while (1) {
        if (pp == qq) {
            if (pp == NULL) {
                break;
            } else {
                mp_number test;
                new_number(test);
                set_number_from_addition(v, mp_get_dep_value(p), mp_get_dep_value(q));
                mp_set_dep_value(p, v);
                s = p;
                p = (mp_value_node) p->link;
                pp = mp_get_dep_info(p);
                number_abs_clone(test, v);
                if (number_less(test, threshold)) {
                    mp_free_dep_node(mp, s);
                } else {
                    if (number_greaterequal(test, coef_bound_k) && mp->watch_coefs) {
                        qq->type = independent_needing_fix;
                        mp->fix_needed = 1;
                    }
                    mp_set_link(r, s);
                    r = s;
                }
                free_number(test);
                q = (mp_value_node) q->link;
                qq = mp_get_dep_info(q);
            }
        } else {
            if (pp == NULL) {
                set_number_to_zero(v);
            } else if (pp->type == mp_independent_type || (pp->type == independent_needing_fix && mp->fix_needed)) {
                set_number_from_scaled(v, mp_get_indep_value(pp));
            } else {
                number_clone(v, mp_get_value_number(pp));
            }
            if (qq == NULL) {
                set_number_to_zero(vv);
            } else if (qq->type == mp_independent_type || (qq->type == independent_needing_fix && mp->fix_needed)) {
                set_number_from_scaled(vv, mp_get_indep_value(qq));
            } else {
                number_clone(vv, mp_get_value_number(qq));
            }
            if (number_less(v, vv)) {
                s = mp_get_dep_node(mp);
                mp_set_dep_info(s, qq);
                mp_set_dep_value(s, mp_get_dep_value(q));
                q = (mp_value_node) q->link;
                qq = mp_get_dep_info(q);
                mp_set_link(r, s);
                r = s;
            } else {
                mp_set_link(r, p);
                r = p;
                p = (mp_value_node) p->link;
                pp = mp_get_dep_info(p);
            }
        }
    }
    {
        mp_number r1;
        new_number(r1);
        slow_add(r1, mp_get_dep_value(p), mp_get_dep_value(q));
        mp_set_dep_value(p, r1);
        free_number(r1);
    }
    mp_set_link(r, p);
    mp->dep_final = p;
    free_number(v);
    free_number(vv);
    free_number(threshold);
    return (mp_value_node) mp->temp_head->link;
}

static mp_value_node mp_p_times_v (MP mp, mp_value_node p, mp_number *v, int t0, int t1, int v_is_scaled)
{
    mp_value_node r, s;
    mp_number w;
    mp_number threshold;
    int scaling_down = (t0 != t1) ? 1 : (! v_is_scaled);
    new_number(threshold);
    new_number(w);
    if (t1 == mp_dependent_type) {
        number_clone(threshold, half_fraction_threshold_k);
    } else {
        number_clone(threshold, half_scaled_threshold_k);
    }
    r = (mp_value_node) mp->temp_head;
    while (mp_get_dep_info(p) != NULL) {
        mp_number test;
        new_number(test);
        if (scaling_down) {
            take_fraction(w, *v, mp_get_dep_value(p));
        } else {
            take_scaled(w, *v, mp_get_dep_value(p));
        }
        number_abs_clone(test, w);
        if (number_lessequal(test, threshold)) {
            s = (mp_value_node) p->link;
            mp_free_dep_node(mp, p);
            p = s;
        } else {
            if (number_greaterequal(test, coef_bound_k)) {
                mp->fix_needed = 1;
                mp_get_dep_info(p)->type = independent_needing_fix;
            }
            mp_set_link(r, p);
            r = p;
            mp_set_dep_value(p, w);
            p = (mp_value_node) p->link;
        }
        free_number(test);
    }
    mp_set_link(r, p);
    {
        mp_number r1;
        new_number(r1);
        if (v_is_scaled) {
            take_scaled(r1, mp_get_dep_value(p), *v);
        } else {
            take_fraction(r1, mp_get_dep_value(p), *v);
        }
        mp_set_dep_value(p, r1);
        free_number(r1);
    }
    free_number(w);
    free_number(threshold);
    return (mp_value_node) mp->temp_head->link;
}

mp_value_node mp_p_over_v (MP mp, mp_value_node p, mp_number *v_orig, int t0, int t1)
{
    mp_value_node r, s;
    mp_number w;
    mp_number threshold;
    mp_number v;
    int scaling_down = (t0 != t1);
    new_number(w);
    new_number(threshold);
    new_number_clone(v, *v_orig);
    if (t1 == mp_dependent_type) {
        number_clone(threshold, half_fraction_threshold_k);
    } else {
        number_clone(threshold, half_scaled_threshold_k);
    }
    r = (mp_value_node) mp->temp_head;
    while (mp_get_dep_info(p) != NULL) {
        if (scaling_down) {
            mp_number x, absv;
            new_number_abs(absv, v);
            if (number_less(absv, p_over_v_threshold_k)) {
                new_number_clone(x, v);
                convert_scaled_to_fraction(x);
                make_scaled(w, mp_get_dep_value(p), x);
            } else {
                new_number_clone(x, mp_get_dep_value(p));
                fraction_to_round_scaled(x);
                make_scaled(w, x, v);
            }
            free_number(x);
            free_number(absv);
        } else {
            make_scaled(w, mp_get_dep_value(p), v);
        }
        {
            mp_number test;
            new_number(test);
            number_abs_clone(test, w);
            if (number_lessequal(test, threshold)) {
                s = (mp_value_node) p->link;
                mp_free_dep_node(mp, p);
                p = s;
            } else {
                if (number_greaterequal(test, coef_bound_k)) {
                    mp->fix_needed = 1;
                    mp_get_dep_info(p)->type = independent_needing_fix;
                }
                mp_set_link(r, p);
                r = p;
                mp_set_dep_value(p, w);
                p = (mp_value_node) p->link;
            }
            free_number(test);
        }
    }
    mp_set_link(r, p);
    {
        mp_number ret;
        new_number(ret);
        make_scaled(ret, mp_get_dep_value(p), v);
        mp_set_dep_value(p, ret);
        free_number(ret);
    }
    free_number(v);
    free_number(w);
    free_number(threshold);
    return (mp_value_node) mp->temp_head->link;
}

static mp_value_node mp_p_with_x_becoming_q (MP mp, mp_value_node p, mp_node x, mp_node q, int t)
{
    mp_value_node s = p;
    mp_value_node r = (mp_value_node) mp->temp_head;
    int sx = mp_get_indep_value(x);
    while (mp_get_dep_info(s) != NULL && mp_get_indep_value(mp_get_dep_info(s)) > sx) {
        r = s;
        s = (mp_value_node) s->link;
    }
    if (mp_get_dep_info(s) == NULL || mp_get_dep_info(s) != x) {
        return p;
    } else {
        mp_value_node ret;
        mp_number v1;
        mp_set_link(mp->temp_head, p);
        mp_set_link(r, s->link);
        new_number_clone(v1, mp_get_dep_value(s));
        mp_free_dep_node(mp, s);
        ret = mp_p_plus_fq(mp, (mp_value_node) mp->temp_head->link, &v1, (mp_value_node) q, t, mp_dependent_type);
        free_number(v1);
        return ret;
    }
}

static void mp_val_too_big (MP mp, mp_number *x)
{
    if (number_positive(internal_value(mp_warning_check_internal))) {
        char msg[256];
        mp_snprintf(msg, 256, "Value is too large (%s)", number_tostring(*x));
        mp_error(
            mp,
            msg,
            "The equation I just processed has given some variable a value outside of the\n"
            "safetyp range. Continue and I'll try to cope with that big value; but it might be\n"
            "dangerous. (Set 'warningcheck := 0' to suppress this message.)"
        );
    }
}

void mp_make_known (MP mp, mp_value_node p, mp_value_node q)
{
    mp_variable_type t = p->type;
    mp_number absp;
    new_number(absp);
    mp_set_prev_dep(q->link, mp_get_prev_dep(p));
    mp_set_link(mp_get_prev_dep(p), q->link);
    p->type = mp_known_type;
    mp_set_value_number(p, mp_get_dep_value(q));
    mp_free_dep_node(mp, q);
    number_abs_clone(absp, mp_get_value_number(p));
    if (number_greaterequal(absp, warning_limit_t)) {
        mp_val_too_big (mp, &(mp_get_value_number(p)));
    }
    if ((number_positive(internal_value(mp_tracing_equations_internal))) && mp_interesting(mp, (mp_node) p)) {
        mp_begin_diagnostic(mp);
        mp_print_nl(mp, "#### ");
        mp_print_variable_name(mp, (mp_node) p);
        mp_print_chr(mp, '=');
        print_number(mp_get_value_number(p));
        mp_end_diagnostic(mp, 0);
    }
    if (cur_exp_node == (mp_node) p && mp->cur_exp.type == t) {
        mp->cur_exp.type = mp_known_type;
        mp_set_cur_exp_value_number(mp, &(mp_get_value_number(p)));
        mp_free_value_node(mp, (mp_node) p);
    }
    free_number(absp);
}

static void mp_fix_dependencies (MP mp)
{
    mp_value_node r = (mp_value_node) mp->dep_head->link;
    mp_value_node s = NULL;
    while (r != mp->dep_head) {
        mp_value_node t = r;
        mp_value_node q;
        while (1) {
            mp_node x;
            if (t == r) {
                q = (mp_value_node) mp_get_dep_list(t);
            } else {
                q = (mp_value_node) r->link;
            }
            x = mp_get_dep_info(q);
            if (x == NULL) {
                break;
            } else if (x->type <= independent_being_fixed) {
                if (x->type < independent_being_fixed) {
                    mp_value_node p = mp_get_dep_node(mp);
                    mp_set_link(p, s);
                    s = p;
                    mp_set_dep_info(s, x);
                    x->type = independent_being_fixed;
                }
                mp_set_dep_value(q, mp_get_dep_value(q));
                number_divide_int(mp_get_dep_value(q), 4);
                if (number_zero(mp_get_dep_value(q))) {
                    mp_set_link(r, q->link);
                    mp_free_dep_node(mp, q);
                    q = r;
                }
            }
            r = q;
        }
        r = (mp_value_node) q->link;
        if (q == (mp_value_node) mp_get_dep_list(t)) {
            mp_make_known(mp, t, q);
        }
    }
    while (s != NULL) {
        mp_value_node p = (mp_value_node) s->link;
        mp_node x = mp_get_dep_info(s);
        mp_free_dep_node(mp, s);
        s = p;
        x->type = mp_independent_type;
        mp_set_indep_scale(x, mp_get_indep_scale(x) + 2);
    }
    mp->fix_needed = 0;
}

static void mp_new_dep (MP mp, mp_node q, mp_variable_type newtype, mp_value_node p)
{
    mp_node r;
    q->type = newtype;
    mp_set_dep_list(q, p);
    mp_set_prev_dep(q, (mp_node) mp->dep_head);
    r = mp->dep_head->link;
    mp_set_link(mp->dep_final, r);
    mp_set_prev_dep(r, (mp_node) mp->dep_final);
    mp_set_link(mp->dep_head, q);
}

static mp_value_node mp_const_dependency (MP mp, mp_number *v)
{
    mp->dep_final = mp_get_dep_node(mp);
    mp_set_dep_value(mp->dep_final, *v);
    mp_set_dep_info(mp->dep_final, NULL);
    return mp->dep_final;
}

static mp_value_node mp_single_dependency (MP mp, mp_node p)
{
    mp_value_node q;
    int m = mp_get_indep_scale(p);
    if (m > 28) {
        q = mp_const_dependency(mp, &zero_t);
    } else {
        mp_value_node rr;
        q = mp_get_dep_node(mp);
        mp_set_dep_value(q, zero_t);
        set_number_from_scaled(mp_get_dep_value(q), (int) two_to_the(28 - m));
        mp_set_dep_info(q, p);
        rr = mp_const_dependency(mp, &zero_t);
        mp_set_link(q, rr);
    }
    return q;
}

static mp_value_node mp_copy_dep_list (MP mp, mp_value_node p)
{
    mp_value_node q = mp_get_dep_node(mp);
    mp->dep_final = q;
    while (1) {
        mp_set_dep_info(mp->dep_final, mp_get_dep_info(p));
        mp_set_dep_value(mp->dep_final, mp_get_dep_value(p));
        if (mp_get_dep_info(mp->dep_final) == NULL) {
            break;
        } else {
            mp_set_link(mp->dep_final, mp_get_dep_node(mp));
            mp->dep_final = (mp_value_node) mp->dep_final->link;
            p = (mp_value_node) p->link;
        }
    }
    return q;
}

static mp_value_node find_node_with_largest_coefficient (MP mp, mp_value_node p, mp_number *v);

static void display_new_dependency (MP mp, mp_value_node p, mp_node x, int n);

static void change_to_known (MP mp, mp_value_node p, mp_node x, mp_value_node final_node, int n);

static mp_value_node divide_p_by_minusv_removing_q (MP mp,
    mp_value_node p, mp_value_node q,
    mp_value_node *final_node, mp_number *v, int t
);

static mp_value_node divide_p_by_2_n (MP mp, mp_value_node p, int n);

static void mp_linear_eq (MP mp, mp_value_node p, int t)
{
    mp_value_node r;
    mp_node x;
    int n;
    mp_number v;
    mp_value_node prev_r;
    mp_value_node final_node;
    mp_value_node qq;
    new_number(v);
    qq = find_node_with_largest_coefficient(mp, p, &v);
    x = mp_get_dep_info(qq);
    n = mp_get_indep_scale(x);
    p = divide_p_by_minusv_removing_q(mp, p, qq, &final_node, &v, t);
    if (number_positive(internal_value(mp_tracing_equations_internal))) {
        display_new_dependency(mp, p, (mp_node) x, n);
    }
    prev_r = (mp_value_node) mp->dep_head;
    r = (mp_value_node) mp->dep_head->link;
    while (r != mp->dep_head) {
        mp_value_node s = (mp_value_node) mp_get_dep_list(r);
        mp_value_node q = mp_p_with_x_becoming_q(mp, s, x, (mp_node) p, r->type);
        if (mp_get_dep_info(q) == NULL) {
            mp_make_known(mp, r, q);
        } else {
            mp_set_dep_list(r, q);
            do {
                q = (mp_value_node) q->link;
            } while (mp_get_dep_info(q) != NULL);
            prev_r = q;
        }
        r = (mp_value_node) prev_r->link;
    }
    if (n > 0) {
        p = divide_p_by_2_n(mp, p, n);
    }
    change_to_known(mp, p, (mp_node) x, final_node, n);
    if (mp->fix_needed) {
        mp_fix_dependencies(mp);
    }
    free_number(v);
}

static mp_value_node find_node_with_largest_coefficient (MP mp, mp_value_node p, mp_number *v)
{
    mp_number vabs;
    mp_number rabs;
    mp_value_node q = p;
    mp_value_node r = (mp_value_node) p->link;
    new_number(vabs);
    new_number(rabs);
    number_clone(*v, mp_get_dep_value(q));
    while (mp_get_dep_info(r) != NULL) {
        number_abs_clone(vabs, *v);
        number_abs_clone(rabs, mp_get_dep_value(r));
        if (number_greater(rabs, vabs)) {
            q = r;
            number_clone(*v, mp_get_dep_value(r));
        }
        r = (mp_value_node) r->link;
    }
    free_number(vabs);
    free_number(rabs);
    return q;
}

static mp_value_node divide_p_by_minusv_removing_q (MP mp,
    mp_value_node p, mp_value_node q,
    mp_value_node *final_node, mp_number *v, int t
)
{
    mp_value_node r = p;
    mp_value_node s = (mp_value_node) mp->temp_head;
    mp_set_link(s, p);
    do {
        if (r == q) {
            mp_set_link(s, r->link);
            mp_free_dep_node(mp, r);
        } else {
            mp_number w;
            mp_number absw;
            new_number(w);
            new_number(absw);
            make_fraction(w, mp_get_dep_value(r), *v);
            number_abs_clone(absw, w);
            if (number_lessequal(absw, half_fraction_threshold_k)) {
                mp_set_link(s, r->link);
                mp_free_dep_node(mp, r);
            } else {
                number_negate(w);
                mp_set_dep_value(r, w);
                s = r;
            }
            free_number(w);
            free_number(absw);
        }
        r = (mp_value_node) s->link;
    } while (mp_get_dep_info(r) != NULL);
    if (t == mp_proto_dependent_type) {
        mp_number ret;
        new_number(ret);
        make_scaled(ret, mp_get_dep_value(r), *v);
        number_negate(ret);
        mp_set_dep_value(r, ret);
        free_number(ret);
    } else if (number_to_scaled(*v) != -number_to_scaled(fraction_one_t)) {
        mp_number ret;
        new_fraction(ret);
        make_fraction(ret, mp_get_dep_value(r), *v);
        number_negate(ret);
        mp_set_dep_value(r, ret);
        free_number(ret);
    }
    *final_node = r;
    return (mp_value_node) mp->temp_head->link;
}

static void display_new_dependency (MP mp, mp_value_node p, mp_node x, int n)
{
    if (mp_interesting(mp, x)) {
        mp_begin_diagnostic(mp);
        mp_print_nl(mp, "## ");
        mp_print_variable_name(mp, x);
        while (n > 0) {
            mp_print_str(mp, "*4");
            n = n - 2;
        }
        mp_print_chr(mp, '=');
        mp_print_dependency(mp, p, mp_dependent_type);
        mp_end_diagnostic(mp, 0);
    }
}

static mp_value_node divide_p_by_2_n (MP mp, mp_value_node p, int n)
{
    mp_value_node pp = NULL;
    if (n > 0) {
        mp_value_node r;
        mp_value_node s;
        mp_number absw;
        mp_number w;
        new_number(w);
        new_number(absw);
        s = (mp_value_node) mp->temp_head;
        mp_set_link(mp->temp_head, p);
        r = p;
        do {
            if (n > 30) {
                set_number_to_zero(w);
            } else {
                number_clone(w, mp_get_dep_value(r));
                number_divide_int(w, two_to_the(n));
            }
            number_abs_clone(absw, w);
            if (number_lessequal(absw, half_fraction_threshold_k) && (mp_get_dep_info(r) != NULL)) {
                mp_set_link(s, r->link);
                mp_free_dep_node(mp, r);
            } else {
                mp_set_dep_value(r, w);
                s = r;
            }
            r = (mp_value_node) s->link;
        } while (mp_get_dep_info(s) != NULL);
        pp = (mp_value_node) mp->temp_head->link;
        free_number(absw);
        free_number(w);
    }
    return pp;
}

static void change_to_known (MP mp, mp_value_node p, mp_node x, mp_value_node final_node, int n)
{
    (void) n;
    if (mp_get_dep_info(p) == NULL) {
        mp_number absx;
        x->type = mp_known_type;
        mp_set_value_number(x, mp_get_dep_value(p));
        new_number_abs(absx, mp_get_value_number(x));
        if (number_greaterequal(absx, warning_limit_t)) {
            mp_val_too_big(mp, &(mp_get_value_number(x)));
        }
        free_number(absx);
        mp_free_dep_node(mp, p);
        if (cur_exp_node == x && mp->cur_exp.type == mp_independent_type) {
            mp_set_cur_exp_value_number(mp, &(mp_get_value_number(x)));
            mp->cur_exp.type = mp_known_type;
            mp_free_value_node(mp, x);
        }
    } else {
        mp->dep_final = final_node;
        mp_new_dep(mp, x, mp_dependent_type, p);
        if (cur_exp_node == x && mp->cur_exp.type == mp_independent_type) {
            mp->cur_exp.type = mp_dependent_type;
        }
    }
}

static mp_node mp_new_ring_entry (MP mp, mp_node p)
{
    mp_node q = mp_new_value_node(mp);
    q->name_type = mp_capsule_operation;
    q->type = p->type;
    if (mp_get_value_node(p) == NULL) {
        mp_set_value_node(q, p);
    } else {
        mp_set_value_node(q, mp_get_value_node(p));
    }
    mp_set_value_node(p, q);
    return q;
}

void mp_ring_delete (MP mp, mp_node p)
{
    mp_node q = mp_get_value_node(p);
    (void) mp;
    if (q != NULL && q != p) {
        while (mp_get_value_node(q) != p) {
            q = mp_get_value_node(q);
        }
        mp_set_value_node(q, mp_get_value_node(p));
    }
}

static void mp_nonlinear_eq (MP mp, mp_value v, mp_node p, int flush_p)
{
    mp_variable_type t = p->type - unknown_tag;
    mp_node q = mp_get_value_node(p);
    if (flush_p) {
        p->type = mp_vacuous_type;
    } else {
        p = q;
    }
    do {
        mp_node r = mp_get_value_node(q);
        q->type = t;
        switch (t) {
            case mp_boolean_type:
                mp_set_value_number(q, v.data.n);
                break;
            case mp_string_type:
                mp_set_value_str(q, v.data.str);
                add_str_ref(v.data.str);
                break;
            case mp_pen_type:
            case mp_nep_type:
                mp_set_value_knot(q, mp_copy_pen(mp, v.data.p));
                break;
            case mp_path_type:
                mp_set_value_knot(q, mp_copy_path(mp, v.data.p));
                break;
            case mp_picture_type:
                mp_set_value_node(q, v.data.node);
                mp_add_edge_ref(mp, v.data.node);
                break;
            default:
                break;
        }
        q = r;
    } while (q != p);
}

static void mp_ring_merge (MP mp, mp_node p, mp_node q)
{
    mp_node r = mp_get_value_node(p);
    while (r != p) {
        if (r == q) {
            mp_exclaim_redundant_equation(mp);
            return;
        } else {
            r = mp_get_value_node(r);
        }
    }
    r = mp_get_value_node(p);
    mp_set_value_node(p, mp_get_value_node(q));
    mp_set_value_node(q, r);
}

static void mp_exclaim_redundant_equation (MP mp)
{
    mp_back_error(
        mp,
        "Redundant equation",
        "I already knew that this equation was true. But perhaps no harm has been done;\n"
        "let's continue."
    );
    mp_get_x_next(mp);
}

const char *mp_cmd_mod_string (MP mp, int c, int m)
{
    switch (c) {
        case mp_add_to_command:        return "addto";
        case mp_assignment_command:    return ":=";
        case mp_at_least_command:      return "atleast";
        case mp_begin_group_command:   return "begingroup";
        case mp_colon_command:         return ":";
        case mp_comma_command:         return ",";
        case mp_controls_command:      return "controls";
        case mp_curl_command:          return "curl";
        case mp_delimiters_command:    return "delimiters";
        case mp_end_group_command:     return "endgroup";
        case mp_every_job_command:     return "everyjob";
        case mp_exit_test_command:     return "exitif";
        case mp_expand_after_command:  return "expandafter";
        case mp_interim_command:       return "interim";
        case mp_left_brace_command:    return "{";
        case mp_left_bracket_command:  return "[";
        case mp_let_command:           return "let";
        case mp_new_internal_command:  return "newinternal";
        case mp_of_command:            return "of";
        case mp_path_join_command:     return "..";
        case mp_relax_command:         return "\\";
        case mp_right_brace_command:   return "}";
        case mp_right_bracket_command: return "]";
        case mp_save_command:          return "save";
        case mp_scan_tokens_command:   return "scantokens";
        case mp_runscript_command:     return "runscript";
        case mp_maketext_command:      return "maketext";
        case mp_semicolon_command:     return ";";
        case mp_ship_out_command:      return "shipout";
        case mp_step_command:          return "step";
        case mp_str_command:           return "str";
        case mp_void_command:          return "void";
        case mp_tension_command:       return "tension";
        case mp_to_command:            return "to";
        case mp_until_command:         return "until";
        case mp_within_command:        return "within";
        case mp_write_command:         return "write";
        case mp_btex_command: return m == mp_btex_code ? "btex" : "verbatimtex";
        case mp_etex_command: return "etex";
        case mp_macro_def_command:
            switch (m) {
                case mp_end_def_code      : return "enddef";
                case mp_def_code          : return "def";
                case mp_var_def_code      : return "vardef";
                case mp_primary_def_code  : return "primarydef";
                case mp_secondary_def_code: return "secondarydef";
                case mp_tertiary_def_code : return "tertiarydef";
                default:                    return "?def";
            }
            break;
        case mp_iteration_command:
            switch (m) {
                case mp_end_for_code          : return "endfor";
                case mp_start_forever_code    : return "forever";
                case mp_start_for_code        : return "for";
                case mp_start_forsuffixes_code: return "forsuffixes";
            }
            break;
        case mp_only_set_command:
            switch (m) {
                case mp_random_seed_code  : return"randomseed";
                case mp_max_knot_pool_code: return"maxknotpool";
            }
            break;
        case mp_macro_special_command:
            switch (m) {
                case mp_macro_prefix_code: return "#@";
                case mp_macro_at_code    : return "@";
                case mp_macro_suffix_code: return "@#";
                case mp_macro_quote_code : return "quote";
            }
            break;
        case mp_parameter_commmand:
            switch (m) {
                case mp_expr_parameter  : return "expr";
                case mp_suffix_parameter: return "suffix";
                case mp_text_parameter  : return "text";
                case mp_primary_macro   : return "primary";
                case mp_secondary_macro : return "secondary";
                default                 : return "tertiary";
            }
            break;
        case mp_input_command:
            return m == 0 ? "input" : "endinput";
        case mp_if_test_command:
        case mp_fi_or_else_command:
            switch (m) {
                case mp_if_code     : return "if";
                case mp_fi_code     : return "fi";
                case mp_else_code   : return "else";
                case mp_else_if_code: return "elseif";
            }
            break;
        case mp_nullary_command:
        case mp_unary_command:
        case mp_of_binary_command:
        case mp_secondary_binary_command:
        case mp_tertiary_binary_command:
        case mp_primary_binary_command:
        case mp_cycle_command:
        case mp_plus_or_minus_command:
        case mp_slash_command:
        case mp_ampersand_command:
        case mp_equals_command:
        case mp_and_command:
            return mp_op_string((int) m);
        case mp_type_name_command:
            return "";
        case mp_stop_command:
            return cur_mod == 0 ? "end" : "dump";
        case mp_mode_command:
            switch (m) {
                case mp_batch_mode     : return "batchmode";
                case mp_nonstop_mode   : return "nonstopmode";
                case mp_scroll_mode    : return "scrollmode";
                case mp_error_stop_mode: return "errorstopmode";
                default                : return "silentmode";
            }
        break;
        case mp_protection_command:
            switch (m) {
                case 0: return "inner";
                case 1: return "outer";
            }
            break;
        case mp_property_command:
            return "setproperty";
        case mp_show_command:
            switch (m) {
                case mp_show_token_code       : return "showtoken";
                case mp_show_stats_code       : return "showstats";
                case mp_show_code             : return "show";
                case mp_show_var_code         : return "showvariable";
                case mp_show_dependencies_code: return "showdependencies";
            }
            break;
        case mp_left_delimiter_command:
        case mp_right_delimiter_command:
            return c == mp_left_delimiter_command ? "left delimiter" : "right delimiter";
        case mp_tag_command:
            return m == 0 ? "tag" : "variable";
        case mp_defined_macro_command:
            return "macro:";
        case mp_primary_def_command:
            return "primarydef";
        case mp_secondary_def_command:
            return "secondarydef";
        case mp_tertiary_def_command:
            return "tertiarydef";
        case mp_repeat_loop_command:
            return "[repeat the loop]";
        case mp_internal_command:
            return internal_name(m);
        case mp_thing_to_add_command:
            switch (m) {
                case mp_add_contour_code    : return "contour";
                case mp_add_double_path_code: return "doublepath";
                case mp_add_also_code       : return "also";
            }
            break;
        case mp_with_option_command:
            switch (m) {
                case mp_with_pen_code                : return "withpen";
                case mp_with_pre_script_code         : return "withprescript";
                case mp_with_post_script_code        : return "withpostscript";
                case mp_with_stacking_code           : return "withstacking";
                case mp_with_no_model_code           : return "withoutcolor";
                case mp_with_rgb_model_code          : return "withrgbcolor";
                case mp_with_uninitialized_model_code: return "withcolor";
                case mp_with_cmyk_model_code         : return "withcmykcolor";
                case mp_with_grey_model_code         : return "withgreyscale";
                case mp_with_linecap_code            : return "withlinecap";
                case mp_with_linejoin_code           : return "withlinejoin";
                case mp_with_miterlimit_code         : return "withmiterlimit";
                default                              : return "dashed";
            }
            break;
        case mp_bounds_command:
            switch (m) {
                case mp_start_clip_node_type  : return "clip";
                case mp_start_group_node_type : return "setgroup";
                case mp_start_bounds_node_type: return "setbounds";
            }
            break;
        case mp_message_command:
            if (m < err_message_code) {
                return "message";
            } else if (m == err_message_code) {
                return "errmessage";
            } else {
                return "errhelp";
            }
    }
    return "[unknown command code!]";
}

void mp_print_cmd_mod (MP mp, int c, int m)
{
    mp_print_str(mp, mp_cmd_mod_string(mp, c, m));
}

static void mp_show_cmd_mod (MP mp, int c, int m)
{
    mp_begin_diagnostic(mp);
    mp_print_nl(mp, "{");
    switch (c) {
        case mp_primary_def_command:
        case mp_secondary_def_command:
        case mp_tertiary_def_command:
            mp_print_cmd_mod(mp, mp_macro_def_command, c);
            mp_print_str(mp, "'d macro:");
            mp_print_ln(mp);
            mp_show_token_list(mp, cur_mod_node->link->link,0);
            break;
        default:
            mp_print_cmd_mod(mp, c, m);
            break;
    }
    mp_print_chr(mp, '}');
    mp_end_diagnostic(mp, 0);
}

static void mp_reallocate_input_stack (MP mp, int newsize)
{
    int n = newsize + 1;
    mp->input_file = mp_memory_reallocate(mp->input_file, (size_t) (n + 1) * sizeof(void *));
    mp->line_stack = mp_memory_reallocate(mp->line_stack, (size_t) (n + 1) * sizeof(int));
    for (int k = mp->max_in_open; k <= n; k++) {
        mp->input_file[k] = NULL;
        mp->line_stack[k] = 0;
    }
    mp->max_in_open = newsize;
}

static void mp_check_param_size (MP mp, int k)
{
    while (k >= mp->param_size) {
        mp->param_stack = mp_memory_reallocate(mp->param_stack, (size_t) ((k + k / 4) + 1) * sizeof(mp_node));
        mp->param_size = k + k / 4;
    }
}

int mp_true_line (MP mp)
{
    int k;
    if (file_state && (name > max_spec_src)) {
        return line;
    } else {
        k = mp->input_ptr;
        while ((k > 0) && ((mp->input_stack[(k - 1)].index_field <  mp_file_bottom_text)
                        || (mp->input_stack[(k - 1)].name_field  <= max_spec_src))) {
            --k;
        }
        return (k > 0 ? mp->line_stack[(k - 1) + mp_file_bottom_text] : 0);
    }
}

void mp_show_context (MP mp)
{
    mp->file_ptr = mp->input_ptr;
    mp->input_stack[mp->file_ptr] = mp->cur_input;
    while (1) {
        mp->cur_input = mp->input_stack[mp->file_ptr];

        if ((mp->file_ptr == mp->input_ptr) || file_state || (token_type != mp_backed_up_text) || (nloc != NULL)) {
            if (file_state) {
                if (name > max_spec_src) {
                    mp_print_nl(mp, "<line ");
                    mp_print_int(mp, mp_true_line(mp));
                    mp_print_chr(mp, '>');
                } else if (terminal_input) {
                    if (mp->file_ptr == 0) {
                        mp_print_nl(mp, "<direct>");
                    } else {
                        mp_print_nl(mp, "<insert>");
                    }
                } else if (name == is_scantok) {
                    mp_print_nl(mp, "<scantokens>");
                } else {
                    mp_print_nl(mp, "<read>");
                }
                mp_print_chr(mp, ' ');

                if (limit > 0) {
                    for (int i = start; i <= limit - 1; i++) {
                        mp_print_chr(mp, mp->buffer[i]);
                    }
                }
            } else {
                {
                    switch (token_type) {
                        case mp_forever_text:
                            mp_print_nl(mp, "<forever> ");
                            break;
                        case mp_loop_text:
                            {
                                mp_node pp = mp->param_stack[param_start];
                                mp_print_nl(mp, "<for(");
                                if (pp != NULL) {
                                    if (pp->link == MP_VOID) {
                                        mp_print_exp(mp, pp, 0);
                                    } else {
                                        mp_show_token_list(mp, pp, NULL);
                                    }
                                }
                                mp_print_str(mp, ")> ");
                            }
                            break;
                        case mp_parameter_text:
                            mp_print_nl(mp, "<argument> ");
                            break;
                        case mp_backed_up_text:
                            mp_print_nl(mp, nloc == NULL ? "<recently read> " : "<to be read again> ");
                            break;
                        case mp_inserted_text:
                            mp_print_nl(mp, "<inserted text> ");
                            break;
                        case mp_macro_text:
                            mp_print_nl(mp, "<macro> ");

                            if (name != NULL) {
                                mp_print_mp_str(mp, name);
                            } else {
                                {
                                    mp_node pp = mp->param_stack[param_start];
                                    if (pp == NULL) {
                                        mp_show_token_list(mp, mp->param_stack[param_start + 1], NULL);
                                    } else {
                                        mp_node qq = pp;
                                        while (qq->link != NULL) {
                                            qq = qq->link;
                                        }
                                        qq->link = mp->param_stack[param_start + 1];
                                        mp_show_token_list(mp, pp, NULL);
                                        qq->link = NULL;
                                    }
                                }
                            }
                            mp_print_str(mp, " -> ");
                            break;
                        default:
                            mp_print_nl(mp, "?");
                            break;
                    }
                }
                if (token_type == mp_macro_text) {
                    mp_show_macro(mp, nstart, nloc);
                } else if (mp->show_mode) {
                    mp_show_token_list_space(mp, nstart, nloc);
                } else {
                    mp_show_token_list(mp, nstart, nloc);
                }
            }
        }
        if (file_state && (name > max_spec_src || mp->file_ptr == 0)) {
            break;
        } else {
            --mp->file_ptr;
        }
    }
    mp->cur_input = mp->input_stack[mp->input_ptr];
}

void mp_push_input (MP mp)
{
    if (mp->input_ptr > mp->max_in_stack) {
        mp->max_in_stack = mp->input_ptr;
        if (mp->input_ptr == mp->stack_size) {
            int l = (mp->stack_size + (mp->stack_size/4));
            if (l > 1000) {
                mp_fatal_error(mp, "job aborted, more than 1000 input levels");
            } else {
                mp_in_state_record *s = mp_memory_reallocate(mp->input_stack, (size_t) (l + 1) * sizeof(mp_in_state_record));
                if (s) {
                    mp->input_stack = s;
                    mp->stack_size = l;
                } else {
                    mp_fatal_error(mp, "job aborted, out of memory");
                }
            }
        }
    }
    mp->input_stack[mp->input_ptr] = mp->cur_input;
    ++mp->input_ptr;
}

void mp_pop_input (MP mp)
{
    --mp->input_ptr;
    mp->cur_input = mp->input_stack[mp->input_ptr];
}

static void mp_begin_token_list (MP mp, mp_node p, int t)
{
    mp_push_input(mp);
    nstart = p;
    token_type = t;
    param_start = mp->param_ptr;
    nloc = p;
}

static void mp_end_token_list (MP mp)
{
    if (token_type >= mp_backed_up_text) {
        if (token_type <= mp_inserted_text) {
            mp_flush_token_list(mp, nstart);
            goto DONE;
        } else {
            mp_delete_mac_ref(mp, nstart);
        }
    }
    while (mp->param_ptr > param_start) {
        mp_node p;
        --mp->param_ptr;
        p = mp->param_stack[mp->param_ptr];
        if (p != NULL) {
            if (p->link == MP_VOID) {
                mp_recycle_value(mp, p);
                mp_free_value_node(mp, p);
            } else {
                mp_flush_token_list(mp, p);
            }
        }
    }
  DONE:
    mp_pop_input(mp);
}

static void mp_encapsulate (MP mp, mp_value_node p)
{
    mp_node q = mp_new_value_node(mp);
    q->name_type = mp_capsule_operation;
    mp_new_dep(mp, q, mp->cur_exp.type, p);
    mp_set_cur_exp_node(mp, q);
}
static void mp_install (MP mp, mp_node r, mp_node q)
{
    if (q->type == mp_known_type) {
        r->type = mp_known_type;
        mp_set_value_number(r, mp_get_value_number(q));
    } else if (q->type == mp_independent_type) {
        mp_value_node p = mp_single_dependency(mp, q);
        if (p == mp->dep_final) {
            r->type = mp_known_type;
            mp_set_value_number(r, zero_t);
            mp_free_dep_node(mp, p);
        } else {
            mp_new_dep(mp, r, mp_dependent_type, p);
        }
    } else {
        mp_new_dep(mp, r, q->type, mp_copy_dep_list(mp, (mp_value_node) mp_get_dep_list((mp_value_node) q)));
    }
}

static void mp_make_exp_copy (MP mp, mp_node p)
{
  RESTART:
    mp->cur_exp.type = p->type;
    switch (mp->cur_exp.type) {
        case mp_vacuous_type:
        case mp_boolean_type:
        case mp_known_type:
            mp_set_cur_exp_value_number(mp, &(mp_get_value_number(p)));
            break;
        case mp_unknown_boolean_type:
        case mp_unknown_string_type:
        case mp_unknown_pen_type:
        case mp_unknown_nep_type:
        case mp_unknown_path_type:
        case mp_unknown_picture_type:
            {
                mp_node t = mp_new_ring_entry(mp, p);
                mp_set_cur_exp_node(mp, t);
            }
            break;
        case mp_string_type:
            mp_set_cur_exp_str(mp, mp_get_value_str(p));
            break;
        case mp_picture_type:
            mp_set_cur_exp_node(mp, mp_get_value_node(p));
            mp_add_edge_ref(mp, cur_exp_node);
            break;
        case mp_pen_type:
        case mp_nep_type:
            mp_set_cur_exp_knot(mp, mp_copy_pen(mp, mp_get_value_knot(p)));
            break;
        case mp_path_type:
            mp_set_cur_exp_knot(mp, mp_copy_path(mp, mp_get_value_knot(p)));
            break;
        case mp_transform_type:
        case mp_color_type:
        case mp_cmykcolor_type:
        case mp_pair_type:
            {
                mp_node t;
                mp_value_node q;
                if (mp_get_value_node(p) == NULL) {
                    switch (p->type) {
                        case mp_pair_type:
                            mp_init_pair_node(mp, p);
                            break;
                        case mp_color_type:
                            mp_init_color_node(mp, p, mp_color_type);
                            break;
                        case mp_cmykcolor_type:
                            mp_init_color_node(mp, p, mp_cmykcolor_type);
                            break;
                        case mp_transform_type:
                            mp_init_transform_node(mp, p);
                            break;
                        default:
                            break;
                    }
                }
                t = mp_new_value_node(mp);
                t->name_type = mp_capsule_operation;
                q = (mp_value_node) mp_get_value_node(p);
                switch (mp->cur_exp.type) {
                    case mp_pair_type:
                        mp_init_pair_node(mp, t);
                        mp_install(mp, mp_y_part(mp_get_value_node(t)), mp_y_part(q));
                        mp_install(mp, mp_x_part(mp_get_value_node(t)), mp_x_part(q));
                        break;
                    case mp_color_type:
                        mp_init_color_node(mp, t, mp_color_type);
                        mp_install(mp, mp_blue_part(mp_get_value_node(t)),  mp_blue_part(q));
                        mp_install(mp, mp_green_part(mp_get_value_node(t)), mp_green_part(q));
                        mp_install(mp, mp_red_part(mp_get_value_node(t)),   mp_red_part(q));
                        break;
                    case mp_cmykcolor_type:
                        mp_init_color_node(mp, t, mp_cmykcolor_type);
                        mp_install(mp, mp_black_part(mp_get_value_node(t)),   mp_black_part(q));
                        mp_install(mp, mp_yellow_part(mp_get_value_node(t)),  mp_yellow_part(q));
                        mp_install(mp, mp_magenta_part(mp_get_value_node(t)), mp_magenta_part(q));
                        mp_install(mp, mp_cyan_part(mp_get_value_node(t)),    mp_cyan_part(q));
                        break;
                    case mp_transform_type:
                        mp_init_transform_node(mp, t);
                        mp_install(mp, mp_yy_part(mp_get_value_node(t)), mp_yy_part(q));
                        mp_install(mp, mp_yx_part(mp_get_value_node(t)), mp_yx_part(q));
                        mp_install(mp, mp_xy_part(mp_get_value_node(t)), mp_xy_part(q));
                        mp_install(mp, mp_xx_part(mp_get_value_node(t)), mp_xx_part(q));
                        mp_install(mp, mp_ty_part(mp_get_value_node(t)), mp_ty_part(q));
                        mp_install(mp, mp_tx_part(mp_get_value_node(t)), mp_tx_part(q));
                        break;
                    default:
                        break;
                }
                mp_set_cur_exp_node(mp, t);
            }
            break;
        case mp_dependent_type:
        case mp_proto_dependent_type:
            mp_encapsulate (mp, mp_copy_dep_list(mp, (mp_value_node) mp_get_dep_list((mp_value_node) p)));
            break;
        case mp_numeric_type:
            mp_new_indep(mp, p);
            goto RESTART;
        case mp_independent_type:
            {
                mp_value_node q = mp_single_dependency(mp, p);
                if (q == mp->dep_final) {
                    mp->cur_exp.type = mp_known_type;
                    mp_set_cur_exp_value_number(mp, &zero_t);
                    mp_free_dep_node(mp, q);
                } else {
                    mp->cur_exp.type = mp_dependent_type;
                    mp_encapsulate (mp, q);
                }
            }
            break;
        case mp_undefined_type:
            mp_confusion(mp, "undefined copy");
            break;
        default:
            mp_confusion(mp, "copy");
            break;
    }
}

static mp_node mp_cur_tok (MP mp)
{
    mp_node p;
    if (cur_sym == NULL && (cur_sym_mod == 0 || cur_sym_mod == mp_normal_operation)) {
        if (cur_cmd == mp_capsule_command) {
            mp_number save_exp_num;
            mp_value save_exp = mp->cur_exp;
            new_number(save_exp_num);
            number_clone(save_exp_num, cur_exp_value_number);
            mp_make_exp_copy(mp, cur_mod_node);
            p = mp_stash_cur_exp(mp);
            p->link = NULL;
            mp->cur_exp = save_exp;
            number_clone(mp->cur_exp.data.n, save_exp_num);
            free_number(save_exp_num);
        } else {
            p = mp_new_token_node(mp);
            p->name_type = mp_token_operation;
            if (cur_cmd == mp_numeric_command) {
                mp_set_value_number(p, cur_mod_number);
                p->type = mp_known_type;
            } else {
                mp_set_value_str(p, cur_mod_str);
                p->type = mp_string_type;
            }
        }
    } else {
        p = mp_new_symbolic_node(mp);
        mp_set_sym_sym(p, cur_sym);
        p->name_type = cur_sym_mod;
    }
    return p;
}

void mp_back_input (MP mp)
{
    mp_node p = mp_cur_tok(mp);
    while (token_state && (nloc == NULL)) {
        mp_end_token_list(mp);
    }
    mp_begin_token_list(mp, p, mp_backed_up_text);
}

static void mp_back_error (MP mp, const char *msg, const char *hlp)
{
    mp_back_input(mp);
    mp_error(mp, msg, hlp);
}

static void mp_ins_error (MP mp, const char *msg, const char *hlp)
{
    mp_back_input(mp);
    token_type = mp_inserted_text;
    mp_error(mp, msg, hlp);
}

void mp_begin_file_reading (MP mp)
{
    if (mp->in_open == (mp->max_in_open-1)) {
        mp_reallocate_input_stack (mp, (mp->max_in_open + mp->max_in_open / 4));
    }
    if (mp->first == mp->buf_size) {
        mp_reallocate_buffer(mp, (mp->buf_size + mp->buf_size / 4));
    }
    mp->in_open++;
    mp_push_input(mp);
    iindex = (int) mp->in_open;
    if (mp->in_open_max < mp->in_open) {
        mp->in_open_max = mp->in_open;
    }
    start = (int) mp->first;
    name = is_term;
}

static void mp_end_file_reading (MP mp)
{
    if (mp->in_open > iindex) {
        if ((name <= max_spec_src)) {
            mp_confusion(mp, "endinput");
        } else {
            (mp->close_file) (mp, mp->input_file[mp->in_open]);
            --mp->in_open;
        }
    }
    mp->first = (size_t) start;
    if (iindex != mp->in_open) {
        mp_confusion(mp, "endinput");
    } else {
        if (name > max_spec_src) {
            (mp->close_file) (mp, cur_file);
        }
        mp_pop_input(mp);
        --mp->in_open;
    }
}

static int mp_check_outer_validity (MP mp)
{
    if (mp->scanner_status == mp_normal_state) {
        return 1;
    } else if (mp->scanner_status == mp_tex_flushing_state) {
        if (cur_sym != NULL) {
            return 1;
        } else {
            char msg[256];
            mp_snprintf(msg, 256, "TeX mode didn't end; all text was ignored after line %d", (int) mp->warning_line);
            set_cur_sym(mp->frozen_etex);
            mp_ins_error(
                mp,
                msg,
                "The file ended while I was looking for the 'etex' to finish this TeX material.\n"
                "I've inserted 'etex' now."
            );
            return 0;
        }
    } else {

        if (mp->scanner_status > mp_skipping_state) {
            {
                char msg[256];
                const char *mst = NULL;
                const char *hlp =
                    "I suspect you have forgotten an 'enddef', causing me to read past where you\n"
                    "wanted me to stop. I'll try to recover.";
                mp_runaway(mp);
                if (cur_sym == NULL) {
                    mst = "File ended while scanning";
                } else {
                    mst = "Forbidden token found while scanning";
                }
                switch (mp->scanner_status) {
                    case mp_flushing_state:
                        {
                            mp_snprintf(msg, 256, "%s to the end of the statement", mst);
                            hlp =
                                "A previous error seems to have propagated, causing me to read past where\n"
                                "you wanted me to stop. I'll try to recover.";
                            set_cur_sym(mp->frozen_semicolon);
                        }
                        break;
                    case mp_absorbing_state:
                        {
                            mp_snprintf(msg, 256, "%s a text argument",  mst);
                            hlp =
                                "It seems that a right delimiter was left out, causing me to read past where\n"
                                "you wanted me to stop. I'll try to recover.";
                            if (mp->warning_info == NULL) {
                                set_cur_sym(mp->frozen_end_group);
                            } else {
                                set_cur_sym(mp->frozen_right_delimiter);
                                set_equiv_sym(cur_sym, mp->warning_info);
                            }
                        }
                        break;
                    case mp_var_defining_state:
                        {
                            mp_string s;
                            int selector = mp->selector;
                            mp->selector = mp_new_string_selector;
                            mp_print_variable_name(mp, mp->warning_info_node);
                            s = mp_make_string(mp);
                            mp->selector = selector;
                            mp_snprintf(msg, 256, "%s the definition of %s", mst, s->str);
                            delete_str_ref(s);
                            set_cur_sym(mp->frozen_end_def);
                        }
                        break;
                    case mp_op_defining_state:
                        {
                            char *s = mp_str(mp, text(mp->warning_info));
                            mp_snprintf(msg, 256, "%s the definition of %s", mst, s);
                            set_cur_sym(mp->frozen_end_def);
                        }
                        break;
                    case mp_loop_defining_state:
                        {
                            char *s = mp_str(mp, text(mp->warning_info));
                            mp_snprintf(msg, 256, "%s the text of a %s loop", mst, s);
                            hlp =
                                "I suspect you have forgotten an 'endfor', causing me to read past where\n"
                                "you wanted me to stop. I'll try to recover.";
                                set_cur_sym(mp->frozen_end_for);
                        }
                    break;
                }
                mp_ins_error(mp, msg, hlp);
            }
        } else {
            char msg[256];
            const char *hlp = NULL;
            mp_snprintf(msg, 256, "Incomplete if; all text was ignored after line %d", (int) mp->warning_line);
            if (cur_sym == NULL) {
                hlp =
                    "The file ended while I was skipping conditional text. This kind of error happens\n"
                    "when you say 'if ...' and forget the matching 'fi'. I've inserted a 'fi'; this\n"
                    "might work.";
            } else {
                hlp =
                    "A forbidden 'outer' token occurred in skipped text. This kind of error happens\n"
                    "when you say 'if ...' and forget the matching 'fi'. I've inserted a 'fi'; this\n"
                    "might work.";
            }
            set_cur_sym(mp->frozen_fi);
            mp_ins_error(mp, msg, hlp);
        }
        return 0;
    }
}

void mp_runaway (MP mp)
{
    if (mp->scanner_status > mp_flushing_state) {
        mp_print_nl(mp, "Runaway ");
        switch (mp->scanner_status) {
            case mp_absorbing_state:
                mp_print_str(mp, "text?");
                break;
            case mp_var_defining_state:
            case mp_op_defining_state:
                mp_print_str(mp, "definition?");
                break;
            case mp_loop_defining_state:
                mp_print_str(mp, "loop?");
                break;
        }
        mp_print_ln(mp);
        mp_show_token_list(mp, mp->hold_head->link, NULL);
    }
}

void mp_get_next (MP mp)
{
    mp_sym cur_sym_;
  RESTART:
    set_cur_sym(NULL);
    set_cur_sym_mod(0);
    if (file_state) {
        int k;
        unsigned char c;
        int cclass;
      SWITCH:
        c = mp->buffer[loc];
        ++loc;
        cclass = mp->char_class[c];
        switch (cclass) {
            case mp_digit_class:
                scan_numeric_token((c - '0'));
                return;
            case mp_period_class:
                cclass = mp->char_class[mp->buffer[loc]];
                if (cclass > mp_period_class) {
                    goto SWITCH;
                } else if (cclass < mp_period_class) {
                    scan_fractional_token(0);
                    return;
                } else {
                    break;
                }
            case mp_space_class:
                goto SWITCH;
            case mp_percent_class:
                if (mp->scanner_status == mp_tex_flushing_state && loc < limit) {
                    goto SWITCH;
                }
                if (mp_move_to_next_line(mp)) {
                    goto RESTART;
                } else {
                    goto SWITCH;
                }
            case mp_string_class:
                if (mp->scanner_status == mp_tex_flushing_state) {
                    goto SWITCH;
                } else {
                    unsigned char cend = c == '"' ? '"' : 3 ;
                    if (mp->buffer[loc] == cend) {
                        set_cur_mod_str(mp_rts(mp,""));
                    } else {
                        k = loc;
                        mp->buffer[limit + 1] = cend;
                        do {
                            ++loc;
                        } while (mp->buffer[loc] != cend);
                        if (loc > limit) {
                            loc = limit;
                            mp_error(
                                mp,
                                "Incomplete string token has been flushed",
                                "Strings should finish on the same line as they began. I've deleted the partial\n"
                                "string."
                            );
                            goto RESTART;
                        }
                        mp_str_room(mp, loc - k);
                        do {
                            mp_append_char(mp, mp->buffer[k]);
                            ++k;
                        } while (k != loc);
                        set_cur_mod_str(mp_make_string(mp));
                    }
                    ++loc;
                    set_cur_cmd(mp_string_command);
                    return;
                }
            case mp_comma_class:
            case mp_semicolon_class:
            case mp_left_parenthesis_class:
            case mp_right_parenthesis_class:
                k = loc - 1;
                goto FOUND;
            case mp_invalid_class:
                if (mp->scanner_status == mp_tex_flushing_state) {
                    goto SWITCH;
                } else {
                    mp_error(
                        mp,
                        "Text line contains an invalid character",
                        "A funny symbol that I can\'t read has just been input. Continue, and I'll forget\n"
                        "that it ever happened."
                    );
                    goto RESTART;
                }
            default:
                break;
        }
        k = loc - 1;
        while (mp->char_class[mp->buffer[loc]] == cclass) {
            ++loc;
        }
      FOUND:
        set_cur_sym(mp_id_lookup(mp, (char *) (mp->buffer + k), (size_t) (loc - k), 1));
    } else {
        if (nloc != NULL && nloc->type == mp_symbol_node_type) {
            int cur_sym_mod_ = nloc->name_type;
            int cur_info = mp_get_sym_info(nloc);
            set_cur_sym(mp_get_sym_sym(nloc));
            set_cur_sym_mod(cur_sym_mod_);
            nloc = nloc->link;
            if (cur_sym_mod_ == mp_expr_operation) {
                set_cur_cmd(mp_capsule_command);
                set_cur_mod_node(mp->param_stack[param_start + cur_info]);
                set_cur_sym_mod(0);
                set_cur_sym(NULL);
                return;
            } else if (cur_sym_mod_ == mp_suffix_operation || cur_sym_mod_ == mp_text_operation) {
                mp_begin_token_list(mp, mp->param_stack[param_start + cur_info], (int) mp_parameter_text);
                goto RESTART;
            }
        } else if (nloc != NULL) {
            if (nloc->name_type == mp_token_operation) {
                if (nloc->type == mp_known_type) {
                    set_cur_mod_number(mp_get_value_number(nloc));
                    set_cur_cmd(mp_numeric_command);
                } else {
                    set_cur_mod_str(mp_get_value_str(nloc));
                    set_cur_cmd(mp_string_command);
                    add_str_ref(cur_mod_str);
                }
            } else {
                set_cur_mod_node(nloc);
                set_cur_cmd(mp_capsule_command);
            }
            nloc = nloc->link;
            return;
        } else {
            mp_end_token_list(mp);
            goto RESTART;
        }
    }
    cur_sym_ = cur_sym;
    set_cur_cmd(eq_type(cur_sym_));
    set_cur_mod(equiv(cur_sym_));
    set_cur_mod_node(equiv_node(cur_sym_));

}

static int mp_move_to_next_line (MP mp)
{
    if (name > max_spec_src) {
        ++line;
        mp->first = (size_t) start;
        if (! mp->force_eof) {
            if (mp_input_ln(mp, cur_file)) {
                mp_firm_up_the_line(mp);
            } else {
                mp->force_eof = 1;
            }
        };
        if (mp->force_eof) {
            mp->force_eof = 0;
            --loc;
            if (mp->interaction < mp_silent_mode) {
                mp_print_chr(mp, ')');
                --mp->open_parens;
                update_terminal();
            }
            mp_end_file_reading(mp);
            mp_check_outer_validity(mp);
            return 1;
        } else {
            mp->buffer[limit] = '%';
            mp->first = (size_t) (limit + 1);
            loc = start;
        }
    } else if (mp->input_ptr > 0) {
        mp_end_file_reading(mp);
        return 1;
    } else if (mp->interaction > mp_nonstop_mode) {
        if (limit == start && mp->interaction < mp_silent_mode) {
            mp_print_nl(mp, "(Please type a command or say `end')");
        }
        mp_print_ln(mp);
        mp->first = (size_t) start;
        if (! mp_input_ln(mp, mp->term_in)) {
            longjmp(*(mp->jump_buf), 1);
        }
        mp->buffer[mp->last] = '%';
        limit = (int) mp->last;
        mp->buffer[limit] = '%';
        mp->first = (size_t) (limit + 1);
        loc = start;
    } else {
        mp_fatal_error(mp, "job aborted, no legal end found");
    }
    return 0;
}

void mp_firm_up_the_line (MP mp)
{
    limit = (int) mp->last;
}

static void mp_t_next (MP mp)
{
    if ((mp->extensions == 1) && (cur_cmd == mp_btex_command)) {
        char *txt = NULL;
        char *ptr = NULL;
        int slin = line;
        int size = 0;
        int done = 0;
        int mode = round_unscaled(internal_value(mp_texscriptmode_internal)) ;
        int verb = cur_mod == mp_verbatim_code;
        int first;
        if (loc <= limit && mp->char_class[mp->buffer[loc]] == mp_space_class) {
            ++loc;
        } else {
        }
        first = loc;
        while (1) {
            if (loc < limit - 4) {
                if (mp->buffer[loc] == 'e') {
                    ++loc;
                    if (mp->buffer[loc] == 't') {
                        ++loc;
                        if (mp->buffer[loc] == 'e') {
                            ++loc;
                            if (mp->buffer[loc] == 'x') {
                                if (first == (loc - 3)) {
                                    done = 1;
                                } else if (mp->char_class[mp->buffer[loc - 4]] == mp_space_class) {
                                    done = 2;
                                }
                                if (done) {
                                    if ((loc + 1) <= limit) {
                                        int c = mp->char_class[mp->buffer[loc + 1]] ;
                                        if (c != mp_letter_class) {
                                            ++loc;
                                            break;
                                        } else {
                                            done = 0;
                                        }
                                    } else {
                                        ++loc;
                                        break;
                                    }
                                }
                            }
                        }
                    }
                }
            }
            if (loc >= limit) {
                if (size) {
                    txt = mp_memory_reallocate(txt, (size_t) (size + limit - first + 1));
                } else {
                    txt = mp_memory_allocate((size_t) (limit - first + 1));
                }
                memcpy(txt + size, mp->buffer + first, limit - first);
                size += limit - first + 1;
                if (mode <= 0) {
                    txt[size - 1] = ' ';
                } else if (verb) {
                    txt[size - 1] = '\n';
                } else if (mode >= 2) {
                    txt[size - 1] = '\n';
                } else {
                    txt[size - 1] = ' ';
                }
                if (mp_move_to_next_line(mp)) {
                    goto FATAL_ERROR;
                }
                first = loc;
            } else {
                ++loc;
            }
        }
        if (done) {
            int l = loc - 5 ;
            int n = l - first + 1 ;
            if (done == 2) {
                l -= 1;
                n -= 1;
            }
            if (size) {
                txt = mp_memory_reallocate(txt, (size_t) (size + n + 1));
            } else {
                txt = mp_memory_allocate((size_t) (n + 1));
            }
            memcpy(txt + size, mp->buffer + first, n);
            size += n;
            if (verb && mode >= 3) {
                txt[size] = '\0';
                ptr = txt;
            } else if (mode >= 4) {
                txt[size] = '\0';
                ptr = txt;
            } else {
                while ((size > 1) && (mp->char_class[(unsigned char) txt[size-1]] == mp_space_class || txt[size-1] == '\n')) {
                    --size;
                }
                txt[size] = '\0';
                ptr = txt;
                while ((size > 1) && (mp->char_class[(unsigned char) ptr[0]] == mp_space_class || ptr[0] == '\n')) {
                    ++ptr;
                    --size;
                }
            }
            check_script_result(mp, mp->make_text(mp, ptr, size, verb));
            mp_memory_free(txt);
            mp_get_next(mp);
            return;
        }
        FATAL_ERROR:
        {
            char msg[256];
            if (slin > 0) {
                mp_snprintf(msg, 256, "No matching 'etex' for '%stex'.", verb ? "verbatim" : "b");
            } else {
                mp_snprintf(msg, 256, "No matching 'etex' for '%stex' in line %d.", verb ? "verbatim" : "b",slin);
            }
            mp_error(mp, msg, "An 'etex' is missing at this input level, nothing gets done.");
            mp_memory_free(txt);
        }
    } else {
        {
            mp_error(
                mp,
                "A 'btex/verbatimtex ... etex' definition needs an extension",
                "This file contains picture expressions for 'btex ... etex' blocks. Such files\n"
                "need an extension (plugin) that seems to be absent."
            );
        }
    }
}

static mp_node mp_scan_toks (MP mp, mp_command_code terminator, mp_subst_list_item * subst_list, mp_node tail_end, int suffix_count)
{
    int cur_data;
    int cur_data_mod = 0;
    mp_node p = mp->hold_head;
    int balance = 1;
    mp->hold_head->link = NULL;
    while (1) {
        get_t_next(mp);
        cur_data = -1;
        if (cur_sym != NULL) {
            {
                mp_subst_list_item *q = subst_list;
                while (q != NULL) {
                    if (q->info == cur_sym && q->info_mod == cur_sym_mod) {
                        cur_data = q->value_data;
                        cur_data_mod = q->value_mod;
                        set_cur_cmd(mp_relax_command);
                        break;
                    }
                    q = q->link;
                }
            }
            if (cur_cmd == terminator) {
                if (cur_mod > 0) {
                    ++balance;
                } else {
                    --balance;
                    if (balance == 0)
                        break;
                }
            } else if (cur_cmd == mp_macro_special_command) {
                if (cur_mod == mp_macro_quote_code) {
                    get_t_next(mp);
                } else if (cur_mod <= suffix_count) {
                    cur_data = cur_mod - 1;
                    cur_data_mod = mp_suffix_operation;
                }
            }
        }
        if (cur_data != -1) {
            mp_node pp = mp_new_symbolic_node(mp);
            mp_set_sym_info(pp, cur_data);
            pp->name_type = cur_data_mod;
            p->link = pp;
        } else {
            p->link = mp_cur_tok(mp);
        }
        p = p->link;
    }
    p->link = tail_end;
    while (subst_list) {
        mp_subst_list_item *q = subst_list->link;
        mp_memory_free(subst_list);
        subst_list = q;
    }
    return mp->hold_head->link;
}

static void mp_get_symbol (MP mp)
{
  RESTART:
    get_t_next(mp);
    if ((cur_sym == NULL) || mp_is_frozen(mp, cur_sym)) {
        const char *hlp = NULL;
        if (cur_sym != NULL) {
            hlp =
                "Sorry: You can't redefine my error-recovery tokens. I've inserted an\n"
                "inaccessible symbol so that your definition will be completed without\n"
                "mixing me up too badly.";
        } else {
            hlp =
                "Sorry: You can't redefine a number, string, or expr. I've inserted an\n"
                "inaccessible symbol so that your definition will be completed without\n"
                "mixing me up too badly.";
            if (cur_cmd == mp_string_command) {
                delete_str_ref(cur_mod_str);
            }
        }
        set_cur_sym(mp->frozen_inaccessible);
        mp_ins_error(mp, "Missing symbolic token inserted", hlp);
        goto RESTART;
    }
}

static void mp_get_clear_symbol (MP mp)
{
    mp_get_symbol(mp);
    mp_clear_symbol(mp, cur_sym, 0);
}

static void mp_check_equals (MP mp)
{
    if (cur_cmd != mp_equals_command && cur_cmd != mp_assignment_command) {
        mp_back_error(
            mp,
            "Missing '=' has been inserted",
            "The next thing in this 'def' should have been '=', because I've already looked at\n"
            "the definition heading. But don't worry; I'll pretend that an equals sign was\n"
            "present. Everything from here to 'enddef' will be the replacement text of this\n"
            "macro."
        );
    }
}

static void mp_make_op_def (MP mp, int code)
{
    mp_node q, r;
    mp_command_code m = (code == mp_primary_def_code) ? mp_primary_def_command : (code == mp_secondary_def_code ? mp_secondary_def_command : mp_tertiary_def_command);
    mp_subst_list_item *qm = NULL;
    mp_subst_list_item *qn = NULL;
    mp_get_symbol(mp);
    qm = mp_memory_allocate(sizeof(mp_subst_list_item));
    qm->link = NULL;
    qm->info = cur_sym;
    qm->info_mod = cur_sym_mod;
    qm->value_data = 0;
    qm->value_mod = mp_expr_operation;
    mp_get_clear_symbol(mp);
    mp->warning_info = cur_sym;
    mp_get_symbol(mp);
    qn = mp_memory_allocate(sizeof(mp_subst_list_item));
    qn->link = qm;
    qn->info = cur_sym;
    qn->info_mod = cur_sym_mod;
    qn->value_data = 1;
    qn->value_mod = mp_expr_operation;
    get_t_next(mp);
    mp_check_equals(mp);
    mp->scanner_status = mp_op_defining_state;
    q = mp_new_symbolic_node(mp);
    mp_set_ref_count(q, 0);
    r = mp_new_symbolic_node(mp);
    q->link = r;
    mp_set_sym_info(r, mp_general_macro);
    r->name_type = mp_macro_operation;
    r->link = mp_scan_toks(mp, mp_macro_def_command, qn, NULL, 0);
    mp->scanner_status = mp_normal_state;
    set_eq_type(mp->warning_info, m);
    set_equiv_node(mp->warning_info, q);
    mp_get_x_next(mp);
}

static void mp_scan_def (MP mp, int code)
{
    int n;
    int k;
    mp_subst_list_item *r = NULL;
    mp_subst_list_item *rp = NULL;
    mp_node q;
    mp_node p;
    int sym_type;
    mp_sym l_delim, r_delim;
    int c = mp_general_macro;
    mp->hold_head->link = NULL;
    q = mp_new_symbolic_node(mp);
    mp_set_ref_count(q, 0);
    r = NULL;
    if (code == mp_def_code) {
        mp_get_clear_symbol(mp);
        mp->warning_info = cur_sym;
        get_t_next(mp);
        mp->scanner_status = mp_op_defining_state;
        n = 0;
        set_eq_type(mp->warning_info, mp_defined_macro_command);
        set_equiv_node(mp->warning_info, q);
    } else {
        p = mp_scan_declared_variable(mp);
        mp_flush_variable(mp, equiv_node(mp_get_sym_sym(p)), p->link, 1);
        mp->warning_info_node = mp_find_variable(mp, p);
        mp_flush_node_list(mp, p);
        if (mp->warning_info_node == NULL) {
            mp_error(
                mp,
                "This variable already starts with a macro",
                "After 'vardef a' you can't say 'vardef a.b'. So I'll have to discard this\n"
                "definition."
            );
            mp->warning_info_node = mp->bad_vardef;
        }
        mp->scanner_status = mp_var_defining_state;
        n = 2;
        if (cur_cmd == mp_macro_special_command && cur_mod == mp_macro_suffix_code) {
            n = 3;
            get_t_next(mp);
        }
        mp->warning_info_node->type = mp_unsuffixed_macro_type - 2 + n;
        mp_set_value_node(mp->warning_info_node, q);
    }
    k = n;
    if (cur_cmd == mp_left_delimiter_command) {
        do {
            l_delim = cur_sym;
            r_delim = equiv_sym(cur_sym);
            get_t_next(mp);
            if (cur_cmd == mp_parameter_commmand) {
                switch (cur_mod) {
                    case mp_expr_parameter:
                        sym_type = mp_expr_operation;
                        goto OKAY;
                        break;
                    case mp_suffix_parameter:
                        sym_type = mp_suffix_operation;
                        goto OKAY;
                        break;
                    case mp_text_parameter:
                        sym_type = mp_text_operation;
                        goto OKAY;
                        break;
                    default:
                        break;
                }
            }
            mp_back_error(
                mp,
                "Missing parameter type; 'expr' will be assumed",
                "You should've had 'expr' or 'suffix' or 'text' here."
            );
            sym_type = mp_expr_operation;
         OKAY:
            do {
                q->link = mp_new_symbolic_node(mp);
                q = q->link;
                q->name_type = sym_type;
                mp_set_sym_info(q, k);
                mp_get_symbol(mp);
                rp = mp_memory_allocate(sizeof(mp_subst_list_item));
                rp->link = NULL;
                rp->value_data = k;
                rp->value_mod = sym_type;
                rp->info = cur_sym;
                rp->info_mod = cur_sym_mod;
                mp_check_param_size(mp, k);
                ++k;
                rp->link = r;
                r = rp;
                get_t_next(mp);
            } while (cur_cmd == mp_comma_command);

            mp_check_delimiter(mp, l_delim, r_delim);
            get_t_next(mp);
        } while (cur_cmd == mp_left_delimiter_command);
    }
    if (cur_cmd == mp_parameter_commmand) {
        rp = mp_memory_allocate(sizeof(mp_subst_list_item));
        rp->link = NULL;
        rp->value_data = k;
        switch (cur_mod) {
            case mp_expr_parameter:
                rp->value_mod = mp_expr_operation;
                c = mp_expr_macro;
                break;
            case mp_suffix_parameter:
                rp->value_mod = mp_suffix_operation;
                c = mp_suffix_macro;
                break;
            case mp_text_parameter:
                rp->value_mod = mp_text_operation;
                c = mp_text_macro;
                break;
            default:
                c = cur_mod;
                rp->value_mod = mp_expr_operation;
                break;
        }
        mp_check_param_size(mp, k);
        ++k;
        mp_get_symbol(mp);
        rp->info = cur_sym;
        rp->info_mod = cur_sym_mod;
        rp->link = r;
        r = rp;
        get_t_next(mp);
        if (c == mp_expr_macro && cur_cmd == mp_of_command) {
            c = mp_of_macro;
            rp = mp_memory_allocate(sizeof(mp_subst_list_item));
            rp->link = NULL;
            mp_check_param_size(mp, k);
            rp->value_data = k;
            rp->value_mod = mp_expr_operation;
            mp_get_symbol(mp);
            rp->info = cur_sym;
            rp->info_mod = cur_sym_mod;
            rp->link = r;
            r = rp;
            get_t_next(mp);
        }
    }
    mp_check_equals(mp);
    p = mp_new_symbolic_node(mp);
    mp_set_sym_info(p, c);
    p->name_type = mp_macro_operation;
    q->link = p;
    if (code == mp_def_code) {
        p->link = mp_scan_toks(mp, mp_macro_def_command, r, NULL, (int) n);
    } else {
        mp_node qq = mp_new_symbolic_node(mp);
        mp_set_sym_sym(qq, mp->bg_loc);
        p->link = qq;
        p = mp_new_symbolic_node(mp);
        mp_set_sym_sym(p, mp->eg_loc);
        qq->link = mp_scan_toks(mp, mp_macro_def_command, r, p, (int) n);
    }
    if (mp->warning_info_node == mp->bad_vardef) {
        mp_flush_token_list(mp, mp_get_value_node(mp->bad_vardef));
    }
    mp->scanner_status = mp_normal_state;
    mp_get_x_next(mp);
}

static void mp_check_expansion_depth (MP mp)
{
    if (++mp->expand_depth_count >= mp->expand_depth) {
        if (mp->interaction >= mp_error_stop_mode) {
            mp->interaction=mp_scroll_mode;
        }
        mp_error(
            mp,
            "Maximum expansion depth reached",
            "Recursive macro expansion cannot be unlimited because of runtime stack\n"
            "constraints. The limit is 10000 recursion levels in total."
        );
        mp->history=mp_fatal_error_stop;
        mp_jump_out(mp);
    }
}

static void mp_expand (MP mp)
{
    mp_check_expansion_depth(mp);
    if (number_greater(internal_value(mp_tracing_commands_internal), unity_t) && cur_cmd != mp_defined_macro_command) {
        mp_show_cmd_mod(mp, cur_cmd, cur_mod);
    }
    switch (cur_cmd) {
        case mp_if_test_command:
            mp_conditional(mp);
            break;
        case mp_fi_or_else_command:
            if (cur_mod > mp->if_limit) {
                if (mp->if_limit == mp_if_code) {
                    mp_back_input(mp);
                    set_cur_sym(mp->frozen_colon);
                    mp_ins_error(mp, "Missing ':' has been inserted", "Something was missing here");
                } else {
                    const char *hlp = "I'm ignoring this; it doesn't match any if.";
                    switch (cur_mod) {
                        case mp_fi_code:
                            mp_error(mp, "Extra 'fi'", hlp);
                            break;
                        case mp_else_code:
                            mp_error(mp, "Extra 'else'", hlp);
                            break;
                        default:
                            mp_error(mp, "Extra 'elseif'", hlp);
                            break;
                    }
                }
            } else {
                while (cur_mod != mp_fi_code) {
                    mp_pass_text(mp);
                }
                mp_pop_condition_stack(mp);
            }
            break;
        case mp_input_command:
            if (cur_mod > 0) {
                mp->force_eof = 1;
            } else {
                mp_start_input(mp);
            }
            break;
        case mp_iteration_command:
            if (cur_mod == mp_end_for_code) {
                {
                    mp_error(
                        mp,
                        "Extra 'endfor'",
                        "I'm not currently working on a for loop, so I had better not try to end anything."
                    );
                }
            } else {
                mp_begin_iteration(mp);
            }
            break;
        case mp_repeat_loop_command:
            {
                while (token_state && (nloc == NULL)) {
                    mp_end_token_list(mp);
                }
                if (mp->loop_ptr == NULL) {
                    mp_error(
                        mp,
                        "Lost loop",
                        "I'm confused; after exiting from a loop, I still seem to want to repeat it. I'll\n"
                        "try to forget the problem."
                    );
                } else {
                    mp_resume_iteration(mp);
                }
            }
            break;
        case mp_exit_test_command:
            {
                mp_get_x_next(mp);
                mp_scan_expression(mp);
                if (mp->cur_exp.type != mp_boolean_type) {
                    do_boolean_error(mp);
                }
                if (number_greater(internal_value(mp_tracing_commands_internal), unity_t)) {
                    mp_show_cmd_mod(mp, mp_nullary_command, cur_exp_value_boolean);
                }
                if (cur_exp_value_boolean == mp_true_operation) {
                    if (mp->loop_ptr != NULL) {
                        mp_node p = NULL;
                        do {
                            if (file_state) {
                                mp_end_file_reading(mp);
                            } else {
                                if (token_type <= mp_loop_text) {
                                    p = nstart;
                                }
                                mp_end_token_list(mp);
                            }
                        } while (p == NULL);
                        if (p != mp->loop_ptr->info) {
                            mp_fatal_error(mp, "*** (loop confusion)");
                        }
                        mp_stop_iteration(mp);
                    } else if (cur_cmd == mp_semicolon_command) {
                        mp_error(
                            mp,
                            "No loop is in progress",
                            "Why say 'exitif' when there's nothing to exit from?"
                        );
                    } else {
                        mp_back_error(
                            mp,
                            "No loop is in progress",
                            "Why say 'exitif' when there's nothing to exit from?"
                        );
                    }
                } else if (cur_cmd != mp_semicolon_command) {
                    mp_back_error(
                        mp,
                        "Missing ';' has been inserted",
                        "After 'exitif <boolean exp>' I expect to see a semicolon. I shall pretend that\n"
                        "one was there."
                    );
                }
            }
            break;
        case mp_relax_command:
            break;
        case mp_expand_after_command:
            {
                mp_node p;
                get_t_next(mp);
                p = mp_cur_tok(mp);
                get_t_next(mp);
                if (cur_cmd < mp_min_command) {
                    mp_expand(mp);
                } else {
                    mp_back_input(mp);
                }
                mp_begin_token_list(mp, p, mp_backed_up_text);
            }
            break;
        case mp_scan_tokens_command:
            {
                mp_get_x_next(mp);
                mp_scan_primary(mp);
                if (mp->cur_exp.type != mp_string_type) {
                    mp_value new_expr;
                    memset(&new_expr, 0, sizeof(mp_value));
                    new_number(new_expr.data.n);
                    mp_disp_err(mp, NULL);
                    mp_back_error(
                        mp,
                        "Not a string",
                        "I'm going to flush this expression, since scantokens should be followed by a\n"
                        "known string."
                    );
                    mp_get_x_next(mp);
                    mp_flush_cur_exp(mp, new_expr);
                } else {
                    mp_back_input(mp);
                    if (cur_exp_str->len > 0) {
                        size_t k;
                        size_t j;
                        mp_value new_expr;
                        memset(&new_expr, 0, sizeof(mp_value));
                        new_number(new_expr.data.n);
                        mp_begin_file_reading(mp);
                        name = is_scantok;
                        k = mp->first + (size_t) cur_exp_str->len;
                        if (k >= mp->max_buf_stack) {
                            while (k >= mp->buf_size) {
                                mp_reallocate_buffer(mp, (mp->buf_size + (mp->buf_size / 4)));
                            }
                            mp->max_buf_stack = k + 1;
                        }
                        j = 0;
                        limit = (int) k;
                        while (mp->first < (size_t) limit) {
                            mp->buffer[mp->first] = *(cur_exp_str->str + j);
                            j++;
                            ++mp->first;
                        }
                        mp->buffer[limit] = '%';
                        mp->first = (size_t) (limit + 1);
                        loc = start;
                        mp_flush_cur_exp(mp, new_expr);
                    }
                }
            }
            break;
        case mp_runscript_command:
            {
               if (mp->extensions) {
                   mp_get_x_next(mp);
                   mp_scan_primary(mp);
                   switch (mp->cur_exp.type) {
                        case mp_string_type:
                            {
                                mp_back_input(mp);
                                if (cur_exp_str->len > 0) {
                                   check_script_result(mp, mp->run_script(mp, (const char*) cur_exp_str->str, cur_exp_str->len, 0));
                                }
                            }
                            break;
                        case mp_numeric_type:
                        case mp_known_type:
                            {
                                int n = 0 ;
                                mp_back_input(mp);
                                n = (int) number_to_scaled (cur_exp_value_number) / 65536;
                                if (n > 0) {
                                    check_script_result(mp, mp->run_script(mp, NULL, 0, n));
                                }
                            }
                            break;
                        default:
                            {
                                mp_value new_expr;
                                memset(&new_expr, 0, sizeof(mp_value));
                                new_number(new_expr.data.n);
                                mp_disp_err(mp, NULL);
                                mp_back_error(
                                    mp,
                                    "Not a string",
                                    "I'm going to flush this expression, since runscript should be followed by a known\n"
                                    "string or number."
                                );
                                mp_get_x_next(mp);
                                mp_flush_cur_exp(mp, new_expr);
                            }
                            break;
                    }
                }
            }
            break;
        case mp_maketext_command:
            {
                if (mp->extensions) {
                    mp_get_x_next(mp);
                    mp_scan_primary(mp);
                    if (mp->cur_exp.type == mp_string_type) {
                        mp_back_input(mp);
                        if (cur_exp_str->len > 0) {
                            check_script_result(mp, mp->make_text(mp, (const char*) cur_exp_str->str, cur_exp_str->len, 0));
                        }
                    } else {
                        mp_value new_expr;
                        memset(&new_expr, 0, sizeof(mp_value));
                        new_number(new_expr.data.n);
                        mp_disp_err(mp, NULL);
                        mp_back_error(
                            mp,
                            "Not a string",
                            "I'm going to flush this expression, since 'maketext' should be followed by a\n"
                            "known string."
                        );
                        mp_get_x_next(mp);
                        mp_flush_cur_exp(mp, new_expr);
                    }
                }
            }
            break;
        case mp_defined_macro_command:
            mp_macro_call(mp, cur_mod_node, NULL, cur_sym);
            break;
        default:
            break;
    };
    mp->expand_depth_count--;
}

void check_script_result (MP mp, char *s)
{
    if (s) {
        size_t size = strlen(s);
        if (size > 0) {
            size_t k ;
            mp_value new_expr;
            memset(&new_expr, 0, sizeof(mp_value));
            new_number(new_expr.data.n);
            mp_begin_file_reading(mp);
            name = is_scantok;
            mp->last = mp->first;
            k = mp->first + size;
            if (k >= mp->max_buf_stack) {
                while (k >= mp->buf_size) {
                    mp_reallocate_buffer(mp, (mp->buf_size + (mp->buf_size / 4)));
                }
                mp->max_buf_stack = k + 1;
            }
            limit = (int) k;
            memcpy((mp->buffer + mp->first), s, size);
            mp->buffer[limit] = '%';
            mp->first = (size_t) (limit + 1);
            loc = start;
            mp_flush_cur_exp(mp, new_expr);
        }
        lmt_memory_free(s);
    }
}

static void mp_get_x_next (MP mp)
{
    get_t_next(mp);
    if (cur_cmd < mp_min_command) {
        mp_node save_exp = mp_stash_cur_exp(mp);
        do {
            if (cur_cmd == mp_defined_macro_command) {
                mp_macro_call(mp, cur_mod_node, NULL, cur_sym);
            } else {
                mp_expand(mp);
            }
            get_t_next(mp);
        } while (cur_cmd < mp_min_command);
        mp_unstash_cur_exp(mp, save_exp);
    }
}

static void mp_macro_call (MP mp, mp_node def_ref, mp_node arg_list, mp_sym macro_name)
{
    int n;
    mp_node tail = 0;
    mp_sym l_delim = NULL;
    mp_sym r_delim = NULL;
    mp_node r = def_ref->link;
    mp_add_mac_ref(def_ref);
    if (arg_list == NULL) {
        n = 0;
    } else {
        n = 1;
        tail = arg_list;
        while (tail->link != NULL) {
            ++n;
            tail = tail->link;
        }
    }
    if (number_positive(internal_value(mp_tracing_macros_internal))) {
        mp_begin_diagnostic(mp);
        mp_print_ln(mp);
        mp_print_macro_name(mp, arg_list, macro_name);
        if (n == 3) {
            mp_print_str(mp, "@#");
        }
        mp_show_macro (mp, def_ref, NULL);
        if (arg_list != NULL) {
            mp_node p = arg_list;
            n = 0;
            do {
                mp_node q = (mp_node) mp_get_sym_sym(p);
                mp_print_arg(mp, q, n, 0, 0);
                ++n;
                p = p->link;
            } while (p != NULL);
        }
        mp_end_diagnostic(mp, 0);
    }
    set_cur_cmd(mp_comma_command + 1);
    while (r->name_type == mp_expr_operation || r->name_type == mp_suffix_operation || r->name_type == mp_text_operation) {
        if (cur_cmd != mp_comma_command) {
            mp_get_x_next(mp);
            if (cur_cmd != mp_left_delimiter_command) {
                char msg[256];
                mp_string sname;
                int selector = mp->selector;
                mp->selector = mp_new_string_selector;
                mp_print_macro_name(mp, arg_list, macro_name);
                sname = mp_make_string(mp);
                mp->selector = selector;
                mp_snprintf(msg, 256, "Missing argument to %s", mp_str(mp, sname));
                delete_str_ref(sname);
                if (r->name_type == mp_suffix_operation || r->name_type == mp_text_operation) {
                    mp_set_cur_exp_value_number(mp, &zero_t);
                    mp->cur_exp.type = mp_token_list_type;
                } else {
                    mp_set_cur_exp_value_number(mp, &zero_t);
                    mp->cur_exp.type = mp_known_type;
                }
                mp_back_error(
                    mp,
                    msg,
                    "That macro has more parameters than you thought. I'll continue by pretending that\n"
                    "each missing argument is either zero or null."
                );
                set_cur_cmd(mp_right_delimiter_command);
                goto FOUND;
            }
            l_delim = cur_sym;
            r_delim = equiv_sym(cur_sym);
        }
        if (r->name_type == mp_text_operation) {
            mp_scan_text_arg(mp, l_delim, r_delim);
        } else {
            mp_get_x_next(mp);
            if (r->name_type == mp_suffix_operation) {
                mp_scan_suffix(mp);
            } else {
                mp_scan_expression(mp);
            }
        }
        if ((cur_cmd != mp_comma_command) && ((cur_cmd != mp_right_delimiter_command) || (equiv_sym(cur_sym) != l_delim))) {
            switch (r->link->name_type) {
                case mp_expr_operation:
                case mp_suffix_operation:
                case mp_text_operation:
                    {
                        mp_back_error(
                            mp,
                            "Missing ',' has been inserted",
                            "I've finished reading a macro argument and am about to read another; the\n"
                            "arguments weren't delimited correctly."
                        );
                        set_cur_cmd(mp_comma_command);
                    }
                    break;
                default:
                    {
                        char msg[256];
                        mp_snprintf(msg, 256, "Missing '%s' has been inserted", mp_str(mp, text(r_delim)));
                        mp_back_error(mp, msg, "I've gotten to the end of the macro parameter list.");
                    }
                    break;
            }
        }
        FOUND:
        {
            mp_node p = mp_new_symbolic_node(mp);
            if (mp->cur_exp.type == mp_token_list_type) {
                mp_set_sym_sym(p, mp->cur_exp.data.node);
            } else {
                mp_set_sym_sym(p, mp_stash_cur_exp(mp));
            }
            if (number_positive(internal_value(mp_tracing_macros_internal))) {
                mp_begin_diagnostic(mp);
                mp_print_arg(mp, (mp_node) mp_get_sym_sym(p), n, mp_get_sym_info(r), r->name_type);
                mp_end_diagnostic(mp, 0);
            }
            if (arg_list == NULL) {
                arg_list = p;
            } else {
                tail->link = p;
            }
            tail = p;
            ++n;
        }

        r = r->link;
    }
    if (cur_cmd == mp_comma_command) {
        char msg[256];
        mp_string rname;
        int selector = mp->selector;
        mp->selector = mp_new_string_selector;
        mp_print_macro_name(mp, arg_list, macro_name);
        rname = mp_make_string(mp);
        mp->selector = selector;
        mp_snprintf(msg, 256, "Too many arguments to %s; Missing '%s' has been inserted",
        mp_str(mp, rname), mp_str(mp, text(r_delim)));
        delete_str_ref(rname);

        mp_error(
            mp,
            msg,
            "I'm going to assume that the comma I just read was a right delimiter, and then:\n"
            "I'll begin expanding the macro."
        );
    }
    if (mp_get_sym_info(r) != mp_general_macro) {
        if (mp_get_sym_info(r) < mp_text_macro) {
            mp_get_x_next(mp);
            if (mp_get_sym_info(r) != mp_suffix_macro) {
                if ((cur_cmd == mp_equals_command) || (cur_cmd == mp_assignment_command)) {
                    mp_get_x_next(mp);
                }
            }
        }
        switch (mp_get_sym_info(r)) {
            case mp_primary_macro:
                mp_scan_primary(mp);
                break;
            case mp_secondary_macro:
                mp_scan_secondary(mp);
                break;
            case mp_tertiary_macro:
                mp_scan_tertiary(mp);
                break;
            case mp_expr_macro:
                mp_scan_expression(mp);
                break;
            case mp_of_macro:
                {
                    mp_node p;
                    mp_scan_expression(mp);
                    p = mp_new_symbolic_node(mp);
                    mp_set_sym_sym(p, mp_stash_cur_exp(mp));
                    if (number_positive(internal_value(mp_tracing_macros_internal))) {
                        mp_begin_diagnostic(mp);
                        mp_print_arg(mp, (mp_node) mp_get_sym_sym(p), n, 0, 0);
                        mp_end_diagnostic(mp, 0);
                    }
                    if (arg_list == NULL) {
                        arg_list = p;
                    } else {
                        tail->link = p;
                    }
                    tail = p;
                    ++n;
                    if (cur_cmd != mp_of_command) {
                        char msg[256];
                        mp_string sname;
                        int selector = mp->selector;
                        mp->selector = mp_new_string_selector;
                        mp_print_macro_name(mp, arg_list, macro_name);
                        sname = mp_make_string(mp);
                        mp->selector = selector;
                        mp_snprintf(msg, 256, "Missing 'of' has been inserted for %s", mp_str(mp, sname));
                        delete_str_ref(sname);
                        mp_back_error(mp, msg, "I've got the first argument; will look now for the other.");
                    }
                    mp_get_x_next(mp);
                    mp_scan_primary(mp);
                }
                break;
            case mp_suffix_macro:
                {
                    if (cur_cmd != mp_left_delimiter_command) {
                        l_delim = NULL;
                    } else {
                        l_delim = cur_sym;
                        r_delim = equiv_sym(cur_sym);
                        mp_get_x_next(mp);
                    }
                    mp_scan_suffix(mp);
                    if (l_delim != NULL) {
                        if ((cur_cmd != mp_right_delimiter_command) || (equiv_sym(cur_sym) != l_delim)) {
                            char msg[256];
                            mp_snprintf(msg, 256, "Missing '%s' has been inserted", mp_str(mp, text(r_delim)));
                            mp_back_error(mp, msg, "I've gotten to the end of the macro parameter list.");
                        }
                        mp_get_x_next(mp);
                    }
                }
                break;
            case mp_text_macro:
                mp_scan_text_arg(mp, NULL, NULL);
                break;
        }
        mp_back_input(mp);
        {
            mp_node p = mp_new_symbolic_node(mp);
            if (mp->cur_exp.type == mp_token_list_type) {
                mp_set_sym_sym(p, mp->cur_exp.data.node);
            } else {
                mp_set_sym_sym(p, mp_stash_cur_exp(mp));
            }
            if (number_positive(internal_value(mp_tracing_macros_internal))) {
                mp_begin_diagnostic(mp);
                mp_print_arg(mp, (mp_node) mp_get_sym_sym(p), n, mp_get_sym_info(r), r->name_type);
                mp_end_diagnostic(mp, 0);
            }
            if (arg_list == NULL) {
                arg_list = p;
            } else {
                tail->link = p;
            }
            tail = p;
            ++n;
        }

    }
    r = r->link;

    while (token_state && (nloc == NULL)) {
        mp_end_token_list(mp);
    }
    if (mp->param_ptr + n > mp->max_param_stack) {
        mp->max_param_stack = mp->param_ptr + n;
        mp_check_param_size(mp, mp->max_param_stack);
    }
    mp_begin_token_list(mp, def_ref, mp_macro_text);
    name = macro_name ? text(macro_name) : NULL;
    nloc = r;
    if (n > 0) {
        mp_node p = arg_list;
        do {
            mp->param_stack[mp->param_ptr] = (mp_node) mp_get_sym_sym(p);
            ++mp->param_ptr;
            p = p->link;
        } while (p != NULL);
        mp_flush_node_list(mp, arg_list);
    }
}

static void mp_print_macro_name (MP mp, mp_node a, mp_sym n)
{
    if (n) {
        mp_print_mp_str(mp,text(n));
    } else {
        mp_node p = (mp_node) mp_get_sym_sym(a);
        if (p) {
            mp_node q = p;
            while (q->link != NULL) {
                q = q->link;
            }
            q->link = (mp_node) mp_get_sym_sym(a->link);
            mp_show_token_list(mp, p, NULL);
            q->link = NULL;
        } else {
            mp_print_mp_str(mp,text(mp_get_sym_sym((mp_node) mp_get_sym_sym(a->link))));
        }
    }
}

static void mp_print_arg (MP mp, mp_node q, int n, int b, int bb)
{
    if (q && q->link == MP_VOID) {
        mp_print_nl(mp, "(EXPR");
    } else if ((bb < mp_text_operation) && (b != mp_text_macro)) {
        mp_print_nl(mp, "(SUFFIX");
    } else {
        mp_print_nl(mp, "(TEXT");
    }
    mp_print_int(mp, n);
    mp_print_str(mp, ")<-");
    if (q && q->link == MP_VOID) {
        mp_print_exp(mp, q, 1);
    } else {
        mp_show_token_list(mp, q, NULL);
    }
}

void mp_scan_text_arg (MP mp, mp_sym l_delim, mp_sym r_delim)
{
    int balance = 1;
    mp_node p = mp->hold_head;
    mp->warning_info = l_delim;
    mp->scanner_status = mp_absorbing_state;
    mp->hold_head->link = NULL;
    while (1) {
        get_t_next(mp);
        if (l_delim == NULL) {
            if (mp_end_of_statement) {
                if (balance == 1) {
                    break;
                } else if (cur_cmd == mp_end_group_command) {
                    --balance;
                }
            } else if (cur_cmd == mp_begin_group_command) {
                ++balance;
            }
        } else {
            if (cur_cmd == mp_right_delimiter_command) {
                if (equiv_sym(cur_sym) == l_delim) {
                    --balance;
                    if (balance == 0) {
                        break;
                    }
                }
            } else if (cur_cmd == mp_left_delimiter_command) {
                if (equiv_sym(cur_sym) == r_delim) {
                    ++balance;
                }
            }
        }
        p->link = mp_cur_tok(mp);
        p = p->link;
    }
    mp_set_cur_exp_node(mp, mp->hold_head->link);
    mp->cur_exp.type = mp_token_list_type;
    mp->scanner_status = mp_normal_state;
}

static void mp_stack_argument (MP mp, mp_node p)
{
    if (mp->param_ptr == mp->max_param_stack) {
        ++mp->max_param_stack;
        mp_check_param_size(mp, mp->max_param_stack);
    }
    mp->param_stack[mp->param_ptr] = p;
    ++mp->param_ptr;
}

static mp_node mp_get_if_node (MP mp) {
    mp_if_node p = (mp_if_node) mp_allocate_node(mp, sizeof(mp_if_node_data));
    p->type = mp_if_node_type;
    return (mp_node) p;
}

void mp_pass_text (MP mp)
{
    int level = 0;
    mp->scanner_status = mp_skipping_state;
    mp->warning_line = mp_true_line(mp);
    while (1) {
        get_t_next(mp);
        if (cur_cmd <= mp_fi_or_else_command) {
            if (cur_cmd < mp_fi_or_else_command) {
                ++level;
            } else if (level == 0) {
                break;
            } else if (cur_mod == mp_fi_code) {
                --level;
            }
        } else {
            if (cur_cmd == mp_string_command) {
                delete_str_ref(cur_mod_str);
            }
        }
    }
    mp->scanner_status = mp_normal_state;
}

static void mp_push_condition_stack (MP mp)
{
    mp_node p = mp_get_if_node(mp);
    p->link = mp->cond_ptr;
    p->type = (int) mp->if_limit;
    p->name_type = mp->cur_if;
    mp_if_line_field(p) = mp->if_line;
    mp->cond_ptr = p;
    mp->if_limit = mp_if_code;
    mp->if_line = mp_true_line(mp);
    mp->cur_if = mp_if_code;
}

static void mp_pop_condition_stack (MP mp)
{
    mp_node p = mp->cond_ptr;
    mp->if_line = mp_if_line_field(p);
    mp->cur_if = p->name_type;
    mp->if_limit = p->type;
    mp->cond_ptr = p->link;
    mp_free_node(mp, p, sizeof(mp_if_node_data));
}

static void mp_change_if_limit (MP mp, int l, mp_node p)
{
    if (p == mp->cond_ptr) {
        mp->if_limit = l;
    } else {
        mp_node q = mp->cond_ptr;
        while (1) {
            if (q == NULL) {
                mp_confusion(mp, "if");
                return;
            } else if (q->link == p) {
                q->type = l;
                return;
            } else {
                q = q->link;
            }
        }
    }
}

static void mp_check_colon (MP mp)
{
    if (cur_cmd != mp_colon_command) {
        mp_back_error(
            mp,
            "Missing ':' has been inserted",
            "There should've been a colon after the condition. I shall pretend that one was\n"
            "there."
        );
    }
}

void mp_conditional (MP mp)
{
    mp_node save_cond_ptr;
    int new_if_limit;
    mp_push_condition_stack(mp);
    save_cond_ptr = mp->cond_ptr;
  RESWITCH:
    mp_get_x_next(mp);
    mp_scan_expression(mp);
    if (mp->cur_exp.type != mp_boolean_type) {
        do_boolean_error(mp);
    }
    new_if_limit = mp_else_if_code;
    if (number_greater(internal_value(mp_tracing_commands_internal), unity_t)) {
        mp_begin_diagnostic(mp);
        mp_print_str(mp, cur_exp_value_boolean == mp_true_operation ? "{true}" : "{false}");
        mp_end_diagnostic(mp, 0);
    }
  FOUND:
    mp_check_colon(mp);
    if (cur_exp_value_boolean == mp_true_operation) {
        mp_change_if_limit (mp, (int) new_if_limit, save_cond_ptr);
        return;
    }
    while (1) {
        mp_pass_text(mp);
        if (mp->cond_ptr == save_cond_ptr) {
            goto DONE;
        } else if (cur_mod == mp_fi_code) {
            mp_pop_condition_stack(mp);
        }
    }
  DONE:
    mp->cur_if = (int) cur_mod;
    mp->if_line = mp_true_line(mp);
    if (cur_mod == mp_fi_code) {
        mp_pop_condition_stack(mp);
    } else if (cur_mod == mp_else_if_code) {
        goto RESWITCH;
    } else {
        mp_set_cur_exp_value_boolean(mp, mp_true_operation);
        new_if_limit = mp_fi_code;
        mp_get_x_next(mp);
        goto FOUND;
    }
}

static void mp_bad_for (MP mp, const char *s)
{
    char msg[256];
    mp_value new_expr;
    memset(&new_expr, 0, sizeof(mp_value));
    new_number(new_expr.data.n);
    mp_disp_err(mp, NULL);
    mp_snprintf(msg, 256, "Improper %s has been replaced by 0", s);
    mp_back_error(
        mp,
        msg,
        "When you say 'for x=a step b until c', the initial value 'a' and the step size\n"
        "'b' and the final value 'c' must have known numeric values. I'm zeroing this one.\n"
        "Proceed, with fingers crossed."
    );
    mp_get_x_next(mp);
    mp_flush_cur_exp(mp, new_expr);
}

void mp_begin_iteration (MP mp)
{
    mp_node q;
    mp_sym n = cur_sym;
    mp_subst_list_item *p = NULL;
    int m = cur_mod;
    mp_loop_data *s = mp_memory_allocate(sizeof(mp_loop_data));
    s->type = NULL;
    s->list = NULL;
    s->info = NULL;
    s->list_start = NULL;
    s->link = NULL;
    s->var = NULL;
    s->point = NULL;
    new_number(s->value);
    new_number(s->old_value);
    new_number(s->step_size);
    new_number(s->final_value);
    if (m == mp_start_forever_code) {
        s->type = MP_VOID;
        mp_get_x_next(mp);
    } else {
        mp_get_symbol(mp);
        p = mp_memory_allocate(sizeof(mp_subst_list_item));
        p->link = NULL;
        p->info = cur_sym;
        s->var  = cur_sym;
        p->info_mod = cur_sym_mod;
        p->value_data = 0;
        if (m == mp_start_for_code) {
            p->value_mod = mp_expr_operation;
        } else {
            p->value_mod = mp_suffix_operation;
        }
        mp_get_x_next(mp);
        if (p->value_mod == mp_expr_operation && cur_cmd == mp_within_command) {
            mp_get_x_next(mp);
            mp_scan_expression(mp);
            if (mp->cur_exp.type == mp_path_type) {
                number_clone(s->value, zero_t);
                number_clone(s->old_value, zero_t);
                number_clone(s->step_size, unity_t);
                {
                    mp_knot p = cur_exp_knot;

                    int l = 0;
                    while (1) {
                        mp_knot n = mp_next_knot(p);
                        if (n == cur_exp_knot) {
                            s->point = p;
                            set_number_from_int(s->final_value, l);
                            break;
                        } else {
                            p = n;
                            ++l;
                        }
                    }
                }
                s->type = MP_PROGRESSION_FLAG;
                s->list = mp_new_symbolic_node(mp);
                s->list_start = s->list;
                q = s->list;
            } else {
                if (mp->cur_exp.type != mp_picture_type) {
                    mp_value new_expr;
                    memset(&new_expr, 0, sizeof(mp_value));
                    new_number(new_expr.data.n);
                    new_expr.data.node = (mp_node) mp_get_edge_header_node(mp);
                    mp_disp_err(mp, NULL);
                    mp_back_error(
                        mp,
                        "Improper iteration spec has been replaced by nullpicture",
                        "When you say 'for x in p', p must be a known picture."
                    );
                    mp_get_x_next(mp);
                    mp_flush_cur_exp(mp, new_expr);
                    mp_init_edges(mp, (mp_edge_header_node) mp->cur_exp.data.node);
                    mp->cur_exp.type = mp_picture_type;
                }
                s->type = mp->cur_exp.data.node;
                mp->cur_exp.type = mp_vacuous_type;
                q = mp_edge_list(mp->cur_exp.data.node)->link;
                if (q != NULL && mp_is_start_or_stop (q) && mp_skip_1component(mp, q) == NULL) {
                    q = q->link;
                }
                s->list = q;
            }
        } else {
            if ((cur_cmd != mp_equals_command) && (cur_cmd != mp_assignment_command)) {
                mp_back_error(
                    mp,
                    "Missing '=' has been inserted",
                    "The next thing in this loop should have been '=' or ':='. But don't worry; I'll\n"
                    "pretend that an equals sign was present, and I'll look for the values next."
                );
            }
            s->type = NULL;
            s->list = mp_new_symbolic_node(mp);
            s->list_start = s->list;
            q = s->list;
            do {
                mp_get_x_next(mp);
                if (m != mp_start_for_code) {
                    mp_scan_suffix(mp);
                } else {
                    if (cur_cmd >= mp_colon_command && cur_cmd <= mp_comma_command) {
                        goto CONTINUE;
                    }
                    mp_scan_expression(mp);
                    if (cur_cmd == mp_step_command && q == s->list) {
                        {
                            if (mp->cur_exp.type != mp_known_type) {
                                mp_bad_for (mp, "initial value");
                            }
                            number_clone(s->value, cur_exp_value_number);
                            number_clone(s->old_value, cur_exp_value_number);
                            mp_get_x_next(mp);
                            mp_scan_expression(mp);
                            if (mp->cur_exp.type != mp_known_type) {
                                mp_bad_for (mp, "step size");
                            }
                            number_clone(s->step_size, cur_exp_value_number);
                            if (cur_cmd != mp_until_command) {
                                mp_back_error(
                                    mp,
                                    "Missing 'until' has been inserted",
                                    "I assume you meant to say 'until' after 'step'. So I'll look for the final value\n"
                                    "and colon next."
                                );
                            }
                            mp_get_x_next(mp);
                            mp_scan_expression(mp);
                            if (mp->cur_exp.type != mp_known_type) {
                                mp_bad_for (mp, "final value");
                            }
                            number_clone(s->final_value, cur_exp_value_number);
                            s->type = MP_PROGRESSION_FLAG;
                            break;
                        }
                    }
                    mp_set_cur_exp_node(mp, mp_stash_cur_exp(mp));
                }
                q->link = mp_new_symbolic_node(mp);
                q = q->link;
                mp_set_sym_sym(q, mp->cur_exp.data.node);
                if (m == mp_start_for_code) {
                    q->name_type = mp_expr_operation;
                } else if (m == mp_start_forsuffixes_code) {
                    q->name_type = mp_suffix_operation;
                }
                mp->cur_exp.type = mp_vacuous_type;
              CONTINUE:
              ;
            } while (cur_cmd == mp_comma_command);
        }
    }
    if (cur_cmd != mp_colon_command) {
        mp_back_error(
            mp,
            "Missing ':' has been inserted",
            "The next thing in this loop should have been a ':'. So I'll pretend that a colon\n"
            "was present; everything from here to 'endfor' will be iterated."
        );
    }
    q = mp_new_symbolic_node(mp);
    mp_set_sym_sym(q, mp->frozen_repeat_loop);
    mp->scanner_status = mp_loop_defining_state;
    mp->warning_info = n;
    s->info = mp_scan_toks(mp, mp_iteration_command, p, q, 0);
    mp->scanner_status = mp_normal_state;
    s->link = mp->loop_ptr;
    mp->loop_ptr = s;

    mp_resume_iteration(mp);
}

void mp_resume_iteration (MP mp)
{
    mp_node p, q;
    p = mp->loop_ptr->type;
    if (p == MP_PROGRESSION_FLAG) {
        if ((number_positive(mp->loop_ptr->step_size) && number_greater(mp->loop_ptr->value, mp->loop_ptr->final_value))
         || (number_negative(mp->loop_ptr->step_size) && number_less   (mp->loop_ptr->value, mp->loop_ptr->final_value))) {
            mp_stop_iteration(mp);
            return;
        }
        mp_set_cur_exp_value_number(mp, &(mp->loop_ptr->value));
        mp->cur_exp.type = mp_known_type;
        q = mp_stash_cur_exp(mp);
        number_clone(mp->loop_ptr->old_value, cur_exp_value_number);
        set_number_from_addition(mp->loop_ptr->value, cur_exp_value_number, mp->loop_ptr->step_size);
        if (number_positive(mp->loop_ptr->step_size) && number_less(mp->loop_ptr->value, cur_exp_value_number)) {
            if (number_positive(mp->loop_ptr->final_value)) {
                number_clone(mp->loop_ptr->value, mp->loop_ptr->final_value);
                number_add_scaled(mp->loop_ptr->final_value, -1);
            } else {
                number_clone(mp->loop_ptr->value, mp->loop_ptr->final_value);
                number_add_scaled(mp->loop_ptr->value, 1);
            }
        } else if (number_negative(mp->loop_ptr->step_size) && number_greater(mp->loop_ptr->value, cur_exp_value_number)) {
            if (number_negative(mp->loop_ptr->final_value)) {
                number_clone(mp->loop_ptr->value, mp->loop_ptr->final_value);
                number_add_scaled(mp->loop_ptr->final_value, 1);
            } else {
                number_clone(mp->loop_ptr->value, mp->loop_ptr->final_value);
                number_add_scaled(mp->loop_ptr->value, -1);
            }
        }
        if (mp->loop_ptr->point != NULL) {
            mp->loop_ptr->point = mp_next_knot(mp->loop_ptr->point);
        }
    } else if (p == NULL) {
        p = mp->loop_ptr->list;
        if (p != NULL && p == mp->loop_ptr->list_start) {
            q = p;
            p = p->link;
            mp_free_symbolic_node(mp, q);
            mp->loop_ptr->list = p;
        }
        if (p == NULL) {
            mp_stop_iteration(mp);
            return;
        }
        mp->loop_ptr->list = p->link;
        q = (mp_node) mp_get_sym_sym(p);
        if (q) {
            number_clone(mp->loop_ptr->old_value, q->data.n);
        }
        mp_free_symbolic_node(mp, p);
    } else if (p == MP_VOID) {
        mp_begin_token_list(mp, mp->loop_ptr->info, mp_forever_text);
        return;
    } else {
        q = mp->loop_ptr->list;
        if (q == NULL) {
            goto NOT_FOUND;
        } else if (! mp_is_start_or_stop(q)) {
            q = q->link;
        } else if (! mp_is_stop(q)) {
            q = mp_skip_1component(mp, q);
        } else {
            goto NOT_FOUND;
        }
        mp_set_cur_exp_node(mp, (mp_node) mp_copy_objects (mp, mp->loop_ptr->list, q));
        mp_init_bbox(mp, (mp_edge_header_node) cur_exp_node);
        mp->cur_exp.type = mp_picture_type;
        mp->loop_ptr->list = q;
        q = mp_stash_cur_exp(mp);
    }
    mp_begin_token_list(mp, mp->loop_ptr->info, mp_loop_text);
    mp_stack_argument(mp, q);
    if (number_greater(internal_value(mp_tracing_commands_internal), unity_t)) {
        mp_begin_diagnostic(mp);
        mp_print_nl(mp, "{loop value=");
        if ((q != NULL) && (q->link == MP_VOID)) {
            mp_print_exp(mp, q, 1);
        } else {
            mp_show_token_list(mp, q, NULL);
        }
        mp_print_chr(mp, '}');
        mp_end_diagnostic(mp, 0);
    }
    return;
  NOT_FOUND:
    mp_stop_iteration(mp);
}

void mp_stop_iteration (MP mp)
{
    mp_node p = mp->loop_ptr->type;
    if (p == MP_PROGRESSION_FLAG) {
        mp_free_symbolic_node(mp, mp->loop_ptr->list);
        if (mp->loop_ptr->point) {
            mp_toss_knot_list(mp, mp->loop_ptr->point);
        }
    } else if (p == NULL) {
        mp_node q = mp->loop_ptr->list;
        while (q != NULL) {
            p = (mp_node) mp_get_sym_sym(q);
            if (p != NULL) {
                if (p->link == MP_VOID) {
                    mp_recycle_value(mp, p);
                    mp_free_value_node(mp, p);
                } else {
                    mp_flush_token_list(mp, p);
                }
            }
            p = q;
            q = q->link;
            mp_free_symbolic_node(mp, p);
        }
    } else if (p > MP_PROGRESSION_FLAG) {
        mp_delete_edge_ref(mp, p);
    }
    {
        mp_loop_data *tmp = mp->loop_ptr;
        mp->loop_ptr = tmp->link;
        mp_flush_token_list(mp, tmp->info);
        free_number(tmp->value);
        free_number(tmp->step_size);
        free_number(tmp->final_value);
        mp_memory_free(tmp);
    }
}

void mp_begin_name (MP mp)
{
    mp_memory_free(mp->cur_name);
    mp->cur_name = NULL;
    mp->quoted_filename = 0;
}

int mp_more_name (MP mp, unsigned char c)
{
    if (c == '"') {
        mp->quoted_filename = ! mp->quoted_filename;
    } else if ((c == ' ' || c == '\t') && (mp->quoted_filename == 0)) {
        return 0;
    } else {
        mp_str_room(mp, 1);
        mp_append_char(mp, c);
    }
    return 1;
}

void mp_end_name (MP mp)
{
    mp->cur_name = mp_memory_allocate((size_t) (mp->cur_length + 1) * sizeof(char));
    (void) memcpy(mp->cur_name, (char *) (mp->cur_string), mp->cur_length);
    mp->cur_name[mp->cur_length] = 0;
    mp_reset_cur_string(mp);
}

void mp_pack_file_name (MP mp, const char *n)
{
    mp_memory_free(mp->name_of_file);
    mp->name_of_file = mp_strdup(n);
}

static mp_string mp_make_name_string (MP mp)
{
    int name_length = (int) strlen(mp->name_of_file);
    mp_str_room(mp, name_length);
    for (int k = 0; k < name_length; k++) {
        mp_append_char(mp, (unsigned char) mp->name_of_file[k]);
    }
    return mp_make_string(mp);
}

static void mp_scan_file_name (MP mp)
{
    mp_begin_name(mp);
    while (mp->buffer[loc] == ' ') {
        ++loc;
    }
    while (1) {
        if ((mp->buffer[loc] == ';') || (mp->buffer[loc] == '%')) {
            break;
        } else if (! mp_more_name(mp, mp->buffer[loc])) {
            break;
        } else {
            ++loc;
        }
    }
    mp_end_name(mp);
}

static void mp_ptr_scan_file (MP mp, char *s)
{
    char *p = s;
    char *q = p + strlen(s);
    mp_begin_name(mp);
    while (p < q) {
        if (! mp_more_name(mp, (unsigned char) (*p))) {
            break;
        } else {
            p++;
        }
    }
    mp_end_name(mp);
}

void mp_start_input (MP mp)
{
    while (token_state && (nloc == NULL)) {
        mp_end_token_list(mp);
    }
    if (token_state) {
        mp_error(
            mp,
            "File names can't appear within macros",
            "Sorry ... I've converted what follows to tokens, possibly garbaging the name you\n"
            "gave. Please delete the tokens and insert the name again."
        );
    }
    if (file_state) {
        mp_scan_file_name(mp);
    } else {
        mp_memory_free(mp->cur_name);
        mp->cur_name = mp_strdup("");
    }
    mp_begin_file_reading(mp);
    mp_pack_file_name(mp, mp->cur_name);
    if (mp_open_in(mp, &cur_file, mp_filetype_program)) {
        char *fname = NULL;
        name = mp_make_name_string(mp);
        fname = mp_strdup(mp->name_of_file);
        if (mp->interaction < mp_silent_mode) {
            if ((mp->term_offset > 0) || (mp->file_offset > 0)) {
                mp_print_chr(mp, ' ');
            }
            mp_print_chr(mp, '(');
            ++mp->open_parens;
            mp_print_str(mp, fname);
        }
        mp_memory_free(fname);
        update_terminal();
        mp_flush_string(mp, name);
        name = mp_rts(mp, mp->cur_name);
        mp_memory_free(mp->cur_name);
        mp->cur_name = NULL;

        line = 1;
        mp_input_ln(mp, cur_file);
        mp_firm_up_the_line(mp);
        mp->buffer[limit] = '%';
        mp->first = (size_t) (limit + 1);
        loc = start;
    } else {
        mp_fatal_error(mp, "invalid input file");
        mp_end_file_reading(mp);
    }
}

static int mp_start_read_input (MP mp, char *s, int n)
{
    mp_ptr_scan_file(mp, s);
    mp_pack_file_name(mp, mp->cur_name);
    mp_begin_file_reading(mp);
    if (! mp_open_in(mp, &mp->rd_file[n], mp_filetype_text + n)) {
        mp_end_file_reading(mp);
        return 0;
    } else if (! mp_input_ln(mp, mp->rd_file[n])) {
        (mp->close_file)(mp, mp->rd_file[n]);
        mp_end_file_reading(mp);
        return 0;
    } else {
        mp->rd_fname[n] = mp_strdup(s);
        return 1;
    }
}

void mp_open_write_file (MP mp, char *s, int n)
{
    mp_ptr_scan_file(mp, s);
    mp_pack_file_name(mp, mp->cur_name);
    if (mp_open_out(mp, &mp->wr_file[n], mp_filetype_text + n)) {
        mp->wr_fname[n] = mp_strdup(s);
    } else {
        mp_fatal_error(mp, "invalid write file");
    }
}

void mp_set_cur_exp_node (MP mp, mp_node n)
{
    if (cur_exp_str) {
        delete_str_ref(cur_exp_str);
    }
    cur_exp_node = n;
    cur_exp_str = NULL;
    cur_exp_knot = NULL;
    set_number_to_zero(mp->cur_exp.data.n);
}

void mp_set_cur_exp_knot (MP mp, mp_knot n)
{
    if (cur_exp_str) {
        delete_str_ref(cur_exp_str);
    }
    cur_exp_knot = n;
    cur_exp_node = NULL;
    cur_exp_str = NULL;
    set_number_to_zero(mp->cur_exp.data.n);
}

void mp_set_cur_exp_value_boolean (MP mp, int b)
{
    if (cur_exp_str) {
        delete_str_ref(cur_exp_str);
    }
    set_number_from_boolean(mp->cur_exp.data.n, b);
    cur_exp_node = NULL;
    cur_exp_str = NULL;
    cur_exp_knot = NULL;
}

void mp_set_cur_exp_value_scaled (MP mp, int s)
{
    if (cur_exp_str) {
        delete_str_ref(cur_exp_str);
    }
    set_number_from_scaled(mp->cur_exp.data.n, s);
    cur_exp_node = NULL;
    cur_exp_str = NULL;
    cur_exp_knot = NULL;
}

void mp_set_cur_exp_value_number (MP mp, mp_number *n)
{
    if (cur_exp_str) {
        delete_str_ref(cur_exp_str);
    }
    number_clone(mp->cur_exp.data.n, *n);
    cur_exp_node = NULL;
    cur_exp_str = NULL;
    cur_exp_knot = NULL;
}

void mp_set_cur_exp_str (MP mp, mp_string s)
{
    if (cur_exp_str) {
        delete_str_ref(cur_exp_str);
    }
    cur_exp_str = s;
    add_str_ref(cur_exp_str);
    cur_exp_node = NULL;
    cur_exp_knot = NULL;
    set_number_to_zero(mp->cur_exp.data.n);
}

static mp_node mp_stash_cur_exp (MP mp)
{
    mp_node p;
    mp_variable_type exp_type = mp->cur_exp.type;
    switch (exp_type) {
        case mp_unknown_boolean_type:
        case mp_unknown_string_type:
        case mp_unknown_pen_type:
        case mp_unknown_nep_type:
        case mp_unknown_path_type:
        case mp_unknown_picture_type:
        case mp_transform_type:
        case mp_color_type:
        case mp_pair_type:
        case mp_dependent_type:
        case mp_proto_dependent_type:
        case mp_independent_type:
        case mp_cmykcolor_type:
            p = cur_exp_node;
            break;
        default:
            p = mp_new_value_node(mp);
            p->name_type = mp_capsule_operation;
            p->type = mp->cur_exp.type;
            mp_set_value_number(p, cur_exp_value_number);
            if (cur_exp_str)    {
                mp_set_value_str(p, cur_exp_str);
            } else if (cur_exp_knot) {
                mp_set_value_knot(p, cur_exp_knot);
            } else if (cur_exp_node) {
                mp_set_value_node(p, cur_exp_node);
            }
            break;
    }
    mp->cur_exp.type = mp_vacuous_type;
    p->link = MP_VOID;
    return p;
}

void mp_unstash_cur_exp (MP mp, mp_node p)
{
    mp->cur_exp.type = p->type;
    switch (mp->cur_exp.type) {
        case mp_unknown_boolean_type:
        case mp_unknown_string_type:
        case mp_unknown_pen_type:
        case mp_unknown_nep_type:
        case mp_unknown_path_type:
        case mp_unknown_picture_type:
        case mp_transform_type:
        case mp_color_type:
        case mp_pair_type:
        case mp_dependent_type:
        case mp_proto_dependent_type:
        case mp_independent_type:
        case mp_cmykcolor_type:
            mp_set_cur_exp_node(mp, p);
            break;
        case mp_token_list_type:
            mp_set_cur_exp_node(mp, mp_get_value_node(p));
            mp_free_value_node(mp, p);
            break;
        case mp_path_type:
        case mp_pen_type:
        case mp_nep_type:
            mp_set_cur_exp_knot(mp, mp_get_value_knot(p));
            mp_free_value_node(mp, p);
            break;
        case mp_string_type:
            mp_set_cur_exp_str(mp, mp_get_value_str(p));
            mp_free_value_node(mp, p);
            break;
        case mp_picture_type:
            mp_set_cur_exp_node(mp, mp_get_value_node(p));
            mp_free_value_node(mp, p);
            break;
        case mp_boolean_type:
        case mp_known_type:
            mp_set_cur_exp_value_number(mp, &(mp_get_value_number(p)));
            mp_free_value_node(mp, p);
            break;
        default:
            mp_set_cur_exp_value_number(mp, &(mp_get_value_number(p)));
            if (mp_get_value_knot(p)) {
                mp_set_cur_exp_knot(mp, mp_get_value_knot(p));
            } else if (mp_get_value_node(p)) {
                mp_set_cur_exp_node(mp, mp_get_value_node(p));
            } else if (mp_get_value_str(p)) {
                mp_set_cur_exp_str(mp, mp_get_value_str(p));
            }
            mp_free_value_node(mp, p);
            break;
    }
}

void mp_print_exp (MP mp, mp_node p, int verbosity)
{
    int restore_cur_exp;
    mp_variable_type t;
    mp_number vv;
    mp_node v = NULL;
    new_number(vv);
    if (p != NULL) {
        restore_cur_exp = 0;
    } else {
        p = mp_stash_cur_exp(mp);
        restore_cur_exp = 1;
    }
    t = p->type;
    if (t < mp_dependent_type) {
        if (t != mp_vacuous_type && t != mp_known_type && mp_get_value_node(p) != NULL) {
            v = mp_get_value_node(p);
        } else {
            number_clone(vv, mp_get_value_number(p));
        }
    } else if (t < mp_independent_type) {
        v = (mp_node) mp_get_dep_list((mp_value_node) p);
    }
    switch (t) {
        case mp_vacuous_type:
            mp_print_str(mp, "vacuous");
            break;
        case mp_boolean_type:
            mp_print_str(mp, number_to_boolean(vv) == mp_true_operation ? "true": "false");
            break;
        case mp_unknown_boolean_type:
        case mp_unknown_string_type:
        case mp_unknown_pen_type:
        case mp_unknown_nep_type:
        case mp_unknown_path_type:
        case mp_unknown_picture_type:
        case mp_numeric_type:
            {
                {
                    mp_print_type(mp, t);
                    if (v != NULL) {
                        mp_print_chr(mp, ' ');
                        while ((v->name_type == mp_capsule_operation) && (v != p)) {
                            v = mp_get_value_node(v);
                        }
                        mp_print_variable_name(mp, v);
                    };
                }
            }
            break;
        case mp_string_type:
            mp_print_chr(mp, '"');
            mp_print_mp_str(mp, mp_get_value_str(p));
            mp_print_chr(mp, '"');
            break;
        case mp_pen_type:
        case mp_nep_type:
        case mp_path_type:
        case mp_picture_type:
            {
                if (verbosity <= 1) {
                    mp_print_type(mp, t);
                } else {
                    if (mp->selector == mp_term_and_log_selector)
                        if (number_nonpositive(internal_value(mp_tracing_online_internal))) {
                            mp->selector = mp_term_only_selector;
                            mp_print_type(mp, t);
                            mp_print_str(mp, " (see the transcript file)");
                            mp->selector = mp_term_and_log_selector;
                        };
                    switch (t) {
                        case mp_pen_type:
                        case mp_nep_type:
                            mp_print_pen(mp, mp_get_value_knot(p), "", 0);
                            break;
                        case mp_path_type:
                            mp_print_path(mp, mp_get_value_knot(p), "", 0);
                            break;
                        case mp_picture_type:
                            mp_print_edges(mp, v, "", 0);
                            break;
                        default:
                            break;
                    }
                }
            }
            break;
        case mp_transform_type:
            if (number_zero(vv) && v == NULL) {
                mp_print_type(mp, t);
            } else {
                mp_print_chr(mp, '(');
                mp_print_big_node(mp, mp_tx_part(v), verbosity);
                mp_print_chr(mp, ',');
                mp_print_big_node(mp, mp_ty_part(v), verbosity);
                mp_print_chr(mp, ',');
                mp_print_big_node(mp, mp_xx_part(v), verbosity);
                mp_print_chr(mp, ',');
                mp_print_big_node(mp, mp_xy_part(v), verbosity);
                mp_print_chr(mp, ',');
                mp_print_big_node(mp, mp_yx_part(v), verbosity);
                mp_print_chr(mp, ',');
                mp_print_big_node(mp, mp_yy_part(v), verbosity);
                mp_print_chr(mp, ')');
            }
            break;
        case mp_color_type:
            if (number_zero(vv) && v == NULL) {
                mp_print_type(mp, t);
            } else {
                mp_print_chr(mp, '(');
                mp_print_big_node(mp, mp_red_part(v), verbosity);
                mp_print_chr(mp, ',');
                mp_print_big_node(mp, mp_green_part(v), verbosity);
                mp_print_chr(mp, ',');
                mp_print_big_node(mp, mp_blue_part(v), verbosity);
                mp_print_chr(mp, ')');
            }
            break;
        case mp_pair_type:
            if (number_zero(vv) && v == NULL) {
                mp_print_type(mp, t);
            } else {
                mp_print_chr(mp, '(');
                mp_print_big_node(mp, mp_x_part(v), verbosity);
                mp_print_chr(mp, ',');
                mp_print_big_node(mp, mp_y_part(v), verbosity);
                mp_print_chr(mp, ')');
            }
            break;
        case mp_cmykcolor_type:
            if (number_zero(vv) && v == NULL) {
                mp_print_type(mp, t);
            } else {
                mp_print_chr(mp, '(');
                mp_print_big_node(mp, mp_cyan_part(v), verbosity);
                mp_print_chr(mp, ',');
                mp_print_big_node(mp, mp_magenta_part(v), verbosity);
                mp_print_chr(mp, ',');
                mp_print_big_node(mp, mp_yellow_part(v), verbosity);
                mp_print_chr(mp, ',');
                mp_print_big_node(mp, mp_black_part(v), verbosity);
                mp_print_chr(mp, ')');
            }
            break;
        case mp_known_type:
            print_number(vv);
            break;
        case mp_dependent_type:
        case mp_proto_dependent_type:
            mp_print_dp(mp, t, (mp_value_node) v, verbosity);
            break;
        case mp_independent_type:
            mp_print_variable_name(mp, p);
            break;
        default:
            mp_confusion(mp, "expression");
            break;
    }
    if (restore_cur_exp) {
        mp_unstash_cur_exp(mp, p);
    }
    free_number(vv);
}

void mp_print_big_node (MP mp, mp_node v, int verbosity)
{
    switch (v->type) {
        case mp_known_type:
            print_number(mp_get_value_number(v));
            break;
        case mp_independent_type:
            mp_print_variable_name(mp, v);
            break;
        default:
            mp_print_dp(mp, v->type, (mp_value_node) mp_get_dep_list((mp_value_node) v), verbosity);
            break;
    }
}

static void mp_print_dp (MP mp, int t, mp_value_node p, int verbosity)
{
    mp_value_node q = (mp_value_node) p->link;
    if ((mp_get_dep_info(q) == NULL) || (verbosity > 0)) {
        mp_print_dependency(mp, p, t);
    } else {
        mp_print_str(mp, "linearform");
    }
}

void mp_disp_err (MP mp, mp_node p)
{
    if (mp->interaction >= mp_error_stop_mode) {
        wake_up_terminal();
    }
    mp_print_nl(mp, "<error> ");
    mp_print_exp(mp, p, 1);
}

void mp_flush_cur_exp (MP mp, mp_value v)
{
    if (is_number(mp->cur_exp.data.n)) {
        free_number(mp->cur_exp.data.n);
    }
    switch (mp->cur_exp.type) {
        case mp_unknown_boolean_type:
        case mp_unknown_string_type:
        case mp_unknown_pen_type:
        case mp_unknown_nep_type:
        case mp_unknown_path_type:
        case mp_unknown_picture_type:
        case mp_transform_type:
        case mp_color_type:
        case mp_pair_type:
        case mp_dependent_type:
        case mp_proto_dependent_type:
        case mp_independent_type:
        case mp_cmykcolor_type:
            mp_recycle_value(mp, cur_exp_node);
            mp_free_value_node(mp, cur_exp_node);
            break;
        case mp_string_type:
            delete_str_ref(cur_exp_str);
            break;
        case mp_pen_type:
        case mp_nep_type:
        case mp_path_type:
            mp_toss_knot_list(mp, cur_exp_knot);
            break;
        case mp_picture_type:
            mp_delete_edge_ref(mp, cur_exp_node);
            break;
        default:
            break;
    }
    mp->cur_exp = v;
    mp->cur_exp.type = mp_known_type;
}

static void mp_recycle_value (MP mp, mp_node p)
{
    if (p != NULL && p != MP_VOID) {
        mp_variable_type t = p->type;
        switch (t) {
            case mp_vacuous_type:
            case mp_boolean_type:
            case mp_known_type:
            case mp_numeric_type:
                break;
            case mp_unknown_boolean_type:
            case mp_unknown_string_type:
            case mp_unknown_pen_type:
            case mp_unknown_nep_type:
            case mp_unknown_path_type:
            case mp_unknown_picture_type:
                mp_ring_delete (mp, p);
                break;
            case mp_string_type:
                delete_str_ref(mp_get_value_str(p));
                break;
            case mp_path_type:
            case mp_pen_type:
            case mp_nep_type:
                mp_toss_knot_list(mp, mp_get_value_knot(p));
                break;
            case mp_picture_type:
                mp_delete_edge_ref(mp, mp_get_value_node(p));
                break;
            case mp_cmykcolor_type:
                if (mp_get_value_node(p) != NULL) {
                    mp_recycle_value(mp, mp_cyan_part(mp_get_value_node(p)));
                    mp_recycle_value(mp, mp_magenta_part(mp_get_value_node(p)));
                    mp_recycle_value(mp, mp_yellow_part(mp_get_value_node(p)));
                    mp_recycle_value(mp, mp_black_part(mp_get_value_node(p)));
                    mp_free_value_node(mp, mp_cyan_part(mp_get_value_node(p)));
                    mp_free_value_node(mp, mp_magenta_part(mp_get_value_node(p)));
                    mp_free_value_node(mp, mp_black_part(mp_get_value_node(p)));
                    mp_free_value_node(mp, mp_yellow_part(mp_get_value_node(p)));
                    mp_free_node(mp, mp_get_value_node(p), sizeof(mp_color_node_data));
                }
                break;
            case mp_pair_type:
                if (mp_get_value_node(p) != NULL) {
                    mp_recycle_value(mp, mp_x_part(mp_get_value_node(p)));
                    mp_recycle_value(mp, mp_y_part(mp_get_value_node(p)));
                    mp_free_value_node(mp, mp_x_part(mp_get_value_node(p)));
                    mp_free_value_node(mp, mp_y_part(mp_get_value_node(p)));
                    mp_free_pair_node(mp, mp_get_value_node(p));
                }
                break;
            case mp_color_type:
                if (mp_get_value_node(p) != NULL) {
                    mp_recycle_value(mp, mp_red_part(mp_get_value_node(p)));
                    mp_recycle_value(mp, mp_green_part(mp_get_value_node(p)));
                    mp_recycle_value(mp, mp_blue_part(mp_get_value_node(p)));
                    mp_free_value_node(mp, mp_red_part(mp_get_value_node(p)));
                    mp_free_value_node(mp, mp_green_part(mp_get_value_node(p)));
                    mp_free_value_node(mp, mp_blue_part(mp_get_value_node(p)));
                    mp_free_node(mp, mp_get_value_node(p), sizeof(mp_color_node_data));
                }
                break;
            case mp_transform_type:
                if (mp_get_value_node(p) != NULL) {
                    mp_recycle_value(mp, mp_tx_part(mp_get_value_node(p)));
                    mp_recycle_value(mp, mp_ty_part(mp_get_value_node(p)));
                    mp_recycle_value(mp, mp_xx_part(mp_get_value_node(p)));
                    mp_recycle_value(mp, mp_xy_part(mp_get_value_node(p)));
                    mp_recycle_value(mp, mp_yx_part(mp_get_value_node(p)));
                    mp_recycle_value(mp, mp_yy_part(mp_get_value_node(p)));
                    mp_free_value_node(mp, mp_tx_part(mp_get_value_node(p)));
                    mp_free_value_node(mp, mp_ty_part(mp_get_value_node(p)));
                    mp_free_value_node(mp, mp_xx_part(mp_get_value_node(p)));
                    mp_free_value_node(mp, mp_xy_part(mp_get_value_node(p)));
                    mp_free_value_node(mp, mp_yx_part(mp_get_value_node(p)));
                    mp_free_value_node(mp, mp_yy_part(mp_get_value_node(p)));
                    mp_free_node(mp, mp_get_value_node(p), sizeof(mp_transform_node_data));
                }
                break;
            case mp_dependent_type:
            case mp_proto_dependent_type:
                {
                    mp_value_node qq = (mp_value_node) mp_get_dep_list((mp_value_node) p);
                    while (mp_get_dep_info(qq) != NULL) {
                        qq = (mp_value_node) qq->link;
                    }
                    mp_set_link(mp_get_prev_dep((mp_value_node) p), qq->link);
                    mp_set_prev_dep(qq->link, mp_get_prev_dep((mp_value_node) p));
                    mp_set_link(qq, NULL);
                    mp_flush_node_list(mp, (mp_node) mp_get_dep_list((mp_value_node) p));
                }
                break;
            case mp_independent_type:
                mp_recycle_independent_value(mp, p);
                break;
            case mp_token_list_type:
            case mp_structured_type:
                mp_confusion(mp, "recycle");
                break;
            case mp_unsuffixed_macro_type:
            case mp_suffixed_macro_type:
                mp_delete_mac_ref(mp, mp_get_value_node(p));
                break;
            default:
                break;
        }
        p->type = mp_undefined_type;
    }
}

static void mp_recycle_independent_value (MP mp, mp_node p)
{
    mp_value_node q, r, s;
    mp_node pp;
    mp_number v ;
    mp_number test;
    mp_variable_type t = p->type;
    new_number(test);
    new_number(v);
    if (t < mp_dependent_type) {
        number_clone(v, mp_get_value_number(p));
    }
    set_number_to_zero(mp->max_c[mp_dependent_type]);
    set_number_to_zero(mp->max_c[mp_proto_dependent_type]);
    mp->max_link[mp_dependent_type] = NULL;
    mp->max_link[mp_proto_dependent_type] = NULL;
    q = (mp_value_node) mp->dep_head->link;
    while (q != mp->dep_head) {
        s = (mp_value_node) mp->temp_head;
        mp_set_link(s, mp_get_dep_list(q));
        while (1) {
            r = (mp_value_node) s->link;
            if (mp_get_dep_info(r) == NULL) {
                break;
            } else if (mp_get_dep_info(r) != p) {
                s = r;
            } else {
                t = q->type;
                if (s->link == mp_get_dep_list(q)) {
                    mp_set_dep_list(q, r->link);
                }
                mp_set_link(s, r->link);
                mp_set_dep_info(r, (mp_node) q);
                number_abs_clone(test, mp_get_dep_value(r));
                if (number_greater(test, mp->max_c[t])) {
                    if (number_positive(mp->max_c[t])) {
                        mp_set_link(mp->max_ptr[t], (mp_node) mp->max_link[t]);
                        mp->max_link[t] = mp->max_ptr[t];
                    }
                    number_clone(mp->max_c[t], test);
                    mp->max_ptr[t] = r;
                } else {
                    mp_set_link(r, mp->max_link[t]);
                    mp->max_link[t] = r;
                }
            }
        }
        q = (mp_value_node) r->link;
    }
    if (number_positive(mp->max_c[mp_dependent_type]) || number_positive(mp->max_c[mp_proto_dependent_type])) {
        mp_number test, ret;
        new_number(ret);
        new_number_clone(test, mp->max_c[mp_dependent_type]);
        number_divide_int(test, 4096);
        if (number_greaterequal(test, mp->max_c[mp_proto_dependent_type])) {
            t = mp_dependent_type;
        } else {
            t = mp_proto_dependent_type;
        }
        s = mp->max_ptr[t];
        pp = (mp_node) mp_get_dep_info(s);
        number_clone(v, mp_get_dep_value(s));
        if (t == mp_dependent_type) {
            mp_set_dep_value(s, fraction_one_t);
        } else {
            mp_set_dep_value(s, unity_t);
        }
        number_negate(mp_get_dep_value(s));
        r = (mp_value_node) mp_get_dep_list((mp_value_node) pp);
        mp_set_link(s, r);
        while (mp_get_dep_info(r) != NULL) {
            r = (mp_value_node) r->link;
        }
        q = (mp_value_node) r->link;
        mp_set_link(r, NULL);
        mp_set_prev_dep(q, mp_get_prev_dep((mp_value_node) pp));
        mp_set_link(mp_get_prev_dep((mp_value_node) pp), (mp_node) q);
        mp_new_indep(mp, pp);
        if (cur_exp_node == pp && mp->cur_exp.type == t) {
            mp->cur_exp.type = mp_independent_type;
        }
        if (number_positive(internal_value(mp_tracing_equations_internal)) && mp_interesting(mp, p)) {
            mp_begin_diagnostic(mp);
            mp_show_transformed_dependency(mp, &v, t, p);
            mp_print_dependency(mp, s, t);
            mp_end_diagnostic(mp, 0);
        }
        t = mp_dependent_type + mp_proto_dependent_type - t;
        if (number_positive(mp->max_c[t])) {
            mp_set_link(mp->max_ptr[t], (mp_node) mp->max_link[t]);
            mp->max_link[t] = mp->max_ptr[t];
        }
        if (t != mp_dependent_type) {
            for (t = mp_dependent_type; t <= mp_proto_dependent_type; t=t+1) {
                r = mp->max_link[t];
                while (r != NULL) {
                    q = (mp_value_node) mp_get_dep_info(r);
                    number_negated_clone(test, v);
                    make_fraction(ret, mp_get_dep_value(r), test);
                    mp_set_dep_list(q, mp_p_plus_fq(mp, (mp_value_node) mp_get_dep_list(q), &ret, s, t, mp_dependent_type));
                    if (mp_get_dep_list(q) == (mp_node) mp->dep_final) {
                        mp_make_known(mp, q, mp->dep_final);
                    }
                    q = r;
                    r = (mp_value_node) r->link;
                    mp_free_dep_node(mp, q);
                }
            }
        } else {
            for (t = mp_dependent_type; t <= mp_proto_dependent_type; t++) {
                r = mp->max_link[t];
                while (r != NULL) {
                    q = (mp_value_node) mp_get_dep_info(r);
                    if (t == mp_dependent_type) {
                        if (cur_exp_node == (mp_node) q && mp->cur_exp.type == mp_dependent_type) {
                            mp->cur_exp.type = mp_proto_dependent_type;
                        }
                        mp_set_dep_list(q, mp_p_over_v(mp, (mp_value_node) mp_get_dep_list(q), &unity_t, mp_dependent_type, mp_proto_dependent_type));
                        q->type = mp_proto_dependent_type;
                        fraction_to_round_scaled(mp_get_dep_value(r));
                    }
                    number_negated_clone(test, v);
                    make_scaled(ret, mp_get_dep_value(r), test);
                    mp_set_dep_list(q, mp_p_plus_fq(mp, (mp_value_node) mp_get_dep_list(q), &ret, s, mp_proto_dependent_type, mp_proto_dependent_type));
                    if (mp_get_dep_list(q) == (mp_node) mp->dep_final) {
                        mp_make_known(mp, q, mp->dep_final);
                    }
                    q = r;
                    r = (mp_value_node) r->link;
                    mp_free_dep_node(mp, q);
                }
            }
        }
        mp_flush_node_list(mp, (mp_node) s);
        if (mp->fix_needed) {
            mp_fix_dependencies(mp);
        }
        check_arith(mp);
        free_number(ret);
    }
    free_number(v);
    free_number(test);
}

static void mp_show_transformed_dependency (MP mp, mp_number *v, mp_variable_type t, mp_node p)
{
    mp_number vv;
    mp_print_nl(mp, "### ");
    if (number_positive(*v)) {
        mp_print_chr(mp, '-');
    }
    if (t == mp_dependent_type) {
        new_number_clone(vv, mp->max_c[mp_dependent_type]);
        fraction_to_round_scaled(vv);
    } else {
        new_number_clone(vv, mp->max_c[mp_proto_dependent_type]);
    }
    if (! number_equal(vv, unity_t)) {
        print_number(vv);
    }
    mp_print_variable_name(mp, p);
    while (mp_get_indep_scale(p) > 0) {
        mp_print_str(mp, "*4");
        mp_set_indep_scale(p, mp_get_indep_scale(p)-2);
    }
    if (t == mp_dependent_type) {
        mp_print_chr(mp, '=');
    } else {
        mp_print_str(mp, " = ");
    }
    free_number(vv);
}

static void mp_bad_exp (MP mp, const char *s)
{
    char msg[256];
    int save_flag;
    {
         mp_string cm;
         int selector = mp->selector;
         mp->selector = mp_new_string_selector;
         mp_print_cmd_mod(mp, cur_cmd, cur_mod);
         mp->selector = selector;
         cm = mp_make_string(mp);
         mp_snprintf(msg, 256, "%s expression can't begin with '%s'", s, mp_str(mp, cm));
         delete_str_ref(cm);
    }
    mp_back_input(mp);
    set_cur_sym(NULL);
    set_cur_cmd(mp_numeric_command);
    set_cur_mod_number(zero_t);
    mp_ins_error(
        mp,
        msg,
        "I'm afraid I need some sort of value in order to continue, so I've tentatively\n"
        "inserted '0'."
    );
    save_flag = mp->var_flag;
    mp->var_flag = 0;
    mp_get_x_next(mp);
    mp->var_flag = save_flag;
}

static void mp_stash_in (MP mp, mp_node p)
{
    p->type = mp->cur_exp.type;
    if (mp->cur_exp.type == mp_known_type) {
        mp_set_value_number(p, cur_exp_value_number);
    } else if (mp->cur_exp.type == mp_independent_type) {
        mp_value_node q = mp_single_dependency(mp, cur_exp_node);
        if (q == mp->dep_final) {
            p->type = mp_known_type;
            mp_set_value_number(p, zero_t);
            mp_free_dep_node(mp, q);
        } else {
            mp_new_dep(mp, p, mp_dependent_type, q);
        }
        mp_recycle_value(mp, cur_exp_node);
        mp_free_value_node(mp, cur_exp_node);
    } else {
        mp_set_dep_list((mp_value_node) p, mp_get_dep_list((mp_value_node) cur_exp_node));
        mp_set_prev_dep((mp_value_node) p, mp_get_prev_dep((mp_value_node) cur_exp_node));
        mp_set_link(mp_get_prev_dep((mp_value_node) p), p);
        mp_free_dep_node(mp, (mp_value_node) cur_exp_node);
    }
    mp->cur_exp.type = mp_vacuous_type;
}

static void mp_back_expr (MP mp)
{
    mp_node p = mp_stash_cur_exp(mp);
    p->link = NULL;
    mp_begin_token_list(mp, p, mp_backed_up_text);
}

static void mp_bad_subscript (MP mp)
{
    mp_value new_expr;
    memset(&new_expr, 0, sizeof(mp_value));
    new_number(new_expr.data.n);
    mp_disp_err(mp, NULL);
    mp_error(
        mp,
        "Improper subscript has been replaced by zero",
        "A bracketed subscript must have a known numeric value; unfortunately, what I\n"
        "found was the value that appears just above this error message. So I'll try a\n"
        "zero subscript."
    );
    mp_flush_cur_exp(mp, new_expr);
}

static char *mp_obliterated (MP mp, mp_node q)
{
    char msg[256];
    mp_string sname;
    int selector = mp->selector;
    mp->selector = mp_new_string_selector;
    mp_show_token_list(mp, q, NULL);
    sname = mp_make_string(mp);
    mp->selector = selector;
    mp_snprintf(msg, 256, "Variable %s has been obliterated", mp_str(mp, sname));
    delete_str_ref(sname);
    return mp_strdup(msg);
}

static void mp_binary_mac (MP mp, mp_node p, mp_node c, mp_sym n)
{
    mp_node q = mp_new_symbolic_node(mp);
    mp_node r = mp_new_symbolic_node(mp);
    q->link = r;
    mp_set_sym_sym(q, p);
    mp_set_sym_sym(r, mp_stash_cur_exp(mp));
    mp_macro_call(mp, c, q, n);
}

static mp_knot mp_pair_to_knot (MP mp)
{
    mp_knot q = mp_new_knot(mp);
    mp_left_type(q) = mp_endpoint_knot;
    mp_right_type(q) = mp_endpoint_knot;
    mp_originator(q) = mp_metapost_user;
    mp_knotstate(q) = mp_regular_knot;
    mp_prev_knot(q) = q;
    mp_next_knot(q) = q;
    mp_known_pair(mp);
    number_clone(q->x_coord, mp->cur_x);
    number_clone(q->y_coord, mp->cur_y);
    return q;
}

void mp_known_pair (MP mp)
{
    mp_value new_expr;
    memset(&new_expr, 0, sizeof(mp_value));
    new_number(new_expr.data.n);
    if (mp->cur_exp.type != mp_pair_type) {
        mp_disp_err(mp, NULL);
        mp_back_error(
            mp,
            "Undefined coordinates have been replaced by (0,0)",
            "I need x and y numbers for this part of the path. The value I found (see above)\n"
            "was no good; so I'll try to keep going by using zero instead."
        );
        mp_get_x_next(mp);
        mp_flush_cur_exp(mp, new_expr);
        set_number_to_zero(mp->cur_x);
        set_number_to_zero(mp->cur_y);
    } else {
        mp_node p = mp_get_value_node(cur_exp_node);
        if (mp_x_part(p)->type == mp_known_type) {
            number_clone(mp->cur_x, mp_get_value_number(mp_x_part(p)));
        } else {
            mp_disp_err(mp, mp_x_part(p));
            mp_back_error(
                mp,
                "Undefined x coordinate has been replaced by 0",
                "I need a 'known' x value for this part of the path. The value I found (see above)\n"
                "was no good; so I'll try to keep going by using zero instead."
            );
            mp_get_x_next(mp);
            mp_recycle_value(mp, mp_x_part(p));
            set_number_to_zero(mp->cur_x);
        }
        if (mp_y_part(p)->type == mp_known_type) {
            number_clone(mp->cur_y, mp_get_value_number(mp_y_part(p)));
        } else {
            mp_disp_err(mp, mp_y_part(p));
            mp_back_error(
                mp,
                "Undefined y coordinate has been replaced by 0",
                "I need a 'known' y value for this part of the path. The value I found (see above)\n"
                "was no good; so I'll try to keep going by using zero instead."
            );
            mp_get_x_next(mp);
            mp_recycle_value(mp, mp_y_part(p));
            set_number_to_zero(mp->cur_y);
        }
        mp_flush_cur_exp(mp, new_expr);
    }
}

static int mp_scan_direction (MP mp)
{
    int t;
    mp_get_x_next(mp);
    if (cur_cmd == mp_curl_command) {
        mp_get_x_next(mp);
        mp_scan_expression(mp);
        if ((mp->cur_exp.type != mp_known_type) || (number_negative(cur_exp_value_number))) {
            mp_value new_expr;
            memset(&new_expr, 0, sizeof(mp_value));
            new_number(new_expr.data.n);
            set_number_to_unity(new_expr.data.n);
            mp_disp_err(mp, NULL);
            mp_back_error(
                mp,
                "Improper curl has been replaced by 1",
                "A curl must be a known, nonnegative number."
            );
            mp_get_x_next(mp);
            mp_flush_cur_exp(mp, new_expr);
        }
        t = mp_curl_knot;
    } else {
        mp_scan_expression(mp);
        if (mp->cur_exp.type > mp_pair_type) {
            mp_number xx;
            new_number(xx);
            if (mp->cur_exp.type != mp_known_type) {
                mp_value new_expr;
                memset(&new_expr, 0, sizeof(mp_value));
                new_number(new_expr.data.n);
                mp_disp_err(mp, NULL);
                mp_back_error(
                    mp,
                    "Undefined x coordinate has been replaced by 0",
                    "I need a 'known' x value for this part of the path. The value I found (see above)\n"
                    "was no good; so I'll try to keep going by using zero instead."
                );
                mp_get_x_next(mp);
                mp_flush_cur_exp(mp, new_expr);
            }
            number_clone(xx, cur_exp_value_number);
            if (cur_cmd != mp_comma_command) {
                mp_back_error(
                    mp,
                    "Missing ',' has been inserted",
                    "I've got the x coordinate of a path direction; will look for the y coordinate\n"
                    "next."
                );
            }
            mp_get_x_next(mp);
            mp_scan_expression(mp);
            if (mp->cur_exp.type != mp_known_type) {
                mp_value new_expr;
                memset(&new_expr, 0, sizeof(mp_value));
                new_number(new_expr.data.n);
                mp_disp_err(mp, NULL);
                mp_back_error(
                    mp,
                    "Undefined y coordinate has been replaced by 0",
                    "I need a 'known' y value for this part of the path. The value I found (see above)\n"
                    "was no good; so I'll try to keep going by using zero instead."
                );
                mp_get_x_next(mp);
                mp_flush_cur_exp(mp, new_expr);
            }
            number_clone(mp->cur_y, cur_exp_value_number);
            number_clone(mp->cur_x, xx);
            free_number(xx);
        } else {
            mp_known_pair(mp);
        }
        if (number_zero(mp->cur_x) && number_zero(mp->cur_y)) {
            t = mp_open_knot;
        } else {
            mp_number narg;
            new_angle(narg);
            n_arg(narg, mp->cur_x, mp->cur_y);
            t = mp_given_knot;
            mp_set_cur_exp_value_number(mp, &narg);
            free_number(narg);
        }
    }
    if (cur_cmd != mp_right_brace_command) {
        mp_back_error(
            mp,
            "Missing '}' has been inserted",
            "I've scanned a direction spec for part of a path, so a right brace should have\n"
            "come next. I shall pretend that one was there."
        );
    }
    mp_get_x_next(mp);
    return t;
}

static void push_of_path_result (MP mp, int what, mp_knot p, mp_number i, mp_number n)
{
    switch (what) {
        case 0:
            mp_pair_value(mp, &(p->x_coord), &(p->y_coord));
            break;
        case 1:
            if (mp_left_type(p) == mp_endpoint_knot) {
                mp_pair_value(mp, &(p->x_coord), &(p->y_coord));
            } else {
                mp_pair_value(mp, &(p->left_x), &(p->left_y));
            }
            break;
        case 2:
            if (mp_right_type(p) == mp_endpoint_knot) {
                mp_pair_value(mp, &(p->x_coord), &(p->y_coord));
            } else {
                mp_pair_value(mp, &(p->right_x), &(p->right_y));
            }
            break;
        case 3:
            {
                mp_number x, y;
                if (mp_right_type(p) == mp_endpoint_knot) {
                    new_number_clone(x, p->x_coord);
                    new_number_clone(y, p->y_coord);
                } else {
                    new_number_clone(x, p->right_x);
                    new_number_clone(y, p->right_y);
                }
                if (mp_left_type(p) == mp_endpoint_knot) {
                    number_subtract(x, p->x_coord);
                    number_subtract(y, p->y_coord);
                } else {
                    number_subtract(x, p->left_x);
                    number_subtract(y, p->left_y);
                }
                mp_pair_value(mp, &x, &y);
                free_number(x);
                free_number(y);
            }
            break;
        case 4:
            {
                mp_value expr;
                memset(&expr, 0, sizeof(mp_value));
                new_number(expr.data.n);
                set_number_from_int(expr.data.n, mp_knotstate(p));
                mp_flush_cur_exp(mp, expr);
            }
            break;
        case 5:
            {
                mp_value expr;
                memset(&expr, 0, sizeof(mp_value));
                set_number_from_subtraction(expr.data.n, i, unity_t);
                mp_flush_cur_exp(mp, expr);
            }
            break;
        case 6:
            {
                mp_value expr;
                memset(&expr, 0, sizeof(mp_value));
                number_clone(expr.data.n, n);
                mp_flush_cur_exp(mp, expr);
            }
            break;
        case 7:
            {
                mp_value expr;
                memset(&expr, 0, sizeof(mp_value));
                set_number_from_addition(expr.data.n, n, unity_t);
                mp_flush_cur_exp(mp, expr);
            }
            break;
        case 8:
            {
                mp->cur_exp.type = mp_boolean_type;
                mp_set_cur_exp_value_boolean(mp, number_equal(i, unity_t) ? mp_true_operation : mp_false_operation);
            }
            break;
        case 9:
            {
                mp->cur_exp.type = mp_boolean_type;
                mp_set_cur_exp_value_boolean(mp, number_greater(i, n) ? mp_true_operation : mp_false_operation);
            }
            break;
    }
}

static void mp_finish_read (MP mp)
{
    mp_str_room(mp, (int) mp->last - (int) start);
    for (size_t k = (size_t) start; k < mp->last; k++) {
        mp_append_char(mp, mp->buffer[k]);
    }
    mp_end_file_reading(mp);
    mp->cur_exp.type = mp_string_type;
    mp_set_cur_exp_str(mp, mp_make_string(mp));
    }
static void mp_do_nullary (MP mp, int c)
{
    check_arith(mp);
    if (number_greater(internal_value(mp_tracing_commands_internal), two_t)) {
        mp_show_cmd_mod(mp, mp_nullary_command, c);
    }
    switch (c) {
        case mp_true_operation:
        case mp_false_operation:
            mp->cur_exp.type = mp_boolean_type;
            mp_set_cur_exp_value_boolean(mp, c);
            break;
        case mp_null_picture_operation:
            mp->cur_exp.type = mp_picture_type;
            mp_set_cur_exp_node(mp, (mp_node) mp_get_edge_header_node(mp));
            mp_init_edges(mp, (mp_edge_header_node) cur_exp_node);
            break;
        case mp_null_pen_operation:
            mp->cur_exp.type = mp_pen_type;
            mp_set_cur_exp_knot(mp, mp_get_pen_circle(mp, &zero_t));
            break;
        case mp_normal_deviate_operation:
            {
                mp_number r;
                new_number(r);
                m_norm_rand(r);
                mp->cur_exp.type = mp_known_type;
                mp_set_cur_exp_value_number(mp, &r);
                free_number(r);
            }
            break;
        case mp_pen_circle_operation:
            mp->cur_exp.type = mp_pen_type;
            mp_set_cur_exp_knot(mp, mp_get_pen_circle(mp, &unity_t));
            break;
        case mp_version_operation:
            mp->cur_exp.type = mp_string_type;
            mp_set_cur_exp_str(mp, mp_intern(mp, metapost_version));
            break;
        case mp_path_point_operation:
        case mp_path_precontrol_operation:
        case mp_path_postcontrol_operation:
        case mp_path_direction_operation:
        case mp_path_state_operation:
        case mp_path_index_operation:
        case mp_path_lastindex_operation:
        case mp_path_length_operation:
        case mp_path_first_operation:
        case mp_path_last_operation:
            if (mp->loop_ptr && mp->loop_ptr->point != NULL) {
                push_of_path_result(mp, c - mp_path_point_operation, mp->loop_ptr->point, mp->loop_ptr->value, mp->loop_ptr->final_value);
            } else {
                mp_pair_value(mp, &zero_t, &zero_t);
            }
            break;
    }
    check_arith(mp);
}

static int mp_pict_color_type (MP mp, int c)
{
    return (
        (mp_edge_list(cur_exp_node)->link != NULL)
        &&
        (
            (! mp_has_color(mp_edge_list(cur_exp_node)->link))
            ||
            ((
                (mp_color_model(mp_edge_list(cur_exp_node)->link) == c)
                ||
                (
                    (mp_color_model(mp_edge_list(cur_exp_node)->link) == mp_uninitialized_model)
                    &&
                    (number_to_scaled(internal_value(mp_default_color_model_internal))/number_to_scaled(unity_t)) == c
                )
            ))
        )
    );
}

static mp_knot mp_simple_knot(MP mp, mp_number *x, mp_number *y)
{
    mp_knot k = mp_new_knot(mp);
    mp_left_type(k) = mp_explicit_knot;
    mp_right_type(k) = mp_explicit_knot;
    mp_originator(k) = mp_program_code;
    mp_knotstate(k) = mp_regular_knot;
    number_clone(k->x_coord, *x);
    number_clone(k->y_coord, *y);
    number_clone(k->left_x, *x);
    number_clone(k->left_y, *y);
    number_clone(k->right_x, *x);
    number_clone(k->right_y, *y);
    return k;
}

static mp_knot mp_complex_knot(MP mp, mp_knot o)
{
    mp_knot k = mp_new_knot(mp);
    mp_left_type(k) = mp_explicit_knot;
    mp_right_type(k) = mp_explicit_knot;
    mp_originator(k) = mp_program_code;
    mp_knotstate(k) = mp_regular_knot;
    number_clone(k->x_coord, o->x_coord);
    number_clone(k->y_coord, o->y_coord);
    number_clone(k->left_x, o->left_x);
    number_clone(k->left_y, o->left_y);
    number_clone(k->right_x, o->right_x);
    number_clone(k->right_y, o->right_y);
    return k;
}

static int mp_nice_pair (MP mp, mp_node p, int t)
{
    (void) mp;
    if (t == mp_pair_type) {
        p = mp_get_value_node(p);
        if (mp_x_part(p)->type == mp_known_type && mp_y_part(p)->type == mp_known_type)
            return 1;
    }
    return 0;
}
static int mp_nice_color_or_pair (MP mp, mp_node p, int t)
{
    mp_node q;
    (void) mp;
    switch (t) {
        case mp_pair_type:
            q = mp_get_value_node(p);
            if (mp_x_part(q)->type == mp_known_type
             && mp_y_part(q)->type == mp_known_type) {
                return 1;
            } else {
                break;
            }
        case mp_color_type:
            q = mp_get_value_node(p);
            if (mp_red_part  (q)->type == mp_known_type
             && mp_green_part(q)->type == mp_known_type
             && mp_blue_part (q)->type == mp_known_type)
                return 1;
            break;
        case mp_cmykcolor_type:
            q = mp_get_value_node(p);
            if (mp_cyan_part   (q)->type == mp_known_type
             && mp_magenta_part(q)->type == mp_known_type
             && mp_yellow_part (q)->type == mp_known_type
             && mp_black_part  (q)->type == mp_known_type)
                return 1;
            break;
    }
    return 0;
}
static void mp_print_known_or_unknown_type (MP mp, int t, mp_node v)
{
    mp_print_chr(mp, '(');
    if (t > mp_known_type) {
        mp_print_str(mp, "unknown numeric");
    } else {
        switch (t) {
            case mp_pair_type:
            case mp_color_type:
            case mp_cmykcolor_type:
                if (! mp_nice_color_or_pair (mp, v, t)) {
                    mp_print_str(mp, "unknown ");
                }
                break;
        }
        mp_print_type(mp, t);
    }
    mp_print_chr(mp, ')');
}
static void mp_bad_unary (MP mp, int c)
{
    char msg[256];
    mp_string sname;
    int selector = mp->selector;
    mp->selector = mp_new_string_selector;
    mp_print_op(mp, c);
    mp_print_known_or_unknown_type(mp, mp->cur_exp.type, cur_exp_node);
    sname = mp_make_string(mp);
    mp->selector = selector;
    mp_snprintf(msg, 256, "Not implemented: %s", mp_str(mp, sname));
    delete_str_ref(sname);
    mp_disp_err(mp, NULL);
    mp_back_error(
        mp,
        msg,
        "I'm afraid I don't know how to apply that operation to that particular type.\n"
        "Continue, and I'll simply return the argument (shown above) as the result of the\n"
        "operation."
    );
    mp_get_x_next(mp);
}
static void mp_negate_dep_list (MP mp, mp_value_node p)
{
    (void) mp;
    while (1) {
        number_negate(mp_get_dep_value(p));
        if (mp_get_dep_info(p) == NULL)
            return;
        p = (mp_value_node) p->link;
    }
}
static void mp_negate_value(MP mp, mp_node r)
{
    if (r->type == mp_known_type) {
        mp_set_value_number(r, mp_get_value_number(r));
        number_negate(mp_get_value_number(r));
    } else {
        mp_negate_dep_list(mp, (mp_value_node) mp_get_dep_list((mp_value_node) r));
    }
}
static void negate_cur_expr (MP mp)
{
    switch (mp->cur_exp.type) {
        case mp_color_type:
        case mp_cmykcolor_type:
        case mp_pair_type:
        case mp_independent_type:
            {
                mp_node q = cur_exp_node;
                mp_make_exp_copy(mp, q);
                if (mp->cur_exp.type == mp_dependent_type) {
                    mp_negate_dep_list(mp, (mp_value_node) mp_get_dep_list((mp_value_node) cur_exp_node));
                } else if (mp->cur_exp.type <= mp_pair_type) {
                    mp_node p = mp_get_value_node(cur_exp_node);

                    switch (mp->cur_exp.type) {
                        case mp_pair_type:
                            mp_negate_value(mp, mp_x_part(p));
                            mp_negate_value(mp, mp_y_part(p));
                            break;
                        case mp_color_type:
                            mp_negate_value(mp, mp_red_part(p));
                            mp_negate_value(mp, mp_green_part(p));
                            mp_negate_value(mp, mp_blue_part(p));
                            break;
                        case mp_cmykcolor_type:
                            mp_negate_value(mp, mp_cyan_part(p));
                            mp_negate_value(mp, mp_magenta_part(p));
                            mp_negate_value(mp, mp_yellow_part(p));
                            mp_negate_value(mp, mp_black_part(p));
                            break;
                        default:
                            break;
                    }
                }
                mp_recycle_value(mp, q);
                mp_free_value_node(mp, q);
            }
            break;
        case mp_dependent_type:
        case mp_proto_dependent_type:
            mp_negate_dep_list(mp, (mp_value_node) mp_get_dep_list((mp_value_node) cur_exp_node));
            break;
        case mp_known_type:
            if (is_number(cur_exp_value_number)) {
                number_negate(cur_exp_value_number);
            }
            break;
        default:
            mp_bad_unary(mp, mp_minus_operation);
            break;
    }
}
static void mp_pair_to_path (MP mp) {
    mp_set_cur_exp_knot(mp, mp_pair_to_knot(mp));
    mp->cur_exp.type = mp_path_type;
}
static void mp_take_part (MP mp, int c)
{
    mp_node p = mp_get_value_node(cur_exp_node);
    mp_set_value_node(mp->temp_val, p);
    mp->temp_val->type = mp->cur_exp.type;
    p->link= mp->temp_val;
    mp_free_value_node(mp, cur_exp_node);
    switch (c) {
        case mp_x_part_operation:
            if (mp->cur_exp.type == mp_pair_type) {
                mp_make_exp_copy(mp, mp_x_part(p));
            } else {
                mp_make_exp_copy(mp, mp_tx_part(p));
            }
            break;
        case mp_y_part_operation:
            if (mp->cur_exp.type == mp_pair_type) {
                mp_make_exp_copy(mp, mp_y_part(p));
            } else {
                mp_make_exp_copy(mp, mp_ty_part(p));
            }
            break;
        case mp_xx_part_operation:
            mp_make_exp_copy(mp, mp_xx_part(p));
            break;
        case mp_xy_part_operation:
            mp_make_exp_copy(mp, mp_xy_part(p));
            break;
        case mp_yx_part_operation:
            mp_make_exp_copy(mp, mp_yx_part(p));
            break;
        case mp_yy_part_operation:
            mp_make_exp_copy(mp, mp_yy_part(p));
            break;
        case mp_red_part_operation:
            mp_make_exp_copy(mp, mp_red_part(p));
            break;
        case mp_green_part_operation:
            mp_make_exp_copy(mp, mp_green_part(p));
            break;
        case mp_blue_part_operation:
            mp_make_exp_copy(mp, mp_blue_part(p));
            break;
        case mp_cyan_part_operation:
            mp_make_exp_copy(mp, mp_cyan_part(p));
            break;
        case mp_magenta_part_operation:
            mp_make_exp_copy(mp, mp_magenta_part(p));
            break;
        case mp_yellow_part_operation:
            mp_make_exp_copy(mp, mp_yellow_part(p));
            break;
        case mp_black_part_operation:
            mp_make_exp_copy(mp, mp_black_part(p));
            break;
        case mp_grey_part_operation:
            mp_make_exp_copy(mp, mp_grey_part(p));
            break;
    }
    mp_recycle_value(mp, mp->temp_val);
}
static void mp_take_pict_part (MP mp, int c)
{
    mp_node p;
    mp_value new_expr;
    memset(&new_expr, 0, sizeof(mp_value));
    new_number(new_expr.data.n);
    p = mp_edge_list(cur_exp_node)->link;
    if (p != NULL) {
        switch (c) {
            case mp_x_part_operation:
            case mp_y_part_operation:
            case mp_xx_part_operation:
            case mp_xy_part_operation:
            case mp_yx_part_operation:
            case mp_yy_part_operation:
                goto NOT_FOUND;
            case mp_red_part_operation:
            case mp_green_part_operation:
            case mp_blue_part_operation:
                if (mp_has_color(p)) {
                    switch (c) {
                        case mp_red_part_operation:
                            number_clone(new_expr.data.n, ((mp_shape_node) p)->red);
                            break;
                        case mp_green_part_operation:
                            number_clone(new_expr.data.n, ((mp_shape_node) p)->green);
                            break;
                        case mp_blue_part_operation:
                            number_clone(new_expr.data.n, ((mp_shape_node) p)->blue);
                            break;
                    }
                    mp_flush_cur_exp(mp, new_expr);
                } else
                    goto NOT_FOUND;
                break;
            case mp_cyan_part_operation:
            case mp_magenta_part_operation:
            case mp_yellow_part_operation:
            case mp_black_part_operation:
                if (mp_has_color(p)) {
                    if (mp_color_model(p) == mp_uninitialized_model && c == mp_black_part_operation) {
                        set_number_to_unity(new_expr.data.n);
                    } else {
                        switch (c) {
                            case mp_cyan_part_operation:
                                number_clone(new_expr.data.n, ((mp_shape_node) p)->cyan);
                                break;
                            case mp_magenta_part_operation:
                                number_clone(new_expr.data.n, ((mp_shape_node) p)->magenta);
                                break;
                            case mp_yellow_part_operation:
                                number_clone(new_expr.data.n, ((mp_shape_node) p)->yellow);
                                break;
                            case mp_black_part_operation:
                                number_clone(new_expr.data.n, ((mp_shape_node) p)->black);
                                break;
                        }
                    }
                    mp_flush_cur_exp(mp, new_expr);
                } else
                    goto NOT_FOUND;
                break;
            case mp_grey_part_operation:
                if (mp_has_color(p)) {
                    number_clone(new_expr.data.n, ((mp_shape_node) p)->grey);
                    mp_flush_cur_exp(mp, new_expr);
                } else
                    goto NOT_FOUND;
                break;
            case mp_color_model_operation:
                if (mp_has_color(p)) {
                    if (mp_color_model(p) == mp_uninitialized_model) {
                        number_clone(new_expr.data.n, internal_value(mp_default_color_model_internal));
                    } else {
                        number_clone(new_expr.data.n, unity_t);
                        number_multiply_int(new_expr.data.n, mp_color_model(p));
                    }
                    mp_flush_cur_exp(mp, new_expr);
                } else
                    goto NOT_FOUND;
                break;
            case mp_prescript_part_operation:
                if (! mp_has_script(p)) {
                    goto NOT_FOUND;
                } else {
                    if (mp_pre_script(p)) {
                        new_expr.data.str = mp_pre_script(p);
                        add_str_ref(new_expr.data.str);
                    } else {
                        new_expr.data.str = mp_rts(mp,"");
                    }
                    mp_flush_cur_exp(mp, new_expr);
                    mp->cur_exp.type = mp_string_type;
                };
                break;
            case mp_postscript_part_operation:
                if (! mp_has_script(p)) {
                    goto NOT_FOUND;
                } else {
                    if (mp_post_script(p)) {
                        new_expr.data.str = mp_post_script(p);
                        add_str_ref(new_expr.data.str);
                    } else {
                        new_expr.data.str = mp_rts(mp,"");
                    }
                    mp_flush_cur_exp(mp, new_expr);
                    mp->cur_exp.type = mp_string_type;
                };
                break;
            case mp_stacking_part_operation:
                number_clone(new_expr.data.n, unity_t);
                number_multiply_int(new_expr.data.n, mp_stacking(p));
                mp_flush_cur_exp(mp, new_expr);
                break;
            case mp_path_part_operation:
                if (mp_is_stop(p)) {
                    mp_confusion(mp, "picture");
                } else {
                    new_expr.data.node = NULL;
                    switch (p->type) {
                        case mp_fill_node_type:
                        case mp_stroked_node_type:
                            new_expr.data.p = mp_copy_path(mp, mp_path_ptr((mp_shape_node) p));
                            break;
                        case mp_start_clip_node_type:
                        case mp_start_group_node_type:
                        case mp_start_bounds_node_type:
                            new_expr.data.p = mp_copy_path(mp, mp_path_ptr((mp_start_node) p));
                            break;
                        default:
                            break;
                    }
                    mp_flush_cur_exp(mp, new_expr);
                    mp->cur_exp.type = mp_path_type;
                }
                break;
            case mp_pen_part_operation:
                if (! mp_has_pen(p)) {
                    goto NOT_FOUND;
                } else {
                    switch (p->type) {
                        case mp_fill_node_type:
                        case mp_stroked_node_type:
                            if (mp_pen_ptr((mp_shape_node) p) == NULL) {
                                goto NOT_FOUND;
                            } else {
                                new_expr.data.p = mp_copy_pen(mp, mp_pen_ptr((mp_shape_node) p));
                                mp_flush_cur_exp(mp, new_expr);
                                mp->cur_exp.type = mp_pen_type((mp_shape_node) p) ? mp_nep_type : mp_pen_type ;
                            }
                            break;
                        default:
                            break;
                    }
                }
                break;
            case mp_dash_part_operation:
                if (p->type != mp_stroked_node_type) {
                    goto NOT_FOUND;
                } else if (mp_dash_ptr(p) == NULL) {
                    goto NOT_FOUND;
                } else {
                    mp_add_edge_ref(mp, mp_dash_ptr(p));
                    new_expr.data.node = (mp_node) mp_scale_edges(mp,
                        &(((mp_shape_node) p)->dashscale), (mp_edge_header_node) mp_dash_ptr(p));
                    mp_flush_cur_exp(mp, new_expr);
                    mp->cur_exp.type = mp_picture_type;
                }
                break;
        }
        return;
    };
  NOT_FOUND:
    switch (c) {
        case mp_prescript_part_operation:
        case mp_postscript_part_operation:
            new_expr.data.str = mp_rts(mp,"");
            mp_flush_cur_exp(mp, new_expr);
            mp->cur_exp.type = mp_string_type;
            break;
        case mp_path_part_operation:
            new_expr.data.p = mp_new_knot(mp);
            mp_flush_cur_exp(mp, new_expr);
            mp_left_type(cur_exp_knot) = mp_endpoint_knot;
            mp_right_type(cur_exp_knot) = mp_endpoint_knot;
            mp_prev_knot(cur_exp_knot) = cur_exp_knot;
            mp_next_knot(cur_exp_knot) = cur_exp_knot;
            set_number_to_zero(cur_exp_knot->x_coord);
            set_number_to_zero(cur_exp_knot->y_coord);
            mp_originator(cur_exp_knot) = mp_metapost_user;
            mp_knotstate(cur_exp_knot) = mp_regular_knot;
            mp->cur_exp.type = mp_path_type;
            break;
        case mp_pen_part_operation:
            new_expr.data.p = mp_get_pen_circle(mp, &zero_t);
            mp_flush_cur_exp(mp, new_expr);
            mp->cur_exp.type = mp_pen_type;
            break;
        case mp_dash_part_operation:
            new_expr.data.node = (mp_node) mp_get_edge_header_node(mp);
            mp_flush_cur_exp(mp, new_expr);
            mp_init_edges(mp, (mp_edge_header_node) cur_exp_node);
            mp->cur_exp.type = mp_picture_type;
            break;
        default:
            set_number_to_zero(new_expr.data.n);
            mp_flush_cur_exp(mp, new_expr);
            break;
    }
}
static void mp_str_to_num (MP mp)
{
    int n;
    mp_value new_expr;
    memset(&new_expr, 0, sizeof(mp_value));
    new_number(new_expr.data.n);
    if (cur_exp_str->len == 0) {
       n = -1;
    } else {
       n = cur_exp_str->str[0];
    }
    number_clone(new_expr.data.n, unity_t);
    number_multiply_int(new_expr.data.n, n);
    mp_flush_cur_exp(mp, new_expr);
}
static void mp_path_length (MP mp, mp_number *n)
{
    mp_knot p = cur_exp_knot;
    int l = mp_left_type(p) == mp_endpoint_knot ? -1 : 0;
    do {
        p = mp_next_knot(p);
        ++l;
    } while (p != cur_exp_knot);
    set_number_from_int(*n, l);
}
static void mp_path_no_length (MP mp, mp_number *n)
{
    set_number_from_boolean(*n, mp_next_knot(cur_exp_knot) == cur_exp_knot ? mp_true_operation : mp_false_operation);
}
static void mp_picture_length (MP mp, mp_number *n)
{
    mp_node p = mp_edge_list(cur_exp_node)->link;
    int l = 0;
    if (p != NULL) {
        if (mp_is_start_or_stop(p) && mp_skip_1component(mp, p) == NULL) {
            p = p->link;
        }
        while (p != NULL) {
            if (! mp_is_start_or_stop(p)) {
                p = p->link;
            } else if (! mp_is_stop(p)) {
                p = mp_skip_1component(mp, p);
            } else {
                break;
            }
            ++l;
        }
    }
    set_number_from_int(*n, l);
}
static void mp_an_angle (MP mp, mp_number *ret, mp_number *xpar, mp_number *ypar)
{
    set_number_to_zero(*ret);
    if (! (number_zero(*xpar) && number_zero(*ypar))) {
        n_arg(*ret, *xpar, *ypar);
    }
}
static void mp_bezier_slope (MP mp,
    mp_number *ret, mp_number *AX, mp_number *AY, mp_number *BX,
    mp_number *BY, mp_number *CX, mp_number *CY, mp_number *DX,
    mp_number *DY
);
static void mp_turn_cycles (MP mp, mp_number *turns, mp_knot c)
{
    int selector;
    mp_number res, ang;
    mp_knot p;
    mp_number xp, yp;
    mp_number x, y;
    mp_number arg1, arg2;
    mp_number in_angle, out_angle;
    mp_number seven_twenty_deg_t;
    set_number_to_zero(*turns);
    new_number(arg1);
    new_number(arg2);
    new_number(xp);
    new_number(yp);
    new_number(x);
    new_number(y);
    new_angle(in_angle);
    new_angle(out_angle);
    new_angle(ang);
    new_angle(res);
    new_angle(seven_twenty_deg_t);
    number_clone(seven_twenty_deg_t, three_sixty_deg_t);
    number_double(seven_twenty_deg_t);
    p = c;
    selector = mp->selector;
    mp->selector = mp_term_only_selector;
    if (number_greater(internal_value(mp_tracing_commands_internal), unity_t)) {
        mp_begin_diagnostic(mp);
        mp_print_nl(mp, "");
        mp_end_diagnostic(mp, 0);
    }
    do {
        number_clone(xp, p_next->x_coord);
        number_clone(yp, p_next->y_coord);
        mp_bezier_slope(mp, &ang, &(p->x_coord),  &(p->y_coord), &(p->right_x), &(p->right_y), &(p_next->left_x), &(p_next->left_y), &xp, &yp);
        if (number_greater(ang, seven_twenty_deg_t)) {
            mp_error(mp, "Strange path", NULL);
            mp->selector = selector;
            set_number_to_zero(*turns);
            goto DONE;
        }
        number_add(res, ang);
        if (number_greater(res, one_eighty_deg_t)) {
            number_subtract(res, three_sixty_deg_t);
            number_add(*turns, unity_t);
        }
        if (number_lessequal(res, negative_one_eighty_deg_t)) {
            number_add(res, three_sixty_deg_t);
            number_subtract(*turns, unity_t);
        }
        number_clone(x, p_next->left_x);
        number_clone(y, p_next->left_y);
        if (number_equal(xp, x) && number_equal(yp, y)) {
            number_clone(x, p->right_x);
            number_clone(y, p->right_y);
        }
        if (number_equal(xp, x) && number_equal(yp, y)) {
            number_clone(x, p->x_coord);
            number_clone(y, p->y_coord);
        }
        set_number_from_subtraction(arg1, xp, x);
        set_number_from_subtraction(arg2, yp, y);
        mp_an_angle(mp, &in_angle, &arg1, &arg2);
        number_clone(x, p_next->right_x);
        number_clone(y, p_next->right_y);
        if (number_equal(xp, x) && number_equal(yp, y)) {
            number_clone(x, p_nextnext->left_x);
            number_clone(y, p_nextnext->left_y);
        }
        if (number_equal(xp, x) && number_equal(yp, y)) {
            number_clone(x, p_nextnext->x_coord);
            number_clone(y, p_nextnext->y_coord);
        }
        set_number_from_subtraction(arg1, x, xp);
        set_number_from_subtraction(arg2, y, yp);
        mp_an_angle(mp, &out_angle, &arg1, &arg2);
        set_number_from_subtraction(ang, out_angle, in_angle);
        mp_reduce_angle(mp, &ang);
        if (number_nonzero(ang)) {
            number_add(res, ang);
            if (number_greaterequal(res, one_eighty_deg_t)) {
                number_subtract(res, three_sixty_deg_t);
                number_add(*turns, unity_t);
            }
            if (number_lessequal(res, negative_one_eighty_deg_t)) {
                number_add(res, three_sixty_deg_t);
                number_subtract(*turns, unity_t);
            }
        }
        p = mp_next_knot(p);
    } while (p != c);
    mp->selector = selector;
  DONE:
    free_number(xp);
    free_number(yp);
    free_number(x);
    free_number(y);
    free_number(seven_twenty_deg_t);
    free_number(in_angle);
    free_number(out_angle);
    free_number(ang);
    free_number(res);
    free_number(arg1);
    free_number(arg2);
}
static void mp_turn_cycles_wrapper (MP mp, mp_number *ret, mp_knot c)
{
    if (mp_next_knot(c) == c) {
        set_number_to_unity(*ret);
    } else {
        mp_turn_cycles (mp, ret, c);
    }
}
static int mp_test_known (MP mp, int c)
{
    int b = mp_false_operation;
    switch (mp->cur_exp.type) {
        case mp_vacuous_type:
        case mp_boolean_type:
        case mp_string_type:
        case mp_pen_type:
        case mp_nep_type:
        case mp_path_type:
        case mp_picture_type:
        case mp_known_type:
            b = mp_true_operation;
            break;
        case mp_transform_type:
            {
                mp_node p = mp_get_value_node(cur_exp_node);
                if ( (mp_tx_part(p)->type == mp_known_type) &&
                     (mp_ty_part(p)->type == mp_known_type) &&
                     (mp_xx_part(p)->type == mp_known_type) &&
                     (mp_xy_part(p)->type== mp_known_type) &&
                     (mp_yx_part(p)->type == mp_known_type) &&
                     (mp_yy_part(p)->type == mp_known_type) ) {
                    b = mp_true_operation;
                }
            }
            break;
        case mp_color_type:
            {
                mp_node p = mp_get_value_node(cur_exp_node);
                if ( (mp_red_part  (p)->type == mp_known_type) &&
                     (mp_green_part(p)->type == mp_known_type) &&
                     (mp_blue_part (p)->type == mp_known_type) ) {
                    b = mp_true_operation;
                }
            }
            break;
        case mp_cmykcolor_type:
            {
                mp_node p = mp_get_value_node(cur_exp_node);
                if ( (mp_cyan_part   (p)->type == mp_known_type) &&
                     (mp_magenta_part(p)->type == mp_known_type) &&
                     (mp_yellow_part (p)->type == mp_known_type) &&
                     (mp_black_part  (p)->type == mp_known_type) ) {
                    b = mp_true_operation;
                }
            }
            break;
        case mp_pair_type:
            {
                mp_node p = mp_get_value_node(cur_exp_node);
                if ( (mp_x_part(p)->type == mp_known_type) &&
                     (mp_y_part(p)->type == mp_known_type) ) {
                    b = mp_true_operation;
                }
            }
            break;
        default:
            break;
    }
    if (c == mp_known_operation) {
        return b;
    } else {
        return b == mp_true_operation ? mp_false_operation : mp_true_operation;
    }
}
static void mp_pair_value (MP mp, mp_number *x, mp_number *y)
{
    mp_node p;
    mp_value new_expr;
    mp_number x1, y1;
    new_number_clone(x1, *x);
    new_number_clone(y1, *y);
    memset(&new_expr, 0, sizeof(mp_value));
    new_number(new_expr.data.n);
    p = mp_new_value_node(mp);
    new_expr.type = p->type;
    new_expr.data.node = p;
    mp_flush_cur_exp(mp, new_expr);
    mp->cur_exp.type = mp_pair_type;
    p->name_type = mp_capsule_operation;
    mp_init_pair_node(mp, p);
    p = mp_get_value_node(p);
    mp_x_part(p)->type = mp_known_type;
    mp_set_value_number(mp_x_part(p), x1);
    mp_y_part(p)->type = mp_known_type;
    mp_set_value_number(mp_y_part(p), y1);
    free_number(x1);
    free_number(y1);
}
static int mp_get_cur_bbox (MP mp)
{
    switch (mp->cur_exp.type) {
        case mp_picture_type:
            {
                mp_edge_header_node p = (mp_edge_header_node) cur_exp_node;
                mp_set_bbox(mp, p, 1);
                if (number_greater(p->minx, p->maxx)) {
                    set_number_to_zero(mp_minx);
                    set_number_to_zero(mp_maxx);
                    set_number_to_zero(mp_miny);
                    set_number_to_zero(mp_maxy);
                } else {
                    number_clone(mp_minx, p->minx);
                    number_clone(mp_maxx, p->maxx);
                    number_clone(mp_miny, p->miny);
                    number_clone(mp_maxy, p->maxy);
                }
            }
            break;
        case mp_path_type:
            mp_path_bbox(mp, cur_exp_knot);
            break;
        case mp_pen_type:
        case mp_nep_type:
            mp_pen_bbox(mp, cur_exp_knot);
            break;
        default:
            return 0;
    }
    return 1;
}
static int mp_get_cur_xbox (MP mp)
{
    if (mp->cur_exp.type == mp_path_type) {
        mp_path_xbox(mp, cur_exp_knot);
        return 1;
    } else {
        return mp_get_cur_bbox(mp);
    }
}
static int mp_get_cur_ybox (MP mp)
{
    if (mp->cur_exp.type == mp_path_type) {
        mp_path_ybox(mp, cur_exp_knot);
        return 1;
    } else {
        return mp_get_cur_bbox(mp);
    }
}
static void mp_do_read_or_close (MP mp, int c)
{
    int n = mp->read_files;
    int n0 = mp->read_files;
    char *fn = mp_strdup(mp_str(mp, cur_exp_str));
    mp_value new_expr;
    memset(&new_expr, 0, sizeof(mp_value));
    new_number(new_expr.data.n);
    while (mp_strcmp(fn, mp->rd_fname[n]) != 0) {
        if (n > 0) {
            --n;
        } else if (c == mp_close_from_operation) {
            goto CLOSE_FILE;
        } else {
            if (n0 == mp->read_files) {
                if (mp->read_files < mp->max_read_files) {
                    ++mp->read_files;
                } else {
                    void **rd_file;
                    char **rd_fname;
                    int l;
                    l = mp->max_read_files + (mp->max_read_files / 4);
                    rd_file = mp_memory_allocate((size_t) (l + 1) * sizeof(void *));
                    rd_fname = mp_memory_allocate((size_t) (l + 1) * sizeof(char *));
                    for (int k = 0; k <= l; k++) {
                        if (k <= mp->max_read_files) {
                            rd_file[k] = mp->rd_file[k];
                            rd_fname[k] = mp->rd_fname[k];
                        } else {
                            rd_file[k] = 0;
                            rd_fname[k] = NULL;
                        }
                    }
                    mp_memory_free(mp->rd_file);
                    mp_memory_free(mp->rd_fname);
                    mp->max_read_files = l;
                    mp->rd_file = rd_file;
                    mp->rd_fname = rd_fname;
                }
            }
            n = n0;
            if (mp_start_read_input(mp, fn, n)) {
                goto FOUND;
            } else {
                goto NOT_FOUND;
            }
        }
        if (mp->rd_fname[n] == NULL) {
            n0 = n;
        }
    }
    if (c == mp_close_from_operation) {
        (mp->close_file) (mp, mp->rd_file[n]);
        goto NOT_FOUND;
    }
    mp_begin_file_reading(mp);
    name = is_read;
    if (mp_input_ln(mp, mp->rd_file[n])) {
        goto FOUND;
    }
    mp_end_file_reading(mp);
  NOT_FOUND:
    mp_memory_free(mp->rd_fname[n]);
    mp->rd_fname[n] = NULL;
    if (n == mp->read_files - 1) {
        mp->read_files = n;
    }
    if (c == mp_close_from_operation) {
        goto CLOSE_FILE;
    }
    new_expr.data.str = mp->eof_file;
    add_str_ref(new_expr.data.str);
    mp_flush_cur_exp(mp, new_expr);
    mp->cur_exp.type = mp_string_type;
    return;
  CLOSE_FILE:
    mp_flush_cur_exp(mp, new_expr);
    mp->cur_exp.type = mp_vacuous_type;
    return;
  FOUND:
    mp_flush_cur_exp(mp, new_expr);
    mp_finish_read(mp);
}


static void mp_do_unary (MP mp, int c)
{
    check_arith(mp);
    if (number_greater(internal_value(mp_tracing_commands_internal), two_t)) {
        mp_begin_diagnostic(mp);
        mp_print_nl(mp, "{");
        mp_print_op(mp, c);
        mp_print_chr(mp, '(');
        mp_print_exp(mp, NULL, 0);
        mp_print_str(mp, ")}");
        mp_end_diagnostic(mp, 0);
    }
    switch (c) {
        case mp_plus_operation:
            if (mp->cur_exp.type < mp_color_type) {
                mp_bad_unary(mp, mp_plus_operation);
            }
            break;
        case mp_minus_operation:
            negate_cur_expr(mp);
            break;
        case mp_not_operation:
            if (mp->cur_exp.type != mp_boolean_type) {
                mp_bad_unary(mp, mp_not_operation);
            } else {
                mp_set_cur_exp_value_boolean(mp, (cur_exp_value_boolean == mp_true_operation) ? mp_false_operation : mp_true_operation);
            }
            break;
        case mp_sqrt_operation:
            if (mp->cur_exp.type != mp_known_type) {
                mp_bad_unary(mp, c);
            } else {
                mp_number n;
                new_number(n);
                square_rt(n, cur_exp_value_number);
                mp_set_cur_exp_value_number(mp, &n);
                free_number(n);
            }
            break;
        case mp_m_exp_operation:
            if (mp->cur_exp.type != mp_known_type) {
                mp_bad_unary(mp, c);
            } else {
                mp_number n;
                new_number(n);
                m_exp(n, cur_exp_value_number);
                mp_set_cur_exp_value_number(mp, &n);
                free_number(n);
            }
            break;
        case mp_m_log_operation:
            if (mp->cur_exp.type != mp_known_type) {
                mp_bad_unary(mp, c);
            } else {
                mp_number n;
                new_number(n);
                m_log(n, cur_exp_value_number);
                mp_set_cur_exp_value_number(mp, &n);
                free_number(n);
            }
            break;
        case mp_sin_d_operation:
        case mp_cos_d_operation:
            if (mp->cur_exp.type != mp_known_type) {
                mp_bad_unary(mp, c);
            } else {
                mp_number n_sin, n_cos, arg1, arg2;
                new_number(arg1);
                new_number(arg2);
                new_fraction(n_sin);
                new_fraction(n_cos);
                number_clone(arg1, cur_exp_value_number);
                number_clone(arg2, unity_t);
                number_multiply_int(arg2, 360);
                number_modulo(arg1, arg2);
                convert_scaled_to_angle(arg1);
                n_sin_cos(arg1, n_cos, n_sin);
                if (c == mp_sin_d_operation) {
                    fraction_to_round_scaled(n_sin);
                    mp_set_cur_exp_value_number(mp, &n_sin);
                } else {
                    fraction_to_round_scaled(n_cos);
                    mp_set_cur_exp_value_number(mp, &n_cos);
                }
                free_number(arg1);
                free_number(arg2);
                free_number(n_sin);
                free_number(n_cos);
            }
            break;
        case mp_floor_operation:
            if (mp->cur_exp.type != mp_known_type) {
                mp_bad_unary(mp, c);
            } else {
                mp_number n;
                new_number(n);
                number_clone(n, cur_exp_value_number);
                floor_scaled(n);
                mp_set_cur_exp_value_number(mp, &n);
                free_number(n);
            }
            break;
        case mp_uniform_deviate_operation:
            if (mp->cur_exp.type != mp_known_type) {
                mp_bad_unary(mp, c);
            } else {
                mp_number n;
                new_number(n);
                m_unif_rand(n, cur_exp_value_number);
                mp_set_cur_exp_value_number(mp, &n);
                free_number(n);
            }
            break;
        case mp_odd_operation:
            if (mp->cur_exp.type != mp_known_type) {
                mp_bad_unary(mp, c);
            } else {
                mp_set_cur_exp_value_boolean(mp, number_odd(cur_exp_value_number) ? mp_true_operation : mp_false_operation);
                mp->cur_exp.type = mp_boolean_type;
            }
            break;
        case mp_angle_operation:
            if (mp_nice_pair (mp, cur_exp_node, mp->cur_exp.type)) {
                mp_value expr;
                mp_node p;
                mp_number narg;
                memset(&expr, 0, sizeof(mp_value));
                new_number(expr.data.n);
                new_angle(narg);
                p = mp_get_value_node(cur_exp_node);
                n_arg(narg, mp_get_value_number(mp_x_part(p)), mp_get_value_number(mp_y_part(p)));
                number_clone(expr.data.n, narg);
                convert_angle_to_scaled(expr.data.n);
                free_number(narg);
                mp_flush_cur_exp(mp, expr);
            } else {
                mp_bad_unary(mp, mp_angle_operation);
            }
            break;
        case mp_x_part_operation:
        case mp_y_part_operation:
            switch (mp->cur_exp.type) {
                case mp_pair_type:
                case mp_transform_type:
                    mp_take_part(mp, c);
                    break;
                case mp_picture_type:
                    mp_take_pict_part(mp, c);
                    break;
                default:
                    mp_bad_unary(mp, c);
                    break;
            }
            break;
        case mp_xx_part_operation:
        case mp_xy_part_operation:
        case mp_yx_part_operation:
        case mp_yy_part_operation:
            switch (mp->cur_exp.type) {
                case mp_transform_type:
                    mp_take_part(mp, c);
                    break;
                case mp_picture_type:
                    mp_take_pict_part(mp, c);
                    break;
                default:
                    mp_bad_unary(mp, c);
                    break;
            }
            break;
        case mp_red_part_operation:
        case mp_green_part_operation:
        case mp_blue_part_operation:
            switch (mp->cur_exp.type) {
                case mp_color_type:
                    mp_take_part(mp, c);
                    break;
                case mp_picture_type:
                    if (mp_pict_color_type(mp, mp_rgb_model)) {
                        mp_take_pict_part(mp, c);
                    } else {
                        mp_bad_color_part(mp, c);
                    }
                    break;
                default:
                    mp_bad_unary(mp, c);
                    break;
            }
            break;
        case mp_cyan_part_operation:
        case mp_magenta_part_operation:
        case mp_yellow_part_operation:
        case mp_black_part_operation:
            switch (mp->cur_exp.type) {
                case mp_cmykcolor_type:
                    mp_take_part(mp, c);
                    break;
                case mp_picture_type:
                    if (mp_pict_color_type(mp, mp_cmyk_model)) {
                        mp_take_pict_part(mp, c);
                    } else {
                        mp_bad_color_part(mp, c);
                    }
                    break;
                default:
                    mp_bad_unary(mp, c);
                    break;
            }
            break;
        case mp_grey_part_operation:
            switch (mp->cur_exp.type) {
                case mp_known_type:
                    break;
                case mp_picture_type:
                    if (mp_pict_color_type(mp, mp_grey_model)) {
                        mp_take_pict_part(mp, c);
                    } else {
                        mp_bad_color_part(mp, c);
                    }
                    break;
                default:
                    mp_bad_unary(mp, c);
                    break;
            }
            break;
        case mp_color_model_operation:
        case mp_path_part_operation:
        case mp_pen_part_operation:
        case mp_dash_part_operation:
        case mp_prescript_part_operation:
        case mp_postscript_part_operation:
        case mp_stacking_part_operation:
            if (mp->cur_exp.type == mp_picture_type) {
                mp_take_pict_part(mp, c);
            } else {
                mp_bad_unary(mp, c);
            }
            break;
        case mp_char_operation:
            if (mp->cur_exp.type != mp_known_type) {
                mp_bad_unary(mp, mp_char_operation);
            } else {
                int n = round_unscaled(cur_exp_value_number) % 256;
                unsigned char s[2];
                mp_set_cur_exp_value_scaled(mp, n);
                mp->cur_exp.type = mp_string_type;
                if (number_negative(cur_exp_value_number)) {
                    n = number_to_scaled(cur_exp_value_number) + 256;
                    mp_set_cur_exp_value_scaled(mp, n);
                }
                s[0] = (unsigned char) number_to_scaled(cur_exp_value_number);
                s[1] = '\0';
                mp_set_cur_exp_str(mp, mp_rtsl (mp, (char *) s, 1));
            }
            break;
        case mp_decimal_operation:
            if (mp->cur_exp.type != mp_known_type) {
                mp_bad_unary(mp, mp_decimal_operation);
            } else {
                int selector = mp->selector;
                mp->selector = mp_new_string_selector;
                print_number(cur_exp_value_number);
                mp_set_cur_exp_str(mp, mp_make_string(mp));
                mp->selector = selector;
                mp->cur_exp.type = mp_string_type;
            }
            break;
        case mp_oct_operation:
        case mp_hex_operation:
        case mp_ASCII_operation:
            if (mp->cur_exp.type != mp_string_type) {
                mp_bad_unary(mp, c);
            } else {
                mp_str_to_num(mp);
            }
            break;
        case mp_length_operation:
            switch (mp->cur_exp.type) {
                case mp_string_type:
                    {
                        mp_value expr;
                        memset(&expr, 0, sizeof(mp_value));
                        new_number(expr.data.n);
                        number_clone(expr.data.n, unity_t);
                        number_multiply_int(expr.data.n, (int) cur_exp_str->len);
                        mp_flush_cur_exp(mp, expr);
                        break;
                    }
                case mp_path_type:
                    {
                        mp_value expr;
                        memset(&expr, 0, sizeof(mp_value));
                        new_number(expr.data.n);
                        mp_path_length(mp, &expr.data.n);
                        mp_flush_cur_exp(mp, expr);
                        break;
                    }
                case mp_known_type:
                    {
                        mp_set_cur_exp_value_number(mp, &cur_exp_value_number);
                        number_abs(cur_exp_value_number);
                        break;
                    }
                case mp_picture_type:
                    {
                        mp_value expr;
                        memset(&expr, 0, sizeof(mp_value));
                        new_number(expr.data.n);
                        mp_picture_length(mp, &expr.data.n);
                        mp_flush_cur_exp(mp, expr);
                        break;
                    }
                default:
                    if (mp_nice_pair (mp, cur_exp_node, mp->cur_exp.type)) {
                        mp_value expr;
                        memset(&expr, 0, sizeof(mp_value));
                        new_number(expr.data.n);
                        pyth_add(expr.data.n,
                            mp_get_value_number(mp_x_part(mp_get_value_node(cur_exp_node))),
                            mp_get_value_number(mp_y_part(mp_get_value_node(cur_exp_node)))
                        );
                        mp_flush_cur_exp(mp, expr);
                    } else {
                        mp_bad_unary(mp, c);
                    }
                    break;
            }
            break;
        case mp_no_length_operation:
            switch (mp->cur_exp.type) {
                case mp_path_type:
                    {
                        mp_value expr;
                        memset(&expr, 0, sizeof(mp_value));
                        new_number(expr.data.n);
                        mp_path_no_length(mp, &expr.data.n);
                        mp_flush_cur_exp(mp, expr);
                        mp->cur_exp.type = mp_boolean_type;
                        break;
                    }
                case mp_string_type:
                case mp_known_type:
                case mp_picture_type:
                default:
                    mp_bad_unary(mp, c);
                    break;
            }
            break;
        case mp_turning_operation:
            if (mp->cur_exp.type == mp_pair_type) {
                mp_value expr;
                memset(&expr, 0, sizeof(mp_value));
                new_number(expr.data.n);
                mp_flush_cur_exp(mp, expr);
            } else if (mp->cur_exp.type != mp_path_type) {
                mp_bad_unary(mp, mp_turning_operation);
            } else if (mp_left_type(cur_exp_knot) == mp_endpoint_knot) {
                mp_value expr;
                memset(&expr, 0, sizeof(mp_value));
                new_number(expr.data.n);
                expr.data.p = NULL;
                mp_flush_cur_exp(mp, expr);
            } else {
                mp_value expr;
                memset(&expr, 0, sizeof(mp_value));
                new_number(expr.data.n);
                mp_turn_cycles_wrapper(mp, &expr.data.n, cur_exp_knot);
                mp_flush_cur_exp(mp, expr);
            }
            break;

        case mp_boolean_type_operation:
        case mp_string_type_operation:
        case mp_pen_type_operation:
        case mp_nep_type_operation:
        case mp_path_type_operation:
        case mp_picture_type_operation:
            {
                mp_value expr;
                int type = (c - mp_boolean_type_operation) * 2 + mp_boolean_type ;
                memset(&expr, 0, sizeof(mp_value));
                new_number(expr.data.n);
                set_number_from_boolean(expr.data.n, (mp->cur_exp.type == type || mp->cur_exp.type == (type + 1)) ? mp_true_operation : mp_false_operation);
                mp_flush_cur_exp(mp, expr);
                mp->cur_exp.type = mp_boolean_type;
                break;
            }
        case mp_transform_type_operation:
        case mp_color_type_operation:
        case mp_cmykcolor_type_operation:
        case mp_pair_type_operation:
            {
                mp_value expr;
                int type = (c - mp_transform_type_operation) + mp_transform_type;
                memset(&expr, 0, sizeof(mp_value));
                new_number(expr.data.n);
                set_number_from_boolean(expr.data.n, mp->cur_exp.type == type ? mp_true_operation : mp_false_operation);
                mp_flush_cur_exp(mp, expr);
                mp->cur_exp.type = mp_boolean_type;
                break;
            }
        case mp_numeric_type_operation:
            {
                mp_value expr;
                memset(&expr, 0, sizeof(mp_value));
                new_number(expr.data.n);
                set_number_from_boolean(expr.data.n, (mp->cur_exp.type >= mp_known_type && mp->cur_exp.type <= mp_independent_type) ? mp_true_operation : mp_false_operation);
                mp_flush_cur_exp(mp, expr);
                mp->cur_exp.type = mp_boolean_type;
                break;
            }
        case mp_known_operation:
        case mp_unknown_operation:
            {
                mp_value expr;
                memset(&expr, 0, sizeof(mp_value));
                new_number(expr.data.n);
                set_number_from_boolean(expr.data.n, mp_test_known(mp, c));
                mp_flush_cur_exp(mp, expr);
                cur_exp_node = NULL;
                mp->cur_exp.type = mp_boolean_type;
                break;
            }
        case mp_cycle_operation:
        case mp_no_cycle_operation:
            {
                mp_value expr;
                int b = 0;
                memset(&expr, 0, sizeof(mp_value));
                new_number(expr.data.n);
                if (mp->cur_exp.type != mp_path_type) {
                    b = (c == mp_cycle_operation) ? mp_false_operation : mp_true_operation;
                } else if (mp_left_type(cur_exp_knot) != mp_endpoint_knot) {
                    b = (c == mp_cycle_operation) ? mp_true_operation : mp_false_operation;
                } else {
                    b = (c == mp_cycle_operation) ? mp_false_operation : mp_true_operation;
                }
                set_number_from_boolean(expr.data.n, b);
                mp_flush_cur_exp(mp, expr);
                mp->cur_exp.type = mp_boolean_type;
                break;
            }
        case mp_arc_length_operation:
            if (mp->cur_exp.type == mp_pair_type) {
                mp_pair_to_path(mp);
            }
            if (mp->cur_exp.type != mp_path_type) {
                mp_bad_unary(mp, mp_arc_length_operation);
            } else {
                mp_value expr;
                memset(&expr, 0, sizeof(mp_value));
                new_number(expr.data.n);
                mp_get_arc_length(mp, &expr.data.n, cur_exp_knot);
                mp_flush_cur_exp(mp, expr);
            }
            break;
        case mp_filled_operation:
        case mp_stroked_operation:
        case mp_clipped_operation:
        case mp_grouped_operation:
        case mp_bounded_operation:
            {
                mp_value expr;
                memset(&expr, 0, sizeof(mp_value));
                new_number(expr.data.n);
                if (mp->cur_exp.type != mp_picture_type) {
                    set_number_from_boolean(expr.data.n, mp_false_operation);
                } else if (mp_edge_list(cur_exp_node)->link == NULL) {
                    set_number_from_boolean(expr.data.n, mp_false_operation);
                } else {
                    int type = c - mp_filled_operation + mp_fill_node_type;
                    set_number_from_boolean(expr.data.n, mp_edge_list(cur_exp_node)->link->type == type ? mp_true_operation: mp_false_operation);
                }
                mp_flush_cur_exp(mp, expr);
                mp->cur_exp.type = mp_boolean_type;
                break;
            }
        case mp_make_pen_operation:
            if (mp->cur_exp.type == mp_pair_type) {
                mp_pair_to_path(mp);
            }
            if (mp->cur_exp.type != mp_path_type) {
                mp_bad_unary(mp, mp_make_pen_operation);
            } else {
                mp->cur_exp.type = mp_pen_type;
                mp_set_cur_exp_knot(mp, mp_make_pen(mp, cur_exp_knot, 1));
            }
            break;
        case mp_make_nep_operation:
            if (mp->cur_exp.type == mp_pair_type) {
                mp_pair_to_path(mp);
            }
            if (mp->cur_exp.type != mp_path_type) {
                mp_bad_unary(mp, c);
            } else {
                mp->cur_exp.type = mp_nep_type;
                mp_set_cur_exp_knot(mp, cur_exp_knot);
            }
            break;
        case mp_convexed_operation:
            if (mp->cur_exp.type != mp_path_type) {
                mp_bad_unary(mp, mp_convexed_operation);
            } else {
                mp->cur_exp.type = mp_path_type;
                mp_set_cur_exp_knot(mp, mp_convex_hull(mp, cur_exp_knot));
                mp_simplify_path(mp, cur_exp_knot);
            }
            break;
        case mp_uncontrolled_operation:
            if (mp->cur_exp.type != mp_path_type) {
                mp_bad_unary(mp, mp_uncontrolled_operation);
            } else {
                mp->cur_exp.type = mp_path_type;
                mp_simplify_path(mp, cur_exp_knot);
            }
            break;
        case mp_make_path_operation:
            if (mp->cur_exp.type != mp_pen_type && mp->cur_exp.type != mp_nep_type) {
                mp_bad_unary(mp, mp_make_path_operation);
            } else {
                mp->cur_exp.type = mp_path_type;
                mp_make_path(mp, cur_exp_knot);
            }
            break;
        case mp_reverse_operation:
            switch (mp->cur_exp.type) {
                case mp_path_type:
                    {
                        mp_knot pk = mp_htap_ypoc(mp, cur_exp_knot);
                        if (mp_right_type(pk) == mp_endpoint_knot) {
                            pk = mp_next_knot(pk);
                        }
                        mp_toss_knot_list(mp, cur_exp_knot);
                        mp_set_cur_exp_knot(mp, pk);
                    }
                    break;
                case mp_pair_type:
                    mp_pair_to_path(mp);
                    break;
                default:
                    mp_bad_unary(mp, mp_reverse_operation);
                    break;
            }
            break;
        case mp_uncycle_operation:
            switch (mp->cur_exp.type) {
                case mp_path_type:
                    mp_right_type(mp_prev_knot(cur_exp_knot)) = mp_endpoint_knot;
                    mp_left_type(cur_exp_knot) = mp_endpoint_knot;
                    break;
                case mp_pair_type:
                    mp_pair_to_path(mp);
                    break;
                default:
                    mp_bad_unary(mp, mp_uncycle_operation);
                    break;
            }
            break;
        case mp_ll_corner_operation:
            if (mp_get_cur_bbox(mp)) {
                mp_pair_value(mp, &mp_minx, &mp_miny);
            } else {
                mp_bad_unary(mp, mp_ll_corner_operation);
            }
            break;
        case mp_lr_corner_operation:
            if (mp_get_cur_bbox(mp)) {
                mp_pair_value(mp, &mp_maxx, &mp_miny);
            } else {
                mp_bad_unary(mp, mp_lr_corner_operation);
            }
            break;
        case mp_ul_corner_operation:
            if (mp_get_cur_bbox(mp)) {
                mp_pair_value(mp, &mp_minx, &mp_maxy);
            } else {
                mp_bad_unary(mp, mp_ul_corner_operation);
            }
            break;
        case mp_ur_corner_operation:
            if (! mp_get_cur_bbox(mp)) {
                mp_bad_unary(mp, mp_ur_corner_operation);
            } else {
                mp_pair_value(mp, &mp_maxx, &mp_maxy);
            }
            break;
        case mp_center_of_operation:
            if (mp->cur_exp.type == mp_pair_type) {
            } else if (mp_get_cur_bbox(mp)) {
                mp_number x, y;
                new_number(x);
                new_number(y);
                set_number_half_from_subtraction(x, mp_maxx, mp_minx);
                set_number_half_from_subtraction(y, mp_maxy, mp_miny);
                number_add(x, mp_minx);
                number_add(y, mp_miny);
                mp_pair_value(mp, &x, &y);
            } else {
                mp_bad_unary(mp, mp_center_of_operation);
            }
            break;
        case mp_center_of_mass_operation:
            if (mp->cur_exp.type == mp_pair_type) {
            } else if (mp->cur_exp.type == mp_path_type) {
                mp_knot p = cur_exp_knot;
                int l = 0;
                mp_number x, y;
                new_number(x);
                new_number(y);
                do {
                    ++l;
                    p = mp_next_knot(p);
                    number_add(x, p->x_coord);
                    number_add(y, p->y_coord);
                } while (p != cur_exp_knot);
                number_divide_int(x, l);
                number_divide_int(y, l);
                mp_pair_value(mp, &x, &y);
                free_number(x);
                free_number(y);
            } else {
                mp_bad_unary(mp, mp_center_of_mass_operation);
            }
            break;
        case mp_corners_operation:
            if (! mp_get_cur_bbox(mp)) {
                mp_bad_unary(mp, mp_corners_operation);
            } else {
                mp_knot ll = mp_simple_knot(mp, &mp_minx, &mp_miny);
                mp_knot lr = mp_simple_knot(mp, &mp_maxx, &mp_miny);
                mp_knot ur = mp_simple_knot(mp, &mp_maxx, &mp_maxy);
                mp_knot ul = mp_simple_knot(mp, &mp_minx, &mp_maxy);
                mp_prev_knot(lr) = ll;
                mp_next_knot(ll) = lr;
                mp_prev_knot(ur) = lr;
                mp_next_knot(lr) = ur;
                mp_prev_knot(ul) = ur;
                mp_next_knot(ur) = ul;
                mp_prev_knot(ll) = ul;
                mp_next_knot(ul) = ll;
                mp->cur_exp.type = mp_path_type;
                mp_set_cur_exp_knot(mp, ll);
            }
            break;
        case mp_x_range_operation:
            if (mp_get_cur_xbox(mp)) {
                mp_pair_value(mp, &mp_minx, &mp_maxx);
            } else {
                mp_bad_unary(mp, mp_x_range_operation);
            }
            break;
        case mp_y_range_operation:
            if (mp_get_cur_ybox(mp)) {
                mp_pair_value(mp, &mp_miny, &mp_maxy);
            } else {
                mp_bad_unary(mp, mp_y_range_operation);
            }
            break;
        case mp_delta_point_operation:
        case mp_delta_precontrol_operation:
        case mp_delta_postcontrol_operation:
        case mp_delta_direction_operation:
            if (mp->cur_exp.type == mp_known_type) {
                mp_set_cur_exp_value_number(mp, &cur_exp_value_number);
                if (mp->loop_ptr && mp->loop_ptr->point != NULL) {
                    mp_knot p = mp->loop_ptr->point;
                    int n = round_unscaled(cur_exp_value_number);
                    if (n > 0) {
                        while (n--) {
                            p = mp_next_knot(p);
                        }
                    } else if (n < 0) {
                        while (n++) {
                            p = mp_prev_knot(p);
                        }
                    }
                    push_of_path_result(mp, c - mp_delta_point_operation, p, mp->loop_ptr->value, mp->loop_ptr->final_value);
                }
            } else {
                mp_bad_unary(mp, c);
            }
            break;
        case mp_read_from_operation:
        case mp_close_from_operation:
            if (mp->cur_exp.type != mp_string_type) {
                mp_bad_unary(mp, c);
            } else {
                mp_do_read_or_close(mp, c);
            }
            break;
    }
    check_arith(mp);
}

static void mp_bad_color_part (MP mp, int c)
{
    mp_node p;
    mp_value new_expr;
    char msg[256];
    int selector;
    mp_string sname;
    memset(&new_expr, 0, sizeof(mp_value));
    new_number(new_expr.data.n);
    p = mp_edge_list(cur_exp_node)->link;
    mp_disp_err(mp, NULL);
    selector = mp->selector;
    mp->selector = mp_new_string_selector;
    mp_print_op(mp, c);
    sname = mp_make_string(mp);
    mp->selector = selector;
    switch (mp_color_model(p)) {
        case mp_grey_model: mp_snprintf(msg, 256, "Wrong picture color model: %s of grey object",      mp_str(mp, sname)); break;
        case mp_cmyk_model: mp_snprintf(msg, 256, "Wrong picture color model: %s of cmyk object",      mp_str(mp, sname)); break;
        case mp_rgb_model:  mp_snprintf(msg, 256, "Wrong picture color model: %s of rgb object",       mp_str(mp, sname)); break;
        case mp_no_model:   mp_snprintf(msg, 256, "Wrong picture color model: %s of marking object",   mp_str(mp, sname)); break;
        default:            mp_snprintf(msg, 256, "Wrong picture color model: %s of defaulted object", mp_str(mp, sname)); break;
    }
    delete_str_ref(sname);
    mp_error(
        mp,
        msg,
        "You can only ask for the redpart, greenpart, bluepart of a rgb object, the\n"
        "cyanpart, magentapart, yellowpart or blackpart of a cmyk object, or the greypart\n"
        "of a grey object. No mixing and matching, please."
    );
    if (c == mp_black_part_operation) {
        number_clone(new_expr.data.n, unity_t);
    } else {
        set_number_to_zero(new_expr.data.n);
    }
    mp_flush_cur_exp(mp, new_expr);
}

static void mp_bezier_slope (MP mp,
    mp_number *ret, mp_number *AX, mp_number *AY, mp_number *BX,
    mp_number *BY, mp_number *CX, mp_number *CY, mp_number *DX,
    mp_number *DY
)
{
    double a, b, c;
    mp_number deltax, deltay;
    mp_number xi, xo, xm;
    double res = 0.0;
    double ax = number_to_double(*AX);
    double ay = number_to_double(*AY);
    double bx = number_to_double(*BX);
    double by = number_to_double(*BY);
    double cx = number_to_double(*CX);
    double cy = number_to_double(*CY);
    double dx = number_to_double(*DX);
    double dy = number_to_double(*DY);
    new_number(deltax);
    new_number(deltay);
    set_number_from_subtraction(deltax, *BX, *AX);
    set_number_from_subtraction(deltay, *BY, *AY);
    if (number_zero(deltax) && number_zero(deltay)) {
        set_number_from_subtraction(deltax, *CX, *AX);
        set_number_from_subtraction(deltay, *CY, *AY);
    }
    if (number_zero(deltax) && number_zero(deltay)) {
        set_number_from_subtraction(deltax, *DX, *AX);
        set_number_from_subtraction(deltay, *DY, *AY);
    }
    new_number(xi);
    new_number(xm);
    new_number(xo);
    mp_an_angle(mp, &xi, &deltax, &deltay);
    set_number_from_subtraction(deltax, *CX, *BX);
    set_number_from_subtraction(deltay, *CY, *BY);
    mp_an_angle(mp, &xm, &deltax, &deltay);
    set_number_from_subtraction(deltax, *DX, *CX);
    set_number_from_subtraction(deltay, *DY, *CY);
    if (number_zero(deltax) && number_zero(deltay)) {
        set_number_from_subtraction(deltax, *DX, *BX);
        set_number_from_subtraction(deltay, *DY, *BY);
    }
    if (number_zero(deltax) && number_zero(deltay)) {
        set_number_from_subtraction(deltax, *DX, *AX);
        set_number_from_subtraction(deltay, *DY, *AY);
    }
    mp_an_angle(mp, &xo, &deltax, &deltay);
    a = (bx - ax) * (cy - by) - (cx - bx) * (by - ay);
    b = (bx - ax) * (dy - cy) - (by - ay) * (dx - cx);
    c = (cx - bx) * (dy - cy) - (dx - cx) * (cy - by);
    if ((a == 0.0) && (c == 0.0)) {
        res = (b == 0.0 ? 0.0 : (mp_out(number_to_double(xo)) - mp_out(number_to_double(xi))));
    } else if ((a == 0.0) || (c == 0.0)) {
        if ((mp_sign (b) == mp_sign (a)) || (mp_sign (b) == mp_sign (c))) {
            res = mp_out(number_to_double(xo)) - mp_out(number_to_double(xi));
            if (res < -180.0) {
                res += 360.0;
            } else if (res > 180.0) {
                res -= 360.0;
            }
        } else {
            res = mp_out(number_to_double(xo)) - mp_out(number_to_double(xi));
        }
    } else if ((mp_sign (a) * mp_sign (c)) < 0.0) {
        res = mp_out(number_to_double(xo)) - mp_out(number_to_double(xi));
        if (res < -180.0) {
            res += 360.0;
        } else if (res > 180.0) {
            res -= 360.0;
        }
    } else if (mp_sign (a) == mp_sign (b)) {
        res = mp_out(number_to_double(xo)) - mp_out(number_to_double(xi));
        if (res < -180.0) {
            res += 360.0;
        } else if (res > 180.0) {
            res -= 360.0;
        }
    } else if ((b * b) == (4.0 * a * c)) {
        res = (double) bezier_error;
    } else if ((b * b) < (4.0 * a * c)) {
        res = mp_out(number_to_double(xo)) - mp_out(number_to_double(xi));
        if (res <= 0.0 && res > -180.0) {
            res += 360.0;
        } else if (res >= 0.0 && res < 180.0) {
            res -= 360.0;
        }
    } else {
        res = mp_out(number_to_double(xo)) - mp_out(number_to_double(xi));
        if (res < -180.0) {
            res += 360.0;
        } else if (res > 180.0) {
            res -= 360.0;
        }
    }
    free_number(deltax);
    free_number(deltay);
    free_number(xi);
    free_number(xo);
    free_number(xm);
    set_number_from_double(*ret, res);
    convert_scaled_to_angle(*ret);
}

static void mp_bad_binary (MP mp, mp_node p, int c)
{
    char msg[256];
    mp_string sname;
    int selector = mp->selector;
    mp->selector = mp_new_string_selector;
    if (c >= mp_min_of_operation) {
        mp_print_op(mp, c);
    }
    mp_print_known_or_unknown_type(mp, p->type, p);
    if (c >= mp_min_of_operation) {
        mp_print_str(mp, "of");
    } else {
        mp_print_op(mp, c);
    }
    mp_print_known_or_unknown_type(mp, mp->cur_exp.type, cur_exp_node);
    sname = mp_make_string(mp);
    mp->selector = selector;
    mp_snprintf(msg, 256, "Not implemented: %s", mp_str(mp, sname));
    delete_str_ref(sname);
    mp_disp_err(mp, p);
    mp_disp_err(mp, NULL);
    mp_back_error(
        mp,
        msg,
        "I'm afraid I don't know how to apply that operation to that combination of types.\n"
        "Continue, and I'll return the second argument (see above) as the result of the"
        "operation."
    );
    mp_get_x_next(mp);
}
static void mp_bad_envelope_pen (MP mp)
{
    mp_disp_err(mp, NULL);
    mp_disp_err(mp, NULL);
    mp_back_error(
        mp,
        "Not implemented: 'envelope(elliptical pen) of (path)'",
        "I'm afraid I don't know how to apply that operation to that combination of types.\n"
        "Continue, and I'll return the second argument (see above) as the result of the\n"
        "operation."
    );
    mp_get_x_next(mp);
}
static mp_node mp_tarnished (MP mp, mp_node p)
{
    mp_node q = mp_get_value_node(p);
    (void) mp;
    switch (p->type) {
        case mp_pair_type:
            return (
                (mp_x_part(q)->type == mp_independent_type) ||
                (mp_y_part(q)->type == mp_independent_type)
            ) ? MP_VOID : NULL;
        case mp_color_type:
            return (
                (mp_red_part  (q)->type == mp_independent_type) ||
                (mp_green_part(q)->type == mp_independent_type) ||
                (mp_blue_part (q)->type == mp_independent_type)
            ) ? MP_VOID : NULL;
        case mp_cmykcolor_type:
            return (
                (mp_cyan_part   (q)->type == mp_independent_type) ||
                (mp_magenta_part(q)->type == mp_independent_type) ||
                (mp_yellow_part (q)->type == mp_independent_type) ||
                (mp_black_part  (q)->type == mp_independent_type)
            ) ? MP_VOID : NULL;
        case mp_transform_type:
            return (
                (mp_tx_part(q)->type == mp_independent_type) ||
                (mp_ty_part(q)->type == mp_independent_type) ||
                (mp_xx_part(q)->type == mp_independent_type) ||
                (mp_xy_part(q)->type == mp_independent_type) ||
                (mp_yx_part(q)->type == mp_independent_type) ||
                (mp_yy_part(q)->type == mp_independent_type)
            ) ? MP_VOID : NULL;
        default:
            return NULL;
    }
}
static void mp_dep_finish (MP mp, mp_value_node v, mp_value_node q, int t)
{
    mp_value_node p = (q == NULL) ? (mp_value_node) cur_exp_node : q;
    mp_set_dep_list(p, v);
    p->type = t;
    if (mp_get_dep_info(v) == NULL) {
        mp_number vv;
        new_number_clone(vv, mp_get_value_number(v));
        if (q == NULL) {
            mp_value new_expr;
            memset(&new_expr, 0, sizeof(mp_value));
            new_number_clone(new_expr.data.n, vv);
            mp_flush_cur_exp(mp, new_expr);
        } else {
            mp_recycle_value(mp, (mp_node) p);
            q->type = mp_known_type;
            mp_set_value_number(q, vv);
        }
        free_number(vv);
    } else if (q == NULL) {
        mp->cur_exp.type = t;
    }
    if (mp->fix_needed) {
        mp_fix_dependencies(mp);
    }
}

static void mp_add_or_subtract (MP mp, mp_node p, mp_node q, int c)
{
    mp_variable_type s, t;
    mp_value_node r;
    mp_value_node v = NULL;
    mp_number vv;
    new_number(vv);
    if (q == NULL) {
        t = mp->cur_exp.type;
        if (t < mp_dependent_type) {
            number_clone(vv, cur_exp_value_number);
        } else {
            v = (mp_value_node) mp_get_dep_list((mp_value_node) cur_exp_node);
        }
    } else {
        t = q->type;
        if (t < mp_dependent_type) {
            number_clone(vv, mp_get_value_number(q));
        } else {
            v = (mp_value_node) mp_get_dep_list((mp_value_node) q);
        }
    }
    if (t == mp_known_type) {
        mp_value_node qq = (mp_value_node) q;
        if (c == mp_minus_operation) {
            number_negate(vv);
        }
        if (p->type == mp_known_type) {
            slow_add(vv, mp_get_value_number(p), vv);
            if (q == NULL) {
                mp_set_cur_exp_value_number(mp, &vv);
            } else {
                mp_set_value_number(q, vv);
            }
            free_number(vv);
            return;
        } else {
            r = (mp_value_node) mp_get_dep_list((mp_value_node) p);
            while (mp_get_dep_info(r) != NULL) {
                r = (mp_value_node) r->link;
            }
            slow_add(vv, mp_get_dep_value(r), vv);
            mp_set_dep_value(r, vv);
            if (qq == NULL) {
                qq = mp_get_dep_node(mp);
                mp_set_cur_exp_node(mp, (mp_node) qq);
                mp->cur_exp.type = p->type;
                qq->name_type = mp_capsule_operation;
            }
            mp_set_dep_list(qq, mp_get_dep_list((mp_value_node) p));
            qq->type = p->type;
            mp_set_prev_dep(qq, mp_get_prev_dep((mp_value_node) p));
            mp_get_prev_dep((mp_value_node) p)->link = (mp_node) qq;
            p->type = mp_known_type;
        }
    } else {
        if (c == mp_minus_operation) {
            mp_negate_dep_list(mp, v);
        }
        if (p->type == mp_known_type) {
            while (mp_get_dep_info(v) != NULL) {
                v = (mp_value_node) v->link;
            }
            slow_add(vv, mp_get_value_number(p), mp_get_dep_value(v));
            mp_set_dep_value(v, vv);
        } else {
            s = p->type;
            r = (mp_value_node) mp_get_dep_list((mp_value_node) p);
            if (t == mp_dependent_type) {
                if (s == mp_dependent_type) {
                    int b;
                    mp_number ret1, ret2;
                    new_fraction(ret1);
                    new_fraction(ret2);
                    mp_max_coef(mp, &ret1, r);
                    mp_max_coef(mp, &ret2, v);
                    number_add(ret1, ret2);
                    b = number_less(ret1, coef_bound_k);
                    free_number(ret1);
                    free_number(ret2);
                    if (b) {
                        v = mp_p_plus_q(mp, v, r, mp_dependent_type);
                        goto DONE;
                    }
                }
                t = mp_proto_dependent_type;
                v = mp_p_over_v(mp, v, &unity_t, mp_dependent_type, mp_proto_dependent_type);
            }
            if (s == mp_proto_dependent_type) {
                v = mp_p_plus_q(mp, v, r, mp_proto_dependent_type);
            } else {
                v = mp_p_plus_fq(mp, v, &unity_t, r, mp_proto_dependent_type, mp_dependent_type);
            }
          DONE:
            if (q != NULL) {
                mp_dep_finish(mp, v, (mp_value_node) q, t);
            } else {
                mp->cur_exp.type = t;
                mp_dep_finish(mp, v, NULL, t);
            }
        }
    }
    free_number(vv);
}
static void mp_dep_mult (MP mp, mp_value_node p, mp_number *v, int v_is_scaled)
{
    mp_value_node q;
    int s, t;
    if (p == NULL) {
        q = (mp_value_node) cur_exp_node;
    } else if (p->type != mp_known_type) {
        q = p;
    } else {
        mp_number r1, arg1;
        new_number_clone(arg1, mp_get_dep_value(p));
        if (v_is_scaled) {
            new_number(r1);
            take_scaled(r1, arg1, *v);
        } else {
            new_fraction(r1);
            take_fraction(r1, arg1, *v);
        }
        mp_set_dep_value(p, r1);
        free_number(r1);
        free_number(arg1);
        return;
    }
    t = q->type;
    q = (mp_value_node) mp_get_dep_list(q);
    s = t;
    if (t == mp_dependent_type && v_is_scaled) {
        mp_number arg1, arg2;
        new_fraction(arg1);
        mp_max_coef(mp, &arg1, q);
        new_number_abs(arg2, *v);
        if (ab_vs_cd(arg1, arg2, coef_bound_minus_1, unity_t) >= 0) {
            t = mp_proto_dependent_type;
        }
        free_number(arg1);
        free_number(arg2);
    }
    q = mp_p_times_v(mp, q, v, s, t, v_is_scaled);
    mp_dep_finish(mp, q, p, t);
}
static void mp_hard_times (MP mp, mp_node p)
{
    mp_value_node q;
    mp_value_node pp;
    mp_number v;
    new_number(v);
    if (p->type <= mp_pair_type) {
        q = (mp_value_node) mp_stash_cur_exp(mp);
        mp_unstash_cur_exp(mp, p);
        p = (mp_node) q;
    }
    pp = (mp_value_node) p;
    switch (mp->cur_exp.type) {
        case mp_pair_type:
            {
                mp_node r = mp_x_part(mp_get_value_node(cur_exp_node));
                number_clone(v, mp_get_value_number(r));
                mp_new_dep(mp, r, pp->type, mp_copy_dep_list(mp, (mp_value_node) mp_get_dep_list(pp)));
                mp_dep_mult(mp, (mp_value_node) r, &v, 1);
                r = mp_y_part(mp_get_value_node(cur_exp_node));
                number_clone(v, mp_get_value_number(r));
                mp_new_dep(mp, r, pp->type, mp_copy_dep_list(mp, (mp_value_node) mp_get_dep_list(pp)));
                mp_dep_mult(mp, (mp_value_node) r, &v, 1);
            }
            break;
        case mp_color_type:
            {
                mp_node r = mp_red_part(mp_get_value_node(cur_exp_node));
                number_clone(v, mp_get_value_number(r));
                mp_new_dep(mp, r, pp->type, mp_copy_dep_list(mp, (mp_value_node) mp_get_dep_list(pp)));
                mp_dep_mult(mp, (mp_value_node) r, &v, 1);
                r = mp_green_part(mp_get_value_node(cur_exp_node));
                number_clone(v, mp_get_value_number(r));
                mp_new_dep(mp, r, pp->type, mp_copy_dep_list(mp, (mp_value_node) mp_get_dep_list(pp)));
                mp_dep_mult(mp, (mp_value_node) r, &v, 1);
                r = mp_blue_part(mp_get_value_node(cur_exp_node));
                number_clone(v, mp_get_value_number(r));
                mp_new_dep(mp, r, pp->type, mp_copy_dep_list(mp, (mp_value_node) mp_get_dep_list(pp)));
                mp_dep_mult(mp, (mp_value_node) r, &v, 1);
            }
            break;
        case mp_cmykcolor_type:
            {
                mp_node r = mp_cyan_part(mp_get_value_node(cur_exp_node));
                number_clone(v, mp_get_value_number(r));
                mp_new_dep(mp, r, pp->type, mp_copy_dep_list(mp, (mp_value_node) mp_get_dep_list(pp)));
                mp_dep_mult(mp, (mp_value_node) r, &v, 1);
                r = mp_yellow_part(mp_get_value_node(cur_exp_node));
                number_clone(v, mp_get_value_number(r));
                mp_new_dep(mp, r, pp->type, mp_copy_dep_list(mp, (mp_value_node) mp_get_dep_list(pp)));
                mp_dep_mult(mp, (mp_value_node) r, &v, 1);
                r = mp_magenta_part(mp_get_value_node(cur_exp_node));
                number_clone(v, mp_get_value_number(r));
                mp_new_dep(mp, r, pp->type, mp_copy_dep_list(mp, (mp_value_node) mp_get_dep_list(pp)));
                mp_dep_mult(mp, (mp_value_node) r, &v, 1);
                r = mp_black_part(mp_get_value_node(cur_exp_node));
                number_clone(v, mp_get_value_number(r));
                mp_new_dep(mp, r, pp->type, mp_copy_dep_list(mp, (mp_value_node) mp_get_dep_list(pp)));
                mp_dep_mult(mp, (mp_value_node) r, &v, 1);
            }
            break;
        default:
            break;
    }
    free_number(v);
}
static void mp_dep_div (MP mp, mp_value_node p, mp_number *v)
{
    mp_value_node q;
    int s, t;
    if (p == NULL) {
        q = (mp_value_node) cur_exp_node;
    } else if (p->type != mp_known_type) {
        q = p;
    } else {
        mp_number ret;
        new_number(ret);
        make_scaled(ret, mp_get_value_number(p), *v);
        mp_set_value_number(p, ret);
        free_number(ret);
        return;
    }
    t = q->type;
    q = (mp_value_node) mp_get_dep_list(q);
    s = t;
    if (t == mp_dependent_type) {
        mp_number arg1, arg2;
        new_number(arg2);
        new_fraction(arg1);
        mp_max_coef(mp, &arg1, q);
        number_abs_clone(arg2, *v);
        if (ab_vs_cd(arg1, unity_t, coef_bound_minus_1, arg2) >= 0) {
            t = mp_proto_dependent_type;
        }
        free_number(arg1);
        free_number(arg2);
    }
    q = mp_p_over_v(mp, q, v, s, t);
    mp_dep_finish(mp, q, p, t);
}
static void mp_set_up_trans (MP mp, int c)
{
    mp_node p, q, r;
    mp_value new_expr;
    memset(&new_expr, 0, sizeof(mp_value));
    if ((c != mp_transformed_operation) || (mp->cur_exp.type != mp_transform_type)) {
        p = mp_stash_cur_exp(mp);
        mp_set_cur_exp_node(mp, mp_id_transform(mp));
        mp->cur_exp.type = mp_transform_type;
        q = mp_get_value_node(cur_exp_node);
        switch (c) {
            case mp_rotated_operation:
                if (p->type == mp_known_type) {
                    mp_number n_sin, n_cos, arg1, arg2;
                    new_fraction(n_sin);
                    new_fraction(n_cos);
                    new_number_clone(arg2, unity_t);
                    new_number_clone(arg1, mp_get_value_number(p));
                    number_multiply_int(arg2, 360);
                    number_modulo(arg1, arg2);
                    convert_scaled_to_angle(arg1);
                    n_sin_cos(arg1, n_cos, n_sin);
                    fraction_to_round_scaled(n_sin);
                    fraction_to_round_scaled(n_cos);
                    mp_set_value_number(mp_xx_part(q), n_cos);
                    mp_set_value_number(mp_yx_part(q), n_sin);
                    mp_set_value_number(mp_xy_part(q), mp_get_value_number(mp_yx_part(q)));
                    number_negate(mp_get_value_number(mp_xy_part(q)));
                    mp_set_value_number(mp_yy_part(q), mp_get_value_number(mp_xx_part(q)));
                    free_number(arg1);
                    free_number(arg2);
                    free_number(n_sin);
                    free_number(n_cos);
                    goto DONE;
                }
                break;
            case mp_slanted_operation:
                if (p->type > mp_pair_type) {
                    mp_install(mp, mp_xy_part(q), p);
                    goto DONE;
                }
                break;
            case mp_scaled_operation:
                if (p->type > mp_pair_type) {
                    mp_install(mp, mp_xx_part(q), p);
                    mp_install(mp, mp_yy_part(q), p);
                    goto DONE;
                }
                break;
            case mp_shifted_operation:
                if (p->type == mp_pair_type) {
                    r = mp_get_value_node(p);
                    mp_install(mp, mp_tx_part(q), mp_x_part(r));
                    mp_install(mp, mp_ty_part(q), mp_y_part(r));
                    goto DONE;
                }
                break;
            case mp_x_scaled_operation:
                if (p->type > mp_pair_type) {
                    mp_install(mp, mp_xx_part(q), p);
                    goto DONE;
                }
                break;
            case mp_y_scaled_operation:
                if (p->type > mp_pair_type) {
                    mp_install(mp, mp_yy_part(q), p);
                    goto DONE;
                }
                break;
            case mp_z_scaled_operation:
                if (p->type == mp_pair_type) {
                    {
                        r = mp_get_value_node(p);
                        mp_install(mp, mp_xx_part(q), mp_x_part(r));
                        mp_install(mp, mp_yy_part(q), mp_x_part(r));
                        mp_install(mp, mp_yx_part(q), mp_y_part(r));
                        if (mp_y_part(r)->type == mp_known_type) {
                            mp_set_value_number(mp_y_part(r), mp_get_value_number(mp_y_part(r)));
                            number_negate(mp_get_value_number(mp_y_part(r)));
                        } else {
                            mp_negate_dep_list(mp, (mp_value_node) mp_get_dep_list((mp_value_node) mp_y_part(r)));
                        }
                        mp_install(mp, mp_xy_part(q), mp_y_part(r));
                        goto DONE;
                    }
                }
                break;
            case mp_transformed_operation:
                break;
        }
        mp_disp_err(mp, p);
        mp_back_error(
            mp,
            "Improper transformation argument",
            "The expression shown above has the wrong type, so I can't transform anything\n"
            "using it. Proceed, and I'll omit the transformation."
         );
        mp_get_x_next(mp);
      DONE:
        mp_recycle_value(mp, p);
        mp_free_value_node(mp, p);
    }
    q = mp_get_value_node(cur_exp_node);
    if ( (mp_tx_part(q)->type == mp_known_type) &&
         (mp_ty_part(q)->type == mp_known_type) &&
         (mp_xx_part(q)->type == mp_known_type) &&
         (mp_xy_part(q)->type == mp_known_type) &&
         (mp_yx_part(q)->type == mp_known_type) &&
         (mp_yy_part(q)->type == mp_known_type) ) {
        number_clone(mp->txx, mp_get_value_number(mp_xx_part(q)));
        number_clone(mp->txy, mp_get_value_number(mp_xy_part(q)));
        number_clone(mp->tyx, mp_get_value_number(mp_yx_part(q)));
        number_clone(mp->tyy, mp_get_value_number(mp_yy_part(q)));
        number_clone(mp->tx,  mp_get_value_number(mp_tx_part(q)));
        number_clone(mp->ty,  mp_get_value_number(mp_ty_part(q)));
        new_number(new_expr.data.n);
        mp_flush_cur_exp(mp, new_expr);
    }
}
static void mp_set_up_known_trans (MP mp, int c)
{
    mp_set_up_trans(mp, c);
    if (mp->cur_exp.type != mp_known_type) {
        mp_value new_expr;
        memset(&new_expr, 0, sizeof(mp_value));
        new_number(new_expr.data.n);
        mp_disp_err(mp, NULL);
        mp_back_error(
            mp,
            "Transform components aren't all known",
            "I'm unable to apply a partially specified transformation except to a fully known\n"
            "pair or transform. Proceed, and I'll omit the transformation."
        );
        mp_get_x_next(mp);
        mp_flush_cur_exp(mp, new_expr);
        set_number_to_unity(mp->txx);
        set_number_to_zero(mp->txy);
        set_number_to_zero(mp->tyx);
        set_number_to_unity(mp->tyy);
        set_number_to_zero(mp->tx);
        set_number_to_zero(mp->ty);
    }
}
static void mp_number_trans (MP mp, mp_number *p, mp_number *q)
{
    mp_number r1, r2, v;
    new_number(r1);
    new_number(r2);
    new_number(v);
    take_scaled(r1, *p, mp->txx);
    take_scaled(r2, *q, mp->txy);
    number_add(r1, r2);
    set_number_from_addition(v, r1, mp->tx);
    take_scaled(r1, *p, mp->tyx);
    take_scaled(r2, *q, mp->tyy);
    number_add(r1, r2);
    set_number_from_addition(*q, r1, mp->ty);
    number_clone(*p,v);
    free_number(r1);
    free_number(r2);
    free_number(v);
}
static void mp_do_path_trans (MP mp, mp_knot p)
{
    mp_knot q = p;
    do {
        if (mp_left_type(q) != mp_endpoint_knot) {
            mp_number_trans(mp, &q->left_x, &q->left_y);
        }
        mp_number_trans(mp, &q->x_coord, &q->y_coord);
        if (mp_right_type(q) != mp_endpoint_knot) {
            mp_number_trans(mp, &q->right_x, &q->right_y);
        }
        q = mp_next_knot(q);
    } while (q != p);
}
static void mp_do_pen_trans (MP mp, mp_knot p)
{
    mp_knot q = p;
    if (mp_pen_is_elliptical(p)) {
        mp_number_trans(mp, &p->left_x, &p->left_y);
        mp_number_trans(mp, &p->right_x, &p->right_y);
    }
    do {
        mp_number_trans(mp, &q->x_coord, &q->y_coord);
        q = mp_next_knot(q);
    } while (q != p);
}
static void mp_do_path_pen_trans (MP mp, mp_shape_node p, mp_number *sqdet, int sgndet)
{
    mp_number sx, sy;
    if (mp_pen_ptr(p) != NULL) {
        new_number_clone(sx, mp->tx);
        new_number_clone(sy, mp->ty);
        set_number_to_zero(mp->tx);
        set_number_to_zero(mp->ty);
        mp_do_pen_trans(mp, mp_pen_ptr(p));
        if (number_nonzero(*sqdet) && ((p->type == mp_stroked_node_type) && (mp_dash_ptr(p) != NULL))) {
            mp_number ret;
            new_number(ret);
            take_scaled(ret, ((mp_shape_node) p)->dashscale, *sqdet);
            number_clone(((mp_shape_node) p)->dashscale, ret);
            free_number(ret);
        }
        if (! mp_pen_is_elliptical(mp_pen_ptr(p)) && sgndet < 0) {
            mp_pen_ptr(p) = mp_make_pen(mp, mp_copy_path(mp, mp_pen_ptr(p)), 1);
        }
        number_clone(mp->tx, sx);
        number_clone(mp->ty, sy);
        free_number(sx);
        free_number(sy);
    }
}
static mp_edge_header_node mp_edges_trans (MP mp, mp_edge_header_node h)
{
    mp_node q;
    mp_dash_node r, s;
    mp_number sqdet;
    int sgndet;
    h = mp_private_edges(mp, h);
    new_number(sqdet);
    mp_sqrt_det(mp, &sqdet, &(mp->txx), &(mp->txy), &(mp->tyx), &(mp->tyy));
    sgndet = ab_vs_cd(mp->txx, mp->tyy, mp->txy, mp->tyx);
    if (mp_get_dash_list(h) != mp->null_dash) {
        if (number_nonzero(mp->txy) || number_nonzero(mp->tyx) || number_nonzero(mp->ty) || number_nonequalabs(mp->txx, mp->tyy)) {
            mp_flush_dash_list(mp, h);
        } else {
            mp_number abs_tyy, ret;
            new_number(abs_tyy);
            if (number_negative(mp->txx)) {
                {
                    r = mp_get_dash_list(h);
                    mp_set_dash_list(h, mp->null_dash);
                    while (r != mp->null_dash) {
                        s = r;
                        r = (mp_dash_node) r->link;
                        number_swap(s->start_x, s->stop_x );
                        s->link = (mp_node) mp_get_dash_list(h);
                        mp_set_dash_list(h, s);
                    }
                }
            }
            r = mp_get_dash_list(h);
            {
                mp_number arg1;
                new_number(arg1);
                while (r != mp->null_dash) {
                    take_scaled(arg1, r->start_x, mp->txx);
                    set_number_from_addition(r->start_x, arg1, mp->tx);
                    take_scaled(arg1, r->stop_x, mp->txx);
                    set_number_from_addition(r->stop_x, arg1, mp->tx);
                    r = (mp_dash_node) r->link;
                }
                free_number(arg1);
            }
            number_abs_clone(abs_tyy, mp->tyy);
            new_number(ret);
            take_scaled(ret, h->dash_y, abs_tyy);
            number_clone(h->dash_y, ret);
            free_number(ret);
            free_number(abs_tyy);
        }
    }
    if (number_zero(mp->txx) && number_zero(mp->tyy)) {
        number_swap(h->minx, h->miny);
        number_swap(h->maxx, h->maxy);
    } else if (number_nonzero(mp->txy) || number_nonzero(mp->tyx)) {
        mp_init_bbox(mp, h);
        goto DONE1;
    }
    if (number_lessequal(h->minx, h->maxx)) {
        mp_number tot, ret;
        new_number(tot);
        new_number(ret);
        set_number_from_addition(tot,mp->txx,mp->txy);
        take_scaled(ret, h->minx, tot);
        set_number_from_addition(h->minx,ret, mp->tx);
        take_scaled(ret, h->maxx, tot);
        set_number_from_addition(h->maxx,ret, mp->tx);
        set_number_from_addition(tot,mp->tyx,mp->tyy);
        take_scaled(ret, h->miny, tot);
        set_number_from_addition(h->miny, ret, mp->ty);
        take_scaled(ret, h->maxy, tot);
        set_number_from_addition(h->maxy, ret, mp->ty);
        set_number_from_addition(tot, mp->txx, mp->txy);
        if (number_negative(tot)) {
            number_swap(h->minx, h->maxx);
        }
        set_number_from_addition(tot, mp->tyx, mp->tyy);
        if (number_negative(tot)) {
            number_swap(h->miny, h->maxy);
        }
        free_number(ret);
        free_number(tot);
    }
    DONE1:
    q = mp_edge_list(h)->link;
    while (q != NULL) {
        switch (q->type) {
            case mp_fill_node_type:
            case mp_stroked_node_type:
                mp_do_path_trans(mp, mp_path_ptr((mp_shape_node) q));
                mp_do_path_pen_trans(mp, (mp_shape_node) q, &sqdet, sgndet);
                break;
            case mp_start_clip_node_type:
            case mp_start_group_node_type:
            case mp_start_bounds_node_type:
                mp_do_path_trans(mp, mp_path_ptr((mp_start_node) q));
                break;
            case mp_stop_clip_node_type:
            case mp_stop_group_node_type:
            case mp_stop_bounds_node_type:
                break;
            default:
                break;
        }
        q = q->link;
    }
    free_number(sqdet);
    return h;
}
static void mp_do_edges_trans (MP mp, mp_node p, int c)
{
    mp_set_up_known_trans (mp, c);
    mp_set_value_node(p, (mp_node) mp_edges_trans(mp, (mp_edge_header_node) mp_get_value_node(p)));
    mp_unstash_cur_exp(mp, p);
}
static mp_edge_header_node mp_scale_edges (MP mp, mp_number *se_sf, mp_edge_header_node se_pic)
{
    number_clone(mp->txx, *se_sf);
    number_clone(mp->tyy, *se_sf);
    set_number_to_zero(mp->txy);
    set_number_to_zero(mp->tyx);
    set_number_to_zero(mp->tx);
    set_number_to_zero(mp->ty);
    return mp_edges_trans(mp, se_pic);
}
static void mp_bilin1 (MP mp, mp_node p, mp_number *t, mp_node q, mp_number *u, mp_number *delta_orig)
{
    mp_number delta;
    new_number_clone(delta, *delta_orig);
    if (! number_equal(*t, unity_t)) {
        mp_dep_mult(mp, (mp_value_node) p, t, 1);
    }
    if (number_nonzero(*u)) {
        if (q->type == mp_known_type) {
            mp_number tmp;
            new_number(tmp);
            take_scaled(tmp, mp_get_value_number(q), *u);
            number_add(delta, tmp);
            free_number(tmp);
        } else {
            if (p->type != mp_proto_dependent_type) {
                if (p->type == mp_known_type) {
                    mp_new_dep(mp, p, p->type, mp_const_dependency(mp, &(mp_get_value_number(p))));
                } else {
                    mp_set_dep_list((mp_value_node) p,
                        mp_p_times_v(mp,
                            (mp_value_node) mp_get_dep_list((mp_value_node) p), &unity_t,
                            mp_dependent_type, mp_proto_dependent_type, 1));
                }
                p->type = mp_proto_dependent_type;
            }
            mp_set_dep_list((mp_value_node) p,
                mp_p_plus_fq(mp,
                    (mp_value_node) mp_get_dep_list((mp_value_node) p), u,
                    (mp_value_node) mp_get_dep_list((mp_value_node) q),
                    mp_proto_dependent_type, q->type));
        }
    }
    if (p->type == mp_known_type) {
        mp_set_value_number(p, mp_get_value_number(p));
        number_add(mp_get_value_number(p), delta);
    } else {
        mp_number tmp;
        mp_value_node r = (mp_value_node) mp_get_dep_list((mp_value_node) p);
        while (mp_get_dep_info(r) != NULL) {
            r = (mp_value_node) r->link;
        }
        new_number_clone(tmp, mp_get_value_number(r));
        number_add(delta, tmp);

        if (r != (mp_value_node) mp_get_dep_list((mp_value_node) p)) {
            mp_set_value_number(r, delta);
        } else {
            mp_recycle_value(mp, p);
            p->type = mp_known_type;
            mp_set_value_number(p, delta);
        }
        free_number(tmp);
    }
    if (mp->fix_needed) {
        mp_fix_dependencies(mp);
    }
    free_number(delta);
}
static void mp_add_mult_dep (MP mp, mp_value_node p, mp_number *v, mp_node r)
{
    if (r->type == mp_known_type) {
        mp_number ret;
        new_number(ret);
        take_scaled(ret, mp_get_value_number(r), *v);
        mp_set_dep_value(mp->dep_final, mp_get_dep_value(mp->dep_final));
        number_add(mp_get_dep_value(mp->dep_final), ret);
        free_number(ret);
    } else {
        mp_set_dep_list(p, mp_p_plus_fq(mp, (mp_value_node) mp_get_dep_list(p), v, (mp_value_node) mp_get_dep_list((mp_value_node) r), mp_proto_dependent_type, r->type));
        if (mp->fix_needed) {
            mp_fix_dependencies(mp);
        }
    }
}
static void mp_bilin2 (MP mp, mp_node p, mp_node t, mp_number *v, mp_node u, mp_node q)
{
    mp_number vv;
    new_number_clone(vv, mp_get_value_number(p));
    mp_new_dep(mp, p, mp_proto_dependent_type, mp_const_dependency(mp, &zero_t));
    if (number_nonzero(vv)) {
        mp_add_mult_dep(mp, (mp_value_node) p, &vv, t);
    }
    if (number_nonzero(*v)) {
        mp_number arg1;
        new_number_clone(arg1, *v);
        mp_add_mult_dep(mp, (mp_value_node) p, &arg1, u);
        free_number(arg1);
    }
    if (q != NULL) {
        mp_add_mult_dep(mp, (mp_value_node) p, &unity_t, q);
    }
    if (mp_get_dep_list((mp_value_node) p) == (mp_node) mp->dep_final) {
        number_clone(vv, mp_get_dep_value(mp->dep_final));
        mp_recycle_value(mp, p);
        p->type = mp_known_type;
        mp_set_value_number(p, vv);
    }
    free_number(vv);
}
static void mp_bilin3 (MP mp, mp_node p, mp_number *t, mp_number *v, mp_number *u, mp_number *delta_orig)
{
    mp_number delta;
    mp_number tmp;
    new_number(tmp);
    new_number_clone(delta, *delta_orig);
    if (! number_equal(*t, unity_t)) {
        take_scaled(tmp, mp_get_value_number(p), *t);
    } else {
        number_clone(tmp, mp_get_value_number(p));
    }
    number_add(delta, tmp);
    if (number_nonzero(*u)) {
        mp_number ret;
        new_number(ret);
        take_scaled(ret, *v, *u);
        mp_set_value_number(p, delta);
        number_add(mp_get_value_number(p), ret);
        free_number(ret);
    } else {
        mp_set_value_number(p, delta);
    }
    free_number(tmp);
    free_number(delta);
}

static void mp_big_trans (MP mp, mp_node p, int c)
{
    mp_node q = mp_get_value_node(p);
    if (q->type == mp_pair_node_type) {
        if (mp_x_part(q)->type != mp_known_type || mp_y_part(q)->type != mp_known_type) {
            goto UNKNOWN;
        }
    } else if (mp_tx_part(q)->type != mp_known_type || mp_ty_part(q)->type != mp_known_type ||
               mp_xx_part(q)->type != mp_known_type || mp_xy_part(q)->type != mp_known_type ||
               mp_yx_part(q)->type != mp_known_type || mp_yy_part(q)->type != mp_known_type) {
        goto UNKNOWN;
    }
    {
        mp_node r, pp, qq;
        mp_set_up_trans(mp, c);
        if (mp->cur_exp.type == mp_known_type) {
            mp_make_exp_copy(mp, p);
            r = mp_get_value_node(cur_exp_node);
            if (mp->cur_exp.type == mp_transform_type) {
                mp_bilin3(mp, mp_yy_part(r), &(mp->tyy), &(mp_get_value_number(mp_xy_part(q))), &(mp->tyx), &zero_t);
                mp_bilin3(mp, mp_yx_part(r), &(mp->tyy), &(mp_get_value_number(mp_xx_part(q))), &(mp->tyx), &zero_t);
                mp_bilin3(mp, mp_xy_part(r), &(mp->txx), &(mp_get_value_number(mp_yy_part(q))), &(mp->txy), &zero_t);
                mp_bilin3(mp, mp_xx_part(r), &(mp->txx), &(mp_get_value_number(mp_yx_part(q))), &(mp->txy), &zero_t);
            }
            mp_bilin3(mp, mp_y_part(r), &(mp->tyy), &(mp_get_value_number(mp_x_part(q))), &(mp->tyx), &(mp->ty));
            mp_bilin3(mp, mp_x_part(r), &(mp->txx), &(mp_get_value_number(mp_y_part(q))), &(mp->txy), &(mp->tx));
        } else {
            pp = mp_stash_cur_exp(mp);
            qq = mp_get_value_node(pp);
            mp_make_exp_copy(mp, p);
            r = mp_get_value_node(cur_exp_node);
            if (mp->cur_exp.type == mp_transform_type) {
                mp_bilin2(mp, mp_yy_part(r), mp_yy_part(qq), &(mp_get_value_number(mp_xy_part(q))), mp_yx_part(qq), NULL);
                mp_bilin2(mp, mp_yx_part(r), mp_yy_part(qq), &(mp_get_value_number(mp_xx_part(q))), mp_yx_part(qq), NULL);
                mp_bilin2(mp, mp_xy_part(r), mp_xx_part(qq), &(mp_get_value_number(mp_yy_part(q))), mp_xy_part(qq), NULL);
                mp_bilin2(mp, mp_xx_part(r), mp_xx_part(qq), &(mp_get_value_number(mp_yx_part(q))), mp_xy_part(qq), NULL);
            }
            mp_bilin2(mp, mp_y_part(r), mp_yy_part(qq), &(mp_get_value_number(mp_x_part(q))), mp_yx_part(qq), mp_y_part(qq));
            mp_bilin2(mp, mp_x_part(r), mp_xx_part(qq), &(mp_get_value_number(mp_y_part(q))), mp_xy_part(qq), mp_x_part(qq));
            mp_recycle_value(mp, pp);
            mp_free_value_node(mp, pp);
        }
        return;
    }
  UNKNOWN:
    {
        mp_node r;
        mp_set_up_known_trans(mp, c);
        mp_make_exp_copy(mp, p);
        r = mp_get_value_node(cur_exp_node);
        if (mp->cur_exp.type == mp_transform_type) {
            mp_bilin1(mp, mp_yy_part(r), &(mp->tyy), mp_xy_part(q), &(mp->tyx), &zero_t);
            mp_bilin1(mp, mp_yx_part(r), &(mp->tyy), mp_xx_part(q), &(mp->tyx), &zero_t);
            mp_bilin1(mp, mp_xy_part(r), &(mp->txx), mp_yy_part(q), &(mp->txy), &zero_t);
            mp_bilin1(mp, mp_xx_part(r), &(mp->txx), mp_yx_part(q), &(mp->txy), &zero_t);
        }
        mp_bilin1(mp, mp_y_part(r), &(mp->tyy), mp_x_part(q), &(mp->tyx), &(mp->ty));
        mp_bilin1(mp, mp_x_part(r), &(mp->txx), mp_y_part(q), &(mp->txy), &(mp->tx));

        return;
    }
}
static void mp_chop_path (MP mp, mp_node p)
{
    mp_knot q;
    mp_knot pp, qq;
    mp_number a, b;
    mp_number l;
    int reversed;
    new_number(l);
    mp_path_length(mp, &l);
    new_number_clone(a, mp_get_value_number(mp_x_part(p)));
    new_number_clone(b, mp_get_value_number(mp_y_part(p)));
    if (number_lessequal(a, b)) {
        reversed = 0;
    } else {
        reversed = 1;
        number_swap (a, b);
    }
    if (number_negative(a)) {
        if (mp_left_type(cur_exp_knot) == mp_endpoint_knot) {
            set_number_to_zero(a);
            if (number_negative(b)) {
                set_number_to_zero(b);
            }
        } else {
            do {
                number_add(a, l);
                number_add(b, l);
            } while (number_negative(a));
        }
    }
    if (number_greater(b, l)) {
        if (mp_left_type(cur_exp_knot) == mp_endpoint_knot) {
            number_clone(b, l);
            if (number_greater(a, l)) {
                number_clone(a, l);
            }
        } else {
            while (number_greaterequal(a, l)) {
                number_subtract(a, l);
                number_subtract(b, l);
            }
        }
    }
    q = cur_exp_knot;
    while (number_greaterequal(a, unity_t)) {
        q = mp_next_knot(q);
        number_subtract(a, unity_t);
        number_subtract(b, unity_t);
    }
    if (number_equal(b, a)) {
        if (number_positive(a)) {
            mp_number arg1;
            new_number_clone(arg1, a);
            convert_scaled_to_fraction(arg1);
            mp_split_cubic(mp, q, &arg1);
            free_number(arg1);
            q = mp_next_knot(q);
        }
        pp = mp_copy_knot(mp, q);
        qq = pp;
    } else {
        mp_knot rr;
        pp = mp_copy_knot(mp, q);
        qq = pp;
        do {
            q = mp_next_knot(q);
            rr = qq;
            qq = mp_copy_knot(mp, q);
            mp_prev_knot(qq) = rr;
            mp_next_knot(rr) = qq;
            number_subtract(b, unity_t);
        } while (number_positive(b));
        if (number_positive(a)) {
            mp_knot ss = pp;
            mp_number arg1;
            new_number_clone(arg1, a);
            convert_scaled_to_fraction(arg1);
            mp_split_cubic(mp, ss, &arg1);
            free_number(arg1);
            pp = mp_next_knot(ss);
            mp_toss_knot(mp, ss);
            if (rr == ss) {
                mp_number arg1, arg2;
                new_number(arg1);
                set_number_from_subtraction(arg1, unity_t, a);
                new_number_clone(arg2, b);
                make_scaled(b, arg2, arg1);
                free_number(arg1);
                free_number(arg2);
                rr = pp;
            }
        }
        if (number_negative(b)) {
            mp_number arg1;
            new_number(arg1);
            set_number_from_addition(arg1, b, unity_t);
            convert_scaled_to_fraction(arg1);
            mp_split_cubic(mp, rr, &arg1);
            free_number(arg1);
            mp_toss_knot(mp, qq);
            qq = mp_next_knot(rr);
        }
    }
    mp_left_type(pp) = mp_endpoint_knot;
    mp_right_type(qq) = mp_endpoint_knot;
    mp_prev_knot(pp) = qq;
    mp_next_knot(qq) = pp;
    mp_toss_knot_list(mp, cur_exp_knot);
    if (reversed) {
        mp_set_cur_exp_knot(mp, mp_next_knot(mp_htap_ypoc(mp, pp)));
        mp_toss_knot_list(mp, pp);
    } else {
        mp_set_cur_exp_knot(mp, pp);
    }
    free_number(l);
    free_number(a);
    free_number(b);
}
static void mp_set_up_offset (MP mp, mp_node p)
{
    mp_find_offset(mp, &(mp_get_value_number(mp_x_part(p))), &(mp_get_value_number(mp_y_part(p))), cur_exp_knot);
    mp_pair_value(mp, &(mp->cur_x), &(mp->cur_y));
}
static void mp_set_up_direction_time (MP mp, mp_node p)
{
    mp_value new_expr;
    memset(&new_expr, 0, sizeof(mp_value));
    new_number(new_expr.data.n);
    mp_find_direction_time(mp, &new_expr.data.n, &(mp_get_value_number(mp_x_part(p))), &(mp_get_value_number(mp_y_part(p))), cur_exp_knot);
    mp_flush_cur_exp(mp, new_expr);
}
static void mp_set_up_envelope (MP mp, mp_node p)
{
    mp_knot q = mp_copy_path(mp, cur_exp_knot);

    if (mp_pen_is_elliptical(mp_get_value_knot(p))) {
        mp_bad_envelope_pen(mp);
        mp_set_cur_exp_knot(mp, q);
        mp->cur_exp.type = mp_path_type;
    } else {
        int linejoin = mp_mitered_linejoin_code;
        int linecap = mp_butt_linecap_code;
        mp_number miterlimit;
        new_number(miterlimit);
        if (number_greater(internal_value(mp_linejoin_internal), unity_t)) {
            linejoin = mp_beveled_linejoin_code;
        } else if (number_positive(internal_value(mp_linejoin_internal))) {
            linejoin = mp_rounded_linejoin_code;
        }
        if (number_greater(internal_value(mp_linecap_internal), unity_t)) {
            linecap = mp_squared_linecap_code;
        } else if (number_positive(internal_value(mp_linecap_internal))) {
            linecap = mp_rounded_linecap_code;
        }
        if (number_less(internal_value(mp_miterlimit_internal), unity_t)) {
            set_number_to_unity(miterlimit);
        } else {
            number_clone(miterlimit, internal_value(mp_miterlimit_internal));
        }
        mp_set_cur_exp_knot(mp, mp_make_envelope(mp, q, mp_get_value_knot(p), linejoin, linecap, &miterlimit));
        mp->cur_exp.type = mp_path_type;
    }
}
static void mp_set_up_boundingpath (MP mp, mp_node p)
{
    mp_number miterlimit;
    mp_knot q = mp_copy_path(mp, cur_exp_knot);
    mp_knot qq;
    int linejoin = mp_mitered_linejoin_code;
    int linecap = mp_butt_linecap_code;
    mp_knot pen = mp_get_value_knot(p);
    new_number(miterlimit);
    if (mp_pen_is_elliptical(mp_get_value_knot(p))) {
        mp_knot kp, kq;
        pen = mp_copy_pen(mp, mp_get_value_knot(p));
        mp_make_path(mp, pen);
        kq = pen;
        do {
            kp = kq;
            kq = mp_next_knot(kq);
            mp_prev_knot(kq) = kp;
        } while (kq != pen);
        mp_close_path_cycle(mp, kp, pen);
    }
    if (number_greater(internal_value(mp_linejoin_internal), unity_t)) {
        linejoin = mp_beveled_linejoin_code;
    } else if (number_positive(internal_value(mp_linejoin_internal))) {
        linejoin = mp_rounded_linejoin_code;
    }
    if (number_greater(internal_value(mp_linecap_internal), unity_t)) {
        linecap = mp_squared_linecap_code;
    } else if (number_positive(internal_value(mp_linecap_internal))) {
        linecap = mp_rounded_linecap_code;
    }
    if (number_less(internal_value(mp_miterlimit_internal), unity_t)) {
        set_number_to_unity(miterlimit);
    } else {
        number_clone(miterlimit, internal_value(mp_miterlimit_internal));
    }
    qq = mp_make_envelope(mp, q, pen, linejoin, linecap, &miterlimit);
    mp_set_cur_exp_knot(mp, qq);
    mp->cur_exp.type = mp_path_type;
    if (! mp_get_cur_bbox(mp)) {
        mp_bad_binary(mp, p, mp_boundingpath_operation);
        mp_set_cur_exp_knot(mp, q);
        mp->cur_exp.type = mp_path_type;
        return;
    } else {
        mp_knot ll = mp_new_knot(mp);
        mp_knot lr = mp_new_knot(mp);
        mp_knot ur = mp_new_knot(mp);
        mp_knot ul = mp_new_knot(mp);
        if (ll == NULL || lr == NULL || ur == NULL || ul == NULL){
            mp_bad_binary(mp, p, mp_boundingpath_operation);
            mp_set_cur_exp_knot(mp, q);
            mp->cur_exp.type = mp_path_type;
            return;
        } else {
            mp_left_type(ll) = mp_endpoint_knot;
            mp_right_type(ll) = mp_endpoint_knot;
            mp_originator(ll) = mp_program_code;
            mp_knotstate(ll) = mp_regular_knot;
            number_clone(ll->x_coord, mp_minx);
            number_clone(ll->y_coord, mp_miny);
            mp_originator(lr) = mp_program_code;
            mp_knotstate(lr) = mp_regular_knot;
            number_clone(lr->x_coord, mp_maxx);
            number_clone(lr->y_coord, mp_miny);
            mp_originator(ur) = mp_program_code;
            mp_knotstate(ur) = mp_regular_knot;
            number_clone(ur->x_coord, mp_maxx);
            number_clone(ur->y_coord, mp_maxy);
            mp_originator(ul) = mp_program_code;
            mp_knotstate(ul) = mp_regular_knot;
            number_clone(ul->x_coord, mp_minx);
            number_clone(ul->y_coord, mp_maxy);
            mp_prev_knot(lr) = ll;
            mp_next_knot(ll) = lr;
            mp_prev_knot(ur) = lr;
            mp_next_knot(lr) = ur;
            mp_prev_knot(ul) = ur;
            mp_next_knot(ur) = ul;
            mp_close_path_cycle(mp, ul, ll);
            mp_make_path(mp,ll);
            mp->cur_exp.type = mp_path_type;
            mp_set_cur_exp_knot(mp, ll);
            mp_free_path(mp,qq);
        }
    }
}
static void mp_find_point (MP mp, mp_number *v_orig, int c)
{
    mp_knot p;
    mp_number n;
    mp_number v;
    new_number(n);
    new_number_clone(v, *v_orig);
    p = cur_exp_knot;
    if (mp_left_type(p) == mp_endpoint_knot) {
        set_number_to_unity(n);
        number_negate(n);
    }
    do {
        p = mp_next_knot(p);
        number_add(n, unity_t);
    } while (p != cur_exp_knot);
    if (number_zero(n)) {
        set_number_to_zero(v);
    } else if (number_negative(v)) {
        if (mp_left_type(p) == mp_endpoint_knot) {
            set_number_to_zero(v);
        } else    {
            number_negate(v);
            number_add_scaled(v, -1);
            number_modulo(v, n);
            number_negate(v);
            number_add_scaled(v, -1);
            number_add(v, n);
        }
    } else if (number_greater(v, n)) {
        if (mp_left_type(p) == mp_endpoint_knot) {
            number_clone(v, n);
        } else {
            number_modulo(v, n);
        }
    }
    p = cur_exp_knot;
    while (number_greaterequal(v, unity_t)) {
        p = mp_next_knot(p);
        number_subtract(v, unity_t);
    }
    if (number_nonzero(v)) {
        convert_scaled_to_fraction(v);
        mp_split_cubic(mp, p, &v);
        p = mp_next_knot(p);
    }
    push_of_path_result(mp, c - mp_point_operation, p, zero_t, zero_t);
    free_number(v);
    free_number(n);
}

static void mp_finish_binary (MP mp, mp_node old_p, mp_node old_exp)
{
    check_arith(mp);
    if (old_p != NULL) {
        mp_recycle_value(mp, old_p);
        mp_free_value_node(mp, old_p);
    }
    if (old_exp != NULL) {
        mp_recycle_value(mp, old_exp);
        mp_free_value_node(mp, old_exp);
    }
}

static void mp_do_binary (MP mp, mp_node p, int c)
{
    mp_node old_p, old_exp;
    mp_value new_expr;
    check_arith(mp);
    if (number_greater(internal_value(mp_tracing_commands_internal), two_t)) {
        mp_begin_diagnostic(mp);
        mp_print_nl(mp, "{(");
        mp_print_exp(mp, p, 0);
        mp_print_chr(mp, ')');
        mp_print_op(mp, (int) c);
        mp_print_chr(mp, '(');
        mp_print_exp(mp, NULL, 0);
        mp_print_str(mp, ")}");
        mp_end_diagnostic(mp, 0);
    }
    switch (p->type) {
        case mp_transform_type:
        case mp_color_type:
        case mp_cmykcolor_type:
        case mp_pair_type:
            old_p = mp_tarnished(mp, p);
            break;
        case mp_independent_type:
            old_p = MP_VOID;
            break;
        default:
            old_p = NULL;
            break;
    }
    if (old_p != NULL) {
        mp_node q = mp_stash_cur_exp(mp);
        old_p = p;
        mp_make_exp_copy(mp, old_p);
        p = mp_stash_cur_exp(mp);
        mp_unstash_cur_exp(mp, q);
    }
    switch (mp->cur_exp.type) {
        case mp_transform_type:
        case mp_color_type:
        case mp_cmykcolor_type:
        case mp_pair_type:
            old_exp = mp_tarnished(mp, cur_exp_node);
            break;
        case mp_independent_type:
            old_exp = MP_VOID;
            break;
        default:
            old_exp = NULL;
            break;
    }
    if (old_exp != NULL) {
        old_exp = cur_exp_node;
        mp_make_exp_copy(mp, old_exp);
    }
    switch (c) {
        case mp_plus_operation:
        case mp_minus_operation:
            if ((mp->cur_exp.type < mp_color_type) || (p->type < mp_color_type)) {
                mp_bad_binary(mp, p, c);
            } else {
                if ((mp->cur_exp.type > mp_pair_type) && (p->type > mp_pair_type)) {
                    mp_add_or_subtract(mp, p, NULL, c);
                } else if (mp->cur_exp.type != p->type) {

                        mp_bad_binary(mp, p, c);
               } else {
                    mp_node q = mp_get_value_node(p);
                    mp_node r = mp_get_value_node(cur_exp_node);
                    switch (mp->cur_exp.type) {
                        case mp_pair_type:
                            mp_add_or_subtract(mp, mp_x_part(q), mp_x_part(r), c);
                            mp_add_or_subtract(mp, mp_y_part(q), mp_y_part(r), c);
                            break;
                        case mp_color_type:
                            mp_add_or_subtract(mp, mp_red_part(q), mp_red_part(r), c);
                            mp_add_or_subtract(mp, mp_green_part(q), mp_green_part(r), c);
                            mp_add_or_subtract(mp, mp_blue_part(q), mp_blue_part(r), c);
                            break;
                        case mp_cmykcolor_type:
                            mp_add_or_subtract(mp, mp_cyan_part(q), mp_cyan_part(r), c);
                            mp_add_or_subtract(mp, mp_magenta_part(q), mp_magenta_part(r), c);
                            mp_add_or_subtract(mp, mp_yellow_part(q), mp_yellow_part(r), c);
                            mp_add_or_subtract(mp, mp_black_part(q), mp_black_part(r), c);
                            break;
                        case mp_transform_type:
                            mp_add_or_subtract(mp, mp_tx_part(q), mp_tx_part(r), c);
                            mp_add_or_subtract(mp, mp_ty_part(q), mp_ty_part(r), c);
                            mp_add_or_subtract(mp, mp_xx_part(q), mp_xx_part(r), c);
                            mp_add_or_subtract(mp, mp_xy_part(q), mp_xy_part(r), c);
                            mp_add_or_subtract(mp, mp_yx_part(q), mp_yx_part(r), c);
                            mp_add_or_subtract(mp, mp_yy_part(q), mp_yy_part(r), c);
                            break;
                        default:
                            break;
                    }
                }
            }
            break;
        case mp_less_than_operation:
        case mp_less_or_equal_operation:
        case mp_greater_than_operation:
        case mp_greater_or_equal_operation:
        case mp_equal_operation:
        case mp_unequal_operation:
            check_arith(mp);
            if ((mp->cur_exp.type > mp_pair_type) && (p->type > mp_pair_type)) {
                mp_add_or_subtract(mp, p, NULL, mp_minus_operation);
            } else if (mp->cur_exp.type != p->type) {
                mp_bad_binary(mp, p, (int) c);
                goto DONE;
            } else {
                switch (mp->cur_exp.type) {
                    case mp_string_type:
                        {
                            memset(&new_expr, 0, sizeof(mp_value));
                            new_number(new_expr.data.n);
                            set_number_from_scaled(new_expr.data.n, mp_str_vs_str(mp, mp_get_value_str(p), cur_exp_str));
                            mp_flush_cur_exp(mp, new_expr);
                        }
                        break;
                    case mp_unknown_string_type:
                    case mp_unknown_boolean_type:
                        {
                            mp_node q = mp_get_value_node(cur_exp_node);
                            while ((q != cur_exp_node) && (q != p)) {
                                q = mp_get_value_node(q);
                            }
                            if (q == p) {
                                memset(&new_expr, 0, sizeof(mp_value));
                                new_number(new_expr.data.n);
                                mp_set_cur_exp_node(mp, NULL);
                                mp_flush_cur_exp(mp, new_expr);
                            }
                        }
                        break;
                    case mp_pair_type:
                        {
                            int part_type = 0;
                            mp_node q = mp_get_value_node(p);
                            mp_node r = mp_get_value_node(cur_exp_node);
                            while (part_type == 0) {
                                mp_node rr = mp_x_part(r);
                                part_type = mp_x_part_operation;
                                mp_add_or_subtract(mp, mp_x_part(q), rr, mp_minus_operation);
                                if (rr->type != mp_known_type || ! number_zero(mp_get_value_number(rr))) {
                                    break;
                                }
                                rr = mp_y_part(r);
                                part_type = mp_y_part_operation;
                                mp_add_or_subtract(mp, mp_y_part(q), rr, mp_minus_operation);
                                if (rr->type != mp_known_type || ! number_zero(mp_get_value_number(rr))) {
                                    break;
                                }
                            }
                            mp_take_part(mp, part_type);
                        }
                        break;
                    case mp_color_type:
                        {
                            int part_type = 0;
                            mp_node q = mp_get_value_node(p);
                            mp_node r = mp_get_value_node(cur_exp_node);
                            while (part_type == 0) {
                                mp_node rr = mp_red_part(r);
                                part_type = mp_red_part_operation;
                                mp_add_or_subtract(mp, mp_red_part(q), rr, mp_minus_operation);
                                if (rr->type != mp_known_type || ! number_zero(mp_get_value_number(rr))) {
                                    break;
                                }
                                rr = mp_green_part(r);
                                part_type = mp_green_part_operation;
                                mp_add_or_subtract(mp, mp_green_part(q), rr, mp_minus_operation);
                                if (rr->type != mp_known_type || ! number_zero(mp_get_value_number(rr))) {
                                    break;
                                }
                                rr = mp_blue_part(r);
                                part_type = mp_blue_part_operation;
                                mp_add_or_subtract(mp, mp_blue_part(q), rr, mp_minus_operation);
                                if (rr->type != mp_known_type || ! number_zero(mp_get_value_number(rr))) {
                                    break;
                                }
                            }
                            mp_take_part(mp, part_type);
                        }
                        break;
                    case mp_cmykcolor_type:
                        {
                            int part_type = 0;
                            mp_node q = mp_get_value_node(p);
                            mp_node r = mp_get_value_node(cur_exp_node);
                            while (part_type == 0) {
                                mp_node rr = mp_cyan_part(r);
                                part_type = mp_cyan_part_operation;
                                mp_add_or_subtract(mp, mp_cyan_part(q), rr, mp_minus_operation);
                                if (rr->type != mp_known_type || ! number_zero(mp_get_value_number(rr))) {
                                    break;
                                }
                                rr = mp_magenta_part(r);
                                part_type = mp_magenta_part_operation;
                                mp_add_or_subtract(mp, mp_magenta_part(q), rr, mp_minus_operation);
                                if (rr->type != mp_known_type || ! number_zero(mp_get_value_number(rr))) {
                                    break;
                                }
                                rr = mp_yellow_part(r);
                                part_type = mp_yellow_part_operation;
                                mp_add_or_subtract(mp, mp_yellow_part(q), rr, mp_minus_operation);
                                if (rr->type != mp_known_type || ! number_zero(mp_get_value_number(rr))) {
                                    break;
                                }
                                rr = mp_black_part(r);
                                part_type = mp_black_part_operation;
                                mp_add_or_subtract(mp, mp_black_part(q), rr, mp_minus_operation);
                                if (rr->type != mp_known_type || ! number_zero(mp_get_value_number(rr))) {
                                    break;
                                }
                            }
                            mp_take_part(mp, part_type);
                        }
                        break;
                    case mp_transform_type:
                        {
                            int part_type = 0;
                            mp_node q = mp_get_value_node(p);
                            mp_node r = mp_get_value_node(cur_exp_node);
                            while (part_type == 0) {
                                mp_node rr = mp_tx_part(r);
                                part_type = mp_x_part_operation;
                                mp_add_or_subtract(mp, mp_tx_part(q), rr, mp_minus_operation);
                                if (rr->type != mp_known_type || ! number_zero(mp_get_value_number(rr))) {
                                    break;
                                }
                                rr = mp_ty_part(r);
                                part_type = mp_y_part_operation;
                                mp_add_or_subtract(mp, mp_ty_part(q), rr, mp_minus_operation);
                                if (rr->type != mp_known_type || ! number_zero(mp_get_value_number(rr))) {
                                    break;
                                }
                                rr = mp_xx_part(r);
                                part_type = mp_xx_part_operation;
                                mp_add_or_subtract(mp, mp_xx_part(q), rr, mp_minus_operation);
                                if (rr->type != mp_known_type || ! number_zero(mp_get_value_number(rr))) {
                                    break;
                                }
                                rr = mp_xy_part(r);
                                part_type = mp_xy_part_operation;
                                mp_add_or_subtract(mp, mp_xy_part(q), rr, mp_minus_operation);
                                if (rr->type != mp_known_type || ! number_zero(mp_get_value_number(rr))) {
                                    break;
                                }
                                rr = mp_yx_part(r);
                                part_type = mp_yx_part_operation;
                                mp_add_or_subtract(mp, mp_yx_part(q), rr, mp_minus_operation);
                                if (rr->type != mp_known_type || ! number_zero(mp_get_value_number(rr))) {
                                    break;
                                }
                                rr = mp_yy_part(r);
                                part_type = mp_yy_part_operation;
                                mp_add_or_subtract(mp, mp_yy_part(q), rr, mp_minus_operation);
                                if (rr->type != mp_known_type || ! number_zero(mp_get_value_number(rr))) {
                                    break;
                                }
                            }
                            mp_take_part(mp, part_type);
                        }
                        break;
                    case mp_boolean_type:
                        {
                            memset(&new_expr, 0, sizeof(mp_value));
                            new_number(new_expr.data.n);
                            set_number_from_boolean(new_expr.data.n, number_to_scaled(cur_exp_value_number) - number_to_scaled(mp_get_value_number(p)));
                            mp_flush_cur_exp(mp, new_expr);
                        }
                        break;
                    default:
                        mp_bad_binary(mp, p, (int) c);
                        goto DONE;
                        break;
                }
            }
            if (mp->cur_exp.type != mp_known_type) {
                const char *hlp = NULL;
                if (mp->cur_exp.type < mp_known_type) {
                    mp_disp_err(mp, p);
                    hlp = "The quantities shown above have not been equated.";
                } else {
                    hlp =
                        "Oh dear. I can't decide if the expression above is positive, negative, or zero.\n"
                        "So this comparison test won't be 'true'.";
                }
                mp_disp_err(mp, NULL);
                memset(&new_expr, 0, sizeof(mp_value));
                new_number(new_expr.data.n);
                set_number_from_boolean(new_expr.data.n, mp_false_operation);
                mp_back_error(mp, "Unknown relation will be considered false", hlp);
                mp_get_x_next(mp);
                mp_flush_cur_exp(mp, new_expr);
            } else {
                int b = 0;
                switch (c) {
                    case mp_less_than_operation:
                        b = number_negative(cur_exp_value_number);
                        break;
                    case mp_less_or_equal_operation:
                        b = number_nonpositive(cur_exp_value_number);
                        break;
                    case mp_greater_than_operation:
                        b = number_positive(cur_exp_value_number);
                        break;
                    case mp_greater_or_equal_operation:
                        b = number_nonnegative(cur_exp_value_number);
                        break;
                    case mp_equal_operation:
                        b = number_zero(cur_exp_value_number);
                        break;
                    case mp_unequal_operation:
                        b = number_nonzero(cur_exp_value_number);
                        break;
                };
                mp_set_cur_exp_value_boolean(mp, b ? mp_true_operation : mp_false_operation);
            }
            mp->cur_exp.type = mp_boolean_type;
          DONE:
            mp->arith_error = 0;
            break;
        case mp_and_operation:
        case mp_or_operation:
            if ((p->type != mp_boolean_type) || (mp->cur_exp.type != mp_boolean_type)) {
                mp_bad_binary(mp, p, (int) c);
            } else if (number_to_boolean(p->data.n) == c + mp_false_operation - mp_and_operation) {
                mp_set_cur_exp_value_boolean(mp, number_to_boolean(p->data.n));
            }
            break;
        case mp_times_operation:
          TIMES:
            if ((mp->cur_exp.type < mp_color_type) || (p->type < mp_color_type)) {
                mp_bad_binary(mp, p, mp_times_operation);
            } else if ((mp->cur_exp.type == mp_known_type) || (p->type == mp_known_type)) {
                mp_number vv;
                new_fraction(vv);
                if (p->type == mp_known_type) {
                    number_clone(vv, mp_get_value_number(p));
                    mp_free_value_node(mp, p);
                } else {
                    number_clone(vv, cur_exp_value_number);
                    mp_unstash_cur_exp(mp, p);
                }
                switch (mp->cur_exp.type) {
                    case mp_known_type:
                        {
                            mp_number ret;
                            new_number(ret);
                            take_scaled(ret, cur_exp_value_number, vv);
                            mp_set_cur_exp_value_number(mp, &ret);
                            free_number(ret);
                        }
                        break;
                    case mp_pair_type:
                        {
                            mp_dep_mult(mp, (mp_value_node) mp_x_part(mp_get_value_node(cur_exp_node)), &vv, 1);
                            mp_dep_mult(mp, (mp_value_node) mp_y_part(mp_get_value_node(cur_exp_node)), &vv, 1);
                        }
                        break;
                    case mp_color_type:
                        {
                            mp_dep_mult(mp, (mp_value_node) mp_red_part  (mp_get_value_node(cur_exp_node)), &vv, 1);
                            mp_dep_mult(mp, (mp_value_node) mp_green_part(mp_get_value_node(cur_exp_node)), &vv, 1);
                            mp_dep_mult(mp, (mp_value_node) mp_blue_part (mp_get_value_node(cur_exp_node)), &vv, 1);
                        }
                        break;
                    case mp_cmykcolor_type:
                        {
                            mp_dep_mult(mp, (mp_value_node) mp_cyan_part   (mp_get_value_node(cur_exp_node)), &vv, 1);
                            mp_dep_mult(mp, (mp_value_node) mp_magenta_part(mp_get_value_node(cur_exp_node)), &vv, 1);
                            mp_dep_mult(mp, (mp_value_node) mp_yellow_part (mp_get_value_node(cur_exp_node)), &vv, 1);
                            mp_dep_mult(mp, (mp_value_node) mp_black_part  (mp_get_value_node(cur_exp_node)), &vv, 1);
                        }
                        break;
                    default:
                        {
                            mp_dep_mult(mp, NULL, &vv, 1);
                        }
                        break;
                }
                free_number(vv);
                mp_finish_binary(mp, old_p, old_exp);
                return;
            } else if ((mp_nice_color_or_pair(mp, p, p->type)                     && (mp->cur_exp.type > mp_pair_type))
                    || (mp_nice_color_or_pair(mp, cur_exp_node, mp->cur_exp.type) && (p->type          > mp_pair_type))) {
                mp_hard_times(mp, p);
                mp_finish_binary(mp, old_p, old_exp);
                return;
            } else {
                mp_bad_binary(mp, p, mp_times_operation);
            }
            break;
        case mp_over_operation:
            if ((mp->cur_exp.type != mp_known_type) || (p->type < mp_color_type)) {
                mp_bad_binary(mp, p, mp_over_operation);
            } else {
                mp_number v_n;
                new_number_clone(v_n, cur_exp_value_number);
                mp_unstash_cur_exp(mp, p);
                if (number_zero(v_n)) {
                    mp_disp_err(mp, NULL);
                    mp_back_error(
                        mp,
                        "Division by zero",
                        "You're trying to divide the quantity shown above the error message by zero. I'm\n"
                        "going to divide it by one instead."
                    );
                    mp_get_x_next(mp);
                } else {
                    switch (mp->cur_exp.type) {
                        case mp_known_type:
                            {
                                mp_number ret;
                                new_number(ret);
                                make_scaled(ret, cur_exp_value_number, v_n);
                                mp_set_cur_exp_value_number(mp, &ret);
                                free_number(ret);
                            }
                            break;
                        case mp_pair_type:
                            {
                                mp_dep_div(mp, (mp_value_node) mp_x_part(mp_get_value_node(cur_exp_node)), &v_n);
                                mp_dep_div(mp, (mp_value_node) mp_y_part(mp_get_value_node(cur_exp_node)), &v_n);
                            }
                            break;
                        case mp_color_type:
                            {
                                mp_dep_div(mp, (mp_value_node) mp_red_part  (mp_get_value_node(cur_exp_node)), &v_n);
                                mp_dep_div(mp, (mp_value_node) mp_green_part(mp_get_value_node(cur_exp_node)), &v_n);
                                mp_dep_div(mp, (mp_value_node) mp_blue_part (mp_get_value_node(cur_exp_node)), &v_n);
                            }
                            break;
                        case mp_cmykcolor_type:
                            {
                                mp_dep_div(mp, (mp_value_node) mp_cyan_part   (mp_get_value_node(cur_exp_node)), &v_n);
                                mp_dep_div(mp, (mp_value_node) mp_magenta_part(mp_get_value_node(cur_exp_node)), &v_n);
                                mp_dep_div(mp, (mp_value_node) mp_yellow_part (mp_get_value_node(cur_exp_node)), &v_n);
                                mp_dep_div(mp, (mp_value_node) mp_black_part  (mp_get_value_node(cur_exp_node)), &v_n);
                            }
                            break;
                        default:
                            {
                                mp_dep_div(mp, NULL, &v_n);
                            }
                            break;
                    }
                }
                free_number(v_n);
                mp_finish_binary(mp, old_p, old_exp);
                return;
            }
            break;
        case mp_power_operation:
            if ((mp->cur_exp.type == mp_known_type) && (p->type == mp_known_type)) {
                mp_number r;
                new_number(r);
                power_of(r, mp_get_value_number(p), cur_exp_value_number);
                check_arith(mp);
                mp_set_cur_exp_value_number(mp, &r);
                free_number(r);
            } else
                mp_bad_binary(mp, p, (int) c);
            break;
        case mp_pythag_add_operation:
        case mp_pythag_sub_operation:
            if ((mp->cur_exp.type == mp_known_type) && (p->type == mp_known_type)) {
                mp_number r;
                new_number(r);
                if (c == mp_pythag_add_operation) {
                    pyth_add(r, mp_get_value_number(p), cur_exp_value_number);
                } else {
                    pyth_sub(r, mp_get_value_number(p), cur_exp_value_number);
                }
                mp_set_cur_exp_value_number(mp, &r);
                free_number(r);
            } else
                mp_bad_binary(mp, p, (int) c);
            break;
        case mp_rotated_operation:
        case mp_slanted_operation:
        case mp_scaled_operation:
        case mp_shifted_operation:
        case mp_transformed_operation:
        case mp_x_scaled_operation:
        case mp_y_scaled_operation:
        case mp_z_scaled_operation:
            switch (p->type) {
                case mp_path_type:
                    mp_set_up_known_trans(mp, (int) c);
                    mp_unstash_cur_exp(mp, p);
                    mp_do_path_trans(mp, cur_exp_knot);
                    mp_finish_binary(mp, old_p, old_exp);
                    return;
                case mp_pen_type:
                    mp_set_up_known_trans(mp, (int) c);
                    mp_unstash_cur_exp(mp, p);
                    mp_do_pen_trans(mp, cur_exp_knot);
                    mp_set_cur_exp_knot(mp, mp_convex_hull(mp, cur_exp_knot));
                    mp_finish_binary(mp, old_p, old_exp);
                    return;
                case mp_nep_type:
                    mp_set_up_known_trans(mp, (int) c);
                    mp_unstash_cur_exp(mp, p);
                    mp_do_pen_trans(mp, cur_exp_knot);
                    mp_set_cur_exp_knot(mp, cur_exp_knot);
                    mp_finish_binary(mp, old_p, old_exp);
                    return;
                case mp_pair_type:
                case mp_transform_type:
                    mp_big_trans(mp, p, (int) c);
                    break;
                case mp_picture_type:
                    mp_do_edges_trans(mp, p, (int) c);
                    mp_finish_binary(mp, old_p, old_exp);
                    return;
                case mp_color_type:
                case mp_cmykcolor_type:
                    if (c == mp_scaled_operation) {
                        goto TIMES;
                    }
                default:
                    mp_bad_binary(mp, p, (int) c);
                    break;
            }
            break;
        case mp_concatenate_operation:
        case mp_just_append_operation:
        case mp_tolerant_concat_operation:
        case mp_tolerant_append_operation:
            if ((mp->cur_exp.type == mp_string_type) && (p->type == mp_string_type)) {
                mp_string str = mp_cat(mp, mp_get_value_str(p), cur_exp_str);
                delete_str_ref(cur_exp_str) ;
                mp_set_cur_exp_str(mp, str);
            } else {
                mp_bad_binary(mp, p, c);
            }
            break;
        case mp_substring_operation:
            if (mp_nice_pair(mp, p, p->type) && (mp->cur_exp.type == mp_string_type)) {
                mp_string str = mp_chop_string (mp,
                    cur_exp_str,
                    round_unscaled(mp_get_value_number(mp_x_part(mp_get_value_node(p)))),
                    round_unscaled(mp_get_value_number(mp_y_part(mp_get_value_node(p))))
                 );
                delete_str_ref(cur_exp_str) ;
                mp_set_cur_exp_str(mp, str);
            } else {
                mp_bad_binary(mp, p, mp_substring_operation);
            }
            break;
        case mp_subpath_operation:
            if (mp->cur_exp.type == mp_pair_type) {
                mp_pair_to_path(mp);
            }
            if (mp_nice_pair(mp, p, p->type) && (mp->cur_exp.type == mp_path_type)) {
                mp_chop_path(mp, mp_get_value_node(p));
            } else {
                mp_bad_binary(mp, p, mp_subpath_operation);
            }
            break;
        case mp_point_operation:
        case mp_precontrol_operation:
        case mp_postcontrol_operation:
        case mp_direction_operation:
            if (mp->cur_exp.type == mp_pair_type) {
                mp_pair_to_path(mp);
            }
            if ((mp->cur_exp.type == mp_path_type) && (p->type == mp_known_type)) {
                mp_find_point(mp, &(mp_get_value_number(p)), (int) c);
            } else {
                mp_bad_binary(mp, p, c);
            }
            break;
        case mp_pen_offset_operation:
            if ((mp->cur_exp.type == mp_pen_type || mp->cur_exp.type == mp_nep_type) && mp_nice_pair(mp, p, p->type)) {
                mp_set_up_offset(mp, mp_get_value_node(p));
            } else {
                mp_bad_binary(mp, p, mp_pen_offset_operation);
            }
            break;
        case mp_direction_time_operation:
            if (mp->cur_exp.type == mp_pair_type) {
                mp_pair_to_path(mp);
            }
            if ((mp->cur_exp.type == mp_path_type) && mp_nice_pair(mp, p, p->type)) {
                mp_set_up_direction_time(mp, mp_get_value_node(p));
            } else {
                mp_bad_binary(mp, p, mp_direction_time_operation);
            }
            break;
        case mp_envelope_operation:
            if ((p->type != mp_pen_type && p->type != mp_nep_type) || (mp->cur_exp.type != mp_path_type)) {
                mp_bad_binary(mp, p, mp_envelope_operation);
            } else {
                mp_set_up_envelope(mp, p);
            }
            break;
        case mp_boundingpath_operation:
            if ((p->type != mp_pen_type && p->type != mp_nep_type) || (mp->cur_exp.type != mp_path_type)) {
                mp_bad_binary(mp, p, mp_boundingpath_operation);
            } else {
                mp_set_up_boundingpath(mp, p);
            }
            break;
        case mp_arc_time_operation:
            if (mp->cur_exp.type == mp_pair_type) {
                mp_pair_to_path(mp);
            }
            if ((mp->cur_exp.type == mp_path_type) && (p->type == mp_known_type)) {
                memset(&new_expr, 0, sizeof(mp_value));
                new_number(new_expr.data.n);
                mp_get_arc_time(mp, &new_expr.data.n, cur_exp_knot, &(mp_get_value_number(p)), 0);
                mp_flush_cur_exp(mp, new_expr);
            } else {
                mp_bad_binary(mp, p, (int) c);
            }
            break;
        case mp_arc_point_operation:
            if (mp->cur_exp.type == mp_pair_type) {
                mp_pair_to_path(mp);
            }
            if ((mp->cur_exp.type == mp_path_type) && (p->type == mp_known_type || p->type == mp_pair_type)) {
                mp_knot k;
                memset(&new_expr, 0, sizeof(mp_value));
                new_number(new_expr.data.n);
                if (p->type == mp_pair_type) {
                    mp_knot f = cur_exp_knot;
                    mp_node q = mp_get_value_node(p);
                    mp_number x;
                    new_number_clone(x, mp_get_value_number(mp_x_part(q)));
                    if (number_greater(x, zero_t)) {
                        while (number_greater(x, zero_t)) {
                            f = mp_next_knot(f);
                            number_subtract(x, unity_t);
                        }
                    } else {
                        while (number_less(x, zero_t)) {
                            f = mp_next_knot(f);
                            number_add(x, unity_t);
                        }
                    }
                    k = mp_get_arc_time(mp, &new_expr.data.n, f, &(mp_get_value_number(mp_y_part(q))), 1);
                    free_number(x);
                } else {
                    k = mp_get_arc_time(mp, &new_expr.data.n, cur_exp_knot, &(mp_get_value_number(p)), 1);
                }
                if (k) {
                    int toss = 0;
                    if (number_equal(new_expr.data.n, unity_t)) {
                        k = mp_next_knot(k);
                    } else if (! number_equal(new_expr.data.n, zero_t)) {
                        convert_scaled_to_fraction(new_expr.data.n);
                        k = mp_split_cubic_knot(mp, k, &new_expr.data.n);
                        toss = 1;
                    }
                    mp_pair_value(mp, &(k->x_coord), &(k->y_coord));
                    if (toss) {
                        mp_toss_knot(mp, k);
                    }
                } else {
                    mp_bad_unary(mp, mp_arc_point_operation);
                }
            } else {
                mp_bad_unary(mp, mp_arc_point_operation);
            }
            break;
        case mp_arc_point_list_operation:

            if (mp->cur_exp.type == mp_pair_type) {
                mp_pair_to_path(mp);
            }
            if ((mp->cur_exp.type == mp_path_type) && p->type == mp_known_type) {

                mp_knot cur = cur_exp_knot;
                mp_number len, aln, seg, tot, tim, stp, acc, tmp;
                mp_knot last = NULL;
                mp_knot list = NULL;
                int iscycle = mp_left_type(cur_exp_knot) == mp_explicit_knot;
                new_number(len);
                mp_path_length(mp, &len);
                new_number(aln);
                mp_get_arc_length(mp, &aln, cur_exp_knot);
                new_number(seg);
                new_number(tot);
                new_number(tim);
                new_number(stp);
                set_number_from_div(stp, aln, mp_get_value_number(p));
                new_number(acc);
                mp_get_subarc_length(mp, &acc, cur_exp_knot, &zero_t, &unity_t);
                new_number(tmp);
                memset(&new_expr, 0, sizeof(mp_value));
                new_number(new_expr.data.n);
                list = mp_complex_knot(mp, cur_exp_knot);
                mp_prev_knot(list) = list;
                mp_next_knot(list) = list;
                last = list;
                number_clone(tot, stp);
                while (number_lessequal(tot, aln)) {
                    mp_knot k;
                    while (1) {
                        if (number_lessequal(tot, acc)) {
                            break;
                        } else {
                            number_add(seg, unity_t);
                            number_clone(tim, acc);
                            cur = mp_next_knot(cur);
                            mp_get_subarc_length(mp, &tmp, cur, &zero_t, &unity_t);
                            number_add(acc, tmp) ;
                        }
                    }
                    number_clone(tmp, tot);
                    number_subtract(tmp, tim);
                    k = mp_get_arc_time(mp, &new_expr.data.n, cur, &tmp, 1);
                    if (k) {
                        int toss = 0;
                        mp_knot kk;
                        if (number_equal(new_expr.data.n, unity_t)) {
                            k = mp_next_knot(k);
                        } else if (! number_equal(new_expr.data.n, zero_t)) {
                            convert_scaled_to_fraction(new_expr.data.n);
                            k = mp_split_cubic_knot(mp, k, &new_expr.data.n);
                            toss = 1;
                        }
                        kk = mp_complex_knot(mp, k);
                        mp_prev_knot(list) = kk;
                        mp_next_knot(kk) = list;
                        mp_prev_knot(kk) = last;
                        mp_next_knot(last) = kk;
                        last = kk;
                        if (toss) {
                            mp_toss_knot(mp, k);
                        }
                        number_add(tot, stp);
                    } else {
                        break;
                    }
                }
                free_number(len);
                free_number(aln);
                free_number(seg);
                free_number(tot);
                free_number(tim);
                free_number(stp);
                free_number(acc);
                free_number(tmp);
                if (list) {
                    if (iscycle) {
                        mp_left_type(list) = mp_explicit_knot;
                        mp_right_type(last) = mp_explicit_knot;
                    } else {
                        mp_left_type(list) = mp_endpoint_knot;
                        mp_right_type(last) = mp_endpoint_knot;
                    }
                    mp->cur_exp.type = mp_path_type;
                    mp_set_cur_exp_knot(mp, list);
                } else {
                    mp_bad_unary(mp, mp_arc_point_list_operation);
                }
            } else {
                mp_bad_unary(mp, mp_arc_point_list_operation);
            }
            break;
        case mp_subarc_length_operation:
            if (mp->cur_exp.type == mp_pair_type) {
                mp_pair_to_path(mp);
            }
            if ((mp->cur_exp.type == mp_path_type) && p->type == mp_pair_type) {
                mp_node q = mp_get_value_node(p);
                memset(&new_expr, 0, sizeof(mp_value));
                new_number(new_expr.data.n);
                mp_get_subarc_length(mp, &new_expr.data.n, cur_exp_knot, &(mp_get_value_number(mp_x_part(q))), &(mp_get_value_number(mp_y_part(q))));
                mp_flush_cur_exp(mp, new_expr);
            } else {
                mp_bad_unary(mp, mp_subarc_length_operation);
            }
            break;
        case mp_intertimes_operation:
        case mp_intertimes_list_operation:
            if (p->type == mp_pair_type) {
                mp_node q = mp_stash_cur_exp(mp);
                mp_unstash_cur_exp(mp, p);
                mp_pair_to_path(mp);
                p = mp_stash_cur_exp(mp);
                mp_unstash_cur_exp(mp, q);
            }
            if (mp->cur_exp.type == mp_pair_type) {
                mp_pair_to_path(mp);
            }
            if ((mp->cur_exp.type == mp_path_type) && (p->type == mp_path_type)) {
                if (c == mp_intertimes_operation) {

                    mp_path_intersection(mp, mp_get_value_knot(p), cur_exp_knot, 0, NULL);
                    mp_pair_value(mp, &mp->cur_t, &mp->cur_tt);
                } else {
                    mp_knot last = NULL;
                    mp_knot list = mp_path_intersection(mp, mp_get_value_knot(p), cur_exp_knot, 1, &last);
                    mp_left_type(list) = mp_endpoint_knot;
                    mp_right_type(last) = mp_endpoint_knot;
                    mp->cur_exp.type = mp_path_type;
                    mp_set_cur_exp_knot(mp, list);
                }
            } else {
                mp_bad_binary(mp, p, c);
            }
            break;
    }
    mp_recycle_value(mp, p);
    mp_free_value_node(mp, p);
    mp_finish_binary(mp, old_p, old_exp);
}

static void mp_frac_mult (MP mp, mp_number *n, mp_number *d)
{
    mp_node old_exp;
    mp_number v;
    new_fraction(v);
    if (number_greater(internal_value(mp_tracing_commands_internal), two_t)) {
        mp_begin_diagnostic(mp);
        mp_print_nl(mp, "{(");
        print_number(*n);
        mp_print_chr(mp, '/');
        print_number(*d);
        mp_print_str(mp, ")*(");
        mp_print_exp(mp, NULL, 0);
        mp_print_str(mp, ")}");
        mp_end_diagnostic(mp, 0);
    }
    switch (mp->cur_exp.type) {
        case mp_transform_type:
        case mp_color_type:
        case mp_cmykcolor_type:
        case mp_pair_type:
            old_exp = mp_tarnished(mp, cur_exp_node);
            break;
        case mp_independent_type:
            old_exp = MP_VOID;
            break;
        default:
            old_exp = NULL;
            break;
    }
    if (old_exp != NULL) {
        old_exp = cur_exp_node;
        mp_make_exp_copy(mp, old_exp);
    }
    make_fraction(v, *n, *d);
    switch (mp->cur_exp.type) {
        case mp_known_type:
            {
                mp_number r1, arg1;
                new_fraction(r1);
                new_number_clone(arg1, cur_exp_value_number);
                take_fraction(r1, arg1, v);
                mp_set_cur_exp_value_number(mp, &r1);
                free_number(r1);
                free_number(arg1);
            }
            break;
        case mp_pair_type:
            {
                mp_dep_mult(mp, (mp_value_node) mp_x_part(mp_get_value_node(cur_exp_node)), &v, 0);
                mp_dep_mult(mp, (mp_value_node) mp_y_part(mp_get_value_node(cur_exp_node)), &v, 0);
            }
            break;
        case mp_color_type:
            {
                mp_dep_mult(mp, (mp_value_node) mp_red_part  (mp_get_value_node(cur_exp_node)), &v, 0);
                mp_dep_mult(mp, (mp_value_node) mp_green_part(mp_get_value_node(cur_exp_node)), &v, 0);
                mp_dep_mult(mp, (mp_value_node) mp_blue_part (mp_get_value_node(cur_exp_node)), &v, 0);
            }
            break;
        case mp_cmykcolor_type:
            {
                mp_dep_mult(mp, (mp_value_node) mp_cyan_part   (mp_get_value_node(cur_exp_node)), &v, 0);
                mp_dep_mult(mp, (mp_value_node) mp_magenta_part(mp_get_value_node(cur_exp_node)), &v, 0);
                mp_dep_mult(mp, (mp_value_node) mp_yellow_part (mp_get_value_node(cur_exp_node)), &v, 0);
                mp_dep_mult(mp, (mp_value_node) mp_black_part  (mp_get_value_node(cur_exp_node)), &v, 0);
            }
            break;
        default:
            {
                mp_dep_mult(mp, NULL, &v, 0);
            }
            break;
    }
    if (old_exp != NULL) {
        mp_recycle_value(mp, old_exp);
        mp_free_value_node(mp, old_exp);
    }
    free_number(v);
}

static void worry_about_bad_statement (MP mp);

static void flush_unparsable_junk_after_statement (MP mp);

void mp_do_statement (MP mp)
{
    mp->cur_exp.type = mp_vacuous_type;
    mp_get_x_next(mp);
    if (cur_cmd > mp_max_primary_command) {
        worry_about_bad_statement(mp);
    } else if (cur_cmd > mp_max_statement_command) {
        mp_value new_expr;
        mp->var_flag = mp_assignment_command;
        mp_scan_expression(mp);
        if (cur_cmd < mp_end_group_command) {
            if (cur_cmd == mp_equals_command) {
                mp_do_equation(mp);
            } else if (cur_cmd == mp_assignment_command) {
                mp_do_assignment(mp);
            } else if (mp->cur_exp.type == mp_string_type) {
                if (number_positive(internal_value(mp_tracing_titles_internal))) {
                    mp_print_nl(mp, "");
                    mp_print_mp_str(mp, cur_exp_str);
                    update_terminal();
                }
            } else if (mp->cur_exp.type != mp_vacuous_type) {
                mp_disp_err(mp, NULL);
                mp_back_error(
                    mp,
                    "Isolated expression",
                    "I couldn't find an '=' or ':=' after the expression that is shown above this\n"
                    "error message, so I guess I'll just ignore it and carry on."
                );
                mp_get_x_next(mp);
            }
            memset(&new_expr, 0, sizeof(mp_value));
            new_number(new_expr.data.n);
            mp_flush_cur_exp(mp, new_expr);
            mp->cur_exp.type = mp_vacuous_type;
        }
    } else {
        if (number_positive(internal_value(mp_tracing_commands_internal))) {
            mp_show_cmd_mod(mp, cur_cmd, cur_mod);
        }
        switch (cur_cmd) {
            case mp_type_name_command:
                mp_do_type_declaration(mp);
                break;
            case mp_macro_def_command:
                switch (cur_mod) {
                    case mp_def_code:
                    case mp_var_def_code:
                        mp_scan_def(mp, cur_mod);
                        break;
                    case mp_primary_def_code:
                    case mp_secondary_def_code:
                    case mp_tertiary_def_code:
                        mp_make_op_def(mp, cur_mod);
                        break;
                }
                break;
            case mp_only_set_command:
                switch (cur_mod) {
                    case mp_random_seed_code:
                        mp_do_random_seed(mp);
                        break;
                    case mp_max_knot_pool_code:
                        mp_do_max_knot_pool(mp);
                        break;
                }
                break;
            case mp_mode_command:
                mp_print_ln(mp);
                mp->interaction = cur_mod;
                mp->selector = mp->interaction == mp_batch_mode ? mp_log_only_selector : mp_term_and_log_selector;
                mp_get_x_next(mp);
                break;
            case mp_protection_command:
                mp_do_protection(mp);
                break;
            case mp_property_command:
                mp_do_property(mp);
                break;
            case mp_delimiters_command:
                mp_def_delims(mp);
                break;
            case mp_save_command:
                do {
                    mp_get_symbol(mp);
                    mp_save_variable(mp, cur_sym);
                    mp_get_x_next(mp);
                } while (cur_cmd == mp_comma_command);
                break;
            case mp_interim_command:
                mp_do_interim(mp);
                break;
            case mp_let_command:
                mp_do_let(mp);
                break;
            case mp_new_internal_command:
                mp_do_new_internal(mp);
                break;
            case mp_show_command:
                mp_do_show_whatever(mp);
                break;
            case mp_add_to_command:
                mp_do_add_to(mp);
                break;
            case mp_bounds_command:
                mp_do_bounds(mp);
                break;
            case mp_ship_out_command:
                mp_do_ship_out(mp);
                break;
            case mp_every_job_command:
                mp_get_symbol(mp);
                mp->every_job_sym = cur_sym;
                mp_get_x_next(mp);
                break;
            case mp_message_command:
                mp_do_message(mp);
                break;
            case mp_write_command:
                mp_do_write(mp);
                break;
            default:
                break;
        }
        mp->cur_exp.type = mp_vacuous_type;
    }
    if (cur_cmd < mp_semicolon_command) {
        flush_unparsable_junk_after_statement(mp);
    }
    mp->error_count = 0;
}

static void worry_about_bad_statement (MP mp)
{
  if (cur_cmd < mp_semicolon_command) {
    char msg[256];
    mp_string sname;
    int selector = mp->selector;
    mp->selector = mp_new_string_selector;
    mp_print_cmd_mod(mp, cur_cmd, cur_mod);
    sname = mp_make_string(mp);
    mp->selector = selector;
    mp_snprintf(msg, 256, "A statement can't begin with '%s'", mp_str(mp, sname));
    delete_str_ref(sname);
    mp_back_error(
        mp,
        msg,
        "I was looking for the beginning of a new statement. If you just proceed without\n"
        "changing anything, I'll ignore everything up to the next ';'."
    );
    mp_get_x_next(mp);
  }
}

static void flush_unparsable_junk_after_statement (MP mp)
{
    mp_back_error(
        mp,
        "Extra tokens will be flushed",
        "I've just read as much of that statement as I could fathom, so a semicolon should\n"
        "have been next. It's very puzzling ... but I'll try to get myself back together,\n"
        "by ignoring everything up to the next ';'."
    );
    mp->scanner_status = mp_flushing_state;
    do {
        get_t_next(mp);
        if (cur_cmd == mp_string_command) {
            delete_str_ref(cur_mod_str);
        }
    } while (! mp_end_of_statement);
    mp->scanner_status = mp_normal_state;
}

static void trace_equation (MP mp, mp_node lhs)
{
    mp_begin_diagnostic(mp);
    mp_print_nl(mp, "{(");
    mp_print_exp(mp, lhs, 0);
    mp_print_str(mp, ")=(");
    mp_print_exp(mp, NULL, 0);
    mp_print_str(mp, ")}");
    mp_end_diagnostic(mp, 0);
}

void mp_do_equation (MP mp)
{
    mp_node lhs = mp_stash_cur_exp(mp);
    mp_get_x_next(mp);
    mp->var_flag = mp_assignment_command;
    mp_scan_expression(mp);
    if (cur_cmd == mp_equals_command) {
        mp_do_equation(mp);
    } else if (cur_cmd == mp_assignment_command) {
        mp_do_assignment(mp);
    }
    if (number_greater(internal_value(mp_tracing_commands_internal), two_t)) {
        trace_equation(mp, lhs);
    }
    if (mp->cur_exp.type == mp_unknown_path_type) {
        if (lhs->type == mp_pair_type) {
            mp_node p;
            p = mp_stash_cur_exp(mp);
            mp_unstash_cur_exp(mp, lhs);
            lhs = p;
        }
    }
    mp_make_eq(mp, lhs);
}

static void bad_lhs (MP mp)
{
    mp_disp_err(mp, NULL);
    mp_error(
        mp,
        "Improper ':=' will be changed to '='",
        "I didn't find a variable name at the left of the ':=', so I'm going to pretend\n"
        "that you said '=' instead."
    );
    mp_do_equation(mp);
}

static void bad_internal_assignment (MP mp, mp_node lhs)
{
    char msg[256];
    mp_disp_err(mp, NULL);
    if (internal_type(mp_get_sym_info(lhs)) == mp_known_type) {
        mp_snprintf(msg, 256,
            "Internal quantity '%s' must receive a known numeric value",
            internal_name(mp_get_sym_info(lhs))
        );
        mp_back_error(
            mp,
            msg,
            "I can't set this internal quantity to anything but a known numeric value, so I'll\n"
            "have to ignore this assignment."
        );
    } else if (internal_type(mp_get_sym_info(lhs)) == mp_boolean_type) {
        mp_snprintf(msg, 256,
            "Internal quantity '%s' must receive a known boolean value",
            internal_name(mp_get_sym_info(lhs))
        );
        mp_back_error(
            mp,
            msg,
            "I can't set this internal quantity to anything but a known boolean value, so I'll\n"
            "have to ignore this assignment."
        );
    } else {
        mp_snprintf(msg, 256,
            "Internal quantity '%s' must receive a known string",
            internal_name(mp_get_sym_info(lhs))
        );
        mp_back_error(
            mp,
            msg,
            "I can't set this internal quantity to anything but a known string value, so I'll\n"
            "have to ignore this assignment."
        );
    }
    mp_get_x_next(mp);
}

static void forbidden_internal_assignment (MP mp, mp_node lhs)
{
    char msg[256];
    mp_snprintf(msg, 256,"Internal quantity '%s' is read-only", internal_name(mp_get_sym_info(lhs)));
    mp_back_error(
        mp,
        msg,
        "I can't set this internal quantity to anything just yet (it is read-only), so\n"
        "I'll have to ignore this assignment."
    );
    mp_get_x_next(mp);
}

static void bad_internal_assignment_precision (MP mp, mp_node lhs, mp_number *min, mp_number *max)
{
    char msg[256];
    char hlp[256];
    mp_snprintf(msg, 256,
        "Bad '%s' has been ignored",
        internal_name(mp_get_sym_info(lhs)));
    mp_snprintf(hlp, 256,
        "Precision values are limited by the current numbersystem.\n"
        "Currently I am using '%s'; the allowed precision range is [%s,%s].",
        mp_str(mp, internal_string(mp_number_system_internal)), number_tostring(*min), number_tostring(*max));
    mp_back_error(mp, msg, hlp);
    mp_get_x_next(mp);
}

static void bad_expression_assignment (MP mp, mp_node lhs)
{
    char *msg = mp_obliterated(mp, lhs);
    mp_back_error(
        mp,
        msg,
        "It seems you did a nasty thing --- probably by accident, but nevertheless you\n"
        "nearly hornswoggled me ... While I was evaluating the right-hand side of this\n"
        "command, something happened, and the left-hand side is no longer a variable! So I\n"
        "won't change anything."
    );
    mp_memory_free(msg);
    mp_get_x_next(mp);
}

static void trace_assignment (MP mp, mp_node lhs)
{
    mp_begin_diagnostic(mp);
    mp_print_nl(mp, "{");
    if (lhs->name_type == mp_internal_operation) {
        mp_print_str(mp, internal_name(mp_get_sym_info(lhs)));
    } else {
        mp_show_token_list(mp, lhs, NULL);
    }
    mp_print_str(mp, ":=");
    mp_print_exp(mp, NULL, 0);
    mp_print_chr(mp, '}');
    mp_end_diagnostic(mp, 0);
}

void mp_do_assignment (MP mp)
{
    if (mp->cur_exp.type != mp_token_list_type) {
        bad_lhs(mp);
    } else {
        mp_node lhs = cur_exp_node;
        mp->cur_exp.type = mp_vacuous_type;
        mp_get_x_next(mp);
        mp->var_flag = mp_assignment_command;
        mp_scan_expression(mp);
        if (cur_cmd == mp_equals_command) {
            mp_do_equation(mp);
        } else if (cur_cmd == mp_assignment_command) {
            mp_do_assignment(mp);
        }
        if (number_greater(internal_value(mp_tracing_commands_internal), two_t)) {
            trace_assignment (mp, lhs);
        }
        if (lhs->name_type == mp_internal_operation) {
            switch (mp->cur_exp.type) {
                case mp_known_type:
                case mp_string_type:
                case mp_boolean_type:
                    if (internal_type(mp_get_sym_info(lhs)) == mp->cur_exp.type) {
                        switch (mp_get_sym_info(lhs)) {
                            case mp_number_system_internal:
                                forbidden_internal_assignment(mp, lhs);
                                break;

                            case mp_number_precision_internal:
                                if (mp->cur_exp.type == mp_known_type
                                      && (! number_less   (cur_exp_value_number, precision_min))
                                      && (! number_greater(cur_exp_value_number, precision_max))
                                    ) {
                                     if (internal_type(mp_get_sym_info(lhs)) == mp_string_type) {
                                         add_str_ref(cur_exp_str);
                                         set_internal_string(mp_get_sym_info(lhs), cur_exp_str);
                                     } else {
                                         number_clone(internal_value(mp_get_sym_info(lhs)), cur_exp_value_number);
                                     }
                                     set_precision();
                                } else {
                                    bad_internal_assignment_precision(mp, lhs, &precision_min, &precision_max);
                                }
                            default:
                                if (internal_type(mp_get_sym_info(lhs)) == mp_string_type) {
                                    add_str_ref(cur_exp_str);
                                    set_internal_string(mp_get_sym_info(lhs), cur_exp_str);
                                } else {
                                    number_clone(internal_value(mp_get_sym_info(lhs)), cur_exp_value_number);
                                }
                                break;
                        }
                    } else {
                        bad_internal_assignment(mp, lhs);
                    }
                    break;
                default:
                    bad_internal_assignment(mp, lhs);
                }
        } else {
            mp_node p = mp_find_variable(mp, lhs);
            if (p != NULL) {
                mp_node q = mp_stash_cur_exp(mp);
                mp->cur_exp.type = mp_und_type(mp, p);
                mp_recycle_value(mp, p);
                p->type = mp->cur_exp.type;
                mp_set_value_number(p, zero_t);
                mp_make_exp_copy(mp, p);
                p = mp_stash_cur_exp(mp);
                mp_unstash_cur_exp(mp, q);
                mp_make_eq(mp, p);
            } else {
                bad_expression_assignment(mp, lhs);
            }
        }
        mp_flush_node_list(mp, lhs);
    }
}

static void announce_bad_equation (MP mp, mp_node lhs)
{
    char msg[256];
    mp_snprintf(msg, 256,
        "Equation cannot be performed (%s=%s)",
        (lhs->type <= mp_pair_type ? mp_type_string(lhs->type) : "numeric"),
        (mp->cur_exp.type <= mp_pair_type ? mp_type_string(mp->cur_exp.type) : "numeric"));
    mp_disp_err(mp, lhs);
    mp_disp_err(mp, NULL);
    mp_back_error(
        mp,
        msg,
        "I'm sorry, but I don't know how to make such things equal. (See the two\n"
        "expressions just above the error message.)"
    );
    mp_get_x_next(mp);
}

static void mp_exclaim_inconsistent_equation (MP mp)
{
    mp_back_error(
        mp,
        "Inconsistent equation",
        "The equation I just read contradicts what was said before. But don't worry;\n"
        "continue and I'll just ignore it."
    );
    mp_get_x_next(mp);
}

static void mp_exclaim_redundant_or_inconsistent_equation (MP mp)
{
    mp_back_error(
        mp,
        "Redundant or inconsistent equation",
        "An equation between already-known quantities can't help. But don't worry;\n"
        "continue and I'll just ignore it."
    );
    mp_get_x_next(mp);
}

static void report_redundant_or_inconsistent_equation (MP mp, mp_node lhs, mp_number *v)
{
    if (mp->cur_exp.type <= mp_string_type) {
        if (mp->cur_exp.type == mp_string_type) {
            if (mp_str_vs_str(mp, mp_get_value_str(lhs), cur_exp_str) != 0) {
                mp_exclaim_inconsistent_equation(mp);
            } else {
                mp_exclaim_redundant_equation(mp);
            }
        } else if (number_equal(*v, cur_exp_value_number)) {
            mp_exclaim_redundant_equation(mp);
        } else {
            mp_exclaim_inconsistent_equation(mp);
        }
    } else {
        mp_exclaim_redundant_or_inconsistent_equation(mp);
    }
}

void mp_make_eq (MP mp, mp_node lhs)
{
    mp_value new_expr;
    mp_variable_type t;
    mp_number v;
    memset(&new_expr, 0, sizeof(mp_value));
    new_number(v);
  RESTART:
    t = lhs->type;
    if (t <= mp_pair_type) {
        number_clone(v, mp_get_value_number(lhs));
    }
    switch (t) {
        case mp_boolean_type:
        case mp_string_type:
        case mp_pen_type:
        case mp_nep_type:
        case mp_path_type:
        case mp_picture_type:
            if (mp->cur_exp.type == t + unknown_tag) {
                new_number(new_expr.data.n);
                switch (t) {
                    case mp_boolean_type:
                        number_clone(new_expr.data.n, v);
                        break;
                    case mp_string_type:
                        new_expr.data.str = mp_get_value_str(lhs);
                        break;
                    case mp_picture_type:
                        new_expr.data.node = mp_get_value_node(lhs);
                        break;
                    default:
                        new_expr.data.p = mp_get_value_knot(lhs);
                        break;
                }
                mp_nonlinear_eq(mp, new_expr, cur_exp_node, 0);
                mp_unstash_cur_exp(mp, cur_exp_node);
            } else if (mp->cur_exp.type == t) {
                report_redundant_or_inconsistent_equation(mp, lhs, &v);
            } else {
                announce_bad_equation(mp, lhs);
            }
            break;
        case mp_unknown_boolean_type:
        case mp_unknown_string_type:
        case mp_unknown_pen_type:
        case mp_unknown_nep_type:
        case mp_unknown_path_type:
        case mp_unknown_picture_type:
            if (mp->cur_exp.type == t - unknown_tag) {
                mp_nonlinear_eq(mp, mp->cur_exp, lhs, 1);
            } else if (mp->cur_exp.type == t) {
                mp_ring_merge (mp, lhs, cur_exp_node);
            } else if (mp->cur_exp.type == mp_pair_type) {
                if (t == mp_unknown_path_type) {
                    mp_pair_to_path(mp);
                    goto RESTART;
                }
            } else {
                announce_bad_equation(mp, lhs);
            }
            break;
        case mp_transform_type:
        case mp_color_type:
        case mp_cmykcolor_type:
        case mp_pair_type:
            if (mp->cur_exp.type == t) {
                mp_node q = mp_get_value_node(cur_exp_node);
                mp_node p = mp_get_value_node(lhs);
                switch (t) {
                    case mp_transform_type:
                        mp_try_eq(mp, mp_yy_part(p), mp_yy_part(q));
                        mp_try_eq(mp, mp_yx_part(p), mp_yx_part(q));
                        mp_try_eq(mp, mp_xy_part(p), mp_xy_part(q));
                        mp_try_eq(mp, mp_xx_part(p), mp_xx_part(q));
                        mp_try_eq(mp, mp_ty_part(p), mp_ty_part(q));
                        mp_try_eq(mp, mp_tx_part(p), mp_tx_part(q));
                        break;
                    case mp_color_type:
                        mp_try_eq(mp, mp_blue_part(p),  mp_blue_part(q));
                        mp_try_eq(mp, mp_green_part(p), mp_green_part(q));
                        mp_try_eq(mp, mp_red_part(p),   mp_red_part(q));
                        break;
                    case mp_cmykcolor_type:
                        mp_try_eq(mp, mp_black_part(p),   mp_black_part(q));
                        mp_try_eq(mp, mp_yellow_part(p),  mp_yellow_part(q));
                        mp_try_eq(mp, mp_magenta_part(p), mp_magenta_part(q));
                        mp_try_eq(mp, mp_cyan_part(p),    mp_cyan_part(q));
                        break;
                    case mp_pair_type:
                        mp_try_eq(mp, mp_y_part(p), mp_y_part(q));
                        mp_try_eq(mp, mp_x_part(p), mp_x_part(q));
                        break;
                    default:
                        break;
                }
            } else {
                announce_bad_equation(mp, lhs);
            }
            break;
        case mp_known_type:
        case mp_dependent_type:
        case mp_proto_dependent_type:
        case mp_independent_type:
            if (mp->cur_exp.type >= mp_known_type) {
                mp_try_eq(mp, lhs, NULL);
            } else {
                announce_bad_equation(mp, lhs);
            }
            break;
        case mp_vacuous_type:
            announce_bad_equation(mp, lhs);
            break;
        default:
            announce_bad_equation(mp, lhs);
            break;
    }
    check_arith(mp);
    mp_recycle_value(mp, lhs);
    free_number(v);
    mp_free_value_node(mp, lhs);
}

static void deal_with_redundant_or_inconsistent_equation (MP mp, mp_value_node p, mp_node r)
{
    mp_number absp;
    new_number_abs(absp, mp_get_value_number(p));
    if (number_greater(absp, equation_threshold_k)) {
        char msg[256];
        mp_snprintf(msg, 256, "Inconsistent equation (off by %s)", number_tostring (mp_get_value_number(p)));
        mp_back_error(
            mp,
            msg,
            "The equation I just read contradicts what was said before. But don't worry;\n"
            "continue and I'll just ignore it."
        );
        mp_get_x_next(mp);
    } else if (r == NULL) {
        mp_exclaim_redundant_equation(mp);
    }
    free_number(absp);
    mp_free_dep_node(mp, p);
}

void mp_try_eq (MP mp, mp_node l, mp_node r)
{
    mp_value_node p;
    mp_value_node q;
    mp_value_node pp;
    mp_variable_type tt;
    int copied;
    mp_variable_type t = l->type;
    switch (t) {
        case mp_known_type:
            {
                mp_number arg1;
                new_number(arg1);
                number_negated_clone(arg1, mp_get_value_number(l));
                t = mp_dependent_type;
                p = mp_const_dependency(mp, &arg1);
                q = p;
                free_number(arg1);
            }
            break;
        case mp_independent_type:
            {
                t = mp_dependent_type;
                p = mp_single_dependency(mp, l);
                number_negate(mp_get_dep_value(p));
                q = mp->dep_final;
            }
            break;
        default:
            {
                mp_value_node ll = (mp_value_node) l;
                p = (mp_value_node) mp_get_dep_list(ll);
                q = p;
                while (1) {
                    number_negate(mp_get_dep_value(q));
                    if (mp_get_dep_info(q) == NULL) {
                        break;
                    } else {
                        q = (mp_value_node) q->link;
                    }
                }
                mp_get_prev_dep(ll)->link = q->link;
                mp_set_prev_dep((mp_value_node) q->link, mp_get_prev_dep(ll));
                ll->type = mp_known_type;
            }
            break;
    }
    if (r == NULL) {
        if (mp->cur_exp.type == mp_known_type) {
            number_add(mp_get_value_number(q), cur_exp_value_number);
            goto DONE1;
        } else {
            tt = mp->cur_exp.type;
            if (tt == mp_independent_type) {
                pp = mp_single_dependency(mp, cur_exp_node);
            } else {
                pp = (mp_value_node) mp_get_dep_list((mp_value_node) cur_exp_node);
            }
        }
    } else if (r->type == mp_known_type) {
        number_add(mp_get_dep_value(q), mp_get_value_number(r));
        goto DONE1;
    } else {
        tt = r->type;
        if (tt == mp_independent_type) {
            pp = mp_single_dependency(mp, r);
        } else {
            pp = (mp_value_node) mp_get_dep_list((mp_value_node) r);
        }
    }
    if (tt != mp_independent_type) {
        copied = 0;
    } else {
        copied = 1;
        tt = mp_dependent_type;
    }
    mp->watch_coefs = 0;
    if (t == tt) {
        p = mp_p_plus_q(mp, p, pp, (int) t);
    } else if (t == mp_proto_dependent_type) {
        p = mp_p_plus_fq(mp, p, &unity_t, pp, mp_proto_dependent_type, mp_dependent_type);
    } else {
        mp_number x;
        new_number(x);
        q = p;
        while (mp_get_dep_info(q) != NULL) {
            number_clone(x, mp_get_dep_value(q));
            fraction_to_round_scaled(x);
            mp_set_dep_value(q, x);
            q = (mp_value_node) q->link;
        }
        free_number(x);
        t = mp_proto_dependent_type;
        p = mp_p_plus_q(mp, p, pp, (int) t);
    }
    mp->watch_coefs = 1;
    if (copied) {
        mp_flush_node_list(mp, (mp_node) pp);
    }
  DONE1:
    if (mp_get_dep_info(p) == NULL) {
        deal_with_redundant_or_inconsistent_equation(mp, p, r);
    } else {
        mp_linear_eq(mp, p, (int) t);
        if (r == NULL && mp->cur_exp.type != mp_known_type && cur_exp_node->type == mp_known_type) {
            mp_node pp = cur_exp_node;
            mp_set_cur_exp_value_number(mp, &(mp_get_value_number(pp)));
            mp->cur_exp.type = mp_known_type;
            mp_free_value_node(mp, pp);
        }
    }
}

mp_node mp_scan_declared_variable (MP mp)
{
    mp_sym x;
    mp_node h, t;
    mp_get_symbol(mp);
    x = cur_sym;
    if (cur_cmd != mp_tag_command) {
        mp_clear_symbol(mp, x, 0);
    }
    h = mp_new_symbolic_node(mp);
    mp_set_sym_sym(h, x);
    t = h;
    while (1) {
        mp_get_x_next(mp);
        if (cur_sym == NULL) {
            break;
        } else if (cur_cmd != mp_tag_command) {
            if (cur_cmd != mp_internal_command) {
                if (cur_cmd == mp_left_bracket_command) {
                    mp_sym ll = cur_sym;
                    mp_get_x_next(mp);
                    if (cur_cmd == mp_right_bracket_command) {
                        set_cur_sym(mp_collective_subscript);
                    } else {
                        mp_back_input(mp);
                        set_cur_sym(ll);
                        set_cur_cmd(mp_left_bracket_command);
                        break;
                    }
                } else {
                    break;
                }
          }
        }
        t->link = mp_new_symbolic_node(mp);
        t = t->link;
        mp_set_sym_sym(t, cur_sym);
        t->name_type = cur_sym_mod;
    }
    if (eq_property(x) != 0) {
        mp_check_overload(mp, x);
    }
    if (eq_type(x) != mp_tag_command) {
        mp_clear_symbol(mp, x, 0);
    }
    if (equiv_node(x) == NULL) {
        mp_new_root (mp, x);
    }
    return h;
}

static void flush_spurious_symbols_after_declared_variable (MP mp);

void mp_do_type_declaration (MP mp)
{
    int t = mp_numeric_type;
    switch (cur_mod) {
        case mp_string_type_operation:    t = mp_unknown_string_type; break;
        case mp_boolean_type_operation:   t = mp_unknown_boolean_type; break;
        case mp_path_type_operation:      t = mp_unknown_path_type; break;
        case mp_pen_type_operation:       t = mp_unknown_pen_type; break;
        case mp_nep_type_operation:       t = mp_unknown_nep_type; break;
        case mp_picture_type_operation:   t = mp_unknown_picture_type; break;
        case mp_transform_type_operation: t = mp_transform_type;  break;
        case mp_color_type_operation:     t = mp_color_type; break;
        case mp_cmykcolor_type_operation: t = mp_cmykcolor_type; break;
        case mp_pair_type_operation:      t = mp_pair_type; break;
        case mp_numeric_type_operation:   t = mp_numeric_type; break;
    }
    do {
        mp_node p = mp_scan_declared_variable(mp);
        mp_node q;
        mp_flush_variable(mp, equiv_node(mp_get_sym_sym(p)), p->link, 0);
        q = mp_find_variable(mp, p);
        if (q != NULL) {
            q->type = t;
            mp_set_value_number(q, zero_t);
        } else {
            mp_back_error(
                mp,
                "Declared variable conflicts with previous vardef",
                "You can't use, e.g., 'numeric foo[]' after 'vardef foo'. Proceed, and I'll ignore\n"
                "the illegal redeclaration."
            );
            mp_get_x_next(mp);
        }
        mp_flush_node_list(mp, p);
        if (cur_cmd < mp_comma_command) {
            flush_spurious_symbols_after_declared_variable(mp);
        }
    } while (! mp_end_of_statement);
}

static void flush_spurious_symbols_after_declared_variable (MP mp)
{
    const char *hlp = NULL;
    if (cur_cmd == mp_numeric_command) {
        hlp =
            "Variables in declarations must consist entirely of names and explicit subscripts\n"
            "like 'x15a' aren't permitted. I'm going to discard the junk I found here, up to the\n"
            "next comma or the end of the declaration.";
    } else {
        hlp =
            "Variables in declarations must consist entirely of names and collective\n"
            "subscripts, e.g., 'x[]a'. Are you trying to use a reserved word in a variable\n"
            "name? I'm going to discard the junk I found here, up to the next comma or the end\n"
            "of the declaration.";
    }
    mp_back_error(
        mp,
        "Illegal suffix of declared variable will be flushed",
        hlp
    );
    mp_get_x_next(mp);
    mp->scanner_status = mp_flushing_state;
    do {
        get_t_next(mp);
        if (cur_cmd == mp_string_command) {
            delete_str_ref(cur_mod_str);
        }
    } while (cur_cmd < mp_comma_command);
    mp->scanner_status = mp_normal_state;
}

static void mp_main_control (MP mp) {
    do {
        mp_do_statement(mp);
        if (cur_cmd == mp_end_group_command) {
            mp_value new_expr;
            memset(&new_expr, 0, sizeof(mp_value));
            new_number(new_expr.data.n);
            mp_error(
                mp,
                "Extra 'endgroup'",
                "I'm not currently working on a 'begingroup', so I had better not try to end\n"
                "anything."
            );
            mp_flush_cur_exp(mp, new_expr);
        }
    } while (cur_cmd != mp_stop_command);
}

int mp_run (MP mp)
{
    if (mp->history < mp_fatal_error_stop) {
        mp_memory_free(mp->jump_buf);
        mp->jump_buf = mp_memory_allocate(sizeof(jmp_buf));
        if (mp->jump_buf == NULL || setjmp (*(mp->jump_buf)) != 0) {
            return mp->history;
        }
        mp_main_control(mp);
        mp_final_cleanup(mp);
        mp_close_files_and_terminate(mp);
    }
    return mp->history;
}

void mp_set_internal (MP mp, char *n, char *v, int isstring)
{
    size_t l = strlen(n);
    char err[256];
    const char *errid = NULL;
    if (l > 0) {
        mp_sym p = mp_id_lookup(mp, n, l, 0);
        if (p == NULL) {
            errid = "variable does not exist";
        } else if (eq_type(p) != mp_internal_command) {
            errid = "variable is not an internal";
        } else if ((internal_type(equiv(p)) == mp_string_type) && (isstring)) {
            set_internal_string(equiv(p), mp_rts(mp, v));
        } else if ((internal_type(equiv(p)) == mp_known_type) && (! isstring)) {
            int test = atoi(v);
            if (test > 16383 && mp->math_mode == mp_math_scaled_mode) {
                errid = "value is too large";
            } else if (test < -16383 && mp->math_mode == mp_math_scaled_mode) {
                errid = "value is too small";
            } else {
                number_clone(internal_value(equiv(p)), unity_t);
                number_multiply_int(internal_value(equiv(p)), test);
            }
        } else {
            errid = "value has the wrong type";
        }
    }
    if (errid != NULL) {
        if (isstring) {
            mp_snprintf(err, 256, "%s=\"%s\": %s, assignment ignored.", n, v, errid);
        } else {
            mp_snprintf(err, 256, "%s=%d: %s, assignment ignored.", n, atoi (v), errid);
        }
        mp_warn(mp, err);
    }
}

void mplib_shipout_backend (MP mp, void *voidh)
{
    mp_edge_header_node h = (mp_edge_header_node) voidh;
    mp_edge_object *hh = mp_gr_export (mp, h);
    if (hh) {
        mp_run_data *run = mp_rundata(mp);
        if (run->edges == NULL) {
            run->edges = hh;
        } else {
            mp_edge_object *p = run->edges;
            while (p->next != NULL) {
                p = p->next;
            }
            p->next = hh;
        }
    }
}

mp_run_data *mp_rundata (MP mp) {
    return &(mp->run_data);
}

int mp_execute (MP mp, const char *s, size_t l)
{
    (void) l;
    if (mp->finished) {
        return mp->history;
    } else if (mp->history < mp_fatal_error_stop) {
        mp_memory_free(mp->jump_buf);
        mp->jump_buf = mp_memory_allocate(sizeof(jmp_buf));
        if (mp->jump_buf == NULL || setjmp (*(mp->jump_buf)) != 0) {
            return mp->history;
        } else {
            mp->term_offset = 0;
            mp->file_offset = 0;
            if (mp->term_in == NULL) {
                mp->term_in = (mp->open_file)(mp,"terminal", "r", mp_filetype_terminal);
                mp->last = 0;
            }
            if (s && l > 0) {
                (mp->write_file)(mp, mp->term_in, s);
            } else {
            }
            if (mp->run_state == 0) {

                mp->term_offset = 0;
                mp->file_offset = 0;
                mp_log_string(mp_term_logging_target, mp->banner);
                mp_log_string(mp_term_logging_target, ", running in ");
                mp_log_string(mp_term_logging_target, mp_str(mp, internal_string(mp_number_system_internal)));
                mp_log_string(mp_term_logging_target, " mode.");
                mp_print_ln(mp);
                update_terminal();

                mp->input_ptr = 0;
                mp->max_in_stack = mp_file_bottom_text;
                mp->in_open = mp_file_bottom_text;
                mp->open_parens = 0;
                mp->max_buf_stack = 0;
                mp->param_ptr = 0;
                mp->max_param_stack = 0;
                start = loc = 0;
                iindex = mp_file_bottom_text;
                nloc = nstart = NULL;
                mp->first = 0;
                line = 0;
                name = is_term;
                mp->force_eof = 0;
                if (mp->term_in == NULL) {
                    mp->term_in = (mp->open_file)(mp,"terminal", "r", mp_filetype_terminal);
                }
                mp->last = 0;
                mp->scanner_status = mp_normal_state;
                mp_fix_date_and_time(mp);
                if (mp->random_seed == 0) {
                    mp->random_seed = (number_to_scaled(internal_value(mp_time_internal))/number_to_scaled(unity_t))
                        + number_to_scaled(internal_value(mp_day_internal));
                }
                init_randoms(mp->random_seed);
                mp->selector = mp->interaction == mp_batch_mode ? mp_no_print_selector : mp_term_only_selector;
                mp->history = mp_spotless;
                if (mp->every_job_sym != NULL) {
                    set_cur_sym(mp->every_job_sym);
                    mp_back_input(mp);
                }
            }
            mp->run_state = 1;
            mp_input_ln(mp, mp->term_in);
            mp_firm_up_the_line(mp);
            mp->buffer[limit] = '%';
            mp->first = (size_t) (limit + 1);
            loc = start;
            do {
                mp_do_statement(mp);
            } while (cur_cmd != mp_stop_command);
            mp_final_cleanup(mp);
            mp_close_files_and_terminate(mp);
        }
    }
    return mp->history;
}

int mp_finish (MP mp)
{
    int history = 0;
    if (mp->finished || mp->history >= mp_fatal_error_stop) {
        history = mp->history;
        mp_free(mp);
    } else {
        mp_memory_free(mp->jump_buf);
        mp->jump_buf = mp_memory_allocate(sizeof(jmp_buf));
        if (mp->jump_buf == NULL || setjmp (*(mp->jump_buf)) != 0) {
            history = mp->history;
        } else {
            history = mp->history;
            mp_final_cleanup(mp);
        }
        mp_close_files_and_terminate(mp);
        mp_free(mp);
    }
    return history;
}

char *mp_metapost_version(void) {
    return mp_strdup(metapost_version);
}

void mp_do_max_knot_pool (MP mp)
{
    mp_value new_expr;
    memset(&new_expr, 0, sizeof(mp_value));
    new_number(new_expr.data.n);
    mp_get_x_next(mp);
    if (cur_cmd != mp_assignment_command) {
        mp_back_error(
            mp,
            "Missing ':=' has been inserted",
            "Always say 'maxknotpool := <numeric expression>'."
        );
    };
    mp_get_x_next(mp);
    mp_scan_expression(mp);
    if (mp->cur_exp.type != mp_known_type) {
        mp_disp_err(mp, NULL);
        mp_back_error(
            mp,
            "Unknown value will be ignored",
            "Your expression was too random for me to handle, so I won't change the maximum\n"
            "seed just now."
        );
        mp_get_x_next(mp);
        mp_flush_cur_exp(mp, new_expr);
    } else {
        int p = (int) number_to_scaled (cur_exp_value_number) / 65536;
        if (p > mp->max_knot_nodes) {
            mp->max_knot_nodes = p;
        } else if (p > max_num_knot_nodes) {
        } else {
        }
    }
}

void mp_do_random_seed (MP mp)
{
    mp_value new_expr;
    memset(&new_expr, 0, sizeof(mp_value));
    new_number(new_expr.data.n);
    mp_get_x_next(mp);
    if (cur_cmd != mp_assignment_command) {
        mp_back_error(
            mp,
            "Missing ':=' has been inserted",
            "Always say 'randomseed := <numeric expression>'."
        );
    };
    mp_get_x_next(mp);
    mp_scan_expression(mp);
    if (mp->cur_exp.type != mp_known_type) {
        mp_disp_err(mp, NULL);
        mp_back_error(
            mp,
            "Unknown value will be ignored",
            "Your expression was too random for me to handle, so I won't change the random\n"
            "seed just now."
        );
        mp_get_x_next(mp);
        mp_flush_cur_exp(mp, new_expr);
    } else {
        init_randoms(number_to_scaled(cur_exp_value_number));
        if (mp->interaction < mp_silent_mode && (mp->selector == mp_log_only_selector || mp->selector == mp_term_and_log_selector)) {
            int selector = mp->selector;
            mp->selector = mp_log_only_selector;
            mp_print_nl(mp, "{randomseed:=");
            print_number(cur_exp_value_number);
            mp_print_chr(mp, '}');
            mp_print_nl(mp, "");
            mp->selector = selector;
        }
    }
}

void mp_do_protection (MP mp)
{

    do {

        mp_get_symbol(mp);

        mp_get_x_next(mp);
    } while (cur_cmd == mp_comma_command);
}

void mp_do_property (MP mp)
{
   int p = 0;
   mp_get_x_next(mp);
   mp_scan_primary(mp);
   switch (mp->cur_exp.type) {
        case mp_numeric_type:
        case mp_known_type:
            {
                mp_back_input(mp);
                p = (int) number_to_scaled (cur_exp_value_number) / 65536;
            }
            break;
        default:
            mp_back_error(mp, "Bad property value", NULL);
            break;
    }
    mp_get_x_next(mp);
    if (cur_cmd == mp_colon_command) {
        do {
            mp_get_symbol(mp);
            set_eq_property(cur_sym, p);
            mp_get_x_next(mp);
        } while (cur_cmd == mp_comma_command);
    } else {
        mp_back_error(mp, "Bad property specification, colon expected", NULL);
    }
}

void mp_def_delims (MP mp)
{
    mp_sym l_delim, r_delim;
    mp_get_clear_symbol(mp);
    l_delim = cur_sym;
    mp_get_clear_symbol(mp);
    r_delim = cur_sym;
    set_eq_type(l_delim, mp_left_delimiter_command);
    set_equiv_sym(l_delim, r_delim);
    set_eq_type(r_delim, mp_right_delimiter_command);
    set_equiv_sym(r_delim, l_delim);
    mp_get_x_next(mp);
}

void mp_check_delimiter (MP mp, mp_sym l_delim, mp_sym r_delim)
{
    if (cur_cmd == mp_right_delimiter_command && equiv_sym(cur_sym) == l_delim) {
        return;
    } else if (cur_sym != r_delim) {
        char msg[256];
        mp_snprintf(msg, 256, "Missing '%s' has been inserted", mp_str(mp, text(r_delim)));
        mp_back_error(
            mp,
            msg,
            "I found no right delimiter to match a left one. So I've put one in, behind the\n"
            "scenes; this may fix the problem."
        );
    } else {
        char msg[256];
        mp_snprintf(msg, 256, "The token '%s' is no longer a right delimiter", mp_str(mp, text(r_delim)));
        mp_error(
            mp,
            msg,
            "Strange: This token has lost its former meaning! I'll read it as a right\n"
            "delimiter this time; but watch out, I'll probably miss it later."
        );
    }
}

void mp_do_interim (MP mp) {
    mp_get_x_next(mp);
    if (cur_cmd != mp_internal_command) {
        char msg[256];
        mp_snprintf(msg, 256,
            "The token '%s' isn't an internal quantity",
            (cur_sym == NULL ? "(%CAPSULE)" : mp_str(mp, text(cur_sym)))
        );
        mp_back_error(mp, msg, "Something like 'tracingonline' should follow 'interim'.");
    } else {
        mp_save_internal(mp, cur_mod);
        mp_back_input(mp);
    }
    mp_do_statement(mp);
}

void mp_do_let (MP mp)
{
    mp_sym l;
    mp_get_symbol(mp);
    l = cur_sym;
    mp_get_x_next(mp);
    if (cur_cmd != mp_equals_command && cur_cmd != mp_assignment_command) {
        mp_back_error(
            mp,
            "Missing '=' has been inserted",
            "You should have said 'let symbol = something'. But don't worry; I'll pretend that\n"
            "an equals sign was present. The next token I read will be 'something'."
        );
    }
    mp_get_symbol(mp);
    switch (cur_cmd) {
        case mp_defined_macro_command:
        case mp_primary_def_command:
        case mp_secondary_def_command:
        case mp_tertiary_def_command:
            mp_add_mac_ref(cur_mod_node);
            break;
        default:
            break;
    }
    mp_clear_symbol(mp, l, 0);
    set_eq_type(l, cur_cmd);
    switch (cur_cmd) {
        case mp_tag_command:
            set_equiv(l, 0);
            break;
        case mp_defined_macro_command:
        case mp_primary_def_command:
        case mp_secondary_def_command:
        case mp_tertiary_def_command:
            set_equiv_node(l, cur_mod_node);
            break;
        case mp_left_delimiter_command:
        case mp_right_delimiter_command:
            set_equiv_sym(l, equiv_sym(cur_sym));
            break;
        default:
            set_equiv(l, cur_mod);
            break;
    }
    mp_get_x_next(mp);
}

void mp_grow_internals (MP mp, int l)
{
    if (l > max_halfword) {
        mp_confusion(mp, "out of memory");
    } else {
        mp_internal *internal = mp_memory_allocate((size_t) (l + 1) * sizeof(mp_internal));
        for (int k = 0; k <= l; k++) {
            if (k <= mp->max_internal) {
                memcpy(internal + k, mp->internal + k, sizeof(mp_internal));
            } else {
                memset(internal + k, 0, sizeof(mp_internal));
                new_number(((mp_internal *)(internal + k))->v.data.n);
            }
        }
        mp_memory_free(mp->internal);
        mp->internal = internal;
        mp->max_internal = l;
    }
}

void mp_do_new_internal (MP mp)
{
    int the_type = mp_known_type;
    int run_script = 0;
    mp_get_next(mp);
    if (cur_cmd == mp_type_name_command && cur_mod == mp_string_type_operation) {
        the_type = mp_string_type;
    } else if (cur_cmd == mp_type_name_command && cur_mod == mp_boolean_type_operation) {
        the_type = mp_boolean_type;
    } else if (cur_cmd == mp_type_name_command && cur_mod == mp_numeric_type_operation) {
        the_type = mp_numeric_type;
    } else if (! (cur_cmd == mp_type_name_command && cur_mod == mp_numeric_type_operation)) {
        mp_back_input(mp);
    }
    if (mp_numeric_type == mp_known_type) {
    } else {
        if (the_type == mp_numeric_type) {
            the_type = mp_known_type;
        }
        mp_get_next(mp);
        if (cur_cmd == mp_runscript_command) {
            run_script = 1;
        } else {
            mp_back_input(mp);
        }
    }
    do {
        if (mp->int_ptr == mp->max_internal) {
            mp_grow_internals(mp, (mp->max_internal + (mp->max_internal / 4)));
        }
        mp_get_clear_symbol(mp);
        ++mp->int_ptr;
        set_eq_type(cur_sym, mp_internal_command);
        set_equiv(cur_sym, mp->int_ptr);
        mp_memory_free(internal_name(mp->int_ptr));
        set_internal_name(mp->int_ptr, mp_strdup(mp_str(mp, text(cur_sym))));
        if (the_type == mp_string_type) {
            set_internal_string(mp->int_ptr, mp_rts(mp,""));
        } else {
            set_number_to_zero(internal_value(mp->int_ptr));
        }
        set_internal_type(mp->int_ptr, the_type);
        set_internal_run(mp->int_ptr, run_script);
        if (run_script) {
           mp->run_internal(mp, 0, mp->int_ptr, the_type, internal_name(mp->int_ptr));
        }
        mp_get_x_next(mp);
  } while (cur_cmd == mp_comma_command);
}

void mp_do_show (MP mp)
{
    do {
        mp_value new_expr;
        memset(&new_expr, 0, sizeof(mp_value));
        new_number(new_expr.data.n);
        mp_get_x_next(mp);
        mp_scan_expression(mp);
        mp_print_nl(mp, ">> ");
        mp_print_exp(mp, NULL, 2);
        mp_flush_cur_exp(mp, new_expr);
    } while (cur_cmd == mp_comma_command);
}

void mp_disp_token (MP mp)
{
    mp_print_nl(mp, "> ");
    if (cur_sym == NULL) {
        switch (cur_cmd) {
            case mp_numeric_command:
                print_number(cur_mod_number);
                break;
            case mp_capsule_command:
                mp_print_capsule(mp, cur_mod_node);
                break;
            default:
                mp_print_chr(mp, '"');
                mp_print_mp_str(mp, cur_mod_str);
                mp_print_chr(mp, '"');
                delete_str_ref(cur_mod_str);
                break;
        }
    } else {
        mp_print_mp_str(mp,text(cur_sym));
        mp_print_chr(mp, '=');

        mp_print_cmd_mod(mp, cur_cmd, cur_mod);
        if (cur_cmd == mp_defined_macro_command) {
            mp_print_ln(mp);
            mp_show_macro (mp, cur_mod_node, NULL);
        }
    }
}

void mp_do_show_token (MP mp)
{
    do {
        get_t_next(mp);
        mp_disp_token(mp);
        mp_get_x_next(mp);
    } while (cur_cmd == mp_comma_command);
}

void mp_do_show_stats (MP mp)
{
    mp_print_nl(mp, "Memory usage ");
    mp_print_int(mp, (int) mp->var_used);
    mp_print_ln(mp);
    mp_print_nl(mp, "String usage ");
    mp_print_int(mp, (int) mp->strs_in_use);
    mp_print_chr(mp, '&');
    mp_print_int(mp, (int) mp->pool_in_use);
    mp_print_ln(mp);
    mp_get_x_next(mp);
}

void mp_disp_var (MP mp, mp_node p)
{
    if (p->type == mp_structured_type) {
        mp_node q = mp_get_attribute_head(p);
        do {
            mp_disp_var(mp, q);
            q = q->link;
        } while (q != mp->end_attr);
        q = mp_get_subscr_head(p);
        while (q->name_type == mp_subscript_operation) {
            mp_disp_var(mp, q);
            q = q->link;
        }
    } else if (p->type >= mp_unsuffixed_macro_type) {
        mp_print_nl(mp, "");
        mp_print_variable_name(mp, p);
        if (p->type > mp_unsuffixed_macro_type) {
            mp_print_str(mp, "@#");
        }
        mp_print_str(mp, "=macro:");
        mp_show_macro(mp, mp_get_value_node(p), NULL);
    } else if (p->type != mp_undefined_type) {
        mp_print_nl(mp, "");
        mp_print_variable_name(mp, p);
        mp_print_chr(mp, '=');
        mp_print_exp(mp, p, 0);
    }
}

void mp_do_show_var (MP mp)
{
    do {
        get_t_next(mp);
        if (cur_sym != NULL && cur_sym_mod == 0 && cur_cmd == mp_tag_command) {
            if (cur_mod != 0 || cur_mod_node != NULL) {
                mp_disp_var(mp, cur_mod_node);
                goto DONE;
            }
        }
        mp_disp_token(mp);
      DONE:
        mp_get_x_next(mp);
    } while (cur_cmd == mp_comma_command);
}

void mp_do_show_dependencies (MP mp)
{
    mp_value_node p = (mp_value_node) mp->dep_head->link;
    while (p != mp->dep_head) {
        if (mp_interesting(mp, (mp_node) p)) {
            mp_print_nl(mp, "");
            mp_print_variable_name(mp, (mp_node) p);
            if (p->type == mp_dependent_type) {
                mp_print_chr(mp, '=');
            } else {
                mp_print_str(mp, " = ");
            }
            mp_print_dependency(mp, (mp_value_node) mp_get_dep_list(p), p->type);
        }
        p = (mp_value_node) mp_get_dep_list(p);
        while (mp_get_dep_info(p) != NULL) {
            p = (mp_value_node) p->link;
        }
        p = (mp_value_node) p->link;
    }
    mp_get_x_next(mp);
}

void mp_do_show_whatever (MP mp)
{
    if (mp->interaction == mp_error_stop_mode) {
        wake_up_terminal();
    }
    switch (cur_mod) {
        case mp_show_token_code:
            mp_do_show_token(mp);
            break;
        case mp_show_stats_code:
            mp_do_show_stats(mp);
            break;
        case mp_show_code:
            mp_do_show(mp);
            break;
        case mp_show_var_code:
            mp_do_show_var(mp);
            break;
        case mp_show_dependencies_code:
            mp_do_show_dependencies(mp);
            break;
    }
    if (number_positive(internal_value(mp_showstopping_internal))) {
        const char *hlp = NULL;
        if (mp->interaction < mp_error_stop_mode) {
            --mp->error_count;
        } else {
            hlp = "This isn't an error message; I'm just showing something.";
        }
        if (cur_cmd == mp_semicolon_command) {
            mp_error(mp, "OK", hlp);
        } else {
            mp_back_error(mp, "OK", hlp);
            mp_get_x_next(mp);
        }
    }
}

static void complain_invalid_with_list (MP mp, mp_variable_type t)
{
    mp_value new_expr;
    const char *hlp = NULL;
    memset(&new_expr, 0, sizeof(mp_value));
    new_number(new_expr.data.n);
    mp_disp_err(mp, NULL);
    switch (t) {
        case mp_with_pre_script_code:
            hlp =
                "Next time say 'withprescript <known string expression>'; I'll ignore the bad\n"
                "'with' clause and look for another.";
            break;
        case mp_with_post_script_code:
            hlp =
                "Next time say 'withpostscript <known string expression>'; I'll ignore the bad\n"
                "'with' clause and look for another.";
            break;
        case mp_with_stacking_code:
            hlp =
                "Next time say 'withstacking <known numeric expression>'; I'll ignore the bad\n"
                "'with' clause and look for another.";
            break;
        case mp_with_dashed_code:
            hlp =
                "Next time say 'dashed <known picture expression>'; I'll ignore the bad 'with'\n"
                "clause and look for another.";
            break;
        case mp_with_uninitialized_model_code:
            hlp =
                "Next time say 'withcolor <known color expression>'; I'll ignore the bad 'with'\n"
                "clause and look for another.";
            break;
        case mp_with_rgb_model_code:
            hlp =
                "Next time say 'withrgbcolor <known color expression>'; I'll ignore the bad 'with'\n"
                "clause and look for another.";
            break;
        case mp_with_cmyk_model_code:
            hlp =
                "Next time say 'withcmykcolor <known cmykcolor expression>'; I'll ignore the bad\n"
                "'with' clause and look for another.";
            break;
        case mp_with_grey_model_code:
            hlp =
                "Next time say 'withgreyscale <known numeric expression>'; I'll ignore the bad\n"
                " with' clause and look for another.";
            break;
        case mp_with_linecap_code:
            hlp =
                "Next time say 'withlinecap <known numeric expression>'; I'll ignore the bad\n"
                "'with' clause and look for another.";
            break;
        case mp_with_linejoin_code:
            hlp =
                "Next time say 'withlinejoin <known numeric expression>'; I'll ignore the bad\n"
                "'with' clause and look for another.";
            break;
        case mp_with_miterlimit_code:
            hlp =
                "Next time say 'miterlimit <known numeric expression>'; I'll ignore the bad\n"
                "'with' clause and look for another.";
            break;
        default:
            hlp =
                "Next time say 'withpen <known pen expression>'; I'll ignore the bad 'with' clause\n"
                "and look for another.";
            break;
    }
    mp_back_error(mp, "Improper type", hlp);
    mp_get_x_next(mp);
    mp_flush_cur_exp(mp, new_expr);
}

void mp_scan_with_list (MP mp, mp_node p, mp_node pstop)
{
    mp_node cp = MP_VOID;
    mp_node pp = MP_VOID;
    mp_node dp = MP_VOID;
    mp_node ap = MP_VOID;
    mp_node bp = MP_VOID;
    mp_node sp = MP_VOID;
    mp_node spstop = MP_VOID;
    mp_number ml;
    int miterlimit = 0;
    int linecap = -1;
    int linejoin = -1;
    while (cur_cmd == mp_with_option_command) {
        int t;
      CONTINUE:
        t = cur_mod;
        mp_get_x_next(mp);
        if (t != mp_with_no_model_code) {
            mp_scan_expression(mp);
        }
        switch (t) {
            case mp_with_uninitialized_model_code :
                switch (mp->cur_exp.type) {
                    case mp_cmykcolor_type:
                    case mp_color_type:
                    case mp_known_type:
                    case mp_boolean_type:
                        {
                            mp_value new_expr;
                            memset(&new_expr, 0, sizeof(mp_value));
                            new_number(new_expr.data.n);
                            if (cp == MP_VOID) {
                                make_cp_a_colored_object(cp, p);
                            }
                            if (cp != NULL) {
                                switch (mp->cur_exp.type) {
                                    case mp_color_type:
                                        {
                                            mp_node q = mp_get_value_node(cur_exp_node);
                                            mp_color_model(cp) = mp_rgb_model;
                                            set_color_val(mp_red_color(cp), mp_get_value_number(mp_red_part(q)));
                                            set_color_val(mp_green_color(cp), mp_get_value_number(mp_green_part(q)));
                                            set_color_val(mp_blue_color(cp), mp_get_value_number(mp_blue_part(q)));
                                            set_number_to_zero(mp_black_color(cp));
                                        }
                                        break;
                                    case mp_cmykcolor_type:
                                        {
                                            mp_node q = mp_get_value_node(cur_exp_node);
                                            mp_color_model(cp) = mp_cmyk_model;
                                            set_color_val(mp_cyan_color(cp), mp_get_value_number(mp_cyan_part(q)));
                                            set_color_val(mp_magenta_color(cp), mp_get_value_number(mp_magenta_part(q)));
                                            set_color_val(mp_yellow_color(cp), mp_get_value_number(mp_yellow_part(q)));
                                            set_color_val(mp_black_color(cp), mp_get_value_number(mp_black_part(q)));
                                        }
                                        break;
                                    case mp_known_type:
                                        {


                                            mp_color_model(cp) = mp_grey_model;
                                            set_number_to_zero(mp_cyan_color(cp));
                                            set_number_to_zero(mp_magenta_color(cp));
                                            set_number_to_zero(mp_yellow_color(cp));
                                            set_color_val(mp_grey_color(cp), cur_exp_value_number);

                                        }
                                        break;
                                    default:
                                        switch (cur_exp_value_boolean) {
                                            case mp_false_operation:
                                                mp_color_model(cp) = mp_no_model;
                                                break;
                                            case mp_true_operation:
                                                mp_color_model(cp) = mp_uninitialized_model;
                                                break;
                                            default:
                                                break;
                                        }
                                        set_number_to_zero(mp_cyan_color(cp));
                                        set_number_to_zero(mp_magenta_color(cp));
                                        set_number_to_zero(mp_yellow_color(cp));
                                        set_number_to_zero(mp_black_color(cp));
                                        break;
                                }
                            }
                            mp_flush_cur_exp(mp, new_expr);
                        }
                        break;
                    default:
                        complain_invalid_with_list(mp, t);
                        goto CONTINUE;
                }
                break;
            case mp_with_rgb_model_code:
                if (mp->cur_exp.type != mp_color_type) {
                    complain_invalid_with_list(mp, t);
                    goto CONTINUE;
                } else {
                    mp_value new_expr;
                    memset(&new_expr, 0, sizeof(mp_value));
                    new_number(new_expr.data.n);
                    if (cp == MP_VOID) {
                        make_cp_a_colored_object(cp, p);
                    }
                    if (cp != NULL) {
                        mp_node q = mp_get_value_node(cur_exp_node);
                        mp_color_model(cp) = mp_rgb_model;
                        set_color_val(mp_red_color(cp), mp_get_value_number(mp_red_part(q)));
                        set_color_val(mp_green_color(cp), mp_get_value_number(mp_green_part(q)));
                        set_color_val(mp_blue_color(cp), mp_get_value_number(mp_blue_part(q)));
                        set_number_to_zero(mp_black_color(cp));
                    }
                    mp_flush_cur_exp(mp, new_expr);
                }
                break;
            case mp_with_cmyk_model_code:
                if (mp->cur_exp.type != mp_cmykcolor_type) {
                    complain_invalid_with_list(mp, t);
                    goto CONTINUE;
                } else {
                    mp_value new_expr;
                    memset(&new_expr, 0, sizeof(mp_value));
                    new_number(new_expr.data.n);
                    if (cp == MP_VOID) {
                        make_cp_a_colored_object(cp, p);
                    }
                    if (cp != NULL) {
                        mp_node q = mp_get_value_node(cur_exp_node);
                        mp_color_model(cp) = mp_cmyk_model;
                        set_color_val(mp_cyan_color(cp), mp_get_value_number(mp_cyan_part(q)));
                        set_color_val(mp_magenta_color(cp), mp_get_value_number(mp_magenta_part(q)));
                        set_color_val(mp_yellow_color(cp), mp_get_value_number(mp_yellow_part(q)));
                        set_color_val(mp_black_color(cp), mp_get_value_number(mp_black_part(q)));
                    }
                    mp_flush_cur_exp(mp, new_expr);
                }
                break;
            case mp_with_grey_model_code:
                if (mp->cur_exp.type != mp_known_type) {
                    complain_invalid_with_list(mp, t);
                    goto CONTINUE;
                } else {
                    mp_value new_expr;
                    memset(&new_expr, 0, sizeof(mp_value));
                    new_number(new_expr.data.n);
                    if (cp == MP_VOID) {
                        make_cp_a_colored_object(cp, p);
                    }
                    if (cp != NULL) {


                        mp_color_model(cp) = mp_grey_model;
                        set_number_to_zero(mp_cyan_color(cp));
                        set_number_to_zero(mp_magenta_color(cp));
                        set_number_to_zero(mp_yellow_color(cp));
                        set_color_val(mp_grey_color(cp), cur_exp_value_number);

                    }
                    mp_flush_cur_exp(mp, new_expr);
                }
                break;
            case mp_with_no_model_code:
                if (cp == MP_VOID) {
                    make_cp_a_colored_object(cp, p);
                }
                if (cp != NULL) {
                    mp_color_model(cp) = mp_no_model;
                    set_number_to_zero(mp_cyan_color(cp));
                    set_number_to_zero(mp_magenta_color(cp));
                    set_number_to_zero(mp_yellow_color(cp));
                    set_number_to_zero(mp_grey_color(cp));
                }
                break;
            case mp_with_pen_code:
                if (mp->cur_exp.type != mp_pen_type && mp->cur_exp.type != mp_nep_type) {
                    complain_invalid_with_list(mp, t);
                    goto CONTINUE;
                } else {
                    if (pp == MP_VOID) {
                        pp = p;
                        while (pp != NULL) {
                            if (mp_has_pen(pp)) {
                                break;
                            } else {
                                pp = pp->link;
                            }
                        }
                    }
                    if (pp != NULL) {
                        switch (pp->type) {
                            case mp_fill_node_type:
                            case mp_stroked_node_type:
                                if (mp_pen_ptr((mp_shape_node) pp) != NULL) {
                                    mp_toss_knot_list(mp, mp_pen_ptr((mp_shape_node) pp));
                                }
                                mp_pen_ptr((mp_shape_node) pp) = cur_exp_knot;
                                mp_pen_type((mp_shape_node) pp) = mp->cur_exp.type == mp_nep_type;
                                break;
                            default:
                                break;
                        }
                        mp->cur_exp.type = mp_vacuous_type;
                    }
                }
                break;
            case mp_with_pre_script_code:
                if (mp->cur_exp.type != mp_string_type) {
                    complain_invalid_with_list(mp, t);
                    goto CONTINUE;
                } else if (cur_exp_str->len) {
                    if (ap == MP_VOID) {
                        ap = p;
                    }
                    while ((ap != NULL) && (! mp_has_script(ap))) {
                        ap = ap->link;
                    }
                    if (ap != NULL) {
                        if (mp_pre_script(ap) != NULL) {
                            int selector = mp->selector;
                            mp_string s = mp_pre_script(ap);
                            mp->selector = mp_new_string_selector;
                            mp_str_room(mp, (int) (mp_pre_script(ap)->len + cur_exp_str->len + 2));
                            mp_print_mp_str(mp, cur_exp_str);
                            mp_str_room(mp, 1);
                            mp_append_char(mp, 13);
                            mp_print_mp_str(mp, mp_pre_script(ap));
                            mp_pre_script(ap) = mp_make_string(mp);
                            delete_str_ref(s);
                            mp->selector = selector;
                        } else {
                            mp_pre_script(ap) = cur_exp_str;
                        }
                        add_str_ref(mp_pre_script(ap));
                        mp->cur_exp.type = mp_vacuous_type;
                    }
                }
                break;
            case mp_with_post_script_code:
                if (mp->cur_exp.type != mp_string_type) {
                    complain_invalid_with_list(mp, t);
                    goto CONTINUE;
                } else if (cur_exp_str->len) {
                    if (bp == MP_VOID) {
                        bp = p;
                    }
                    while ((bp != NULL) && (! mp_has_script(bp))) {
                        bp = bp->link;
                    }
                    if (bp != NULL) {
                        if (mp_post_script(bp) != NULL) {
                            int selector = mp->selector;
                            mp_string s = mp_post_script(bp);
                            mp->selector = mp_new_string_selector;
                            mp_str_room(mp, (int) (mp_post_script(bp)->len + cur_exp_str->len + 2));
                            mp_print_mp_str(mp, mp_post_script(bp));
                            mp_str_room(mp, 1);
                            mp_append_char(mp, 13);
                            mp_print_mp_str(mp, cur_exp_str);
                            mp_post_script(bp) = mp_make_string(mp);
                            delete_str_ref(s);
                            mp->selector = selector;
                        } else {
                            mp_post_script(bp) = cur_exp_str;
                        }
                        add_str_ref(mp_post_script(bp));
                        mp->cur_exp.type = mp_vacuous_type;
                    }
                }
                break;
            case mp_with_stacking_code:
                switch (mp->cur_exp.type) {
                    case mp_known_type:
                        {
                            if (sp == MP_VOID) {
                                sp = p;
                            }
                            if (pp && spstop == MP_VOID) {
                                spstop = pstop;
                            }
                            if (sp != NULL) {
                                mp_stacking(sp) = round_unscaled(cur_exp_value_number);
                            }
                            if (pp && spstop != NULL) {
                                mp_stacking(spstop) = round_unscaled(cur_exp_value_number);
                            }
                            mp->cur_exp.type = mp_vacuous_type;
                        }
                        break;
                    case mp_pair_type:
                        {
                            if (pp && mp_nice_pair(mp, cur_exp_node, mp->cur_exp.type)) {
                                if (sp == MP_VOID) {
                                    sp = p;
                                }
                                if (spstop == MP_VOID) {
                                    spstop = pstop;
                                }
                                if (sp != NULL) {
                                    mp_stacking(sp) = round_unscaled(mp_get_value_number(mp_x_part(mp_get_value_node(cur_exp_node))));
                                }
                                if (spstop != NULL) {
                                    mp_stacking(spstop) = round_unscaled(mp_get_value_number(mp_y_part(mp_get_value_node(cur_exp_node))));
                                }
                                mp->cur_exp.type = mp_vacuous_type;
                            } else {
                                complain_invalid_with_list(mp, t);
                                goto CONTINUE;
                            }
                        }
                        break;
                    default:
                        {
                            complain_invalid_with_list(mp, t);
                            goto CONTINUE;
                        }
                }
                break;
            case mp_with_linecap_code:
                switch (mp->cur_exp.type) {
                    case mp_known_type:
                        {
                            linecap = round_unscaled(cur_exp_value_number);
                            mp->cur_exp.type = mp_vacuous_type;
                            break;
                        }
                    default:
                        {
                            complain_invalid_with_list(mp, t);
                            goto CONTINUE;
                        }
                }
                break;
            case mp_with_linejoin_code:
                switch (mp->cur_exp.type) {
                    case mp_known_type:
                        {
                            linejoin = round_unscaled(cur_exp_value_number);
                            mp->cur_exp.type = mp_vacuous_type;
                            break;
                        }
                    default:
                        {
                            complain_invalid_with_list(mp, t);
                            goto CONTINUE;
                        }
                }
                break;
            case mp_with_miterlimit_code:
                switch (mp->cur_exp.type) {
                    case mp_known_type:
                        {
                            miterlimit = 1;
                            new_number_clone(ml, cur_exp_value_number);
                            mp->cur_exp.type = mp_vacuous_type;
                            break;
                        }
                    default:
                        {
                            complain_invalid_with_list(mp, t);
                            goto CONTINUE;
                        }
                }
                break;
            case mp_with_dashed_code:
                if (mp->cur_exp.type != mp_picture_type) {
                    complain_invalid_with_list(mp, t);
                    goto CONTINUE;
                }
                // fall through
            default:
                if (dp == MP_VOID) {
                    dp = p;
                    while (dp != NULL) {
                        if (dp->type == mp_stroked_node_type) {
                            break;
                        } else {
                            dp = dp->link;
                        }
                    }
                }
                if (dp != NULL) {
                    if (mp_dash_ptr(dp) != NULL) {
                        mp_delete_edge_ref(mp, mp_dash_ptr(dp));
                    }
                    mp_dash_ptr(dp) = (mp_node) mp_make_dashes(mp, (mp_edge_header_node) cur_exp_node);
                    set_number_to_unity(((mp_shape_node) dp)->dashscale);
                    mp->cur_exp.type = mp_vacuous_type;
                }
                break;
        }
    }
    if (cp > MP_VOID) {
        mp_node q = cp->link;
        while (q != NULL) {
            if (mp_has_color(q)) {
                mp_shape_node q0 = (mp_shape_node) q;
                mp_shape_node cp0 = (mp_shape_node) cp;
                number_clone(q0->red, cp0->red);
                number_clone(q0->green, cp0->green);
                number_clone(q0->blue, cp0->blue);
                number_clone(q0->black, cp0->black);
                mp_color_model(q) = mp_color_model(cp);
            }
            q = q->link;
        }
    }
    if (pp > MP_VOID) {
        mp_node q = pp->link;
        while (q != NULL) {
            if (mp_has_pen(q)) {
                switch (q->type) {
                    case mp_fill_node_type:
                    case mp_stroked_node_type:
                        if (mp_pen_ptr((mp_shape_node) q) != NULL) {
                            mp_toss_knot_list(mp, mp_pen_ptr((mp_shape_node) q));
                        }
                        mp_pen_ptr((mp_shape_node) q) = mp_copy_pen(mp, mp_pen_ptr((mp_shape_node) pp));
                        break;
                    default:
                        break;
                }
            }
            q = q->link;
        }
    }
    if (dp > MP_VOID) {
        mp_node q = dp->link;
        while (q != NULL) {
            if (q->type == mp_stroked_node_type) {
                if (mp_dash_ptr(q) != NULL) {
                    mp_delete_edge_ref(mp, mp_dash_ptr(q));
                }
                mp_dash_ptr(q) = mp_dash_ptr(dp);
                set_number_to_unity(((mp_shape_node) q)->dashscale);
                if (mp_dash_ptr(q) != NULL) {
                    mp_add_edge_ref(mp, mp_dash_ptr(q));
                }
            }
            q = q->link;
        }
    }
    if (linecap >= 0 && linecap < mp_weird_linecap_code) {
        mp_node q = p;
        while (q != NULL) {
            switch (q->type) {
                case mp_fill_node_type:
                case mp_stroked_node_type:
                    mp_set_linecap(q, linecap);
                    break;
                default:
                    break;
            }
            q = q->link;
        }
    }
    if (linejoin >= 0 && linejoin < mp_weird_linejoin_code) {
        mp_node q = p;
        while (q != NULL) {
            switch (q->type) {
                case mp_fill_node_type:
                case mp_stroked_node_type:
                    mp_set_linejoin(q, linejoin);
                    break;
                default:
                    break;
            }
            q = q->link;
        }
    }
    if (miterlimit) {
        mp_node q = p;
        while (q != NULL) {
            switch (q->type) {
                case mp_fill_node_type:
                case mp_stroked_node_type:
                    number_clone(mp_miterlimit(q), ml);
                    break;
                default:
                    break;
            }
            q = q->link;
        }
        free_number(ml);
    }
    if (! pp && sp > MP_VOID) {
        mp_node q = sp->link;
        while (q != NULL) {
            mp_stacking(q) = mp_stacking(sp);
            q = q->link;
        }
    }
}

mp_edge_header_node mp_find_edges_var (MP mp, mp_node t)
{
    mp_edge_header_node cur_edges = NULL;
    mp_node p = mp_find_variable(mp, t);
    if (p == NULL) {
        char *msg = mp_obliterated(mp, t);
        mp_back_error(
            mp,
            msg,
            "It seems you did a nasty thing --- probably by accident, but nevertheless you\n"
            "nearly hornswoggled me ... While I was evaluating the right-hand side of thisn"
            "command, something happened, and the left-hand side is no longer a variable! So In"
            "won't change anything."
        );
        mp_memory_free(msg);
        mp_get_x_next(mp);
    } else if (p->type != mp_picture_type) {
        char msg[256];
        mp_string sname;
        int selector = mp->selector;
        mp->selector = mp_new_string_selector;
        mp_show_token_list(mp, t, NULL);
        sname = mp_make_string(mp);
        mp->selector = selector;
        mp_snprintf(msg, 256, "Variable %s is the wrong type(%s)", mp_str(mp, sname), mp_type_string(p->type));
        delete_str_ref(sname);
        mp_back_error(
            mp,
            msg,
            "I was looking for a 'known' picture variable. So I'll not change anything just\n"
            "now."
        );
        mp_get_x_next(mp);
    } else {
        mp_set_value_node(p, (mp_node) mp_private_edges(mp, (mp_edge_header_node) mp_get_value_node(p)));
        cur_edges = (mp_edge_header_node) mp_get_value_node(p);
    }
    mp_flush_node_list(mp, t);
    return cur_edges;
}

mp_node mp_start_draw_cmd (MP mp, int sep)
{
    mp_node lhv = NULL;
    int add_type = 0;
    mp_get_x_next(mp);
    mp->var_flag = sep;
    mp_scan_primary(mp);
    if (mp->cur_exp.type != mp_token_list_type) {
        mp_value new_expr;
        memset(&new_expr, 0, sizeof(mp_value));
        new_number(new_expr.data.n);
        mp_disp_err(mp, NULL);
        mp_back_error(
            mp,
            "Not a suitable variable",
            "At this point I needed to see the name of a picture variable. (Or perhaps you\n"
            "have indeed presented me with one; I might have missed it, if it wasn't followed\n"
            "by the proper token.) So I'll not change anything just now.\n"
        );
        mp_get_x_next(mp);
        mp_flush_cur_exp(mp, new_expr);
    } else {
        lhv = cur_exp_node;
        add_type = (int) cur_mod;
        mp->cur_exp.type = mp_vacuous_type;
        mp_get_x_next(mp);
        mp_scan_expression(mp);
    }
    mp->last_add_type = add_type;
    return lhv;
}

void mp_do_bounds (MP mp)
{
    mp_edge_header_node lhe;
    int c = cur_cmd;
    int m = cur_mod;
    mp_node lhv = mp_start_draw_cmd(mp, mp_to_command);
    if (lhv != NULL) {
        mp_value new_expr;
        memset(&new_expr, 0, sizeof(mp_value));
        lhe = mp_find_edges_var(mp, lhv);
        if (lhe == NULL) {
            new_number(new_expr.data.n);
            mp_flush_cur_exp(mp, new_expr);
        } else if (mp->cur_exp.type != mp_path_type) {
            char msg[256];
            mp_disp_err(mp, NULL);
            new_number(new_expr.data.n);
            mp_snprintf(msg, 256, "Improper '%s'", mp_cmd_mod_string(mp, c, m));
            mp_back_error(
                mp,
                msg,
                "This expression should have specified a known path. So I'll not change anything\n"
                "just now."
            );
            mp_get_x_next(mp);
            mp_flush_cur_exp(mp, new_expr);
        } else if (mp_left_type(cur_exp_knot) == mp_endpoint_knot) {
            mp_back_error(
                mp,
                "Not a cycle",
                "That contour should have ended with '..cycle' or '&cycle'. So I'll not change\n"
                "anything just now."
            );
            mp_get_x_next(mp);
        } else {
            mp_node p = mp_new_bounds_node(mp, cur_exp_knot, (int) m);
            mp_node pp;
            int mm = 0;
            switch (m) {
                case mp_start_clip_node_type  : mm = mp_stop_clip_node_type; break;
                case mp_start_group_node_type : mm = mp_stop_group_node_type; break;
                case mp_start_bounds_node_type: mm = mp_stop_bounds_node_type; break;
            }
            pp = mp_new_bounds_node(mp, NULL, mm);
            mp_scan_with_list(mp, p, pp);
            p->link = mp_edge_list(lhe)->link;
            mp_edge_list(lhe)->link = p;
            if (mp_obj_tail(lhe) == mp_edge_list(lhe)) {
                mp_obj_tail(lhe) = p;
            }
            mp_obj_tail(lhe)->link = pp;
            mp_obj_tail(lhe) = pp;
            mp_init_bbox(mp, lhe);
        }
    }
}

void mp_do_add_to (MP mp)
{
    mp_node lhv = mp_start_draw_cmd(mp, mp_thing_to_add_command);
    if (lhv != NULL) {
        mp_edge_header_node lhe;
        mp_node p;
        mp_edge_header_node e;
        int add_type = mp->last_add_type;
        if (add_type == mp_add_also_code) {
            p = NULL;
            e = NULL;
            if (mp->cur_exp.type != mp_picture_type) {
                mp_value new_expr;
                memset(&new_expr, 0, sizeof(mp_value));
                new_number(new_expr.data.n);
                mp_disp_err(mp, NULL);
                mp_back_error(
                    mp,
                    "Improper 'addto'",
                    "This expression should have specified a known picture. So I'll not change\n"
                    "anything just now."
                );
                mp_get_x_next(mp);
                mp_flush_cur_exp(mp, new_expr);
            } else {
                e = mp_private_edges(mp, (mp_edge_header_node) cur_exp_node);
                mp->cur_exp.type = mp_vacuous_type;
                p = mp_edge_list(e)->link;
            }
        } else {
            e = NULL;
            p = NULL;
            if (mp->cur_exp.type == mp_pair_type) {
                mp_pair_to_path(mp);
            }
            if (mp->cur_exp.type != mp_path_type) {
                mp_value new_expr;
                memset(&new_expr, 0, sizeof(mp_value));
                new_number(new_expr.data.n);
                mp_disp_err(mp, NULL);
                mp_back_error(
                    mp,
                    "Improper 'addto'",
                    "This expression should have specified a known path. So I'll not change anything\n"
                    "just now."
                );
                mp_get_x_next(mp);
                mp_flush_cur_exp(mp, new_expr);
            } else if (add_type != mp_add_contour_code) {
                p = mp_new_shape_node(mp, cur_exp_knot, mp_stroked_node_type);
                mp->cur_exp.type = mp_vacuous_type;
            } else if (mp_left_type(cur_exp_knot) == mp_endpoint_knot) {
                mp_back_error(
                    mp,
                    "Not a cycle",
                    "That contour should have ended with '.. cycle' or '& cycle'. So I'll not change\n"
                    "anything just now."
                );
                mp_get_x_next(mp);
            } else {
                p = mp_new_shape_node(mp, cur_exp_knot, mp_fill_node_type);
                mp->cur_exp.type = mp_vacuous_type;
            }
        }
        mp_scan_with_list(mp, p, NULL);
        lhe = mp_find_edges_var(mp, lhv);
        if (lhe == NULL) {
            if ((e == NULL) && (p != NULL)) {
                e = mp_toss_gr_object(mp, p);
            }
            if (e != NULL) {
                mp_delete_edge_ref(mp, e);
            }
        } else if (add_type == mp_add_also_code) {
            if (e != NULL) {
                if (mp_edge_list(e)->link != NULL) {
                    mp_obj_tail(lhe)->link = mp_edge_list(e)->link;
                    mp_obj_tail(lhe) = mp_obj_tail(e);
                    mp_obj_tail(e) = mp_edge_list(e);
                    mp_edge_list(e)->link = NULL;
                    mp_flush_dash_list(mp, lhe);
                }
                mp_toss_edges(mp, e);
            }
        } else if (p != NULL) {
            mp_obj_tail(lhe)->link = p;
            mp_obj_tail(lhe) = p;
            if (add_type == mp_add_double_path_code) {
                if (mp_pen_ptr((mp_shape_node) p) == NULL) {
                    mp_pen_ptr((mp_shape_node) p) = mp_get_pen_circle(mp, &zero_t);
                }
            }
        }
    }
}

void mp_do_ship_out (MP mp)
{
    mp_value new_expr;
    memset(&new_expr, 0, sizeof(mp_value));
    new_number(new_expr.data.n);
    mp_get_x_next(mp);
    mp_scan_expression(mp);
    if (mp->cur_exp.type != mp_picture_type) {
        mp_disp_err(mp, NULL);
        set_number_to_zero(new_expr.data.n);
        mp_back_error(mp, "Not a known picture", "I can only output known pictures.");
        mp_get_x_next(mp);
        mp_flush_cur_exp(mp, new_expr);
    } else {
        mp_ship_out(mp, cur_exp_node);
        set_number_to_zero(new_expr.data.n);
        mp_flush_cur_exp(mp, new_expr);
    }
}

void mp_do_message (MP mp)
{
    mp_value new_expr;
    int m = cur_mod;
    memset(&new_expr, 0, sizeof(mp_value));
    new_number(new_expr.data.n);
    mp_get_x_next(mp);
    mp_scan_expression(mp);
    if (mp->cur_exp.type != mp_string_type) {
        mp_no_string_err(mp, "A message should be a known string expression.");
    } else {
        switch (m) {
            case message_code:
                mp_print_nl(mp, "");
                mp_print_mp_str(mp, cur_exp_str);
                break;
            case err_message_code:
                {
                    char msg[256];
                    mp_snprintf(msg, 256, "%s", mp_str(mp, cur_exp_str));
                    if (mp->err_help != NULL) {
                        mp->use_err_help = 1;
                        mp_back_error(mp, msg, NULL);
                    } else if (mp->long_help_seen) {
                        mp_back_error(mp, msg, "(That was another 'errmessage'.)");
                    } else {
                        if (mp->interaction < mp_error_stop_mode) {
                            mp->long_help_seen = 1;
                        }
                        mp_back_error(
                            mp,
                            msg,
                            "This error message was generated by an 'errmessage' command, so I can't give any\n"
                            "explicit help. Pretend that you're Miss Marple: Examine all clues, and deduce the\n"
                            "truth by inspired guesses."
                        );
                    }
                    mp_get_x_next(mp);
                    mp->use_err_help = 0;
                }
                break;
            case err_help_code:
                {
                    if (mp->err_help != NULL) {
                        delete_str_ref(mp->err_help);
                    } if (cur_exp_str->len == 0) {
                        mp->err_help = NULL;
                    } else {
                        mp->err_help = cur_exp_str;
                        add_str_ref(mp->err_help);
                    }
                }
                break;
        }
    }
    set_number_to_zero(new_expr.data.n);
    mp_flush_cur_exp(mp, new_expr);
}

static void mp_no_string_err (MP mp, const char *s)
{
    mp_disp_err(mp, NULL);
    mp_back_error(mp, "Not a string", s);
    mp_get_x_next(mp);
}

void mp_do_write (MP mp)
{
    mp_value new_expr;
    memset(&new_expr, 0, sizeof(mp_value));
    new_number(new_expr.data.n);
    mp_get_x_next(mp);
    mp_scan_expression(mp);
    if (mp->cur_exp.type != mp_string_type) {
        mp_no_string_err(mp, "The text to be written should be a known string expression");
    } else if (cur_cmd != mp_to_command) {
        mp_back_error(mp, "Missing 'to' clause", "A write command should end with 'to <filename>'");
        mp_get_x_next(mp);
    } else {
        mp_string t = cur_exp_str;
        mp->cur_exp.type = mp_vacuous_type;
        mp_get_x_next(mp);
        mp_scan_expression(mp);
        if (mp->cur_exp.type != mp_string_type) {
            mp_no_string_err(mp, "I can\'t write to that file name. It isn't a known string");
        } else {
            mp_do_write_string(mp, t);
        }
    }
    set_number_to_zero(new_expr.data.n);
    mp_flush_cur_exp(mp, new_expr);
}

static void mp_do_write_string (MP mp, mp_string t)
{
    char *fn = mp_str(mp, cur_exp_str);
    int n = mp->write_files;
    int n0 = mp->write_files;
    while (mp_strcmp(fn, mp->wr_fname[n]) != 0) {
        if (n == 0) {
            if (n0 == mp->write_files) {
                if (mp->write_files < mp->max_write_files) {
                    ++mp->write_files;
                } else {
                    int l = mp->max_write_files + (mp->max_write_files / 4);
                    void **wr_file = mp_memory_allocate((size_t) (l + 1) * sizeof(void *));
                    char **wr_fname = mp_memory_allocate((size_t) (l + 1) * sizeof(char *));
                    for (int k = 0; k <= l; k++) {
                        if (k <= mp->max_write_files) {
                            wr_file[k] = mp->wr_file[k];
                            wr_fname[k] = mp->wr_fname[k];
                        } else {
                            wr_file[k] = 0;
                            wr_fname[k] = NULL;
                        }
                    }
                    mp_memory_free(mp->wr_file);
                    mp_memory_free(mp->wr_fname);
                    mp->max_write_files = l;
                    mp->wr_file = wr_file;
                    mp->wr_fname = wr_fname;
                }
            }
            n = n0;
            mp_open_write_file(mp, fn, n);
        } else {
            --n;
            if (mp->wr_fname[n] == NULL) {
                n0 = n;
            }
        }
    }
    if (mp_str_vs_str(mp, t, mp->eof_line) == 0) {
        (mp->close_file)(mp, mp->wr_file[n]);
        mp_memory_free(mp->wr_fname[n]);
        mp->wr_fname[n] = NULL;
        if (n == mp->write_files - 1) {
            mp->write_files = n;
        }
    } else {
        int selector = mp->selector;
        mp->selector = n + mp_first_file_selector;
        mp_print_mp_str(mp, t);
        mp_print_ln(mp);
        mp->selector = selector;
    }
}

struct mp_edge_object *mp_gr_export (MP mp, mp_edge_header_node h)
{
    mp_node p;
    mp_edge_object *hh = mp_memory_allocate(sizeof(mp_edge_object));
    mp_graphic_object *hp = NULL;
    mp_set_bbox(mp, h, 1);
    hh->parent = mp;
    hh->body = NULL;
    hh->next = NULL;
    hh->minx = number_to_double(h->minx);
    hh->minx = fabs(hh->minx) < 0.00001 ? 0 : hh->minx;
    hh->miny = number_to_double(h->miny);
    hh->miny = fabs(hh->miny) < 0.00001 ? 0 : hh->miny;
    hh->maxx = number_to_double(h->maxx);
    hh->maxx = fabs(hh->maxx) < 0.00001 ? 0 : hh->maxx;
    hh->maxy = number_to_double(h->maxy);
    hh->maxy = fabs(hh->maxy) < 0.00001 ? 0 : hh->maxy;
    hh->charcode = round_unscaled(internal_value(mp_char_code_internal));
    hh->width = number_to_double(internal_value(mp_char_wd_internal));
    hh->height = number_to_double(internal_value(mp_char_ht_internal));
    hh->depth = number_to_double(internal_value(mp_char_dp_internal));
    hh->italic = number_to_double(internal_value(mp_char_ic_internal));
    p = mp_edge_list(h)->link;
    while (p != NULL) {
        mp_graphic_object *hq = mp_new_graphic_object(mp, (int) ((p->type - mp_fill_node_type) + 1));
        switch (p->type) {
            case mp_fill_node_type:
                {
                    mp_number d_width;
                    mp_shape_node p0 = (mp_shape_node) p;
                    mp_shape_object *tf = (mp_shape_object *) hq;
                    gr_pen_ptr(tf) = mp_export_knot_list(mp, mp_pen_ptr(p0));
                    new_number(d_width);
                    mp_get_pen_scale(mp, &d_width, mp_pen_ptr(p0));
                    free_number(d_width);
                    if ((mp_pen_ptr(p0) == NULL) || mp_pen_is_elliptical(mp_pen_ptr(p0))) {
                        gr_path_ptr(tf) = mp_export_knot_list(mp, mp_path_ptr(p0));
                    } else {
                        mp_knot pc = mp_copy_path(mp, mp_path_ptr(p0));
                        mp_knot pp = mp_make_envelope(mp, pc, mp_pen_ptr(p0), p0->linejoin, 0, &(p0->miterlimit));
                        gr_path_ptr(tf) = mp_export_knot_list(mp, pp);
                        mp_toss_knot_list(mp, pp);
                        pc = mp_htap_ypoc(mp, mp_path_ptr(p0));
                        pp = mp_make_envelope(mp, pc, mp_pen_ptr((mp_shape_node) p), p0->linejoin, 0, &(p0->miterlimit));
                        gr_htap_ptr(tf) = mp_export_knot_list(mp, pp);
                        mp_toss_knot_list(mp, pp);
                    }
                    mp_gr_export_color(tf, p0);
                    mp_gr_export_scripts(tf, p);
                    gr_linejoin_val(tf) = p0->linejoin;
                    gr_stacking_val(tf) = p0->stacking;
                    gr_miterlimit_val(tf) = number_to_double(p0->miterlimit);
                }
                break;
            case mp_stroked_node_type:
                {
                    mp_number d_width;
                    mp_shape_node p0 = (mp_shape_node) p;
                    mp_shape_object *ts = (mp_shape_object *) hq;
                    gr_pen_ptr(ts) = mp_export_knot_list(mp, mp_pen_ptr(p0));
                    new_number(d_width);
                    mp_get_pen_scale(mp, &d_width, mp_pen_ptr(p0));
                    if (mp_pen_is_elliptical(mp_pen_ptr(p0))) {
                        gr_path_ptr(ts) = mp_export_knot_list(mp, mp_path_ptr(p0));
                    } else {
                        mp_knot pc = mp_copy_path(mp, mp_path_ptr(p0));
                        int t = p0->linecap;
                        if (mp_left_type(pc) != mp_endpoint_knot) {
                            mp_left_type(mp_insert_knot(mp, pc, &(pc->x_coord), &(pc->y_coord))) =  mp_endpoint_knot;
                            mp_right_type(pc) = mp_endpoint_knot;
                            pc = mp_next_knot(pc);
                            t = 1;
                        }
                        pc = mp_make_envelope(mp, pc, mp_pen_ptr(p0), p0->linejoin, (int) t, &(p0->miterlimit));
                        gr_path_ptr(ts) = mp_export_knot_list(mp, pc);
                        mp_toss_knot_list(mp, pc);
                    }
                    mp_gr_export_color(ts, p0);
                    mp_gr_export_scripts(ts, p);
                    gr_linejoin_val(ts) = p0->linejoin;
                    gr_miterlimit_val(ts) = number_to_double(p0->miterlimit);
                    gr_linecap_val(ts) = p0->linecap;
                    gr_stacking_val(ts) = p0->stacking;
                    gr_dash_ptr(ts) = mp_export_dashes(mp, p0, &d_width);
                    free_number(d_width);
                }
                break;
            case mp_start_clip_node_type:
            case mp_start_group_node_type:
            case mp_start_bounds_node_type:
                {
                    mp_start_node p0 = (mp_start_node) p;
                    mp_start_object *tb = (mp_start_object *) hq;
                    gr_path_ptr(tb) = mp_export_knot_list(mp, mp_path_ptr((mp_start_node) p));
                    gr_stacking_val(tb) = p0->stacking;
                    mp_gr_export_scripts(tb, p);
                }
                break;
            case mp_stop_clip_node_type:
            case mp_stop_group_node_type:
            case mp_stop_bounds_node_type:
                {
                    mp_stop_node p0 = (mp_stop_node) p;
                    mp_stop_object *tb = (mp_stop_object *) hq;
                    gr_stacking_val(tb) = p0->stacking;
                }
                break;
            default:
                break;
        }
        if (hh->body == NULL) {
            hh->body = hq;
        } else {
            gr_link(hp) = hq;
        }
        hp = hq;
        p = p->link;
    }
    return hh;
}

static void mp_do_gr_toss_dashes (mp_dash_object *dl) {
    if (dl) {
        mp_memory_free(dl->array);
        mp_memory_free(dl);
    }
}

static void mp_do_gr_toss_knot_list (mp_gr_knot p)
{
    if (p) {
        mp_gr_knot q = p;
        do {
            mp_gr_knot r = gr_next_knot(q);
            mp_memory_free(q);
            q = r;
        } while (q != p);
    }
}

mp_graphic_object *mp_new_graphic_object (MP mp, int type)
{
    mp_graphic_object *p;
    size_t size;
    (void) mp;
    switch (type) {
        case mp_fill_code:
        case mp_stroked_code:
            size = sizeof(mp_shape_object);
            break;
        case mp_start_clip_code:
        case mp_start_group_code:
        case mp_start_bounds_code:
            size = sizeof(mp_start_object);
            break;
        default:
            size = sizeof(mp_graphic_object);
            break;
    }
    p = (mp_graphic_object *) mp_memory_allocate(size);
    memset(p, 0, size);
    gr_type(p) = type;
    return p;
}

void mp_gr_toss_object (mp_graphic_object *p)
{
    switch (gr_type(p)) {
        case mp_fill_code:
        case mp_stroked_code:
            {
                mp_shape_object *o = (mp_shape_object *) p;
                mp_memory_free(gr_pre_script(o));
                mp_memory_free(gr_post_script(o));
                mp_do_gr_toss_knot_list(gr_pen_ptr(o));
                mp_do_gr_toss_knot_list(gr_path_ptr(o));
                if (gr_htap_ptr(o)) {
                    mp_do_gr_toss_knot_list(gr_htap_ptr(o));
                }
                if (gr_dash_ptr(o)) {
                    mp_do_gr_toss_dashes(gr_dash_ptr(o));
                }
            }
            break;
        case mp_start_clip_code:
        case mp_start_group_code:
        case mp_start_bounds_code:
            {
                mp_start_object *o = (mp_start_object *) p;
                mp_memory_free(gr_pre_script(o));
                mp_memory_free(gr_post_script(o));
                mp_do_gr_toss_knot_list(gr_path_ptr(o));
            }
            break;
        case mp_stop_clip_code:
        case mp_stop_group_code:
        case mp_stop_bounds_code:
            break;
    }
    mp_memory_free(p);
}

void mp_gr_toss_objects (mp_edge_object *hh)
{
    mp_graphic_object *p = hh->body;
    while (p) {
        mp_graphic_object *q = gr_link(p);
        mp_gr_toss_object(p);
        p = q;
    }
    mp_memory_free(hh);
}

void mp_ship_out (MP mp, mp_node h) {
    (mp->shipout_backend)(mp, h);
}

static void mp_shipout_backend (MP mp, void *voidh)
{
    (void) mp;
    (void) voidh;
}

void mp_scan_symbol_value (MP mp, int keep, char **s, int expand)
{
    if (mp->extensions) {
        if (expand) {
            mp_get_x_next(mp);
        } else {
            mp_get_next(mp);
        }
        if (keep) {
            mp_back_input(mp);
        }
        if (cur_sym == NULL && (cur_sym_mod == 0 || cur_sym_mod == mp_normal_operation)) {
            *s = NULL;
        } else {
            unsigned char *r = NULL;
            mp_node p = mp_new_symbolic_node(mp);
            mp_set_sym_sym(p, cur_sym);
            p->name_type = cur_sym_mod;
            if (p->type == mp_symbol_node_type) {
                mp_sym sr = mp_get_sym_sym(p);
                mp_string rr = text(sr);
                if (rr && rr->str) {
                    r = rr->str;
                }
            } else if (p->name_type == mp_token_operation) {
                if (p->type == mp_string_type) {
                    r = mp_get_value_str(p)->str;
                }
            }
            mp_free_symbolic_node(mp, p);
            if (r) {
                *s = (char *) mp_strdup((char *) r);
            } else {
                *s = NULL;
            }
        }
    }
}

void mp_scan_property_value (MP mp, int keep, int *kind, char **str, int *property, int *detail)
{
    if (mp->extensions) {
        mp_symbol_entry *entry;
        mp_get_symbol(mp);
        entry = cur_sym;
        if (entry) {
            mp_node node = entry->type == mp_tag_command ? entry->v.data.node : NULL;
            *kind = entry->type;
            *str = (char *) mp_strdup((char *) entry->text->str);
            *property = entry->property;
            if (node) {
                *detail = node->type;
            }
            if (keep) {
                mp_back_input(mp);
            }
        }
    }
}

void mp_scan_next_value (MP mp, int keep, int *token, int *mode, int *kind)
{
    if (mp->extensions) {
        mp_get_next(mp);
        if (keep) {
            mp_back_input(mp);
        }
        *token = cur_cmd;
        *mode = cur_mod;
        *kind = mp->cur_exp.type;
    }
}

void mp_scan_expr_value (MP mp, int keep, int *kind)
{
    if (mp->extensions) {
        mp_get_next(mp);
        mp_scan_primary(mp);
        *kind = mp->cur_exp.type;
        if (keep) {
            mp_back_input(mp);
            mp_back_expr(mp);
        }
    }
}

void mp_scan_token_value (MP mp, int keep, int *token, int *mode, int *kind)
{
    if (mp->extensions) {
        mp_get_x_next(mp);
        if (keep) {
            mp_back_input(mp);
        }
        *token = cur_cmd;
        *mode = cur_mod;
        *kind = mp->cur_exp.type;
    }
}

int mp_skip_token_value (MP mp, int token)
{
    if (mp->extensions) {
        mp_get_x_next(mp);
        if (token == cur_cmd) {
            return 1;
        } else {
            mp_back_input(mp);
        }
    }
    return 0;
}

static void mp_scan_something (MP mp, int primary)
{
    mp_get_x_next(mp);
    switch (primary) {
        case 0:  mp_scan_expression(mp); break;
        case 1:  mp_scan_primary(mp);    break;
        case 2:  mp_scan_secondary(mp);  break;
        case 3:  mp_scan_tertiary(mp);   break;
        default: mp_scan_expression(mp); break;
    }
}

void mp_scan_numeric_value (MP mp, int primary, double *d)
{
    if (mp->extensions) {
        mp_scan_something(mp, primary);
        if (mp->cur_exp.type != mp_known_type) {
            mp_back_input(mp);
        } else {
            mp_back_input(mp);
            *d = number_to_double(cur_exp_value_number);
        }
    }
}

# define mp_set_double_value(mp,target,what) \
if (what->type == mp_known_type) { \
    *target = number_to_double(mp_get_value_number(what)); \
}

void mp_scan_pair_value (MP mp, int primary, double *x, double *y)
{
    if (mp->extensions) {
        mp_scan_something(mp, primary);
        if (mp->cur_exp.type != mp_pair_type) {
            mp_back_input(mp);
        } else {
            mp_node p ;
            mp_back_input(mp);
            p = mp_get_value_node(cur_exp_node);
            mp_set_double_value(mp, x, mp_x_part(p));
            mp_set_double_value(mp, y, mp_y_part(p));
        }
    }
}

void mp_scan_color_value (MP mp, int primary, double *r, double *g, double *b)
{
    if (mp->extensions) {
        mp_scan_something(mp, primary);
        if (mp->cur_exp.type != mp_color_type) {
            mp_back_input(mp);
        } else {
            mp_node p ;
            mp_back_input(mp);
            p = mp_get_value_node(cur_exp_node);
            mp_set_double_value(mp, r, mp_red_part(p));
            mp_set_double_value(mp, g, mp_green_part(p));
            mp_set_double_value(mp, b, mp_blue_part(p));
        }
    }
}

void mp_scan_cmykcolor_value (MP mp, int primary, double *c, double *m, double *y, double *k)
{
    if (mp->extensions) {
        mp_scan_something(mp, primary);
        if (mp->cur_exp.type != mp_cmykcolor_type) {
            mp_back_input(mp);
        } else {
            mp_node p ;
            mp_back_input(mp);
            p = mp_get_value_node(cur_exp_node);
            mp_set_double_value(mp, c, mp_cyan_part(p));
            mp_set_double_value(mp, m, mp_magenta_part(p));
            mp_set_double_value(mp, y, mp_yellow_part(p));
            mp_set_double_value(mp, k, mp_black_part(p));
        }
    }
}

void mp_scan_transform_value (MP mp, int primary, double *x, double *y, double *xx, double *xy, double *yx, double *yy)
{
    if (mp->extensions) {
        mp_scan_something(mp, primary);
        if (mp->cur_exp.type != mp_transform_type) {
            mp_back_input(mp);
        } else {
            mp_node p ;
            mp_back_input(mp);
            p = mp_get_value_node(cur_exp_node);
            mp_set_double_value(mp, x, mp_x_part(p));
            mp_set_double_value(mp, y, mp_y_part(p));
            mp_set_double_value(mp, xx, mp_xx_part(p));
            mp_set_double_value(mp, xy, mp_xy_part(p));
            mp_set_double_value(mp, yx, mp_yx_part(p));
            mp_set_double_value(mp, yy, mp_yy_part(p));
        }
    }
}

void mp_scan_path_value (MP mp, int primary, mp_knot *k)
{
    if (mp->extensions) {
        mp_scan_something(mp, primary);
        if (mp->cur_exp.type != mp_path_type && mp->cur_exp.type != mp_pen_type) {
            mp_back_input(mp);
        } else {
            mp_back_input(mp);
            *k = cur_exp_knot;
        }
    }
}

void mp_scan_boolean_value (MP mp, int primary, int *b)
{
    if (mp->extensions) {
        mp_scan_something(mp, primary);
        if (mp->cur_exp.type != mp_boolean_type) {
            mp_back_input(mp);
        } else {
            mp_back_input(mp);
            *b = cur_exp_value_boolean == mp_true_operation ? 1 : 0 ;
        }
    }
}

void mp_scan_string_value (MP mp, int primary, char **s, size_t *l)
{
    if (mp->extensions) {
        mp_scan_something(mp, primary);
        if (mp->cur_exp.type != mp_string_type) {
            mp_back_input(mp);
            *s = NULL ;
            *l = 0;
        } else {
            mp_back_input(mp);
            *s = (char *) cur_exp_str->str ;
            *l = cur_exp_str->len;
        }
    }
}

void mp_push_numeric_value (MP mp, double n)
{
    mp_number m;
    new_number_from_double(mp, m, n);
    mp->cur_exp.type = mp_known_type;
    mp_set_cur_exp_value_number(mp, &m);
    mp_back_expr(mp);
}

void mp_push_integer_value (MP mp, int i)
{
    mp_number m;
    new_number(m);
    set_number_from_int(m, i);
    mp->cur_exp.type = mp_known_type;
    mp_set_cur_exp_value_number(mp, &m);
    mp_back_expr(mp);
}

void mp_push_boolean_value (MP mp, int b)
{
    mp->cur_exp.type = mp_boolean_type;
    mp_set_cur_exp_value_boolean(mp, b ? mp_true_operation : mp_false_operation);
    mp_back_expr(mp);
}

void mp_push_string_value (MP mp, const char *s, int l)
{
    mp->cur_exp.type = mp_string_type;
    mp_set_cur_exp_str(mp, mp_rtsl(mp, (char *) s, l));
    mp_back_expr(mp);
}

void mp_push_pair_value (MP mp, double x, double y)
{
    mp_number px, py;
    mp_node p = mp_new_value_node(mp);
    mp_node v;
    mp_init_pair_node(mp, p);
    v = mp_get_value_node(p);
    new_number_from_double(mp, px, x);
    new_number_from_double(mp, py, y);
    mp_x_part(v)->type = mp_known_type;
    mp_y_part(v)->type = mp_known_type;
    mp_set_value_number(mp_x_part(v), px);
    mp_set_value_number(mp_y_part(v), py);
    free_number(px);
    free_number(py);
    p->name_type = mp_capsule_operation;
    mp->cur_exp.type = mp_pair_type;
    mp_set_cur_exp_node(mp, p);
    mp_back_expr(mp);
}

void mp_push_color_value (MP mp, double r, double g, double b)
{
    mp_number pr, pg, pb;
    mp_node p = mp_new_value_node(mp);
    mp_node v;
    mp_init_color_node(mp, p, mp_color_type);
    v = mp_get_value_node(p);
    new_number_from_double(mp, pr, r);
    new_number_from_double(mp, pg, g);
    new_number_from_double(mp, pb, b);
    mp_red_part  (v)->type = mp_known_type;
    mp_green_part(v)->type = mp_known_type;
    mp_blue_part (v)->type = mp_known_type;
    mp_set_value_number(mp_red_part  (v), pr);
    mp_set_value_number(mp_green_part(v), pg);
    mp_set_value_number(mp_blue_part (v), pb);
    free_number(pr);
    free_number(pg);
    free_number(pb);
    p->name_type = mp_capsule_operation;
    mp->cur_exp.type = mp_color_type;
    mp_set_cur_exp_node(mp, p);
    mp_back_expr(mp);
}

void mp_push_cmykcolor_value (MP mp, double c, double m, double y, double k)
{
    mp_number pc, pm, py, pk;
    mp_node p = mp_new_value_node(mp);
    mp_node v;
    mp_init_color_node(mp, p, mp_cmykcolor_type);
    v = mp_get_value_node(p);
    new_number_from_double(mp, pc, c);
    new_number_from_double(mp, pm, m);
    new_number_from_double(mp, py, y);
    new_number_from_double(mp, pk, k);
    mp_cyan_part   (v)->type = mp_known_type;
    mp_magenta_part(v)->type = mp_known_type;
    mp_yellow_part (v)->type = mp_known_type;
    mp_black_part  (v)->type = mp_known_type;
    mp_set_value_number(mp_cyan_part   (v), pc);
    mp_set_value_number(mp_magenta_part(v), pm);
    mp_set_value_number(mp_yellow_part (v), py);
    mp_set_value_number(mp_black_part  (v), pk);
    free_number(pc);
    free_number(pm);
    free_number(py);
    free_number(pk);
    p->name_type = mp_capsule_operation;
    mp->cur_exp.type = mp_cmykcolor_type;
    mp_set_cur_exp_node(mp, p);
    mp_back_expr(mp);
}

void mp_push_transform_value (MP mp, double x, double y, double xx, double xy, double yx, double yy)
{
    mp_number px, py, pxx, pxy, pyx, pyy ;
    mp_node p = mp_new_value_node(mp);
    mp_node v;
    mp_init_transform_node(mp, p);
    v = mp_get_value_node(p);
    new_number_from_double(mp, px, x);
    new_number_from_double(mp, py, y);
    new_number_from_double(mp, pxx, xx);
    new_number_from_double(mp, pxy, xy);
    new_number_from_double(mp, pyx, yx);
    new_number_from_double(mp, pyy, yy);
    mp_x_part (v)->type = mp_known_type;
    mp_y_part (v)->type = mp_known_type;
    mp_xx_part(v)->type = mp_known_type;
    mp_xy_part(v)->type = mp_known_type;
    mp_yx_part(v)->type = mp_known_type;
    mp_yy_part(v)->type = mp_known_type;
    mp_set_value_number(mp_x_part (v), px);
    mp_set_value_number(mp_y_part (v), py);
    mp_set_value_number(mp_xx_part(v), pxx);
    mp_set_value_number(mp_xy_part(v), pxy);
    mp_set_value_number(mp_yx_part(v), pyx);
    mp_set_value_number(mp_yy_part(v), pyy);
    free_number(px);
    free_number(py);
    free_number(pxx);
    free_number(pxy);
    free_number(pyx);
    free_number(pyy);
    p->name_type = mp_capsule_operation;
    mp->cur_exp.type = mp_transform_type;
    mp_set_cur_exp_node(mp, p);
    mp_back_expr(mp);
}

void mp_push_path_value (MP mp, mp_knot k)
{
    mp->cur_exp.type = mp_path_type;
    mp_set_cur_exp_knot(mp, k);
    mp_back_expr(mp);
}

static void check_for_mediation (MP mp);
static void mp_primary_error(MP mp)
{
    mp_value new_expr;
    memset(&new_expr, 0, sizeof(mp_value));
    mp_disp_err(mp, NULL);
    new_number(new_expr.data.n);
    mp_back_error(
        mp,
        "Nonnumeric part has been replaced by 0",
        "I've started to scan a pair (x,y), color (r,g,b), cmykcolor (c,m,y,k) or\n"
        "transform (tx,ty,xx,xy,yx,yy) but ran into a non-numeric type. I'll recover\n"
        "as good as possible."
    );
    mp_get_x_next(mp);
    mp_flush_cur_exp(mp, new_expr);
}
void mp_scan_primary (MP mp)
{
    mp_command_code my_var_flag = mp->var_flag;
    mp->var_flag = 0;
  RESTART:
    check_arith(mp);
    switch (cur_cmd) {
        case mp_left_delimiter_command:
            {
                mp_sym l_delim = cur_sym;
                mp_sym r_delim = equiv_sym(cur_sym);
                mp_get_x_next(mp);
                mp_scan_expression(mp);
                if ((cur_cmd == mp_comma_command) && (mp->cur_exp.type >= mp_known_type)) {
                    mp_node q = mp_new_value_node(mp);
                    mp_node p1 = mp_stash_cur_exp(mp);
                    mp_node r;
                    q->name_type = mp_capsule_operation;
                    mp_get_x_next(mp);
                    mp_scan_expression(mp);
                    if (mp->cur_exp.type < mp_known_type) {
                        mp_primary_error(mp);
                    }
                    if (cur_cmd != mp_comma_command) {
                        mp_init_pair_node(mp, q);
                        r = mp_get_value_node(q);
                        mp_stash_in(mp, mp_y_part(r));
                        mp_unstash_cur_exp(mp, p1);
                        mp_stash_in(mp, mp_x_part(r));
                    } else {
                        mp_node p2 = mp_stash_cur_exp(mp);
                        mp_get_x_next(mp);
                        mp_scan_expression(mp);
                        if (mp->cur_exp.type < mp_known_type) {
                            mp_primary_error(mp);
                        }
                        if (cur_cmd != mp_comma_command) {
                            mp_init_color_node(mp, q, mp_color_type);
                            r = mp_get_value_node(q);
                            mp_stash_in(mp, mp_blue_part(r));
                            mp_unstash_cur_exp(mp, p1);
                            mp_stash_in(mp, mp_red_part(r));
                            mp_unstash_cur_exp(mp, p2);
                            mp_stash_in(mp, mp_green_part(r));
                        } else {
                            mp_node p3 = mp_stash_cur_exp(mp);
                            mp_get_x_next(mp);
                            mp_scan_expression(mp);
                            if (mp->cur_exp.type < mp_known_type) {
                                mp_primary_error(mp);
                            }
                            if (cur_cmd != mp_comma_command) {
                                mp_init_color_node(mp, q, mp_cmykcolor_type);
                                r = mp_get_value_node(q);
                                mp_stash_in(mp, mp_black_part(r));
                                mp_unstash_cur_exp(mp, p1);
                                mp_stash_in(mp, mp_cyan_part(r));
                                mp_unstash_cur_exp(mp, p2);
                                mp_stash_in(mp, mp_magenta_part(r));
                                mp_unstash_cur_exp(mp, p3);
                                mp_stash_in(mp, mp_yellow_part(r));
                            } else {
                                mp_node p4 = mp_stash_cur_exp(mp);
                                mp_node p5;
                                mp_get_x_next(mp);
                                mp_scan_expression(mp);
                                if (mp->cur_exp.type < mp_known_type) {
                                    mp_primary_error(mp);
                                    p5 = mp_stash_cur_exp(mp);
                                    goto HERE;
                                }
                                if (cur_cmd != mp_comma_command) {
                                    mp_primary_error(mp);
                                    p5 = mp_stash_cur_exp(mp);
                                    goto HERE;
                                }
                                p5 = mp_stash_cur_exp(mp);
                                mp_get_x_next(mp);
                                mp_scan_expression(mp);
                                if (mp->cur_exp.type < mp_known_type) {
                                    mp_primary_error(mp);
                                }
                              HERE:
                                mp_init_transform_node(mp, q);
                                r = mp_get_value_node(q);
                                mp_stash_in(mp, mp_ty_part(r));
                                mp_unstash_cur_exp(mp, p5);
                                mp_stash_in(mp, mp_tx_part(r));
                                mp_unstash_cur_exp(mp, p4);
                                mp_stash_in(mp, mp_yy_part(r));
                                mp_unstash_cur_exp(mp, p3);
                                mp_stash_in(mp, mp_yx_part(r));
                                mp_unstash_cur_exp(mp, p2);
                                mp_stash_in(mp, mp_xy_part(r));
                                mp_unstash_cur_exp(mp, p1);
                                mp_stash_in(mp, mp_xx_part(r));
                            }
                        }
                    }
                    mp_check_delimiter(mp, l_delim, r_delim);
                    mp->cur_exp.type = q->type;
                    mp_set_cur_exp_node(mp, q);
                } else {
                    mp_check_delimiter(mp, l_delim, r_delim);
                }
            }
            break;
        case mp_begin_group_command:
            {
                int group_line = mp_true_line(mp);
                if (number_positive(internal_value(mp_tracing_commands_internal))) {
                    mp_show_cmd_mod(mp, cur_cmd, cur_mod);
                }
                mp_save_boundary(mp);
                do {
                    mp_do_statement(mp);
                } while (cur_cmd == mp_semicolon_command);
                if (cur_cmd != mp_end_group_command) {
                    char msg[256];
                    mp_snprintf(msg, 256, "A group begun on line %d never ended", (int) group_line);
                    mp_back_error(
                        mp,
                        msg,
                        "I saw a 'begingroup' back there that hasn't been matched by 'endgroup'. So I've\n"
                        "inserted 'endgroup' now."
                    );
                    set_cur_cmd(mp_end_group_command);
                }
                mp_unsave(mp);
                if (number_positive(internal_value(mp_tracing_commands_internal))) {
                    mp_show_cmd_mod(mp, cur_cmd, cur_mod);
                }
            }
            break;
        case mp_string_command:
            mp->cur_exp.type = mp_string_type;
            mp_set_cur_exp_str(mp, cur_mod_str);
            break;
        case mp_numeric_command:
            {
                mp_number num, denom;
                mp_set_cur_exp_value_number(mp, &cur_mod_number);
                mp->cur_exp.type = mp_known_type;
                mp_get_x_next(mp);


                if (cur_cmd != mp_slash_command) {
                    new_number(num);
                    new_number(denom);
                } else {
                    mp_get_x_next(mp);
                    if (cur_cmd != mp_numeric_command) {
                        mp_back_input(mp);
                        set_cur_cmd(mp_slash_command);
                        set_cur_mod(mp_over_operation);
                        set_cur_sym(mp->frozen_slash);

                        goto DONE;
                    } else {
                        new_number_clone(num, cur_exp_value_number);
                        new_number_clone(denom, cur_mod_number);


                        if (number_zero(denom)) {
                            mp_error(mp, "Division by zero", "I'll pretend that you meant to divide by 1.");
                        } else {
                            mp_number ret;
                            new_number(ret);
                            make_scaled(ret, num, denom);
                            mp_set_cur_exp_value_number(mp, &ret);
                            free_number(ret);
                        }
                        check_arith(mp);
                        mp_get_x_next(mp);
                    }
                }
                if (cur_cmd >= mp_min_primary_command && cur_cmd < mp_numeric_command) {
                    mp_number absnum, absdenom;
                    mp_node p = mp_stash_cur_exp(mp);
                    mp_scan_primary(mp);
                    new_number_abs(absnum, num);
                    new_number_abs(absdenom, denom);
                    if (number_greaterequal(absnum, absdenom) || (mp->cur_exp.type < mp_color_type)) {
                        mp_do_binary(mp, p, mp_times_operation);
                    } else {
                        mp_frac_mult(mp, &num, &denom);
                        mp_free_value_node(mp, p);
                    }
                    free_number(absnum);
                    free_number(absdenom);
                }
                free_number(num);
                free_number(denom);
                goto DONE;
            }
        case mp_nullary_command:
            mp_do_nullary(mp, (int) cur_mod);
            break;
        case mp_unary_command:
        case mp_type_name_command:
        case mp_cycle_command:
        case mp_plus_or_minus_command:
            {
                int c = (int) cur_mod;
                mp_get_x_next(mp);
                mp_scan_primary(mp);
                mp_do_unary(mp, c);
                goto DONE;
            }
        case mp_of_binary_command:
            {
                mp_node p;
                int c = (int) cur_mod;
                mp_get_x_next(mp);
                mp_scan_expression(mp);
                if (cur_cmd != mp_of_command) {
                    char msg[256];
                    mp_string sname;
                    int selector = mp->selector;
                    mp->selector = mp_new_string_selector;
                    mp_print_cmd_mod(mp, mp_of_binary_command, c);
                    mp->selector = selector;
                    sname = mp_make_string(mp);
                    mp_snprintf(msg, 256, "Missing 'of' has been inserted for %s", mp_str(mp, sname));
                    delete_str_ref(sname);
                    mp_back_error(mp, msg, "I've got the first argument; will look now for the other.");
                }
                p = mp_stash_cur_exp(mp);
                mp_get_x_next(mp);
                mp_scan_primary(mp);
                mp_do_binary(mp, p, c);
                goto DONE;
            }
        case mp_str_command:
            {
                int selector = mp->selector;
                mp_get_x_next(mp);
                mp_scan_suffix(mp);
                mp->selector = mp_new_string_selector;
                mp_show_token_list(mp, cur_exp_node, NULL);
                mp_flush_token_list(mp, cur_exp_node);
                mp_set_cur_exp_str(mp, mp_make_string(mp));
                mp->selector = selector;
                mp->cur_exp.type = mp_string_type;
                goto DONE;
            }
        case mp_void_command:
            {
                mp_value new_expr;
                memset(&new_expr, 0, sizeof(mp_value));
                new_number(new_expr.data.n);
                mp_get_x_next(mp);
                mp_scan_suffix(mp);
                if (cur_exp_node == NULL) {
                    set_number_from_boolean(new_expr.data.n, mp_true_operation);
                } else {
                    set_number_from_boolean(new_expr.data.n, mp_false_operation);
                }
                mp_flush_cur_exp(mp, new_expr);
                cur_exp_node = NULL;
                mp->cur_exp.type = mp_boolean_type;
                goto DONE;
            }
        case mp_internal_command:
            {
                int qq = cur_mod;
                if (my_var_flag == mp_assignment_command) {
                    mp_get_x_next(mp);
                    if (cur_cmd == mp_assignment_command) {
                        mp_set_cur_exp_node(mp, mp_new_symbolic_node(mp));
                        mp_set_sym_info(cur_exp_node, qq);
                        cur_exp_node->name_type = mp_internal_operation;
                        mp->cur_exp.type = mp_token_list_type;
                        goto DONE;
                    }
                    mp_back_input(mp);
                }
                if (internal_type(qq) == mp_string_type) {
                    mp_set_cur_exp_str(mp, internal_string(qq));
                } else {
                    mp_set_cur_exp_value_number(mp, &(internal_value(qq)));
                }
                mp->cur_exp.type = internal_type(qq);
            }
            break;
        case mp_capsule_command:
            mp_make_exp_copy(mp, cur_mod_node);
            break;
        case mp_tag_command:
            {
                mp_node p = 0;
                mp_node q = 0;
                mp_node t = 0;
                mp_node macro_ref = 0;
                int tt = mp_vacuous_type;
                mp_node pre_head = mp_new_symbolic_node(mp);
                mp_node tail = pre_head;
                mp_node post_head = NULL;
                while (1) {
                    t = mp_cur_tok(mp);
                    tail->link = t;
                    if (tt != mp_undefined_type) {
                        mp_sym qq;
                        p = pre_head->link;
                        qq = mp_get_sym_sym(p);
                        tt = mp_undefined_type;

                        if (eq_type(qq) == mp_tag_command) {
                            q = equiv_node(qq);
                            if (q == NULL) {
                                goto DONE2;
                            }
                            while (1) {
                                p = p->link;
                                if (p == NULL) {
                                    tt = q->type;
                                    goto DONE2;
                                }
                                if (q->type != mp_structured_type) {
                                    goto DONE2;
                                }
                                q = mp_get_attribute_head(q)->link;
                                if (p->type == mp_symbol_node_type) {
                                    do {
                                        q = q->link;
                                    } while (! (mp_get_hashloc(q) >= mp_get_sym_sym(p)));
                                    if (mp_get_hashloc(q) > mp_get_sym_sym(p)) {
                                        goto DONE2;
                                    }
                                }
                            }
                        }
                      DONE2:
                        if (tt >= mp_unsuffixed_macro_type) {
                            tail->link = NULL;
                            if (tt > mp_unsuffixed_macro_type) {
                                post_head = mp_new_symbolic_node(mp);
                                tail = post_head;
                                tail->link = t;
                                tt = mp_undefined_type;
                                macro_ref = mp_get_value_node(q);
                                mp_add_mac_ref(macro_ref);
                            } else {
                                p = mp_new_symbolic_node(mp);
                                mp_set_sym_sym(pre_head, pre_head->link);
                                pre_head->link = p;
                                mp_set_sym_sym(p, t);
                                mp_macro_call(mp, mp_get_value_node(q), pre_head, NULL);
                                mp_get_x_next(mp);
                                goto RESTART;
                            }
                        }
                    }
                    mp_get_x_next(mp);
                    tail = t;
                    if (cur_cmd == mp_left_bracket_command) {
                        mp_get_x_next(mp);
                        mp_scan_expression(mp);
                        if (cur_cmd != mp_right_bracket_command) {
                            mp_back_input(mp);
                            mp_back_expr(mp);
                            set_cur_cmd(mp_left_bracket_command);
                            set_cur_mod_number(zero_t);
                            set_cur_sym(mp->frozen_left_bracket);
                        } else {
                            if (mp->cur_exp.type != mp_known_type) {
                                mp_bad_subscript(mp);
                            }
                            set_cur_cmd(mp_numeric_command);
                            set_cur_mod_number(cur_exp_value_number);
                            set_cur_sym(NULL);
                        }
                    }
                    if (cur_cmd > mp_max_suffix_token) {
                        break;
                    } else if (cur_cmd < mp_min_suffix_token) {
                        break;
                    }
                }
                if (post_head != NULL) {
                    mp_back_input(mp);
                    p = mp_new_symbolic_node(mp);
                    q = post_head->link;
                    mp_set_sym_sym(pre_head, pre_head->link);
                    pre_head->link = post_head;
                    mp_set_sym_sym(post_head, q);
                    post_head->link = p;
                    mp_set_sym_sym(p, q->link);
                    q->link = NULL;
                    mp_macro_call(mp, macro_ref, pre_head, NULL);
                    mp_decr_mac_ref(macro_ref);
                    mp_get_x_next(mp);
                    goto RESTART;
                }
                q = pre_head->link;
                mp_free_symbolic_node(mp, pre_head);
                if (cur_cmd == my_var_flag) {
                    mp->cur_exp.type = mp_token_list_type;
                    mp_set_cur_exp_node(mp, q);
                    goto DONE;
                }
                p = mp_find_variable(mp, q);
                if (p != NULL) {
                    mp_make_exp_copy(mp, p);
                } else {
                    mp_value new_expr;
                    char *msg = mp_obliterated (mp, q);
                    memset(&new_expr, 0, sizeof(mp_value));
                    new_number(new_expr.data.n);
                    mp_back_error(
                        mp,
                        msg,
                        "While I was evaluating the suffix of this variable, something was redefined, and\n"
                        "it's no longer a variable! In order to get back on my feet, I've inserted '0'\n"
                        "instead."
                    );
                    mp_memory_free(msg);
                    mp_get_x_next(mp);
                    mp_flush_cur_exp(mp, new_expr);
                }
                mp_flush_node_list(mp, q);
                goto DONE;
            }
            break;
        default:
            mp_bad_exp(mp, "A primary");
            goto RESTART;
            break;
    }
    mp_get_x_next(mp);
  DONE:
    check_for_mediation(mp);
}
static void check_for_mediation (MP mp)
{
    if (cur_cmd == mp_left_bracket_command && mp->cur_exp.type >= mp_known_type) {
        mp_node p = mp_stash_cur_exp(mp);
        mp_get_x_next(mp);
        mp_scan_expression(mp);
        if (cur_cmd != mp_comma_command) {
            mp_back_input(mp);
            mp_back_expr(mp);
            set_cur_cmd(mp_left_bracket_command);
            set_cur_mod_number(zero_t);
            set_cur_sym(mp->frozen_left_bracket);
            mp_unstash_cur_exp(mp, p);
        } else {
            mp_node q = mp_stash_cur_exp(mp);
            mp_node r;
            mp_get_x_next(mp);
            mp_scan_expression(mp);
            if (cur_cmd != mp_right_bracket_command) {
                mp_back_error(
                    mp,
                    "Missing ']' has been inserted",
                    "I've scanned an expression of the form 'a[b,c', so a right bracket should have\n"
                    "come next. I shall pretend that one was there."
                );
            }
            r = mp_stash_cur_exp(mp);
            mp_make_exp_copy(mp, q);
            mp_do_binary(mp, r, mp_minus_operation);
            mp_do_binary(mp, p, mp_times_operation);
            mp_do_binary(mp, q, mp_plus_operation);
            mp_get_x_next(mp);
        }
    }
}
static void mp_scan_suffix (MP mp)
{
    mp_node h = mp_new_symbolic_node(mp);
    mp_node t = h;
    while (1) {
        mp_node p;
        if (cur_cmd == mp_left_bracket_command) {
            mp_get_x_next(mp);
            mp_scan_expression(mp);
            if (mp->cur_exp.type != mp_known_type) {
                mp_bad_subscript(mp);
            }
            if (cur_cmd != mp_right_bracket_command) {
                mp_back_error(
                    mp,
                    "Missing ']' has been inserted",
                    "I've seen a '[' and a subscript value, in a suffix, so a right bracket should\n"
                    "have come next. I shall pretend that one was there."
                );
            }
            set_cur_cmd(mp_numeric_command);
            set_cur_mod_number(cur_exp_value_number);
        }
        if (cur_cmd == mp_numeric_command) {
            mp_number arg1;
            new_number_clone(arg1, cur_mod_number);
            p = mp_new_num_tok(mp, &arg1);
            free_number(arg1);
        } else if ((cur_cmd == mp_tag_command) || (cur_cmd == mp_internal_command)) {
            p = mp_new_symbolic_node(mp);
            mp_set_sym_sym(p, cur_sym);
            p->name_type = cur_sym_mod;
        } else {
            break;
        }
        t->link = p;
        t = p;
        mp_get_x_next(mp);
    }
    mp_set_cur_exp_node(mp, h->link);
    mp_free_symbolic_node(mp, h);
    mp->cur_exp.type = mp_token_list_type;
}
static void mp_scan_secondary (MP mp)
{
    mp_node cc = NULL;
    mp_sym mac_name = NULL;
  RESTART:
    if ((cur_cmd < mp_min_primary_command) || (cur_cmd > mp_max_primary_command)) {
        mp_bad_exp(mp, "A secondary");
    }
    mp_scan_primary(mp);
  CONTINUE:
    if (cur_cmd <= mp_max_secondary_command && cur_cmd >= mp_min_secondary_command) {
        mp_node p = mp_stash_cur_exp(mp);
        int d = cur_cmd;
        int c = cur_mod;
        if (d == mp_primary_def_command) {
            cc = cur_mod_node;
            mac_name = cur_sym;
            mp_add_mac_ref(cc);
        }
        mp_get_x_next(mp);
        mp_scan_primary(mp);
        if (d == mp_primary_def_command) {
            mp_back_input(mp);
            mp_binary_mac(mp, p, cc, mac_name);
            mp_decr_mac_ref(cc);
            mp_get_x_next(mp);
            goto RESTART;
        } else {
            mp_do_binary(mp, p, c);
            goto CONTINUE;
        }
    }
}
static void mp_scan_tertiary (MP mp)
{
    mp_node cc = NULL;
    mp_sym mac_name = NULL;
  RESTART:
    if ((cur_cmd < mp_min_primary_command) || (cur_cmd > mp_max_primary_command)) {
        mp_bad_exp(mp, "A tertiary");
    }
    mp_scan_secondary(mp);
  CONTINUE:
    if (cur_cmd <= mp_max_tertiary_command && cur_cmd >= mp_min_tertiary_command) {
        mp_node p = mp_stash_cur_exp(mp);
        int c = cur_mod;
        int d = cur_cmd;
        if (d == mp_secondary_def_command) {
            cc = cur_mod_node;
            mac_name = cur_sym;
            mp_add_mac_ref(cc);
        }
        mp_get_x_next(mp);
        mp_scan_secondary(mp);
        if (d == mp_secondary_def_command) {
            mp_back_input(mp);
            mp_binary_mac(mp, p, cc, mac_name);
            mp_decr_mac_ref(cc);
            mp_get_x_next(mp);
            goto RESTART;
        } else {
            mp_do_binary(mp, p, c);
            goto CONTINUE;
        }
    }
}
static int mp_scan_path (MP mp);
static void mp_scan_expression (MP mp)
{
    int my_var_flag = mp->var_flag;
    mp_check_expansion_depth(mp);
  RESTART:
    if ((cur_cmd < mp_min_primary_command) || (cur_cmd > mp_max_primary_command)) {
        mp_bad_exp(mp, "An");
    }
    mp_scan_tertiary(mp);
  CONTINUE:
    if (cur_cmd <= mp_max_expression_command) {
        if (cur_cmd >= mp_min_expression_command) {
            if ((cur_cmd != mp_equals_command) || (my_var_flag != mp_assignment_command)) {
                mp_node cc = NULL;
                mp_sym mac_name = NULL;
                mp_node p = mp_stash_cur_exp(mp);
                int d = cur_cmd;
                int c = cur_mod;
                if (d == mp_tertiary_def_command) {
                    cc = cur_mod_node;
                    mac_name = cur_sym;
                    mp_add_mac_ref(cc);
                }
                if ((d < mp_ampersand_command) || ((d == mp_ampersand_command) && ((p->type == mp_pair_type) || (p->type == mp_path_type)))) {
                    mp_unstash_cur_exp(mp, p);
                    if (! mp_scan_path(mp)) {
                        mp->expand_depth_count--;
                        return;
                    }
                } else {
                    mp_get_x_next(mp);
                    mp_scan_tertiary(mp);
                    if (d != mp_tertiary_def_command) {
                        mp_do_binary(mp, p, c);
                    } else {
                        mp_back_input(mp);
                        mp_binary_mac(mp, p, cc, mac_name);
                        mp_decr_mac_ref(cc);
                        mp_get_x_next(mp);
                        goto RESTART;
                    }
                }
                goto CONTINUE;
            }
        }
    }
    mp->expand_depth_count--;
}
static void force_valid_tension_setting (MP mp)
{
    if ((mp->cur_exp.type != mp_known_type) || number_less(cur_exp_value_number, min_tension)) {
        mp_value new_expr;
        memset(&new_expr, 0, sizeof(mp_value));
        new_number(new_expr.data.n);
        mp_disp_err(mp, NULL);
        number_clone(new_expr.data.n, unity_t);
        mp_back_error(
            mp,
            "Improper tension has been set to 1",
            "The expression above should have been a number >= 3/4."
        );
        mp_get_x_next(mp);
        mp_flush_cur_exp(mp, new_expr);
    }
}
static int mp_scan_path (MP mp)
{
    mp_knot path_p, path_q, r;
    mp_knot pp = NULL;
    mp_knot qq = NULL;
    int d, dd;
    int cycle_hit = 0;
    mp_number x, y;
    int t = mp_endpoint_knot;
    if (mp->cur_exp.type == mp_pair_type) {
        path_p = mp_pair_to_knot(mp);
    } else if (mp->cur_exp.type == mp_path_type) {
        path_p = cur_exp_knot;
    } else {
        return 0;
    }
    path_q = path_p;
    while (mp_next_knot(path_q) != path_p) {
        path_q = mp_next_knot(path_q);
    }
    if (mp_left_type(path_p) != mp_endpoint_knot) {
        r = mp_copy_knot(mp, path_p);
        mp_prev_knot(r) = path_q;
        mp_next_knot(path_q) = r;
        path_q = r;
    }
    mp_left_type(path_p) = mp_open_knot;
    mp_right_type(path_q) = mp_open_knot;
    new_number(y);
    new_number(x);
  CONTINUE_PATH:
    if (cur_cmd == mp_left_brace_command) {
        t = mp_scan_direction(mp);
        if (t != mp_open_knot) {
            mp_right_type(path_q) = (unsigned char) t;
            number_clone(path_q->right_given, cur_exp_value_number);
            if (mp_left_type(path_q) == mp_open_knot) {
                mp_left_type(path_q) = (unsigned char) t;
                number_clone(path_q->left_given, cur_exp_value_number);
            }
        }
    }
    d = cur_cmd;
    dd = cur_mod;
    if (d == mp_path_join_command) {
        mp_get_x_next(mp);
        switch (cur_cmd) {
            case mp_tension_command:
                mp_get_x_next(mp);
                set_number_from_scaled(y, cur_cmd);
                if (cur_cmd == mp_at_least_command) {
                    mp_get_x_next(mp);
                }
                mp_scan_primary(mp);
                force_valid_tension_setting(mp);
                if (number_to_scaled(y) == mp_at_least_command && is_number(cur_exp_value_number)) {
                    number_negate(cur_exp_value_number);
                }
                number_clone(path_q->right_tension, cur_exp_value_number);
                if (cur_cmd == mp_and_command) {
                    mp_get_x_next(mp);
                    set_number_from_scaled(y, cur_cmd);
                    if (cur_cmd == mp_at_least_command) {
                        mp_get_x_next(mp);
                    }
                    mp_scan_primary(mp);
                    force_valid_tension_setting(mp);
                    if (number_to_scaled(y) == mp_at_least_command && is_number(cur_exp_value_number)) {
                        number_negate(cur_exp_value_number);
                    }
                }
                number_clone(y, cur_exp_value_number);
                break;
            case mp_controls_command:
                mp_right_type(path_q) = mp_explicit_knot;
                t = mp_explicit_knot;
                mp_get_x_next(mp);
                mp_scan_primary(mp);
                mp_known_pair(mp);
                number_clone(path_q->right_x, mp->cur_x);
                number_clone(path_q->right_y, mp->cur_y);
                if (cur_cmd != mp_and_command) {
                    number_clone(x, path_q->right_x);
                    number_clone(y, path_q->right_y);
                } else {
                    mp_get_x_next(mp);
                    mp_scan_primary(mp);
                    mp_known_pair(mp);
                    number_clone(x, mp->cur_x);
                    number_clone(y, mp->cur_y);
                }
                break;
            default:
                set_number_to_unity(path_q->right_tension);
                set_number_to_unity(y);
                mp_back_input(mp);
                goto DONE;
                break;
        }
        if (cur_cmd != mp_path_join_command) {
            mp_back_error(
                mp,
                "Missing '..' has been inserted",
                "A path join command should end with two dots."
            );
        }
  DONE:
    ;
    } else if (d != mp_ampersand_command) {
        goto FINISH_PATH;
    }
    mp_get_x_next(mp);
    if (cur_cmd == mp_left_brace_command) {
        t = mp_scan_direction(mp);
        if (mp_right_type(path_q) != mp_explicit_knot) {
            number_clone(x, cur_exp_value_number);
        } else {
            t = mp_explicit_knot;
        }
    } else if (mp_right_type(path_q) != mp_explicit_knot) {
        t = mp_open_knot;
        set_number_to_zero(x);
    }
    if (cur_cmd == mp_cycle_command) {
        if (cur_mod == mp_cycle_operation) {
            cycle_hit = 1;
            mp_get_x_next(mp);
            pp = path_p;
            qq = path_p;
            if (d == mp_ampersand_command && path_p == path_q) {
                d = mp_path_join_command;
                set_number_to_unity(path_q->right_tension);
                set_number_to_unity(y);
            }
        } else {
            mp_get_x_next(mp);
            qq = pp;
            goto FINISH_PATH;
        }
    } else {
        mp_scan_tertiary(mp);
        if (mp->cur_exp.type != mp_path_type) {
            pp = mp_pair_to_knot(mp);
        } else {
            pp = cur_exp_knot;
        }
        qq = pp;
        while (mp_next_knot(qq) != pp) {
            qq = mp_next_knot(qq);
        }
        if (mp_left_type(pp) != mp_endpoint_knot) {
            r = mp_copy_knot(mp, pp);
            mp_prev_knot(r) = qq;
            mp_next_knot(qq) = r;
            qq = r;
        }
        mp_left_type(pp) = mp_open_knot;
        mp_right_type(qq) = mp_open_knot;
    }
    if (d == mp_ampersand_command && dd != mp_just_append_operation && dd != mp_tolerant_concat_operation && dd != mp_tolerant_append_operation) {
        if (! (number_equal(path_q->x_coord, pp->x_coord)) || ! (number_equal(path_q->y_coord, pp->y_coord))) {
            mp_back_error(
                mp,
                "Paths don't touch; '&' will be changed to '..'",
                "When you join paths 'p & q', the ending point of p must be exactly equal to the\n"
                "starting point of q. So I'm going to pretend that you said 'p .. q' instead."
            );
            mp_get_x_next(mp);
            d = mp_path_join_command;
            set_number_to_unity(path_q->right_tension);
            set_number_to_unity(y);
        }
    }
    if (mp_right_type(pp) == mp_open_knot && ((t == mp_curl_knot) || (t == mp_given_knot))) {
        mp_right_type(pp) = (unsigned char) t;
        number_clone(pp->right_given, x);
    }
    if (d == mp_ampersand_command) {
        if (dd == mp_tolerant_concat_operation || dd == mp_tolerant_append_operation) {
            mp_number dx, dy;
            new_number(dx);
            new_number(dy);
            set_number_from_subtraction(dx, path_q->x_coord, pp->x_coord);
            set_number_from_subtraction(dy, path_q->y_coord, pp->y_coord);
            number_abs(dx);
            number_abs(dy);
            if (number_lessequal(dx, epsilon_t) && number_lessequal(dy, epsilon_t)) {
                set_number_half_from_addition(dx, path_q->x_coord, pp->x_coord);
                set_number_half_from_addition(dy, path_q->y_coord, pp->y_coord);
                number_clone(pp->left_x, dx);
                number_clone(path_q->right_x, dx);
                number_clone(pp->left_y, dy);
                number_clone(path_q->right_y, dy);
            }
            dd = dd == mp_tolerant_concat_operation ? mp_concatenate_operation : mp_just_append_operation;
            free_number(dx);
            free_number(dy);
        }
        if (dd == mp_just_append_operation) {
            mp_left_type(pp) = mp_explicit_knot;
            mp_right_type(path_q) = mp_explicit_knot;
            mp_prev_knot(pp) = path_q;
            mp_next_knot(path_q) = pp;
            number_clone(pp->left_x, path_q->x_coord);
            number_clone(pp->left_y, path_q->y_coord);
            number_clone(path_q->right_x, pp->x_coord);
            number_clone(path_q->right_y, pp->y_coord);
            mp_knotstate(pp) = mp_begin_knot;
            mp_knotstate(path_q) = mp_end_knot;
            path_q = pp;
        } else {
            if (mp_left_type(path_q) == mp_open_knot && mp_right_type(path_q) == mp_open_knot) {
                mp_left_type(path_q) = mp_curl_knot;
                set_number_to_unity(path_q->left_curl);
            }
            if (mp_right_type(pp) == mp_open_knot && t == mp_open_knot) {
                mp_right_type(pp) = mp_curl_knot;
                set_number_to_unity(pp->right_curl);
            }
            mp_right_type(path_q) = mp_right_type(pp);
            mp_prev_knot(pp) = mp_next_knot(path_q);
            mp_next_knot(path_q) = mp_next_knot(pp);
            number_clone(path_q->right_x, pp->right_x);
            number_clone(path_q->right_y, pp->right_y);
            mp_memory_free(pp);
        }
        if (qq == pp) {
            qq = path_q;
        }
    } else {
        if (mp_right_type(path_q) == mp_open_knot && ((mp_left_type(path_q) == mp_curl_knot) || (mp_left_type(path_q) == mp_given_knot))) {
            mp_right_type(path_q) = mp_left_type(path_q);
            number_clone(path_q->right_given, path_q->left_given);
        }
        mp_prev_knot(pp) = path_q;
        mp_next_knot(path_q) = pp;
        number_clone(pp->left_y, y);
        if (t != mp_open_knot) {
            number_clone(pp->left_x, x);
            mp_left_type(pp) = (unsigned char) t;
        };
    }
    path_q = qq;
    if (cur_cmd >= mp_min_expression_command && cur_cmd <= mp_ampersand_command && ! cycle_hit) {
        goto CONTINUE_PATH;
    }
  FINISH_PATH:
    if (cycle_hit) {
        if (d == mp_ampersand_command) {
            path_p = path_q;
        }
    } else {
        mp_left_type(path_p) = mp_endpoint_knot;
        if (mp_right_type(path_p) == mp_open_knot) {
            mp_right_type(path_p) = mp_curl_knot;
            set_number_to_unity(path_p->right_curl);
        }
        mp_right_type(path_q) = mp_endpoint_knot;
        if (mp_left_type(path_q) == mp_open_knot) {
            mp_left_type(path_q) = mp_curl_knot;
            set_number_to_unity(path_q->left_curl);
        }
        mp_prev_knot(path_p) = path_q;
        mp_next_knot(path_q) = path_p;
    }
    mp_make_choices(mp, path_p);
    mp->cur_exp.type = mp_path_type;
    mp_set_cur_exp_knot(mp, path_p);
    free_number(x);
    free_number(y);
    return 1;
}
static void do_boolean_error (MP mp)
{
    mp_value new_expr;
    memset(&new_expr, 0, sizeof(mp_value));
    new_number(new_expr.data.n);
    mp_disp_err(mp, NULL);
    set_number_from_boolean(new_expr.data.n, mp_false_operation);
    mp_back_error(
        mp,
        "Undefined condition will be treated as 'false'",
        "The expression shown above should have had a definite true-or-false value. I'm\n"
        "changing it to 'false'."
    );
    mp_get_x_next(mp);
    mp_flush_cur_exp(mp, new_expr);
    mp->cur_exp.type = mp_boolean_type;
}

void mp_print_capsule (MP mp, mp_node p)
{
    mp_print_chr(mp, '(');
    mp_print_exp(mp, p, 0);
    mp_print_chr(mp, ')');
}

static void mp_close_files (MP mp)
{
    if (mp->rd_fname != NULL) {
        for (int k = 0; k < (int) mp->read_files; k++) {
            if (mp->rd_fname[k] != NULL) {
                (mp->close_file)(mp, mp->rd_file[k]);
                mp_memory_free(mp->rd_fname[k]);
                mp->rd_fname[k] = NULL;
            }
        }
    }
    if (mp->wr_fname != NULL) {
        for (int k = 0; k < (int) mp->write_files; k++) {
            if (mp->wr_fname[k] != NULL) {
                (mp->close_file)(mp, mp->wr_file[k]);
                mp_memory_free(mp->wr_fname[k]);
                mp->wr_fname[k] = NULL;
            }
        }
    }
}

void mp_close_files_and_terminate (MP mp)
{
    if (mp->finished) {
        return;
    } else {
        mp_close_files(mp);
        wake_up_terminal();
        mp_print_ln(mp);
        mp->finished = 1;
    }
}

void mp_final_cleanup (MP mp)
{
    while (mp->input_ptr > 0) {
        if (token_state) {
            mp_end_token_list(mp);
        } else {
            mp_end_file_reading(mp);
        }
    }
    while (mp->loop_ptr != NULL) {
        mp_stop_iteration(mp);
    }
    if (mp->interaction < mp_silent_mode) {
        while (mp->open_parens > 0) {
            mp_print_str(mp, " )");
            --mp->open_parens;
        }
    }
    while (mp->cond_ptr != NULL) {
        mp_print_nl(mp, "(end occurred when ");
        mp_print_cmd_mod(mp, mp_fi_or_else_command, mp->cur_if);
        if (mp->if_line != 0) {
            mp_print_str(mp, " on line ");
            mp_print_int(mp, mp->if_line);
        }
        mp_print_str(mp, " was incomplete)");
        mp->if_line = mp_if_line_field(mp->cond_ptr);
        mp->cur_if = mp->cond_ptr->name_type;
        mp->cond_ptr = mp->cond_ptr->link;
    }
    if (mp->history != mp_spotless) {
        if (((mp->history == mp_warning_issued) || (mp->interaction < mp_error_stop_mode))) {
            if (mp->selector == mp_term_and_log_selector) {
                mp->selector = mp_term_only_selector;
                mp_print_nl(mp, "(see the transcript file for additional information)");
                mp->selector = mp_term_and_log_selector;
            }
        }
    }
}

 void mp_init_prim (MP mp)
{
    mp_primitive(mp, "tracingtitles", mp_internal_command, mp_tracing_titles_internal);
    mp_primitive(mp, "tracingequations", mp_internal_command, mp_tracing_equations_internal);
    mp_primitive(mp, "tracingcapsules", mp_internal_command, mp_tracing_capsules_internal);
    mp_primitive(mp, "tracingchoices", mp_internal_command, mp_tracing_choices_internal);
    mp_primitive(mp, "tracingspecs", mp_internal_command, mp_tracing_specs_internal);
    mp_primitive(mp, "tracingcommands", mp_internal_command, mp_tracing_commands_internal);
    mp_primitive(mp, "tracingrestores", mp_internal_command, mp_tracing_restores_internal);
    mp_primitive(mp, "tracingmacros", mp_internal_command, mp_tracing_macros_internal);
    mp_primitive(mp, "tracingoutput", mp_internal_command, mp_tracing_output_internal);
    mp_primitive(mp, "tracingstats", mp_internal_command, mp_tracing_stats_internal);
    mp_primitive(mp, "tracingonline", mp_internal_command, mp_tracing_online_internal);
    mp_primitive(mp, "year", mp_internal_command, mp_year_internal);
    mp_primitive(mp, "month", mp_internal_command, mp_month_internal);
    mp_primitive(mp, "day", mp_internal_command, mp_day_internal);
    mp_primitive(mp, "time", mp_internal_command, mp_time_internal);
    mp_primitive(mp, "hour", mp_internal_command, mp_hour_internal);
    mp_primitive(mp, "minute", mp_internal_command, mp_minute_internal);
    mp_primitive(mp, "charcode", mp_internal_command, mp_char_code_internal);
    mp_primitive(mp, "charwd", mp_internal_command, mp_char_wd_internal);
    mp_primitive(mp, "charht", mp_internal_command, mp_char_ht_internal);
    mp_primitive(mp, "chardp", mp_internal_command, mp_char_dp_internal);
    mp_primitive(mp, "charic", mp_internal_command, mp_char_ic_internal);
    mp_primitive(mp, "pausing", mp_internal_command, mp_pausing_internal);
    mp_primitive(mp, "showstopping", mp_internal_command, mp_showstopping_internal);
    mp_primitive(mp, "texscriptmode", mp_internal_command, mp_texscriptmode_internal);
    mp_primitive(mp, "overloadmode", mp_internal_command, mp_overloadmode_internal);
    mp_primitive(mp, "linejoin", mp_internal_command, mp_linejoin_internal);
    mp_primitive(mp, "linecap", mp_internal_command, mp_linecap_internal);
    mp_primitive(mp, "stacking", mp_internal_command, mp_stacking_internal);
    mp_primitive(mp, "miterlimit", mp_internal_command, mp_miterlimit_internal);
    mp_primitive(mp, "warningcheck", mp_internal_command, mp_warning_check_internal);
    mp_primitive(mp, "truecorners", mp_internal_command, mp_true_corners_internal);
    mp_primitive(mp, "defaultcolormodel", mp_internal_command, mp_default_color_model_internal);
    mp_primitive(mp, "restoreclipcolor", mp_internal_command, mp_restore_clip_color_internal);
    mp_primitive(mp, "numbersystem", mp_internal_command, mp_number_system_internal);
    mp_primitive(mp, "numberprecision", mp_internal_command, mp_number_precision_internal);
    mp_primitive(mp, "jobname", mp_internal_command, mp_job_name_internal);
    mp_primitive(mp, "..", mp_path_join_command, 0);
    mp_primitive(mp, "[", mp_left_bracket_command, 0);
    mp->frozen_left_bracket = mp_frozen_primitive (mp, "[", mp_left_bracket_command, 0);
    mp_primitive(mp, "]", mp_right_bracket_command, 0);
    mp_primitive(mp, "}", mp_right_brace_command, 0);
    mp_primitive(mp, "{", mp_left_brace_command, 0);
    mp_primitive(mp, ":", mp_colon_command, 0);
    mp->frozen_colon = mp_frozen_primitive (mp, ":", mp_colon_command, 0);
    mp_primitive(mp, ":=", mp_assignment_command, 0);
    mp_primitive(mp, ",", mp_comma_command, 0);
    mp_primitive(mp, ";", mp_semicolon_command, 0);
    mp->frozen_semicolon = mp_frozen_primitive (mp, ";", mp_semicolon_command, 0);
    mp_primitive(mp, "\\", mp_relax_command, 0);
    mp_primitive(mp, "addto", mp_add_to_command, 0);
    mp_primitive(mp, "atleast", mp_at_least_command, 0);
    mp_primitive(mp, "begingroup", mp_begin_group_command, 0);
    mp->bg_loc = cur_sym;
    mp_primitive(mp, "controls", mp_controls_command, 0);
    mp_primitive(mp, "curl", mp_curl_command, 0);
    mp_primitive(mp, "delimiters", mp_delimiters_command, 0);
    mp_primitive(mp, "endgroup", mp_end_group_command, 0);
    mp->eg_loc = cur_sym;
    mp->frozen_end_group = mp_frozen_primitive (mp, "endgroup", mp_end_group_command, 0);
    mp_primitive(mp, "everyjob", mp_every_job_command, 0);
    mp_primitive(mp, "exitif", mp_exit_test_command, 0);
    mp_primitive(mp, "expandafter", mp_expand_after_command, 0);
    mp_primitive(mp, "interim", mp_interim_command, 0);
    mp_primitive(mp, "let", mp_let_command, 0);
    mp_primitive(mp, "newinternal", mp_new_internal_command, 0);
    mp_primitive(mp, "of", mp_of_command, 0);
    mp_primitive(mp, "randomseed", mp_only_set_command, mp_random_seed_code);
    mp_primitive(mp, "maxknotpool", mp_only_set_command, mp_max_knot_pool_code);
    mp_primitive(mp, "save", mp_save_command, 0);
    mp_primitive(mp, "scantokens", mp_scan_tokens_command, 0);
    mp_primitive(mp, "runscript", mp_runscript_command, 0);
    mp_primitive(mp, "maketext", mp_maketext_command, 0);
    mp_primitive(mp, "shipout", mp_ship_out_command, 0);
    mp_primitive(mp, "step", mp_step_command, 0);
    mp_primitive(mp, "str", mp_str_command, 0);
    mp_primitive(mp, "void", mp_void_command, 0);
    mp_primitive(mp, "tension", mp_tension_command, 0);
    mp_primitive(mp, "to", mp_to_command, 0);
    mp_primitive(mp, "until", mp_until_command, 0);
    mp_primitive(mp, "within", mp_within_command, 0);
    mp_primitive(mp, "write", mp_write_command, 0);
    mp_primitive(mp, "btex", mp_btex_command, mp_btex_code);
    mp_primitive(mp, "verbatimtex", mp_btex_command, mp_verbatim_code);
    mp_primitive(mp, "etex", mp_etex_command, 0);
    mp->frozen_etex = mp_frozen_primitive (mp, "etex", mp_etex_command, 0);
    mp_primitive(mp, "def", mp_macro_def_command, mp_def_code);
    mp_primitive(mp, "vardef", mp_macro_def_command, mp_var_def_code);
    mp_primitive(mp, "primarydef", mp_macro_def_command, mp_primary_def_code);
    mp_primitive(mp, "secondarydef", mp_macro_def_command, mp_secondary_def_code);
    mp_primitive(mp, "tertiarydef", mp_macro_def_command, mp_tertiary_def_code);
    mp_primitive(mp, "enddef", mp_macro_def_command, mp_end_def_code);
    mp->frozen_end_def = mp_frozen_primitive(mp, "enddef", mp_macro_def_command, mp_end_def_code);
    mp_primitive(mp, "for", mp_iteration_command, mp_start_for_code);
    mp_primitive(mp, "forsuffixes", mp_iteration_command, mp_start_forsuffixes_code);
    mp_primitive(mp, "forever", mp_iteration_command, mp_start_forever_code);
    mp_primitive(mp, "endfor", mp_iteration_command, mp_end_for_code);
    mp->frozen_end_for = mp_frozen_primitive (mp, "endfor", mp_iteration_command, mp_end_for_code);
    mp_primitive(mp, "quote", mp_macro_special_command, mp_macro_quote_code);
    mp_primitive(mp, "#@", mp_macro_special_command, mp_macro_prefix_code);
    mp_primitive(mp, "@", mp_macro_special_command, mp_macro_at_code);
    mp_primitive(mp, "@#", mp_macro_special_command, mp_macro_suffix_code);
    mp_primitive(mp, "expr", mp_parameter_commmand, mp_expr_parameter);
    mp_primitive(mp, "suffix", mp_parameter_commmand, mp_suffix_parameter);
    mp_primitive(mp, "text", mp_parameter_commmand, mp_text_parameter);
    mp_primitive(mp, "primary", mp_parameter_commmand, mp_primary_macro);
    mp_primitive(mp, "secondary", mp_parameter_commmand, mp_secondary_macro);
    mp_primitive(mp, "tertiary", mp_parameter_commmand, mp_tertiary_macro);
    mp_primitive(mp, "input", mp_input_command, 0);
    mp_primitive(mp, "endinput", mp_input_command, 1);
    mp_primitive(mp, "if", mp_if_test_command, mp_if_code);
    mp_primitive(mp, "fi", mp_fi_or_else_command, mp_fi_code);
    mp->frozen_fi = mp_frozen_primitive (mp, "fi", mp_fi_or_else_command, mp_fi_code);
    mp_primitive(mp, "else", mp_fi_or_else_command, mp_else_code);
    mp_primitive(mp, "elseif", mp_fi_or_else_command, mp_else_if_code);
    mp_primitive(mp, "true", mp_nullary_command, mp_true_operation);
    mp_primitive(mp, "false", mp_nullary_command, mp_false_operation);
    mp_primitive(mp, "nullpicture", mp_nullary_command, mp_null_picture_operation);
    mp_primitive(mp, "nullpen", mp_nullary_command, mp_null_pen_operation);
    mp_primitive(mp, "readstring", mp_nullary_command, mp_read_string_operation);
    mp_primitive(mp, "pencircle", mp_nullary_command, mp_pen_circle_operation);
    mp_primitive(mp, "normaldeviate", mp_nullary_command, mp_normal_deviate_operation);
    mp_primitive(mp, "readfrom", mp_unary_command, mp_read_from_operation);
    mp_primitive(mp, "closefrom", mp_unary_command, mp_close_from_operation);
    mp_primitive(mp, "odd", mp_unary_command, mp_odd_operation);
    mp_primitive(mp, "known", mp_unary_command, mp_known_operation);
    mp_primitive(mp, "unknown", mp_unary_command, mp_unknown_operation);
    mp_primitive(mp, "not", mp_unary_command, mp_not_operation);
    mp_primitive(mp, "decimal", mp_unary_command, mp_decimal_operation);
    mp_primitive(mp, "reverse", mp_unary_command, mp_reverse_operation);
    mp_primitive(mp, "uncycle", mp_unary_command, mp_uncycle_operation);
    mp_primitive(mp, "makepath", mp_unary_command, mp_make_path_operation);
    mp_primitive(mp, "makepen", mp_unary_command, mp_make_pen_operation);
    mp_primitive(mp, "makenep", mp_unary_command, mp_make_nep_operation);
    mp_primitive(mp, "convexed", mp_unary_command, mp_convexed_operation);
    mp_primitive(mp, "uncontrolled", mp_unary_command, mp_uncontrolled_operation);
    mp_primitive(mp, "oct", mp_unary_command, mp_oct_operation);
    mp_primitive(mp, "hex", mp_unary_command, mp_hex_operation);
    mp_primitive(mp, "ASCII", mp_unary_command, mp_ASCII_operation);
    mp_primitive(mp, "char", mp_unary_command, mp_char_operation);
    mp_primitive(mp, "length", mp_unary_command, mp_length_operation);
    mp_primitive(mp, "nolength", mp_unary_command, mp_no_length_operation);
    mp_primitive(mp, "turningnumber", mp_unary_command, mp_turning_operation);
    mp_primitive(mp, "xpart", mp_unary_command, mp_x_part_operation);
    mp_primitive(mp, "ypart", mp_unary_command, mp_y_part_operation);
    mp_primitive(mp, "xxpart", mp_unary_command, mp_xx_part_operation);
    mp_primitive(mp, "xypart", mp_unary_command, mp_xy_part_operation);
    mp_primitive(mp, "yxpart", mp_unary_command, mp_yx_part_operation);
    mp_primitive(mp, "yypart", mp_unary_command, mp_yy_part_operation);
    mp_primitive(mp, "redpart", mp_unary_command, mp_red_part_operation);
    mp_primitive(mp, "greenpart", mp_unary_command, mp_green_part_operation);
    mp_primitive(mp, "bluepart", mp_unary_command, mp_blue_part_operation);
    mp_primitive(mp, "cyanpart", mp_unary_command, mp_cyan_part_operation);
    mp_primitive(mp, "magentapart", mp_unary_command, mp_magenta_part_operation);
    mp_primitive(mp, "yellowpart", mp_unary_command, mp_yellow_part_operation);
    mp_primitive(mp, "blackpart", mp_unary_command, mp_black_part_operation);
    mp_primitive(mp, "greypart", mp_unary_command, mp_grey_part_operation);
    mp_primitive(mp, "colormodel", mp_unary_command, mp_color_model_operation);
    mp_primitive(mp, "prescriptpart", mp_unary_command, mp_prescript_part_operation);
    mp_primitive(mp, "postscriptpart", mp_unary_command, mp_postscript_part_operation);
    mp_primitive(mp, "stackingpart", mp_unary_command, mp_stacking_part_operation);
    mp_primitive(mp, "pathpart", mp_unary_command, mp_path_part_operation);
    mp_primitive(mp, "penpart", mp_unary_command, mp_pen_part_operation);
    mp_primitive(mp, "dashpart", mp_unary_command, mp_dash_part_operation);
    mp_primitive(mp, "sqrt", mp_unary_command, mp_sqrt_operation);
    mp_primitive(mp, "mexp", mp_unary_command, mp_m_exp_operation);
    mp_primitive(mp, "mlog", mp_unary_command, mp_m_log_operation);
    mp_primitive(mp, "sind", mp_unary_command, mp_sin_d_operation);
    mp_primitive(mp, "cosd", mp_unary_command, mp_cos_d_operation);
    mp_primitive(mp, "floor", mp_unary_command, mp_floor_operation);
    mp_primitive(mp, "uniformdeviate", mp_unary_command, mp_uniform_deviate_operation);
    mp_primitive(mp, "llcorner", mp_unary_command, mp_ll_corner_operation);
    mp_primitive(mp, "lrcorner", mp_unary_command, mp_lr_corner_operation);
    mp_primitive(mp, "ulcorner", mp_unary_command, mp_ul_corner_operation);
    mp_primitive(mp, "urcorner", mp_unary_command, mp_ur_corner_operation);
    mp_primitive(mp, "centerof", mp_unary_command, mp_center_of_operation);
    mp_primitive(mp, "centerofmass", mp_unary_command, mp_center_of_mass_operation);
    mp_primitive(mp, "corners", mp_unary_command, mp_corners_operation);
    mp_primitive(mp, "xrange", mp_unary_command, mp_x_range_operation);
    mp_primitive(mp, "yrange", mp_unary_command, mp_y_range_operation);
    mp_primitive(mp, "deltapoint", mp_unary_command, mp_delta_point_operation);
    mp_primitive(mp, "deltaprecontrol", mp_unary_command, mp_delta_precontrol_operation);
    mp_primitive(mp, "deltapostcontrol", mp_unary_command, mp_delta_postcontrol_operation);
    mp_primitive(mp, "deltadirection", mp_unary_command, mp_delta_direction_operation);
    mp_primitive(mp, "arclength", mp_unary_command, mp_arc_length_operation);
    mp_primitive(mp, "angle", mp_unary_command, mp_angle_operation);
    mp_primitive(mp, "cycle", mp_cycle_command, mp_cycle_operation);
    mp_primitive(mp, "nocycle", mp_cycle_command, mp_no_cycle_operation);
    mp_primitive(mp, "stroked", mp_unary_command, mp_stroked_operation);
    mp_primitive(mp, "filled", mp_unary_command, mp_filled_operation);
    mp_primitive(mp, "clipped", mp_unary_command, mp_clipped_operation);
    mp_primitive(mp, "grouped", mp_unary_command, mp_grouped_operation);
    mp_primitive(mp, "bounded", mp_unary_command, mp_bounded_operation);
    mp_primitive(mp, "+", mp_plus_or_minus_command, mp_plus_operation);
    mp_primitive(mp, "-", mp_plus_or_minus_command, mp_minus_operation);
    mp_primitive(mp, "*", mp_secondary_binary_command, mp_times_operation);
    mp_primitive(mp, "/", mp_slash_command, mp_over_operation);
    mp->frozen_slash = mp_frozen_primitive (mp, "/", mp_slash_command, mp_over_operation);
    mp_primitive(mp, "^", mp_secondary_binary_command, mp_power_operation);
    mp_primitive(mp, "++", mp_tertiary_binary_command, mp_pythag_add_operation);
    mp_primitive(mp, "+-+", mp_tertiary_binary_command, mp_pythag_sub_operation);
    mp_primitive(mp, "or", mp_tertiary_binary_command, mp_or_operation);
    mp_primitive(mp, "and", mp_and_command, mp_and_operation);
    mp_primitive(mp, "<", mp_primary_binary_command, mp_less_than_operation);
    mp_primitive(mp, "<=", mp_primary_binary_command, mp_less_or_equal_operation);
    mp_primitive(mp, ">", mp_primary_binary_command, mp_greater_than_operation);
    mp_primitive(mp, ">=", mp_primary_binary_command, mp_greater_or_equal_operation);
    mp_primitive(mp, "=", mp_equals_command, mp_equal_operation);
    mp_primitive(mp, "<>", mp_primary_binary_command, mp_unequal_operation);
    mp_primitive(mp, "substring", mp_of_binary_command, mp_substring_operation);
    mp_primitive(mp, "subpath", mp_of_binary_command, mp_subpath_operation);
    mp_primitive(mp, "directiontime", mp_of_binary_command, mp_direction_time_operation);
    mp_primitive(mp, "point", mp_of_binary_command, mp_point_operation);
    mp_primitive(mp, "precontrol", mp_of_binary_command, mp_precontrol_operation);
    mp_primitive(mp, "postcontrol", mp_of_binary_command, mp_postcontrol_operation);
    mp_primitive(mp, "direction", mp_of_binary_command, mp_direction_operation);
    mp_primitive(mp, "pathpoint", mp_nullary_command, mp_path_point_operation);
    mp_primitive(mp, "pathprecontrol", mp_nullary_command, mp_path_precontrol_operation);
    mp_primitive(mp, "pathpostcontrol", mp_nullary_command, mp_path_postcontrol_operation);
    mp_primitive(mp, "pathdirection", mp_nullary_command, mp_path_direction_operation);
    mp_primitive(mp, "pathstate", mp_nullary_command, mp_path_state_operation);
    mp_primitive(mp, "pathindex", mp_nullary_command, mp_path_index_operation);
    mp_primitive(mp, "pathlastindex", mp_nullary_command, mp_path_lastindex_operation);
    mp_primitive(mp, "pathlength", mp_nullary_command, mp_path_length_operation);
    mp_primitive(mp, "pathfirst", mp_nullary_command, mp_path_first_operation);
    mp_primitive(mp, "pathlast", mp_nullary_command, mp_path_last_operation);
    mp_primitive(mp, "penoffset", mp_of_binary_command, mp_pen_offset_operation);
    mp_primitive(mp, "arctime", mp_of_binary_command, mp_arc_time_operation);
    mp_primitive(mp, "arcpoint", mp_of_binary_command, mp_arc_point_operation);
    mp_primitive(mp, "arcpointlist", mp_of_binary_command, mp_arc_point_list_operation);
    mp_primitive(mp, "subarclength", mp_of_binary_command, mp_subarc_length_operation);
    mp_primitive(mp, "mpversion", mp_nullary_command, mp_version_operation);
    mp_primitive(mp, "&", mp_ampersand_command, mp_concatenate_operation);
    mp_primitive(mp, "&&", mp_ampersand_command, mp_just_append_operation);
    mp_primitive(mp, "&&&", mp_ampersand_command, mp_tolerant_concat_operation);
    mp_primitive(mp, "&&&&", mp_ampersand_command, mp_tolerant_append_operation);
    mp_primitive(mp, "rotated", mp_secondary_binary_command, mp_rotated_operation);
    mp_primitive(mp, "slanted", mp_secondary_binary_command, mp_slanted_operation);
    mp_primitive(mp, "scaled", mp_secondary_binary_command, mp_scaled_operation);
    mp_primitive(mp, "shifted", mp_secondary_binary_command, mp_shifted_operation);
    mp_primitive(mp, "transformed", mp_secondary_binary_command, mp_transformed_operation);
    mp_primitive(mp, "xscaled", mp_secondary_binary_command, mp_x_scaled_operation);
    mp_primitive(mp, "yscaled", mp_secondary_binary_command, mp_y_scaled_operation);
    mp_primitive(mp, "zscaled", mp_secondary_binary_command, mp_z_scaled_operation);
    mp_primitive(mp, "intersectiontimes", mp_tertiary_binary_command, mp_intertimes_operation);
    mp_primitive(mp, "intersectiontimeslist", mp_tertiary_binary_command, mp_intertimes_list_operation);
    mp_primitive(mp, "envelope", mp_of_binary_command, mp_envelope_operation);
    mp_primitive(mp, "boundingpath", mp_of_binary_command, mp_boundingpath_operation);
    mp_primitive(mp, "numeric", mp_type_name_command, mp_numeric_type_operation);
    mp_primitive(mp, "string", mp_type_name_command, mp_string_type_operation);
    mp_primitive(mp, "boolean", mp_type_name_command, mp_boolean_type_operation);
    mp_primitive(mp, "path", mp_type_name_command, mp_path_type_operation);
    mp_primitive(mp, "pen", mp_type_name_command, mp_pen_type_operation);
    mp_primitive(mp, "nep", mp_type_name_command, mp_nep_type_operation);
    mp_primitive(mp, "picture", mp_type_name_command, mp_picture_type_operation);
    mp_primitive(mp, "transform", mp_type_name_command, mp_transform_type_operation);
    mp_primitive(mp, "color", mp_type_name_command, mp_color_type_operation);
    mp_primitive(mp, "rgbcolor", mp_type_name_command, mp_color_type_operation);
    mp_primitive(mp, "cmykcolor", mp_type_name_command, mp_cmykcolor_type_operation);
    mp_primitive(mp, "pair", mp_type_name_command, mp_pair_type_operation);
    mp_primitive(mp, "end", mp_stop_command, 0);
    mp_primitive(mp, "dump", mp_stop_command, 1);
    mp->frozen_dump = mp_frozen_primitive (mp, "dump", mp_stop_command, 1);
    mp_primitive(mp, "batchmode", mp_mode_command, mp_batch_mode);
    mp_primitive(mp, "nonstopmode", mp_mode_command, mp_nonstop_mode);
    mp_primitive(mp, "scrollmode", mp_mode_command, mp_scroll_mode);
    mp_primitive(mp, "errorstopmode", mp_mode_command, mp_error_stop_mode);
    mp_primitive(mp, "silentmode", mp_mode_command, mp_silent_mode);
    mp_primitive(mp, "inner", mp_protection_command, 0);
    mp_primitive(mp, "outer", mp_protection_command, 1);
    mp_primitive(mp, "setproperty", mp_property_command, 1);
    mp_primitive(mp, "showtoken", mp_show_command, mp_show_token_code);
    mp_primitive(mp, "showstats", mp_show_command, mp_show_stats_code);
    mp_primitive(mp, "show", mp_show_command, mp_show_code);
    mp_primitive(mp, "showvariable", mp_show_command, mp_show_var_code);
    mp_primitive(mp, "showdependencies", mp_show_command, mp_show_dependencies_code);
    mp_primitive(mp, "doublepath", mp_thing_to_add_command, mp_add_double_path_code);
    mp_primitive(mp, "contour", mp_thing_to_add_command, mp_add_contour_code);
    mp_primitive(mp, "also", mp_thing_to_add_command, mp_add_also_code);
    mp_primitive(mp, "withpen", mp_with_option_command, mp_with_pen_code);
    mp_primitive(mp, "dashed", mp_with_option_command, mp_with_dashed_code);
    mp_primitive(mp, "withprescript", mp_with_option_command, mp_with_pre_script_code);
    mp_primitive(mp, "withpostscript", mp_with_option_command, mp_with_post_script_code);
    mp_primitive(mp, "withstacking", mp_with_option_command, mp_with_stacking_code);
    mp_primitive(mp, "withlinecap", mp_with_option_command, mp_with_linecap_code);
    mp_primitive(mp, "withlinejoin", mp_with_option_command, mp_with_linejoin_code);
    mp_primitive(mp, "withmiterlimit", mp_with_option_command, mp_with_miterlimit_code);
    mp_primitive(mp, "withoutcolor", mp_with_option_command, mp_with_no_model_code);
    mp_primitive(mp, "withgreyscale", mp_with_option_command, mp_with_grey_model_code);
    mp_primitive(mp, "withcolor", mp_with_option_command, mp_with_uninitialized_model_code);
    mp_primitive(mp, "withrgbcolor", mp_with_option_command, mp_with_rgb_model_code);
    mp_primitive(mp, "withcmykcolor", mp_with_option_command, mp_with_cmyk_model_code);
    mp_primitive(mp, "clip", mp_bounds_command, mp_start_clip_node_type);
    mp_primitive(mp, "setgroup", mp_bounds_command, mp_start_group_node_type);
    mp_primitive(mp, "setbounds", mp_bounds_command, mp_start_bounds_node_type);
    mp_primitive(mp, "message", mp_message_command, message_code);
    mp_primitive(mp, "errmessage", mp_message_command, err_message_code);
    mp_primitive(mp, "errhelp", mp_message_command, err_help_code);

}

 void mp_init_tab (MP mp)
{
    mp->spec_head = mp_new_symbolic_node(mp);
    mp->temp_head = mp_new_symbolic_node(mp);
    mp->hold_head = mp_new_symbolic_node(mp);
    number_clone(internal_value(mp_default_color_model_internal),unity_t);
    number_multiply_int(internal_value(mp_default_color_model_internal), mp_rgb_model);
    number_clone(internal_value(mp_restore_clip_color_internal), unity_t);
    set_internal_string(mp_number_system_internal, mp_intern (mp, "scaled"));
    number_clone(internal_value(mp_number_precision_internal), precision_default);
    number_clone(internal_value(mp_texscriptmode_internal), unity_t);
    number_clone(internal_value(mp_overloadmode_internal), zero_t);
    set_internal_name(mp_tracing_titles_internal,      mp_strdup("tracingtitles"));
    set_internal_name(mp_tracing_equations_internal,   mp_strdup("tracingequations"));
    set_internal_name(mp_tracing_capsules_internal,    mp_strdup("tracingcapsules"));
    set_internal_name(mp_tracing_choices_internal,     mp_strdup("tracingchoices"));
    set_internal_name(mp_tracing_specs_internal,       mp_strdup("tracingspecs"));
    set_internal_name(mp_tracing_commands_internal,    mp_strdup("tracingcommands"));
    set_internal_name(mp_tracing_restores_internal,    mp_strdup("tracingrestores"));
    set_internal_name(mp_tracing_macros_internal,      mp_strdup("tracingmacros"));
    set_internal_name(mp_tracing_output_internal,      mp_strdup("tracingoutput"));
    set_internal_name(mp_tracing_stats_internal,       mp_strdup("tracingstats"));
    set_internal_name(mp_tracing_online_internal,      mp_strdup("tracingonline"));
    set_internal_name(mp_year_internal,                mp_strdup("year"));
    set_internal_name(mp_month_internal,               mp_strdup("month"));
    set_internal_name(mp_day_internal,                 mp_strdup("day"));
    set_internal_name(mp_time_internal,                mp_strdup("time"));
    set_internal_name(mp_hour_internal,                mp_strdup("hour"));
    set_internal_name(mp_minute_internal,              mp_strdup("minute"));
    set_internal_name(mp_char_code_internal,           mp_strdup("charcode"));
    set_internal_name(mp_char_wd_internal,             mp_strdup("charwd"));
    set_internal_name(mp_char_ht_internal,             mp_strdup("charht"));
    set_internal_name(mp_char_dp_internal,             mp_strdup("chardp"));
    set_internal_name(mp_char_ic_internal,             mp_strdup("charic"));
    set_internal_name(mp_pausing_internal,             mp_strdup("pausing"));
    set_internal_name(mp_showstopping_internal,        mp_strdup("showstopping"));
    set_internal_name(mp_texscriptmode_internal,       mp_strdup("texscriptmode"));
    set_internal_name(mp_overloadmode_internal,        mp_strdup("overloadmode"));
    set_internal_name(mp_linejoin_internal,            mp_strdup("linejoin"));
    set_internal_name(mp_linecap_internal,             mp_strdup("linecap"));
    set_internal_name(mp_stacking_internal,            mp_strdup("stacking"));
    set_internal_name(mp_miterlimit_internal,          mp_strdup("miterlimit"));
    set_internal_name(mp_warning_check_internal,       mp_strdup("warningcheck"));
    set_internal_name(mp_true_corners_internal,        mp_strdup("truecorners"));
    set_internal_name(mp_default_color_model_internal, mp_strdup("defaultcolormodel"));
    set_internal_name(mp_restore_clip_color_internal,  mp_strdup("restoreclipcolor"));
    set_internal_name(mp_job_name_internal,            mp_strdup("jobname"));
    set_internal_name(mp_number_system_internal,       mp_strdup("numbersystem"));
    set_internal_name(mp_number_precision_internal,    mp_strdup("numberprecision"));
    mp->id_lookup_test = new_symbols_entry(mp, NULL, 0);
    mp->st_count = 0;
    mp->frozen_bad_vardef      = mp_frozen_primitive(mp, "a bad variable", mp_tag_command, 0);
    mp->frozen_right_delimiter = mp_frozen_primitive(mp, ")",              mp_right_delimiter_command, 0);
    mp->frozen_inaccessible    = mp_frozen_primitive(mp, " INACCESSIBLE",  mp_tag_command, 0);
    mp->frozen_undefined       = mp_frozen_primitive(mp, " UNDEFINED",     mp_tag_command, 0);
    mp->end_attr = (mp_node) mp_get_attribute_node(mp);
    mp_set_hashloc(mp->end_attr, (mp_sym)-1);
    mp_set_parent((mp_value_node) mp->end_attr, NULL);
    new_fraction(mp->st);
    new_fraction(mp->ct);
    new_fraction(mp->sf);
    new_fraction(mp->cf);
    for (int i = 0; i <= mp_y_code; i++) {
        new_number(mp->bbmin[i]);
        new_number(mp->bbmax[i]);
    }
    new_number(mp->cur_x);
    new_number(mp->cur_y);
    mp->null_dash = mp_get_dash_node(mp);
    new_number(mp->cur_t);
    new_number(mp->cur_tt);
    new_number(mp->max_t);
    new_number(mp->delx);
    new_number(mp->dely);
    new_number(mp->appr_t);
    new_number(mp->appr_tt);
    mp->serial_no = 0;
    mp->dep_head  = mp_get_dep_node(mp);
    mp_set_link(mp->dep_head, mp->dep_head);
    mp_set_prev_dep(mp->dep_head, mp->dep_head);
    mp_set_dep_info(mp->dep_head, NULL);
    mp_set_dep_list(mp->dep_head, NULL);
    mp->cur_mod_ = mp_new_symbolic_node(mp);
    mp->bad_vardef = mp_new_value_node(mp);
    mp->bad_vardef->name_type = mp_root_operation;
    mp_set_value_sym(mp->bad_vardef, mp->frozen_bad_vardef);
    mp->frozen_repeat_loop =

    mp_frozen_primitive (mp, " ENDFOR", mp_repeat_loop_command, 0);
    for (int i = 0; i < mp_proto_dependent_type + 1; i++) {
        new_number(mp->max_c[i]);
    }
    mp->temp_val = mp_new_value_node(mp);
    mp->temp_val->name_type = mp_capsule_operation;
    new_number(mp->txx);
    new_number(mp->txy);
    new_number(mp->tyx);
    new_number(mp->tyy);
    new_number(mp->tx);
    new_number(mp->ty);
    mp->inf_val = mp_new_value_node(mp);
    mp_set_value_number(mp->inf_val, fraction_four_t);
    mp->zero_val = mp_new_value_node(mp);
    mp_set_value_number(mp->zero_val, zero_t);

}

